| <?php |
|
|
| |
| |
| |
| |
| |
| |
|
|
| namespace Piwik\Db; |
|
|
| use Exception; |
| use Piwik\Common; |
| use Piwik\Config; |
| use Piwik\Config\DatabaseConfig; |
| use Piwik\Container\StaticContainer; |
| use Piwik\Db; |
| use Piwik\ExceptionHandler; |
| use Piwik\Log; |
| use Piwik\SettingsServer; |
| use Piwik\SettingsPiwik; |
|
|
| class BatchInsert |
| { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static function tableInsertBatchIterate($tableName, $fields, $values, $ignoreWhenDuplicate = true) |
| { |
| $tableName = preg_replace('/[^a-zA-Z\d_-]/', '', $tableName); |
| $fieldList = '(' . join(',', $fields) . ')'; |
| $ignore = $ignoreWhenDuplicate ? 'IGNORE' : ''; |
|
|
| foreach ($values as $row) { |
| $row = array_values($row); |
| $query = "INSERT $ignore INTO `" . $tableName . "` |
| $fieldList |
| VALUES (" . Common::getSqlStringFieldsArray($row) . ")"; |
| Db::query($query, $row); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public static function tableInsertBatchSql($tableName, $fields, $values, $ignoreWhenDuplicate = true) |
| { |
| $insertLines = array(); |
| $bind = array(); |
| foreach ($values as $row) { |
| $insertLines[] = "(" . Common::getSqlStringFieldsArray($row) . ")"; |
| $bind = array_merge($bind, $row); |
| } |
|
|
| $fieldList = '(' . implode(',', $fields) . ')'; |
| $insertLines = implode(',', $insertLines); |
| $ignore = $ignoreWhenDuplicate ? 'IGNORE' : ''; |
| $query = "INSERT $ignore INTO $tableName $fieldList VALUES $insertLines"; |
| Db::query($query, $bind); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static function tableInsertBatch($tableName, $fields, $values, $throwException = false, $charset = 'utf8') |
| { |
| $loadDataInfileEnabled = Config::getInstance()->General['enable_load_data_infile']; |
|
|
| if ( |
| $loadDataInfileEnabled |
| && Db::get()->hasBulkLoader() |
| ) { |
| $path = self::getBestPathForLoadData(); |
| $instanceId = SettingsPiwik::getPiwikInstanceId(); |
| if (empty($instanceId)) { |
| $instanceId = ''; |
| } |
| $filePath = $path . $tableName . '-' . $instanceId . Common::generateUniqId() . '.csv'; |
|
|
| |
| if (DatabaseConfig::isTiDb()) { |
| $charset = 'utf8'; |
| } |
|
|
| try { |
| $fileSpec = array( |
| 'delim' => "\t", |
| 'quote' => '"', |
| 'escape' => '\\\\', |
| 'escapespecial_cb' => function ($str) { |
| return str_replace(array(chr(92), chr(34)), array(chr(92) . chr(92), chr(92) . chr(34)), $str); |
| }, |
| 'eol' => "\r\n", |
| 'null' => 'NULL', |
| 'charset' => $charset, |
| ); |
|
|
| self::createCSVFile($filePath, $fileSpec, $values); |
|
|
| if (!is_readable($filePath)) { |
| throw new Exception("File $filePath could not be read."); |
| } |
|
|
| $rc = self::createTableFromCSVFile($tableName, $fields, $filePath, $fileSpec); |
| if ($rc) { |
| unlink($filePath); |
| return true; |
| } |
| } catch (Exception $e) { |
| if ($throwException) { |
| throw $e; |
| } |
| } |
|
|
| |
| if (file_exists($filePath)) { |
| @unlink($filePath); |
| } |
| } |
|
|
| self::tableInsertBatchIterate($tableName, $fields, $values); |
|
|
| return false; |
| } |
|
|
| private static function getBestPathForLoadData() |
| { |
| try { |
| $path = Db::fetchOne('SELECT @@secure_file_priv'); |
| } catch (Exception $e) { |
| |
| |
| } |
|
|
| if (empty($path) || !@is_dir($path) || !@is_writable($path)) { |
| $path = StaticContainer::get('path.tmp') . '/assets/'; |
| } elseif (!Common::stringEndsWith($path, '/')) { |
| $path .= '/'; |
| } |
|
|
| return $path; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public static function createTableFromCSVFile($tableName, $fields, $filePath, $fileSpec) |
| { |
| |
| $chrootPath = Config::getInstance()->General['absolute_chroot_path']; |
| if (!empty($chrootPath)) { |
| $filePath = $chrootPath . $filePath; |
| } |
|
|
| |
| if (SettingsServer::isWindows()) { |
| $filePath = str_replace('\\', '/', $filePath); |
| } |
|
|
| $query = " |
| '$filePath' |
| REPLACE |
| INTO TABLE |
| `" . $tableName . "`"; |
|
|
| if (isset($fileSpec['charset'])) { |
| $query .= ' CHARACTER SET ' . $fileSpec['charset']; |
| } |
|
|
| $fieldList = '(' . join(',', $fields) . ')'; |
|
|
| $query .= " |
| FIELDS TERMINATED BY |
| '" . $fileSpec['delim'] . "' |
| ENCLOSED BY |
| '" . $fileSpec['quote'] . "' |
| "; |
| if (isset($fileSpec['escape'])) { |
| $query .= " ESCAPED BY '" . $fileSpec['escape'] . "'"; |
| } |
| $query .= " |
| LINES TERMINATED BY |
| '" . $fileSpec['eol'] . "' |
| $fieldList |
| "; |
|
|
| |
| |
| |
| |
| |
| if (Config::getInstance()->General['multi_server_environment']) { |
| $keywords = array(); |
| } else { |
| $keywords = array(''); |
| } |
|
|
| |
| |
| |
| |
| |
| $openBaseDir = ini_get('open_basedir'); |
| $safeMode = ini_get('safe_mode'); |
|
|
| if ((function_exists('mysqli_get_client_stats') || empty($openBaseDir)) && empty($safeMode)) { |
| |
| |
| $keywords[] = 'LOCAL '; |
| } |
|
|
| $exceptions = array(); |
| foreach ($keywords as $keyword) { |
| $queryStart = 'LOAD DATA ' . $keyword . 'INFILE '; |
| $sql = $queryStart . $query; |
| try { |
| $result = @Db::exec($sql); |
| if (empty($result) || $result < 0) { |
| continue; |
| } |
|
|
| return true; |
| } catch (Exception $e) { |
| $code = $e->getCode(); |
| $message = $e->getMessage() . ($code ? "[$code]" : ''); |
| if (ExceptionHandler::shouldPrintBackTraceWithMessage()) { |
| $message .= "\n" . $e->getTraceAsString(); |
| } |
| $exceptions[] = "\n Try #" . (count($exceptions) + 1) . ': ' . $queryStart . ": " . $message; |
| } |
| } |
|
|
| if (count($exceptions)) { |
| $message = "LOAD DATA INFILE failed... Error was: " . implode(",", $exceptions); |
| Log::info($message); |
| throw new Exception($message); |
| } |
|
|
| return false; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| protected static function createCSVFile($filePath, $fileSpec, $rows) |
| { |
| |
| $delim = $fileSpec['delim']; |
| $quote = $fileSpec['quote']; |
| $eol = $fileSpec['eol']; |
| $null = $fileSpec['null']; |
| $escapespecial_cb = $fileSpec['escapespecial_cb']; |
|
|
| $fp = @fopen($filePath, 'wb'); |
| if (!$fp) { |
| throw new Exception('Error creating the tmp file ' . $filePath . ', please check that the webserver has write permission to write this file.'); |
| } |
|
|
| foreach ($rows as $row) { |
| $output = ''; |
| foreach ($row as $value) { |
| if (!isset($value) || is_null($value) || $value === false) { |
| $output .= $null . $delim; |
| } else { |
| $output .= $quote . $escapespecial_cb($value) . $quote . $delim; |
| } |
| } |
|
|
| |
| $output = substr_replace($output, $eol, -1); |
|
|
| $ret = fwrite($fp, $output); |
| if (!$ret) { |
| fclose($fp); |
| throw new Exception('Error writing to the tmp file ' . $filePath); |
| } |
| } |
|
|
| fclose($fp); |
|
|
| @chmod($filePath, 0777); |
| } |
| } |
|
|