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 * Include the string library as we use it heavily
36 require_once './libraries/string.lib.php';
39 * Include data for the SQL Parser
41 require_once './libraries/sqlparser.data.php';
46 if (!defined('TESTSUITE')) {
47 include_once './libraries/mysql_charsets.lib.php';
49 if (! isset($mysql_charsets)) {
50 $mysql_charsets = array();
51 $mysql_collations_flat = array();
55 * Stores parsed elemented of query to array.
57 * Currently we don't need the $pos (token position in query)
58 * for other purposes than LIMIT clause verification,
59 * so many calls to this function do not include the 4th parameter
61 * @param array &$arr Array to store element
62 * @param string $type Type of element
63 * @param string $data Data (text) of element
64 * @param int &$arrsize Size of array
65 * @param int $pos Position of an element
69 function PMA_SQP_arrayAdd(&$arr, $type, $data, &$arrsize, $pos = 0)
71 $arr[] = array('type' => $type, 'data' => $data, 'pos' => $pos);
73 } // end of the "PMA_SQP_arrayAdd()" function
76 * Reset the error variable for the SQL parser
82 function PMA_SQP_resetError()
84 global $SQP_errorString;
85 $SQP_errorString = '';
86 unset($SQP_errorString);
90 * Get the contents of the error variable for the SQL parser
92 * @return string Error string from SQL parser
96 function PMA_SQP_getErrorString()
98 global $SQP_errorString;
99 return isset($SQP_errorString) ?
$SQP_errorString : '';
103 * Check if the SQL parser hit an error
105 * @return boolean error state
109 function PMA_SQP_isError()
111 global $SQP_errorString;
112 return isset($SQP_errorString) && !empty($SQP_errorString);
116 * Set an error message for the system
118 * @param string $message The error message
119 * @param string $sql The failing SQL query
124 * @scope SQL Parser internal
126 function PMA_SQP_throwError($message, $sql)
128 global $SQP_errorString;
129 $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"
131 . 'ERROR: ' . $message . "\n"
132 . 'SQL: ' . htmlspecialchars($sql) . "\n"
135 } // end of the "PMA_SQP_throwError()" function
139 * Do display the bug report
141 * @param string $message The error message
142 * @param string $sql The failing SQL query
148 function PMA_SQP_bug($message, $sql)
150 global $SQP_errorString;
151 $debugstr = 'ERROR: ' . $message . "\n";
152 $debugstr .= 'MySQL: '.PMA_MYSQL_STR_VERSION
. "\n";
153 $debugstr .= 'USR OS, AGENT, VER: ' . PMA_USR_OS
. ' ';
154 $debugstr .= PMA_USR_BROWSER_AGENT
. ' ' . PMA_USR_BROWSER_VER
. "\n";
155 $debugstr .= 'PMA: ' . PMA_VERSION
. "\n";
156 $debugstr .= 'PHP VER,OS: ' . PMA_PHP_STR_VERSION
. ' ' . PHP_OS
. "\n";
157 $debugstr .= 'LANG: ' . $GLOBALS['lang'] . "\n";
158 $debugstr .= 'SQL: ' . htmlspecialchars($sql);
160 $encodedstr = $debugstr;
161 if (@function_exists
('gzcompress')) {
162 $encodedstr = gzcompress($debugstr, 9);
164 $encodedstr = preg_replace(
165 "/(\015\012)|(\015)|(\012)/",
167 chunk_split(base64_encode($encodedstr))
171 $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:')
173 . '----' . __('BEGIN CUT') . '----' . '<br />' . "\n"
175 . '----' . __('END CUT') . '----' . '<br />' . "\n";
177 $SQP_errorString .= '----' . __('BEGIN RAW') . '----<br />' . "\n"
181 . '----' . __('END RAW') . '----<br />' . "\n";
183 } // end of the "PMA_SQP_bug()" function
187 * Parses the SQL queries
189 * @param string $sql The SQL query list
191 * @return mixed Most of times, nothing...
193 * @global array The current PMA configuration
194 * @global array MySQL column attributes
195 * @global array MySQL reserved words
196 * @global array MySQL column types
197 * @global array MySQL function names
198 * @global array List of available character sets
199 * @global array List of available collations
203 function PMA_SQP_parse($sql)
205 static $PMA_SQPdata_column_attrib, $PMA_SQPdata_reserved_word;
206 static $PMA_SQPdata_column_type;
207 static $PMA_SQPdata_function_name, $PMA_SQPdata_forbidden_word;
208 global $mysql_charsets, $mysql_collations_flat;
210 // Convert all line feeds to Unix style
211 $sql = str_replace("\r\n", "\n", $sql);
212 $sql = str_replace("\r", "\n", $sql);
214 $len = PMA_strlen($sql);
219 // Create local hashtables
220 if (!isset($PMA_SQPdata_column_attrib)) {
221 $PMA_SQPdata_column_attrib = array_flip(
222 $GLOBALS['PMA_SQPdata_column_attrib']
224 $PMA_SQPdata_function_name = array_flip(
225 $GLOBALS['PMA_SQPdata_function_name']
227 $PMA_SQPdata_reserved_word = array_flip(
228 $GLOBALS['PMA_SQPdata_reserved_word']
230 $PMA_SQPdata_forbidden_word = array_flip(
231 $GLOBALS['PMA_SQPdata_forbidden_word']
233 $PMA_SQPdata_column_type = array_flip(
234 $GLOBALS['PMA_SQPdata_column_type']
238 $sql_array = array();
239 $sql_array['raw'] = $sql;
242 $punct_queryend = ';';
243 $punct_qualifier = '.';
244 $punct_listsep = ',';
245 $punct_level_plus = '(';
246 $punct_level_minus = ')';
248 $digit_floatdecimal = '.';
250 $bracket_list = '()[]{}';
251 $allpunct_list = '-,;:!?/.^~\*&%+<=>|';
252 $allpunct_list_pair = array(
265 $quote_list = '\'"`';
268 $previous_was_space = false;
269 $this_was_space = false;
270 $previous_was_bracket = false;
271 $this_was_bracket = false;
272 $previous_was_punct = false;
273 $this_was_punct = false;
274 $previous_was_listsep = false;
275 $this_was_listsep = false;
276 $previous_was_quote = false;
277 $this_was_quote = false;
279 while ($count2 < $len) {
280 $c = PMA_substr($sql, $count2, 1);
283 $previous_was_space = $this_was_space;
284 $this_was_space = false;
285 $previous_was_bracket = $this_was_bracket;
286 $this_was_bracket = false;
287 $previous_was_punct = $this_was_punct;
288 $this_was_punct = false;
289 $previous_was_listsep = $this_was_listsep;
290 $this_was_listsep = false;
291 $previous_was_quote = $this_was_quote;
292 $this_was_quote = false;
295 $this_was_space = true;
297 PMA_SQP_arrayAdd($sql_array, 'white_newline', '', $arraysize);
301 // Checks for white space
302 if (PMA_STR_isSpace($c)) {
303 $this_was_space = true;
308 // Checks for comment lines.
312 $next_c = PMA_substr($sql, $count2 +
1, 1);
314 ||
(($count2 +
1 < $len) && ($c == '/') && ($next_c == '*'))
315 ||
(($count2 +
2 == $len) && ($c == '-') && ($next_c == '-'))
316 ||
(($count2 +
2 < $len) && ($c == '-') && ($next_c == '-') && ((PMA_substr($sql, $count2 +
2, 1) <= ' ')))
326 $pos = PMA_strpos($sql, "\n", $count2);
330 $pos = PMA_strpos($sql, '*/', $count2);
336 $count2 = ($pos < $count2) ?
$len : $pos;
337 $str = PMA_substr($sql, $count1, $count2 - $count1);
338 PMA_SQP_arrayAdd($sql_array, 'comment_' . $type, $str, $arraysize);
342 // Checks for something inside quotation marks
343 if (PMA_strpos($quote_list, $c) !== false) {
344 $startquotepos = $count2;
351 $pos = PMA_strpos(' ' . $sql, $quotetype, $oldpos +
1) - 1;
356 * Behave same as MySQL and accept end of query as end of backtick.
357 * I know this is sick, but MySQL behaves like this:
359 * SELECT * FROM `table
363 * SELECT * FROM `table`
365 $pos_quote_separator = PMA_strpos(' ' . $sql, $GLOBALS['sql_delimiter'], $oldpos +
1) - 1;
366 if ($pos_quote_separator < 0) {
369 $sql_array['raw'] .= '`';
373 $sql = PMA_substr($sql, 0, $pos_quote_separator) . '`' . PMA_substr($sql, $pos_quote_separator);
374 $sql_array['raw'] = $sql;
375 $pos = $pos_quote_separator;
377 if (class_exists('PMA_Message') && $GLOBALS['is_ajax_request'] != true) {
378 PMA_Message
::notice(__('Automatically appended backtick to the end of query!'))->display();
381 $debugstr = __('Unclosed quote') . ' @ ' . $startquotepos. "\n"
382 . 'STR: ' . htmlspecialchars($quotetype);
383 PMA_SQP_throwError($debugstr, $sql);
388 // If the quote is the first character, it can't be
389 // escaped, so don't do the rest of the code
394 // Checks for MySQL escaping using a \
395 // And checks for ANSI escaping using the $quotetype character
396 if (($pos < $len) && PMA_STR_charIsEscaped($sql, $pos) && $c != '`') {
399 } elseif (($pos +
1 < $len) && (PMA_substr($sql, $pos, 1) == $quotetype) && (PMA_substr($sql, $pos +
1, 1) == $quotetype)) {
405 } while ($len > $pos); // end do
410 switch ($quotetype) {
413 $this_was_quote = true;
417 $this_was_quote = true;
421 $this_was_quote = true;
426 $data = PMA_substr($sql, $count1, $count2 - $count1);
427 PMA_SQP_arrayAdd($sql_array, $type, $data, $arraysize);
431 // Checks for brackets
432 if (PMA_strpos($bracket_list, $c) !== false) {
433 // All bracket tokens are only one item long
434 $this_was_bracket = true;
437 if (PMA_strpos('([{', $c) !== false) {
440 $type_type = 'close';
444 if (PMA_strpos('()', $c) !== false) {
445 $type_style = 'round';
446 } elseif (PMA_strpos('[]', $c) !== false) {
447 $type_style = 'square';
449 $type_style = 'curly';
452 $type = 'punct_bracket_' . $type_type . '_' . $type_style;
453 PMA_SQP_arrayAdd($sql_array, $type, $c, $arraysize);
459 var_dump(PMA_STR_isSqlIdentifier($c, false));
462 var_dump(PMA_STR_isDigit(PMA_substr($sql, $count2 + 1, 1)));
463 var_dump($previous_was_space);
464 var_dump($previous_was_bracket);
465 var_dump($previous_was_listsep);
469 // Checks for identifier (alpha or numeric)
470 if (PMA_STR_isSqlIdentifier($c, false)
473 && PMA_STR_isDigit(PMA_substr($sql, $count2 +
1, 1))
474 && ($previous_was_space ||
$previous_was_bracket ||
$previous_was_listsep))
477 echo PMA_substr($sql, $count2);
484 * @todo a @ can also be present in expressions like
485 * FROM 'user'@'%' or TO 'user'@'%'
486 * in this case, the @ is wrongly marked as alpha_variable
488 $is_identifier = $previous_was_punct;
489 $is_sql_variable = $c == '@' && ! $previous_was_quote;
490 $is_user = $c == '@' && $previous_was_quote;
491 $is_digit = !$is_identifier && !$is_sql_variable && PMA_STR_isDigit($c);
492 $is_hex_digit = $is_digit && $c == '0' && $count2 < $len && PMA_substr($sql, $count2, 1) == 'x';
493 $is_float_digit = $c == '.';
494 $is_float_digit_exponent = false;
498 var_dump($is_identifier);
499 var_dump($is_sql_variable);
501 var_dump($is_float_digit);
505 // Fast skip is especially needed for huge BLOB data
508 $pos = strspn($sql, '0123456789abcdefABCDEF', $count2);
509 if ($pos > $count2) {
513 } elseif ($is_digit) {
514 $pos = strspn($sql, '0123456789', $count2);
515 if ($pos > $count2) {
521 while (($count2 < $len) && PMA_STR_isSqlIdentifier(PMA_substr($sql, $count2, 1), ($is_sql_variable ||
$is_digit))) {
522 $c2 = PMA_substr($sql, $count2, 1);
523 if ($is_sql_variable && ($c2 == '.')) {
527 if ($is_digit && (!$is_hex_digit) && ($c2 == '.')) {
529 if (!$is_float_digit) {
530 $is_float_digit = true;
533 $debugstr = __('Invalid Identifer') . ' @ ' . ($count1+
1) . "\n"
534 . 'STR: ' . htmlspecialchars(PMA_substr($sql, $count1, $count2 - $count1));
535 PMA_SQP_throwError($debugstr, $sql);
539 if ($is_digit && (!$is_hex_digit) && (($c2 == 'e') ||
($c2 == 'E'))) {
540 if (!$is_float_digit_exponent) {
541 $is_float_digit_exponent = true;
542 $is_float_digit = true;
547 $is_float_digit = false;
550 if (($is_hex_digit && PMA_STR_isHexDigit($c2)) ||
($is_digit && PMA_STR_isDigit($c2))) {
555 $is_hex_digit = false;
561 $l = $count2 - $count1;
562 $str = PMA_substr($sql, $count1, $l);
565 if ($is_digit ||
$is_float_digit ||
$is_hex_digit) {
567 if ($is_float_digit) {
569 } elseif ($is_hex_digit) {
574 } elseif ($is_user) {
575 $type = 'punct_user';
576 } elseif ($is_sql_variable != false) {
577 $type = 'alpha_variable';
580 } // end if... else....
581 PMA_SQP_arrayAdd($sql_array, $type, $str, $arraysize, $count2);
587 if (PMA_strpos($allpunct_list, $c) !== false) {
588 while (($count2 < $len) && PMA_strpos($allpunct_list, PMA_substr($sql, $count2, 1)) !== false) {
591 $l = $count2 - $count1;
595 $punct_data = PMA_substr($sql, $count1, $l);
598 // Special case, sometimes, althought two characters are
599 // adjectent directly, they ACTUALLY need to be seperate
603 var_dump($punct_data);
609 switch ($punct_data) {
610 case $punct_queryend:
611 $t_suffix = '_queryend';
613 case $punct_qualifier:
614 $t_suffix = '_qualifier';
615 $this_was_punct = true;
618 $this_was_listsep = true;
619 $t_suffix = '_listsep';
624 PMA_SQP_arrayAdd($sql_array, 'punct' . $t_suffix, $punct_data, $arraysize);
625 } elseif ($punct_data == $GLOBALS['sql_delimiter'] ||
isset($allpunct_list_pair[$punct_data])) {
626 // Ok, we have one of the valid combined punct expressions
627 PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize);
629 // Bad luck, lets split it up more
630 $first = $punct_data[0];
631 $last2 = $punct_data[$l - 2] . $punct_data[$l - 1];
632 $last = $punct_data[$l - 1];
633 if (($first == ',') ||
($first == ';') ||
($first == '.') ||
($first == '*')) {
634 $count2 = $count1 +
1;
635 $punct_data = $first;
636 } elseif (($last2 == '/*') ||
(($last2 == '--') && ($count2 == $len ||
PMA_substr($sql, $count2, 1) <= ' '))) {
638 $punct_data = PMA_substr($sql, $count1, $count2 - $count1);
639 } elseif (($last == '-') ||
($last == '+') ||
($last == '!')) {
641 $punct_data = PMA_substr($sql, $count1, $count2 - $count1);
642 } elseif ($last != '~') {
644 * @todo for negation operator, split in 2 tokens ?
645 * "select x&~1 from t"
646 * becomes "select x & ~ 1 from t" ?
648 $debugstr = __('Unknown Punctuation String') . ' @ ' . ($count1+
1) . "\n"
649 . 'STR: ' . htmlspecialchars($punct_data);
650 PMA_SQP_throwError($debugstr, $sql);
653 PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize);
655 } // end if... elseif... else
662 $debugstr = 'C1 C2 LEN: ' . $count1 . ' ' . $count2 . ' ' . $len . "\n"
663 . 'STR: ' . PMA_substr($sql, $count1, $count2 - $count1) . "\n";
664 PMA_SQP_bug($debugstr, $sql);
667 } // end while ($count2 < $len)
675 if ($arraysize > 0) {
676 $t_next = $sql_array[0]['type'];
680 $d_next = $sql_array[0]['data'];
684 $d_next_upper = $t_next == 'alpha' ?
strtoupper($d_next) : $d_next;
686 $d_bef_prev_upper = '';
690 for ($i = 0; $i < $arraysize; $i++
) {
691 $t_bef_prev = $t_prev;
694 $d_bef_prev = $d_prev;
697 $d_bef_prev_upper = $d_prev_upper;
698 $d_prev_upper = $d_cur_upper;
699 $d_cur_upper = $d_next_upper;
700 if (($i +
1) < $arraysize) {
701 $t_next = $sql_array[$i +
1]['type'];
702 $d_next = $sql_array[$i +
1]['data'];
703 $d_next_upper = $t_next == 'alpha' ?
strtoupper($d_next) : $d_next;
710 //DEBUG echo "[prev: <strong>".$d_prev."</strong> ".$t_prev."][cur: <strong>".$d_cur."</strong> ".$t_cur."][next: <strong>".$d_next."</strong> ".$t_next."]<br />";
712 if ($t_cur == 'alpha') {
713 $t_suffix = '_identifier';
714 // for example: `thebit` bit(8) NOT NULL DEFAULT b'0'
715 if ($t_prev == 'alpha' && $d_prev == 'DEFAULT' && $d_cur == 'b' && $t_next == 'quote_single') {
716 $t_suffix = '_bitfield_constant_introducer';
717 } elseif (($t_next == 'punct_qualifier') ||
($t_prev == 'punct_qualifier')) {
718 $t_suffix = '_identifier';
719 } elseif (($t_next == 'punct_bracket_open_round')
720 && isset($PMA_SQPdata_function_name[$d_cur_upper])) {
722 * @todo 2005-10-16: in the case of a CREATE TABLE containing
723 * a TIMESTAMP, since TIMESTAMP() is also a function, it's
724 * found here and the token is wrongly marked as alpha_functionName.
725 * But we compensate for this when analysing for timestamp_not_null
726 * later in this script.
728 * Same applies to CHAR vs. CHAR() function.
730 $t_suffix = '_functionName';
731 /* There are functions which might be as well column types */
732 } elseif (isset($PMA_SQPdata_column_type[$d_cur_upper])) {
733 $t_suffix = '_columnType';
736 * Temporary fix for bugs #621357 and #2027720
738 * @todo FIX PROPERLY NEEDS OVERHAUL OF SQL TOKENIZER
740 if (($d_cur_upper == 'SET' ||
$d_cur_upper == 'BINARY') && $t_next != 'punct_bracket_open_round') {
741 $t_suffix = '_reservedWord';
743 //END OF TEMPORARY FIX
745 // CHARACTER is a synonym for CHAR, but can also be meant as
746 // CHARACTER SET. In this case, we have a reserved word.
747 if ($d_cur_upper == 'CHARACTER' && $d_next_upper == 'SET') {
748 $t_suffix = '_reservedWord';
752 // current is a column type, so previous must not be
753 // a reserved word but an identifier
754 // CREATE TABLE SG_Persons (first varchar(64))
756 //if ($sql_array[$i-1]['type'] =='alpha_reservedWord') {
757 // $sql_array[$i-1]['type'] = 'alpha_identifier';
760 } elseif (isset($PMA_SQPdata_reserved_word[$d_cur_upper])) {
761 $t_suffix = '_reservedWord';
762 } elseif (isset($PMA_SQPdata_column_attrib[$d_cur_upper])) {
763 $t_suffix = '_columnAttrib';
764 // INNODB is a MySQL table type, but in "SHOW INNODB STATUS",
765 // it should be regarded as a reserved word.
766 if ($d_cur_upper == 'INNODB'
767 && $d_prev_upper == 'SHOW'
768 && $d_next_upper == 'STATUS'
770 $t_suffix = '_reservedWord';
773 if ($d_cur_upper == 'DEFAULT' && $d_next_upper == 'CHARACTER') {
774 $t_suffix = '_reservedWord';
776 // Binary as character set
777 if ($d_cur_upper == 'BINARY'
778 && (($d_bef_prev_upper == 'CHARACTER' && $d_prev_upper == 'SET')
779 ||
($d_bef_prev_upper == 'SET' && $d_prev_upper == '=')
780 ||
($d_bef_prev_upper == 'CHARSET' && $d_prev_upper == '=')
781 ||
$d_prev_upper == 'CHARSET')
782 && in_array($d_cur, $mysql_charsets)
784 $t_suffix = '_charset';
786 } elseif (in_array($d_cur, $mysql_charsets)
787 ||
in_array($d_cur, $mysql_collations_flat)
788 ||
($d_cur{0} == '_' && in_array(substr($d_cur, 1), $mysql_charsets))) {
789 $t_suffix = '_charset';
793 // check if present in the list of forbidden words
794 if ($t_suffix == '_reservedWord'
795 && isset($PMA_SQPdata_forbidden_word[$d_cur_upper])
797 $sql_array[$i]['forbidden'] = true;
799 $sql_array[$i]['forbidden'] = false;
801 $sql_array[$i]['type'] .= $t_suffix;
805 // Stores the size of the array inside the array, as count() is a slow
807 $sql_array['len'] = $arraysize;
809 // DEBUG echo 'After parsing<pre>'; print_r($sql_array); echo '</pre>';
810 // Sends the data back
812 } // end of the "PMA_SQP_parse()" function
815 * Checks for token types being what we want...
817 * @param string $toCheck String of type that we have
818 * @param string $whatWeWant String of type that we want
820 * @return boolean result of check
824 function PMA_SQP_typeCheck($toCheck, $whatWeWant)
826 $typeSeparator = '_';
827 if (strcmp($whatWeWant, $toCheck) == 0) {
830 if (strpos($whatWeWant, $typeSeparator) === false) {
832 $whatWeWant, $toCheck,
833 strpos($toCheck, $typeSeparator)
843 * Analyzes SQL queries
845 * @param array $arr The SQL queries
847 * @return array The analyzed SQL queries
851 function PMA_SQP_analyze($arr)
853 if ($arr == array() ||
! isset($arr['len'])) {
860 'select_expr_clause'=> '', // the whole stuff between SELECT and FROM , except DISTINCT
861 'position_of_first_select' => '', // the array index
863 'group_by_clause'=> '',
864 'order_by_clause'=> '',
865 'having_clause' => '',
866 'limit_clause' => '',
867 'where_clause' => '',
868 'where_clause_identifiers' => array(),
869 'unsorted_query' => '',
870 'queryflags' => array(),
871 'select_expr' => array(),
872 'table_ref' => array(),
873 'foreign_keys' => array(),
874 'create_table_fields' => array()
876 $subresult_empty = $subresult;
877 $seek_queryend = false;
878 $seen_end_of_table_ref = false;
879 $number_of_brackets_in_extract = 0;
880 $number_of_brackets_in_group_concat = 0;
882 $number_of_brackets = 0;
883 $in_subquery = false;
884 $seen_subquery = false;
887 // for SELECT EXTRACT(YEAR_MONTH FROM CURDATE())
888 // we must not use CURDATE as a table_ref
889 // so we track whether we are in the EXTRACT()
892 // for GROUP_CONCAT(...)
893 $in_group_concat = false;
895 /* Description of analyzer results
897 * db, table, column, alias
898 * ------------------------
900 * Inside the $subresult array, we create ['select_expr'] and ['table_ref']
903 * The SELECT syntax (simplified) is
906 * select_expression,...
907 * [FROM [table_references]
910 * ['select_expr'] is filled with each expression, the key represents the
911 * expression position in the list (0-based) (so we don't lose track of
912 * multiple occurences of the same column).
914 * ['table_ref'] is filled with each table ref, same thing for the key.
916 * I create all sub-values empty, even if they are
917 * not present (for example no select_expression alias).
919 * There is a debug section at the end of loop #1, if you want to
920 * see the exact contents of select_expr and table_ref
925 * In $subresult, array 'queryflags' is filled, according to what we
928 * Currently, those are generated:
930 * ['queryflags']['need_confirm'] = 1; if the query needs confirmation
931 * ['queryflags']['select_from'] = 1; if this is a real SELECT...FROM
932 * ['queryflags']['distinct'] = 1; for a DISTINCT
933 * ['queryflags']['union'] = 1; for a UNION
934 * ['queryflags']['join'] = 1; for a JOIN
935 * ['queryflags']['offset'] = 1; for the presence of OFFSET
936 * ['queryflags']['procedure'] = 1; for the presence of PROCEDURE
941 * The select is splitted in those clauses:
942 * ['select_expr_clause']
944 * ['group_by_clause']
945 * ['order_by_clause']
950 * The identifiers of the WHERE clause are put into the array
951 * ['where_clause_identifier']
953 * For a SELECT, the whole query without the ORDER BY clause is put into
958 * The CREATE TABLE may contain FOREIGN KEY clauses, so they get
959 * analyzed and ['foreign_keys'] is an array filled with
960 * the constraint name, the index list,
961 * the REFERENCES table name and REFERENCES index list,
962 * and ON UPDATE | ON DELETE clauses
964 * position_of_first_select
965 * ------------------------
967 * The array index of the first SELECT we find. Will be used to
968 * insert a SQL_CALC_FOUND_ROWS.
970 * create_table_fields
971 * -------------------
973 * Used to detect the DEFAULT CURRENT_TIMESTAMP and
974 * ON UPDATE CURRENT_TIMESTAMP clauses of the CREATE TABLE query.
975 * Also used to store the default value of the field.
976 * An array, each element is the identifier name.
977 * Note that for now, the timestamp_not_null element is created
978 * even for non-TIMESTAMP fields.
980 * Sub-elements: ['type'] which contains the column type
981 * optional (currently they are never false but can be absent):
982 * ['default_current_timestamp'] boolean
983 * ['on_update_current_timestamp'] boolean
984 * ['timestamp_not_null'] boolean
986 * section_before_limit, section_after_limit
987 * -----------------------------------------
989 * Marks the point of the query where we can insert a LIMIT clause;
990 * so the section_before_limit will contain the left part before
991 * a possible LIMIT clause
994 * End of description of analyzer results
998 // TODO: current logic checks for only one word, so I put only the
999 // first word of the reserved expressions that end a table ref;
1000 // maybe this is not ok (the first word might mean something else)
1001 // $words_ending_table_ref = array(
1006 // 'LOCK IN SHARE MODE',
1012 $words_ending_table_ref = array(
1024 $words_ending_clauses = array(
1032 $supported_query_types = array(
1035 // Support for these additional query types will come later on.
1050 // loop #1 for each token: select_expr, table_ref for SELECT
1052 for ($i = 0; $i < $size; $i++
) {
1053 //DEBUG echo "Loop1 <strong>" . $arr[$i]['data']
1054 //. "</strong> (" . $arr[$i]['type'] . ")<br />";
1056 // High speed seek for locating the end of the current query
1057 if ($seek_queryend == true) {
1058 if ($arr[$i]['type'] == 'punct_queryend') {
1059 $seek_queryend = false;
1062 } // end if (type == punct_queryend)
1063 } // end if ($seek_queryend)
1066 * Note: do not split if this is a punct_queryend for the first and only
1068 * @todo when we find a UNION, should we split in another subresult?
1070 if ($arr[$i]['type'] == 'punct_queryend' && ($i +
1 != $size)) {
1071 $result[] = $subresult;
1072 $subresult = $subresult_empty;
1074 } // end if (type == punct_queryend)
1076 // ==============================================================
1077 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1078 $number_of_brackets++
;
1080 $number_of_brackets_in_extract++
;
1082 if ($in_group_concat) {
1083 $number_of_brackets_in_group_concat++
;
1086 // ==============================================================
1087 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1088 $number_of_brackets--;
1089 if ($number_of_brackets == 0) {
1090 $in_subquery = false;
1093 $number_of_brackets_in_extract--;
1094 if ($number_of_brackets_in_extract == 0) {
1095 $in_extract = false;
1098 if ($in_group_concat) {
1099 $number_of_brackets_in_group_concat--;
1100 if ($number_of_brackets_in_group_concat == 0) {
1101 $in_group_concat = false;
1108 * skip the subquery to avoid setting
1109 * select_expr or table_ref with the contents
1110 * of this subquery; this is to avoid a bug when
1111 * trying to edit the results of
1112 * select * from child where not exists (select id from
1113 * parent where child.parent_id = parent.id);
1117 // ==============================================================
1118 if ($arr[$i]['type'] == 'alpha_functionName') {
1119 $upper_data = strtoupper($arr[$i]['data']);
1120 if ($upper_data =='EXTRACT') {
1122 $number_of_brackets_in_extract = 0;
1124 if ($upper_data =='GROUP_CONCAT') {
1125 $in_group_concat = true;
1126 $number_of_brackets_in_group_concat = 0;
1130 // ==============================================================
1131 if ($arr[$i]['type'] == 'alpha_reservedWord') {
1132 // We don't know what type of query yet, so run this
1133 if ($subresult['querytype'] == '') {
1134 $subresult['querytype'] = strtoupper($arr[$i]['data']);
1135 } // end if (querytype was empty)
1137 // Check if we support this type of query
1138 if (!isset($supported_query_types[$subresult['querytype']])) {
1139 // Skip ahead to the next one if we don't
1140 $seek_queryend = true;
1142 } // end if (query not supported)
1145 $upper_data = strtoupper($arr[$i]['data']);
1147 * @todo reset for each query?
1150 if ($upper_data == 'SELECT') {
1151 if ($number_of_brackets > 0) {
1152 $in_subquery = true;
1153 $seen_subquery = true;
1154 // this is a subquery so do not analyze inside it
1158 $previous_was_identifier = false;
1159 $current_select_expr = -1;
1160 $seen_end_of_table_ref = false;
1161 } // end if (data == SELECT)
1163 if ($upper_data =='FROM' && !$in_extract) {
1164 $current_table_ref = -1;
1166 $previous_was_identifier = false;
1167 $save_table_ref = true;
1168 } // end if (data == FROM)
1170 // here, do not 'continue' the loop, as we have more work for
1171 // reserved words below
1172 } // end if (type == alpha_reservedWord)
1174 // ==============================
1175 if ($arr[$i]['type'] == 'quote_backtick'
1176 ||
$arr[$i]['type'] == 'quote_double'
1177 ||
$arr[$i]['type'] == 'quote_single'
1178 ||
$arr[$i]['type'] == 'alpha_identifier'
1179 ||
($arr[$i]['type'] == 'alpha_reservedWord'
1180 && $arr[$i]['forbidden'] == false)
1182 switch ($arr[$i]['type']) {
1183 case 'alpha_identifier':
1184 case 'alpha_reservedWord':
1186 * this is not a real reservedWord, because it's not
1187 * present in the list of forbidden words, for example
1188 * "storage" which can be used as an identifier
1190 * @todo avoid the pretty printing in color in this case
1192 $identifier = $arr[$i]['data'];
1195 case 'quote_backtick':
1196 case 'quote_double':
1197 case 'quote_single':
1198 $identifier = PMA_Util
::unQuote($arr[$i]['data']);
1202 if ($subresult['querytype'] == 'SELECT'
1203 && ! $in_group_concat
1204 && ! ($seen_subquery && $arr[$i - 1]['type'] == 'punct_bracket_close_round')
1207 if ($previous_was_identifier && isset($chain)) {
1208 // found alias for this select_expr, save it
1209 // but only if we got something in $chain
1210 // (for example, SELECT COUNT(*) AS cnt
1211 // puts nothing in $chain, so we avoid
1212 // setting the alias)
1213 $alias_for_select_expr = $identifier;
1215 $chain[] = $identifier;
1216 $previous_was_identifier = true;
1218 } // end if !$previous_was_identifier
1221 if ($save_table_ref && !$seen_end_of_table_ref) {
1222 if ($previous_was_identifier) {
1223 // found alias for table ref
1224 // save it for later
1225 $alias_for_table_ref = $identifier;
1227 $chain[] = $identifier;
1228 $previous_was_identifier = true;
1230 } // end if ($previous_was_identifier)
1231 } // end if ($save_table_ref &&!$seen_end_of_table_ref)
1232 } // end if (!$seen_from)
1233 } // end if (querytype SELECT)
1234 } // end if (quote_backtick or double quote or alpha_identifier)
1236 // ===================================
1237 if ($arr[$i]['type'] == 'punct_qualifier') {
1238 // to be able to detect an identifier following another
1239 $previous_was_identifier = false;
1241 } // end if (punct_qualifier)
1244 * @todo check if 3 identifiers following one another -> error
1247 // s a v e a s e l e c t e x p r
1248 // finding a list separator or FROM
1249 // means that we must save the current chain of identifiers
1250 // into a select expression
1252 // for now, we only save a select expression if it contains
1253 // at least one identifier, as we are interested in checking
1254 // the columns and table names, so in "select * from persons",
1255 // the "*" is not saved
1257 if (isset($chain) && !$seen_end_of_table_ref
1258 && ((!$seen_from && $arr[$i]['type'] == 'punct_listsep')
1259 ||
($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data == 'FROM'))
1261 $size_chain = count($chain);
1262 $current_select_expr++
;
1263 $subresult['select_expr'][$current_select_expr] = array(
1268 'table_true_name' => '',
1272 if (isset($alias_for_select_expr) && strlen($alias_for_select_expr)) {
1273 // we had found an alias for this select expression
1274 $subresult['select_expr'][$current_select_expr]['alias'] = $alias_for_select_expr;
1275 unset($alias_for_select_expr);
1277 // there is at least a column
1278 $subresult['select_expr'][$current_select_expr]['column'] = $chain[$size_chain - 1];
1279 $subresult['select_expr'][$current_select_expr]['expr'] = $chain[$size_chain - 1];
1282 if ($size_chain > 1) {
1283 $subresult['select_expr'][$current_select_expr]['table_name'] = $chain[$size_chain - 2];
1284 // we assume for now that this is also the true name
1285 $subresult['select_expr'][$current_select_expr]['table_true_name'] = $chain[$size_chain - 2];
1286 $subresult['select_expr'][$current_select_expr]['expr']
1287 = $subresult['select_expr'][$current_select_expr]['table_name']
1288 . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
1289 } // end if ($size_chain > 1)
1292 if ($size_chain > 2) {
1293 $subresult['select_expr'][$current_select_expr]['db'] = $chain[$size_chain - 3];
1294 $subresult['select_expr'][$current_select_expr]['expr']
1295 = $subresult['select_expr'][$current_select_expr]['db']
1296 . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
1297 } // end if ($size_chain > 2)
1301 * @todo explain this:
1303 if (($arr[$i]['type'] == 'alpha_reservedWord')
1304 && ($upper_data != 'FROM')
1306 $previous_was_identifier = true;
1309 } // end if (save a select expr)
1312 //======================================
1313 // s a v e a t a b l e r e f
1314 //======================================
1316 // maybe we just saw the end of table refs
1317 // but the last table ref has to be saved
1318 // or we are at the last token
1319 // or we just got a reserved word
1321 * @todo there could be another query after this one
1324 if (isset($chain) && $seen_from && $save_table_ref
1325 && ($arr[$i]['type'] == 'punct_listsep'
1326 ||
($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data != "AS")
1327 ||
$seen_end_of_table_ref
1331 $size_chain = count($chain);
1332 $current_table_ref++
;
1333 $subresult['table_ref'][$current_table_ref] = array(
1337 'table_alias' => '',
1338 'table_true_name' => ''
1340 if (isset($alias_for_table_ref) && strlen($alias_for_table_ref)) {
1341 $subresult['table_ref'][$current_table_ref]['table_alias'] = $alias_for_table_ref;
1342 unset($alias_for_table_ref);
1344 $subresult['table_ref'][$current_table_ref]['table_name'] = $chain[$size_chain - 1];
1345 // we assume for now that this is also the true name
1346 $subresult['table_ref'][$current_table_ref]['table_true_name'] = $chain[$size_chain - 1];
1347 $subresult['table_ref'][$current_table_ref]['expr']
1348 = $subresult['table_ref'][$current_table_ref]['table_name'];
1350 if ($size_chain > 1) {
1351 $subresult['table_ref'][$current_table_ref]['db'] = $chain[$size_chain - 2];
1352 $subresult['table_ref'][$current_table_ref]['expr']
1353 = $subresult['table_ref'][$current_table_ref]['db']
1354 . '.' . $subresult['table_ref'][$current_table_ref]['expr'];
1355 } // end if ($size_chain > 1)
1357 // add the table alias into the whole expression
1358 $subresult['table_ref'][$current_table_ref]['expr']
1359 .= ' ' . $subresult['table_ref'][$current_table_ref]['table_alias'];
1362 $previous_was_identifier = true;
1365 } // end if (save a table ref)
1368 // when we have found all table refs,
1369 // for each table_ref alias, put the true name of the table
1370 // in the corresponding select expressions
1372 if (isset($current_table_ref)
1373 && ($seen_end_of_table_ref ||
$i == $size-1)
1374 && $subresult != $subresult_empty
1376 for ($tr=0; $tr <= $current_table_ref; $tr++
) {
1377 $alias = $subresult['table_ref'][$tr]['table_alias'];
1378 $truename = $subresult['table_ref'][$tr]['table_true_name'];
1379 for ($se=0; $se <= $current_select_expr; $se++
) {
1382 && $subresult['select_expr'][$se]['table_true_name'] == $alias
1384 $subresult['select_expr'][$se]['table_true_name'] = $truename;
1385 } // end if (found the alias)
1386 } // end for (select expressions)
1388 } // end for (table refs)
1389 } // end if (set the true names)
1392 // e n d i n g l o o p #1
1393 // set the $previous_was_identifier to false if the current
1394 // token is not an identifier
1395 if (($arr[$i]['type'] != 'alpha_identifier')
1396 && ($arr[$i]['type'] != 'quote_double')
1397 && ($arr[$i]['type'] != 'quote_single')
1398 && ($arr[$i]['type'] != 'quote_backtick')
1400 $previous_was_identifier = false;
1403 // however, if we are on AS, we must keep the $previous_was_identifier
1404 if (($arr[$i]['type'] == 'alpha_reservedWord')
1405 && ($upper_data == 'AS')
1407 $previous_was_identifier = true;
1410 if (($arr[$i]['type'] == 'alpha_reservedWord')
1411 && ($upper_data =='ON' ||
$upper_data =='USING')
1413 $save_table_ref = false;
1414 } // end if (data == ON)
1416 if (($arr[$i]['type'] == 'alpha_reservedWord')
1417 && ($upper_data =='JOIN' ||
$upper_data =='FROM')
1419 $save_table_ref = true;
1420 } // end if (data == JOIN)
1423 * no need to check the end of table ref if we already did
1425 * @todo maybe add "&& $seen_from"
1427 if (!$seen_end_of_table_ref) {
1428 // if this is the last token, it implies that we have
1429 // seen the end of table references
1430 // Check for the end of table references
1432 // Note: if we are analyzing a GROUP_CONCAT clause,
1433 // we might find a word that seems to indicate that
1434 // we have found the end of table refs (like ORDER)
1435 // but it's a modifier of the GROUP_CONCAT so
1436 // it's not the real end of table refs
1438 ||
($arr[$i]['type'] == 'alpha_reservedWord'
1439 && !$in_group_concat
1440 && isset($words_ending_table_ref[$upper_data]))
1442 $seen_end_of_table_ref = true;
1443 // to be able to save the last table ref, but do not
1444 // set it true if we found a word like "ON" that has
1445 // already set it to false
1446 if (isset($save_table_ref) && $save_table_ref != false) {
1447 $save_table_ref = true;
1450 } // end if (check for end of table ref)
1451 } //end if (!$seen_end_of_table_ref)
1453 if ($seen_end_of_table_ref) {
1454 $save_table_ref = false;
1457 } // end for $i (loop #1)
1461 if (isset($current_select_expr)) {
1462 for ($trace=0; $trace<=$current_select_expr; $trace++) {
1464 reset ($subresult['select_expr'][$trace]);
1465 while (list ($key, $val) = each ($subresult['select_expr'][$trace]))
1466 echo "sel expr $trace $key => $val<br />\n";
1470 if (isset($current_table_ref)) {
1471 echo "current_table_ref = " . $current_table_ref . "<br>";
1472 for ($trace=0; $trace<=$current_table_ref; $trace++) {
1475 reset ($subresult['table_ref'][$trace]);
1476 while (list ($key, $val) = each ($subresult['table_ref'][$trace]))
1477 echo "table ref $trace $key => $val<br />\n";
1481 // -------------------------------------------------------
1484 // loop #2: - queryflags
1485 // - querytype (for queries != 'SELECT')
1486 // - section_before_limit, section_after_limit
1488 // we will also need this queryflag in loop 2
1490 if (isset($current_table_ref) && $current_table_ref > -1) {
1491 $subresult['queryflags']['select_from'] = 1;
1494 $section_before_limit = '';
1495 $section_after_limit = ''; // truly the section after the limit clause
1496 $seen_reserved_word = false;
1497 $seen_group = false;
1498 $seen_order = false;
1499 $seen_order_by = false;
1500 $in_group_by = false; // true when we are inside the GROUP BY clause
1501 $in_order_by = false; // true when we are inside the ORDER BY clause
1502 $in_having = false; // true when we are inside the HAVING clause
1503 $in_select_expr = false; // true when we are inside the select expr clause
1504 $in_where = false; // true when we are inside the WHERE clause
1505 $seen_limit = false; // true if we have seen a LIMIT clause
1506 $in_limit = false; // true when we are inside the LIMIT clause
1507 $after_limit = false; // true when we are after the LIMIT clause
1508 $in_from = false; // true when we are in the FROM clause
1509 $in_group_concat = false;
1510 $first_reserved_word = '';
1511 $current_identifier = '';
1512 $unsorted_query = $arr['raw']; // in case there is no ORDER BY
1513 $number_of_brackets = 0;
1514 $in_subquery = false;
1516 for ($i = 0; $i < $size; $i++
) {
1517 //DEBUG echo "Loop2 <strong>" . $arr[$i]['data']
1518 //. "</strong> (" . $arr[$i]['type'] . ")<br />";
1522 // check for reserved words that will have to generate
1523 // a confirmation request later in sql.php
1527 // ALTER TABLE... DROP
1530 // this code is not used for confirmations coming from functions.js
1532 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1533 $number_of_brackets++
;
1536 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1537 $number_of_brackets--;
1538 if ($number_of_brackets == 0) {
1539 $in_subquery = false;
1543 if ($arr[$i]['type'] == 'alpha_reservedWord') {
1544 $upper_data = strtoupper($arr[$i]['data']);
1546 if ($upper_data == 'SELECT' && $number_of_brackets > 0) {
1547 $in_subquery = true;
1550 if (!$seen_reserved_word) {
1551 $first_reserved_word = $upper_data;
1552 $subresult['querytype'] = $upper_data;
1553 $seen_reserved_word = true;
1555 // if the first reserved word is DROP or DELETE,
1556 // we know this is a query that needs to be confirmed
1557 if ($first_reserved_word=='DROP'
1558 ||
$first_reserved_word == 'DELETE'
1559 ||
$first_reserved_word == 'TRUNCATE'
1561 $subresult['queryflags']['need_confirm'] = 1;
1564 if ($first_reserved_word=='SELECT') {
1565 $position_of_first_select = $i;
1569 if ($upper_data == 'DROP' && $first_reserved_word == 'ALTER') {
1570 $subresult['queryflags']['need_confirm'] = 1;
1574 if ($upper_data == 'LIMIT' && ! $in_subquery) {
1575 $section_before_limit = substr($arr['raw'], 0, $arr[$i]['pos'] - 5);
1579 $in_order_by = false; // @todo maybe others to set false
1582 if ($upper_data == 'PROCEDURE') {
1583 $subresult['queryflags']['procedure'] = 1;
1585 $after_limit = true;
1588 * @todo set also to false if we find FOR UPDATE or LOCK IN SHARE MODE
1590 if ($upper_data == 'SELECT') {
1591 $in_select_expr = true;
1592 $select_expr_clause = '';
1594 if ($upper_data == 'DISTINCT' && !$in_group_concat) {
1595 $subresult['queryflags']['distinct'] = 1;
1598 if ($upper_data == 'UNION') {
1599 $subresult['queryflags']['union'] = 1;
1602 if ($upper_data == 'JOIN') {
1603 $subresult['queryflags']['join'] = 1;
1606 if ($upper_data == 'OFFSET') {
1607 $subresult['queryflags']['offset'] = 1;
1610 // if this is a real SELECT...FROM
1611 if ($upper_data == 'FROM'
1612 && isset($subresult['queryflags']['select_from'])
1613 && $subresult['queryflags']['select_from'] == 1
1617 $in_select_expr = false;
1621 // (we could have less resetting of variables to false
1622 // if we trust that the query respects the standard
1623 // MySQL order for clauses)
1625 // we use $seen_group and $seen_order because we are looking
1627 if ($upper_data == 'GROUP') {
1629 $seen_order = false;
1631 $in_order_by = false;
1633 $in_select_expr = false;
1636 if ($upper_data == 'ORDER' && !$in_group_concat) {
1638 $seen_group = false;
1640 $in_group_by = false;
1642 $in_select_expr = false;
1645 if ($upper_data == 'HAVING') {
1647 $having_clause = '';
1648 $seen_group = false;
1649 $seen_order = false;
1650 $in_group_by = false;
1651 $in_order_by = false;
1653 $in_select_expr = false;
1657 if ($upper_data == 'WHERE') {
1660 $where_clause_identifiers = array();
1661 $seen_group = false;
1662 $seen_order = false;
1663 $in_group_by = false;
1664 $in_order_by = false;
1666 $in_select_expr = false;
1670 if ($upper_data == 'BY') {
1672 $in_group_by = true;
1673 $group_by_clause = '';
1676 $seen_order_by = true;
1677 // Here we assume that the ORDER BY keywords took
1678 // exactly 8 characters.
1679 // We use PMA_substr() to be charset-safe; otherwise
1680 // if the table name contains accents, the unsorted
1681 // query would be missing some characters.
1682 $unsorted_query = PMA_substr(
1683 $arr['raw'], 0, $arr[$i]['pos'] - 8
1685 $in_order_by = true;
1686 $order_by_clause = '';
1690 // if we find one of the words that could end the clause
1691 if (isset($words_ending_clauses[$upper_data])) {
1693 $in_group_by = false;
1694 $in_order_by = false;
1697 $in_select_expr = false;
1701 } // endif (reservedWord)
1704 // do not add a space after a function name
1706 * @todo can we combine loop 2 and loop 1? some code is repeated here...
1710 if ($arr[$i]['type'] == 'alpha_functionName') {
1712 $upper_data = strtoupper($arr[$i]['data']);
1713 if ($upper_data =='GROUP_CONCAT') {
1714 $in_group_concat = true;
1715 $number_of_brackets_in_group_concat = 0;
1719 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1720 if ($in_group_concat) {
1721 $number_of_brackets_in_group_concat++
;
1724 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1725 if ($in_group_concat) {
1726 $number_of_brackets_in_group_concat--;
1727 if ($number_of_brackets_in_group_concat == 0) {
1728 $in_group_concat = false;
1733 // do not add a space after an identifier if followed by a dot
1734 if ($arr[$i]['type'] == 'alpha_identifier'
1735 && $i < $size - 1 && $arr[$i +
1]['data'] == '.'
1740 // do not add a space after a dot if followed by an identifier
1741 if ($arr[$i]['data'] == '.' && $i < $size - 1
1742 && $arr[$i +
1]['type'] == 'alpha_identifier'
1747 if ($in_select_expr && $upper_data != 'SELECT'
1748 && $upper_data != 'DISTINCT'
1750 $select_expr_clause .= $arr[$i]['data'] . $sep;
1752 if ($in_from && $upper_data != 'FROM') {
1753 $from_clause .= $arr[$i]['data'] . $sep;
1755 if ($in_group_by && $upper_data != 'GROUP' && $upper_data != 'BY') {
1756 $group_by_clause .= $arr[$i]['data'] . $sep;
1758 if ($in_order_by && $upper_data != 'ORDER' && $upper_data != 'BY') {
1759 // add a space only before ASC or DESC
1760 // not around the dot between dbname and tablename
1761 if ($arr[$i]['type'] == 'alpha_reservedWord') {
1762 $order_by_clause .= $sep;
1764 $order_by_clause .= $arr[$i]['data'];
1766 if ($in_having && $upper_data != 'HAVING') {
1767 $having_clause .= $arr[$i]['data'] . $sep;
1769 if ($in_where && $upper_data != 'WHERE') {
1770 $where_clause .= $arr[$i]['data'] . $sep;
1772 if (($arr[$i]['type'] == 'quote_backtick')
1773 ||
($arr[$i]['type'] == 'alpha_identifier')
1775 $where_clause_identifiers[] = $arr[$i]['data'];
1779 // to grab the rest of the query after the ORDER BY clause
1780 if (isset($subresult['queryflags']['select_from'])
1781 && $subresult['queryflags']['select_from'] == 1
1784 && $upper_data != 'BY'
1786 $unsorted_query .= $arr[$i]['data'];
1787 if ($arr[$i]['type'] != 'punct_bracket_open_round'
1788 && $arr[$i]['type'] != 'punct_bracket_close_round'
1789 && $arr[$i]['type'] != 'punct'
1791 $unsorted_query .= $sep;
1796 if ($upper_data == 'OFFSET') {
1797 $limit_clause .= $sep;
1799 $limit_clause .= $arr[$i]['data'];
1800 if ($upper_data == 'LIMIT' ||
$upper_data == 'OFFSET') {
1801 $limit_clause .= $sep;
1804 if ($after_limit && $seen_limit) {
1805 $section_after_limit .= $arr[$i]['data'] . $sep;
1808 // clear $upper_data for next iteration
1810 } // end for $i (loop #2)
1811 if (empty($section_before_limit)) {
1812 $section_before_limit = $arr['raw'];
1815 // -----------------------------------------------------
1816 // loop #3: foreign keys and MySQL 4.1.2+ TIMESTAMP options
1817 // (for now, check only the first query)
1818 // (for now, identifiers are assumed to be backquoted)
1820 // If we find that we are dealing with a CREATE TABLE query,
1821 // we look for the next punct_bracket_open_round, which
1822 // introduces the fields list. Then, when we find a
1823 // quote_backtick, it must be a field, so we put it into
1824 // the create_table_fields array. Even if this field is
1825 // not a timestamp, it will be useful when logic has been
1826 // added for complete field attributes analysis.
1828 $seen_foreign = false;
1829 $seen_references = false;
1830 $seen_constraint = false;
1831 $foreign_key_number = -1;
1832 $seen_create_table = false;
1833 $seen_create = false;
1834 $seen_alter = false;
1835 $in_create_table_fields = false;
1836 $brackets_level = 0;
1837 $in_timestamp_options = false;
1838 $seen_default = false;
1840 for ($i = 0; $i < $size; $i++
) {
1841 if ($arr[$i]['type'] == 'alpha_reservedWord') {
1842 $upper_data = strtoupper($arr[$i]['data']);
1844 if ($upper_data == 'NOT' && $in_timestamp_options) {
1845 $create_table_fields[$current_identifier]['timestamp_not_null'] = true;
1849 if ($upper_data == 'CREATE') {
1850 $seen_create = true;
1853 if ($upper_data == 'ALTER') {
1857 if ($upper_data == 'TABLE' && $seen_create) {
1858 $seen_create_table = true;
1859 $create_table_fields = array();
1862 if ($upper_data == 'CURRENT_TIMESTAMP') {
1863 if ($in_timestamp_options) {
1864 if ($seen_default) {
1865 $create_table_fields[$current_identifier]['default_current_timestamp'] = true;
1870 if ($upper_data == 'CONSTRAINT') {
1871 $foreign_key_number++
;
1872 $seen_foreign = false;
1873 $seen_references = false;
1874 $seen_constraint = true;
1876 if ($upper_data == 'FOREIGN') {
1877 $seen_foreign = true;
1878 $seen_references = false;
1879 $seen_constraint = false;
1881 if ($upper_data == 'REFERENCES') {
1882 $seen_foreign = false;
1883 $seen_references = true;
1884 $seen_constraint = false;
1890 // [ON DELETE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
1891 // [ON UPDATE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
1893 // but we set ['on_delete'] or ['on_cascade'] to
1894 // CASCADE | SET_NULL | NO_ACTION | RESTRICT
1896 // ON UPDATE CURRENT_TIMESTAMP
1898 if ($upper_data == 'ON') {
1899 if (isset($arr[$i+
1]) && $arr[$i+
1]['type'] == 'alpha_reservedWord') {
1900 $second_upper_data = strtoupper($arr[$i+
1]['data']);
1901 if ($second_upper_data == 'DELETE') {
1902 $clause = 'on_delete';
1904 if ($second_upper_data == 'UPDATE') {
1905 $clause = 'on_update';
1908 && ($arr[$i+
2]['type'] == 'alpha_reservedWord'
1909 // ugly workaround because currently, NO is not
1910 // in the list of reserved words in sqlparser.data
1911 // (we got a bug report about not being able to use
1912 // 'no' as an identifier)
1913 ||
($arr[$i+
2]['type'] == 'alpha_identifier'
1914 && strtoupper($arr[$i+
2]['data'])=='NO'))
1916 $third_upper_data = strtoupper($arr[$i+
2]['data']);
1917 if ($third_upper_data == 'CASCADE'
1918 ||
$third_upper_data == 'RESTRICT'
1920 $value = $third_upper_data;
1921 } elseif ($third_upper_data == 'SET'
1922 ||
$third_upper_data == 'NO'
1924 if ($arr[$i+
3]['type'] == 'alpha_reservedWord') {
1925 $value = $third_upper_data . '_' . strtoupper($arr[$i+
3]['data']);
1927 } elseif ($third_upper_data == 'CURRENT_TIMESTAMP') {
1928 if ($clause == 'on_update'
1929 && $in_timestamp_options
1931 $create_table_fields[$current_identifier]['on_update_current_timestamp'] = true;
1932 $seen_default = false;
1938 if (!empty($value)) {
1939 $foreign[$foreign_key_number][$clause] = $value;
1942 } // endif (isset($clause))
1946 } // end of reserved words analysis
1949 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1951 if ($seen_create_table && $brackets_level == 1) {
1952 $in_create_table_fields = true;
1957 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1959 if ($seen_references) {
1960 $seen_references = false;
1962 if ($seen_create_table && $brackets_level == 0) {
1963 $in_create_table_fields = false;
1967 if (($arr[$i]['type'] == 'alpha_columnAttrib')) {
1968 $upper_data = strtoupper($arr[$i]['data']);
1969 if ($seen_create_table && $in_create_table_fields) {
1970 if ($upper_data == 'DEFAULT') {
1971 $seen_default = true;
1972 $create_table_fields[$current_identifier]['default_value'] = $arr[$i +
1]['data'];
1978 * @see @todo 2005-10-16 note: the "or" part here is a workaround for a bug
1980 if (($arr[$i]['type'] == 'alpha_columnType')
1981 ||
($arr[$i]['type'] == 'alpha_functionName' && $seen_create_table)
1983 $upper_data = strtoupper($arr[$i]['data']);
1984 if ($seen_create_table && $in_create_table_fields
1985 && isset($current_identifier)
1987 $create_table_fields[$current_identifier]['type'] = $upper_data;
1988 if ($upper_data == 'TIMESTAMP') {
1989 $arr[$i]['type'] = 'alpha_columnType';
1990 $in_timestamp_options = true;
1992 $in_timestamp_options = false;
1993 if ($upper_data == 'CHAR') {
1994 $arr[$i]['type'] = 'alpha_columnType';
2001 if ($arr[$i]['type'] == 'quote_backtick'
2002 ||
$arr[$i]['type'] == 'alpha_identifier'
2005 if ($arr[$i]['type'] == 'quote_backtick') {
2006 // remove backquotes
2007 $identifier = PMA_Util
::unQuote($arr[$i]['data']);
2009 $identifier = $arr[$i]['data'];
2012 if ($seen_create_table && $in_create_table_fields) {
2013 $current_identifier = $identifier;
2014 // we set this one even for non TIMESTAMP type
2015 $create_table_fields[$current_identifier]['timestamp_not_null'] = false;
2018 if ($seen_constraint) {
2019 $foreign[$foreign_key_number]['constraint'] = $identifier;
2022 if ($seen_foreign && $brackets_level > 0) {
2023 $foreign[$foreign_key_number]['index_list'][] = $identifier;
2026 if ($seen_references) {
2027 if ($seen_alter && $brackets_level > 0) {
2028 $foreign[$foreign_key_number]['ref_index_list'][] = $identifier;
2029 // here, the first bracket level corresponds to the
2030 // bracket of CREATE TABLE
2031 // so if we are on level 2, it must be the index list
2032 // of the foreign key REFERENCES
2033 } elseif ($brackets_level > 1) {
2034 $foreign[$foreign_key_number]['ref_index_list'][] = $identifier;
2035 } elseif ($arr[$i+
1]['type'] == 'punct_qualifier') {
2036 // identifier is `db`.`table`
2037 // the first pass will pick the db name
2038 // the next pass will pick the table name
2039 $foreign[$foreign_key_number]['ref_db_name'] = $identifier;
2041 // identifier is `table`
2042 $foreign[$foreign_key_number]['ref_table_name'] = $identifier;
2046 } // end for $i (loop #3)
2049 // Fill the $subresult array
2051 if (isset($create_table_fields)) {
2052 $subresult['create_table_fields'] = $create_table_fields;
2055 if (isset($foreign)) {
2056 $subresult['foreign_keys'] = $foreign;
2059 if (isset($select_expr_clause)) {
2060 $subresult['select_expr_clause'] = $select_expr_clause;
2062 if (isset($from_clause)) {
2063 $subresult['from_clause'] = $from_clause;
2065 if (isset($group_by_clause)) {
2066 $subresult['group_by_clause'] = $group_by_clause;
2068 if (isset($order_by_clause)) {
2069 $subresult['order_by_clause'] = $order_by_clause;
2071 if (isset($having_clause)) {
2072 $subresult['having_clause'] = $having_clause;
2074 if (isset($limit_clause)) {
2075 $subresult['limit_clause'] = $limit_clause;
2077 if (isset($where_clause)) {
2078 $subresult['where_clause'] = $where_clause;
2080 if (isset($unsorted_query) && !empty($unsorted_query)) {
2081 $subresult['unsorted_query'] = $unsorted_query;
2083 if (isset($where_clause_identifiers)) {
2084 $subresult['where_clause_identifiers'] = $where_clause_identifiers;
2087 if (isset($position_of_first_select)) {
2088 $subresult['position_of_first_select'] = $position_of_first_select;
2089 $subresult['section_before_limit'] = $section_before_limit;
2090 $subresult['section_after_limit'] = $section_after_limit;
2093 // They are naughty and didn't have a trailing semi-colon,
2094 // then still handle it properly
2095 if ($subresult['querytype'] != '') {
2096 $result[] = $subresult;
2099 } // end of the "PMA_SQP_analyze()" function
2103 * Colorizes SQL queries html formatted
2105 * @param array $arr The SQL queries html formatted
2107 * @return array The colorized SQL queries
2109 * @todo check why adding a "\n" after the </span> would cause extra blanks
2110 * to be displayed: SELECT p . person_name
2114 function PMA_SQP_formatHtml_colorize($arr)
2116 $i = PMA_strpos($arr['type'], '_');
2119 $class = 'syntax_' . PMA_substr($arr['type'], 0, $i) . ' ';
2122 $class .= 'syntax_' . $arr['type'];
2124 return '<span class="' . $class . '">'
2125 . htmlspecialchars($arr['data']) . '</span>';
2126 } // end of the "PMA_SQP_formatHtml_colorize()" function
2130 * Formats SQL queries to html
2132 * @param array $arr The SQL queries
2133 * @param string $mode mode of printing
2134 * @param integer $start_token starting token
2135 * @param integer $number_of_tokens number of tokens to format, -1 = all
2137 * @return string The formatted SQL queries
2141 function PMA_SQP_formatHtml(
2142 $arr, $mode='color', $start_token=0,
2143 $number_of_tokens=-1
2145 global $PMA_SQPdata_operators_docs, $PMA_SQPdata_functions_docs;
2147 //DEBUG echo 'in Format<pre>'; print_r($arr); echo '</pre>';
2148 // then check for an array
2149 if (! is_array($arr)) {
2150 return htmlspecialchars($arr);
2152 // first check for the SQL parser having hit an error
2153 if (PMA_SQP_isError()) {
2154 return htmlspecialchars($arr['raw']);
2156 // else do it properly
2159 $str = '<span class="syntax">';
2160 $html_line_break = '<br />';
2165 $html_line_break = "\n";
2170 $html_line_break = '<br />';
2174 // inner_sql is a span that exists for all cases, except query_only
2175 // of $cfg['SQP']['fmtType'] to make possible a replacement
2176 // for inline editing
2177 if ($mode!='query_only') {
2178 $str .= '<span class="inner_sql">';
2180 $close_docu_link = false;
2184 $infunction = false;
2185 $space_punct_listsep = ' ';
2186 $space_punct_listsep_function_name = ' ';
2187 // $space_alpha_reserved_word = '<br />'."\n";
2188 $space_alpha_reserved_word = ' ';
2190 $keywords_with_brackets_1before = array(
2197 $keywords_with_brackets_2before = array(
2209 // These reserved words do NOT get a newline placed near them.
2210 $keywords_no_newline = array(
2226 // These reserved words introduce a privilege list
2227 $keywords_priv_list = array(
2232 if ($number_of_tokens == -1) {
2233 $number_of_tokens = $arr['len'];
2236 if ($number_of_tokens >= 0) {
2240 $typearr[3] = $arr[$start_token]['type'];
2243 $in_priv_list = false;
2244 for ($i = $start_token; $i < $number_of_tokens; $i++
) {
2245 // DEBUG echo "Loop format <strong>" . $arr[$i]['data']
2246 // . "</strong> " . $arr[$i]['type'] . "<br />";
2249 // array_shift($typearr);
2256 if (($i +
1) < $number_of_tokens) {
2257 $typearr[4] = $arr[$i +
1]['type'];
2262 for ($j=0; $j<4; $j++
) {
2263 $typearr[$j] = $typearr[$j +
1];
2266 switch ($typearr[2]) {
2267 case 'alpha_bitfield_constant_introducer':
2271 case 'white_newline':
2274 case 'punct_bracket_open_round':
2276 $infunction = false;
2277 $keyword_brackets_2before = isset(
2278 $keywords_with_brackets_2before[strtoupper($arr[$i - 2]['data'])]
2280 $keyword_brackets_1before = isset(
2281 $keywords_with_brackets_1before[strtoupper($arr[$i - 1]['data'])]
2283 // Make sure this array is sorted!
2284 if (($typearr[1] == 'alpha_functionName')
2285 ||
($typearr[1] == 'alpha_columnType') ||
($typearr[1] == 'punct')
2286 ||
($typearr[3] == 'digit_integer') ||
($typearr[3] == 'digit_hex')
2287 ||
($typearr[3] == 'digit_float')
2288 ||
($typearr[0] == 'alpha_reservedWord' && $keyword_brackets_2before)
2289 ||
($typearr[1] == 'alpha_reservedWord' && $keyword_brackets_1before)
2296 if ($mode != 'query_only') {
2297 $after .= '<div class="syntax_indent' . $indent . '">';
2303 case 'alpha_identifier':
2304 if (($typearr[1] == 'punct_qualifier')
2305 ||
($typearr[3] == 'punct_qualifier')
2310 // for example SELECT 1 somealias
2311 if ($typearr[1] == 'digit_integer') {
2314 if (($typearr[3] == 'alpha_columnType')
2315 ||
($typearr[3] == 'alpha_identifier')
2321 case 'punct_qualifier':
2325 case 'punct_listsep':
2326 if ($infunction == true) {
2327 $after .= $space_punct_listsep_function_name;
2329 $after .= $space_punct_listsep;
2332 case 'punct_queryend':
2333 if (($typearr[3] != 'comment_mysql')
2334 && ($typearr[3] != 'comment_ansi')
2335 && $typearr[3] != 'comment_c'
2337 $after .= $html_line_break;
2338 $after .= $html_line_break;
2340 $space_punct_listsep = ' ';
2341 $space_punct_listsep_function_name = ' ';
2342 $space_alpha_reserved_word = ' ';
2343 $in_priv_list = false;
2345 case 'comment_mysql':
2346 case 'comment_ansi':
2347 $after .= $html_line_break;
2351 if ($docu && isset($PMA_SQPdata_operators_docs[$arr[$i]['data']])
2352 && ($arr[$i]['data'] != '*' ||
in_array($arr[$i]['type'], array('digit_integer','digit_float','digit_hex')))
2354 $before .= PMA_Util
::showMySQLDocu(
2356 $PMA_SQPdata_operators_docs[$arr[$i]['data']]['link'],
2358 $PMA_SQPdata_operators_docs[$arr[$i]['data']]['anchor'],
2365 // select * from mytable limit 0,-1
2366 // (a side effect of this workaround is that
2371 if ($typearr[3] != 'digit_integer') {
2375 case 'punct_bracket_close_round':
2376 // only close bracket level when it was opened before
2377 if ($bracketlevel > 0) {
2379 if ($infunction == true) {
2385 $before .= ($mode != 'query_only' ?
'</div>' : ' ');
2387 $infunction = ($functionlevel > 0) ?
true : false;
2390 case 'alpha_columnType':
2392 switch ($arr[$i]['data']) {
2405 $before .= PMA_Util
::showMySQLDocu(
2412 $after = '</a>' . $after;
2419 $before .= PMA_Util
::showMySQLDocu(
2421 'date-and-time-types',
2426 $after = '</a>' . $after;
2442 $before .= PMA_Util
::showMySQLDocu(
2449 $after = '</a>' . $after;
2453 if ($typearr[3] == 'alpha_columnAttrib') {
2456 if ($typearr[1] == 'alpha_columnType') {
2460 case 'alpha_columnAttrib':
2462 // ALTER TABLE tbl_name AUTO_INCREMENT = 1
2463 // COLLATE LATIN1_GENERAL_CI DEFAULT
2464 if ($typearr[1] == 'alpha_identifier'
2465 ||
$typearr[1] == 'alpha_charset'
2469 if (($typearr[3] == 'alpha_columnAttrib')
2470 ||
($typearr[3] == 'quote_single')
2471 ||
($typearr[3] == 'digit_integer')
2476 // AUTO_INCREMENT = 31DEFAULT_CHARSET = utf-8
2478 if ($typearr[2] == 'alpha_columnAttrib'
2479 && $typearr[3] == 'alpha_reservedWord'
2484 // select * from mysql.user where binary user="root"
2485 // binary is marked as alpha_columnAttrib
2486 // but should be marked as a reserved word
2487 if (strtoupper($arr[$i]['data']) == 'BINARY'
2488 && $typearr[3] == 'alpha_identifier'
2493 case 'alpha_functionName':
2494 $funcname = strtoupper($arr[$i]['data']);
2495 if ($docu && isset($PMA_SQPdata_functions_docs[$funcname])) {
2496 $before .= PMA_Util
::showMySQLDocu(
2498 $PMA_SQPdata_functions_docs[$funcname]['link'],
2500 $PMA_SQPdata_functions_docs[$funcname]['anchor'],
2506 case 'alpha_reservedWord':
2507 // do not uppercase the reserved word if we are calling
2508 // this function in query_only mode, because we need
2509 // the original query (otherwise we get problems with
2510 // semi-reserved words like "storage" which is legal
2511 // as an identifier name)
2513 if ($mode != 'query_only') {
2514 $arr[$i]['data'] = strtoupper($arr[$i]['data']);
2517 if ((($typearr[1] != 'alpha_reservedWord')
2518 ||
(($typearr[1] == 'alpha_reservedWord')
2519 && isset($keywords_no_newline[strtoupper($arr[$i - 1]['data'])])))
2520 && ($typearr[1] != 'punct_level_plus')
2521 && (!isset($keywords_no_newline[$arr[$i]['data']]))
2523 // do not put a space before the first token, because
2524 // we use a lot of pattern matching checking for the
2525 // first reserved word at beginning of query
2526 // so do not put a newline before
2528 // also we must not be inside a privilege list
2530 // the alpha_identifier exception is there to
2532 // GRANT SELECT ON mydb.mytable TO myuser@localhost
2533 // (else, we get mydb.mytableTO)
2535 // the quote_single exception is there to
2537 // GRANT ... TO 'marc'@'domain.com' IDENTIFIED...
2539 * @todo fix all cases and find why this happens
2543 ||
$typearr[1] == 'alpha_identifier'
2544 ||
$typearr[1] == 'quote_single'
2545 ||
$typearr[1] == 'white_newline'
2547 $before .= $space_alpha_reserved_word;
2550 // on first keyword, check if it introduces a
2552 if (isset($keywords_priv_list[$arr[$i]['data']])) {
2553 $in_priv_list = true;
2560 switch ($arr[$i]['data']) {
2570 switch ($arr[$i +
1]['data']) {
2581 $before .= PMA_Util
::showMySQLDocu(
2583 $arr[$i]['data'] . '_' . $arr[$i +
1]['data'],
2588 $close_docu_link = true;
2591 if ($arr[$i +
1]['data'] == 'LOGFILE'
2592 && $arr[$i +
2]['data'] == 'GROUP'
2594 $before .= PMA_Util
::showMySQLDocu(
2596 $arr[$i]['data'] . '_LOGFILE_GROUP',
2601 $close_docu_link = true;
2604 if (!$in_priv_list) {
2605 $space_punct_listsep = $html_line_break;
2606 $space_alpha_reserved_word = ' ';
2620 if ($close_docu_link) {
2621 $after = '</a>' . $after;
2622 $close_docu_link = false;
2626 if ($docu && ($i == 0 ||
$arr[$i - 1]['data'] != 'CHARACTER')) {
2627 $before .= PMA_Util
::showMySQLDocu(
2634 $after = '</a>' . $after;
2636 if (!$in_priv_list) {
2637 $space_punct_listsep = $html_line_break;
2638 $space_alpha_reserved_word = ' ';
2647 $before .= PMA_Util
::showMySQLDocu(
2654 $after = '</a>' . $after;
2656 if (!$in_priv_list) {
2657 $space_punct_listsep = $html_line_break;
2658 $space_alpha_reserved_word = ' ';
2664 $before .= PMA_Util
::showMySQLDocu(
2671 $after = '</a>' . $after;
2673 if (!$in_priv_list) {
2674 $space_punct_listsep = $html_line_break;
2675 $space_alpha_reserved_word = $html_line_break;
2679 $space_punct_listsep = ' ';
2680 $space_alpha_reserved_word = $html_line_break;
2684 $before .= PMA_Util
::showMySQLDocu(
2691 $after = '</a>' . $after;
2693 $space_punct_listsep = ' ';
2694 $space_alpha_reserved_word = $html_line_break;
2700 $before .= PMA_Util
::showMySQLDocu(
2707 $after = '</a>' . $after;
2711 if ($close_docu_link
2714 array('LIKE', 'NOT', 'IN', 'REGEXP', 'NULL')
2718 $close_docu_link = false;
2720 && isset($PMA_SQPdata_functions_docs[$arr[$i]['data']])
2722 /* Handle multi word statements first */
2723 if (isset($typearr[4])
2724 && $typearr[4] == 'alpha_reservedWord'
2725 && $typearr[3] == 'alpha_reservedWord'
2726 && isset($PMA_SQPdata_functions_docs[strtoupper(
2727 $arr[$i]['data'] . '_'
2728 . $arr[$i +
1]['data'] . '_'
2729 . $arr[$i +
2]['data']
2732 $tempname = strtoupper(
2733 $arr[$i]['data'] . '_'
2734 . $arr[$i +
1]['data'] . '_'
2735 . $arr[$i +
2]['data']
2737 $before .= PMA_Util
::showMySQLDocu(
2739 $PMA_SQPdata_functions_docs[$tempname]['link'],
2741 $PMA_SQPdata_functions_docs[$tempname]['anchor'],
2744 $close_docu_link = true;
2745 } else if (isset($typearr[3])
2746 && $typearr[3] == 'alpha_reservedWord'
2747 && isset($PMA_SQPdata_functions_docs[strtoupper(
2748 $arr[$i]['data'] . '_' . $arr[$i +
1]['data']
2751 $tempname = strtoupper(
2752 $arr[$i]['data'] . '_' . $arr[$i +
1]['data']
2754 $before .= PMA_Util
::showMySQLDocu(
2756 $PMA_SQPdata_functions_docs[$tempname]['link'],
2758 $PMA_SQPdata_functions_docs[$tempname]['anchor'],
2761 $close_docu_link = true;
2763 $before .= PMA_Util
::showMySQLDocu(
2765 $PMA_SQPdata_functions_docs[$arr[$i]['data']]['link'],
2767 $PMA_SQPdata_functions_docs[$arr[$i]['data']]['anchor'],
2774 } // end switch ($arr[$i]['data'])
2778 case 'digit_integer':
2782 * @todo could there be other types preceding a digit?
2784 if ($typearr[1] == 'alpha_reservedWord') {
2787 if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
2790 if ($typearr[1] == 'alpha_columnAttrib') {
2794 case 'alpha_variable':
2797 case 'quote_double':
2798 case 'quote_single':
2799 // workaround: for the query
2800 // REVOKE SELECT ON `base2\_db`.* FROM 'user'@'%'
2801 // the @ is incorrectly marked as alpha_variable
2802 // in the parser, and here, the '%' gets a blank before,
2803 // which is a syntax error
2804 if ($typearr[1] != 'punct_user'
2805 && $typearr[1] != 'alpha_bitfield_constant_introducer'
2809 if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
2813 case 'quote_backtick':
2814 // here we check for punct_user to handle correctly
2815 // DEFINER = `username`@`%`
2816 // where @ is the punct_user and `%` is the quote_backtick
2817 if ($typearr[3] != 'punct_qualifier'
2818 && $typearr[3] != 'alpha_variable'
2819 && $typearr[3] != 'punct_user'
2823 if ($typearr[1] != 'punct_qualifier'
2824 && $typearr[1] != 'alpha_variable'
2825 && $typearr[1] != 'punct_user'
2832 } // end switch ($typearr[2])
2835 if ($typearr[3] != 'punct_qualifier') {
2841 if ($mode=='color') {
2842 $str .= PMA_SQP_formatHTML_colorize($arr[$i]);
2843 } elseif ($mode == 'text') {
2844 $str .= htmlspecialchars($arr[$i]['data']);
2846 $str .= $arr[$i]['data'];
2850 // close unclosed indent levels
2851 while ($indent > 0) {
2853 $str .= ($mode != 'query_only' ?
'</div>' : ' ');
2855 /* End possibly unclosed documentation link */
2856 if ($close_docu_link) {
2858 $close_docu_link = false;
2860 if ($mode!='query_only') {
2861 // close inner_sql span
2864 if ($mode=='color') {
2865 // close syntax span
2870 } // end of the "PMA_SQP_formatHtml()" function
2873 * Gets SQL queries with no format
2875 * @param array $arr The SQL queries list
2877 * @return string The SQL queries with no format
2881 function PMA_SQP_formatNone($arr)
2883 $formatted_sql = htmlspecialchars($arr['raw']);
2884 $formatted_sql = preg_replace(
2885 "@((\015\012)|(\015)|(\012)){3,}@",
2890 return $formatted_sql;
2891 } // end of the "PMA_SQP_formatNone()" function
2894 * Checks whether a given name is MySQL reserved word
2896 * @param string $column The word to be checked
2898 * @return boolean whether true or false
2900 function PMA_SQP_isKeyWord($column)
2902 global $PMA_SQPdata_forbidden_word;
2903 return in_array(strtoupper($column), $PMA_SQPdata_forbidden_word);