/**
  * 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;
 }
Esempio n. 2
0
/**
 * 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;
    // 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();
    }
    // 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;
    $count1 = 0;
    $count2 = 0;
    $punct_queryend = ';';
    $punct_qualifier = '.';
    $punct_listsep = ',';
    $punct_level_plus = '(';
    $punct_level_minus = ')';
    $punct_user = '******';
    $digit_floatdecimal = '.';
    $digit_hexset = 'x';
    $bracket_list = '()[]{}';
    $allpunct_list = '-,;:!?/.^~\\*&%+<=>|';
    $allpunct_list_pair = array('!=' => 1, '&&' => 1, ':=' => 1, '<<' => 1, '<=' => 1, '<=>' => 1, '<>' => 1, '>=' => 1, '>>' => 1, '||' => 1, '==' => 1);
    $quote_list = '\'"`';
    $arraysize = 0;
    $previous_was_space = false;
    $this_was_space = false;
    $previous_was_bracket = false;
    $this_was_bracket = false;
    $previous_was_punct = false;
    $this_was_punct = false;
    $previous_was_listsep = false;
    $this_was_listsep = false;
    $previous_was_quote = false;
    $this_was_quote = false;
    while ($count2 < $len) {
        $c = PMA_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', '', $arraysize);
            continue;
        }
        // Checks for white space
        if (PMA_STR_isSpace($c)) {
            $this_was_space = true;
            $count2++;
            continue;
        }
        // Checks for comment lines.
        // MySQL style #
        // C style /* */
        // ANSI style --
        $next_c = PMA_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 == '-' && PMA_substr($sql, $count2 + 2, 1) <= ' ') {
            $count2++;
            $pos = 0;
            $type = 'bad';
            switch ($c) {
                case '#':
                    $type = 'mysql';
                case '-':
                    $type = 'ansi';
                    $pos = PMA_strpos($sql, "\n", $count2);
                    break;
                case '/':
                    $type = 'c';
                    $pos = 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_strpos($quote_list, $c) !== false) {
            $startquotepos = $count2;
            $quotetype = $c;
            $count2++;
            $escaped = false;
            $pos = $count2;
            $oldpos = 0;
            do {
                $oldpos = $pos;
                $pos = PMA_strpos(' ' . $sql, $quotetype, $oldpos + 1) - 1;
                // ($pos === false)
                if ($pos < 0) {
                    if ($c == '`') {
                        /*
                         * 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 = PMA_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 = PMA_substr($sql, 0, $pos_quote_separator) . '`' . PMA_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();
                        }
                    } else {
                        $debugstr = __('Unclosed quote') . ' @ ' . $startquotepos . "\n" . 'STR: ' . htmlspecialchars($quotetype);
                        PMA_SQP_throwError($debugstr, $sql);
                        return $sql_array;
                    }
                }
                // 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) && $c != '`') {
                    $pos++;
                    continue;
                } elseif ($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';
                    $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 = PMA_substr($sql, $count1, $count2 - $count1);
            PMA_SQP_arrayAdd($sql_array, $type, $data, $arraysize);
            continue;
        }
        // Checks for brackets
        if (PMA_strpos($bracket_list, $c) !== false) {
            // All bracket tokens are only one item long
            $this_was_bracket = true;
            $count2++;
            $type_type = '';
            if (PMA_strpos('([{', $c) !== false) {
                $type_type = 'open';
            } else {
                $type_type = 'close';
            }
            $type_style = '';
            if (PMA_strpos('()', $c) !== false) {
                $type_style = 'round';
            } elseif (PMA_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);
            continue;
        }
        /* DEBUG
           echo '<pre>1';
           var_dump(PMA_STR_isSqlIdentifier($c, false));
           var_dump($c == '@');
           var_dump($c == '.');
           var_dump(PMA_STR_isDigit(PMA_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 (PMA_STR_isSqlIdentifier($c, false) || $c == '@' || $c == '.' && PMA_STR_isDigit(PMA_substr($sql, $count2 + 1, 1)) && ($previous_was_space || $previous_was_bracket || $previous_was_listsep)) {
            /* DEBUG
               echo PMA_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 && PMA_STR_isDigit($c);
            $is_hex_digit = $is_digit && $c == '0' && $count2 < $len && PMA_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 && 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 = __('Invalid Identifer') . ' @ ' . ($count1 + 1) . "\n" . 'STR: ' . htmlspecialchars(PMA_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 && 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 || $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 (PMA_strpos($allpunct_list, $c) !== false) {
            while ($count2 < $len && PMA_strpos($allpunct_list, PMA_substr($sql, $count2, 1)) !== false) {
                $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
            /* 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);
            } 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);
            } 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;
                } elseif ($last2 == '/*' || $last2 == '--' && ($count2 == $len || PMA_substr($sql, $count2, 1) <= ' ')) {
                    $count2 -= 2;
                    $punct_data = PMA_substr($sql, $count1, $count2 - $count1);
                } elseif ($last == '-' || $last == '+' || $last == '!') {
                    $count2--;
                    $punct_data = PMA_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);
                continue;
            }
            // end if... elseif... 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_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_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: <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 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(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;
}