2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /** SQL Parser Functions for phpMyAdmin
5 * These functions define an SQL parser system, capable of understanding and
6 * extracting data from a MySQL type SQL query.
8 * The basic procedure for using the new SQL parser:
9 * On any page that needs to extract data from a query or to pretty-print a
10 * query, you need code like this up at the top:
12 * ($sql contains the query)
13 * $parsed_sql = PMA_SQP_parse($sql);
15 * If you want to extract data from it then, you just need to run
16 * $sql_info = PMA_SQP_analyze($parsed_sql);
18 * See comments in PMA_SQP_analyze for the returned info
21 * If you want a pretty-printed version of the query, do:
22 * $string = PMA_SQP_formatHtml($parsed_sql);
23 * (note that that you need to have syntax.css.php included somehow in your
24 * page for it to work, I recommend '<link rel="stylesheet" type="text/css"
25 * href="syntax.css.php" />' at the moment.)
29 if (! defined('PHPMYADMIN')) {
34 * Minimum inclusion? (i.e. for the stylesheet builder)
36 if (! defined('PMA_MINIMUM_COMMON')) {
38 * Include the string library as we use it heavily
40 require_once './libraries/string.lib.php';
43 * Include data for the SQL Parser
45 require_once './libraries/sqlparser.data.php';
46 if (!defined('TESTSUITE')) {
47 require_once './libraries/mysql_charsets.lib.php';
49 if (!isset($mysql_charsets)) {
50 $mysql_charsets = array();
51 $mysql_charsets_count = 0;
52 $mysql_collations_flat = array();
53 $mysql_collations_count = 0;
56 if (!defined('DEBUG_TIMING')) {
58 * currently we don't need the $pos (token position in query)
59 * for other purposes than LIMIT clause verification,
60 * so many calls to this function do not include the 4th parameter
62 function PMA_SQP_arrayAdd(&$arr, $type, $data, &$arrsize, $pos = 0)
64 $arr[] = array('type' => $type, 'data' => $data, 'pos' => $pos);
66 } // end of the "PMA_SQP_arrayAdd()" function
69 * This is debug variant of above.
72 function PMA_SQP_arrayAdd(&$arr, $type, $data, &$arrsize, $pos = 0)
77 $arr[] = array('type' => $type, 'data' => $data, 'pos' => $pos, 'time' => $t);
80 } // end of the "PMA_SQP_arrayAdd()" function
81 } // end if... else...
85 * Reset the error variable for the SQL parser
89 // Added, Robbat2 - 13 Janurary 2003, 2:59PM
90 function PMA_SQP_resetError()
92 global $SQP_errorString;
93 $SQP_errorString = '';
94 unset($SQP_errorString);
98 * Get the contents of the error variable for the SQL parser
100 * @return string Error string from SQL parser
104 // Added, Robbat2 - 13 Janurary 2003, 2:59PM
105 function PMA_SQP_getErrorString()
107 global $SQP_errorString;
108 return isset($SQP_errorString) ?
$SQP_errorString : '';
112 * Check if the SQL parser hit an error
114 * @return boolean error state
118 // Added, Robbat2 - 13 Janurary 2003, 2:59PM
119 function PMA_SQP_isError()
121 global $SQP_errorString;
122 return isset($SQP_errorString) && !empty($SQP_errorString);
126 * Set an error message for the system
128 * @param string The error message
129 * @param string The failing SQL query
132 * @scope SQL Parser internal
134 // Revised, Robbat2 - 13 Janurary 2003, 2:59PM
135 function PMA_SQP_throwError($message, $sql)
137 global $SQP_errorString;
138 $SQP_errorString = '<p>'.__('There seems to be an error in your SQL query. The MySQL server error output below, if there is any, may also help you in diagnosing the problem') . '</p>' . "\n"
140 . 'ERROR: ' . $message . "\n"
141 . 'SQL: ' . htmlspecialchars($sql) . "\n"
144 } // end of the "PMA_SQP_throwError()" function
148 * Do display the bug report
150 * @param string The error message
151 * @param string The failing SQL query
155 function PMA_SQP_bug($message, $sql)
157 global $SQP_errorString;
158 $debugstr = 'ERROR: ' . $message . "\n";
159 $debugstr .= 'MySQL: '.PMA_MYSQL_STR_VERSION
. "\n";
160 $debugstr .= 'USR OS, AGENT, VER: ' . PMA_USR_OS
. ' ' . PMA_USR_BROWSER_AGENT
. ' ' . PMA_USR_BROWSER_VER
. "\n";
161 $debugstr .= 'PMA: ' . PMA_VERSION
. "\n";
162 $debugstr .= 'PHP VER,OS: ' . PMA_PHP_STR_VERSION
. ' ' . PHP_OS
. "\n";
163 $debugstr .= 'LANG: ' . $GLOBALS['lang'] . "\n";
164 $debugstr .= 'SQL: ' . htmlspecialchars($sql);
166 $encodedstr = $debugstr;
167 if (@function_exists
('gzcompress')) {
168 $encodedstr = gzcompress($debugstr, 9);
170 $encodedstr = preg_replace("/(\015\012)|(\015)|(\012)/", '<br />' . "\n", chunk_split(base64_encode($encodedstr)));
173 $SQP_errorString .= __('There is a chance that you may have found a bug in the SQL parser. Please examine your query closely, and check that the quotes are correct and not mis-matched. Other possible failure causes may be that you are uploading a file with binary outside of a quoted text area. You can also try your query on the MySQL command line interface. The MySQL server error output below, if there is any, may also help you in diagnosing the problem. If you still have problems or if the parser fails where the command line interface succeeds, please reduce your SQL query input to the single query that causes problems, and submit a bug report with the data chunk in the CUT section below:')
175 . '----' . __('BEGIN CUT') . '----' . '<br />' . "\n"
177 . '----' . __('END CUT') . '----' . '<br />' . "\n";
179 $SQP_errorString .= '----' . __('BEGIN RAW') . '----<br />' . "\n"
183 . '----' . __('END RAW') . '----<br />' . "\n";
185 } // end of the "PMA_SQP_bug()" function
189 * Parses the SQL queries
191 * @param string The SQL query list
193 * @return mixed Most of times, nothing...
195 * @global array The current PMA configuration
196 * @global array MySQL column attributes
197 * @global array MySQL reserved words
198 * @global array MySQL column types
199 * @global array MySQL function names
200 * @global integer MySQL column attributes count
201 * @global integer MySQL reserved words count
202 * @global integer MySQL column types count
203 * @global integer MySQL function names count
204 * @global array List of available character sets
205 * @global array List of available collations
206 * @global integer Character sets count
207 * @global integer Collations count
211 function PMA_SQP_parse($sql)
213 global $PMA_SQPdata_column_attrib, $PMA_SQPdata_reserved_word, $PMA_SQPdata_column_type, $PMA_SQPdata_function_name,
214 $PMA_SQPdata_column_attrib_cnt, $PMA_SQPdata_reserved_word_cnt, $PMA_SQPdata_column_type_cnt, $PMA_SQPdata_function_name_cnt;
215 global $mysql_charsets, $mysql_collations_flat, $mysql_charsets_count, $mysql_collations_count;
216 global $PMA_SQPdata_forbidden_word, $PMA_SQPdata_forbidden_word_cnt;
218 // Convert all line feeds to Unix style
219 $sql = str_replace("\r\n", "\n", $sql);
220 $sql = str_replace("\r", "\n", $sql);
222 $len = PMA_strlen($sql);
227 $sql_array = array();
228 $sql_array['raw'] = $sql;
231 $punct_queryend = ';';
232 $punct_qualifier = '.';
233 $punct_listsep = ',';
234 $punct_level_plus = '(';
235 $punct_level_minus = ')';
237 $digit_floatdecimal = '.';
239 $bracket_list = '()[]{}';
240 $allpunct_list = '-,;:!?/.^~\*&%+<=>|';
241 $allpunct_list_pair = array (
253 $allpunct_list_pair_size = 10; //count($allpunct_list_pair);
254 $quote_list = '\'"`';
257 $previous_was_space = false;
258 $this_was_space = false;
259 $previous_was_bracket = false;
260 $this_was_bracket = false;
261 $previous_was_punct = false;
262 $this_was_punct = false;
263 $previous_was_listsep = false;
264 $this_was_listsep = false;
265 $previous_was_quote = false;
266 $this_was_quote = false;
268 while ($count2 < $len) {
269 $c = $GLOBALS['PMA_substr']($sql, $count2, 1);
272 $previous_was_space = $this_was_space;
273 $this_was_space = false;
274 $previous_was_bracket = $this_was_bracket;
275 $this_was_bracket = false;
276 $previous_was_punct = $this_was_punct;
277 $this_was_punct = false;
278 $previous_was_listsep = $this_was_listsep;
279 $this_was_listsep = false;
280 $previous_was_quote = $this_was_quote;
281 $this_was_quote = false;
284 $this_was_space = true;
286 PMA_SQP_arrayAdd($sql_array, 'white_newline', '', $arraysize);
290 // Checks for white space
291 if ($GLOBALS['PMA_STR_isSpace']($c)) {
292 $this_was_space = true;
297 // Checks for comment lines.
302 ||
(($count2 +
1 < $len) && ($c == '/') && ($GLOBALS['PMA_substr']($sql, $count2 +
1, 1) == '*'))
303 ||
(($count2 +
2 == $len) && ($c == '-') && ($GLOBALS['PMA_substr']($sql, $count2 +
1, 1) == '-'))
304 ||
(($count2 +
2 < $len) && ($c == '-') && ($GLOBALS['PMA_substr']($sql, $count2 +
1, 1) == '-') && (($GLOBALS['PMA_substr']($sql, $count2 +
2, 1) <= ' ')))) {
313 $pos = $GLOBALS['PMA_strpos']($sql, "\n", $count2);
317 $pos = $GLOBALS['PMA_strpos']($sql, '*/', $count2);
323 $count2 = ($pos < $count2) ?
$len : $pos;
324 $str = $GLOBALS['PMA_substr']($sql, $count1, $count2 - $count1);
325 PMA_SQP_arrayAdd($sql_array, 'comment_' . $type, $str, $arraysize);
329 // Checks for something inside quotation marks
330 if ($GLOBALS['PMA_strpos']($quote_list, $c) !== false) {
331 $startquotepos = $count2;
335 $escaped_escaped = FALSE;
340 $pos = $GLOBALS['PMA_strpos'](' ' . $sql, $quotetype, $oldpos +
1) - 1;
345 * Behave same as MySQL and accept end of query as end of backtick.
346 * I know this is sick, but MySQL behaves like this:
348 * SELECT * FROM `table
352 * SELECT * FROM `table`
354 $pos_quote_separator = $GLOBALS['PMA_strpos'](' ' . $sql, $GLOBALS['sql_delimiter'], $oldpos +
1) - 1;
355 if ($pos_quote_separator < 0) {
358 $sql_array['raw'] .= '`';
362 $sql = $GLOBALS['PMA_substr']($sql, 0, $pos_quote_separator) . '`' . $GLOBALS['PMA_substr']($sql, $pos_quote_separator);
363 $sql_array['raw'] = $sql;
364 $pos = $pos_quote_separator;
366 if (class_exists('PMA_Message')) {
367 PMA_Message
::warning(__('Automatically appended backtick to the end of query!'))->display();
370 $debugstr = __('Unclosed quote') . ' @ ' . $startquotepos. "\n"
371 . 'STR: ' . htmlspecialchars($quotetype);
372 PMA_SQP_throwError($debugstr, $sql);
377 // If the quote is the first character, it can't be
378 // escaped, so don't do the rest of the code
383 // Checks for MySQL escaping using a \
384 // And checks for ANSI escaping using the $quotetype character
385 if (($pos < $len) && PMA_STR_charIsEscaped($sql, $pos)) {
388 } elseif (($pos +
1 < $len) && ($GLOBALS['PMA_substr']($sql, $pos, 1) == $quotetype) && ($GLOBALS['PMA_substr']($sql, $pos +
1, 1) == $quotetype)) {
394 } while ($len > $pos); // end do
399 switch ($quotetype) {
402 $this_was_quote = true;
406 $this_was_quote = true;
410 $this_was_quote = true;
415 $data = $GLOBALS['PMA_substr']($sql, $count1, $count2 - $count1);
416 PMA_SQP_arrayAdd($sql_array, $type, $data, $arraysize);
420 // Checks for brackets
421 if ($GLOBALS['PMA_strpos']($bracket_list, $c) !== false) {
422 // All bracket tokens are only one item long
423 $this_was_bracket = true;
426 if ($GLOBALS['PMA_strpos']('([{', $c) !== false) {
429 $type_type = 'close';
433 if ($GLOBALS['PMA_strpos']('()', $c) !== false) {
434 $type_style = 'round';
435 } elseif ($GLOBALS['PMA_strpos']('[]', $c) !== false) {
436 $type_style = 'square';
438 $type_style = 'curly';
441 $type = 'punct_bracket_' . $type_type . '_' . $type_style;
442 PMA_SQP_arrayAdd($sql_array, $type, $c, $arraysize);
448 var_dump(PMA_STR_isSqlIdentifier($c, false));
451 var_dump(PMA_STR_isDigit(PMA_substr($sql, $count2 + 1, 1)));
452 var_dump($previous_was_space);
453 var_dump($previous_was_bracket);
454 var_dump($previous_was_listsep);
458 // Checks for identifier (alpha or numeric)
459 if (PMA_STR_isSqlIdentifier($c, false)
462 && $GLOBALS['PMA_STR_isDigit']($GLOBALS['PMA_substr']($sql, $count2 +
1, 1))
463 && ($previous_was_space ||
$previous_was_bracket ||
$previous_was_listsep))) {
466 echo PMA_substr($sql, $count2);
473 * @todo a @ can also be present in expressions like
474 * FROM 'user'@'%' or TO 'user'@'%'
475 * in this case, the @ is wrongly marked as alpha_variable
477 $is_identifier = $previous_was_punct;
478 $is_sql_variable = $c == '@' && ! $previous_was_quote;
479 $is_user = $c == '@' && $previous_was_quote;
480 $is_digit = !$is_identifier && !$is_sql_variable && $GLOBALS['PMA_STR_isDigit']($c);
481 $is_hex_digit = $is_digit && $c == '0' && $count2 < $len && $GLOBALS['PMA_substr']($sql, $count2, 1) == 'x';
482 $is_float_digit = $c == '.';
483 $is_float_digit_exponent = FALSE;
487 var_dump($is_identifier);
488 var_dump($is_sql_variable);
490 var_dump($is_float_digit);
494 // Nijel: Fast skip is especially needed for huge BLOB data, requires PHP at least 4.3.0:
497 $pos = strspn($sql, '0123456789abcdefABCDEF', $count2);
498 if ($pos > $count2) {
502 } elseif ($is_digit) {
503 $pos = strspn($sql, '0123456789', $count2);
504 if ($pos > $count2) {
510 while (($count2 < $len) && PMA_STR_isSqlIdentifier($GLOBALS['PMA_substr']($sql, $count2, 1), ($is_sql_variable ||
$is_digit))) {
511 $c2 = $GLOBALS['PMA_substr']($sql, $count2, 1);
512 if ($is_sql_variable && ($c2 == '.')) {
516 if ($is_digit && (!$is_hex_digit) && ($c2 == '.')) {
518 if (!$is_float_digit) {
519 $is_float_digit = TRUE;
522 $debugstr = __('Invalid Identifer') . ' @ ' . ($count1+
1) . "\n"
523 . 'STR: ' . htmlspecialchars(PMA_substr($sql, $count1, $count2 - $count1));
524 PMA_SQP_throwError($debugstr, $sql);
528 if ($is_digit && (!$is_hex_digit) && (($c2 == 'e') ||
($c2 == 'E'))) {
529 if (!$is_float_digit_exponent) {
530 $is_float_digit_exponent = TRUE;
531 $is_float_digit = TRUE;
536 $is_float_digit = FALSE;
539 if (($is_hex_digit && PMA_STR_isHexDigit($c2)) ||
($is_digit && $GLOBALS['PMA_STR_isDigit']($c2))) {
544 $is_hex_digit = FALSE;
550 $l = $count2 - $count1;
551 $str = $GLOBALS['PMA_substr']($sql, $count1, $l);
554 if ($is_digit ||
$is_float_digit ||
$is_hex_digit) {
556 if ($is_float_digit) {
558 } elseif ($is_hex_digit) {
563 } elseif ($is_user) {
564 $type = 'punct_user';
565 } elseif ($is_sql_variable != FALSE) {
566 $type = 'alpha_variable';
569 } // end if... else....
570 PMA_SQP_arrayAdd($sql_array, $type, $str, $arraysize, $count2);
576 if ($GLOBALS['PMA_strpos']($allpunct_list, $c) !== false) {
577 while (($count2 < $len) && $GLOBALS['PMA_strpos']($allpunct_list, $GLOBALS['PMA_substr']($sql, $count2, 1)) !== false) {
580 $l = $count2 - $count1;
584 $punct_data = $GLOBALS['PMA_substr']($sql, $count1, $l);
587 // Special case, sometimes, althought two characters are
588 // adjectent directly, they ACTUALLY need to be seperate
592 var_dump($punct_data);
598 switch ($punct_data) {
599 case $punct_queryend:
600 $t_suffix = '_queryend';
602 case $punct_qualifier:
603 $t_suffix = '_qualifier';
604 $this_was_punct = true;
607 $this_was_listsep = true;
608 $t_suffix = '_listsep';
613 PMA_SQP_arrayAdd($sql_array, 'punct' . $t_suffix, $punct_data, $arraysize);
614 } elseif ($punct_data == $GLOBALS['sql_delimiter'] ||
PMA_STR_binarySearchInArr($punct_data, $allpunct_list_pair, $allpunct_list_pair_size)) {
615 // Ok, we have one of the valid combined punct expressions
616 PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize);
618 // Bad luck, lets split it up more
619 $first = $punct_data[0];
620 $first2 = $punct_data[0] . $punct_data[1];
621 $last2 = $punct_data[$l - 2] . $punct_data[$l - 1];
622 $last = $punct_data[$l - 1];
623 if (($first == ',') ||
($first == ';') ||
($first == '.') ||
($first == '*')) {
624 $count2 = $count1 +
1;
625 $punct_data = $first;
626 } elseif (($last2 == '/*') ||
(($last2 == '--') && ($count2 == $len ||
$GLOBALS['PMA_substr']($sql, $count2, 1) <= ' '))) {
628 $punct_data = $GLOBALS['PMA_substr']($sql, $count1, $count2 - $count1);
629 } elseif (($last == '-') ||
($last == '+') ||
($last == '!')) {
631 $punct_data = $GLOBALS['PMA_substr']($sql, $count1, $count2 - $count1);
633 * @todo for negation operator, split in 2 tokens ?
634 * "select x&~1 from t"
635 * becomes "select x & ~ 1 from t" ?
638 } elseif ($last != '~') {
639 $debugstr = __('Unknown Punctuation String') . ' @ ' . ($count1+
1) . "\n"
640 . 'STR: ' . htmlspecialchars($punct_data);
641 PMA_SQP_throwError($debugstr, $sql);
644 PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize);
646 } // end if... elseif... else
653 $debugstr = 'C1 C2 LEN: ' . $count1 . ' ' . $count2 . ' ' . $len . "\n"
654 . 'STR: ' . $GLOBALS['PMA_substr']($sql, $count1, $count2 - $count1) . "\n";
655 PMA_SQP_bug($debugstr, $sql);
658 } // end while ($count2 < $len)
666 if ($arraysize > 0) {
667 $t_next = $sql_array[0]['type'];
671 $d_next = $sql_array[0]['data'];
675 $d_next_upper = $t_next == 'alpha' ?
strtoupper($d_next) : $d_next;
677 $d_bef_prev_upper = '';
681 for ($i = 0; $i < $arraysize; $i++
) {
682 $t_bef_prev = $t_prev;
685 $d_bef_prev = $d_prev;
688 $d_bef_prev_upper = $d_prev_upper;
689 $d_prev_upper = $d_cur_upper;
690 $d_cur_upper = $d_next_upper;
691 if (($i +
1) < $arraysize) {
692 $t_next = $sql_array[$i +
1]['type'];
693 $d_next = $sql_array[$i +
1]['data'];
694 $d_next_upper = $t_next == 'alpha' ?
strtoupper($d_next) : $d_next;
701 //DEBUG echo "[prev: <strong>".$d_prev."</strong> ".$t_prev."][cur: <strong>".$d_cur."</strong> ".$t_cur."][next: <strong>".$d_next."</strong> ".$t_next."]<br />";
703 if ($t_cur == 'alpha') {
704 $t_suffix = '_identifier';
705 // for example: `thebit` bit(8) NOT NULL DEFAULT b'0'
706 if ($t_prev == 'alpha' && $d_prev == 'DEFAULT' && $d_cur == 'b' && $t_next == 'quote_single') {
707 $t_suffix = '_bitfield_constant_introducer';
708 } elseif (($t_next == 'punct_qualifier') ||
($t_prev == 'punct_qualifier')) {
709 $t_suffix = '_identifier';
710 } elseif (($t_next == 'punct_bracket_open_round')
711 && PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_function_name, $PMA_SQPdata_function_name_cnt)) {
713 * @todo 2005-10-16: in the case of a CREATE TABLE containing
714 * a TIMESTAMP, since TIMESTAMP() is also a function, it's
715 * found here and the token is wrongly marked as alpha_functionName.
716 * But we compensate for this when analysing for timestamp_not_null
717 * later in this script.
719 * Same applies to CHAR vs. CHAR() function.
721 $t_suffix = '_functionName';
722 /* There are functions which might be as well column types */
723 } elseif (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_type, $PMA_SQPdata_column_type_cnt)) {
724 $t_suffix = '_columnType';
727 * Temporary fix for BUG #621357 and #2027720
729 * @todo FIX PROPERLY NEEDS OVERHAUL OF SQL TOKENIZER
731 if (($d_cur_upper == 'SET' ||
$d_cur_upper == 'BINARY') && $t_next != 'punct_bracket_open_round') {
732 $t_suffix = '_reservedWord';
734 //END OF TEMPORARY FIX
736 // CHARACTER is a synonym for CHAR, but can also be meant as
737 // CHARACTER SET. In this case, we have a reserved word.
738 if ($d_cur_upper == 'CHARACTER' && $d_next_upper == 'SET') {
739 $t_suffix = '_reservedWord';
743 // current is a column type, so previous must not be
744 // a reserved word but an identifier
745 // CREATE TABLE SG_Persons (first varchar(64))
747 //if ($sql_array[$i-1]['type'] =='alpha_reservedWord') {
748 // $sql_array[$i-1]['type'] = 'alpha_identifier';
751 } elseif (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_reserved_word, $PMA_SQPdata_reserved_word_cnt)) {
752 $t_suffix = '_reservedWord';
753 } elseif (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_attrib, $PMA_SQPdata_column_attrib_cnt)) {
754 $t_suffix = '_columnAttrib';
755 // INNODB is a MySQL table type, but in "SHOW INNODB STATUS",
756 // it should be regarded as a reserved word.
757 if ($d_cur_upper == 'INNODB' && $d_prev_upper == 'SHOW' && $d_next_upper == 'STATUS') {
758 $t_suffix = '_reservedWord';
761 if ($d_cur_upper == 'DEFAULT' && $d_next_upper == 'CHARACTER') {
762 $t_suffix = '_reservedWord';
764 // Binary as character set
765 if ($d_cur_upper == 'BINARY' && (
766 ($d_bef_prev_upper == 'CHARACTER' && $d_prev_upper == 'SET')
767 ||
($d_bef_prev_upper == 'SET' && $d_prev_upper == '=')
768 ||
($d_bef_prev_upper == 'CHARSET' && $d_prev_upper == '=')
769 ||
$d_prev_upper == 'CHARSET'
770 ) && PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, count($mysql_charsets))) {
771 $t_suffix = '_charset';
773 } elseif (PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, $mysql_charsets_count)
774 ||
PMA_STR_binarySearchInArr($d_cur, $mysql_collations_flat, $mysql_collations_count)
775 ||
($d_cur{0} == '_' && PMA_STR_binarySearchInArr(substr($d_cur, 1), $mysql_charsets, $mysql_charsets_count))) {
776 $t_suffix = '_charset';
780 // check if present in the list of forbidden words
781 if ($t_suffix == '_reservedWord' && PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_forbidden_word, $PMA_SQPdata_forbidden_word_cnt)) {
782 $sql_array[$i]['forbidden'] = TRUE;
784 $sql_array[$i]['forbidden'] = FALSE;
786 $sql_array[$i]['type'] .= $t_suffix;
790 // Stores the size of the array inside the array, as count() is a slow
792 $sql_array['len'] = $arraysize;
794 // DEBUG echo 'After parsing<pre>'; print_r($sql_array); echo '</pre>';
795 // Sends the data back
797 } // end of the "PMA_SQP_parse()" function
800 * Checks for token types being what we want...
802 * @param string String of type that we have
803 * @param string String of type that we want
805 * @return boolean result of check
809 function PMA_SQP_typeCheck($toCheck, $whatWeWant)
811 $typeSeperator = '_';
812 if (strcmp($whatWeWant, $toCheck) == 0) {
815 if (strpos($whatWeWant, $typeSeperator) === FALSE) {
816 return strncmp($whatWeWant, $toCheck, strpos($toCheck, $typeSeperator)) == 0;
825 * Analyzes SQL queries
827 * @param array The SQL queries
829 * @return array The analyzed SQL queries
833 function PMA_SQP_analyze($arr)
835 if ($arr == array() ||
!isset($arr['len'])) {
842 'select_expr_clause'=> '', // the whole stuff between SELECT and FROM , except DISTINCT
843 'position_of_first_select' => '', // the array index
845 'group_by_clause'=> '',
846 'order_by_clause'=> '',
847 'having_clause' => '',
848 'limit_clause' => '',
849 'where_clause' => '',
850 'where_clause_identifiers' => array(),
851 'unsorted_query' => '',
852 'queryflags' => array(),
853 'select_expr' => array(),
854 'table_ref' => array(),
855 'foreign_keys' => array(),
856 'create_table_fields' => array()
858 $subresult_empty = $subresult;
859 $seek_queryend = FALSE;
860 $seen_end_of_table_ref = FALSE;
861 $number_of_brackets_in_extract = 0;
862 $number_of_brackets_in_group_concat = 0;
864 $number_of_brackets = 0;
865 $in_subquery = false;
866 $seen_subquery = false;
869 // for SELECT EXTRACT(YEAR_MONTH FROM CURDATE())
870 // we must not use CURDATE as a table_ref
871 // so we track whether we are in the EXTRACT()
874 // for GROUP_CONCAT(...)
875 $in_group_concat = FALSE;
877 /* Description of analyzer results
879 * db, table, column, alias
880 * ------------------------
882 * Inside the $subresult array, we create ['select_expr'] and ['table_ref'] arrays.
884 * The SELECT syntax (simplified) is
887 * select_expression,...
888 * [FROM [table_references]
891 * ['select_expr'] is filled with each expression, the key represents the
892 * expression position in the list (0-based) (so we don't lose track of
893 * multiple occurences of the same column).
895 * ['table_ref'] is filled with each table ref, same thing for the key.
897 * I create all sub-values empty, even if they are
898 * not present (for example no select_expression alias).
900 * There is a debug section at the end of loop #1, if you want to
901 * see the exact contents of select_expr and table_ref
906 * In $subresult, array 'queryflags' is filled, according to what we
909 * Currently, those are generated:
911 * ['queryflags']['need_confirm'] = 1; if the query needs confirmation
912 * ['queryflags']['select_from'] = 1; if this is a real SELECT...FROM
913 * ['queryflags']['distinct'] = 1; for a DISTINCT
914 * ['queryflags']['union'] = 1; for a UNION
915 * ['queryflags']['join'] = 1; for a JOIN
916 * ['queryflags']['offset'] = 1; for the presence of OFFSET
917 * ['queryflags']['procedure'] = 1; for the presence of PROCEDURE
922 * The select is splitted in those clauses:
923 * ['select_expr_clause']
925 * ['group_by_clause']
926 * ['order_by_clause']
931 * The identifiers of the WHERE clause are put into the array
932 * ['where_clause_identifier']
934 * For a SELECT, the whole query without the ORDER BY clause is put into
939 * The CREATE TABLE may contain FOREIGN KEY clauses, so they get
940 * analyzed and ['foreign_keys'] is an array filled with
941 * the constraint name, the index list,
942 * the REFERENCES table name and REFERENCES index list,
943 * and ON UPDATE | ON DELETE clauses
945 * position_of_first_select
946 * ------------------------
948 * The array index of the first SELECT we find. Will be used to
949 * insert a SQL_CALC_FOUND_ROWS.
951 * create_table_fields
952 * -------------------
954 * Used to detect the DEFAULT CURRENT_TIMESTAMP and
955 * ON UPDATE CURRENT_TIMESTAMP clauses of the CREATE TABLE query.
956 * Also used to store the default value of the field.
957 * An array, each element is the identifier name.
958 * Note that for now, the timestamp_not_null element is created
959 * even for non-TIMESTAMP fields.
961 * Sub-elements: ['type'] which contains the column type
962 * optional (currently they are never false but can be absent):
963 * ['default_current_timestamp'] boolean
964 * ['on_update_current_timestamp'] boolean
965 * ['timestamp_not_null'] boolean
967 * section_before_limit, section_after_limit
968 * -----------------------------------------
970 * Marks the point of the query where we can insert a LIMIT clause;
971 * so the section_before_limit will contain the left part before
972 * a possible LIMIT clause
975 * End of description of analyzer results
979 // TODO: current logic checks for only one word, so I put only the
980 // first word of the reserved expressions that end a table ref;
981 // maybe this is not ok (the first word might mean something else)
982 // $words_ending_table_ref = array(
987 // 'LOCK IN SHARE MODE',
993 $words_ending_table_ref = array(
1004 $words_ending_table_ref_cnt = 9; //count($words_ending_table_ref);
1006 $words_ending_clauses = array(
1013 $words_ending_clauses_cnt = 5; //count($words_ending_clauses);
1019 $supported_query_types = array(
1022 // Support for these additional query types will come later on.
1036 $supported_query_types_cnt = count($supported_query_types);
1038 // loop #1 for each token: select_expr, table_ref for SELECT
1040 for ($i = 0; $i < $size; $i++
) {
1041 //DEBUG echo "Loop1 <strong>" . $arr[$i]['data'] . "</strong> (" . $arr[$i]['type'] . ")<br />";
1043 // High speed seek for locating the end of the current query
1044 if ($seek_queryend == TRUE) {
1045 if ($arr[$i]['type'] == 'punct_queryend') {
1046 $seek_queryend = FALSE;
1049 } // end if (type == punct_queryend)
1050 } // end if ($seek_queryend)
1053 * Note: do not split if this is a punct_queryend for the first and only query
1054 * @todo when we find a UNION, should we split in another subresult?
1056 if ($arr[$i]['type'] == 'punct_queryend' && ($i +
1 != $size)) {
1057 $result[] = $subresult;
1058 $subresult = $subresult_empty;
1060 } // end if (type == punct_queryend)
1062 // ==============================================================
1063 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1064 $number_of_brackets++
;
1066 $number_of_brackets_in_extract++
;
1068 if ($in_group_concat) {
1069 $number_of_brackets_in_group_concat++
;
1072 // ==============================================================
1073 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1074 $number_of_brackets--;
1075 if ($number_of_brackets == 0) {
1076 $in_subquery = false;
1079 $number_of_brackets_in_extract--;
1080 if ($number_of_brackets_in_extract == 0) {
1081 $in_extract = FALSE;
1084 if ($in_group_concat) {
1085 $number_of_brackets_in_group_concat--;
1086 if ($number_of_brackets_in_group_concat == 0) {
1087 $in_group_concat = FALSE;
1094 * skip the subquery to avoid setting
1095 * select_expr or table_ref with the contents
1096 * of this subquery; this is to avoid a bug when
1097 * trying to edit the results of
1098 * select * from child where not exists (select id from
1099 * parent where child.parent_id = parent.id);
1103 // ==============================================================
1104 if ($arr[$i]['type'] == 'alpha_functionName') {
1105 $upper_data = strtoupper($arr[$i]['data']);
1106 if ($upper_data =='EXTRACT') {
1108 $number_of_brackets_in_extract = 0;
1110 if ($upper_data =='GROUP_CONCAT') {
1111 $in_group_concat = TRUE;
1112 $number_of_brackets_in_group_concat = 0;
1116 // ==============================================================
1117 if ($arr[$i]['type'] == 'alpha_reservedWord'
1118 // && $arr[$i]['forbidden'] == FALSE) {
1120 // We don't know what type of query yet, so run this
1121 if ($subresult['querytype'] == '') {
1122 $subresult['querytype'] = strtoupper($arr[$i]['data']);
1123 } // end if (querytype was empty)
1125 // Check if we support this type of query
1126 if (!PMA_STR_binarySearchInArr($subresult['querytype'], $supported_query_types, $supported_query_types_cnt)) {
1127 // Skip ahead to the next one if we don't
1128 $seek_queryend = TRUE;
1130 } // end if (query not supported)
1133 $upper_data = strtoupper($arr[$i]['data']);
1135 * @todo reset for each query?
1138 if ($upper_data == 'SELECT') {
1139 if ($number_of_brackets > 0) {
1140 $in_subquery = true;
1141 $seen_subquery = true;
1142 // this is a subquery so do not analyze inside it
1146 $previous_was_identifier = FALSE;
1147 $current_select_expr = -1;
1148 $seen_end_of_table_ref = FALSE;
1149 } // end if (data == SELECT)
1151 if ($upper_data =='FROM' && !$in_extract) {
1152 $current_table_ref = -1;
1154 $previous_was_identifier = FALSE;
1155 $save_table_ref = TRUE;
1156 } // end if (data == FROM)
1158 // here, do not 'continue' the loop, as we have more work for
1159 // reserved words below
1160 } // end if (type == alpha_reservedWord)
1162 // ==============================
1163 if ($arr[$i]['type'] == 'quote_backtick'
1164 ||
$arr[$i]['type'] == 'quote_double'
1165 ||
$arr[$i]['type'] == 'quote_single'
1166 ||
$arr[$i]['type'] == 'alpha_identifier'
1167 ||
($arr[$i]['type'] == 'alpha_reservedWord'
1168 && $arr[$i]['forbidden'] == FALSE)) {
1170 switch ($arr[$i]['type']) {
1171 case 'alpha_identifier':
1172 case 'alpha_reservedWord':
1174 * this is not a real reservedWord, because it's not
1175 * present in the list of forbidden words, for example
1176 * "storage" which can be used as an identifier
1178 * @todo avoid the pretty printing in color in this case
1180 $identifier = $arr[$i]['data'];
1183 case 'quote_backtick':
1184 case 'quote_double':
1185 case 'quote_single':
1186 $identifier = PMA_unQuote($arr[$i]['data']);
1190 if ($subresult['querytype'] == 'SELECT'
1191 && ! $in_group_concat
1192 && ! ($seen_subquery && $arr[$i - 1]['type'] == 'punct_bracket_close_round')) {
1194 if ($previous_was_identifier && isset($chain)) {
1195 // found alias for this select_expr, save it
1196 // but only if we got something in $chain
1197 // (for example, SELECT COUNT(*) AS cnt
1198 // puts nothing in $chain, so we avoid
1199 // setting the alias)
1200 $alias_for_select_expr = $identifier;
1202 $chain[] = $identifier;
1203 $previous_was_identifier = TRUE;
1205 } // end if !$previous_was_identifier
1208 if ($save_table_ref && !$seen_end_of_table_ref) {
1209 if ($previous_was_identifier) {
1210 // found alias for table ref
1211 // save it for later
1212 $alias_for_table_ref = $identifier;
1214 $chain[] = $identifier;
1215 $previous_was_identifier = TRUE;
1217 } // end if ($previous_was_identifier)
1218 } // end if ($save_table_ref &&!$seen_end_of_table_ref)
1219 } // end if (!$seen_from)
1220 } // end if (querytype SELECT)
1221 } // end if (quote_backtick or double quote or alpha_identifier)
1223 // ===================================
1224 if ($arr[$i]['type'] == 'punct_qualifier') {
1225 // to be able to detect an identifier following another
1226 $previous_was_identifier = FALSE;
1228 } // end if (punct_qualifier)
1231 * @todo check if 3 identifiers following one another -> error
1234 // s a v e a s e l e c t e x p r
1235 // finding a list separator or FROM
1236 // means that we must save the current chain of identifiers
1237 // into a select expression
1239 // for now, we only save a select expression if it contains
1240 // at least one identifier, as we are interested in checking
1241 // the columns and table names, so in "select * from persons",
1242 // the "*" is not saved
1244 if (isset($chain) && !$seen_end_of_table_ref
1245 && ((!$seen_from && $arr[$i]['type'] == 'punct_listsep')
1246 ||
($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data == 'FROM'))) {
1247 $size_chain = count($chain);
1248 $current_select_expr++
;
1249 $subresult['select_expr'][$current_select_expr] = array(
1254 'table_true_name' => '',
1258 if (isset($alias_for_select_expr) && strlen($alias_for_select_expr)) {
1259 // we had found an alias for this select expression
1260 $subresult['select_expr'][$current_select_expr]['alias'] = $alias_for_select_expr;
1261 unset($alias_for_select_expr);
1263 // there is at least a column
1264 $subresult['select_expr'][$current_select_expr]['column'] = $chain[$size_chain - 1];
1265 $subresult['select_expr'][$current_select_expr]['expr'] = $chain[$size_chain - 1];
1268 if ($size_chain > 1) {
1269 $subresult['select_expr'][$current_select_expr]['table_name'] = $chain[$size_chain - 2];
1270 // we assume for now that this is also the true name
1271 $subresult['select_expr'][$current_select_expr]['table_true_name'] = $chain[$size_chain - 2];
1272 $subresult['select_expr'][$current_select_expr]['expr']
1273 = $subresult['select_expr'][$current_select_expr]['table_name']
1274 . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
1275 } // end if ($size_chain > 1)
1278 if ($size_chain > 2) {
1279 $subresult['select_expr'][$current_select_expr]['db'] = $chain[$size_chain - 3];
1280 $subresult['select_expr'][$current_select_expr]['expr']
1281 = $subresult['select_expr'][$current_select_expr]['db']
1282 . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
1283 } // end if ($size_chain > 2)
1287 * @todo explain this:
1289 if (($arr[$i]['type'] == 'alpha_reservedWord')
1290 && ($upper_data != 'FROM')) {
1291 $previous_was_identifier = TRUE;
1294 } // end if (save a select expr)
1297 //======================================
1298 // s a v e a t a b l e r e f
1299 //======================================
1301 // maybe we just saw the end of table refs
1302 // but the last table ref has to be saved
1303 // or we are at the last token
1304 // or we just got a reserved word
1306 * @todo there could be another query after this one
1309 if (isset($chain) && $seen_from && $save_table_ref
1310 && ($arr[$i]['type'] == 'punct_listsep'
1311 ||
($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data!="AS")
1312 ||
$seen_end_of_table_ref
1315 $size_chain = count($chain);
1316 $current_table_ref++
;
1317 $subresult['table_ref'][$current_table_ref] = array(
1321 'table_alias' => '',
1322 'table_true_name' => ''
1324 if (isset($alias_for_table_ref) && strlen($alias_for_table_ref)) {
1325 $subresult['table_ref'][$current_table_ref]['table_alias'] = $alias_for_table_ref;
1326 unset($alias_for_table_ref);
1328 $subresult['table_ref'][$current_table_ref]['table_name'] = $chain[$size_chain - 1];
1329 // we assume for now that this is also the true name
1330 $subresult['table_ref'][$current_table_ref]['table_true_name'] = $chain[$size_chain - 1];
1331 $subresult['table_ref'][$current_table_ref]['expr']
1332 = $subresult['table_ref'][$current_table_ref]['table_name'];
1334 if ($size_chain > 1) {
1335 $subresult['table_ref'][$current_table_ref]['db'] = $chain[$size_chain - 2];
1336 $subresult['table_ref'][$current_table_ref]['expr']
1337 = $subresult['table_ref'][$current_table_ref]['db']
1338 . '.' . $subresult['table_ref'][$current_table_ref]['expr'];
1339 } // end if ($size_chain > 1)
1341 // add the table alias into the whole expression
1342 $subresult['table_ref'][$current_table_ref]['expr']
1343 .= ' ' . $subresult['table_ref'][$current_table_ref]['table_alias'];
1346 $previous_was_identifier = TRUE;
1349 } // end if (save a table ref)
1352 // when we have found all table refs,
1353 // for each table_ref alias, put the true name of the table
1354 // in the corresponding select expressions
1356 if (isset($current_table_ref) && ($seen_end_of_table_ref ||
$i == $size-1) && $subresult != $subresult_empty) {
1357 for ($tr=0; $tr <= $current_table_ref; $tr++
) {
1358 $alias = $subresult['table_ref'][$tr]['table_alias'];
1359 $truename = $subresult['table_ref'][$tr]['table_true_name'];
1360 for ($se=0; $se <= $current_select_expr; $se++
) {
1361 if (isset($alias) && strlen($alias) && $subresult['select_expr'][$se]['table_true_name']
1363 $subresult['select_expr'][$se]['table_true_name']
1365 } // end if (found the alias)
1366 } // end for (select expressions)
1368 } // end for (table refs)
1369 } // end if (set the true names)
1372 // e n d i n g l o o p #1
1373 // set the $previous_was_identifier to FALSE if the current
1374 // token is not an identifier
1375 if (($arr[$i]['type'] != 'alpha_identifier')
1376 && ($arr[$i]['type'] != 'quote_double')
1377 && ($arr[$i]['type'] != 'quote_single')
1378 && ($arr[$i]['type'] != 'quote_backtick')) {
1379 $previous_was_identifier = FALSE;
1382 // however, if we are on AS, we must keep the $previous_was_identifier
1383 if (($arr[$i]['type'] == 'alpha_reservedWord')
1384 && ($upper_data == 'AS')) {
1385 $previous_was_identifier = TRUE;
1388 if (($arr[$i]['type'] == 'alpha_reservedWord')
1389 && ($upper_data =='ON' ||
$upper_data =='USING')) {
1390 $save_table_ref = FALSE;
1391 } // end if (data == ON)
1393 if (($arr[$i]['type'] == 'alpha_reservedWord')
1394 && ($upper_data =='JOIN' ||
$upper_data =='FROM')) {
1395 $save_table_ref = TRUE;
1396 } // end if (data == JOIN)
1399 * no need to check the end of table ref if we already did
1401 * @todo maybe add "&& $seen_from"
1403 if (!$seen_end_of_table_ref) {
1404 // if this is the last token, it implies that we have
1405 // seen the end of table references
1406 // Check for the end of table references
1408 // Note: if we are analyzing a GROUP_CONCAT clause,
1409 // we might find a word that seems to indicate that
1410 // we have found the end of table refs (like ORDER)
1411 // but it's a modifier of the GROUP_CONCAT so
1412 // it's not the real end of table refs
1414 ||
($arr[$i]['type'] == 'alpha_reservedWord'
1415 && !$in_group_concat
1416 && PMA_STR_binarySearchInArr($upper_data, $words_ending_table_ref, $words_ending_table_ref_cnt))) {
1417 $seen_end_of_table_ref = TRUE;
1418 // to be able to save the last table ref, but do not
1419 // set it true if we found a word like "ON" that has
1420 // already set it to false
1421 if (isset($save_table_ref) && $save_table_ref != FALSE) {
1422 $save_table_ref = TRUE;
1425 } // end if (check for end of table ref)
1426 } //end if (!$seen_end_of_table_ref)
1428 if ($seen_end_of_table_ref) {
1429 $save_table_ref = FALSE;
1432 } // end for $i (loop #1)
1436 if (isset($current_select_expr)) {
1437 for ($trace=0; $trace<=$current_select_expr; $trace++) {
1439 reset ($subresult['select_expr'][$trace]);
1440 while (list ($key, $val) = each ($subresult['select_expr'][$trace]))
1441 echo "sel expr $trace $key => $val<br />\n";
1445 if (isset($current_table_ref)) {
1446 echo "current_table_ref = " . $current_table_ref . "<br>";
1447 for ($trace=0; $trace<=$current_table_ref; $trace++) {
1450 reset ($subresult['table_ref'][$trace]);
1451 while (list ($key, $val) = each ($subresult['table_ref'][$trace]))
1452 echo "table ref $trace $key => $val<br />\n";
1456 // -------------------------------------------------------
1459 // loop #2: - queryflags
1460 // - querytype (for queries != 'SELECT')
1461 // - section_before_limit, section_after_limit
1463 // we will also need this queryflag in loop 2
1465 if (isset($current_table_ref) && $current_table_ref > -1) {
1466 $subresult['queryflags']['select_from'] = 1;
1469 $section_before_limit = '';
1470 $section_after_limit = ''; // truly the section after the limit clause
1471 $seen_reserved_word = FALSE;
1472 $seen_group = FALSE;
1473 $seen_order = FALSE;
1474 $seen_order_by = FALSE;
1475 $in_group_by = FALSE; // true when we are inside the GROUP BY clause
1476 $in_order_by = FALSE; // true when we are inside the ORDER BY clause
1477 $in_having = FALSE; // true when we are inside the HAVING clause
1478 $in_select_expr = FALSE; // true when we are inside the select expr clause
1479 $in_where = FALSE; // true when we are inside the WHERE clause
1480 $seen_limit = FALSE; // true if we have seen a LIMIT clause
1481 $in_limit = FALSE; // true when we are inside the LIMIT clause
1482 $after_limit = FALSE; // true when we are after the LIMIT clause
1483 $in_from = FALSE; // true when we are in the FROM clause
1484 $in_group_concat = FALSE;
1485 $first_reserved_word = '';
1486 $current_identifier = '';
1487 $unsorted_query = $arr['raw']; // in case there is no ORDER BY
1488 $number_of_brackets = 0;
1489 $in_subquery = false;
1491 for ($i = 0; $i < $size; $i++
) {
1492 //DEBUG echo "Loop2 <strong>" . $arr[$i]['data'] . "</strong> (" . $arr[$i]['type'] . ")<br />";
1496 // check for reserved words that will have to generate
1497 // a confirmation request later in sql.php
1501 // ALTER TABLE... DROP
1504 // this code is not used for confirmations coming from functions.js
1506 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1507 $number_of_brackets++
;
1510 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1511 $number_of_brackets--;
1512 if ($number_of_brackets == 0) {
1513 $in_subquery = false;
1517 if ($arr[$i]['type'] == 'alpha_reservedWord') {
1518 $upper_data = strtoupper($arr[$i]['data']);
1520 if ($upper_data == 'SELECT' && $number_of_brackets > 0) {
1521 $in_subquery = true;
1524 if (!$seen_reserved_word) {
1525 $first_reserved_word = $upper_data;
1526 $subresult['querytype'] = $upper_data;
1527 $seen_reserved_word = TRUE;
1529 // if the first reserved word is DROP or DELETE,
1530 // we know this is a query that needs to be confirmed
1531 if ($first_reserved_word=='DROP'
1532 ||
$first_reserved_word == 'DELETE'
1533 ||
$first_reserved_word == 'TRUNCATE') {
1534 $subresult['queryflags']['need_confirm'] = 1;
1537 if ($first_reserved_word=='SELECT'){
1538 $position_of_first_select = $i;
1542 if ($upper_data == 'DROP' && $first_reserved_word == 'ALTER') {
1543 $subresult['queryflags']['need_confirm'] = 1;
1547 if ($upper_data == 'LIMIT' && ! $in_subquery) {
1548 $section_before_limit = substr($arr['raw'], 0, $arr[$i]['pos'] - 5);
1552 $in_order_by = FALSE; // @todo maybe others to set FALSE
1555 if ($upper_data == 'PROCEDURE') {
1556 $subresult['queryflags']['procedure'] = 1;
1558 $after_limit = TRUE;
1561 * @todo set also to FALSE if we find FOR UPDATE or LOCK IN SHARE MODE
1563 if ($upper_data == 'SELECT') {
1564 $in_select_expr = TRUE;
1565 $select_expr_clause = '';
1567 if ($upper_data == 'DISTINCT' && !$in_group_concat) {
1568 $subresult['queryflags']['distinct'] = 1;
1571 if ($upper_data == 'UNION') {
1572 $subresult['queryflags']['union'] = 1;
1575 if ($upper_data == 'JOIN') {
1576 $subresult['queryflags']['join'] = 1;
1579 if ($upper_data == 'OFFSET') {
1580 $subresult['queryflags']['offset'] = 1;
1583 // if this is a real SELECT...FROM
1584 if ($upper_data == 'FROM' && isset($subresult['queryflags']['select_from']) && $subresult['queryflags']['select_from'] == 1) {
1587 $in_select_expr = FALSE;
1591 // (we could have less resetting of variables to FALSE
1592 // if we trust that the query respects the standard
1593 // MySQL order for clauses)
1595 // we use $seen_group and $seen_order because we are looking
1597 if ($upper_data == 'GROUP') {
1599 $seen_order = FALSE;
1601 $in_order_by = FALSE;
1603 $in_select_expr = FALSE;
1606 if ($upper_data == 'ORDER' && !$in_group_concat) {
1608 $seen_group = FALSE;
1610 $in_group_by = FALSE;
1612 $in_select_expr = FALSE;
1615 if ($upper_data == 'HAVING') {
1617 $having_clause = '';
1618 $seen_group = FALSE;
1619 $seen_order = FALSE;
1620 $in_group_by = FALSE;
1621 $in_order_by = FALSE;
1623 $in_select_expr = FALSE;
1627 if ($upper_data == 'WHERE') {
1630 $where_clause_identifiers = array();
1631 $seen_group = FALSE;
1632 $seen_order = FALSE;
1633 $in_group_by = FALSE;
1634 $in_order_by = FALSE;
1636 $in_select_expr = FALSE;
1640 if ($upper_data == 'BY') {
1642 $in_group_by = TRUE;
1643 $group_by_clause = '';
1646 $seen_order_by = TRUE;
1647 // Here we assume that the ORDER BY keywords took
1648 // exactly 8 characters.
1649 // We use PMA_substr() to be charset-safe; otherwise
1650 // if the table name contains accents, the unsorted
1651 // query would be missing some characters.
1652 $unsorted_query = PMA_substr($arr['raw'], 0, $arr[$i]['pos'] - 8);
1653 $in_order_by = TRUE;
1654 $order_by_clause = '';
1658 // if we find one of the words that could end the clause
1659 if (PMA_STR_binarySearchInArr($upper_data, $words_ending_clauses, $words_ending_clauses_cnt)) {
1661 $in_group_by = FALSE;
1662 $in_order_by = FALSE;
1665 $in_select_expr = FALSE;
1669 } // endif (reservedWord)
1672 // do not add a space after a function name
1674 * @todo can we combine loop 2 and loop 1? some code is repeated here...
1678 if ($arr[$i]['type'] == 'alpha_functionName') {
1680 $upper_data = strtoupper($arr[$i]['data']);
1681 if ($upper_data =='GROUP_CONCAT') {
1682 $in_group_concat = TRUE;
1683 $number_of_brackets_in_group_concat = 0;
1687 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1688 if ($in_group_concat) {
1689 $number_of_brackets_in_group_concat++
;
1692 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1693 if ($in_group_concat) {
1694 $number_of_brackets_in_group_concat--;
1695 if ($number_of_brackets_in_group_concat == 0) {
1696 $in_group_concat = FALSE;
1701 // do not add a space after an identifier if followed by a dot
1702 if ($arr[$i]['type'] == 'alpha_identifier' && $i < $size - 1 && $arr[$i +
1]['data'] == '.') {
1706 // do not add a space after a dot if followed by an identifier
1707 if ($arr[$i]['data'] == '.' && $i < $size - 1 && $arr[$i +
1]['type'] == 'alpha_identifier') {
1711 if ($in_select_expr && $upper_data != 'SELECT' && $upper_data != 'DISTINCT') {
1712 $select_expr_clause .= $arr[$i]['data'] . $sep;
1714 if ($in_from && $upper_data != 'FROM') {
1715 $from_clause .= $arr[$i]['data'] . $sep;
1717 if ($in_group_by && $upper_data != 'GROUP' && $upper_data != 'BY') {
1718 $group_by_clause .= $arr[$i]['data'] . $sep;
1720 if ($in_order_by && $upper_data != 'ORDER' && $upper_data != 'BY') {
1721 // add a space only before ASC or DESC
1722 // not around the dot between dbname and tablename
1723 if ($arr[$i]['type'] == 'alpha_reservedWord') {
1724 $order_by_clause .= $sep;
1726 $order_by_clause .= $arr[$i]['data'];
1728 if ($in_having && $upper_data != 'HAVING') {
1729 $having_clause .= $arr[$i]['data'] . $sep;
1731 if ($in_where && $upper_data != 'WHERE') {
1732 $where_clause .= $arr[$i]['data'] . $sep;
1734 if (($arr[$i]['type'] == 'quote_backtick')
1735 ||
($arr[$i]['type'] == 'alpha_identifier')) {
1736 $where_clause_identifiers[] = $arr[$i]['data'];
1740 // to grab the rest of the query after the ORDER BY clause
1741 if (isset($subresult['queryflags']['select_from'])
1742 && $subresult['queryflags']['select_from'] == 1
1745 && $upper_data != 'BY') {
1746 $unsorted_query .= $arr[$i]['data'];
1747 if ($arr[$i]['type'] != 'punct_bracket_open_round'
1748 && $arr[$i]['type'] != 'punct_bracket_close_round'
1749 && $arr[$i]['type'] != 'punct') {
1750 $unsorted_query .= $sep;
1755 if ($upper_data == 'OFFSET') {
1756 $limit_clause .= $sep;
1758 $limit_clause .= $arr[$i]['data'];
1759 if ($upper_data == 'LIMIT' ||
$upper_data == 'OFFSET') {
1760 $limit_clause .= $sep;
1763 if ($after_limit && $seen_limit) {
1764 $section_after_limit .= $arr[$i]['data'] . $sep;
1767 // clear $upper_data for next iteration
1769 } // end for $i (loop #2)
1770 if (empty($section_before_limit)) {
1771 $section_before_limit = $arr['raw'];
1774 // -----------------------------------------------------
1775 // loop #3: foreign keys and MySQL 4.1.2+ TIMESTAMP options
1776 // (for now, check only the first query)
1777 // (for now, identifiers are assumed to be backquoted)
1779 // If we find that we are dealing with a CREATE TABLE query,
1780 // we look for the next punct_bracket_open_round, which
1781 // introduces the fields list. Then, when we find a
1782 // quote_backtick, it must be a field, so we put it into
1783 // the create_table_fields array. Even if this field is
1784 // not a timestamp, it will be useful when logic has been
1785 // added for complete field attributes analysis.
1787 $seen_foreign = FALSE;
1788 $seen_references = FALSE;
1789 $seen_constraint = FALSE;
1790 $foreign_key_number = -1;
1791 $seen_create_table = FALSE;
1792 $seen_create = FALSE;
1793 $seen_alter = FALSE;
1794 $in_create_table_fields = FALSE;
1795 $brackets_level = 0;
1796 $in_timestamp_options = FALSE;
1797 $seen_default = FALSE;
1799 for ($i = 0; $i < $size; $i++
) {
1800 // DEBUG echo "Loop 3 <strong>" . $arr[$i]['data'] . "</strong> " . $arr[$i]['type'] . "<br />";
1802 if ($arr[$i]['type'] == 'alpha_reservedWord') {
1803 $upper_data = strtoupper($arr[$i]['data']);
1805 if ($upper_data == 'NOT' && $in_timestamp_options) {
1806 $create_table_fields[$current_identifier]['timestamp_not_null'] = TRUE;
1810 if ($upper_data == 'CREATE') {
1811 $seen_create = TRUE;
1814 if ($upper_data == 'ALTER') {
1818 if ($upper_data == 'TABLE' && $seen_create) {
1819 $seen_create_table = TRUE;
1820 $create_table_fields = array();
1823 if ($upper_data == 'CURRENT_TIMESTAMP') {
1824 if ($in_timestamp_options) {
1825 if ($seen_default) {
1826 $create_table_fields[$current_identifier]['default_current_timestamp'] = TRUE;
1831 if ($upper_data == 'CONSTRAINT') {
1832 $foreign_key_number++
;
1833 $seen_foreign = FALSE;
1834 $seen_references = FALSE;
1835 $seen_constraint = TRUE;
1837 if ($upper_data == 'FOREIGN') {
1838 $seen_foreign = TRUE;
1839 $seen_references = FALSE;
1840 $seen_constraint = FALSE;
1842 if ($upper_data == 'REFERENCES') {
1843 $seen_foreign = FALSE;
1844 $seen_references = TRUE;
1845 $seen_constraint = FALSE;
1851 // [ON DELETE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
1852 // [ON UPDATE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
1854 // but we set ['on_delete'] or ['on_cascade'] to
1855 // CASCADE | SET_NULL | NO_ACTION | RESTRICT
1857 // ON UPDATE CURRENT_TIMESTAMP
1859 if ($upper_data == 'ON') {
1860 if (isset($arr[$i+
1]) && $arr[$i+
1]['type'] == 'alpha_reservedWord') {
1861 $second_upper_data = strtoupper($arr[$i+
1]['data']);
1862 if ($second_upper_data == 'DELETE') {
1863 $clause = 'on_delete';
1865 if ($second_upper_data == 'UPDATE') {
1866 $clause = 'on_update';
1869 && ($arr[$i+
2]['type'] == 'alpha_reservedWord'
1871 // ugly workaround because currently, NO is not
1872 // in the list of reserved words in sqlparser.data
1873 // (we got a bug report about not being able to use
1874 // 'no' as an identifier)
1875 ||
($arr[$i+
2]['type'] == 'alpha_identifier'
1876 && strtoupper($arr[$i+
2]['data'])=='NO'))
1878 $third_upper_data = strtoupper($arr[$i+
2]['data']);
1879 if ($third_upper_data == 'CASCADE'
1880 ||
$third_upper_data == 'RESTRICT') {
1881 $value = $third_upper_data;
1882 } elseif ($third_upper_data == 'SET'
1883 ||
$third_upper_data == 'NO') {
1884 if ($arr[$i+
3]['type'] == 'alpha_reservedWord') {
1885 $value = $third_upper_data . '_' . strtoupper($arr[$i+
3]['data']);
1887 } elseif ($third_upper_data == 'CURRENT_TIMESTAMP') {
1888 if ($clause == 'on_update'
1889 && $in_timestamp_options) {
1890 $create_table_fields[$current_identifier]['on_update_current_timestamp'] = TRUE;
1891 $seen_default = FALSE;
1897 if (!empty($value)) {
1898 $foreign[$foreign_key_number][$clause] = $value;
1901 } // endif (isset($clause))
1905 } // end of reserved words analysis
1908 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1910 if ($seen_create_table && $brackets_level == 1) {
1911 $in_create_table_fields = TRUE;
1916 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1918 if ($seen_references) {
1919 $seen_references = FALSE;
1921 if ($seen_create_table && $brackets_level == 0) {
1922 $in_create_table_fields = FALSE;
1926 if (($arr[$i]['type'] == 'alpha_columnAttrib')) {
1927 $upper_data = strtoupper($arr[$i]['data']);
1928 if ($seen_create_table && $in_create_table_fields) {
1929 if ($upper_data == 'DEFAULT') {
1930 $seen_default = TRUE;
1931 $create_table_fields[$current_identifier]['default_value'] = $arr[$i +
1]['data'];
1937 * @see @todo 2005-10-16 note: the "or" part here is a workaround for a bug
1939 if (($arr[$i]['type'] == 'alpha_columnType') ||
($arr[$i]['type'] == 'alpha_functionName' && $seen_create_table)) {
1940 $upper_data = strtoupper($arr[$i]['data']);
1941 if ($seen_create_table && $in_create_table_fields && isset($current_identifier)) {
1942 $create_table_fields[$current_identifier]['type'] = $upper_data;
1943 if ($upper_data == 'TIMESTAMP') {
1944 $arr[$i]['type'] = 'alpha_columnType';
1945 $in_timestamp_options = TRUE;
1947 $in_timestamp_options = FALSE;
1948 if ($upper_data == 'CHAR') {
1949 $arr[$i]['type'] = 'alpha_columnType';
1956 if ($arr[$i]['type'] == 'quote_backtick' ||
$arr[$i]['type'] == 'alpha_identifier') {
1958 if ($arr[$i]['type'] == 'quote_backtick') {
1959 // remove backquotes
1960 $identifier = PMA_unQuote($arr[$i]['data']);
1962 $identifier = $arr[$i]['data'];
1965 if ($seen_create_table && $in_create_table_fields) {
1966 $current_identifier = $identifier;
1967 // warning: we set this one even for non TIMESTAMP type
1968 $create_table_fields[$current_identifier]['timestamp_not_null'] = FALSE;
1971 if ($seen_constraint) {
1972 $foreign[$foreign_key_number]['constraint'] = $identifier;
1975 if ($seen_foreign && $brackets_level > 0) {
1976 $foreign[$foreign_key_number]['index_list'][] = $identifier;
1979 if ($seen_references) {
1980 if ($seen_alter && $brackets_level > 0) {
1981 $foreign[$foreign_key_number]['ref_index_list'][] = $identifier;
1982 // here, the first bracket level corresponds to the
1983 // bracket of CREATE TABLE
1984 // so if we are on level 2, it must be the index list
1985 // of the foreign key REFERENCES
1986 } elseif ($brackets_level > 1) {
1987 $foreign[$foreign_key_number]['ref_index_list'][] = $identifier;
1988 } elseif ($arr[$i+
1]['type'] == 'punct_qualifier') {
1989 // identifier is `db`.`table`
1990 // the first pass will pick the db name
1991 // the next pass will pick the table name
1992 $foreign[$foreign_key_number]['ref_db_name'] = $identifier;
1994 // identifier is `table`
1995 $foreign[$foreign_key_number]['ref_table_name'] = $identifier;
1999 } // end for $i (loop #3)
2002 // Fill the $subresult array
2004 if (isset($create_table_fields)) {
2005 $subresult['create_table_fields'] = $create_table_fields;
2008 if (isset($foreign)) {
2009 $subresult['foreign_keys'] = $foreign;
2012 if (isset($select_expr_clause)) {
2013 $subresult['select_expr_clause'] = $select_expr_clause;
2015 if (isset($from_clause)) {
2016 $subresult['from_clause'] = $from_clause;
2018 if (isset($group_by_clause)) {
2019 $subresult['group_by_clause'] = $group_by_clause;
2021 if (isset($order_by_clause)) {
2022 $subresult['order_by_clause'] = $order_by_clause;
2024 if (isset($having_clause)) {
2025 $subresult['having_clause'] = $having_clause;
2027 if (isset($limit_clause)) {
2028 $subresult['limit_clause'] = $limit_clause;
2030 if (isset($where_clause)) {
2031 $subresult['where_clause'] = $where_clause;
2033 if (isset($unsorted_query) && !empty($unsorted_query)) {
2034 $subresult['unsorted_query'] = $unsorted_query;
2036 if (isset($where_clause_identifiers)) {
2037 $subresult['where_clause_identifiers'] = $where_clause_identifiers;
2040 if (isset($position_of_first_select)) {
2041 $subresult['position_of_first_select'] = $position_of_first_select;
2042 $subresult['section_before_limit'] = $section_before_limit;
2043 $subresult['section_after_limit'] = $section_after_limit;
2046 // They are naughty and didn't have a trailing semi-colon,
2047 // then still handle it properly
2048 if ($subresult['querytype'] != '') {
2049 $result[] = $subresult;
2052 } // end of the "PMA_SQP_analyze()" function
2056 * Colorizes SQL queries html formatted
2058 * @todo check why adding a "\n" after the </span> would cause extra blanks
2059 * to be displayed: SELECT p . person_name
2060 * @param array The SQL queries html formatted
2062 * @return array The colorized SQL queries
2066 function PMA_SQP_formatHtml_colorize($arr)
2068 $i = $GLOBALS['PMA_strpos']($arr['type'], '_');
2071 $class = 'syntax_' . PMA_substr($arr['type'], 0, $i) . ' ';
2074 $class .= 'syntax_' . $arr['type'];
2076 return '<span class="' . $class . '">' . htmlspecialchars($arr['data']) . '</span>';
2077 } // end of the "PMA_SQP_formatHtml_colorize()" function
2081 * Formats SQL queries to html
2083 * @param array The SQL queries
2084 * @param string mode
2085 * @param integer starting token
2086 * @param integer number of tokens to format, -1 = all
2088 * @return string The formatted SQL queries
2092 function PMA_SQP_formatHtml($arr, $mode='color', $start_token=0,
2093 $number_of_tokens=-1)
2095 //DEBUG echo 'in Format<pre>'; print_r($arr); echo '</pre>';
2096 // then check for an array
2097 if (!is_array($arr)) {
2098 return htmlspecialchars($arr);
2100 // first check for the SQL parser having hit an error
2101 if (PMA_SQP_isError()) {
2102 return htmlspecialchars($arr['raw']);
2104 // else do it properly
2107 $str = '<span class="syntax">';
2108 $html_line_break = '<br />';
2113 $html_line_break = "\n";
2118 $html_line_break = '<br />';
2122 // inner_sql is a span that exists for all cases, except query_only
2123 // of $cfg['SQP']['fmtType'] to make possible a replacement
2124 // for inline editing
2125 if ($mode!='query_only') {
2126 $str .= '<span class="inner_sql">';
2128 $close_docu_link = false;
2132 $infunction = FALSE;
2133 $space_punct_listsep = ' ';
2134 $space_punct_listsep_function_name = ' ';
2135 // $space_alpha_reserved_word = '<br />'."\n";
2136 $space_alpha_reserved_word = ' ';
2138 $keywords_with_brackets_1before = array(
2144 $keywords_with_brackets_1before_cnt = 4;
2146 $keywords_with_brackets_2before = array(
2157 // $keywords_with_brackets_2before_cnt = count($keywords_with_brackets_2before);
2158 $keywords_with_brackets_2before_cnt = 9;
2160 // These reserved words do NOT get a newline placed near them.
2161 $keywords_no_newline = array(
2176 $keywords_no_newline_cnt = 12;
2178 // These reserved words introduce a privilege list
2179 $keywords_priv_list = array(
2183 $keywords_priv_list_cnt = 2;
2185 if ($number_of_tokens == -1) {
2186 $arraysize = $arr['len'];
2188 $arraysize = $number_of_tokens;
2191 if ($arraysize >= 0) {
2195 //$typearr[3] = $arr[0]['type'];
2196 $typearr[3] = $arr[$start_token]['type'];
2199 $in_priv_list = FALSE;
2200 for ($i = $start_token; $i < $arraysize; $i++
) {
2201 // DEBUG echo "Loop format <strong>" . $arr[$i]['data'] . "</strong> " . $arr[$i]['type'] . "<br />";
2205 // array_shift($typearr);
2212 if (($i +
1) < $arraysize) {
2213 // array_push($typearr, $arr[$i + 1]['type']);
2214 $typearr[4] = $arr[$i +
1]['type'];
2216 //array_push($typearr, null);
2220 for ($j=0; $j<4; $j++
) {
2221 $typearr[$j] = $typearr[$j +
1];
2224 switch ($typearr[2]) {
2225 case 'alpha_bitfield_constant_introducer':
2229 case 'white_newline':
2232 case 'punct_bracket_open_round':
2234 $infunction = FALSE;
2235 // Make sure this array is sorted!
2236 if (($typearr[1] == 'alpha_functionName') ||
($typearr[1] == 'alpha_columnType') ||
($typearr[1] == 'punct')
2237 ||
($typearr[3] == 'digit_integer') ||
($typearr[3] == 'digit_hex') ||
($typearr[3] == 'digit_float')
2238 ||
(($typearr[0] == 'alpha_reservedWord')
2239 && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 2]['data']), $keywords_with_brackets_2before, $keywords_with_brackets_2before_cnt))
2240 ||
(($typearr[1] == 'alpha_reservedWord')
2241 && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 1]['data']), $keywords_with_brackets_1before, $keywords_with_brackets_1before_cnt))
2248 $after .= ($mode != 'query_only' ?
'<div class="syntax_indent' . $indent . '">' : ' ');
2251 case 'alpha_identifier':
2252 if (($typearr[1] == 'punct_qualifier') ||
($typearr[3] == 'punct_qualifier')) {
2256 // for example SELECT 1 somealias
2257 if ($typearr[1] == 'digit_integer') {
2260 if (($typearr[3] == 'alpha_columnType') ||
($typearr[3] == 'alpha_identifier')) {
2265 case 'punct_qualifier':
2269 case 'punct_listsep':
2270 if ($infunction == TRUE) {
2271 $after .= $space_punct_listsep_function_name;
2273 $after .= $space_punct_listsep;
2276 case 'punct_queryend':
2277 if (($typearr[3] != 'comment_mysql') && ($typearr[3] != 'comment_ansi') && $typearr[3] != 'comment_c') {
2278 $after .= $html_line_break;
2279 $after .= $html_line_break;
2281 $space_punct_listsep = ' ';
2282 $space_punct_listsep_function_name = ' ';
2283 $space_alpha_reserved_word = ' ';
2284 $in_priv_list = FALSE;
2286 case 'comment_mysql':
2287 case 'comment_ansi':
2288 $after .= $html_line_break;
2293 // select * from mytable limit 0,-1
2294 // (a side effect of this workaround is that
2299 if ($typearr[3] != 'digit_integer') {
2303 case 'punct_bracket_close_round':
2305 if ($infunction == TRUE) {
2311 $before .= ($mode != 'query_only' ?
'</div>' : ' ');
2313 $infunction = ($functionlevel > 0) ?
TRUE : FALSE;
2315 case 'alpha_columnType':
2317 switch ($arr[$i]['data']) {
2330 $before .= PMA_showMySQLDocu('data-types', 'numeric-types', false, '', true);
2331 $after = '</a>' . $after;
2338 $before .= PMA_showMySQLDocu('data-types', 'date-and-time-types', false, '', true);
2339 $after = '</a>' . $after;
2355 $before .= PMA_showMySQLDocu('data-types', 'string-types', false, '', true);
2356 $after = '</a>' . $after;
2360 if ($typearr[3] == 'alpha_columnAttrib') {
2363 if ($typearr[1] == 'alpha_columnType') {
2367 case 'alpha_columnAttrib':
2369 // ALTER TABLE tbl_name AUTO_INCREMENT = 1
2370 // COLLATE LATIN1_GENERAL_CI DEFAULT
2371 if ($typearr[1] == 'alpha_identifier' ||
$typearr[1] == 'alpha_charset') {
2374 if (($typearr[3] == 'alpha_columnAttrib') ||
($typearr[3] == 'quote_single') ||
($typearr[3] == 'digit_integer')) {
2378 // AUTO_INCREMENT = 31DEFAULT_CHARSET = utf-8
2380 if ($typearr[2] == 'alpha_columnAttrib' && $typearr[3] == 'alpha_reservedWord') {
2384 // select * from mysql.user where binary user="root"
2385 // binary is marked as alpha_columnAttrib
2386 // but should be marked as a reserved word
2387 if (strtoupper($arr[$i]['data']) == 'BINARY'
2388 && $typearr[3] == 'alpha_identifier') {
2392 case 'alpha_reservedWord':
2393 // do not uppercase the reserved word if we are calling
2394 // this function in query_only mode, because we need
2395 // the original query (otherwise we get problems with
2396 // semi-reserved words like "storage" which is legal
2397 // as an identifier name)
2399 if ($mode != 'query_only') {
2400 $arr[$i]['data'] = strtoupper($arr[$i]['data']);
2403 if ((($typearr[1] != 'alpha_reservedWord')
2404 ||
(($typearr[1] == 'alpha_reservedWord')
2405 && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 1]['data']), $keywords_no_newline, $keywords_no_newline_cnt)))
2406 && ($typearr[1] != 'punct_level_plus')
2407 && (!PMA_STR_binarySearchInArr($arr[$i]['data'], $keywords_no_newline, $keywords_no_newline_cnt))) {
2408 // do not put a space before the first token, because
2409 // we use a lot of pattern matching checking for the
2410 // first reserved word at beginning of query
2411 // so do not put a newline before
2413 // also we must not be inside a privilege list
2415 // the alpha_identifier exception is there to
2417 // GRANT SELECT ON mydb.mytable TO myuser@localhost
2418 // (else, we get mydb.mytableTO)
2420 // the quote_single exception is there to
2422 // GRANT ... TO 'marc'@'domain.com' IDENTIFIED...
2424 * @todo fix all cases and find why this happens
2427 if (!$in_priv_list ||
$typearr[1] == 'alpha_identifier' ||
$typearr[1] == 'quote_single' ||
$typearr[1] == 'white_newline') {
2428 $before .= $space_alpha_reserved_word;
2431 // on first keyword, check if it introduces a
2433 if (PMA_STR_binarySearchInArr($arr[$i]['data'], $keywords_priv_list, $keywords_priv_list_cnt)) {
2434 $in_priv_list = TRUE;
2441 switch ($arr[$i]['data']) {
2451 switch ($arr[$i +
1]['data']) {
2462 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'] . '_' . $arr[$i +
1]['data'], false, '', true);
2463 $close_docu_link = true;
2466 if ($arr[$i +
1]['data'] == 'LOGFILE' && $arr[$i +
2]['data'] == 'GROUP') {
2467 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'] . '_LOGFILE_GROUP', false, '', true);
2468 $close_docu_link = true;
2471 if (!$in_priv_list) {
2472 $space_punct_listsep = $html_line_break;
2473 $space_alpha_reserved_word = ' ';
2487 if ($close_docu_link) {
2488 $after = '</a>' . $after;
2489 $close_docu_link = false;
2493 if ($docu && ($i == 0 ||
$arr[$i - 1]['data'] != 'CHARACTER')) {
2494 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
2495 $after = '</a>' . $after;
2497 if (!$in_priv_list) {
2498 $space_punct_listsep = $html_line_break;
2499 $space_alpha_reserved_word = ' ';
2508 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
2509 $after = '</a>' . $after;
2511 if (!$in_priv_list) {
2512 $space_punct_listsep = $html_line_break;
2513 $space_alpha_reserved_word = ' ';
2519 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
2520 $after = '</a>' . $after;
2522 if (!$in_priv_list) {
2523 $space_punct_listsep = $html_line_break;
2524 $space_alpha_reserved_word = $html_line_break;
2528 $space_punct_listsep = ' ';
2529 $space_alpha_reserved_word = $html_line_break;
2533 $before .= PMA_showMySQLDocu('SQL-Syntax', 'SELECT', false, '', true);
2534 $after = '</a>' . $after;
2536 $space_punct_listsep = ' ';
2537 $space_alpha_reserved_word = $html_line_break;
2543 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
2544 $after = '</a>' . $after;
2549 } // end switch ($arr[$i]['data'])
2553 case 'digit_integer':
2557 * @todo could there be other types preceding a digit?
2559 if ($typearr[1] == 'alpha_reservedWord') {
2562 if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
2565 if ($typearr[1] == 'alpha_columnAttrib') {
2569 case 'alpha_variable':
2572 case 'quote_double':
2573 case 'quote_single':
2574 // workaround: for the query
2575 // REVOKE SELECT ON `base2\_db`.* FROM 'user'@'%'
2576 // the @ is incorrectly marked as alpha_variable
2577 // in the parser, and here, the '%' gets a blank before,
2578 // which is a syntax error
2579 if ($typearr[1] != 'punct_user' && $typearr[1] != 'alpha_bitfield_constant_introducer') {
2582 if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
2586 case 'quote_backtick':
2587 // here we check for punct_user to handle correctly
2588 // DEFINER = `username`@`%`
2589 // where @ is the punct_user and `%` is the quote_backtick
2590 if ($typearr[3] != 'punct_qualifier' && $typearr[3] != 'alpha_variable' && $typearr[3] != 'punct_user') {
2593 if ($typearr[1] != 'punct_qualifier' && $typearr[1] != 'alpha_variable' && $typearr[1] != 'punct_user') {
2599 } // end switch ($typearr[2])
2602 if ($typearr[3] != 'punct_qualifier') {
2608 if ($mode=='color') {
2609 $str .= PMA_SQP_formatHTML_colorize($arr[$i]);
2610 } elseif ($mode == 'text') {
2611 $str .= htmlspecialchars($arr[$i]['data']);
2613 $str .= $arr[$i]['data'];
2617 /* End possibly unclosed documentation link */
2618 if ($close_docu_link) {
2620 $close_docu_link = false;
2622 if ($mode!='query_only') {
2623 // close inner_sql span
2626 if ($mode=='color') {
2627 // close syntax span
2632 } // end of the "PMA_SQP_formatHtml()" function
2636 * Builds a CSS rule used for html formatted SQL queries
2638 * @param string The class name
2639 * @param string The property name
2640 * @param string The property value
2642 * @return string The CSS rule
2646 * @see PMA_SQP_buildCssData()
2648 function PMA_SQP_buildCssRule($classname, $property, $value)
2650 $str = '.' . $classname . ' {';
2652 $str .= $property . ': ' . $value . ';';
2657 } // end of the "PMA_SQP_buildCssRule()" function
2661 * Builds CSS rules used for html formatted SQL queries
2663 * @return string The CSS rules set
2667 * @global array The current PMA configuration
2669 * @see PMA_SQP_buildCssRule()
2671 function PMA_SQP_buildCssData()
2676 foreach ($cfg['SQP']['fmtColor'] AS $key => $col) {
2677 $css_string .= PMA_SQP_buildCssRule('syntax_' . $key, 'color', $col);
2680 for ($i = 0; $i < 8; $i++
) {
2681 $css_string .= PMA_SQP_buildCssRule('syntax_indent' . $i, 'margin-left', ($i * $cfg['SQP']['fmtInd']) . $cfg['SQP']['fmtIndUnit']);
2685 } // end of the "PMA_SQP_buildCssData()" function
2687 if (! defined('PMA_MINIMUM_COMMON')) {
2689 * Gets SQL queries with no format
2691 * @param array The SQL queries list
2693 * @return string The SQL queries with no format
2697 function PMA_SQP_formatNone($arr)
2699 $formatted_sql = htmlspecialchars($arr['raw']);
2700 $formatted_sql = preg_replace("@((\015\012)|(\015)|(\012)){3,}@", "\n\n", $formatted_sql);
2702 return $formatted_sql;
2703 } // end of the "PMA_SQP_formatNone()" function
2705 } // end if: minimal common.lib needed?