/** * Parses the SQL queries * * @param string The SQL query list * * @return mixed Most of times, nothing... * * @global array The current PMA configuration * @global array MySQL column attributes * @global array MySQL reserved words * @global array MySQL column types * @global array MySQL function names * @global integer MySQL column attributes count * @global integer MySQL reserved words count * @global integer MySQL column types count * @global integer MySQL function names count * @global array List of available character sets * @global array List of available collations * @global integer Character sets count * @global integer Collations count * * @access public */ function PMA_SQP_parse($sql) { global $cfg; global $PMA_SQPdata_column_attrib, $PMA_SQPdata_reserved_word, $PMA_SQPdata_column_type, $PMA_SQPdata_function_name, $PMA_SQPdata_column_attrib_cnt, $PMA_SQPdata_reserved_word_cnt, $PMA_SQPdata_column_type_cnt, $PMA_SQPdata_function_name_cnt; global $mysql_charsets, $mysql_collations_flat, $mysql_charsets_count, $mysql_collations_count; global $PMA_SQPdata_forbidden_word, $PMA_SQPdata_forbidden_word_cnt; // rabus: Convert all line feeds to Unix style $sql = str_replace("\r\n", "\n", $sql); $sql = str_replace("\r", "\n", $sql); $len = PMA_strlen($sql); if ($len == 0) { return array(); } $sql_array = array(); $sql_array['raw'] = $sql; $count1 = 0; $count2 = 0; $punct_queryend = ';'; $punct_qualifier = '.'; $punct_listsep = ','; $punct_level_plus = '('; $punct_level_minus = ')'; $digit_floatdecimal = '.'; $digit_hexset = 'x'; $bracket_list = '()[]{}'; $allpunct_list = '-,;:!?/.^~\\*&%+<=>|'; $allpunct_list_pair = array(0 => '!=', 1 => '&&', 2 => ':=', 3 => '<<', 4 => '<=', 5 => '<=>', 6 => '<>', 7 => '>=', 8 => '>>', 9 => '||'); $allpunct_list_pair_size = 10; //count($allpunct_list_pair); $quote_list = '\'"`'; $arraysize = 0; while ($count2 < $len) { $c = PMA_substr($sql, $count2, 1); $count1 = $count2; if ($c == "\n") { $count2++; PMA_SQP_arrayAdd($sql_array, 'white_newline', '', $arraysize); continue; } // Checks for white space if (PMA_STR_isSpace($c)) { $count2++; continue; } // Checks for comment lines. // MySQL style # // C style /* */ // ANSI style -- if ($c == '#' || $count2 + 1 < $len && $c == '/' && PMA_substr($sql, $count2 + 1, 1) == '*' || $count2 + 2 == $len && $c == '-' && PMA_substr($sql, $count2 + 1, 1) == '-' || $count2 + 2 < $len && $c == '-' && PMA_substr($sql, $count2 + 1, 1) == '-' && PMA_substr($sql, $count2 + 2, 1) <= ' ') { $count2++; $pos = 0; $type = 'bad'; switch ($c) { case '#': $type = 'mysql'; case '-': $type = 'ansi'; $pos = $GLOBALS['PMA_strpos']($sql, "\n", $count2); break; case '/': $type = 'c'; $pos = $GLOBALS['PMA_strpos']($sql, '*/', $count2); $pos += 2; break; default: break; } // end switch $count2 = $pos < $count2 ? $len : $pos; $str = PMA_substr($sql, $count1, $count2 - $count1); PMA_SQP_arrayAdd($sql_array, 'comment_' . $type, $str, $arraysize); continue; } // end if // Checks for something inside quotation marks if (PMA_STR_strInStr($c, $quote_list)) { $startquotepos = $count2; $quotetype = $c; $count2++; $escaped = FALSE; $escaped_escaped = FALSE; $pos = $count2; $oldpos = 0; do { $oldpos = $pos; $pos = $GLOBALS['PMA_strpos'](' ' . $sql, $quotetype, $oldpos + 1) - 1; // ($pos === FALSE) if ($pos < 0) { $debugstr = $GLOBALS['strSQPBugUnclosedQuote'] . ' @ ' . $startquotepos . "\n" . 'STR: ' . htmlspecialchars($quotetype); PMA_SQP_throwError($debugstr, $sql); return $sql; } // If the quote is the first character, it can't be // escaped, so don't do the rest of the code if ($pos == 0) { break; } // Checks for MySQL escaping using a \ // And checks for ANSI escaping using the $quotetype character if ($pos < $len && PMA_STR_charIsEscaped($sql, $pos)) { $pos++; continue; } else { if ($pos + 1 < $len && PMA_substr($sql, $pos, 1) == $quotetype && PMA_substr($sql, $pos + 1, 1) == $quotetype) { $pos = $pos + 2; continue; } else { break; } } } while ($len > $pos); // end do $count2 = $pos; $count2++; $type = 'quote_'; switch ($quotetype) { case '\'': $type .= 'single'; break; case '"': $type .= 'double'; break; case '`': $type .= 'backtick'; break; default: break; } // end switch $data = PMA_substr($sql, $count1, $count2 - $count1); PMA_SQP_arrayAdd($sql_array, $type, $data, $arraysize); continue; } // Checks for brackets if (PMA_STR_strInStr($c, $bracket_list)) { // All bracket tokens are only one item long $count2++; $type_type = ''; if (PMA_STR_strInStr($c, '([{')) { $type_type = 'open'; } else { $type_type = 'close'; } $type_style = ''; if (PMA_STR_strInStr($c, '()')) { $type_style = 'round'; } elseif (PMA_STR_strInStr($c, '[]')) { $type_style = 'square'; } else { $type_style = 'curly'; } $type = 'punct_bracket_' . $type_type . '_' . $type_style; PMA_SQP_arrayAdd($sql_array, $type, $c, $arraysize); continue; } // Checks for identifier (alpha or numeric) if (PMA_STR_isSqlIdentifier($c, FALSE) || $c == '@' || $c == '.' && PMA_STR_isDigit(PMA_substr($sql, $count2 + 1, 1))) { $count2++; //TODO: a @ can also be present in expressions like // FROM 'user'@'%' // or TO 'user'@'%' // in this case, the @ is wrongly marked as alpha_variable $is_sql_variable = $c == '@'; $is_digit = !$is_sql_variable && PMA_STR_isDigit($c); $is_hex_digit = $is_digit && $c == '.' && $c == '0' && $count2 < $len && PMA_substr($sql, $count2, 1) == 'x'; $is_float_digit = $c == '.'; $is_float_digit_exponent = FALSE; // Nijel: Fast skip is especially needed for huge BLOB data, requires PHP at least 4.3.0: if (PMA_PHP_INT_VERSION >= 40300) { if ($is_hex_digit) { $count2++; $pos = strspn($sql, '0123456789abcdefABCDEF', $count2); if ($pos > $count2) { $count2 = $pos; } unset($pos); } elseif ($is_digit) { $pos = strspn($sql, '0123456789', $count2); if ($pos > $count2) { $count2 = $pos; } unset($pos); } } while ($count2 < $len && PMA_STR_isSqlIdentifier(PMA_substr($sql, $count2, 1), $is_sql_variable || $is_digit)) { $c2 = PMA_substr($sql, $count2, 1); if ($is_sql_variable && $c2 == '.') { $count2++; continue; } if ($is_digit && !$is_hex_digit && $c2 == '.') { $count2++; if (!$is_float_digit) { $is_float_digit = TRUE; continue; } else { $debugstr = $GLOBALS['strSQPBugInvalidIdentifer'] . ' @ ' . ($count1 + 1) . "\n" . 'STR: ' . htmlspecialchars(PMA_substr($sql, $count1, $count2 - $count1)); PMA_SQP_throwError($debugstr, $sql); return $sql; } } if ($is_digit && !$is_hex_digit && ($c2 == 'e' || $c2 == 'E')) { if (!$is_float_digit_exponent) { $is_float_digit_exponent = TRUE; $is_float_digit = TRUE; $count2++; continue; } else { $is_digit = FALSE; $is_float_digit = FALSE; } } if ($is_hex_digit && PMA_STR_isHexDigit($c2) || $is_digit && PMA_STR_isDigit($c2)) { $count2++; continue; } else { $is_digit = FALSE; $is_hex_digit = FALSE; } $count2++; } // end while $l = $count2 - $count1; $str = PMA_substr($sql, $count1, $l); $type = ''; if ($is_digit) { $type = 'digit'; if ($is_float_digit) { $type .= '_float'; } else { if ($is_hex_digit) { $type .= '_hex'; } else { $type .= '_integer'; } } } else { if ($is_sql_variable != FALSE) { $type = 'alpha_variable'; } else { $type = 'alpha'; } } // end if... else.... PMA_SQP_arrayAdd($sql_array, $type, $str, $arraysize); continue; } // Checks for punct if (PMA_STR_strInStr($c, $allpunct_list)) { while ($count2 < $len && PMA_STR_strInStr(PMA_substr($sql, $count2, 1), $allpunct_list)) { $count2++; } $l = $count2 - $count1; if ($l == 1) { $punct_data = $c; } else { $punct_data = PMA_substr($sql, $count1, $l); } // Special case, sometimes, althought two characters are // adjectent directly, they ACTUALLY need to be seperate if ($l == 1) { $t_suffix = ''; switch ($punct_data) { case $punct_queryend: $t_suffix = '_queryend'; break; case $punct_qualifier: $t_suffix = '_qualifier'; break; case $punct_listsep: $t_suffix = '_listsep'; break; default: break; } PMA_SQP_arrayAdd($sql_array, 'punct' . $t_suffix, $punct_data, $arraysize); } else { if (PMA_STR_binarySearchInArr($punct_data, $allpunct_list_pair, $allpunct_list_pair_size)) { // Ok, we have one of the valid combined punct expressions PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize); } else { // Bad luck, lets split it up more $first = $punct_data[0]; $first2 = $punct_data[0] . $punct_data[1]; $last2 = $punct_data[$l - 2] . $punct_data[$l - 1]; $last = $punct_data[$l - 1]; if ($first == ',' || $first == ';' || $first == '.' || $first == '*') { $count2 = $count1 + 1; $punct_data = $first; } else { if ($last2 == '/*' || $last2 == '--' && ($count2 == $len || PMA_substr($sql, $count2, 1) <= ' ')) { $count2 -= 2; $punct_data = PMA_substr($sql, $count1, $count2 - $count1); } else { if ($last == '-' || $last == '+' || $last == '!') { $count2--; $punct_data = PMA_substr($sql, $count1, $count2 - $count1); // TODO: for negation operator, split in 2 tokens ? // "select x&~1 from t" // becomes "select x & ~ 1 from t" ? } else { if ($last != '~') { $debugstr = $GLOBALS['strSQPBugUnknownPunctuation'] . ' @ ' . ($count1 + 1) . "\n" . 'STR: ' . htmlspecialchars($punct_data); PMA_SQP_throwError($debugstr, $sql); return $sql; } } } } PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize); continue; } } // end if... else if... else continue; } // DEBUG $count2++; $debugstr = 'C1 C2 LEN: ' . $count1 . ' ' . $count2 . ' ' . $len . "\n" . 'STR: ' . PMA_substr($sql, $count1, $count2 - $count1) . "\n"; PMA_SQP_bug($debugstr, $sql); return $sql; } // end while ($count2 < $len) if ($arraysize > 0) { $t_next = $sql_array[0]['type']; $t_prev = ''; $t_bef_prev = ''; $t_cur = ''; $d_next = $sql_array[0]['data']; $d_prev = ''; $d_bef_prev = ''; $d_cur = ''; $d_next_upper = $t_next == 'alpha' ? strtoupper($d_next) : $d_next; $d_prev_upper = ''; $d_bef_prev_upper = ''; $d_cur_upper = ''; } for ($i = 0; $i < $arraysize; $i++) { $t_bef_prev = $t_prev; $t_prev = $t_cur; $t_cur = $t_next; $d_bef_prev = $d_prev; $d_prev = $d_cur; $d_cur = $d_next; $d_bef_prev_upper = $d_prev_upper; $d_prev_upper = $d_cur_upper; $d_cur_upper = $d_next_upper; if ($i + 1 < $arraysize) { $t_next = $sql_array[$i + 1]['type']; $d_next = $sql_array[$i + 1]['data']; $d_next_upper = $t_next == 'alpha' ? strtoupper($d_next) : $d_next; } else { $t_next = ''; $d_next = ''; $d_next_upper = ''; } //DEBUG echo "[prev: <b>".$d_prev."</b> ".$t_prev."][cur: <b>".$d_cur."</b> ".$t_cur."][next: <b>".$d_next."</b> ".$t_next."]<br />"; if ($t_cur == 'alpha') { $t_suffix = '_identifier'; if ($t_next == 'punct_qualifier' || $t_prev == 'punct_qualifier') { $t_suffix = '_identifier'; } else { if ($t_next == 'punct_bracket_open_round' && PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_function_name, $PMA_SQPdata_function_name_cnt)) { // FIXME-2005-10-16: in the case of a CREATE TABLE containing a TIMESTAMP, // since TIMESTAMP() is also a function, it's found here and // the token is wrongly marked as alpha_functionName. But we // compensate for this when analysing for timestamp_not_null // later in this script. $t_suffix = '_functionName'; } else { if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_type, $PMA_SQPdata_column_type_cnt)) { $t_suffix = '_columnType'; // Temporary fix for BUG #621357 //TODO FIX PROPERLY NEEDS OVERHAUL OF SQL TOKENIZER if ($d_cur_upper == 'SET' && $t_next != 'punct_bracket_open_round') { $t_suffix = '_reservedWord'; } //END OF TEMPORARY FIX // CHARACTER is a synonym for CHAR, but can also be meant as // CHARACTER SET. In this case, we have a reserved word. if ($d_cur_upper == 'CHARACTER' && $d_next_upper == 'SET') { $t_suffix = '_reservedWord'; } // experimental // current is a column type, so previous must not be // a reserved word but an identifier // CREATE TABLE SG_Persons (first varchar(64)) //if ($sql_array[$i-1]['type'] =='alpha_reservedWord') { // $sql_array[$i-1]['type'] = 'alpha_identifier'; //} } else { if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_reserved_word, $PMA_SQPdata_reserved_word_cnt)) { $t_suffix = '_reservedWord'; } else { if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_attrib, $PMA_SQPdata_column_attrib_cnt)) { $t_suffix = '_columnAttrib'; // INNODB is a MySQL table type, but in "SHOW INNODB STATUS", // it should be regarded as a reserved word. if ($d_cur_upper == 'INNODB' && $d_prev_upper == 'SHOW' && $d_next_upper == 'STATUS') { $t_suffix = '_reservedWord'; } if ($d_cur_upper == 'DEFAULT' && $d_next_upper == 'CHARACTER') { $t_suffix = '_reservedWord'; } // Binary as character set if ($d_cur_upper == 'BINARY' && ($d_bef_prev_upper == 'CHARACTER' && $d_prev_upper == 'SET' || $d_bef_prev_upper == 'SET' && $d_prev_upper == '=' || $d_bef_prev_upper == 'CHARSET' && $d_prev_upper == '=' || $d_prev_upper == 'CHARSET') && PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, count($mysql_charsets))) { $t_suffix = '_charset'; } } elseif (PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, $mysql_charsets_count) || PMA_STR_binarySearchInArr($d_cur, $mysql_collations_flat, $mysql_collations_count) || $d_cur[0] == '_' && PMA_STR_binarySearchInArr(substr($d_cur, 1), $mysql_charsets, $mysql_charsets_count)) { $t_suffix = '_charset'; } else { // Do nothing } } } } } // check if present in the list of forbidden words if ($t_suffix == '_reservedWord' && PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_forbidden_word, $PMA_SQPdata_forbidden_word_cnt)) { $sql_array[$i]['forbidden'] = TRUE; } else { $sql_array[$i]['forbidden'] = FALSE; } $sql_array[$i]['type'] .= $t_suffix; } } // end for // Stores the size of the array inside the array, as count() is a slow // operation. $sql_array['len'] = $arraysize; // Sends the data back return $sql_array; }
/** * Parses the SQL queries * * @param string $sql The SQL query list * * @return mixed Most of times, nothing... * * @global array The current PMA configuration * @global array MySQL column attributes * @global array MySQL reserved words * @global array MySQL column types * @global array MySQL function names * @global array List of available character sets * @global array List of available collations * * @access public */ function PMA_SQP_parse($sql) { static $PMA_SQPdata_column_attrib, $PMA_SQPdata_reserved_word; static $PMA_SQPdata_column_type; static $PMA_SQPdata_function_name, $PMA_SQPdata_forbidden_word; global $mysql_charsets, $mysql_collations_flat; /* @var $pmaString PMA_String */ $pmaString = $GLOBALS['PMA_String']; // Convert all line feeds to Unix style $sql = str_replace("\r\n", "\n", $sql); $sql = str_replace("\r", "\n", $sql); $len = mb_strlen($sql); if ($len == 0) { return array(); } // Create local hashtables if (!isset($PMA_SQPdata_column_attrib)) { $PMA_SQPdata_column_attrib = array_flip($GLOBALS['PMA_SQPdata_column_attrib']); $PMA_SQPdata_function_name = array_flip($GLOBALS['PMA_SQPdata_function_name']); $PMA_SQPdata_reserved_word = array_flip($GLOBALS['PMA_SQPdata_reserved_word']); $PMA_SQPdata_forbidden_word = array_flip($GLOBALS['PMA_SQPdata_forbidden_word']); $PMA_SQPdata_column_type = array_flip($GLOBALS['PMA_SQPdata_column_type']); } $sql_array = array(); $sql_array['raw'] = $sql; $count2 = 0; $punct_queryend = ';'; $punct_qualifier = '.'; $punct_listsep = ','; $bracket_list = '()[]{}'; $allpunct_list = '-,;:!?/.^~\\*&%+<=>|'; $allpunct_list_pair = array('!=' => 1, '&&' => 1, ':=' => 1, '<<' => 1, '<=' => 1, '<=>' => 1, '<>' => 1, '>=' => 1, '>>' => 1, '||' => 1, '==' => 1); $quote_list = '\'"`'; $arraysize = 0; $this_was_space = false; $this_was_bracket = false; $this_was_punct = false; $this_was_listsep = false; $this_was_quote = false; while ($count2 < $len) { $c = mb_substr($sql, $count2, 1); $count1 = $count2; $previous_was_space = $this_was_space; $this_was_space = false; $previous_was_bracket = $this_was_bracket; $this_was_bracket = false; $previous_was_punct = $this_was_punct; $this_was_punct = false; $previous_was_listsep = $this_was_listsep; $this_was_listsep = false; $previous_was_quote = $this_was_quote; $this_was_quote = false; if ($c === "\n") { $this_was_space = true; $count2++; PMA_SQP_arrayAdd($sql_array, 'white_newline', "\n", $arraysize, $count2); continue; } // Checks for white space if ($pmaString->isSpace($c)) { $this_was_space = true; $count2++; continue; } // Checks for comment lines. // MySQL style # // C style /* */ // ANSI style -- $next_c = mb_substr($sql, $count2 + 1, 1); if ($c == '#' || $count2 + 1 < $len && $c == '/' && $next_c == '*' || $count2 + 2 == $len && $c == '-' && $next_c == '-' || $count2 + 2 < $len && $c == '-' && $next_c == '-' && mb_substr($sql, $count2 + 2, 1) <= ' ') { $count2++; $pos = 0; $type = 'bad'; switch ($c) { case '#': $type = 'mysql'; $pos = mb_strpos($sql, "\n", $count2); break; case '-': $type = 'ansi'; $pos = mb_strpos($sql, "\n", $count2); break; case '/': $type = 'c'; $pos = mb_strpos($sql, '*/', $count2); $pos += 2; break; default: break; } // end switch $count2 = $pos < $count2 ? $len : $pos; $str = mb_substr($sql, $count1, $count2 - $count1); PMA_SQP_arrayAdd($sql_array, 'comment_' . $type, $str, $arraysize, $count2); continue; } // end if // Checks for something inside quotation marks if (mb_strpos($quote_list, $c) !== false) { $startquotepos = $count2; $quotetype = $c; $count2++; $pos = $count2; do { $oldpos = $pos; $pos = mb_strpos(' ' . $sql, $quotetype, $oldpos + 1) - 1; // ($pos === false) if ($pos < 0) { if ($c != '`') { $debugstr = __('Unclosed quote') . ' @ ' . $startquotepos . "\n" . 'STR: ' . htmlspecialchars($quotetype); PMA_SQP_throwError($debugstr, $sql); return $sql_array; } /* * 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` */ $pos_quote_separator = mb_strpos(' ' . $sql, $GLOBALS['sql_delimiter'], $oldpos + 1) - 1; if ($pos_quote_separator < 0) { $len += 1; $sql .= '`'; $sql_array['raw'] .= '`'; $pos = $len; } else { $len += 1; $sql = mb_substr($sql, 0, $pos_quote_separator) . '`' . mb_substr($sql, $pos_quote_separator); $sql_array['raw'] = $sql; $pos = $pos_quote_separator; } if (class_exists('PMA_Message') && $GLOBALS['is_ajax_request'] != true) { PMA_Message::notice(__('Automatically appended backtick to the end of query!'))->display(); } } // If the quote is the first character, it can't be // escaped, so don't do the rest of the code if ($pos == 0) { break; } // Checks for MySQL escaping using a \ // And checks for ANSI escaping using the $quotetype character if ($pos < $len && $pmaString->charIsEscaped($sql, $pos) && $c != '`') { $pos++; continue; } elseif ($pos + 1 < $len && mb_substr($sql, $pos, 1) == $quotetype && mb_substr($sql, $pos + 1, 1) == $quotetype) { $pos = $pos + 2; continue; } else { break; } } while ($len > $pos); // end do $count2 = $pos; $count2++; $type = 'quote_'; switch ($quotetype) { case '\'': $type .= 'single'; $this_was_quote = true; break; case '"': $type .= 'double'; $this_was_quote = true; break; case '`': $type .= 'backtick'; $this_was_quote = true; break; default: break; } // end switch $data = mb_substr($sql, $count1, $count2 - $count1); PMA_SQP_arrayAdd($sql_array, $type, $data, $arraysize, $count2); continue; } // Checks for brackets if (mb_strpos($bracket_list, $c) !== false) { // All bracket tokens are only one item long $this_was_bracket = true; $count2++; if (mb_strpos('([{', $c) !== false) { $type_type = 'open'; } else { $type_type = 'close'; } if (mb_strpos('()', $c) !== false) { $type_style = 'round'; } elseif (mb_strpos('[]', $c) !== false) { $type_style = 'square'; } else { $type_style = 'curly'; } $type = 'punct_bracket_' . $type_type . '_' . $type_style; PMA_SQP_arrayAdd($sql_array, $type, $c, $arraysize, $count2); continue; } /* DEBUG echo '<pre>1'; var_dump($pmaString->isSqlIdentifier($c, false)); var_dump($c == '@'); var_dump($c == '.'); var_dump( $pmaString->isDigit( $pmaString->substr($sql, $count2 + 1, 1) ) ); var_dump($previous_was_space); var_dump($previous_was_bracket); var_dump($previous_was_listsep); echo '</pre>'; */ // Checks for identifier (alpha or numeric) if ($pmaString->isSqlIdentifier($c, false) || $c == '@' || $c == '.' && $pmaString->isDigit(mb_substr($sql, $count2 + 1, 1)) && ($previous_was_space || $previous_was_bracket || $previous_was_listsep)) { /* DEBUG echo $pmaString->substr($sql, $count2); echo '<hr />'; */ $count2++; /** * @todo a @ can also be present in expressions like * FROM 'user'@'%' or TO 'user'@'%' * in this case, the @ is wrongly marked as alpha_variable */ $is_identifier = $previous_was_punct; $is_sql_variable = $c == '@' && !$previous_was_quote; $is_user = $c == '@' && $previous_was_quote; $is_digit = !$is_identifier && !$is_sql_variable && $pmaString->isDigit($c); $is_hex_digit = $is_digit && $c == '0' && $count2 < $len && mb_substr($sql, $count2, 1) == 'x'; $is_float_digit = $c == '.'; $is_float_digit_exponent = false; /* DEBUG echo '<pre>2'; var_dump($is_identifier); var_dump($is_sql_variable); var_dump($is_digit); var_dump($is_float_digit); echo '</pre>'; */ // Fast skip is especially needed for huge BLOB data if ($is_hex_digit) { $count2++; $pos = strspn($sql, '0123456789abcdefABCDEF', $count2); if ($pos > $count2) { $count2 = $pos; } unset($pos); } elseif ($is_digit) { $pos = strspn($sql, '0123456789', $count2); if ($pos > $count2) { $count2 = $pos; } unset($pos); } while ($count2 < $len && $pmaString->isSqlIdentifier(mb_substr($sql, $count2, 1), $is_sql_variable || $is_digit)) { $c2 = mb_substr($sql, $count2, 1); if ($is_sql_variable && $c2 == '.') { $count2++; continue; } if ($is_digit && !$is_hex_digit && $c2 == '.') { $count2++; if (!$is_float_digit) { $is_float_digit = true; continue; } else { $debugstr = __('Invalid Identifer') . ' @ ' . ($count1 + 1) . "\n" . 'STR: ' . htmlspecialchars(mb_substr($sql, $count1, $count2 - $count1)); PMA_SQP_throwError($debugstr, $sql); return $sql_array; } } if ($is_digit && !$is_hex_digit && ($c2 == 'e' || $c2 == 'E')) { if (!$is_float_digit_exponent) { $is_float_digit_exponent = true; $is_float_digit = true; $count2++; continue; } else { $is_digit = false; $is_float_digit = false; } } if ($is_hex_digit && $pmaString->isHexDigit($c2) || $is_digit && $pmaString->isDigit($c2)) { $count2++; continue; } else { $is_digit = false; $is_hex_digit = false; } $count2++; } // end while $l = $count2 - $count1; $str = mb_substr($sql, $count1, $l); if ($is_digit || $is_float_digit || $is_hex_digit) { $type = 'digit'; if ($is_float_digit) { $type .= '_float'; } elseif ($is_hex_digit) { $type .= '_hex'; } else { $type .= '_integer'; } } elseif ($is_user) { $type = 'punct_user'; } elseif ($is_sql_variable != false) { $type = 'alpha_variable'; } else { $type = 'alpha'; } // end if... else.... PMA_SQP_arrayAdd($sql_array, $type, $str, $arraysize, $count2); continue; } // Checks for punct if (mb_strpos($allpunct_list, $c) !== false) { while ($count2 < $len && mb_strpos($allpunct_list, mb_substr($sql, $count2, 1)) !== false) { $count2++; } $l = $count2 - $count1; if ($l == 1) { $punct_data = $c; } else { $punct_data = mb_substr($sql, $count1, $l); } // Special case, sometimes, although two characters are // adjacent directly, they ACTUALLY need to be separate /* DEBUG echo '<pre>'; var_dump($l); var_dump($punct_data); echo '</pre>'; */ if ($l == 1) { $t_suffix = ''; switch ($punct_data) { case $punct_queryend: $t_suffix = '_queryend'; break; case $punct_qualifier: $t_suffix = '_qualifier'; $this_was_punct = true; break; case $punct_listsep: $this_was_listsep = true; $t_suffix = '_listsep'; break; default: break; } PMA_SQP_arrayAdd($sql_array, 'punct' . $t_suffix, $punct_data, $arraysize, $count2); } elseif ($punct_data == $GLOBALS['sql_delimiter'] || isset($allpunct_list_pair[$punct_data])) { // Ok, we have one of the valid combined punct expressions PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize, $count2); } else { // Bad luck, lets split it up more $first = $punct_data[0]; $last2 = $punct_data[$l - 2] . $punct_data[$l - 1]; $last = $punct_data[$l - 1]; if ($first == ',' || $first == ';' || $first == '.' || $first == '*') { $count2 = $count1 + 1; $punct_data = $first; } elseif ($last2 == '/*' || $last2 == '--' && ($count2 == $len || mb_substr($sql, $count2, 1) <= ' ')) { $count2 -= 2; $punct_data = mb_substr($sql, $count1, $count2 - $count1); } elseif ($last == '-' || $last == '+' || $last == '!') { $count2--; $punct_data = mb_substr($sql, $count1, $count2 - $count1); } elseif ($last != '~') { /** * @todo for negation operator, split in 2 tokens ? * "select x&~1 from t" * becomes "select x & ~ 1 from t" ? */ $debugstr = __('Unknown Punctuation String') . ' @ ' . ($count1 + 1) . "\n" . 'STR: ' . htmlspecialchars($punct_data); PMA_SQP_throwError($debugstr, $sql); return $sql_array; } PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize, $count2); continue; } // end if... elseif... else continue; } // DEBUG $count2++; $debugstr = 'C1 C2 LEN: ' . $count1 . ' ' . $count2 . ' ' . $len . "\n" . 'STR: ' . mb_substr($sql, $count1, $count2 - $count1) . "\n"; PMA_SQP_bug($debugstr, $sql); return $sql_array; } // end while ($count2 < $len) /* echo '<pre>'; print_r($sql_array); echo '</pre>'; */ if ($arraysize > 0) { $t_next = $sql_array[0]['type']; $t_prev = ''; $t_cur = ''; $d_next = $sql_array[0]['data']; $d_prev = ''; $d_cur = ''; $d_next_upper = $t_next == 'alpha' ? mb_strtoupper($d_next) : $d_next; $d_prev_upper = ''; $d_cur_upper = ''; } for ($i = 0; $i < $arraysize; $i++) { $t_prev = $t_cur; $t_cur = $t_next; $d_prev = $d_cur; $d_cur = $d_next; $d_bef_prev_upper = $d_prev_upper; $d_prev_upper = $d_cur_upper; $d_cur_upper = $d_next_upper; if ($i + 1 < $arraysize) { $t_next = $sql_array[$i + 1]['type']; $d_next = $sql_array[$i + 1]['data']; $d_next_upper = $t_next == 'alpha' ? mb_strtoupper($d_next) : $d_next; } else { $t_next = ''; $d_next = ''; $d_next_upper = ''; } /* DEBUG echo "[prev: <strong>".$d_prev."</strong> ".$t_prev."][cur: <strong>" . $d_cur."</strong> ".$t_cur."][next: <strong>".$d_next."</strong> " . $t_next."]<br />"; */ if ($t_cur == 'alpha') { $t_suffix = '_identifier'; // for example: `thebit` bit(8) NOT NULL DEFAULT b'0' if ($t_prev == 'alpha' && $d_prev == 'DEFAULT' && $d_cur == 'b' && $t_next == 'quote_single') { $t_suffix = '_bitfield_constant_introducer'; } elseif ($t_next == 'punct_qualifier' || $t_prev == 'punct_qualifier') { $t_suffix = '_identifier'; } elseif ($t_next == 'punct_bracket_open_round' && isset($PMA_SQPdata_function_name[$d_cur_upper])) { /** * @todo 2005-10-16: in the case of a CREATE TABLE containing * a TIMESTAMP, since TIMESTAMP() is also a function, it's * found here and the token is wrongly marked as alpha_functionName. * But we compensate for this when analysing for timestamp_not_null * later in this script. * * Same applies to CHAR vs. CHAR() function. */ $t_suffix = '_functionName'; /* There are functions which might be as well column types */ } elseif (isset($PMA_SQPdata_column_type[$d_cur_upper])) { $t_suffix = '_columnType'; /** * Temporary fix for bugs #621357 and #2027720 * * @todo FIX PROPERLY NEEDS OVERHAUL OF SQL TOKENIZER */ if (($d_cur_upper == 'SET' || $d_cur_upper == 'BINARY') && $t_next != 'punct_bracket_open_round') { $t_suffix = '_reservedWord'; } //END OF TEMPORARY FIX // CHARACTER is a synonym for CHAR, but can also be meant as // CHARACTER SET. In this case, we have a reserved word. if ($d_cur_upper == 'CHARACTER' && $d_next_upper == 'SET') { $t_suffix = '_reservedWord'; } // experimental // current is a column type, so previous must not be // a reserved word but an identifier // CREATE TABLE SG_Persons (first varchar(64)) //if ($sql_array[$i-1]['type'] =='alpha_reservedWord') { // $sql_array[$i-1]['type'] = 'alpha_identifier'; //} } elseif (isset($PMA_SQPdata_reserved_word[$d_cur_upper])) { $t_suffix = '_reservedWord'; } elseif (isset($PMA_SQPdata_column_attrib[$d_cur_upper])) { $t_suffix = '_columnAttrib'; // INNODB is a MySQL table type, but in "SHOW ENGINE INNODB STATUS", // it should be regarded as a reserved word. if ($d_cur_upper == 'INNODB' && $d_prev_upper == 'SHOW' && $d_next_upper == 'STATUS') { $t_suffix = '_reservedWord'; } if ($d_cur_upper == 'DEFAULT' && $d_next_upper == 'CHARACTER') { $t_suffix = '_reservedWord'; } // Binary as character set if ($d_cur_upper == 'BINARY' && ($d_bef_prev_upper == 'CHARACTER' && $d_prev_upper == 'SET' || $d_bef_prev_upper == 'SET' && $d_prev_upper == '=' || $d_bef_prev_upper == 'CHARSET' && $d_prev_upper == '=' || $d_prev_upper == 'CHARSET') && in_array($d_cur, $mysql_charsets)) { $t_suffix = '_charset'; } } elseif (in_array($d_cur, $mysql_charsets) || in_array($d_cur, $mysql_collations_flat) || $d_cur[0] == '_' && in_array(mb_substr($d_cur, 1), $mysql_charsets)) { $t_suffix = '_charset'; } else { // Do nothing } // check if present in the list of forbidden words if ($t_suffix == '_reservedWord' && isset($PMA_SQPdata_forbidden_word[$d_cur_upper])) { $sql_array[$i]['forbidden'] = true; } else { $sql_array[$i]['forbidden'] = false; } $sql_array[$i]['type'] .= $t_suffix; } } // end for // Stores the size of the array inside the array, as count() is a slow // operation. $sql_array['len'] = $arraysize; // DEBUG echo 'After parsing<pre>'; print_r($sql_array); echo '</pre>'; // Sends the data back return $sql_array; }