diff options
Diffstat (limited to 'hugo/libraries/plugins/import/ImportSql.class.php')
| -rw-r--r-- | hugo/libraries/plugins/import/ImportSql.class.php | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/hugo/libraries/plugins/import/ImportSql.class.php b/hugo/libraries/plugins/import/ImportSql.class.php new file mode 100644 index 0000000..146485a --- /dev/null +++ b/hugo/libraries/plugins/import/ImportSql.class.php @@ -0,0 +1,440 @@ +<?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * SQL import plugin for phpMyAdmin + * + * @package PhpMyAdmin-Import + * @subpackage SQL + */ +if (! defined('PHPMYADMIN')) { + exit; +} + +/* Get the import interface */ +require_once 'libraries/plugins/ImportPlugin.class.php'; + +/** + * Handles the import for the SQL format + * + * @package PhpMyAdmin-Import + * @subpackage SQL + */ +class ImportSql extends ImportPlugin +{ + /** + * Constructor + */ + public function __construct() + { + $this->setProperties(); + } + + /** + * Sets the import plugin properties. + * Called in the constructor. + * + * @return void + */ + protected function setProperties() + { + $props = 'libraries/properties/'; + include_once "$props/plugins/ImportPluginProperties.class.php"; + include_once "$props/options/groups/OptionsPropertyRootGroup.class.php"; + include_once "$props/options/groups/OptionsPropertyMainGroup.class.php"; + include_once "$props/options/items/SelectPropertyItem.class.php"; + include_once "$props/options/items/BoolPropertyItem.class.php"; + + $importPluginProperties = new ImportPluginProperties(); + $importPluginProperties->setText('SQL'); + $importPluginProperties->setExtension('sql'); + $importPluginProperties->setOptionsText(__('Options')); + + $compats = PMA_DBI_getCompatibilities(); + if (count($compats) > 0) { + $values = array(); + foreach ($compats as $val) { + $values[$val] = $val; + } + + // create the root group that will be the options field for + // $importPluginProperties + // this will be shown as "Format specific options" + $importSpecificOptions = new OptionsPropertyRootGroup(); + $importSpecificOptions->setName("Format Specific Options"); + + // general options main group + $generalOptions = new OptionsPropertyMainGroup(); + $generalOptions->setName("general_opts"); + // create primary items and add them to the group + $leaf = new SelectPropertyItem(); + $leaf->setName("compatibility"); + $leaf->setText(__('SQL compatibility mode:')); + $leaf->setValues($values); + $leaf->setDoc( + array( + 'manual_MySQL_Database_Administration', + 'Server_SQL_mode', + ) + ); + $generalOptions->addProperty($leaf); + $leaf = new BoolPropertyItem(); + $leaf->setName("no_auto_value_on_zero"); + $leaf->setText( + __('Do not use <code>AUTO_INCREMENT</code> for zero values') + ); + $leaf->setDoc( + array( + 'manual_MySQL_Database_Administration', + 'Server_SQL_mode', + 'sqlmode_no_auto_value_on_zero' + ) + ); + $generalOptions->addProperty($leaf); + + // add the main group to the root group + $importSpecificOptions->addProperty($generalOptions); + // set the options for the import plugin property item + $importPluginProperties->setOptions($importSpecificOptions); + } + + $this->properties = $importPluginProperties; + } + + /** + * This method is called when any PluginManager to which the observer + * is attached calls PluginManager::notify() + * + * @param SplSubject $subject The PluginManager notifying the observer + * of an update. + * + * @return void + */ + public function update (SplSubject $subject) + { + } + + /** + * Handles the whole import logic + * + * @param array &$sql_data 2-element array with sql data + * + * @return void + */ + public function doImport(&$sql_data = array()) + { + global $error, $timeout_passed; + + $buffer = ''; + // Defaults for parser + $sql = ''; + $start_pos = 0; + $i = 0; + $len= 0; + $big_value = 2147483647; + // include the space because it's mandatory + $delimiter_keyword = 'DELIMITER '; + $length_of_delimiter_keyword = strlen($delimiter_keyword); + + if (isset($_POST['sql_delimiter'])) { + $sql_delimiter = $_POST['sql_delimiter']; + } else { + $sql_delimiter = ';'; + } + + // Handle compatibility options + $sql_modes = array(); + if (isset($_REQUEST['sql_compatibility']) + && 'NONE' != $_REQUEST['sql_compatibility'] + ) { + $sql_modes[] = $_REQUEST['sql_compatibility']; + } + if (isset($_REQUEST['sql_no_auto_value_on_zero'])) { + $sql_modes[] = 'NO_AUTO_VALUE_ON_ZERO'; + } + if (count($sql_modes) > 0) { + PMA_DBI_try_query('SET SQL_MODE="' . implode(',', $sql_modes) . '"'); + } + unset($sql_modes); + + /** + * will be set in PMA_importGetNextChunk() + * + * @global boolean $GLOBALS['finished'] + */ + $GLOBALS['finished'] = false; + + while (! ($GLOBALS['finished'] && $i >= $len) + && ! $error + && ! $timeout_passed + ) { + $data = PMA_importGetNextChunk(); + if ($data === false) { + // subtract data we didn't handle yet and stop processing + $offset -= strlen($buffer); + break; + } elseif ($data === true) { + // Handle rest of buffer + } else { + // Append new data to buffer + $buffer .= $data; + // free memory + unset($data); + // Do not parse string when we're not at the end + // and don't have ; inside + if ((strpos($buffer, $sql_delimiter, $i) === false) + && ! $GLOBALS['finished'] + ) { + continue; + } + } + // Current length of our buffer + $len = strlen($buffer); + + // Grab some SQL queries out of it + while ($i < $len) { + $found_delimiter = false; + // Find first interesting character + $old_i = $i; + // this is about 7 times faster that looking for each sequence i + // one by one with strpos() + $match = preg_match( + '/(\'|"|#|-- |\/\*|`|(?i)(?<![A-Z0-9_])' + . $delimiter_keyword . ')/', + $buffer, + $matches, + PREG_OFFSET_CAPTURE, + $i + ); + if ($match) { + // in $matches, index 0 contains the match for the complete + // expression but we don't use it + $first_position = $matches[1][1]; + } else { + $first_position = $big_value; + } + /** + * @todo we should not look for a delimiter that might be + * inside quotes (or even double-quotes) + */ + // the cost of doing this one with preg_match() would be too high + $first_sql_delimiter = strpos($buffer, $sql_delimiter, $i); + if ($first_sql_delimiter === false) { + $first_sql_delimiter = $big_value; + } else { + $found_delimiter = true; + } + + // set $i to the position of the first quote, + // comment.start or delimiter found + $i = min($first_position, $first_sql_delimiter); + + if ($i == $big_value) { + // none of the above was found in the string + + $i = $old_i; + if (! $GLOBALS['finished']) { + break; + } + // at the end there might be some whitespace... + if (trim($buffer) == '') { + $buffer = ''; + $len = 0; + break; + } + // We hit end of query, go there! + $i = strlen($buffer) - 1; + } + + // Grab current character + $ch = $buffer[$i]; + + // Quotes + if (strpos('\'"`', $ch) !== false) { + $quote = $ch; + $endq = false; + while (! $endq) { + // Find next quote + $pos = strpos($buffer, $quote, $i + 1); + /* + * Behave same as MySQL and accept end of query as end + * of backtick. + * I know this is sick, but MySQL behaves like this: + * + * SELECT * FROM `table + * + * is treated like + * + * SELECT * FROM `table` + */ + if ($pos === false && $quote == '`' && $found_delimiter) { + $pos = $first_sql_delimiter - 1; + } elseif ($pos === false) { // No quote? Too short string + // We hit end of string => unclosed quote, + // but we handle it as end of query + if ($GLOBALS['finished']) { + $endq = true; + $i = $len - 1; + } + $found_delimiter = false; + break; + } + // Was not the quote escaped? + $j = $pos - 1; + while ($buffer[$j] == '\\') { + $j--; + } + // Even count means it was not escaped + $endq = (((($pos - 1) - $j) % 2) == 0); + // Skip the string + $i = $pos; + + if ($first_sql_delimiter < $pos) { + $found_delimiter = false; + } + } + if (! $endq) { + break; + } + $i++; + // Aren't we at the end? + if ($GLOBALS['finished'] && $i == $len) { + $i--; + } else { + continue; + } + } + + // Not enough data to decide + if ((($i == ($len - 1) && ($ch == '-' || $ch == '/')) + || ($i == ($len - 2) && (($ch == '-' && $buffer[$i + 1] == '-') + || ($ch == '/' && $buffer[$i + 1] == '*')))) + && ! $GLOBALS['finished'] + ) { + break; + } + + // Comments + if ($ch == '#' + || ($i < ($len - 1) && $ch == '-' && $buffer[$i + 1] == '-' + && (($i < ($len - 2) && $buffer[$i + 2] <= ' ') + || ($i == ($len - 1) && $GLOBALS['finished']))) + || ($i < ($len - 1) && $ch == '/' && $buffer[$i + 1] == '*') + ) { + // Copy current string to SQL + if ($start_pos != $i) { + $sql .= substr($buffer, $start_pos, $i - $start_pos); + } + // Skip the rest + $start_of_comment = $i; + // do not use PHP_EOL here instead of "\n", because the export + // file might have been produced on a different system + $i = strpos($buffer, $ch == '/' ? '*/' : "\n", $i); + // didn't we hit end of string? + if ($i === false) { + if ($GLOBALS['finished']) { + $i = $len - 1; + } else { + break; + } + } + // Skip * + if ($ch == '/') { + $i++; + } + // Skip last char + $i++; + // We need to send the comment part in case we are defining + // a procedure or function and comments in it are valuable + $sql .= substr( + $buffer, + $start_of_comment, + $i - $start_of_comment + ); + // Next query part will start here + $start_pos = $i; + // Aren't we at the end? + if ($i == $len) { + $i--; + } else { + continue; + } + } + // Change delimiter, if redefined, and skip it + // (don't send to server!) + if (($i + $length_of_delimiter_keyword < $len) + && strtoupper( + substr($buffer, $i, $length_of_delimiter_keyword) + ) == $delimiter_keyword + ) { + // look for EOL on the character immediately after 'DELIMITER ' + // (see previous comment about PHP_EOL) + $new_line_pos = strpos( + $buffer, + "\n", + $i + $length_of_delimiter_keyword + ); + // it might happen that there is no EOL + if (false === $new_line_pos) { + $new_line_pos = $len; + } + $sql_delimiter = substr( + $buffer, + $i + $length_of_delimiter_keyword, + $new_line_pos - $i - $length_of_delimiter_keyword + ); + $i = $new_line_pos + 1; + // Next query part will start here + $start_pos = $i; + continue; + } + + // End of SQL + if ($found_delimiter + || ($GLOBALS['finished'] + && ($i == $len - 1)) + ) { + $tmp_sql = $sql; + if ($start_pos < $len) { + $length_to_grab = $i - $start_pos; + + if (! $found_delimiter) { + $length_to_grab++; + } + $tmp_sql .= substr($buffer, $start_pos, $length_to_grab); + unset($length_to_grab); + } + // Do not try to execute empty SQL + if (! preg_match('/^([\s]*;)*$/', trim($tmp_sql))) { + $sql = $tmp_sql; + PMA_importRunQuery( + $sql, + substr($buffer, 0, $i + strlen($sql_delimiter)), + false, + $sql_data + ); + $buffer = substr($buffer, $i + strlen($sql_delimiter)); + // Reset parser: + $len = strlen($buffer); + $sql = ''; + $i = 0; + $start_pos = 0; + // Any chance we will get a complete query? + //if ((strpos($buffer, ';') === false) + //&& ! $GLOBALS['finished']) { + if (strpos($buffer, $sql_delimiter) === false + && ! $GLOBALS['finished'] + ) { + break; + } + } else { + $i++; + $start_pos = $i; + } + } + } // End of parser loop + } // End of import loop + // Commit any possible data in buffers + PMA_importRunQuery('', substr($buffer, 0, $len), false, $sql_data); + PMA_importRunQuery('', '', false, $sql_data); + } +}
\ No newline at end of file |
