123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- <?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);
- }
- }
|