Merge pull request #1360 from Tithugues/b4528_master

Fix 14528: Fix SQL import.
This commit is contained in:
Hugues Peccatte 2014-09-17 19:38:18 +02:00
commit 75be2a0fc4
2 changed files with 75 additions and 67 deletions

View File

@ -297,6 +297,8 @@ class PMA_StringMB implements PMA_StringByte
/**
* Perform a regular expression match
*
* Take care: might not work with lookbehind expressions.
*
* @param string $pattern Pattern to search for
* @param string $subject Input string
* @param int $offset Start from search
@ -307,14 +309,13 @@ class PMA_StringMB implements PMA_StringByte
{
$matches = array();
$bFind = preg_match(
$pattern, $subject, $matches, PREG_OFFSET_CAPTURE, $offset
$pattern, $this->substr($subject, $offset), $matches, PREG_OFFSET_CAPTURE
);
if (1 !== $bFind) {
return false;
}
$strFound = $matches[1][0];
return $this->strpos($subject, $strFound, $offset);
return $matches[1][1] + $offset;
}
}
?>

View File

@ -118,7 +118,7 @@ class ImportSql extends ImportPlugin
// Defaults for parser
$sql = '';
$start_pos = 0;
$i = 0;
$posInQueryString = 0;
$len= 0;
$big_value = 2147483647;
// include the space because it's mandatory
@ -155,7 +155,7 @@ class ImportSql extends ImportPlugin
*/
$GLOBALS['finished'] = false;
while (! ($GLOBALS['finished'] && $i >= $len)
while (! ($GLOBALS['finished'] && $posInQueryString >= $len)
&& ! $error
&& ! $timeout_passed
) {
@ -173,7 +173,7 @@ class ImportSql extends ImportPlugin
unset($data);
// Do not parse string when we're not at the end
// and don't have ; inside
if (($pmaString->strpos($buffer, $sql_delimiter, $i) === false)
if ($pmaString->strpos($buffer, $sql_delimiter, $posInQueryString) === false
&& ! $GLOBALS['finished']
) {
continue;
@ -188,17 +188,17 @@ class ImportSql extends ImportPlugin
$len = $pmaString->strlen($buffer);
// Grab some SQL queries out of it
while ($i < $len) {
while ($posInQueryString < $len) {
$found_delimiter = false;
// Find first interesting character
$old_i = $i;
$old_i = $posInQueryString;
// this is about 7 times faster that looking for each sequence i
// one by one with strpos()
$posPattern = $pmaString->pregStrpos(
'/(\'|"|#|-- |\/\*|`|(?i)(?<![A-Z0-9_])'
. $delimiter_keyword . ')/',
$buffer,
$i
$posInQueryString
);
if (false !== $posPattern) {
// in $matches, index 0 contains the match for the complete
@ -215,7 +215,7 @@ class ImportSql extends ImportPlugin
$first_sql_delimiter = $pmaString->strpos(
$buffer,
$sql_delimiter,
$i
$posInQueryString
);
if ($first_sql_delimiter === false) {
$first_sql_delimiter = $big_value;
@ -225,12 +225,12 @@ class ImportSql extends ImportPlugin
// set $i to the position of the first quote,
// comment.start or delimiter found
$i = min($first_position, $first_sql_delimiter);
$posInQueryString = min($first_position, $first_sql_delimiter);
if ($i == $big_value) {
if ($posInQueryString == $big_value) {
// none of the above was found in the string
$i = $old_i;
$posInQueryString = $old_i;
if (! $GLOBALS['finished']) {
break;
}
@ -241,12 +241,12 @@ class ImportSql extends ImportPlugin
break;
}
// We hit end of query, go there!
$i = $pmaString->strlen($buffer) - 1;
$posInQueryString = $pmaString->strlen($buffer) - 1;
}
// Grab current character
//$ch = $buffer[$i]; //Don't use this syntax, because of UTF8 strings
$ch = $pmaString->substr($buffer, $i, 1);
$ch = $pmaString->substr($buffer, $posInQueryString, 1);
// Quotes
if ($pmaString->strpos('\'"`', $ch) !== false) {
@ -254,7 +254,8 @@ class ImportSql extends ImportPlugin
$endq = false;
while (! $endq) {
// Find next quote
$pos = $pmaString->strpos($buffer, $quote, $i + 1);
$posQuote = $pmaString
->strpos($buffer, $quote, $posInQueryString + 1);
/*
* Behave same as MySQL and accept end of query as end
* of backtick.
@ -266,48 +267,50 @@ class ImportSql extends ImportPlugin
*
* SELECT * FROM `table`
*/
if ($pos === false && $quote == '`' && $found_delimiter) {
$pos = $first_sql_delimiter - 1;
} elseif ($pos === false) { // No quote? Too short string
if ($posQuote === false && $quote == '`'
&& $found_delimiter
) {
$posQuote = $first_sql_delimiter - 1;
} elseif ($posQuote === false) {// No quote? Too short string
// We hit end of string => unclosed quote,
// but we handle it as end of query
list($endq, $i)
= $this->getEndQuoteAndPos($len, $endq, $i);
list($endq, $posInQueryString) = $this
->getEndQuoteAndPos($len, $endq, $posInQueryString);
$found_delimiter = false;
break;
}
// Was not the quote escaped?
$j = $pos - 1;
while ($pmaString->substr($buffer, $j, 1) == '\\') {
$j--;
$posEscape = $posQuote - 1;
while ($pmaString->substr($buffer, $posEscape, 1) == '\\') {
$posEscape--;
}
// Even count means it was not escaped
$endq = (((($pos - 1) - $j) % 2) == 0);
$endq = (((($posQuote - 1) - $posEscape) % 2) == 0);
// Skip the string
$i = $pos;
$posInQueryString = $posQuote;
if ($first_sql_delimiter < $pos) {
if ($first_sql_delimiter < $posQuote) {
$found_delimiter = false;
}
}
if (! $endq) {
break;
}
$i++;
$posInQueryString++;
// Aren't we at the end?
if ($GLOBALS['finished'] && $i == $len) {
$i--;
if ($GLOBALS['finished'] && $posInQueryString == $len) {
$posInQueryString--;
} else {
continue;
}
}
// Not enough data to decide
if ((($i == ($len - 1) && ($ch == '-' || $ch == '/'))
|| ($i == ($len - 2) && (($ch == '-'
&& $pmaString->substr($buffer, $i + 1, 1) == '-')
if ((($posInQueryString == ($len - 1) && ($ch == '-' || $ch == '/'))
|| ($posInQueryString == ($len - 2) && (($ch == '-'
&& $pmaString->substr($buffer, $posInQueryString + 1, 1) == '-')
|| ($ch == '/'
&& $pmaString->substr($buffer, $i + 1, 1) == '*'))))
&& $pmaString->substr($buffer, $posInQueryString + 1, 1) == '*'))))
&& ! $GLOBALS['finished']
) {
break;
@ -315,62 +318,66 @@ class ImportSql extends ImportPlugin
// Comments
if ($ch == '#'
|| ($i < ($len - 1) && $ch == '-'
&& $pmaString->substr($buffer, $i + 1, 1) == '-'
&& (($i < ($len - 2)
&& $pmaString->substr($buffer, $i + 2, 1) <= ' ')
|| ($i == ($len - 1) && $GLOBALS['finished'])))
|| ($i < ($len - 1) && $ch == '/'
&& $pmaString->substr($buffer, $i + 1, 1) == '*')
|| ($posInQueryString < ($len - 1) && $ch == '-'
&& $pmaString->substr($buffer, $posInQueryString + 1, 1) == '-'
&& (($posInQueryString < ($len - 2)
&& $pmaString->substr($buffer, $posInQueryString + 2, 1) <= ' ')
|| ($posInQueryString == ($len - 1) && $GLOBALS['finished'])))
|| ($posInQueryString < ($len - 1) && $ch == '/'
&& $pmaString->substr($buffer, $posInQueryString + 1, 1) == '*')
) {
// Copy current string to SQL
if ($start_pos != $i) {
if ($start_pos != $posInQueryString) {
$sql .= $pmaString->substr(
$buffer,
$start_pos,
$i - $start_pos
$posInQueryString - $start_pos
);
}
// Skip the rest
$start_of_comment = $i;
$start_of_comment = $posInQueryString;
// do not use PHP_EOL here instead of "\n", because the export
// file might have been produced on a different system
$i = $pmaString->strpos($buffer, $ch == '/' ? '*/' : "\n", $i);
$posInQueryString = $pmaString->strpos(
$buffer,
$ch == '/' ? '*/' : "\n",
$posInQueryString
);
// didn't we hit end of string?
if ($i === false) {
if ($posInQueryString === false) {
if ($GLOBALS['finished']) {
$i = $len - 1;
$posInQueryString = $len - 1;
} else {
break;
}
}
// Skip *
if ($ch == '/') {
$i++;
$posInQueryString++;
}
// Skip last char
$i++;
$posInQueryString++;
// We need to send the comment part in case we are defining
// a procedure or function and comments in it are valuable
$sql .= $pmaString->substr(
$buffer,
$start_of_comment,
$i - $start_of_comment
$posInQueryString - $start_of_comment
);
// Next query part will start here
$start_pos = $i;
$start_pos = $posInQueryString;
// Aren't we at the end?
if ($i == $len) {
$i--;
if ($posInQueryString == $len) {
$posInQueryString--;
} else {
continue;
}
}
// Change delimiter, if redefined, and skip it
// (don't send to server!)
if (($i + $length_of_delimiter_keyword < $len)
if (($posInQueryString + $length_of_delimiter_keyword < $len)
&& $pmaString->strtoupper(
$pmaString->substr($buffer, $i, $length_of_delimiter_keyword)
$pmaString->substr($buffer, $posInQueryString, $length_of_delimiter_keyword)
) == $delimiter_keyword
) {
// look for EOL on the character immediately after 'DELIMITER '
@ -378,7 +385,7 @@ class ImportSql extends ImportPlugin
$new_line_pos = $pmaString->strpos(
$buffer,
"\n",
$i + $length_of_delimiter_keyword
$posInQueryString + $length_of_delimiter_keyword
);
// it might happen that there is no EOL
if (false === $new_line_pos) {
@ -386,23 +393,23 @@ class ImportSql extends ImportPlugin
}
$sql_delimiter = $pmaString->substr(
$buffer,
$i + $length_of_delimiter_keyword,
$new_line_pos - $i - $length_of_delimiter_keyword
$posInQueryString + $length_of_delimiter_keyword,
$new_line_pos - $posInQueryString - $length_of_delimiter_keyword
);
$i = $new_line_pos + 1;
$posInQueryString = $new_line_pos + 1;
// Next query part will start here
$start_pos = $i;
$start_pos = $posInQueryString;
continue;
}
// End of SQL
if ($found_delimiter
|| ($GLOBALS['finished']
&& ($i == $len - 1))
&& ($posInQueryString == $len - 1))
) {
$tmp_sql = $sql;
if ($start_pos < $len) {
$length_to_grab = $i - $start_pos;
$length_to_grab = $posInQueryString - $start_pos;
if (! $found_delimiter) {
$length_to_grab++;
@ -422,19 +429,19 @@ class ImportSql extends ImportPlugin
$pmaString->substr(
$buffer,
0,
$i + $pmaString->strlen($sql_delimiter)
$posInQueryString + $pmaString->strlen($sql_delimiter)
),
false,
$sql_data
);
$buffer = $pmaString->substr(
$buffer,
$i + $pmaString->strlen($sql_delimiter)
$posInQueryString + $pmaString->strlen($sql_delimiter)
);
// Reset parser:
$len = $pmaString->strlen($buffer);
$sql = '';
$i = 0;
$posInQueryString = 0;
$start_pos = 0;
// Any chance we will get a complete query?
//if ((strpos($buffer, ';') === false)
@ -445,8 +452,8 @@ class ImportSql extends ImportPlugin
break;
}
} else {
$i++;
$start_pos = $i;
$posInQueryString++;
$start_pos = $posInQueryString;
}
}
} // End of parser loop