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 include_once './libraries/string.lib.php';
43 * Include data for the SQL Parser
45 include_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();
54 if (!defined('DEBUG_TIMING')) {
56 * currently we don't need the $pos (token position in query)
57 * for other purposes than LIMIT clause verification,
58 * so many calls to this function do not include the 4th parameter
60 function PMA_SQP_arrayAdd(&$arr, $type, $data, &$arrsize, $pos = 0)
62 $arr[] = array('type' => $type, 'data' => $data, 'pos' => $pos);
64 } // end of the "PMA_SQP_arrayAdd()" function
67 * This is debug variant of above.
70 function PMA_SQP_arrayAdd(&$arr, $type, $data, &$arrsize, $pos = 0)
82 } // end of the "PMA_SQP_arrayAdd()" function
83 } // end if... else...
87 * Reset the error variable for the SQL parser
91 function PMA_SQP_resetError()
93 global $SQP_errorString;
94 $SQP_errorString = '';
95 unset($SQP_errorString);
99 * Get the contents of the error variable for the SQL parser
101 * @return string Error string from SQL parser
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 function PMA_SQP_isError()
120 global $SQP_errorString;
121 return isset($SQP_errorString) && !empty($SQP_errorString);
125 * Set an error message for the system
127 * @param string The error message
128 * @param string The failing SQL query
131 * @scope SQL Parser internal
133 function PMA_SQP_throwError($message, $sql)
135 global $SQP_errorString;
136 $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"
138 . 'ERROR: ' . $message . "\n"
139 . 'SQL: ' . htmlspecialchars($sql) . "\n"
142 } // end of the "PMA_SQP_throwError()" function
146 * Do display the bug report
148 * @param string The error message
149 * @param string The failing SQL query
153 function PMA_SQP_bug($message, $sql)
155 global $SQP_errorString;
156 $debugstr = 'ERROR: ' . $message . "\n";
157 $debugstr .= 'MySQL: '.PMA_MYSQL_STR_VERSION
. "\n";
158 $debugstr .= 'USR OS, AGENT, VER: ' . PMA_USR_OS
. ' ';
159 $debugstr .= PMA_USR_BROWSER_AGENT
. ' ' . PMA_USR_BROWSER_VER
. "\n";
160 $debugstr .= 'PMA: ' . PMA_VERSION
. "\n";
161 $debugstr .= 'PHP VER,OS: ' . PMA_PHP_STR_VERSION
. ' ' . PHP_OS
. "\n";
162 $debugstr .= 'LANG: ' . $GLOBALS['lang'] . "\n";
163 $debugstr .= 'SQL: ' . htmlspecialchars($sql);
165 $encodedstr = $debugstr;
166 if (@function_exists
('gzcompress')) {
167 $encodedstr = gzcompress($debugstr, 9);
169 $encodedstr = preg_replace(
170 "/(\015\012)|(\015)|(\012)/",
172 chunk_split(base64_encode($encodedstr)));
175 $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:')
177 . '----' . __('BEGIN CUT') . '----' . '<br />' . "\n"
179 . '----' . __('END CUT') . '----' . '<br />' . "\n";
181 $SQP_errorString .= '----' . __('BEGIN RAW') . '----<br />' . "\n"
185 . '----' . __('END RAW') . '----<br />' . "\n";
187 } // end of the "PMA_SQP_bug()" function
191 * Parses the SQL queries
193 * @param string The SQL query list
195 * @return mixed Most of times, nothing...
197 * @global array The current PMA configuration
198 * @global array MySQL column attributes
199 * @global array MySQL reserved words
200 * @global array MySQL column types
201 * @global array MySQL function names
202 * @global array List of available character sets
203 * @global array List of available collations
207 function PMA_SQP_parse($sql)
209 static $PMA_SQPdata_column_attrib, $PMA_SQPdata_reserved_word;
210 static $PMA_SQPdata_column_type;
211 static $PMA_SQPdata_function_name, $PMA_SQPdata_forbidden_word;
212 global $mysql_charsets, $mysql_collations_flat;
214 // Convert all line feeds to Unix style
215 $sql = str_replace("\r\n", "\n", $sql);
216 $sql = str_replace("\r", "\n", $sql);
218 $len = PMA_strlen($sql);
223 // Create local hashtables
224 if (!isset($PMA_SQPdata_column_attrib)) {
225 $PMA_SQPdata_column_attrib = array_flip(
226 $GLOBALS['PMA_SQPdata_column_attrib']
228 $PMA_SQPdata_function_name = array_flip(
229 $GLOBALS['PMA_SQPdata_function_name']
231 $PMA_SQPdata_reserved_word = array_flip(
232 $GLOBALS['PMA_SQPdata_reserved_word']
234 $PMA_SQPdata_forbidden_word = array_flip(
235 $GLOBALS['PMA_SQPdata_forbidden_word']
237 $PMA_SQPdata_column_type = array_flip(
238 $GLOBALS['PMA_SQPdata_column_type']
242 $sql_array = array();
243 $sql_array['raw'] = $sql;
246 $punct_queryend = ';';
247 $punct_qualifier = '.';
248 $punct_listsep = ',';
249 $punct_level_plus = '(';
250 $punct_level_minus = ')';
252 $digit_floatdecimal = '.';
254 $bracket_list = '()[]{}';
255 $allpunct_list = '-,;:!?/.^~\*&%+<=>|';
256 $allpunct_list_pair = array(
269 $quote_list = '\'"`';
272 $previous_was_space = false;
273 $this_was_space = false;
274 $previous_was_bracket = false;
275 $this_was_bracket = false;
276 $previous_was_punct = false;
277 $this_was_punct = false;
278 $previous_was_listsep = false;
279 $this_was_listsep = false;
280 $previous_was_quote = false;
281 $this_was_quote = false;
283 while ($count2 < $len) {
284 $c = PMA_substr($sql, $count2, 1);
287 $previous_was_space = $this_was_space;
288 $this_was_space = false;
289 $previous_was_bracket = $this_was_bracket;
290 $this_was_bracket = false;
291 $previous_was_punct = $this_was_punct;
292 $this_was_punct = false;
293 $previous_was_listsep = $this_was_listsep;
294 $this_was_listsep = false;
295 $previous_was_quote = $this_was_quote;
296 $this_was_quote = false;
299 $this_was_space = true;
301 PMA_SQP_arrayAdd($sql_array, 'white_newline', '', $arraysize);
305 // Checks for white space
306 if (PMA_STR_isSpace($c)) {
307 $this_was_space = true;
312 // Checks for comment lines.
316 $next_c = PMA_substr($sql, $count2 +
1, 1);
318 ||
(($count2 +
1 < $len) && ($c == '/') && ($next_c == '*'))
319 ||
(($count2 +
2 == $len) && ($c == '-') && ($next_c == '-'))
320 ||
(($count2 +
2 < $len) && ($c == '-') && ($next_c == '-') && ((PMA_substr($sql, $count2 +
2, 1) <= ' ')))) {
329 $pos = PMA_strpos($sql, "\n", $count2);
333 $pos = PMA_strpos($sql, '*/', $count2);
339 $count2 = ($pos < $count2) ?
$len : $pos;
340 $str = PMA_substr($sql, $count1, $count2 - $count1);
341 PMA_SQP_arrayAdd($sql_array, 'comment_' . $type, $str, $arraysize);
345 // Checks for something inside quotation marks
346 if (PMA_strpos($quote_list, $c) !== false) {
347 $startquotepos = $count2;
355 $pos = PMA_strpos(' ' . $sql, $quotetype, $oldpos +
1) - 1;
360 * Behave same as MySQL and accept end of query as end of backtick.
361 * I know this is sick, but MySQL behaves like this:
363 * SELECT * FROM `table
367 * SELECT * FROM `table`
369 $pos_quote_separator = PMA_strpos(' ' . $sql, $GLOBALS['sql_delimiter'], $oldpos +
1) - 1;
370 if ($pos_quote_separator < 0) {
373 $sql_array['raw'] .= '`';
377 $sql = PMA_substr($sql, 0, $pos_quote_separator) . '`' . PMA_substr($sql, $pos_quote_separator);
378 $sql_array['raw'] = $sql;
379 $pos = $pos_quote_separator;
381 if (class_exists('PMA_Message') && $GLOBALS['is_ajax_request'] != true) {
382 PMA_Message
::notice(__('Automatically appended backtick to the end of query!'))->display();
385 $debugstr = __('Unclosed quote') . ' @ ' . $startquotepos. "\n"
386 . 'STR: ' . htmlspecialchars($quotetype);
387 PMA_SQP_throwError($debugstr, $sql);
392 // If the quote is the first character, it can't be
393 // escaped, so don't do the rest of the code
398 // Checks for MySQL escaping using a \
399 // And checks for ANSI escaping using the $quotetype character
400 if (($pos < $len) && PMA_STR_charIsEscaped($sql, $pos) && $c != '`') {
403 } elseif (($pos +
1 < $len) && (PMA_substr($sql, $pos, 1) == $quotetype) && (PMA_substr($sql, $pos +
1, 1) == $quotetype)) {
409 } while ($len > $pos); // end do
414 switch ($quotetype) {
417 $this_was_quote = true;
421 $this_was_quote = true;
425 $this_was_quote = true;
430 $data = PMA_substr($sql, $count1, $count2 - $count1);
431 PMA_SQP_arrayAdd($sql_array, $type, $data, $arraysize);
435 // Checks for brackets
436 if (PMA_strpos($bracket_list, $c) !== false) {
437 // All bracket tokens are only one item long
438 $this_was_bracket = true;
441 if (PMA_strpos('([{', $c) !== false) {
444 $type_type = 'close';
448 if (PMA_strpos('()', $c) !== false) {
449 $type_style = 'round';
450 } elseif (PMA_strpos('[]', $c) !== false) {
451 $type_style = 'square';
453 $type_style = 'curly';
456 $type = 'punct_bracket_' . $type_type . '_' . $type_style;
457 PMA_SQP_arrayAdd($sql_array, $type, $c, $arraysize);
463 var_dump(PMA_STR_isSqlIdentifier($c, false));
466 var_dump(PMA_STR_isDigit(PMA_substr($sql, $count2 + 1, 1)));
467 var_dump($previous_was_space);
468 var_dump($previous_was_bracket);
469 var_dump($previous_was_listsep);
473 // Checks for identifier (alpha or numeric)
474 if (PMA_STR_isSqlIdentifier($c, false)
477 && PMA_STR_isDigit(PMA_substr($sql, $count2 +
1, 1))
478 && ($previous_was_space ||
$previous_was_bracket ||
$previous_was_listsep))) {
481 echo PMA_substr($sql, $count2);
488 * @todo a @ can also be present in expressions like
489 * FROM 'user'@'%' or TO 'user'@'%'
490 * in this case, the @ is wrongly marked as alpha_variable
492 $is_identifier = $previous_was_punct;
493 $is_sql_variable = $c == '@' && ! $previous_was_quote;
494 $is_user = $c == '@' && $previous_was_quote;
495 $is_digit = !$is_identifier && !$is_sql_variable && PMA_STR_isDigit($c);
496 $is_hex_digit = $is_digit && $c == '0' && $count2 < $len && PMA_substr($sql, $count2, 1) == 'x';
497 $is_float_digit = $c == '.';
498 $is_float_digit_exponent = false;
502 var_dump($is_identifier);
503 var_dump($is_sql_variable);
505 var_dump($is_float_digit);
509 // Fast skip is especially needed for huge BLOB data
512 $pos = strspn($sql, '0123456789abcdefABCDEF', $count2);
513 if ($pos > $count2) {
517 } elseif ($is_digit) {
518 $pos = strspn($sql, '0123456789', $count2);
519 if ($pos > $count2) {
525 while (($count2 < $len) && PMA_STR_isSqlIdentifier(PMA_substr($sql, $count2, 1), ($is_sql_variable ||
$is_digit))) {
526 $c2 = PMA_substr($sql, $count2, 1);
527 if ($is_sql_variable && ($c2 == '.')) {
531 if ($is_digit && (!$is_hex_digit) && ($c2 == '.')) {
533 if (!$is_float_digit) {
534 $is_float_digit = true;
537 $debugstr = __('Invalid Identifer') . ' @ ' . ($count1+
1) . "\n"
538 . 'STR: ' . htmlspecialchars(PMA_substr($sql, $count1, $count2 - $count1));
539 PMA_SQP_throwError($debugstr, $sql);
543 if ($is_digit && (!$is_hex_digit) && (($c2 == 'e') ||
($c2 == 'E'))) {
544 if (!$is_float_digit_exponent) {
545 $is_float_digit_exponent = true;
546 $is_float_digit = true;
551 $is_float_digit = false;
554 if (($is_hex_digit && PMA_STR_isHexDigit($c2)) ||
($is_digit && PMA_STR_isDigit($c2))) {
559 $is_hex_digit = false;
565 $l = $count2 - $count1;
566 $str = PMA_substr($sql, $count1, $l);
569 if ($is_digit ||
$is_float_digit ||
$is_hex_digit) {
571 if ($is_float_digit) {
573 } elseif ($is_hex_digit) {
578 } elseif ($is_user) {
579 $type = 'punct_user';
580 } elseif ($is_sql_variable != false) {
581 $type = 'alpha_variable';
584 } // end if... else....
585 PMA_SQP_arrayAdd($sql_array, $type, $str, $arraysize, $count2);
591 if (PMA_strpos($allpunct_list, $c) !== false) {
592 while (($count2 < $len) && PMA_strpos($allpunct_list, PMA_substr($sql, $count2, 1)) !== false) {
595 $l = $count2 - $count1;
599 $punct_data = PMA_substr($sql, $count1, $l);
602 // Special case, sometimes, althought two characters are
603 // adjectent directly, they ACTUALLY need to be seperate
607 var_dump($punct_data);
613 switch ($punct_data) {
614 case $punct_queryend:
615 $t_suffix = '_queryend';
617 case $punct_qualifier:
618 $t_suffix = '_qualifier';
619 $this_was_punct = true;
622 $this_was_listsep = true;
623 $t_suffix = '_listsep';
628 PMA_SQP_arrayAdd($sql_array, 'punct' . $t_suffix, $punct_data, $arraysize);
629 } elseif ($punct_data == $GLOBALS['sql_delimiter'] ||
isset($allpunct_list_pair[$punct_data])) {
630 // Ok, we have one of the valid combined punct expressions
631 PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize);
633 // Bad luck, lets split it up more
634 $first = $punct_data[0];
635 $first2 = $punct_data[0] . $punct_data[1];
636 $last2 = $punct_data[$l - 2] . $punct_data[$l - 1];
637 $last = $punct_data[$l - 1];
638 if (($first == ',') ||
($first == ';') ||
($first == '.') ||
($first == '*')) {
639 $count2 = $count1 +
1;
640 $punct_data = $first;
641 } elseif (($last2 == '/*') ||
(($last2 == '--') && ($count2 == $len ||
PMA_substr($sql, $count2, 1) <= ' '))) {
643 $punct_data = PMA_substr($sql, $count1, $count2 - $count1);
644 } elseif (($last == '-') ||
($last == '+') ||
($last == '!')) {
646 $punct_data = PMA_substr($sql, $count1, $count2 - $count1);
647 } elseif ($last != '~') {
649 * @todo for negation operator, split in 2 tokens ?
650 * "select x&~1 from t"
651 * becomes "select x & ~ 1 from t" ?
653 $debugstr = __('Unknown Punctuation String') . ' @ ' . ($count1+
1) . "\n"
654 . 'STR: ' . htmlspecialchars($punct_data);
655 PMA_SQP_throwError($debugstr, $sql);
658 PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize);
660 } // end if... elseif... else
667 $debugstr = 'C1 C2 LEN: ' . $count1 . ' ' . $count2 . ' ' . $len . "\n"
668 . 'STR: ' . PMA_substr($sql, $count1, $count2 - $count1) . "\n";
669 PMA_SQP_bug($debugstr, $sql);
672 } // end while ($count2 < $len)
680 if ($arraysize > 0) {
681 $t_next = $sql_array[0]['type'];
685 $d_next = $sql_array[0]['data'];
689 $d_next_upper = $t_next == 'alpha' ?
strtoupper($d_next) : $d_next;
691 $d_bef_prev_upper = '';
695 for ($i = 0; $i < $arraysize; $i++
) {
696 $t_bef_prev = $t_prev;
699 $d_bef_prev = $d_prev;
702 $d_bef_prev_upper = $d_prev_upper;
703 $d_prev_upper = $d_cur_upper;
704 $d_cur_upper = $d_next_upper;
705 if (($i +
1) < $arraysize) {
706 $t_next = $sql_array[$i +
1]['type'];
707 $d_next = $sql_array[$i +
1]['data'];
708 $d_next_upper = $t_next == 'alpha' ?
strtoupper($d_next) : $d_next;
715 //DEBUG echo "[prev: <strong>".$d_prev."</strong> ".$t_prev."][cur: <strong>".$d_cur."</strong> ".$t_cur."][next: <strong>".$d_next."</strong> ".$t_next."]<br />";
717 if ($t_cur == 'alpha') {
718 $t_suffix = '_identifier';
719 // for example: `thebit` bit(8) NOT NULL DEFAULT b'0'
720 if ($t_prev == 'alpha' && $d_prev == 'DEFAULT' && $d_cur == 'b' && $t_next == 'quote_single') {
721 $t_suffix = '_bitfield_constant_introducer';
722 } elseif (($t_next == 'punct_qualifier') ||
($t_prev == 'punct_qualifier')) {
723 $t_suffix = '_identifier';
724 } elseif (($t_next == 'punct_bracket_open_round')
725 && isset($PMA_SQPdata_function_name[$d_cur_upper])) {
727 * @todo 2005-10-16: in the case of a CREATE TABLE containing
728 * a TIMESTAMP, since TIMESTAMP() is also a function, it's
729 * found here and the token is wrongly marked as alpha_functionName.
730 * But we compensate for this when analysing for timestamp_not_null
731 * later in this script.
733 * Same applies to CHAR vs. CHAR() function.
735 $t_suffix = '_functionName';
736 /* There are functions which might be as well column types */
737 } elseif (isset($PMA_SQPdata_column_type[$d_cur_upper])) {
738 $t_suffix = '_columnType';
741 * Temporary fix for BUG #621357 and #2027720
743 * @todo FIX PROPERLY NEEDS OVERHAUL OF SQL TOKENIZER
745 if (($d_cur_upper == 'SET' ||
$d_cur_upper == 'BINARY') && $t_next != 'punct_bracket_open_round') {
746 $t_suffix = '_reservedWord';
748 //END OF TEMPORARY FIX
750 // CHARACTER is a synonym for CHAR, but can also be meant as
751 // CHARACTER SET. In this case, we have a reserved word.
752 if ($d_cur_upper == 'CHARACTER' && $d_next_upper == 'SET') {
753 $t_suffix = '_reservedWord';
757 // current is a column type, so previous must not be
758 // a reserved word but an identifier
759 // CREATE TABLE SG_Persons (first varchar(64))
761 //if ($sql_array[$i-1]['type'] =='alpha_reservedWord') {
762 // $sql_array[$i-1]['type'] = 'alpha_identifier';
765 } elseif (isset($PMA_SQPdata_reserved_word[$d_cur_upper])) {
766 $t_suffix = '_reservedWord';
767 } elseif (isset($PMA_SQPdata_column_attrib[$d_cur_upper])) {
768 $t_suffix = '_columnAttrib';
769 // INNODB is a MySQL table type, but in "SHOW INNODB STATUS",
770 // it should be regarded as a reserved word.
771 if ($d_cur_upper == 'INNODB' && $d_prev_upper == 'SHOW' && $d_next_upper == 'STATUS') {
772 $t_suffix = '_reservedWord';
775 if ($d_cur_upper == 'DEFAULT' && $d_next_upper == 'CHARACTER') {
776 $t_suffix = '_reservedWord';
778 // Binary as character set
779 if ($d_cur_upper == 'BINARY' && (
780 ($d_bef_prev_upper == 'CHARACTER' && $d_prev_upper == 'SET')
781 ||
($d_bef_prev_upper == 'SET' && $d_prev_upper == '=')
782 ||
($d_bef_prev_upper == 'CHARSET' && $d_prev_upper == '=')
783 ||
$d_prev_upper == 'CHARSET'
784 ) && in_array($d_cur, $mysql_charsets)) {
785 $t_suffix = '_charset';
787 } elseif (in_array($d_cur, $mysql_charsets)
788 ||
in_array($d_cur, $mysql_collations_flat)
789 ||
($d_cur{0} == '_' && in_array(substr($d_cur, 1), $mysql_charsets))) {
790 $t_suffix = '_charset';
794 // check if present in the list of forbidden words
795 if ($t_suffix == '_reservedWord' && isset($PMA_SQPdata_forbidden_word[$d_cur_upper])) {
796 $sql_array[$i]['forbidden'] = true;
798 $sql_array[$i]['forbidden'] = false;
800 $sql_array[$i]['type'] .= $t_suffix;
804 // Stores the size of the array inside the array, as count() is a slow
806 $sql_array['len'] = $arraysize;
808 // DEBUG echo 'After parsing<pre>'; print_r($sql_array); echo '</pre>';
809 // Sends the data back
811 } // end of the "PMA_SQP_parse()" function
814 * Checks for token types being what we want...
816 * @param string String of type that we have
817 * @param string String of type that we want
819 * @return boolean result of check
823 function PMA_SQP_typeCheck($toCheck, $whatWeWant)
825 $typeSeperator = '_';
826 if (strcmp($whatWeWant, $toCheck) == 0) {
829 if (strpos($whatWeWant, $typeSeperator) === false) {
830 return strncmp($whatWeWant, $toCheck, strpos($toCheck, $typeSeperator)) == 0;
839 * Analyzes SQL queries
841 * @param array The SQL queries
843 * @return array The analyzed SQL queries
847 function PMA_SQP_analyze($arr)
849 if ($arr == array() ||
! isset($arr['len'])) {
856 'select_expr_clause'=> '', // the whole stuff between SELECT and FROM , except DISTINCT
857 'position_of_first_select' => '', // the array index
859 'group_by_clause'=> '',
860 'order_by_clause'=> '',
861 'having_clause' => '',
862 'limit_clause' => '',
863 'where_clause' => '',
864 'where_clause_identifiers' => array(),
865 'unsorted_query' => '',
866 'queryflags' => array(),
867 'select_expr' => array(),
868 'table_ref' => array(),
869 'foreign_keys' => array(),
870 'create_table_fields' => array()
872 $subresult_empty = $subresult;
873 $seek_queryend = false;
874 $seen_end_of_table_ref = false;
875 $number_of_brackets_in_extract = 0;
876 $number_of_brackets_in_group_concat = 0;
878 $number_of_brackets = 0;
879 $in_subquery = false;
880 $seen_subquery = false;
883 // for SELECT EXTRACT(YEAR_MONTH FROM CURDATE())
884 // we must not use CURDATE as a table_ref
885 // so we track whether we are in the EXTRACT()
888 // for GROUP_CONCAT(...)
889 $in_group_concat = false;
891 /* Description of analyzer results
893 * db, table, column, alias
894 * ------------------------
896 * Inside the $subresult array, we create ['select_expr'] and ['table_ref'] arrays.
898 * The SELECT syntax (simplified) is
901 * select_expression,...
902 * [FROM [table_references]
905 * ['select_expr'] is filled with each expression, the key represents the
906 * expression position in the list (0-based) (so we don't lose track of
907 * multiple occurences of the same column).
909 * ['table_ref'] is filled with each table ref, same thing for the key.
911 * I create all sub-values empty, even if they are
912 * not present (for example no select_expression alias).
914 * There is a debug section at the end of loop #1, if you want to
915 * see the exact contents of select_expr and table_ref
920 * In $subresult, array 'queryflags' is filled, according to what we
923 * Currently, those are generated:
925 * ['queryflags']['need_confirm'] = 1; if the query needs confirmation
926 * ['queryflags']['select_from'] = 1; if this is a real SELECT...FROM
927 * ['queryflags']['distinct'] = 1; for a DISTINCT
928 * ['queryflags']['union'] = 1; for a UNION
929 * ['queryflags']['join'] = 1; for a JOIN
930 * ['queryflags']['offset'] = 1; for the presence of OFFSET
931 * ['queryflags']['procedure'] = 1; for the presence of PROCEDURE
936 * The select is splitted in those clauses:
937 * ['select_expr_clause']
939 * ['group_by_clause']
940 * ['order_by_clause']
945 * The identifiers of the WHERE clause are put into the array
946 * ['where_clause_identifier']
948 * For a SELECT, the whole query without the ORDER BY clause is put into
953 * The CREATE TABLE may contain FOREIGN KEY clauses, so they get
954 * analyzed and ['foreign_keys'] is an array filled with
955 * the constraint name, the index list,
956 * the REFERENCES table name and REFERENCES index list,
957 * and ON UPDATE | ON DELETE clauses
959 * position_of_first_select
960 * ------------------------
962 * The array index of the first SELECT we find. Will be used to
963 * insert a SQL_CALC_FOUND_ROWS.
965 * create_table_fields
966 * -------------------
968 * Used to detect the DEFAULT CURRENT_TIMESTAMP and
969 * ON UPDATE CURRENT_TIMESTAMP clauses of the CREATE TABLE query.
970 * Also used to store the default value of the field.
971 * An array, each element is the identifier name.
972 * Note that for now, the timestamp_not_null element is created
973 * even for non-TIMESTAMP fields.
975 * Sub-elements: ['type'] which contains the column type
976 * optional (currently they are never false but can be absent):
977 * ['default_current_timestamp'] boolean
978 * ['on_update_current_timestamp'] boolean
979 * ['timestamp_not_null'] boolean
981 * section_before_limit, section_after_limit
982 * -----------------------------------------
984 * Marks the point of the query where we can insert a LIMIT clause;
985 * so the section_before_limit will contain the left part before
986 * a possible LIMIT clause
989 * End of description of analyzer results
993 // TODO: current logic checks for only one word, so I put only the
994 // first word of the reserved expressions that end a table ref;
995 // maybe this is not ok (the first word might mean something else)
996 // $words_ending_table_ref = array(
1001 // 'LOCK IN SHARE MODE',
1007 $words_ending_table_ref = array(
1019 $words_ending_clauses = array(
1027 $supported_query_types = array(
1030 // Support for these additional query types will come later on.
1045 // loop #1 for each token: select_expr, table_ref for SELECT
1047 for ($i = 0; $i < $size; $i++
) {
1048 //DEBUG echo "Loop1 <strong>" . $arr[$i]['data'] . "</strong> (" . $arr[$i]['type'] . ")<br />";
1050 // High speed seek for locating the end of the current query
1051 if ($seek_queryend == true) {
1052 if ($arr[$i]['type'] == 'punct_queryend') {
1053 $seek_queryend = false;
1056 } // end if (type == punct_queryend)
1057 } // end if ($seek_queryend)
1060 * Note: do not split if this is a punct_queryend for the first and only query
1061 * @todo when we find a UNION, should we split in another subresult?
1063 if ($arr[$i]['type'] == 'punct_queryend' && ($i +
1 != $size)) {
1064 $result[] = $subresult;
1065 $subresult = $subresult_empty;
1067 } // end if (type == punct_queryend)
1069 // ==============================================================
1070 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1071 $number_of_brackets++
;
1073 $number_of_brackets_in_extract++
;
1075 if ($in_group_concat) {
1076 $number_of_brackets_in_group_concat++
;
1079 // ==============================================================
1080 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1081 $number_of_brackets--;
1082 if ($number_of_brackets == 0) {
1083 $in_subquery = false;
1086 $number_of_brackets_in_extract--;
1087 if ($number_of_brackets_in_extract == 0) {
1088 $in_extract = false;
1091 if ($in_group_concat) {
1092 $number_of_brackets_in_group_concat--;
1093 if ($number_of_brackets_in_group_concat == 0) {
1094 $in_group_concat = false;
1101 * skip the subquery to avoid setting
1102 * select_expr or table_ref with the contents
1103 * of this subquery; this is to avoid a bug when
1104 * trying to edit the results of
1105 * select * from child where not exists (select id from
1106 * parent where child.parent_id = parent.id);
1110 // ==============================================================
1111 if ($arr[$i]['type'] == 'alpha_functionName') {
1112 $upper_data = strtoupper($arr[$i]['data']);
1113 if ($upper_data =='EXTRACT') {
1115 $number_of_brackets_in_extract = 0;
1117 if ($upper_data =='GROUP_CONCAT') {
1118 $in_group_concat = true;
1119 $number_of_brackets_in_group_concat = 0;
1123 // ==============================================================
1124 if ($arr[$i]['type'] == 'alpha_reservedWord'
1125 //&& $arr[$i]['forbidden'] == false) {
1127 // We don't know what type of query yet, so run this
1128 if ($subresult['querytype'] == '') {
1129 $subresult['querytype'] = strtoupper($arr[$i]['data']);
1130 } // end if (querytype was empty)
1132 // Check if we support this type of query
1133 if (!isset($supported_query_types[$subresult['querytype']])) {
1134 // Skip ahead to the next one if we don't
1135 $seek_queryend = true;
1137 } // end if (query not supported)
1140 $upper_data = strtoupper($arr[$i]['data']);
1142 * @todo reset for each query?
1145 if ($upper_data == 'SELECT') {
1146 if ($number_of_brackets > 0) {
1147 $in_subquery = true;
1148 $seen_subquery = true;
1149 // this is a subquery so do not analyze inside it
1153 $previous_was_identifier = false;
1154 $current_select_expr = -1;
1155 $seen_end_of_table_ref = false;
1156 } // end if (data == SELECT)
1158 if ($upper_data =='FROM' && !$in_extract) {
1159 $current_table_ref = -1;
1161 $previous_was_identifier = false;
1162 $save_table_ref = true;
1163 } // end if (data == FROM)
1165 // here, do not 'continue' the loop, as we have more work for
1166 // reserved words below
1167 } // end if (type == alpha_reservedWord)
1169 // ==============================
1170 if ($arr[$i]['type'] == 'quote_backtick'
1171 ||
$arr[$i]['type'] == 'quote_double'
1172 ||
$arr[$i]['type'] == 'quote_single'
1173 ||
$arr[$i]['type'] == 'alpha_identifier'
1174 ||
($arr[$i]['type'] == 'alpha_reservedWord'
1175 && $arr[$i]['forbidden'] == false)) {
1177 switch ($arr[$i]['type']) {
1178 case 'alpha_identifier':
1179 case 'alpha_reservedWord':
1181 * this is not a real reservedWord, because it's not
1182 * present in the list of forbidden words, for example
1183 * "storage" which can be used as an identifier
1185 * @todo avoid the pretty printing in color in this case
1187 $identifier = $arr[$i]['data'];
1190 case 'quote_backtick':
1191 case 'quote_double':
1192 case 'quote_single':
1193 $identifier = PMA_unQuote($arr[$i]['data']);
1197 if ($subresult['querytype'] == 'SELECT'
1198 && ! $in_group_concat
1199 && ! ($seen_subquery && $arr[$i - 1]['type'] == 'punct_bracket_close_round')) {
1201 if ($previous_was_identifier && isset($chain)) {
1202 // found alias for this select_expr, save it
1203 // but only if we got something in $chain
1204 // (for example, SELECT COUNT(*) AS cnt
1205 // puts nothing in $chain, so we avoid
1206 // setting the alias)
1207 $alias_for_select_expr = $identifier;
1209 $chain[] = $identifier;
1210 $previous_was_identifier = true;
1212 } // end if !$previous_was_identifier
1215 if ($save_table_ref && !$seen_end_of_table_ref) {
1216 if ($previous_was_identifier) {
1217 // found alias for table ref
1218 // save it for later
1219 $alias_for_table_ref = $identifier;
1221 $chain[] = $identifier;
1222 $previous_was_identifier = true;
1224 } // end if ($previous_was_identifier)
1225 } // end if ($save_table_ref &&!$seen_end_of_table_ref)
1226 } // end if (!$seen_from)
1227 } // end if (querytype SELECT)
1228 } // end if (quote_backtick or double quote or alpha_identifier)
1230 // ===================================
1231 if ($arr[$i]['type'] == 'punct_qualifier') {
1232 // to be able to detect an identifier following another
1233 $previous_was_identifier = false;
1235 } // end if (punct_qualifier)
1238 * @todo check if 3 identifiers following one another -> error
1241 // s a v e a s e l e c t e x p r
1242 // finding a list separator or FROM
1243 // means that we must save the current chain of identifiers
1244 // into a select expression
1246 // for now, we only save a select expression if it contains
1247 // at least one identifier, as we are interested in checking
1248 // the columns and table names, so in "select * from persons",
1249 // the "*" is not saved
1251 if (isset($chain) && !$seen_end_of_table_ref
1252 && ((!$seen_from && $arr[$i]['type'] == 'punct_listsep')
1253 ||
($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data == 'FROM'))) {
1254 $size_chain = count($chain);
1255 $current_select_expr++
;
1256 $subresult['select_expr'][$current_select_expr] = array(
1261 'table_true_name' => '',
1265 if (isset($alias_for_select_expr) && strlen($alias_for_select_expr)) {
1266 // we had found an alias for this select expression
1267 $subresult['select_expr'][$current_select_expr]['alias'] = $alias_for_select_expr;
1268 unset($alias_for_select_expr);
1270 // there is at least a column
1271 $subresult['select_expr'][$current_select_expr]['column'] = $chain[$size_chain - 1];
1272 $subresult['select_expr'][$current_select_expr]['expr'] = $chain[$size_chain - 1];
1275 if ($size_chain > 1) {
1276 $subresult['select_expr'][$current_select_expr]['table_name'] = $chain[$size_chain - 2];
1277 // we assume for now that this is also the true name
1278 $subresult['select_expr'][$current_select_expr]['table_true_name'] = $chain[$size_chain - 2];
1279 $subresult['select_expr'][$current_select_expr]['expr']
1280 = $subresult['select_expr'][$current_select_expr]['table_name']
1281 . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
1282 } // end if ($size_chain > 1)
1285 if ($size_chain > 2) {
1286 $subresult['select_expr'][$current_select_expr]['db'] = $chain[$size_chain - 3];
1287 $subresult['select_expr'][$current_select_expr]['expr']
1288 = $subresult['select_expr'][$current_select_expr]['db']
1289 . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
1290 } // end if ($size_chain > 2)
1294 * @todo explain this:
1296 if (($arr[$i]['type'] == 'alpha_reservedWord')
1297 && ($upper_data != 'FROM')) {
1298 $previous_was_identifier = true;
1301 } // end if (save a select expr)
1304 //======================================
1305 // s a v e a t a b l e r e f
1306 //======================================
1308 // maybe we just saw the end of table refs
1309 // but the last table ref has to be saved
1310 // or we are at the last token
1311 // or we just got a reserved word
1313 * @todo there could be another query after this one
1316 if (isset($chain) && $seen_from && $save_table_ref
1317 && ($arr[$i]['type'] == 'punct_listsep'
1318 ||
($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data!="AS")
1319 ||
$seen_end_of_table_ref
1322 $size_chain = count($chain);
1323 $current_table_ref++
;
1324 $subresult['table_ref'][$current_table_ref] = array(
1328 'table_alias' => '',
1329 'table_true_name' => ''
1331 if (isset($alias_for_table_ref) && strlen($alias_for_table_ref)) {
1332 $subresult['table_ref'][$current_table_ref]['table_alias'] = $alias_for_table_ref;
1333 unset($alias_for_table_ref);
1335 $subresult['table_ref'][$current_table_ref]['table_name'] = $chain[$size_chain - 1];
1336 // we assume for now that this is also the true name
1337 $subresult['table_ref'][$current_table_ref]['table_true_name'] = $chain[$size_chain - 1];
1338 $subresult['table_ref'][$current_table_ref]['expr']
1339 = $subresult['table_ref'][$current_table_ref]['table_name'];
1341 if ($size_chain > 1) {
1342 $subresult['table_ref'][$current_table_ref]['db'] = $chain[$size_chain - 2];
1343 $subresult['table_ref'][$current_table_ref]['expr']
1344 = $subresult['table_ref'][$current_table_ref]['db']
1345 . '.' . $subresult['table_ref'][$current_table_ref]['expr'];
1346 } // end if ($size_chain > 1)
1348 // add the table alias into the whole expression
1349 $subresult['table_ref'][$current_table_ref]['expr']
1350 .= ' ' . $subresult['table_ref'][$current_table_ref]['table_alias'];
1353 $previous_was_identifier = true;
1356 } // end if (save a table ref)
1359 // when we have found all table refs,
1360 // for each table_ref alias, put the true name of the table
1361 // in the corresponding select expressions
1363 if (isset($current_table_ref) && ($seen_end_of_table_ref ||
$i == $size-1) && $subresult != $subresult_empty) {
1364 for ($tr=0; $tr <= $current_table_ref; $tr++
) {
1365 $alias = $subresult['table_ref'][$tr]['table_alias'];
1366 $truename = $subresult['table_ref'][$tr]['table_true_name'];
1367 for ($se=0; $se <= $current_select_expr; $se++
) {
1370 && $subresult['select_expr'][$se]['table_true_name'] == $alias
1372 $subresult['select_expr'][$se]['table_true_name'] = $truename;
1373 } // end if (found the alias)
1374 } // end for (select expressions)
1376 } // end for (table refs)
1377 } // end if (set the true names)
1380 // e n d i n g l o o p #1
1381 // set the $previous_was_identifier to false if the current
1382 // token is not an identifier
1383 if (($arr[$i]['type'] != 'alpha_identifier')
1384 && ($arr[$i]['type'] != 'quote_double')
1385 && ($arr[$i]['type'] != 'quote_single')
1386 && ($arr[$i]['type'] != 'quote_backtick')) {
1387 $previous_was_identifier = false;
1390 // however, if we are on AS, we must keep the $previous_was_identifier
1391 if (($arr[$i]['type'] == 'alpha_reservedWord')
1392 && ($upper_data == 'AS')) {
1393 $previous_was_identifier = true;
1396 if (($arr[$i]['type'] == 'alpha_reservedWord')
1397 && ($upper_data =='ON' ||
$upper_data =='USING')) {
1398 $save_table_ref = false;
1399 } // end if (data == ON)
1401 if (($arr[$i]['type'] == 'alpha_reservedWord')
1402 && ($upper_data =='JOIN' ||
$upper_data =='FROM')) {
1403 $save_table_ref = true;
1404 } // end if (data == JOIN)
1407 * no need to check the end of table ref if we already did
1409 * @todo maybe add "&& $seen_from"
1411 if (!$seen_end_of_table_ref) {
1412 // if this is the last token, it implies that we have
1413 // seen the end of table references
1414 // Check for the end of table references
1416 // Note: if we are analyzing a GROUP_CONCAT clause,
1417 // we might find a word that seems to indicate that
1418 // we have found the end of table refs (like ORDER)
1419 // but it's a modifier of the GROUP_CONCAT so
1420 // it's not the real end of table refs
1422 ||
($arr[$i]['type'] == 'alpha_reservedWord'
1423 && !$in_group_concat
1424 && isset($words_ending_table_ref[$upper_data]))) {
1425 $seen_end_of_table_ref = true;
1426 // to be able to save the last table ref, but do not
1427 // set it true if we found a word like "ON" that has
1428 // already set it to false
1429 if (isset($save_table_ref) && $save_table_ref != false) {
1430 $save_table_ref = true;
1433 } // end if (check for end of table ref)
1434 } //end if (!$seen_end_of_table_ref)
1436 if ($seen_end_of_table_ref) {
1437 $save_table_ref = false;
1440 } // end for $i (loop #1)
1444 if (isset($current_select_expr)) {
1445 for ($trace=0; $trace<=$current_select_expr; $trace++) {
1447 reset ($subresult['select_expr'][$trace]);
1448 while (list ($key, $val) = each ($subresult['select_expr'][$trace]))
1449 echo "sel expr $trace $key => $val<br />\n";
1453 if (isset($current_table_ref)) {
1454 echo "current_table_ref = " . $current_table_ref . "<br>";
1455 for ($trace=0; $trace<=$current_table_ref; $trace++) {
1458 reset ($subresult['table_ref'][$trace]);
1459 while (list ($key, $val) = each ($subresult['table_ref'][$trace]))
1460 echo "table ref $trace $key => $val<br />\n";
1464 // -------------------------------------------------------
1467 // loop #2: - queryflags
1468 // - querytype (for queries != 'SELECT')
1469 // - section_before_limit, section_after_limit
1471 // we will also need this queryflag in loop 2
1473 if (isset($current_table_ref) && $current_table_ref > -1) {
1474 $subresult['queryflags']['select_from'] = 1;
1477 $section_before_limit = '';
1478 $section_after_limit = ''; // truly the section after the limit clause
1479 $seen_reserved_word = false;
1480 $seen_group = false;
1481 $seen_order = false;
1482 $seen_order_by = false;
1483 $in_group_by = false; // true when we are inside the GROUP BY clause
1484 $in_order_by = false; // true when we are inside the ORDER BY clause
1485 $in_having = false; // true when we are inside the HAVING clause
1486 $in_select_expr = false; // true when we are inside the select expr clause
1487 $in_where = false; // true when we are inside the WHERE clause
1488 $seen_limit = false; // true if we have seen a LIMIT clause
1489 $in_limit = false; // true when we are inside the LIMIT clause
1490 $after_limit = false; // true when we are after the LIMIT clause
1491 $in_from = false; // true when we are in the FROM clause
1492 $in_group_concat = false;
1493 $first_reserved_word = '';
1494 $current_identifier = '';
1495 $unsorted_query = $arr['raw']; // in case there is no ORDER BY
1496 $number_of_brackets = 0;
1497 $in_subquery = false;
1499 for ($i = 0; $i < $size; $i++
) {
1500 //DEBUG echo "Loop2 <strong>" . $arr[$i]['data'] . "</strong> (" . $arr[$i]['type'] . ")<br />";
1504 // check for reserved words that will have to generate
1505 // a confirmation request later in sql.php
1509 // ALTER TABLE... DROP
1512 // this code is not used for confirmations coming from functions.js
1514 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1515 $number_of_brackets++
;
1518 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1519 $number_of_brackets--;
1520 if ($number_of_brackets == 0) {
1521 $in_subquery = false;
1525 if ($arr[$i]['type'] == 'alpha_reservedWord') {
1526 $upper_data = strtoupper($arr[$i]['data']);
1528 if ($upper_data == 'SELECT' && $number_of_brackets > 0) {
1529 $in_subquery = true;
1532 if (!$seen_reserved_word) {
1533 $first_reserved_word = $upper_data;
1534 $subresult['querytype'] = $upper_data;
1535 $seen_reserved_word = true;
1537 // if the first reserved word is DROP or DELETE,
1538 // we know this is a query that needs to be confirmed
1539 if ($first_reserved_word=='DROP'
1540 ||
$first_reserved_word == 'DELETE'
1541 ||
$first_reserved_word == 'TRUNCATE') {
1542 $subresult['queryflags']['need_confirm'] = 1;
1545 if ($first_reserved_word=='SELECT') {
1546 $position_of_first_select = $i;
1550 if ($upper_data == 'DROP' && $first_reserved_word == 'ALTER') {
1551 $subresult['queryflags']['need_confirm'] = 1;
1555 if ($upper_data == 'LIMIT' && ! $in_subquery) {
1556 $section_before_limit = substr($arr['raw'], 0, $arr[$i]['pos'] - 5);
1560 $in_order_by = false; // @todo maybe others to set false
1563 if ($upper_data == 'PROCEDURE') {
1564 $subresult['queryflags']['procedure'] = 1;
1566 $after_limit = true;
1569 * @todo set also to false if we find FOR UPDATE or LOCK IN SHARE MODE
1571 if ($upper_data == 'SELECT') {
1572 $in_select_expr = true;
1573 $select_expr_clause = '';
1575 if ($upper_data == 'DISTINCT' && !$in_group_concat) {
1576 $subresult['queryflags']['distinct'] = 1;
1579 if ($upper_data == 'UNION') {
1580 $subresult['queryflags']['union'] = 1;
1583 if ($upper_data == 'JOIN') {
1584 $subresult['queryflags']['join'] = 1;
1587 if ($upper_data == 'OFFSET') {
1588 $subresult['queryflags']['offset'] = 1;
1591 // if this is a real SELECT...FROM
1592 if ($upper_data == 'FROM' && isset($subresult['queryflags']['select_from']) && $subresult['queryflags']['select_from'] == 1) {
1595 $in_select_expr = false;
1599 // (we could have less resetting of variables to false
1600 // if we trust that the query respects the standard
1601 // MySQL order for clauses)
1603 // we use $seen_group and $seen_order because we are looking
1605 if ($upper_data == 'GROUP') {
1607 $seen_order = false;
1609 $in_order_by = false;
1611 $in_select_expr = false;
1614 if ($upper_data == 'ORDER' && !$in_group_concat) {
1616 $seen_group = false;
1618 $in_group_by = false;
1620 $in_select_expr = false;
1623 if ($upper_data == 'HAVING') {
1625 $having_clause = '';
1626 $seen_group = false;
1627 $seen_order = false;
1628 $in_group_by = false;
1629 $in_order_by = false;
1631 $in_select_expr = false;
1635 if ($upper_data == 'WHERE') {
1638 $where_clause_identifiers = array();
1639 $seen_group = false;
1640 $seen_order = false;
1641 $in_group_by = false;
1642 $in_order_by = false;
1644 $in_select_expr = false;
1648 if ($upper_data == 'BY') {
1650 $in_group_by = true;
1651 $group_by_clause = '';
1654 $seen_order_by = true;
1655 // Here we assume that the ORDER BY keywords took
1656 // exactly 8 characters.
1657 // We use PMA_substr() to be charset-safe; otherwise
1658 // if the table name contains accents, the unsorted
1659 // query would be missing some characters.
1660 $unsorted_query = PMA_substr($arr['raw'], 0, $arr[$i]['pos'] - 8);
1661 $in_order_by = true;
1662 $order_by_clause = '';
1666 // if we find one of the words that could end the clause
1667 if (isset($words_ending_clauses[$upper_data])) {
1669 $in_group_by = false;
1670 $in_order_by = false;
1673 $in_select_expr = false;
1677 } // endif (reservedWord)
1680 // do not add a space after a function name
1682 * @todo can we combine loop 2 and loop 1? some code is repeated here...
1686 if ($arr[$i]['type'] == 'alpha_functionName') {
1688 $upper_data = strtoupper($arr[$i]['data']);
1689 if ($upper_data =='GROUP_CONCAT') {
1690 $in_group_concat = true;
1691 $number_of_brackets_in_group_concat = 0;
1695 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1696 if ($in_group_concat) {
1697 $number_of_brackets_in_group_concat++
;
1700 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1701 if ($in_group_concat) {
1702 $number_of_brackets_in_group_concat--;
1703 if ($number_of_brackets_in_group_concat == 0) {
1704 $in_group_concat = false;
1709 // do not add a space after an identifier if followed by a dot
1710 if ($arr[$i]['type'] == 'alpha_identifier' && $i < $size - 1 && $arr[$i +
1]['data'] == '.') {
1714 // do not add a space after a dot if followed by an identifier
1715 if ($arr[$i]['data'] == '.' && $i < $size - 1 && $arr[$i +
1]['type'] == 'alpha_identifier') {
1719 if ($in_select_expr && $upper_data != 'SELECT' && $upper_data != 'DISTINCT') {
1720 $select_expr_clause .= $arr[$i]['data'] . $sep;
1722 if ($in_from && $upper_data != 'FROM') {
1723 $from_clause .= $arr[$i]['data'] . $sep;
1725 if ($in_group_by && $upper_data != 'GROUP' && $upper_data != 'BY') {
1726 $group_by_clause .= $arr[$i]['data'] . $sep;
1728 if ($in_order_by && $upper_data != 'ORDER' && $upper_data != 'BY') {
1729 // add a space only before ASC or DESC
1730 // not around the dot between dbname and tablename
1731 if ($arr[$i]['type'] == 'alpha_reservedWord') {
1732 $order_by_clause .= $sep;
1734 $order_by_clause .= $arr[$i]['data'];
1736 if ($in_having && $upper_data != 'HAVING') {
1737 $having_clause .= $arr[$i]['data'] . $sep;
1739 if ($in_where && $upper_data != 'WHERE') {
1740 $where_clause .= $arr[$i]['data'] . $sep;
1742 if (($arr[$i]['type'] == 'quote_backtick')
1743 ||
($arr[$i]['type'] == 'alpha_identifier')) {
1744 $where_clause_identifiers[] = $arr[$i]['data'];
1748 // to grab the rest of the query after the ORDER BY clause
1749 if (isset($subresult['queryflags']['select_from'])
1750 && $subresult['queryflags']['select_from'] == 1
1753 && $upper_data != 'BY') {
1754 $unsorted_query .= $arr[$i]['data'];
1755 if ($arr[$i]['type'] != 'punct_bracket_open_round'
1756 && $arr[$i]['type'] != 'punct_bracket_close_round'
1757 && $arr[$i]['type'] != 'punct') {
1758 $unsorted_query .= $sep;
1763 if ($upper_data == 'OFFSET') {
1764 $limit_clause .= $sep;
1766 $limit_clause .= $arr[$i]['data'];
1767 if ($upper_data == 'LIMIT' ||
$upper_data == 'OFFSET') {
1768 $limit_clause .= $sep;
1771 if ($after_limit && $seen_limit) {
1772 $section_after_limit .= $arr[$i]['data'] . $sep;
1775 // clear $upper_data for next iteration
1777 } // end for $i (loop #2)
1778 if (empty($section_before_limit)) {
1779 $section_before_limit = $arr['raw'];
1782 // -----------------------------------------------------
1783 // loop #3: foreign keys and MySQL 4.1.2+ TIMESTAMP options
1784 // (for now, check only the first query)
1785 // (for now, identifiers are assumed to be backquoted)
1787 // If we find that we are dealing with a CREATE TABLE query,
1788 // we look for the next punct_bracket_open_round, which
1789 // introduces the fields list. Then, when we find a
1790 // quote_backtick, it must be a field, so we put it into
1791 // the create_table_fields array. Even if this field is
1792 // not a timestamp, it will be useful when logic has been
1793 // added for complete field attributes analysis.
1795 $seen_foreign = false;
1796 $seen_references = false;
1797 $seen_constraint = false;
1798 $foreign_key_number = -1;
1799 $seen_create_table = false;
1800 $seen_create = false;
1801 $seen_alter = false;
1802 $in_create_table_fields = false;
1803 $brackets_level = 0;
1804 $in_timestamp_options = false;
1805 $seen_default = false;
1807 for ($i = 0; $i < $size; $i++
) {
1808 // DEBUG echo "Loop 3 <strong>" . $arr[$i]['data'] . "</strong> " . $arr[$i]['type'] . "<br />";
1810 if ($arr[$i]['type'] == 'alpha_reservedWord') {
1811 $upper_data = strtoupper($arr[$i]['data']);
1813 if ($upper_data == 'NOT' && $in_timestamp_options) {
1814 $create_table_fields[$current_identifier]['timestamp_not_null'] = true;
1818 if ($upper_data == 'CREATE') {
1819 $seen_create = true;
1822 if ($upper_data == 'ALTER') {
1826 if ($upper_data == 'TABLE' && $seen_create) {
1827 $seen_create_table = true;
1828 $create_table_fields = array();
1831 if ($upper_data == 'CURRENT_TIMESTAMP') {
1832 if ($in_timestamp_options) {
1833 if ($seen_default) {
1834 $create_table_fields[$current_identifier]['default_current_timestamp'] = true;
1839 if ($upper_data == 'CONSTRAINT') {
1840 $foreign_key_number++
;
1841 $seen_foreign = false;
1842 $seen_references = false;
1843 $seen_constraint = true;
1845 if ($upper_data == 'FOREIGN') {
1846 $seen_foreign = true;
1847 $seen_references = false;
1848 $seen_constraint = false;
1850 if ($upper_data == 'REFERENCES') {
1851 $seen_foreign = false;
1852 $seen_references = true;
1853 $seen_constraint = false;
1859 // [ON DELETE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
1860 // [ON UPDATE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
1862 // but we set ['on_delete'] or ['on_cascade'] to
1863 // CASCADE | SET_NULL | NO_ACTION | RESTRICT
1865 // ON UPDATE CURRENT_TIMESTAMP
1867 if ($upper_data == 'ON') {
1868 if (isset($arr[$i+
1]) && $arr[$i+
1]['type'] == 'alpha_reservedWord') {
1869 $second_upper_data = strtoupper($arr[$i+
1]['data']);
1870 if ($second_upper_data == 'DELETE') {
1871 $clause = 'on_delete';
1873 if ($second_upper_data == 'UPDATE') {
1874 $clause = 'on_update';
1877 && ($arr[$i+
2]['type'] == 'alpha_reservedWord'
1879 // ugly workaround because currently, NO is not
1880 // in the list of reserved words in sqlparser.data
1881 // (we got a bug report about not being able to use
1882 // 'no' as an identifier)
1883 ||
($arr[$i+
2]['type'] == 'alpha_identifier'
1884 && strtoupper($arr[$i+
2]['data'])=='NO'))
1886 $third_upper_data = strtoupper($arr[$i+
2]['data']);
1887 if ($third_upper_data == 'CASCADE'
1888 ||
$third_upper_data == 'RESTRICT') {
1889 $value = $third_upper_data;
1890 } elseif ($third_upper_data == 'SET'
1891 ||
$third_upper_data == 'NO') {
1892 if ($arr[$i+
3]['type'] == 'alpha_reservedWord') {
1893 $value = $third_upper_data . '_' . strtoupper($arr[$i+
3]['data']);
1895 } elseif ($third_upper_data == 'CURRENT_TIMESTAMP') {
1896 if ($clause == 'on_update'
1897 && $in_timestamp_options) {
1898 $create_table_fields[$current_identifier]['on_update_current_timestamp'] = true;
1899 $seen_default = false;
1905 if (!empty($value)) {
1906 $foreign[$foreign_key_number][$clause] = $value;
1909 } // endif (isset($clause))
1913 } // end of reserved words analysis
1916 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1918 if ($seen_create_table && $brackets_level == 1) {
1919 $in_create_table_fields = true;
1924 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1926 if ($seen_references) {
1927 $seen_references = false;
1929 if ($seen_create_table && $brackets_level == 0) {
1930 $in_create_table_fields = false;
1934 if (($arr[$i]['type'] == 'alpha_columnAttrib')) {
1935 $upper_data = strtoupper($arr[$i]['data']);
1936 if ($seen_create_table && $in_create_table_fields) {
1937 if ($upper_data == 'DEFAULT') {
1938 $seen_default = true;
1939 $create_table_fields[$current_identifier]['default_value'] = $arr[$i +
1]['data'];
1945 * @see @todo 2005-10-16 note: the "or" part here is a workaround for a bug
1947 if (($arr[$i]['type'] == 'alpha_columnType') ||
($arr[$i]['type'] == 'alpha_functionName' && $seen_create_table)) {
1948 $upper_data = strtoupper($arr[$i]['data']);
1949 if ($seen_create_table && $in_create_table_fields && isset($current_identifier)) {
1950 $create_table_fields[$current_identifier]['type'] = $upper_data;
1951 if ($upper_data == 'TIMESTAMP') {
1952 $arr[$i]['type'] = 'alpha_columnType';
1953 $in_timestamp_options = true;
1955 $in_timestamp_options = false;
1956 if ($upper_data == 'CHAR') {
1957 $arr[$i]['type'] = 'alpha_columnType';
1964 if ($arr[$i]['type'] == 'quote_backtick' ||
$arr[$i]['type'] == 'alpha_identifier') {
1966 if ($arr[$i]['type'] == 'quote_backtick') {
1967 // remove backquotes
1968 $identifier = PMA_unQuote($arr[$i]['data']);
1970 $identifier = $arr[$i]['data'];
1973 if ($seen_create_table && $in_create_table_fields) {
1974 $current_identifier = $identifier;
1975 // we set this one even for non TIMESTAMP type
1976 $create_table_fields[$current_identifier]['timestamp_not_null'] = false;
1979 if ($seen_constraint) {
1980 $foreign[$foreign_key_number]['constraint'] = $identifier;
1983 if ($seen_foreign && $brackets_level > 0) {
1984 $foreign[$foreign_key_number]['index_list'][] = $identifier;
1987 if ($seen_references) {
1988 if ($seen_alter && $brackets_level > 0) {
1989 $foreign[$foreign_key_number]['ref_index_list'][] = $identifier;
1990 // here, the first bracket level corresponds to the
1991 // bracket of CREATE TABLE
1992 // so if we are on level 2, it must be the index list
1993 // of the foreign key REFERENCES
1994 } elseif ($brackets_level > 1) {
1995 $foreign[$foreign_key_number]['ref_index_list'][] = $identifier;
1996 } elseif ($arr[$i+
1]['type'] == 'punct_qualifier') {
1997 // identifier is `db`.`table`
1998 // the first pass will pick the db name
1999 // the next pass will pick the table name
2000 $foreign[$foreign_key_number]['ref_db_name'] = $identifier;
2002 // identifier is `table`
2003 $foreign[$foreign_key_number]['ref_table_name'] = $identifier;
2007 } // end for $i (loop #3)
2010 // Fill the $subresult array
2012 if (isset($create_table_fields)) {
2013 $subresult['create_table_fields'] = $create_table_fields;
2016 if (isset($foreign)) {
2017 $subresult['foreign_keys'] = $foreign;
2020 if (isset($select_expr_clause)) {
2021 $subresult['select_expr_clause'] = $select_expr_clause;
2023 if (isset($from_clause)) {
2024 $subresult['from_clause'] = $from_clause;
2026 if (isset($group_by_clause)) {
2027 $subresult['group_by_clause'] = $group_by_clause;
2029 if (isset($order_by_clause)) {
2030 $subresult['order_by_clause'] = $order_by_clause;
2032 if (isset($having_clause)) {
2033 $subresult['having_clause'] = $having_clause;
2035 if (isset($limit_clause)) {
2036 $subresult['limit_clause'] = $limit_clause;
2038 if (isset($where_clause)) {
2039 $subresult['where_clause'] = $where_clause;
2041 if (isset($unsorted_query) && !empty($unsorted_query)) {
2042 $subresult['unsorted_query'] = $unsorted_query;
2044 if (isset($where_clause_identifiers)) {
2045 $subresult['where_clause_identifiers'] = $where_clause_identifiers;
2048 if (isset($position_of_first_select)) {
2049 $subresult['position_of_first_select'] = $position_of_first_select;
2050 $subresult['section_before_limit'] = $section_before_limit;
2051 $subresult['section_after_limit'] = $section_after_limit;
2054 // They are naughty and didn't have a trailing semi-colon,
2055 // then still handle it properly
2056 if ($subresult['querytype'] != '') {
2057 $result[] = $subresult;
2060 } // end of the "PMA_SQP_analyze()" function
2064 * Colorizes SQL queries html formatted
2066 * @todo check why adding a "\n" after the </span> would cause extra blanks
2067 * to be displayed: SELECT p . person_name
2068 * @param array The SQL queries html formatted
2070 * @return array The colorized SQL queries
2074 function PMA_SQP_formatHtml_colorize($arr)
2076 $i = PMA_strpos($arr['type'], '_');
2079 $class = 'syntax_' . PMA_substr($arr['type'], 0, $i) . ' ';
2082 $class .= 'syntax_' . $arr['type'];
2084 return '<span class="' . $class . '">' . htmlspecialchars($arr['data']) . '</span>';
2085 } // end of the "PMA_SQP_formatHtml_colorize()" function
2089 * Formats SQL queries to html
2091 * @param array The SQL queries
2092 * @param string mode
2093 * @param integer starting token
2094 * @param integer number of tokens to format, -1 = all
2096 * @return string The formatted SQL queries
2100 function PMA_SQP_formatHtml($arr, $mode='color', $start_token=0,
2101 $number_of_tokens=-1)
2103 global $PMA_SQPdata_operators_docs, $PMA_SQPdata_functions_docs;
2104 //DEBUG echo 'in Format<pre>'; print_r($arr); echo '</pre>';
2105 // then check for an array
2106 if (! is_array($arr)) {
2107 return htmlspecialchars($arr);
2109 // first check for the SQL parser having hit an error
2110 if (PMA_SQP_isError()) {
2111 return htmlspecialchars($arr['raw']);
2113 // else do it properly
2116 $str = '<span class="syntax">';
2117 $html_line_break = '<br />';
2122 $html_line_break = "\n";
2127 $html_line_break = '<br />';
2131 // inner_sql is a span that exists for all cases, except query_only
2132 // of $cfg['SQP']['fmtType'] to make possible a replacement
2133 // for inline editing
2134 if ($mode!='query_only') {
2135 $str .= '<span class="inner_sql">';
2137 $close_docu_link = false;
2141 $infunction = false;
2142 $space_punct_listsep = ' ';
2143 $space_punct_listsep_function_name = ' ';
2144 // $space_alpha_reserved_word = '<br />'."\n";
2145 $space_alpha_reserved_word = ' ';
2147 $keywords_with_brackets_1before = array(
2154 $keywords_with_brackets_2before = array(
2166 // These reserved words do NOT get a newline placed near them.
2167 $keywords_no_newline = array(
2183 // These reserved words introduce a privilege list
2184 $keywords_priv_list = array(
2189 if ($number_of_tokens == -1) {
2190 $number_of_tokens = $arr['len'];
2193 if ($number_of_tokens >= 0) {
2197 $typearr[3] = $arr[$start_token]['type'];
2200 $in_priv_list = false;
2201 for ($i = $start_token; $i < $number_of_tokens; $i++
) {
2202 // DEBUG echo "Loop format <strong>" . $arr[$i]['data'] . "</strong> " . $arr[$i]['type'] . "<br />";
2205 // array_shift($typearr);
2212 if (($i +
1) < $number_of_tokens) {
2213 $typearr[4] = $arr[$i +
1]['type'];
2218 for ($j=0; $j<4; $j++
) {
2219 $typearr[$j] = $typearr[$j +
1];
2222 switch ($typearr[2]) {
2223 case 'alpha_bitfield_constant_introducer':
2227 case 'white_newline':
2230 case 'punct_bracket_open_round':
2232 $infunction = false;
2233 // Make sure this array is sorted!
2234 if (($typearr[1] == 'alpha_functionName') ||
($typearr[1] == 'alpha_columnType') ||
($typearr[1] == 'punct')
2235 ||
($typearr[3] == 'digit_integer') ||
($typearr[3] == 'digit_hex') ||
($typearr[3] == 'digit_float')
2236 ||
(($typearr[0] == 'alpha_reservedWord')
2237 && isset($keywords_with_brackets_2before[strtoupper($arr[$i - 2]['data'])]))
2238 ||
(($typearr[1] == 'alpha_reservedWord')
2239 && isset($keywords_with_brackets_1before[strtoupper($arr[$i - 1]['data'])]))
2246 $after .= ($mode != 'query_only' ?
'<div class="syntax_indent' . $indent . '">' : ' ');
2249 case 'alpha_identifier':
2250 if (($typearr[1] == 'punct_qualifier') ||
($typearr[3] == 'punct_qualifier')) {
2254 // for example SELECT 1 somealias
2255 if ($typearr[1] == 'digit_integer') {
2258 if (($typearr[3] == 'alpha_columnType') ||
($typearr[3] == 'alpha_identifier')) {
2263 case 'punct_qualifier':
2267 case 'punct_listsep':
2268 if ($infunction == true) {
2269 $after .= $space_punct_listsep_function_name;
2271 $after .= $space_punct_listsep;
2274 case 'punct_queryend':
2275 if (($typearr[3] != 'comment_mysql') && ($typearr[3] != 'comment_ansi') && $typearr[3] != 'comment_c') {
2276 $after .= $html_line_break;
2277 $after .= $html_line_break;
2279 $space_punct_listsep = ' ';
2280 $space_punct_listsep_function_name = ' ';
2281 $space_alpha_reserved_word = ' ';
2282 $in_priv_list = false;
2284 case 'comment_mysql':
2285 case 'comment_ansi':
2286 $after .= $html_line_break;
2290 if ($docu && isset($PMA_SQPdata_operators_docs[$arr[$i]['data']]) &&
2291 ($arr[$i]['data'] != '*' ||
in_array($arr[$i]['type'], array('digit_integer','digit_float','digit_hex')))) {
2292 $before .= PMA_showMySQLDocu(
2294 $PMA_SQPdata_operators_docs[$arr[$i]['data']]['link'],
2296 $PMA_SQPdata_operators_docs[$arr[$i]['data']]['anchor'],
2302 // select * from mytable limit 0,-1
2303 // (a side effect of this workaround is that
2308 if ($typearr[3] != 'digit_integer') {
2312 case 'punct_bracket_close_round':
2313 // only close bracket level when it was opened before
2314 if ($bracketlevel > 0) {
2316 if ($infunction == true) {
2322 $before .= ($mode != 'query_only' ?
'</div>' : ' ');
2324 $infunction = ($functionlevel > 0) ?
true : false;
2327 case 'alpha_columnType':
2329 switch ($arr[$i]['data']) {
2342 $before .= PMA_showMySQLDocu('data-types', 'numeric-types', false, '', true);
2343 $after = '</a>' . $after;
2350 $before .= PMA_showMySQLDocu('data-types', 'date-and-time-types', false, '', true);
2351 $after = '</a>' . $after;
2367 $before .= PMA_showMySQLDocu('data-types', 'string-types', false, '', true);
2368 $after = '</a>' . $after;
2372 if ($typearr[3] == 'alpha_columnAttrib') {
2375 if ($typearr[1] == 'alpha_columnType') {
2379 case 'alpha_columnAttrib':
2381 // ALTER TABLE tbl_name AUTO_INCREMENT = 1
2382 // COLLATE LATIN1_GENERAL_CI DEFAULT
2383 if ($typearr[1] == 'alpha_identifier' ||
$typearr[1] == 'alpha_charset') {
2386 if (($typearr[3] == 'alpha_columnAttrib') ||
($typearr[3] == 'quote_single') ||
($typearr[3] == 'digit_integer')) {
2390 // AUTO_INCREMENT = 31DEFAULT_CHARSET = utf-8
2392 if ($typearr[2] == 'alpha_columnAttrib' && $typearr[3] == 'alpha_reservedWord') {
2396 // select * from mysql.user where binary user="root"
2397 // binary is marked as alpha_columnAttrib
2398 // but should be marked as a reserved word
2399 if (strtoupper($arr[$i]['data']) == 'BINARY'
2400 && $typearr[3] == 'alpha_identifier') {
2404 case 'alpha_functionName':
2405 $funcname = strtoupper($arr[$i]['data']);
2406 if ($docu && isset($PMA_SQPdata_functions_docs[$funcname])) {
2407 $before .= PMA_showMySQLDocu(
2409 $PMA_SQPdata_functions_docs[$funcname]['link'],
2411 $PMA_SQPdata_functions_docs[$funcname]['anchor'],
2416 case 'alpha_reservedWord':
2417 // do not uppercase the reserved word if we are calling
2418 // this function in query_only mode, because we need
2419 // the original query (otherwise we get problems with
2420 // semi-reserved words like "storage" which is legal
2421 // as an identifier name)
2423 if ($mode != 'query_only') {
2424 $arr[$i]['data'] = strtoupper($arr[$i]['data']);
2427 if ((($typearr[1] != 'alpha_reservedWord')
2428 ||
(($typearr[1] == 'alpha_reservedWord')
2429 && isset($keywords_no_newline[strtoupper($arr[$i - 1]['data'])])))
2430 && ($typearr[1] != 'punct_level_plus')
2431 && (!isset($keywords_no_newline[$arr[$i]['data']]))) {
2432 // do not put a space before the first token, because
2433 // we use a lot of pattern matching checking for the
2434 // first reserved word at beginning of query
2435 // so do not put a newline before
2437 // also we must not be inside a privilege list
2439 // the alpha_identifier exception is there to
2441 // GRANT SELECT ON mydb.mytable TO myuser@localhost
2442 // (else, we get mydb.mytableTO)
2444 // the quote_single exception is there to
2446 // GRANT ... TO 'marc'@'domain.com' IDENTIFIED...
2448 * @todo fix all cases and find why this happens
2451 if (!$in_priv_list ||
$typearr[1] == 'alpha_identifier' ||
$typearr[1] == 'quote_single' ||
$typearr[1] == 'white_newline') {
2452 $before .= $space_alpha_reserved_word;
2455 // on first keyword, check if it introduces a
2457 if (isset($keywords_priv_list[$arr[$i]['data']])) {
2458 $in_priv_list = true;
2465 switch ($arr[$i]['data']) {
2475 switch ($arr[$i +
1]['data']) {
2486 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'] . '_' . $arr[$i +
1]['data'], false, '', true);
2487 $close_docu_link = true;
2490 if ($arr[$i +
1]['data'] == 'LOGFILE' && $arr[$i +
2]['data'] == 'GROUP') {
2491 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'] . '_LOGFILE_GROUP', false, '', true);
2492 $close_docu_link = true;
2495 if (!$in_priv_list) {
2496 $space_punct_listsep = $html_line_break;
2497 $space_alpha_reserved_word = ' ';
2511 if ($close_docu_link) {
2512 $after = '</a>' . $after;
2513 $close_docu_link = false;
2517 if ($docu && ($i == 0 ||
$arr[$i - 1]['data'] != 'CHARACTER')) {
2518 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
2519 $after = '</a>' . $after;
2521 if (!$in_priv_list) {
2522 $space_punct_listsep = $html_line_break;
2523 $space_alpha_reserved_word = ' ';
2532 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
2533 $after = '</a>' . $after;
2535 if (!$in_priv_list) {
2536 $space_punct_listsep = $html_line_break;
2537 $space_alpha_reserved_word = ' ';
2543 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
2544 $after = '</a>' . $after;
2546 if (!$in_priv_list) {
2547 $space_punct_listsep = $html_line_break;
2548 $space_alpha_reserved_word = $html_line_break;
2552 $space_punct_listsep = ' ';
2553 $space_alpha_reserved_word = $html_line_break;
2557 $before .= PMA_showMySQLDocu('SQL-Syntax', 'SELECT', false, '', true);
2558 $after = '</a>' . $after;
2560 $space_punct_listsep = ' ';
2561 $space_alpha_reserved_word = $html_line_break;
2567 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
2568 $after = '</a>' . $after;
2572 if ($close_docu_link && in_array($arr[$i]['data'], array('LIKE', 'NOT', 'IN', 'REGEXP', 'NULL'))) {
2574 $close_docu_link = false;
2575 } else if ($docu && isset($PMA_SQPdata_functions_docs[$arr[$i]['data']])) {
2576 /* Handle multi word statements first */
2577 if (isset($typearr[4]) && $typearr[4] == 'alpha_reservedWord' && $typearr[3] == 'alpha_reservedWord' && isset($PMA_SQPdata_functions_docs[strtoupper($arr[$i]['data'] . '_' . $arr[$i +
1]['data'] . '_' . $arr[$i +
2]['data'])])) {
2578 $tempname = strtoupper($arr[$i]['data'] . '_' . $arr[$i +
1]['data'] . '_' . $arr[$i +
2]['data']);
2579 $before .= PMA_showMySQLDocu('functions', $PMA_SQPdata_functions_docs[$tempname]['link'], false, $PMA_SQPdata_functions_docs[$tempname]['anchor'], true);
2580 $close_docu_link = true;
2581 } else if (isset($typearr[3]) && $typearr[3] == 'alpha_reservedWord' && isset($PMA_SQPdata_functions_docs[strtoupper($arr[$i]['data'] . '_' . $arr[$i +
1]['data'])])) {
2582 $tempname = strtoupper($arr[$i]['data'] . '_' . $arr[$i +
1]['data']);
2583 $before .= PMA_showMySQLDocu('functions', $PMA_SQPdata_functions_docs[$tempname]['link'], false, $PMA_SQPdata_functions_docs[$tempname]['anchor'], true);
2584 $close_docu_link = true;
2586 $before .= PMA_showMySQLDocu('functions', $PMA_SQPdata_functions_docs[$arr[$i]['data']]['link'], false, $PMA_SQPdata_functions_docs[$arr[$i]['data']]['anchor'], true);
2591 } // end switch ($arr[$i]['data'])
2595 case 'digit_integer':
2599 * @todo could there be other types preceding a digit?
2601 if ($typearr[1] == 'alpha_reservedWord') {
2604 if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
2607 if ($typearr[1] == 'alpha_columnAttrib') {
2611 case 'alpha_variable':
2614 case 'quote_double':
2615 case 'quote_single':
2616 // workaround: for the query
2617 // REVOKE SELECT ON `base2\_db`.* FROM 'user'@'%'
2618 // the @ is incorrectly marked as alpha_variable
2619 // in the parser, and here, the '%' gets a blank before,
2620 // which is a syntax error
2621 if ($typearr[1] != 'punct_user' && $typearr[1] != 'alpha_bitfield_constant_introducer') {
2624 if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
2628 case 'quote_backtick':
2629 // here we check for punct_user to handle correctly
2630 // DEFINER = `username`@`%`
2631 // where @ is the punct_user and `%` is the quote_backtick
2632 if ($typearr[3] != 'punct_qualifier' && $typearr[3] != 'alpha_variable' && $typearr[3] != 'punct_user') {
2635 if ($typearr[1] != 'punct_qualifier' && $typearr[1] != 'alpha_variable' && $typearr[1] != 'punct_user') {
2641 } // end switch ($typearr[2])
2644 if ($typearr[3] != 'punct_qualifier') {
2650 if ($mode=='color') {
2651 $str .= PMA_SQP_formatHTML_colorize($arr[$i]);
2652 } elseif ($mode == 'text') {
2653 $str .= htmlspecialchars($arr[$i]['data']);
2655 $str .= $arr[$i]['data'];
2659 // close unclosed indent levels
2660 while ($indent > 0) {
2662 $str .= ($mode != 'query_only' ?
'</div>' : ' ');
2664 /* End possibly unclosed documentation link */
2665 if ($close_docu_link) {
2667 $close_docu_link = false;
2669 if ($mode!='query_only') {
2670 // close inner_sql span
2673 if ($mode=='color') {
2674 // close syntax span
2679 } // end of the "PMA_SQP_formatHtml()" function
2683 * Builds a CSS rule used for html formatted SQL queries
2685 * @param string The class name
2686 * @param string The property name
2687 * @param string The property value
2689 * @return string The CSS rule
2693 * @see PMA_SQP_buildCssData()
2695 function PMA_SQP_buildCssRule($classname, $property, $value)
2697 $str = '.' . $classname . ' {';
2699 $str .= $property . ': ' . $value . ';';
2704 } // end of the "PMA_SQP_buildCssRule()" function
2708 * Builds CSS rules used for html formatted SQL queries
2710 * @return string The CSS rules set
2714 * @global array The current PMA configuration
2716 * @see PMA_SQP_buildCssRule()
2718 function PMA_SQP_buildCssData()
2723 foreach ($cfg['SQP']['fmtColor'] AS $key => $col) {
2724 $css_string .= PMA_SQP_buildCssRule('syntax_' . $key, 'color', $col);
2727 for ($i = 0; $i < 8; $i++
) {
2728 $css_string .= PMA_SQP_buildCssRule(
2729 'syntax_indent' . $i, 'margin-left',
2730 ($i * $cfg['SQP']['fmtInd']) . $cfg['SQP']['fmtIndUnit']);
2734 } // end of the "PMA_SQP_buildCssData()" function
2736 if (! defined('PMA_MINIMUM_COMMON')) {
2738 * Gets SQL queries with no format
2740 * @param array The SQL queries list
2742 * @return string The SQL queries with no format
2746 function PMA_SQP_formatNone($arr)
2748 $formatted_sql = htmlspecialchars($arr['raw']);
2749 $formatted_sql = preg_replace(
2750 "@((\015\012)|(\015)|(\012)){3,}@",
2754 return $formatted_sql;
2755 } // end of the "PMA_SQP_formatNone()" function
2757 } // end if: minimal common.lib needed?