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++
) {
1368 if (isset($alias) && strlen($alias) && $subresult['select_expr'][$se]['table_true_name']
1370 $subresult['select_expr'][$se]['table_true_name']
1372 } // end if (found the alias)
1373 } // end for (select expressions)
1375 } // end for (table refs)
1376 } // end if (set the true names)
1379 // e n d i n g l o o p #1
1380 // set the $previous_was_identifier to false if the current
1381 // token is not an identifier
1382 if (($arr[$i]['type'] != 'alpha_identifier')
1383 && ($arr[$i]['type'] != 'quote_double')
1384 && ($arr[$i]['type'] != 'quote_single')
1385 && ($arr[$i]['type'] != 'quote_backtick')) {
1386 $previous_was_identifier = false;
1389 // however, if we are on AS, we must keep the $previous_was_identifier
1390 if (($arr[$i]['type'] == 'alpha_reservedWord')
1391 && ($upper_data == 'AS')) {
1392 $previous_was_identifier = true;
1395 if (($arr[$i]['type'] == 'alpha_reservedWord')
1396 && ($upper_data =='ON' ||
$upper_data =='USING')) {
1397 $save_table_ref = false;
1398 } // end if (data == ON)
1400 if (($arr[$i]['type'] == 'alpha_reservedWord')
1401 && ($upper_data =='JOIN' ||
$upper_data =='FROM')) {
1402 $save_table_ref = true;
1403 } // end if (data == JOIN)
1406 * no need to check the end of table ref if we already did
1408 * @todo maybe add "&& $seen_from"
1410 if (!$seen_end_of_table_ref) {
1411 // if this is the last token, it implies that we have
1412 // seen the end of table references
1413 // Check for the end of table references
1415 // Note: if we are analyzing a GROUP_CONCAT clause,
1416 // we might find a word that seems to indicate that
1417 // we have found the end of table refs (like ORDER)
1418 // but it's a modifier of the GROUP_CONCAT so
1419 // it's not the real end of table refs
1421 ||
($arr[$i]['type'] == 'alpha_reservedWord'
1422 && !$in_group_concat
1423 && isset($words_ending_table_ref[$upper_data]))) {
1424 $seen_end_of_table_ref = true;
1425 // to be able to save the last table ref, but do not
1426 // set it true if we found a word like "ON" that has
1427 // already set it to false
1428 if (isset($save_table_ref) && $save_table_ref != false) {
1429 $save_table_ref = true;
1432 } // end if (check for end of table ref)
1433 } //end if (!$seen_end_of_table_ref)
1435 if ($seen_end_of_table_ref) {
1436 $save_table_ref = false;
1439 } // end for $i (loop #1)
1443 if (isset($current_select_expr)) {
1444 for ($trace=0; $trace<=$current_select_expr; $trace++) {
1446 reset ($subresult['select_expr'][$trace]);
1447 while (list ($key, $val) = each ($subresult['select_expr'][$trace]))
1448 echo "sel expr $trace $key => $val<br />\n";
1452 if (isset($current_table_ref)) {
1453 echo "current_table_ref = " . $current_table_ref . "<br>";
1454 for ($trace=0; $trace<=$current_table_ref; $trace++) {
1457 reset ($subresult['table_ref'][$trace]);
1458 while (list ($key, $val) = each ($subresult['table_ref'][$trace]))
1459 echo "table ref $trace $key => $val<br />\n";
1463 // -------------------------------------------------------
1466 // loop #2: - queryflags
1467 // - querytype (for queries != 'SELECT')
1468 // - section_before_limit, section_after_limit
1470 // we will also need this queryflag in loop 2
1472 if (isset($current_table_ref) && $current_table_ref > -1) {
1473 $subresult['queryflags']['select_from'] = 1;
1476 $section_before_limit = '';
1477 $section_after_limit = ''; // truly the section after the limit clause
1478 $seen_reserved_word = false;
1479 $seen_group = false;
1480 $seen_order = false;
1481 $seen_order_by = false;
1482 $in_group_by = false; // true when we are inside the GROUP BY clause
1483 $in_order_by = false; // true when we are inside the ORDER BY clause
1484 $in_having = false; // true when we are inside the HAVING clause
1485 $in_select_expr = false; // true when we are inside the select expr clause
1486 $in_where = false; // true when we are inside the WHERE clause
1487 $seen_limit = false; // true if we have seen a LIMIT clause
1488 $in_limit = false; // true when we are inside the LIMIT clause
1489 $after_limit = false; // true when we are after the LIMIT clause
1490 $in_from = false; // true when we are in the FROM clause
1491 $in_group_concat = false;
1492 $first_reserved_word = '';
1493 $current_identifier = '';
1494 $unsorted_query = $arr['raw']; // in case there is no ORDER BY
1495 $number_of_brackets = 0;
1496 $in_subquery = false;
1498 for ($i = 0; $i < $size; $i++
) {
1499 //DEBUG echo "Loop2 <strong>" . $arr[$i]['data'] . "</strong> (" . $arr[$i]['type'] . ")<br />";
1503 // check for reserved words that will have to generate
1504 // a confirmation request later in sql.php
1508 // ALTER TABLE... DROP
1511 // this code is not used for confirmations coming from functions.js
1513 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1514 $number_of_brackets++
;
1517 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1518 $number_of_brackets--;
1519 if ($number_of_brackets == 0) {
1520 $in_subquery = false;
1524 if ($arr[$i]['type'] == 'alpha_reservedWord') {
1525 $upper_data = strtoupper($arr[$i]['data']);
1527 if ($upper_data == 'SELECT' && $number_of_brackets > 0) {
1528 $in_subquery = true;
1531 if (!$seen_reserved_word) {
1532 $first_reserved_word = $upper_data;
1533 $subresult['querytype'] = $upper_data;
1534 $seen_reserved_word = true;
1536 // if the first reserved word is DROP or DELETE,
1537 // we know this is a query that needs to be confirmed
1538 if ($first_reserved_word=='DROP'
1539 ||
$first_reserved_word == 'DELETE'
1540 ||
$first_reserved_word == 'TRUNCATE') {
1541 $subresult['queryflags']['need_confirm'] = 1;
1544 if ($first_reserved_word=='SELECT') {
1545 $position_of_first_select = $i;
1549 if ($upper_data == 'DROP' && $first_reserved_word == 'ALTER') {
1550 $subresult['queryflags']['need_confirm'] = 1;
1554 if ($upper_data == 'LIMIT' && ! $in_subquery) {
1555 $section_before_limit = substr($arr['raw'], 0, $arr[$i]['pos'] - 5);
1559 $in_order_by = false; // @todo maybe others to set false
1562 if ($upper_data == 'PROCEDURE') {
1563 $subresult['queryflags']['procedure'] = 1;
1565 $after_limit = true;
1568 * @todo set also to false if we find FOR UPDATE or LOCK IN SHARE MODE
1570 if ($upper_data == 'SELECT') {
1571 $in_select_expr = true;
1572 $select_expr_clause = '';
1574 if ($upper_data == 'DISTINCT' && !$in_group_concat) {
1575 $subresult['queryflags']['distinct'] = 1;
1578 if ($upper_data == 'UNION') {
1579 $subresult['queryflags']['union'] = 1;
1582 if ($upper_data == 'JOIN') {
1583 $subresult['queryflags']['join'] = 1;
1586 if ($upper_data == 'OFFSET') {
1587 $subresult['queryflags']['offset'] = 1;
1590 // if this is a real SELECT...FROM
1591 if ($upper_data == 'FROM' && isset($subresult['queryflags']['select_from']) && $subresult['queryflags']['select_from'] == 1) {
1594 $in_select_expr = false;
1598 // (we could have less resetting of variables to false
1599 // if we trust that the query respects the standard
1600 // MySQL order for clauses)
1602 // we use $seen_group and $seen_order because we are looking
1604 if ($upper_data == 'GROUP') {
1606 $seen_order = false;
1608 $in_order_by = false;
1610 $in_select_expr = false;
1613 if ($upper_data == 'ORDER' && !$in_group_concat) {
1615 $seen_group = false;
1617 $in_group_by = false;
1619 $in_select_expr = false;
1622 if ($upper_data == 'HAVING') {
1624 $having_clause = '';
1625 $seen_group = false;
1626 $seen_order = false;
1627 $in_group_by = false;
1628 $in_order_by = false;
1630 $in_select_expr = false;
1634 if ($upper_data == 'WHERE') {
1637 $where_clause_identifiers = array();
1638 $seen_group = false;
1639 $seen_order = false;
1640 $in_group_by = false;
1641 $in_order_by = false;
1643 $in_select_expr = false;
1647 if ($upper_data == 'BY') {
1649 $in_group_by = true;
1650 $group_by_clause = '';
1653 $seen_order_by = true;
1654 // Here we assume that the ORDER BY keywords took
1655 // exactly 8 characters.
1656 // We use PMA_substr() to be charset-safe; otherwise
1657 // if the table name contains accents, the unsorted
1658 // query would be missing some characters.
1659 $unsorted_query = PMA_substr($arr['raw'], 0, $arr[$i]['pos'] - 8);
1660 $in_order_by = true;
1661 $order_by_clause = '';
1665 // if we find one of the words that could end the clause
1666 if (isset($words_ending_clauses[$upper_data])) {
1668 $in_group_by = false;
1669 $in_order_by = false;
1672 $in_select_expr = false;
1676 } // endif (reservedWord)
1679 // do not add a space after a function name
1681 * @todo can we combine loop 2 and loop 1? some code is repeated here...
1685 if ($arr[$i]['type'] == 'alpha_functionName') {
1687 $upper_data = strtoupper($arr[$i]['data']);
1688 if ($upper_data =='GROUP_CONCAT') {
1689 $in_group_concat = true;
1690 $number_of_brackets_in_group_concat = 0;
1694 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1695 if ($in_group_concat) {
1696 $number_of_brackets_in_group_concat++
;
1699 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1700 if ($in_group_concat) {
1701 $number_of_brackets_in_group_concat--;
1702 if ($number_of_brackets_in_group_concat == 0) {
1703 $in_group_concat = false;
1708 // do not add a space after an identifier if followed by a dot
1709 if ($arr[$i]['type'] == 'alpha_identifier' && $i < $size - 1 && $arr[$i +
1]['data'] == '.') {
1713 // do not add a space after a dot if followed by an identifier
1714 if ($arr[$i]['data'] == '.' && $i < $size - 1 && $arr[$i +
1]['type'] == 'alpha_identifier') {
1718 if ($in_select_expr && $upper_data != 'SELECT' && $upper_data != 'DISTINCT') {
1719 $select_expr_clause .= $arr[$i]['data'] . $sep;
1721 if ($in_from && $upper_data != 'FROM') {
1722 $from_clause .= $arr[$i]['data'] . $sep;
1724 if ($in_group_by && $upper_data != 'GROUP' && $upper_data != 'BY') {
1725 $group_by_clause .= $arr[$i]['data'] . $sep;
1727 if ($in_order_by && $upper_data != 'ORDER' && $upper_data != 'BY') {
1728 // add a space only before ASC or DESC
1729 // not around the dot between dbname and tablename
1730 if ($arr[$i]['type'] == 'alpha_reservedWord') {
1731 $order_by_clause .= $sep;
1733 $order_by_clause .= $arr[$i]['data'];
1735 if ($in_having && $upper_data != 'HAVING') {
1736 $having_clause .= $arr[$i]['data'] . $sep;
1738 if ($in_where && $upper_data != 'WHERE') {
1739 $where_clause .= $arr[$i]['data'] . $sep;
1741 if (($arr[$i]['type'] == 'quote_backtick')
1742 ||
($arr[$i]['type'] == 'alpha_identifier')) {
1743 $where_clause_identifiers[] = $arr[$i]['data'];
1747 // to grab the rest of the query after the ORDER BY clause
1748 if (isset($subresult['queryflags']['select_from'])
1749 && $subresult['queryflags']['select_from'] == 1
1752 && $upper_data != 'BY') {
1753 $unsorted_query .= $arr[$i]['data'];
1754 if ($arr[$i]['type'] != 'punct_bracket_open_round'
1755 && $arr[$i]['type'] != 'punct_bracket_close_round'
1756 && $arr[$i]['type'] != 'punct') {
1757 $unsorted_query .= $sep;
1762 if ($upper_data == 'OFFSET') {
1763 $limit_clause .= $sep;
1765 $limit_clause .= $arr[$i]['data'];
1766 if ($upper_data == 'LIMIT' ||
$upper_data == 'OFFSET') {
1767 $limit_clause .= $sep;
1770 if ($after_limit && $seen_limit) {
1771 $section_after_limit .= $arr[$i]['data'] . $sep;
1774 // clear $upper_data for next iteration
1776 } // end for $i (loop #2)
1777 if (empty($section_before_limit)) {
1778 $section_before_limit = $arr['raw'];
1781 // -----------------------------------------------------
1782 // loop #3: foreign keys and MySQL 4.1.2+ TIMESTAMP options
1783 // (for now, check only the first query)
1784 // (for now, identifiers are assumed to be backquoted)
1786 // If we find that we are dealing with a CREATE TABLE query,
1787 // we look for the next punct_bracket_open_round, which
1788 // introduces the fields list. Then, when we find a
1789 // quote_backtick, it must be a field, so we put it into
1790 // the create_table_fields array. Even if this field is
1791 // not a timestamp, it will be useful when logic has been
1792 // added for complete field attributes analysis.
1794 $seen_foreign = false;
1795 $seen_references = false;
1796 $seen_constraint = false;
1797 $foreign_key_number = -1;
1798 $seen_create_table = false;
1799 $seen_create = false;
1800 $seen_alter = false;
1801 $in_create_table_fields = false;
1802 $brackets_level = 0;
1803 $in_timestamp_options = false;
1804 $seen_default = false;
1806 for ($i = 0; $i < $size; $i++
) {
1807 // DEBUG echo "Loop 3 <strong>" . $arr[$i]['data'] . "</strong> " . $arr[$i]['type'] . "<br />";
1809 if ($arr[$i]['type'] == 'alpha_reservedWord') {
1810 $upper_data = strtoupper($arr[$i]['data']);
1812 if ($upper_data == 'NOT' && $in_timestamp_options) {
1813 $create_table_fields[$current_identifier]['timestamp_not_null'] = true;
1817 if ($upper_data == 'CREATE') {
1818 $seen_create = true;
1821 if ($upper_data == 'ALTER') {
1825 if ($upper_data == 'TABLE' && $seen_create) {
1826 $seen_create_table = true;
1827 $create_table_fields = array();
1830 if ($upper_data == 'CURRENT_TIMESTAMP') {
1831 if ($in_timestamp_options) {
1832 if ($seen_default) {
1833 $create_table_fields[$current_identifier]['default_current_timestamp'] = true;
1838 if ($upper_data == 'CONSTRAINT') {
1839 $foreign_key_number++
;
1840 $seen_foreign = false;
1841 $seen_references = false;
1842 $seen_constraint = true;
1844 if ($upper_data == 'FOREIGN') {
1845 $seen_foreign = true;
1846 $seen_references = false;
1847 $seen_constraint = false;
1849 if ($upper_data == 'REFERENCES') {
1850 $seen_foreign = false;
1851 $seen_references = true;
1852 $seen_constraint = false;
1858 // [ON DELETE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
1859 // [ON UPDATE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
1861 // but we set ['on_delete'] or ['on_cascade'] to
1862 // CASCADE | SET_NULL | NO_ACTION | RESTRICT
1864 // ON UPDATE CURRENT_TIMESTAMP
1866 if ($upper_data == 'ON') {
1867 if (isset($arr[$i+
1]) && $arr[$i+
1]['type'] == 'alpha_reservedWord') {
1868 $second_upper_data = strtoupper($arr[$i+
1]['data']);
1869 if ($second_upper_data == 'DELETE') {
1870 $clause = 'on_delete';
1872 if ($second_upper_data == 'UPDATE') {
1873 $clause = 'on_update';
1876 && ($arr[$i+
2]['type'] == 'alpha_reservedWord'
1878 // ugly workaround because currently, NO is not
1879 // in the list of reserved words in sqlparser.data
1880 // (we got a bug report about not being able to use
1881 // 'no' as an identifier)
1882 ||
($arr[$i+
2]['type'] == 'alpha_identifier'
1883 && strtoupper($arr[$i+
2]['data'])=='NO'))
1885 $third_upper_data = strtoupper($arr[$i+
2]['data']);
1886 if ($third_upper_data == 'CASCADE'
1887 ||
$third_upper_data == 'RESTRICT') {
1888 $value = $third_upper_data;
1889 } elseif ($third_upper_data == 'SET'
1890 ||
$third_upper_data == 'NO') {
1891 if ($arr[$i+
3]['type'] == 'alpha_reservedWord') {
1892 $value = $third_upper_data . '_' . strtoupper($arr[$i+
3]['data']);
1894 } elseif ($third_upper_data == 'CURRENT_TIMESTAMP') {
1895 if ($clause == 'on_update'
1896 && $in_timestamp_options) {
1897 $create_table_fields[$current_identifier]['on_update_current_timestamp'] = true;
1898 $seen_default = false;
1904 if (!empty($value)) {
1905 $foreign[$foreign_key_number][$clause] = $value;
1908 } // endif (isset($clause))
1912 } // end of reserved words analysis
1915 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1917 if ($seen_create_table && $brackets_level == 1) {
1918 $in_create_table_fields = true;
1923 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1925 if ($seen_references) {
1926 $seen_references = false;
1928 if ($seen_create_table && $brackets_level == 0) {
1929 $in_create_table_fields = false;
1933 if (($arr[$i]['type'] == 'alpha_columnAttrib')) {
1934 $upper_data = strtoupper($arr[$i]['data']);
1935 if ($seen_create_table && $in_create_table_fields) {
1936 if ($upper_data == 'DEFAULT') {
1937 $seen_default = true;
1938 $create_table_fields[$current_identifier]['default_value'] = $arr[$i +
1]['data'];
1944 * @see @todo 2005-10-16 note: the "or" part here is a workaround for a bug
1946 if (($arr[$i]['type'] == 'alpha_columnType') ||
($arr[$i]['type'] == 'alpha_functionName' && $seen_create_table)) {
1947 $upper_data = strtoupper($arr[$i]['data']);
1948 if ($seen_create_table && $in_create_table_fields && isset($current_identifier)) {
1949 $create_table_fields[$current_identifier]['type'] = $upper_data;
1950 if ($upper_data == 'TIMESTAMP') {
1951 $arr[$i]['type'] = 'alpha_columnType';
1952 $in_timestamp_options = true;
1954 $in_timestamp_options = false;
1955 if ($upper_data == 'CHAR') {
1956 $arr[$i]['type'] = 'alpha_columnType';
1963 if ($arr[$i]['type'] == 'quote_backtick' ||
$arr[$i]['type'] == 'alpha_identifier') {
1965 if ($arr[$i]['type'] == 'quote_backtick') {
1966 // remove backquotes
1967 $identifier = PMA_unQuote($arr[$i]['data']);
1969 $identifier = $arr[$i]['data'];
1972 if ($seen_create_table && $in_create_table_fields) {
1973 $current_identifier = $identifier;
1974 // we set this one even for non TIMESTAMP type
1975 $create_table_fields[$current_identifier]['timestamp_not_null'] = false;
1978 if ($seen_constraint) {
1979 $foreign[$foreign_key_number]['constraint'] = $identifier;
1982 if ($seen_foreign && $brackets_level > 0) {
1983 $foreign[$foreign_key_number]['index_list'][] = $identifier;
1986 if ($seen_references) {
1987 if ($seen_alter && $brackets_level > 0) {
1988 $foreign[$foreign_key_number]['ref_index_list'][] = $identifier;
1989 // here, the first bracket level corresponds to the
1990 // bracket of CREATE TABLE
1991 // so if we are on level 2, it must be the index list
1992 // of the foreign key REFERENCES
1993 } elseif ($brackets_level > 1) {
1994 $foreign[$foreign_key_number]['ref_index_list'][] = $identifier;
1995 } elseif ($arr[$i+
1]['type'] == 'punct_qualifier') {
1996 // identifier is `db`.`table`
1997 // the first pass will pick the db name
1998 // the next pass will pick the table name
1999 $foreign[$foreign_key_number]['ref_db_name'] = $identifier;
2001 // identifier is `table`
2002 $foreign[$foreign_key_number]['ref_table_name'] = $identifier;
2006 } // end for $i (loop #3)
2009 // Fill the $subresult array
2011 if (isset($create_table_fields)) {
2012 $subresult['create_table_fields'] = $create_table_fields;
2015 if (isset($foreign)) {
2016 $subresult['foreign_keys'] = $foreign;
2019 if (isset($select_expr_clause)) {
2020 $subresult['select_expr_clause'] = $select_expr_clause;
2022 if (isset($from_clause)) {
2023 $subresult['from_clause'] = $from_clause;
2025 if (isset($group_by_clause)) {
2026 $subresult['group_by_clause'] = $group_by_clause;
2028 if (isset($order_by_clause)) {
2029 $subresult['order_by_clause'] = $order_by_clause;
2031 if (isset($having_clause)) {
2032 $subresult['having_clause'] = $having_clause;
2034 if (isset($limit_clause)) {
2035 $subresult['limit_clause'] = $limit_clause;
2037 if (isset($where_clause)) {
2038 $subresult['where_clause'] = $where_clause;
2040 if (isset($unsorted_query) && !empty($unsorted_query)) {
2041 $subresult['unsorted_query'] = $unsorted_query;
2043 if (isset($where_clause_identifiers)) {
2044 $subresult['where_clause_identifiers'] = $where_clause_identifiers;
2047 if (isset($position_of_first_select)) {
2048 $subresult['position_of_first_select'] = $position_of_first_select;
2049 $subresult['section_before_limit'] = $section_before_limit;
2050 $subresult['section_after_limit'] = $section_after_limit;
2053 // They are naughty and didn't have a trailing semi-colon,
2054 // then still handle it properly
2055 if ($subresult['querytype'] != '') {
2056 $result[] = $subresult;
2059 } // end of the "PMA_SQP_analyze()" function
2063 * Colorizes SQL queries html formatted
2065 * @todo check why adding a "\n" after the </span> would cause extra blanks
2066 * to be displayed: SELECT p . person_name
2067 * @param array The SQL queries html formatted
2069 * @return array The colorized SQL queries
2073 function PMA_SQP_formatHtml_colorize($arr)
2075 $i = PMA_strpos($arr['type'], '_');
2078 $class = 'syntax_' . PMA_substr($arr['type'], 0, $i) . ' ';
2081 $class .= 'syntax_' . $arr['type'];
2083 return '<span class="' . $class . '">' . htmlspecialchars($arr['data']) . '</span>';
2084 } // end of the "PMA_SQP_formatHtml_colorize()" function
2088 * Formats SQL queries to html
2090 * @param array The SQL queries
2091 * @param string mode
2092 * @param integer starting token
2093 * @param integer number of tokens to format, -1 = all
2095 * @return string The formatted SQL queries
2099 function PMA_SQP_formatHtml($arr, $mode='color', $start_token=0,
2100 $number_of_tokens=-1)
2102 global $PMA_SQPdata_operators_docs, $PMA_SQPdata_functions_docs;
2103 //DEBUG echo 'in Format<pre>'; print_r($arr); echo '</pre>';
2104 // then check for an array
2105 if (! is_array($arr)) {
2106 return htmlspecialchars($arr);
2108 // first check for the SQL parser having hit an error
2109 if (PMA_SQP_isError()) {
2110 return htmlspecialchars($arr['raw']);
2112 // else do it properly
2115 $str = '<span class="syntax">';
2116 $html_line_break = '<br />';
2121 $html_line_break = "\n";
2126 $html_line_break = '<br />';
2130 // inner_sql is a span that exists for all cases, except query_only
2131 // of $cfg['SQP']['fmtType'] to make possible a replacement
2132 // for inline editing
2133 if ($mode!='query_only') {
2134 $str .= '<span class="inner_sql">';
2136 $close_docu_link = false;
2140 $infunction = false;
2141 $space_punct_listsep = ' ';
2142 $space_punct_listsep_function_name = ' ';
2143 // $space_alpha_reserved_word = '<br />'."\n";
2144 $space_alpha_reserved_word = ' ';
2146 $keywords_with_brackets_1before = array(
2153 $keywords_with_brackets_2before = array(
2165 // These reserved words do NOT get a newline placed near them.
2166 $keywords_no_newline = array(
2182 // These reserved words introduce a privilege list
2183 $keywords_priv_list = array(
2188 if ($number_of_tokens == -1) {
2189 $number_of_tokens = $arr['len'];
2192 if ($number_of_tokens >= 0) {
2196 $typearr[3] = $arr[$start_token]['type'];
2199 $in_priv_list = false;
2200 for ($i = $start_token; $i < $number_of_tokens; $i++
) {
2201 // DEBUG echo "Loop format <strong>" . $arr[$i]['data'] . "</strong> " . $arr[$i]['type'] . "<br />";
2204 // array_shift($typearr);
2211 if (($i +
1) < $number_of_tokens) {
2212 $typearr[4] = $arr[$i +
1]['type'];
2217 for ($j=0; $j<4; $j++
) {
2218 $typearr[$j] = $typearr[$j +
1];
2221 switch ($typearr[2]) {
2222 case 'alpha_bitfield_constant_introducer':
2226 case 'white_newline':
2229 case 'punct_bracket_open_round':
2231 $infunction = false;
2232 // Make sure this array is sorted!
2233 if (($typearr[1] == 'alpha_functionName') ||
($typearr[1] == 'alpha_columnType') ||
($typearr[1] == 'punct')
2234 ||
($typearr[3] == 'digit_integer') ||
($typearr[3] == 'digit_hex') ||
($typearr[3] == 'digit_float')
2235 ||
(($typearr[0] == 'alpha_reservedWord')
2236 && isset($keywords_with_brackets_2before[strtoupper($arr[$i - 2]['data'])]))
2237 ||
(($typearr[1] == 'alpha_reservedWord')
2238 && isset($keywords_with_brackets_1before[strtoupper($arr[$i - 1]['data'])]))
2245 $after .= ($mode != 'query_only' ?
'<div class="syntax_indent' . $indent . '">' : ' ');
2248 case 'alpha_identifier':
2249 if (($typearr[1] == 'punct_qualifier') ||
($typearr[3] == 'punct_qualifier')) {
2253 // for example SELECT 1 somealias
2254 if ($typearr[1] == 'digit_integer') {
2257 if (($typearr[3] == 'alpha_columnType') ||
($typearr[3] == 'alpha_identifier')) {
2262 case 'punct_qualifier':
2266 case 'punct_listsep':
2267 if ($infunction == true) {
2268 $after .= $space_punct_listsep_function_name;
2270 $after .= $space_punct_listsep;
2273 case 'punct_queryend':
2274 if (($typearr[3] != 'comment_mysql') && ($typearr[3] != 'comment_ansi') && $typearr[3] != 'comment_c') {
2275 $after .= $html_line_break;
2276 $after .= $html_line_break;
2278 $space_punct_listsep = ' ';
2279 $space_punct_listsep_function_name = ' ';
2280 $space_alpha_reserved_word = ' ';
2281 $in_priv_list = false;
2283 case 'comment_mysql':
2284 case 'comment_ansi':
2285 $after .= $html_line_break;
2289 if ($docu && isset($PMA_SQPdata_operators_docs[$arr[$i]['data']]) &&
2290 ($arr[$i]['data'] != '*' ||
in_array($arr[$i]['type'], array('digit_integer','digit_float','digit_hex')))) {
2291 $before .= PMA_showMySQLDocu(
2293 $PMA_SQPdata_operators_docs[$arr[$i]['data']]['link'],
2295 $PMA_SQPdata_operators_docs[$arr[$i]['data']]['anchor'],
2301 // select * from mytable limit 0,-1
2302 // (a side effect of this workaround is that
2307 if ($typearr[3] != 'digit_integer') {
2311 case 'punct_bracket_close_round':
2312 // only close bracket level when it was opened before
2313 if ($bracketlevel > 0) {
2315 if ($infunction == true) {
2321 $before .= ($mode != 'query_only' ?
'</div>' : ' ');
2323 $infunction = ($functionlevel > 0) ?
true : false;
2326 case 'alpha_columnType':
2328 switch ($arr[$i]['data']) {
2341 $before .= PMA_showMySQLDocu('data-types', 'numeric-types', false, '', true);
2342 $after = '</a>' . $after;
2349 $before .= PMA_showMySQLDocu('data-types', 'date-and-time-types', false, '', true);
2350 $after = '</a>' . $after;
2366 $before .= PMA_showMySQLDocu('data-types', 'string-types', false, '', true);
2367 $after = '</a>' . $after;
2371 if ($typearr[3] == 'alpha_columnAttrib') {
2374 if ($typearr[1] == 'alpha_columnType') {
2378 case 'alpha_columnAttrib':
2380 // ALTER TABLE tbl_name AUTO_INCREMENT = 1
2381 // COLLATE LATIN1_GENERAL_CI DEFAULT
2382 if ($typearr[1] == 'alpha_identifier' ||
$typearr[1] == 'alpha_charset') {
2385 if (($typearr[3] == 'alpha_columnAttrib') ||
($typearr[3] == 'quote_single') ||
($typearr[3] == 'digit_integer')) {
2389 // AUTO_INCREMENT = 31DEFAULT_CHARSET = utf-8
2391 if ($typearr[2] == 'alpha_columnAttrib' && $typearr[3] == 'alpha_reservedWord') {
2395 // select * from mysql.user where binary user="root"
2396 // binary is marked as alpha_columnAttrib
2397 // but should be marked as a reserved word
2398 if (strtoupper($arr[$i]['data']) == 'BINARY'
2399 && $typearr[3] == 'alpha_identifier') {
2403 case 'alpha_functionName':
2404 $funcname = strtoupper($arr[$i]['data']);
2405 if ($docu && isset($PMA_SQPdata_functions_docs[$funcname])) {
2406 $before .= PMA_showMySQLDocu(
2408 $PMA_SQPdata_functions_docs[$funcname]['link'],
2410 $PMA_SQPdata_functions_docs[$funcname]['anchor'],
2415 case 'alpha_reservedWord':
2416 // do not uppercase the reserved word if we are calling
2417 // this function in query_only mode, because we need
2418 // the original query (otherwise we get problems with
2419 // semi-reserved words like "storage" which is legal
2420 // as an identifier name)
2422 if ($mode != 'query_only') {
2423 $arr[$i]['data'] = strtoupper($arr[$i]['data']);
2426 if ((($typearr[1] != 'alpha_reservedWord')
2427 ||
(($typearr[1] == 'alpha_reservedWord')
2428 && isset($keywords_no_newline[strtoupper($arr[$i - 1]['data'])])))
2429 && ($typearr[1] != 'punct_level_plus')
2430 && (!isset($keywords_no_newline[$arr[$i]['data']]))) {
2431 // do not put a space before the first token, because
2432 // we use a lot of pattern matching checking for the
2433 // first reserved word at beginning of query
2434 // so do not put a newline before
2436 // also we must not be inside a privilege list
2438 // the alpha_identifier exception is there to
2440 // GRANT SELECT ON mydb.mytable TO myuser@localhost
2441 // (else, we get mydb.mytableTO)
2443 // the quote_single exception is there to
2445 // GRANT ... TO 'marc'@'domain.com' IDENTIFIED...
2447 * @todo fix all cases and find why this happens
2450 if (!$in_priv_list ||
$typearr[1] == 'alpha_identifier' ||
$typearr[1] == 'quote_single' ||
$typearr[1] == 'white_newline') {
2451 $before .= $space_alpha_reserved_word;
2454 // on first keyword, check if it introduces a
2456 if (isset($keywords_priv_list[$arr[$i]['data']])) {
2457 $in_priv_list = true;
2464 switch ($arr[$i]['data']) {
2474 switch ($arr[$i +
1]['data']) {
2485 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'] . '_' . $arr[$i +
1]['data'], false, '', true);
2486 $close_docu_link = true;
2489 if ($arr[$i +
1]['data'] == 'LOGFILE' && $arr[$i +
2]['data'] == 'GROUP') {
2490 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'] . '_LOGFILE_GROUP', false, '', true);
2491 $close_docu_link = true;
2494 if (!$in_priv_list) {
2495 $space_punct_listsep = $html_line_break;
2496 $space_alpha_reserved_word = ' ';
2510 if ($close_docu_link) {
2511 $after = '</a>' . $after;
2512 $close_docu_link = false;
2516 if ($docu && ($i == 0 ||
$arr[$i - 1]['data'] != 'CHARACTER')) {
2517 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
2518 $after = '</a>' . $after;
2520 if (!$in_priv_list) {
2521 $space_punct_listsep = $html_line_break;
2522 $space_alpha_reserved_word = ' ';
2531 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
2532 $after = '</a>' . $after;
2534 if (!$in_priv_list) {
2535 $space_punct_listsep = $html_line_break;
2536 $space_alpha_reserved_word = ' ';
2542 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
2543 $after = '</a>' . $after;
2545 if (!$in_priv_list) {
2546 $space_punct_listsep = $html_line_break;
2547 $space_alpha_reserved_word = $html_line_break;
2551 $space_punct_listsep = ' ';
2552 $space_alpha_reserved_word = $html_line_break;
2556 $before .= PMA_showMySQLDocu('SQL-Syntax', 'SELECT', false, '', true);
2557 $after = '</a>' . $after;
2559 $space_punct_listsep = ' ';
2560 $space_alpha_reserved_word = $html_line_break;
2566 $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
2567 $after = '</a>' . $after;
2571 if ($close_docu_link && in_array($arr[$i]['data'], array('LIKE', 'NOT', 'IN', 'REGEXP', 'NULL'))) {
2573 $close_docu_link = false;
2574 } else if ($docu && isset($PMA_SQPdata_functions_docs[$arr[$i]['data']])) {
2575 /* Handle multi word statements first */
2576 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'])])) {
2577 $tempname = strtoupper($arr[$i]['data'] . '_' . $arr[$i +
1]['data'] . '_' . $arr[$i +
2]['data']);
2578 $before .= PMA_showMySQLDocu('functions', $PMA_SQPdata_functions_docs[$tempname]['link'], false, $PMA_SQPdata_functions_docs[$tempname]['anchor'], true);
2579 $close_docu_link = true;
2580 } else if (isset($typearr[3]) && $typearr[3] == 'alpha_reservedWord' && isset($PMA_SQPdata_functions_docs[strtoupper($arr[$i]['data'] . '_' . $arr[$i +
1]['data'])])) {
2581 $tempname = strtoupper($arr[$i]['data'] . '_' . $arr[$i +
1]['data']);
2582 $before .= PMA_showMySQLDocu('functions', $PMA_SQPdata_functions_docs[$tempname]['link'], false, $PMA_SQPdata_functions_docs[$tempname]['anchor'], true);
2583 $close_docu_link = true;
2585 $before .= PMA_showMySQLDocu('functions', $PMA_SQPdata_functions_docs[$arr[$i]['data']]['link'], false, $PMA_SQPdata_functions_docs[$arr[$i]['data']]['anchor'], true);
2590 } // end switch ($arr[$i]['data'])
2594 case 'digit_integer':
2598 * @todo could there be other types preceding a digit?
2600 if ($typearr[1] == 'alpha_reservedWord') {
2603 if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
2606 if ($typearr[1] == 'alpha_columnAttrib') {
2610 case 'alpha_variable':
2613 case 'quote_double':
2614 case 'quote_single':
2615 // workaround: for the query
2616 // REVOKE SELECT ON `base2\_db`.* FROM 'user'@'%'
2617 // the @ is incorrectly marked as alpha_variable
2618 // in the parser, and here, the '%' gets a blank before,
2619 // which is a syntax error
2620 if ($typearr[1] != 'punct_user' && $typearr[1] != 'alpha_bitfield_constant_introducer') {
2623 if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
2627 case 'quote_backtick':
2628 // here we check for punct_user to handle correctly
2629 // DEFINER = `username`@`%`
2630 // where @ is the punct_user and `%` is the quote_backtick
2631 if ($typearr[3] != 'punct_qualifier' && $typearr[3] != 'alpha_variable' && $typearr[3] != 'punct_user') {
2634 if ($typearr[1] != 'punct_qualifier' && $typearr[1] != 'alpha_variable' && $typearr[1] != 'punct_user') {
2640 } // end switch ($typearr[2])
2643 if ($typearr[3] != 'punct_qualifier') {
2649 if ($mode=='color') {
2650 $str .= PMA_SQP_formatHTML_colorize($arr[$i]);
2651 } elseif ($mode == 'text') {
2652 $str .= htmlspecialchars($arr[$i]['data']);
2654 $str .= $arr[$i]['data'];
2658 // close unclosed indent levels
2659 while ($indent > 0) {
2661 $str .= ($mode != 'query_only' ?
'</div>' : ' ');
2663 /* End possibly unclosed documentation link */
2664 if ($close_docu_link) {
2666 $close_docu_link = false;
2668 if ($mode!='query_only') {
2669 // close inner_sql span
2672 if ($mode=='color') {
2673 // close syntax span
2678 } // end of the "PMA_SQP_formatHtml()" function
2682 * Builds a CSS rule used for html formatted SQL queries
2684 * @param string The class name
2685 * @param string The property name
2686 * @param string The property value
2688 * @return string The CSS rule
2692 * @see PMA_SQP_buildCssData()
2694 function PMA_SQP_buildCssRule($classname, $property, $value)
2696 $str = '.' . $classname . ' {';
2698 $str .= $property . ': ' . $value . ';';
2703 } // end of the "PMA_SQP_buildCssRule()" function
2707 * Builds CSS rules used for html formatted SQL queries
2709 * @return string The CSS rules set
2713 * @global array The current PMA configuration
2715 * @see PMA_SQP_buildCssRule()
2717 function PMA_SQP_buildCssData()
2722 foreach ($cfg['SQP']['fmtColor'] AS $key => $col) {
2723 $css_string .= PMA_SQP_buildCssRule('syntax_' . $key, 'color', $col);
2726 for ($i = 0; $i < 8; $i++
) {
2727 $css_string .= PMA_SQP_buildCssRule(
2728 'syntax_indent' . $i, 'margin-left',
2729 ($i * $cfg['SQP']['fmtInd']) . $cfg['SQP']['fmtIndUnit']);
2733 } // end of the "PMA_SQP_buildCssData()" function
2735 if (! defined('PMA_MINIMUM_COMMON')) {
2737 * Gets SQL queries with no format
2739 * @param array The SQL queries list
2741 * @return string The SQL queries with no format
2745 function PMA_SQP_formatNone($arr)
2747 $formatted_sql = htmlspecialchars($arr['raw']);
2748 $formatted_sql = preg_replace(
2749 "@((\015\012)|(\015)|(\012)){3,}@",
2753 return $formatted_sql;
2754 } // end of the "PMA_SQP_formatNone()" function
2756 } // end if: minimal common.lib needed?