sorry, wrong version checked in
[phpmyadmin/arisferyanto.git] / libraries / sqlparser.lib.php
blob257ede81d4f343308050914dd15b9df98d911009
1 <?php
2 /* $Id$ */
3 // vim: expandtab sw=4 ts=4 sts=4:
5 /** SQL Parser Functions for phpMyAdmin
7 * Copyright 2002 Robin Johnson <robbat2@users.sourceforge.net>
8 * http://www.orbis-terrarum.net/?l=people.robbat2
10 * These functions define an SQL parser system, capable of understanding and
11 * extracting data from a MySQL type SQL query.
13 * The basic procedure for using the new SQL parser:
14 * On any page that needs to extract data from a query or to pretty-print a
15 * query, you need code like this up at the top:
17 * ($sql contains the query)
18 * $parsed_sql = PMA_SQP_parse($sql);
20 * If you want to extract data from it then, you just need to run
21 * $sql_info = PMA_SQP_analyze($parsed_sql);
23 * lem9: See comments in PMA_SQP_analyze for the returned info
24 * from the analyzer.
26 * If you want a pretty-printed version of the query, do:
27 * $string = PMA_SQP_formatHtml($parsed_sql);
28 * (note that that you need to have syntax.css.php included somehow in your
29 * page for it to work, I recommend '<link rel="stylesheet" type="text/css"
30 * href="syntax.css.php" />' at the moment.)
34 /**
35 * Minimum inclusion? (i.e. for the stylesheet builder)
37 if ( ! defined( 'PMA_MINIMUM_COMMON' ) ) {
38 /**
39 * Include the string library as we use it heavily
41 require_once('./libraries/string.lib.php');
43 /**
44 * Include data for the SQL Parser
46 require_once('./libraries/sqlparser.data.php');
47 require_once('./libraries/mysql_charsets.lib.php');
48 if (!isset($mysql_charsets)) {
49 $mysql_charsets = array();
50 $mysql_charsets_count = 0;
51 $mysql_collations_flat = array();
52 $mysql_collations_count = 0;
55 if (!defined('DEBUG_TIMING')) {
56 function PMA_SQP_arrayAdd(&$arr, $type, $data, &$arrsize)
58 $arr[] = array('type' => $type, 'data' => $data);
59 $arrsize++;
60 } // end of the "PMA_SQP_arrayAdd()" function
61 } else {
62 function PMA_SQP_arrayAdd(&$arr, $type, $data, &$arrsize)
64 global $timer;
66 $t = $timer;
67 $arr[] = array('type' => $type, 'data' => $data , 'time' => $t);
68 $timer = microtime();
69 $arrsize++;
70 } // end of the "PMA_SQP_arrayAdd()" function
71 } // end if... else...
74 /**
75 * Reset the error variable for the SQL parser
77 * @access public
79 // Added, Robbat2 - 13 Janurary 2003, 2:59PM
80 function PMA_SQP_resetError() {
81 global $SQP_errorString;
82 $SQP_errorString = '';
83 unset($SQP_errorString);
86 /**
87 * Get the contents of the error variable for the SQL parser
89 * @return string Error string from SQL parser
91 * @access public
93 // Added, Robbat2 - 13 Janurary 2003, 2:59PM
94 function PMA_SQP_getErrorString() {
95 global $SQP_errorString;
96 return isset($SQP_errorString) ? $SQP_errorString : '';
99 /**
100 * Check if the SQL parser hit an error
102 * @return boolean error state
104 * @access public
106 // Added, Robbat2 - 13 Janurary 2003, 2:59PM
107 function PMA_SQP_isError() {
108 global $SQP_errorString;
109 return isset($SQP_errorString) && !empty($SQP_errorString);
113 * Set an error message for the system
115 * @param string The error message
116 * @param string The failing SQL query
118 * @access private
119 * @scope SQL Parser internal
121 // Revised, Robbat2 - 13 Janurary 2003, 2:59PM
122 function PMA_SQP_throwError($message, $sql)
125 global $SQP_errorString;
126 $SQP_errorString = '<p>'.$GLOBALS['strSQLParserUserError'] . '</p>' . "\n"
127 . '<pre>' . "\n"
128 . 'ERROR: ' . $message . "\n"
129 . 'SQL: ' . htmlspecialchars($sql) . "\n"
130 . '</pre>' . "\n";
132 } // end of the "PMA_SQP_throwError()" function
136 * Do display the bug report
138 * @param string The error message
139 * @param string The failing SQL query
141 * @access public
143 function PMA_SQP_bug($message, $sql)
145 global $SQP_errorString;
146 $debugstr = 'ERROR: ' . $message . "\n";
147 $debugstr .= 'CVS: $Id$' . "\n";
148 $debugstr .= 'MySQL: '.PMA_MYSQL_STR_VERSION . "\n";
149 $debugstr .= 'USR OS, AGENT, VER: ' . PMA_USR_OS . ' ' . PMA_USR_BROWSER_AGENT . ' ' . PMA_USR_BROWSER_VER . "\n";
150 $debugstr .= 'PMA: ' . PMA_VERSION . "\n";
151 $debugstr .= 'PHP VER,OS: ' . PMA_PHP_STR_VERSION . ' ' . PHP_OS . "\n";
152 $debugstr .= 'LANG: ' . $GLOBALS['lang'] . "\n";
153 $debugstr .= 'SQL: ' . htmlspecialchars($sql);
155 $encodedstr = $debugstr;
156 if (@function_exists('gzcompress')) {
157 $encodedstr = gzcompress($debugstr, 9);
159 $encodedstr = preg_replace("/(\015\012)|(\015)|(\012)/", '<br />' . "\n", chunk_split(base64_encode($encodedstr)));
161 $SQP_errorString .= $GLOBALS['strSQLParserBugMessage'] . '<br />' . "\n"
162 . '----' . $GLOBALS['strBeginCut'] . '----' . '<br />' . "\n"
163 . $encodedstr . "\n"
164 . '----' . $GLOBALS['strEndCut'] . '----' . '<br />' . "\n";
166 $SQP_errorString .= '----' . $GLOBALS['strBeginRaw'] . '----<br />' . "\n"
167 . '<pre>' . "\n"
168 . $debugstr
169 . '</pre>' . "\n"
170 . '----' . $GLOBALS['strEndRaw'] . '----<br />' . "\n";
172 } // end of the "PMA_SQP_bug()" function
176 * Parses the SQL queries
178 * @param string The SQL query list
180 * @return mixed Most of times, nothing...
182 * @global array The current PMA configuration
183 * @global array MySQL column attributes
184 * @global array MySQL reserved words
185 * @global array MySQL column types
186 * @global array MySQL function names
187 * @global integer MySQL column attributes count
188 * @global integer MySQL reserved words count
189 * @global integer MySQL column types count
190 * @global integer MySQL function names count
191 * @global array List of available character sets
192 * @global array List of available collations
193 * @global integer Character sets count
194 * @global integer Collations count
196 * @access public
198 function PMA_SQP_parse($sql)
200 global $cfg;
201 global $PMA_SQPdata_column_attrib, $PMA_SQPdata_reserved_word, $PMA_SQPdata_column_type, $PMA_SQPdata_function_name,
202 $PMA_SQPdata_column_attrib_cnt, $PMA_SQPdata_reserved_word_cnt, $PMA_SQPdata_column_type_cnt, $PMA_SQPdata_function_name_cnt;
203 global $mysql_charsets, $mysql_collations_flat, $mysql_charsets_count, $mysql_collations_count;
204 global $PMA_SQPdata_forbidden_word, $PMA_SQPdata_forbidden_word_cnt;
206 // rabus: Convert all line feeds to Unix style
207 $sql = str_replace("\r\n", "\n", $sql);
208 $sql = str_replace("\r", "\n", $sql);
210 $len = PMA_strlen($sql);
211 if ($len == 0) {
212 return array();
215 $sql_array = array();
216 $sql_array['raw'] = $sql;
217 $count1 = 0;
218 $count2 = 0;
219 $punct_queryend = ';';
220 $punct_qualifier = '.';
221 $punct_listsep = ',';
222 $punct_level_plus = '(';
223 $punct_level_minus = ')';
224 $digit_floatdecimal = '.';
225 $digit_hexset = 'x';
226 $bracket_list = '()[]{}';
227 $allpunct_list = '-,;:!?/.^~\*&%+<=>|';
228 $allpunct_list_pair = array (
229 0 => '!=',
230 1 => '&&',
231 2 => ':=',
232 3 => '<<',
233 4 => '<=',
234 5 => '<=>',
235 6 => '<>',
236 7 => '>=',
237 8 => '>>',
238 9 => '||'
240 $allpunct_list_pair_size = 10; //count($allpunct_list_pair);
241 $quote_list = '\'"`';
242 $arraysize = 0;
244 while ($count2 < $len) {
245 $c = PMA_substr($sql, $count2, 1);
246 $count1 = $count2;
248 if (($c == "\n")) {
249 $count2++;
250 PMA_SQP_arrayAdd($sql_array, 'white_newline', '', $arraysize);
251 continue;
254 // Checks for white space
255 if (PMA_STR_isSpace($c)) {
256 $count2++;
257 continue;
260 // Checks for comment lines.
261 // MySQL style #
262 // C style /* */
263 // ANSI style --
264 if (($c == '#')
265 || (($count2 + 1 < $len) && ($c == '/') && (PMA_substr($sql, $count2 + 1, 1) == '*'))
266 || (($count2 + 2 == $len) && ($c == '-') && (PMA_substr($sql, $count2 + 1, 1) == '-'))
267 || (($count2 + 2 < $len) && ($c == '-') && (PMA_substr($sql, $count2 + 1, 1) == '-') && ((PMA_substr($sql, $count2 + 2, 1) <= ' ')))) {
268 $count2++;
269 $pos = 0;
270 $type = 'bad';
271 switch ($c) {
272 case '#':
273 $type = 'mysql';
274 case '-':
275 $type = 'ansi';
276 $pos = $GLOBALS['PMA_strpos']($sql, "\n", $count2);
277 break;
278 case '/':
279 $type = 'c';
280 $pos = $GLOBALS['PMA_strpos']($sql, '*/', $count2);
281 $pos += 2;
282 break;
283 default:
284 break;
285 } // end switch
286 $count2 = ($pos < $count2) ? $len : $pos;
287 $str = PMA_substr($sql, $count1, $count2 - $count1);
288 PMA_SQP_arrayAdd($sql_array, 'comment_' . $type, $str, $arraysize);
289 continue;
290 } // end if
292 // Checks for something inside quotation marks
293 if (PMA_STR_strInStr($c, $quote_list)) {
294 $startquotepos = $count2;
295 $quotetype = $c;
296 $count2++;
297 $escaped = FALSE;
298 $escaped_escaped = FALSE;
299 $pos = $count2;
300 $oldpos = 0;
301 do {
302 $oldpos = $pos;
303 $pos = $GLOBALS['PMA_strpos'](' ' . $sql, $quotetype, $oldpos + 1) - 1;
304 // ($pos === FALSE)
305 if ($pos < 0) {
306 $debugstr = $GLOBALS['strSQPBugUnclosedQuote'] . ' @ ' . $startquotepos. "\n"
307 . 'STR: ' . htmlspecialchars($quotetype);
308 PMA_SQP_throwError($debugstr, $sql);
309 return $sql;
312 // If the quote is the first character, it can't be
313 // escaped, so don't do the rest of the code
314 if ($pos == 0) {
315 break;
318 // Checks for MySQL escaping using a \
319 // And checks for ANSI escaping using the $quotetype character
320 if (($pos < $len) && PMA_STR_charIsEscaped($sql, $pos)) {
321 $pos ++;
322 continue;
323 } else if (($pos + 1 < $len) && (PMA_substr($sql, $pos, 1) == $quotetype) && (PMA_substr($sql, $pos + 1, 1) == $quotetype)) {
324 $pos = $pos + 2;
325 continue;
326 } else {
327 break;
329 } while ($len > $pos); // end do
331 $count2 = $pos;
332 $count2++;
333 $type = 'quote_';
334 switch ($quotetype) {
335 case '\'':
336 $type .= 'single';
337 break;
338 case '"':
339 $type .= 'double';
340 break;
341 case '`':
342 $type .= 'backtick';
343 break;
344 default:
345 break;
346 } // end switch
347 $data = PMA_substr($sql, $count1, $count2 - $count1);
348 PMA_SQP_arrayAdd($sql_array, $type, $data, $arraysize);
349 continue;
352 // Checks for brackets
353 if (PMA_STR_strInStr($c, $bracket_list)) {
354 // All bracket tokens are only one item long
355 $count2++;
356 $type_type = '';
357 if (PMA_STR_strInStr($c, '([{')) {
358 $type_type = 'open';
359 } else {
360 $type_type = 'close';
363 $type_style = '';
364 if (PMA_STR_strInStr($c, '()')) {
365 $type_style = 'round';
366 } elseif (PMA_STR_strInStr($c, '[]')) {
367 $type_style = 'square';
368 } else {
369 $type_style = 'curly';
372 $type = 'punct_bracket_' . $type_type . '_' . $type_style;
373 PMA_SQP_arrayAdd($sql_array, $type, $c, $arraysize);
374 continue;
377 // Checks for identifier (alpha or numeric)
378 if (PMA_STR_isSqlIdentifier($c, FALSE) || ($c == '@') || ($c == '.' && PMA_STR_isDigit(PMA_substr($sql, $count2 + 1, 1)))) {
379 $count2 ++;
381 //TODO: a @ can also be present in expressions like
382 // FROM 'user'@'%'
383 // or TO 'user'@'%'
384 // in this case, the @ is wrongly marked as alpha_variable
386 $is_sql_variable = ($c == '@');
387 $is_digit = (!$is_sql_variable) && PMA_STR_isDigit($c);
388 $is_hex_digit = ($is_digit) && ($c == '.') && ($c == '0') && ($count2 < $len) && (PMA_substr($sql, $count2, 1) == 'x');
389 $is_float_digit = $c == '.';
390 $is_float_digit_exponent = FALSE;
392 // Nijel: Fast skip is especially needed for huge BLOB data:
393 if ($is_hex_digit) {
394 $count2++;
395 $pos = strspn($sql, '0123456789abcdefABCDEF', $count2);
396 if ($pos > $count2) $count2 = $pos;
397 unset($pos);
398 } elseif ($is_digit) {
399 $pos = strspn($sql, '0123456789', $count2);
400 if ($pos > $count2) $count2 = $pos;
401 unset($pos);
404 while (($count2 < $len) && PMA_STR_isSqlIdentifier(PMA_substr($sql, $count2, 1), ($is_sql_variable || $is_digit))) {
405 $c2 = PMA_substr($sql, $count2, 1);
406 if ($is_sql_variable && ($c2 == '.')) {
407 $count2++;
408 continue;
410 if ($is_digit && (!$is_hex_digit) && ($c2 == '.')) {
411 $count2++;
412 if (!$is_float_digit) {
413 $is_float_digit = TRUE;
414 continue;
415 } else {
416 $debugstr = $GLOBALS['strSQPBugInvalidIdentifer'] . ' @ ' . ($count1+1) . "\n"
417 . 'STR: ' . htmlspecialchars(PMA_substr($sql, $count1, $count2 - $count1));
418 PMA_SQP_throwError($debugstr, $sql);
419 return $sql;
422 if ($is_digit && (!$is_hex_digit) && (($c2 == 'e') || ($c2 == 'E'))) {
423 if (!$is_float_digit_exponent) {
424 $is_float_digit_exponent = TRUE;
425 $is_float_digit = TRUE;
426 $count2++;
427 continue;
428 } else {
429 $is_digit = FALSE;
430 $is_float_digit = FALSE;
433 if (($is_hex_digit && PMA_STR_isHexDigit($c2)) || ($is_digit && PMA_STR_isDigit($c2))) {
434 $count2++;
435 continue;
436 } else {
437 $is_digit = FALSE;
438 $is_hex_digit = FALSE;
441 $count2++;
442 } // end while
444 $l = $count2 - $count1;
445 $str = PMA_substr($sql, $count1, $l);
447 $type = '';
448 if ($is_digit) {
449 $type = 'digit';
450 if ($is_float_digit) {
451 $type .= '_float';
452 } else if ($is_hex_digit) {
453 $type .= '_hex';
454 } else {
455 $type .= '_integer';
458 else {
459 if ($is_sql_variable != FALSE) {
460 $type = 'alpha_variable';
461 } else {
462 $type = 'alpha';
464 } // end if... else....
465 PMA_SQP_arrayAdd($sql_array, $type, $str, $arraysize);
467 continue;
470 // Checks for punct
471 if (PMA_STR_strInStr($c, $allpunct_list)) {
472 while (($count2 < $len) && PMA_STR_strInStr(PMA_substr($sql, $count2, 1), $allpunct_list)) {
473 $count2++;
475 $l = $count2 - $count1;
476 if ($l == 1) {
477 $punct_data = $c;
478 } else {
479 $punct_data = PMA_substr($sql, $count1, $l);
482 // Special case, sometimes, althought two characters are
483 // adjectent directly, they ACTUALLY need to be seperate
484 if ($l == 1) {
485 $t_suffix = '';
486 switch ($punct_data) {
487 case $punct_queryend:
488 $t_suffix = '_queryend';
489 break;
490 case $punct_qualifier:
491 $t_suffix = '_qualifier';
492 break;
493 case $punct_listsep:
494 $t_suffix = '_listsep';
495 break;
496 default:
497 break;
499 PMA_SQP_arrayAdd($sql_array, 'punct' . $t_suffix, $punct_data, $arraysize);
501 else if (PMA_STR_binarySearchInArr($punct_data, $allpunct_list_pair, $allpunct_list_pair_size)) {
502 // Ok, we have one of the valid combined punct expressions
503 PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize);
505 else {
506 // Bad luck, lets split it up more
507 $first = $punct_data[0];
508 $first2 = $punct_data[0] . $punct_data[1];
509 $last2 = $punct_data[$l - 2] . $punct_data[$l - 1];
510 $last = $punct_data[$l - 1];
511 if (($first == ',') || ($first == ';') || ($first == '.') || ($first == '*')) {
512 $count2 = $count1 + 1;
513 $punct_data = $first;
514 } else if (($last2 == '/*') || (($last2 == '--') && ($count2 == $len || PMA_substr($sql, $count2, 1) <= ' ') )) {
515 $count2 -= 2;
516 $punct_data = PMA_substr($sql, $count1, $count2 - $count1);
517 } else if (($last == '-') || ($last == '+') || ($last == '!')) {
518 $count2--;
519 $punct_data = PMA_substr($sql, $count1, $count2 - $count1);
520 // TODO: for negation operator, split in 2 tokens ?
521 // "select x&~1 from t"
522 // becomes "select x & ~ 1 from t" ?
524 } else if ($last != '~') {
525 $debugstr = $GLOBALS['strSQPBugUnknownPunctuation'] . ' @ ' . ($count1+1) . "\n"
526 . 'STR: ' . htmlspecialchars($punct_data);
527 PMA_SQP_throwError($debugstr, $sql);
528 return $sql;
530 PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize);
531 continue;
532 } // end if... else if... else
533 continue;
536 // DEBUG
537 $count2++;
539 $debugstr = 'C1 C2 LEN: ' . $count1 . ' ' . $count2 . ' ' . $len . "\n"
540 . 'STR: ' . PMA_substr($sql, $count1, $count2 - $count1) . "\n";
541 PMA_SQP_bug($debugstr, $sql);
542 return $sql;
544 } // end while ($count2 < $len)
547 if ($arraysize > 0) {
548 $t_next = $sql_array[0]['type'];
549 $t_prev = '';
550 $t_bef_prev = '';
551 $t_cur = '';
552 $d_next = $sql_array[0]['data'];
553 $d_prev = '';
554 $d_bef_prev = '';
555 $d_cur = '';
556 $d_next_upper = $t_next == 'alpha' ? strtoupper($d_next) : $d_next;
557 $d_prev_upper = '';
558 $d_bef_prev_upper = '';
559 $d_cur_upper = '';
562 for ($i = 0; $i < $arraysize; $i++) {
563 $t_bef_prev = $t_prev;
564 $t_prev = $t_cur;
565 $t_cur = $t_next;
566 $d_bef_prev = $d_prev;
567 $d_prev = $d_cur;
568 $d_cur = $d_next;
569 $d_bef_prev_upper = $d_prev_upper;
570 $d_prev_upper = $d_cur_upper;
571 $d_cur_upper = $d_next_upper;
572 if (($i + 1) < $arraysize) {
573 $t_next = $sql_array[$i + 1]['type'];
574 $d_next = $sql_array[$i + 1]['data'];
575 $d_next_upper = $t_next == 'alpha' ? strtoupper($d_next) : $d_next;
576 } else {
577 $t_next = '';
578 $d_next = '';
579 $d_next_upper = '';
582 //DEBUG echo "[prev: <b>".$d_prev."</b> ".$t_prev."][cur: <b>".$d_cur."</b> ".$t_cur."][next: <b>".$d_next."</b> ".$t_next."]<br />";
584 if ($t_cur == 'alpha') {
585 $t_suffix = '_identifier';
586 if (($t_next == 'punct_qualifier') || ($t_prev == 'punct_qualifier')) {
587 $t_suffix = '_identifier';
588 } else if (($t_next == 'punct_bracket_open_round')
589 && PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_function_name, $PMA_SQPdata_function_name_cnt)) {
590 // FIXME-2005-10-16: in the case of a CREATE TABLE containing a TIMESTAMP,
591 // since TIMESTAMP() is also a function, it's found here and
592 // the token is wrongly marked as alpha_functionName. But we
593 // compensate for this when analysing for timestamp_not_null
594 // later in this script.
595 $t_suffix = '_functionName';
596 } else if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_type, $PMA_SQPdata_column_type_cnt)) {
597 $t_suffix = '_columnType';
599 // Temporary fix for BUG #621357
600 //TODO FIX PROPERLY NEEDS OVERHAUL OF SQL TOKENIZER
601 if ($d_cur_upper == 'SET' && $t_next != 'punct_bracket_open_round') {
602 $t_suffix = '_reservedWord';
604 //END OF TEMPORARY FIX
606 // CHARACTER is a synonym for CHAR, but can also be meant as
607 // CHARACTER SET. In this case, we have a reserved word.
608 if ($d_cur_upper == 'CHARACTER' && $d_next_upper == 'SET') {
609 $t_suffix = '_reservedWord';
612 // experimental
613 // current is a column type, so previous must not be
614 // a reserved word but an identifier
615 // CREATE TABLE SG_Persons (first varchar(64))
617 //if ($sql_array[$i-1]['type'] =='alpha_reservedWord') {
618 // $sql_array[$i-1]['type'] = 'alpha_identifier';
621 } else if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_reserved_word, $PMA_SQPdata_reserved_word_cnt)) {
622 $t_suffix = '_reservedWord';
623 } else if (PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_column_attrib, $PMA_SQPdata_column_attrib_cnt)) {
624 $t_suffix = '_columnAttrib';
625 // INNODB is a MySQL table type, but in "SHOW INNODB STATUS",
626 // it should be regarded as a reserved word.
627 if ($d_cur_upper == 'INNODB' && $d_prev_upper == 'SHOW' && $d_next_upper == 'STATUS') {
628 $t_suffix = '_reservedWord';
631 if ($d_cur_upper == 'DEFAULT' && $d_next_upper == 'CHARACTER') {
632 $t_suffix = '_reservedWord';
634 // Binary as character set
635 if ($d_cur_upper == 'BINARY' && (
636 ($d_bef_prev_upper == 'CHARACTER' && $d_prev_upper == 'SET')
637 || ($d_bef_prev_upper == 'SET' && $d_prev_upper == '=')
638 || ($d_bef_prev_upper == 'CHARSET' && $d_prev_upper == '=')
639 || $d_prev_upper == 'CHARSET'
640 ) && PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, count($mysql_charsets))) {
641 $t_suffix = '_charset';
643 } elseif (PMA_STR_binarySearchInArr($d_cur, $mysql_charsets, $mysql_charsets_count)
644 || PMA_STR_binarySearchInArr($d_cur, $mysql_collations_flat, $mysql_collations_count)
645 || ($d_cur{0} == '_' && PMA_STR_binarySearchInArr(substr($d_cur, 1), $mysql_charsets, $mysql_charsets_count))) {
646 $t_suffix = '_charset';
647 } else {
648 // Do nothing
650 // check if present in the list of forbidden words
651 if ($t_suffix == '_reservedWord' && PMA_STR_binarySearchInArr($d_cur_upper, $PMA_SQPdata_forbidden_word, $PMA_SQPdata_forbidden_word_cnt)) {
652 $sql_array[$i]['forbidden'] = TRUE;
653 } else {
654 $sql_array[$i]['forbidden'] = FALSE;
656 $sql_array[$i]['type'] .= $t_suffix;
658 } // end for
660 // Stores the size of the array inside the array, as count() is a slow
661 // operation.
662 $sql_array['len'] = $arraysize;
664 // Sends the data back
665 return $sql_array;
666 } // end of the "PMA_SQP_parse()" function
669 * Checks for token types being what we want...
671 * @param string String of type that we have
672 * @param string String of type that we want
674 * @return boolean result of check
676 * @access private
678 function PMA_SQP_typeCheck($toCheck, $whatWeWant)
680 $typeSeperator = '_';
681 if (strcmp($whatWeWant, $toCheck) == 0) {
682 return TRUE;
683 } else {
684 if (strpos($whatWeWant, $typeSeperator) === FALSE) {
685 return strncmp($whatWeWant, $toCheck , strpos($toCheck, $typeSeperator)) == 0;
686 } else {
687 return FALSE;
694 * Analyzes SQL queries
696 * @param array The SQL queries
698 * @return array The analyzed SQL queries
700 * @access public
702 function PMA_SQP_analyze($arr)
704 if ($arr == array()) return array();
705 $result = array();
706 $size = $arr['len'];
707 $subresult = array(
708 'querytype' => '',
709 'select_expr_clause'=> '', // the whole stuff between SELECT and FROM , except DISTINCT
710 'position_of_first_select' => '', // the array index
711 'from_clause'=> '',
712 'group_by_clause'=> '',
713 'order_by_clause'=> '',
714 'having_clause' => '',
715 'where_clause' => '',
716 'where_clause_identifiers' => array(),
717 'unsorted_query' => '',
718 'queryflags' => array(),
719 'select_expr' => array(),
720 'table_ref' => array(),
721 'foreign_keys' => array(),
722 'create_table_fields' => array()
724 $subresult_empty = $subresult;
725 $seek_queryend = FALSE;
726 $seen_end_of_table_ref = FALSE;
727 $number_of_brackets_in_extract = 0;
728 $number_of_brackets_in_group_concat = 0;
730 // for SELECT EXTRACT(YEAR_MONTH FROM CURDATE())
731 // we must not use CURDATE as a table_ref
732 // so we track wether we are in the EXTRACT()
733 $in_extract = FALSE;
735 // for GROUP_CONCAT( ... )
736 $in_group_concat = FALSE;
738 /* Description of analyzer results by lem9
740 * db, table, column, alias
741 * ------------------------
743 * Inside the $subresult array, we create ['select_expr'] and ['table_ref'] arrays.
745 * The SELECT syntax (simplified) is
747 * SELECT
748 * select_expression,...
749 * [FROM [table_references]
752 * ['select_expr'] is filled with each expression, the key represents the
753 * expression position in the list (0-based) (so we don't lose track of
754 * multiple occurences of the same column).
756 * ['table_ref'] is filled with each table ref, same thing for the key.
758 * I create all sub-values empty, even if they are
759 * not present (for example no select_expression alias).
761 * There is a debug section at the end of loop #1, if you want to
762 * see the exact contents of select_expr and table_ref
764 * queryflags
765 * ----------
767 * In $subresult, array 'queryflags' is filled, according to what we
768 * find in the query.
770 * Currently, those are generated:
772 * ['queryflags']['need_confirm'] = 1; if the query needs confirmation
773 * ['queryflags']['select_from'] = 1; if this is a real SELECT...FROM
774 * ['queryflags']['distinct'] = 1; for a DISTINCT
775 * ['queryflags']['union'] = 1; for a UNION
776 * ['queryflags']['join'] = 1; for a JOIN
777 * ['queryflags']['offset'] = 1; for the presence of OFFSET
779 * query clauses
780 * -------------
782 * The select is splitted in those clauses:
783 * ['select_expr_clause']
784 * ['from_clause']
785 * ['group_by_clause']
786 * ['order_by_clause']
787 * ['having_clause']
788 * ['where_clause']
790 * The identifiers of the WHERE clause are put into the array
791 * ['where_clause_identifier']
793 * For a SELECT, the whole query without the ORDER BY clause is put into
794 * ['unsorted_query']
796 * foreign keys
797 * ------------
798 * The CREATE TABLE may contain FOREIGN KEY clauses, so they get
799 * analyzed and ['foreign_keys'] is an array filled with
800 * the constraint name, the index list,
801 * the REFERENCES table name and REFERENCES index list,
802 * and ON UPDATE | ON DELETE clauses
804 * position_of_first_select
805 * ------------------------
807 * The array index of the first SELECT we find. Will be used to
808 * insert a SQL_CALC_FOUND_ROWS.
810 * create_table_fields
811 * -------------------
813 * For now, mostly used to detect the DEFAULT CURRENT_TIMESTAMP and
814 * ON UPDATE CURRENT_TIMESTAMP clauses of the CREATE TABLE query.
815 * An array, each element is the identifier name.
816 * Note that for now, the timestamp_not_null element is created
817 * even for non-TIMESTAMP fields.
819 * Sub-elements: ['type'] which contains the column type
820 * optional (currently they are never false but can be absent):
821 * ['default_current_timestamp'] boolean
822 * ['on_update_current_timestamp'] boolean
823 * ['timestamp_not_null'] boolean
825 * section_before_limit, section_after_limit
826 * -----------------------------------------
828 * Marks the point of the query where we can insert a LIMIT clause;
829 * so the section_before_limit will contain the left part before
830 * a possible LIMIT clause
833 * End of description of analyzer results
836 // must be sorted
837 // TODO: current logic checks for only one word, so I put only the
838 // first word of the reserved expressions that end a table ref;
839 // maybe this is not ok (the first word might mean something else)
840 // $words_ending_table_ref = array(
841 // 'FOR UPDATE',
842 // 'GROUP BY',
843 // 'HAVING',
844 // 'LIMIT',
845 // 'LOCK IN SHARE MODE',
846 // 'ORDER BY',
847 // 'PROCEDURE',
848 // 'UNION',
849 // 'WHERE'
850 // );
851 $words_ending_table_ref = array(
852 'FOR',
853 'GROUP',
854 'HAVING',
855 'LIMIT',
856 'LOCK',
857 'ORDER',
858 'PROCEDURE',
859 'UNION',
860 'WHERE'
862 $words_ending_table_ref_cnt = 9; //count($words_ending_table_ref);
864 $words_ending_clauses = array(
865 'FOR',
866 'LIMIT',
867 'LOCK',
868 'PROCEDURE',
869 'UNION'
871 $words_ending_clauses_cnt = 5; //count($words_ending_clauses);
876 // must be sorted
877 $supported_query_types = array(
878 'SELECT'
880 // Support for these additional query types will come later on.
881 'DELETE',
882 'INSERT',
883 'REPLACE',
884 'TRUNCATE',
885 'UPDATE'
886 'EXPLAIN',
887 'DESCRIBE',
888 'SHOW',
889 'CREATE',
890 'SET',
891 'ALTER'
894 $supported_query_types_cnt = count($supported_query_types);
896 // loop #1 for each token: select_expr, table_ref for SELECT
898 for ($i = 0; $i < $size; $i++) {
899 //DEBUG echo "trace loop1 <b>" . $arr[$i]['data'] . "</b> (" . $arr[$i]['type'] . ")<br />";
901 // High speed seek for locating the end of the current query
902 if ($seek_queryend == TRUE) {
903 if ($arr[$i]['type'] == 'punct_queryend') {
904 $seek_queryend = FALSE;
905 } else {
906 continue;
907 } // end if (type == punct_queryend)
908 } // end if ($seek_queryend)
910 // TODO: when we find a UNION, should we split
911 // in another subresult?
912 if ($arr[$i]['type'] == 'punct_queryend') {
913 $result[] = $subresult;
914 $subresult = $subresult_empty;
915 continue;
916 } // end if (type == punct_queryend)
918 // ==============================================================
919 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
920 if ($in_extract) {
921 $number_of_brackets_in_extract++;
923 if ($in_group_concat) {
924 $number_of_brackets_in_group_concat++;
927 // ==============================================================
928 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
929 if ($in_extract) {
930 $number_of_brackets_in_extract--;
931 if ($number_of_brackets_in_extract == 0) {
932 $in_extract = FALSE;
935 if ($in_group_concat) {
936 $number_of_brackets_in_group_concat--;
937 if ($number_of_brackets_in_group_concat == 0) {
938 $in_group_concat = FALSE;
942 // ==============================================================
943 if ($arr[$i]['type'] == 'alpha_functionName') {
944 $upper_data = strtoupper($arr[$i]['data']);
945 if ($upper_data =='EXTRACT') {
946 $in_extract = TRUE;
947 $number_of_brackets_in_extract = 0;
949 if ($upper_data =='GROUP_CONCAT') {
950 $in_group_concat = TRUE;
951 $number_of_brackets_in_group_concat = 0;
955 // ==============================================================
956 if ($arr[$i]['type'] == 'alpha_reservedWord'
957 // && $arr[$i]['forbidden'] == FALSE) {
959 // We don't know what type of query yet, so run this
960 if ($subresult['querytype'] == '') {
961 $subresult['querytype'] = strtoupper($arr[$i]['data']);
962 } // end if (querytype was empty)
964 // Check if we support this type of query
965 if (!PMA_STR_binarySearchInArr($subresult['querytype'], $supported_query_types, $supported_query_types_cnt)) {
966 // Skip ahead to the next one if we don't
967 $seek_queryend = TRUE;
968 continue;
969 } // end if (query not supported)
971 // upper once
972 $upper_data = strtoupper($arr[$i]['data']);
973 //TODO: reset for each query?
975 if ($upper_data == 'SELECT') {
976 $seen_from = FALSE;
977 $previous_was_identifier = FALSE;
978 $current_select_expr = -1;
979 $seen_end_of_table_ref = FALSE;
980 } // end if ( data == SELECT)
982 if ($upper_data =='FROM' && !$in_extract) {
983 $current_table_ref = -1;
984 $seen_from = TRUE;
985 $previous_was_identifier = FALSE;
986 $save_table_ref = TRUE;
987 } // end if (data == FROM)
989 // here, do not 'continue' the loop, as we have more work for
990 // reserved words below
991 } // end if (type == alpha_reservedWord)
993 // ==============================
994 if ($arr[$i]['type'] == 'quote_backtick'
995 || $arr[$i]['type'] == 'quote_double'
996 || $arr[$i]['type'] == 'quote_single'
997 || $arr[$i]['type'] == 'alpha_identifier'
998 || ($arr[$i]['type'] == 'alpha_reservedWord'
999 && $arr[$i]['forbidden'] == FALSE)) {
1001 switch ($arr[$i]['type']) {
1002 case 'alpha_identifier':
1003 case 'alpha_reservedWord':
1004 // this is not a real reservedWord, because
1005 // it's not present in the list of forbidden words,
1006 // for example "storage" which can be used as
1007 // an identifier
1009 // TODO: avoid the pretty printing in color
1010 // in this case
1012 $identifier = $arr[$i]['data'];
1013 break;
1015 //TODO: check embedded double quotes or backticks?
1016 // and/or remove just the first and last character?
1017 case 'quote_backtick':
1018 $identifier = str_replace('`','',$arr[$i]['data']);
1019 break;
1020 case 'quote_double':
1021 $identifier = str_replace('"','',$arr[$i]['data']);
1022 break;
1023 case 'quote_single':
1024 $identifier = str_replace("'","",$arr[$i]['data']);
1025 break;
1026 } // end switch
1028 if ($subresult['querytype'] == 'SELECT' && !$in_group_concat) {
1029 if (!$seen_from) {
1030 if ($previous_was_identifier && isset($chain)) {
1031 // found alias for this select_expr, save it
1032 // but only if we got something in $chain
1033 // (for example, SELECT COUNT(*) AS cnt
1034 // puts nothing in $chain, so we avoid
1035 // setting the alias)
1036 $alias_for_select_expr = $identifier;
1037 } else {
1038 $chain[] = $identifier;
1039 $previous_was_identifier = TRUE;
1041 } // end if !$previous_was_identifier
1042 } else {
1043 // ($seen_from)
1044 if ($save_table_ref && !$seen_end_of_table_ref) {
1045 if ($previous_was_identifier) {
1046 // found alias for table ref
1047 // save it for later
1048 $alias_for_table_ref = $identifier;
1049 } else {
1050 $chain[] = $identifier;
1051 $previous_was_identifier = TRUE;
1053 } // end if ($previous_was_identifier)
1054 } // end if ($save_table_ref &&!$seen_end_of_table_ref)
1055 } // end if (!$seen_from)
1056 } // end if (querytype SELECT)
1057 } // end if ( quote_backtick or double quote or alpha_identifier)
1059 // ===================================
1060 if ($arr[$i]['type'] == 'punct_qualifier') {
1061 // to be able to detect an identifier following another
1062 $previous_was_identifier = FALSE;
1063 continue;
1064 } // end if (punct_qualifier)
1066 // TODO: check if 3 identifiers following one another -> error
1068 // s a v e a s e l e c t e x p r
1069 // finding a list separator or FROM
1070 // means that we must save the current chain of identifiers
1071 // into a select expression
1073 // for now, we only save a select expression if it contains
1074 // at least one identifier, as we are interested in checking
1075 // the columns and table names, so in "select * from persons",
1076 // the "*" is not saved
1078 if (isset($chain) && !$seen_end_of_table_ref
1079 && ( (!$seen_from
1080 && $arr[$i]['type'] == 'punct_listsep')
1081 || ($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data == 'FROM')) ) {
1082 $size_chain = count($chain);
1083 $current_select_expr++;
1084 $subresult['select_expr'][$current_select_expr] = array(
1085 'expr' => '',
1086 'alias' => '',
1087 'db' => '',
1088 'table_name' => '',
1089 'table_true_name' => '',
1090 'column' => ''
1093 if (!empty($alias_for_select_expr)) {
1094 // we had found an alias for this select expression
1095 $subresult['select_expr'][$current_select_expr]['alias'] = $alias_for_select_expr;
1096 unset($alias_for_select_expr);
1098 // there is at least a column
1099 $subresult['select_expr'][$current_select_expr]['column'] = $chain[$size_chain - 1];
1100 $subresult['select_expr'][$current_select_expr]['expr'] = $chain[$size_chain - 1];
1102 // maybe a table
1103 if ($size_chain > 1) {
1104 $subresult['select_expr'][$current_select_expr]['table_name'] = $chain[$size_chain - 2];
1105 // we assume for now that this is also the true name
1106 $subresult['select_expr'][$current_select_expr]['table_true_name'] = $chain[$size_chain - 2];
1107 $subresult['select_expr'][$current_select_expr]['expr']
1108 = $subresult['select_expr'][$current_select_expr]['table_name']
1109 . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
1110 } // end if ($size_chain > 1)
1112 // maybe a db
1113 if ($size_chain > 2) {
1114 $subresult['select_expr'][$current_select_expr]['db'] = $chain[$size_chain - 3];
1115 $subresult['select_expr'][$current_select_expr]['expr']
1116 = $subresult['select_expr'][$current_select_expr]['db']
1117 . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
1118 } // end if ($size_chain > 2)
1119 unset($chain);
1121 // TODO: explain this:
1122 if (($arr[$i]['type'] == 'alpha_reservedWord')
1123 && ($upper_data != 'FROM')) {
1124 $previous_was_identifier = TRUE;
1127 } // end if (save a select expr)
1130 //======================================
1131 // s a v e a t a b l e r e f
1132 //======================================
1134 // maybe we just saw the end of table refs
1135 // but the last table ref has to be saved
1136 // or we are at the last token (TODO: there could be another
1137 // query after this one)
1138 // or we just got a reserved word
1140 if (isset($chain) && $seen_from && $save_table_ref
1141 && ($arr[$i]['type'] == 'punct_listsep'
1142 || ($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data!="AS")
1143 || $seen_end_of_table_ref
1144 || $i==$size-1 )) {
1146 $size_chain = count($chain);
1147 $current_table_ref++;
1148 $subresult['table_ref'][$current_table_ref] = array(
1149 'expr' => '',
1150 'db' => '',
1151 'table_name' => '',
1152 'table_alias' => '',
1153 'table_true_name' => ''
1155 if (!empty($alias_for_table_ref)) {
1156 $subresult['table_ref'][$current_table_ref]['table_alias'] = $alias_for_table_ref;
1157 unset($alias_for_table_ref);
1159 $subresult['table_ref'][$current_table_ref]['table_name'] = $chain[$size_chain - 1];
1160 // we assume for now that this is also the true name
1161 $subresult['table_ref'][$current_table_ref]['table_true_name'] = $chain[$size_chain - 1];
1162 $subresult['table_ref'][$current_table_ref]['expr']
1163 = $subresult['table_ref'][$current_table_ref]['table_name'];
1164 // maybe a db
1165 if ($size_chain > 1) {
1166 $subresult['table_ref'][$current_table_ref]['db'] = $chain[$size_chain - 2];
1167 $subresult['table_ref'][$current_table_ref]['expr']
1168 = $subresult['table_ref'][$current_table_ref]['db']
1169 . '.' . $subresult['table_ref'][$current_table_ref]['expr'];
1170 } // end if ($size_chain > 1)
1172 // add the table alias into the whole expression
1173 $subresult['table_ref'][$current_table_ref]['expr']
1174 .= ' ' . $subresult['table_ref'][$current_table_ref]['table_alias'];
1176 unset($chain);
1177 $previous_was_identifier = TRUE;
1178 //continue;
1180 } // end if (save a table ref)
1183 // when we have found all table refs,
1184 // for each table_ref alias, put the true name of the table
1185 // in the corresponding select expressions
1187 if (isset($current_table_ref) && ($seen_end_of_table_ref || $i == $size-1) && $subresult != $subresult_empty) {
1188 for ($tr=0; $tr <= $current_table_ref; $tr++) {
1189 $alias = $subresult['table_ref'][$tr]['table_alias'];
1190 $truename = $subresult['table_ref'][$tr]['table_true_name'];
1191 for ($se=0; $se <= $current_select_expr; $se++) {
1192 if (!empty($alias) && $subresult['select_expr'][$se]['table_true_name']
1193 == $alias) {
1194 $subresult['select_expr'][$se]['table_true_name']
1195 = $truename;
1196 } // end if (found the alias)
1197 } // end for (select expressions)
1199 } // end for (table refs)
1200 } // end if (set the true names)
1203 // e n d i n g l o o p #1
1204 // set the $previous_was_identifier to FALSE if the current
1205 // token is not an identifier
1206 if (($arr[$i]['type'] != 'alpha_identifier')
1207 && ($arr[$i]['type'] != 'quote_double')
1208 && ($arr[$i]['type'] != 'quote_single')
1209 && ($arr[$i]['type'] != 'quote_backtick')) {
1210 $previous_was_identifier = FALSE;
1211 } // end if
1213 // however, if we are on AS, we must keep the $previous_was_identifier
1214 if (($arr[$i]['type'] == 'alpha_reservedWord')
1215 && ($upper_data == 'AS')) {
1216 $previous_was_identifier = TRUE;
1219 if (($arr[$i]['type'] == 'alpha_reservedWord')
1220 && ($upper_data =='ON' || $upper_data =='USING')) {
1221 $save_table_ref = FALSE;
1222 } // end if (data == ON)
1224 if (($arr[$i]['type'] == 'alpha_reservedWord')
1225 && ($upper_data =='JOIN' || $upper_data =='FROM')) {
1226 $save_table_ref = TRUE;
1227 } // end if (data == JOIN)
1229 // no need to check the end of table ref if we already did
1230 // TODO: maybe add "&& $seen_from"
1231 if (!$seen_end_of_table_ref) {
1232 // if this is the last token, it implies that we have
1233 // seen the end of table references
1234 // Check for the end of table references
1236 // Note: if we are analyzing a GROUP_CONCAT clause,
1237 // we might find a word that seems to indicate that
1238 // we have found the end of table refs (like ORDER)
1239 // but it's a modifier of the GROUP_CONCAT so
1240 // it's not the real end of table refs
1241 if (($i == $size-1)
1242 || ($arr[$i]['type'] == 'alpha_reservedWord'
1243 && !$in_group_concat
1244 && PMA_STR_binarySearchInArr($upper_data, $words_ending_table_ref, $words_ending_table_ref_cnt))) {
1245 $seen_end_of_table_ref = TRUE;
1246 // to be able to save the last table ref, but do not
1247 // set it true if we found a word like "ON" that has
1248 // already set it to false
1249 if (isset($save_table_ref) && $save_table_ref != FALSE) {
1250 $save_table_ref = TRUE;
1251 } //end if
1253 } // end if (check for end of table ref)
1254 } //end if (!$seen_end_of_table_ref)
1256 if ($seen_end_of_table_ref) {
1257 $save_table_ref = FALSE;
1258 } // end if
1260 } // end for $i (loop #1)
1262 // -------------------------------------------------------
1263 // This is a big hunk of debugging code by Marc for this.
1264 // -------------------------------------------------------
1266 if (isset($current_select_expr)) {
1267 for ($trace=0; $trace<=$current_select_expr; $trace++) {
1268 echo "<br />";
1269 reset ($subresult['select_expr'][$trace]);
1270 while (list ($key, $val) = each ($subresult['select_expr'][$trace]))
1271 echo "sel expr $trace $key => $val<br />\n";
1275 if (isset($current_table_ref)) {
1276 echo "current_table_ref = " . $current_table_ref . "<br>";
1277 for ($trace=0; $trace<=$current_table_ref; $trace++) {
1279 echo "<br />";
1280 reset ($subresult['table_ref'][$trace]);
1281 while (list ($key, $val) = each ($subresult['table_ref'][$trace]))
1282 echo "table ref $trace $key => $val<br />\n";
1286 // -------------------------------------------------------
1289 // loop #2: - queryflags
1290 // - querytype (for queries != 'SELECT')
1291 // - section_before_limit, section_after_limit
1293 // we will also need this queryflag in loop 2
1294 // so set it here
1295 if (isset($current_table_ref) && $current_table_ref > -1) {
1296 $subresult['queryflags']['select_from'] = 1;
1299 $collect_section_before_limit = TRUE;
1300 $section_before_limit = '';
1301 $section_after_limit = '';
1302 $seen_reserved_word = FALSE;
1303 $seen_group = FALSE;
1304 $seen_order = FALSE;
1305 $in_group_by = FALSE; // true when we are inside the GROUP BY clause
1306 $in_order_by = FALSE; // true when we are inside the ORDER BY clause
1307 $in_having = FALSE; // true when we are inside the HAVING clause
1308 $in_select_expr = FALSE; // true when we are inside the select expr clause
1309 $in_where = FALSE; // true when we are inside the WHERE clause
1310 $in_from = FALSE;
1311 $in_group_concat = FALSE;
1312 $unsorted_query = '';
1313 $first_reserved_word = '';
1314 $current_identifier = '';
1316 for ($i = 0; $i < $size; $i++) {
1317 //DEBUG echo "trace loop2 <b>" . $arr[$i]['data'] . "</b> (" . $arr[$i]['type'] . ")<br />";
1319 // need_confirm
1321 // check for reserved words that will have to generate
1322 // a confirmation request later in sql.php
1323 // the cases are:
1324 // DROP TABLE
1325 // DROP DATABASE
1326 // ALTER TABLE... DROP
1327 // DELETE FROM...
1329 // this code is not used for confirmations coming from functions.js
1331 // TODO: check for punct_queryend
1334 // TODO: verify C-style comments?
1335 if ($arr[$i]['type'] == 'comment_ansi') {
1336 $collect_section_before_limit = FALSE;
1339 if ($arr[$i]['type'] == 'alpha_reservedWord') {
1340 $upper_data = strtoupper($arr[$i]['data']);
1341 if (!$seen_reserved_word) {
1342 $first_reserved_word = $upper_data;
1343 $subresult['querytype'] = $upper_data;
1344 $seen_reserved_word = TRUE;
1346 // if the first reserved word is DROP or DELETE,
1347 // we know this is a query that needs to be confirmed
1348 if ($first_reserved_word=='DROP'
1349 || $first_reserved_word == 'DELETE'
1350 || $first_reserved_word == 'TRUNCATE') {
1351 $subresult['queryflags']['need_confirm'] = 1;
1354 if ($first_reserved_word=='SELECT'){
1355 $position_of_first_select = $i;
1358 } else {
1359 if ($upper_data=='DROP' && $first_reserved_word=='ALTER') {
1360 $subresult['queryflags']['need_confirm'] = 1;
1364 if ($upper_data == 'PROCEDURE') {
1365 $collect_section_before_limit = FALSE;
1367 // TODO: set also to FALSE if we find
1368 // FOR UPDATE
1369 // LOCK IN SHARE MODE
1371 if ($upper_data == 'SELECT') {
1372 $in_select_expr = TRUE;
1373 $select_expr_clause = '';
1375 if ($upper_data == 'DISTINCT' && !$in_group_concat) {
1376 $subresult['queryflags']['distinct'] = 1;
1379 if ($upper_data == 'UNION') {
1380 $subresult['queryflags']['union'] = 1;
1383 if ($upper_data == 'JOIN') {
1384 $subresult['queryflags']['join'] = 1;
1387 if ($upper_data == 'OFFSET') {
1388 $subresult['queryflags']['offset'] = 1;
1391 // if this is a real SELECT...FROM
1392 if ($upper_data == 'FROM' && isset($subresult['queryflags']['select_from']) && $subresult['queryflags']['select_from'] == 1) {
1393 $in_from = TRUE;
1394 $from_clause = '';
1395 $in_select_expr = FALSE;
1399 // (we could have less resetting of variables to FALSE
1400 // if we trust that the query respects the standard
1401 // MySQL order for clauses)
1403 // we use $seen_group and $seen_order because we are looking
1404 // for the BY
1405 if ($upper_data == 'GROUP') {
1406 $seen_group = TRUE;
1407 $seen_order = FALSE;
1408 $in_having = FALSE;
1409 $in_order_by = FALSE;
1410 $in_where = FALSE;
1411 $in_select_expr = FALSE;
1412 $in_from = FALSE;
1414 if ($upper_data == 'ORDER' && !$in_group_concat) {
1415 $seen_order = TRUE;
1416 $seen_group = FALSE;
1417 $in_having = FALSE;
1418 $in_group_by = FALSE;
1419 $in_where = FALSE;
1420 $in_select_expr = FALSE;
1421 $in_from = FALSE;
1423 if ($upper_data == 'HAVING') {
1424 $in_having = TRUE;
1425 $having_clause = '';
1426 $seen_group = FALSE;
1427 $seen_order = FALSE;
1428 $in_group_by = FALSE;
1429 $in_order_by = FALSE;
1430 $in_where = FALSE;
1431 $in_select_expr = FALSE;
1432 $in_from = FALSE;
1435 if ($upper_data == 'WHERE') {
1436 $in_where = TRUE;
1437 $where_clause = '';
1438 $where_clause_identifiers = array();
1439 $seen_group = FALSE;
1440 $seen_order = FALSE;
1441 $in_group_by = FALSE;
1442 $in_order_by = FALSE;
1443 $in_having = FALSE;
1444 $in_select_expr = FALSE;
1445 $in_from = FALSE;
1448 if ($upper_data == 'BY') {
1449 if ($seen_group) {
1450 $in_group_by = TRUE;
1451 $group_by_clause = '';
1453 if ($seen_order) {
1454 $in_order_by = TRUE;
1455 $order_by_clause = '';
1459 // if we find one of the words that could end the clause
1460 if (PMA_STR_binarySearchInArr($upper_data, $words_ending_clauses, $words_ending_clauses_cnt)) {
1462 $in_group_by = FALSE;
1463 $in_order_by = FALSE;
1464 $in_having = FALSE;
1465 $in_where = FALSE;
1466 $in_select_expr = FALSE;
1467 $in_from = FALSE;
1470 } // endif (reservedWord)
1473 // do not add a blank after a function name
1474 // TODO: can we combine loop 2 and loop 1?
1475 // some code is repeated here...
1477 $sep=' ';
1478 if ($arr[$i]['type'] == 'alpha_functionName') {
1479 $sep='';
1480 $upper_data = strtoupper($arr[$i]['data']);
1481 if ($upper_data =='GROUP_CONCAT') {
1482 $in_group_concat = TRUE;
1483 $number_of_brackets_in_group_concat = 0;
1487 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1488 if ($in_group_concat) {
1489 $number_of_brackets_in_group_concat++;
1492 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1493 if ($in_group_concat) {
1494 $number_of_brackets_in_group_concat--;
1495 if ($number_of_brackets_in_group_concat == 0) {
1496 $in_group_concat = FALSE;
1501 if ($in_select_expr && $upper_data != 'SELECT' && $upper_data != 'DISTINCT') {
1502 $select_expr_clause .= $arr[$i]['data'] . $sep;
1504 if ($in_from && $upper_data != 'FROM') {
1505 $from_clause .= $arr[$i]['data'] . $sep;
1507 if ($in_group_by && $upper_data != 'GROUP' && $upper_data != 'BY') {
1508 $group_by_clause .= $arr[$i]['data'] . $sep;
1510 if ($in_order_by && $upper_data != 'ORDER' && $upper_data != 'BY') {
1511 $order_by_clause .= $arr[$i]['data'] . $sep;
1513 if ($in_having && $upper_data != 'HAVING') {
1514 $having_clause .= $arr[$i]['data'] . $sep;
1516 if ($in_where && $upper_data != 'WHERE') {
1517 $where_clause .= $arr[$i]['data'] . $sep;
1519 if (($arr[$i]['type'] == 'quote_backtick')
1520 || ($arr[$i]['type'] == 'alpha_identifier')) {
1521 $where_clause_identifiers[] = $arr[$i]['data'];
1525 if (isset($subresult['queryflags']['select_from'])
1526 && $subresult['queryflags']['select_from'] == 1
1527 && !$seen_order) {
1528 $unsorted_query .= $arr[$i]['data'];
1530 if ($arr[$i]['type'] != 'punct_bracket_open_round'
1531 && $arr[$i]['type'] != 'punct_bracket_close_round'
1532 && $arr[$i]['type'] != 'punct') {
1533 $unsorted_query .= $sep;
1537 // clear $upper_data for next iteration
1538 $upper_data='';
1540 if ($collect_section_before_limit) {
1541 $section_before_limit .= $arr[$i]['data'] . $sep;
1542 } else {
1543 $section_after_limit .= $arr[$i]['data'] . $sep;
1547 } // end for $i (loop #2)
1550 // -----------------------------------------------------
1551 // loop #3: foreign keys and MySQL 4.1.2+ TIMESTAMP options
1552 // (for now, check only the first query)
1553 // (for now, identifiers are assumed to be backquoted)
1555 // If we find that we are dealing with a CREATE TABLE query,
1556 // we look for the next punct_bracket_open_round, which
1557 // introduces the fields list. Then, when we find a
1558 // quote_backtick, it must be a field, so we put it into
1559 // the create_table_fields array. Even if this field is
1560 // not a timestamp, it will be useful when logic has been
1561 // added for complete field attributes analysis.
1563 $seen_foreign = FALSE;
1564 $seen_references = FALSE;
1565 $seen_constraint = FALSE;
1566 $foreign_key_number = -1;
1567 $seen_create_table = FALSE;
1568 $seen_create = FALSE;
1569 $in_create_table_fields = FALSE;
1570 $brackets_level = 0;
1571 $in_timestamp_options = FALSE;
1572 $seen_default = FALSE;
1574 for ($i = 0; $i < $size; $i++) {
1575 // DEBUG echo "<b>" . $arr[$i]['data'] . "</b> " . $arr[$i]['type'] . "<br />";
1577 if ($arr[$i]['type'] == 'alpha_reservedWord') {
1578 $upper_data = strtoupper($arr[$i]['data']);
1580 if ($upper_data == 'NOT' && $in_timestamp_options) {
1581 $create_table_fields[$current_identifier]['timestamp_not_null'] = TRUE;
1585 if ($upper_data == 'CREATE') {
1586 $seen_create = TRUE;
1589 if ($upper_data == 'TABLE' && $seen_create) {
1590 $seen_create_table = TRUE;
1591 $create_table_fields = array();
1594 if ($upper_data == 'CURRENT_TIMESTAMP') {
1595 if ($in_timestamp_options) {
1596 if ($seen_default) {
1597 $create_table_fields[$current_identifier]['default_current_timestamp'] = TRUE;
1602 if ($upper_data == 'CONSTRAINT') {
1603 $foreign_key_number++;
1604 $seen_foreign = FALSE;
1605 $seen_references = FALSE;
1606 $seen_constraint = TRUE;
1608 if ($upper_data == 'FOREIGN') {
1609 $seen_foreign = TRUE;
1610 $seen_references = FALSE;
1611 $seen_constraint = FALSE;
1613 if ($upper_data == 'REFERENCES') {
1614 $seen_foreign = FALSE;
1615 $seen_references = TRUE;
1616 $seen_constraint = FALSE;
1620 // Cases covered:
1622 // [ON DELETE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
1623 // [ON UPDATE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
1625 // but we set ['on_delete'] or ['on_cascade'] to
1626 // CASCADE | SET_NULL | NO_ACTION | RESTRICT
1628 // ON UPDATE CURRENT_TIMESTAMP
1630 if ($upper_data == 'ON') {
1631 if ($arr[$i+1]['type'] == 'alpha_reservedWord') {
1632 $second_upper_data = strtoupper($arr[$i+1]['data']);
1633 if ($second_upper_data == 'DELETE') {
1634 $clause = 'on_delete';
1636 if ($second_upper_data == 'UPDATE') {
1637 $clause = 'on_update';
1639 if (isset($clause)
1640 && ($arr[$i+2]['type'] == 'alpha_reservedWord'
1642 // ugly workaround because currently, NO is not
1643 // in the list of reserved words in sqlparser.data
1644 // (we got a bug report about not being able to use
1645 // 'no' as an identifier)
1646 || ($arr[$i+2]['type'] == 'alpha_identifier'
1647 && strtoupper($arr[$i+2]['data'])=='NO') )
1649 $third_upper_data = strtoupper($arr[$i+2]['data']);
1650 if ($third_upper_data == 'CASCADE'
1651 || $third_upper_data == 'RESTRICT') {
1652 $value = $third_upper_data;
1653 } elseif ($third_upper_data == 'SET'
1654 || $third_upper_data == 'NO') {
1655 if ($arr[$i+3]['type'] == 'alpha_reservedWord') {
1656 $value = $third_upper_data . '_' . strtoupper($arr[$i+3]['data']);
1658 } elseif ($third_upper_data == 'CURRENT_TIMESTAMP') {
1659 if ($clause == 'on_update'
1660 && $in_timestamp_options) {
1661 $create_table_fields[$current_identifier]['on_update_current_timestamp'] = TRUE;
1662 $seen_default = FALSE;
1665 } else {
1666 $value = '';
1668 if (!empty($value)) {
1669 $foreign[$foreign_key_number][$clause] = $value;
1671 unset($clause);
1672 } // endif (isset($clause))
1676 } // end of reserved words analysis
1679 if ($arr[$i]['type'] == 'punct_bracket_open_round') {
1680 $brackets_level++;
1681 if ($seen_create_table && $brackets_level == 1) {
1682 $in_create_table_fields = TRUE;
1687 if ($arr[$i]['type'] == 'punct_bracket_close_round') {
1688 $brackets_level--;
1689 if ($seen_references) {
1690 $seen_references = FALSE;
1692 if ($seen_create_table && $brackets_level == 0) {
1693 $in_create_table_fields = FALSE;
1697 if (($arr[$i]['type'] == 'alpha_columnAttrib')) {
1698 $upper_data = strtoupper($arr[$i]['data']);
1699 if ($seen_create_table && $in_create_table_fields) {
1700 if ($upper_data == 'DEFAULT') {
1701 $seen_default = TRUE;
1706 // note: the "or" part here is a workaround for a bug
1707 // (see FIXME-2005-10-16)
1708 if (($arr[$i]['type'] == 'alpha_columnType') || ($arr[$i]['type'] == 'alpha_functionName' && $seen_create_table)) {
1709 $upper_data = strtoupper($arr[$i]['data']);
1710 if ($seen_create_table && $in_create_table_fields && isset($current_identifier)) {
1711 $create_table_fields[$current_identifier]['type'] = $upper_data;
1712 if ($upper_data == 'TIMESTAMP') {
1713 $in_timestamp_options = TRUE;
1714 } else {
1715 $in_timestamp_options = FALSE;
1721 if ($arr[$i]['type'] == 'quote_backtick' || $arr[$i]['type'] == 'alpha_identifier') {
1723 if ($arr[$i]['type'] == 'quote_backtick') {
1724 // remove backquotes
1725 $identifier = str_replace('`','',$arr[$i]['data']);
1726 } else {
1727 $identifier = $arr[$i]['data'];
1730 if ($seen_create_table && $in_create_table_fields) {
1731 $current_identifier = $identifier;
1732 // warning: we set this one even for non TIMESTAMP type
1733 $create_table_fields[$current_identifier]['timestamp_not_null'] = FALSE;
1736 if ($seen_constraint) {
1737 $foreign[$foreign_key_number]['constraint'] = $identifier;
1740 if ($seen_foreign && $brackets_level > 0) {
1741 $foreign[$foreign_key_number]['index_list'][] = $identifier;
1744 if ($seen_references) {
1745 // here, the first bracket level corresponds to the
1746 // bracket of CREATE TABLE
1747 // so if we are on level 2, it must be the index list
1748 // of the foreign key REFERENCES
1749 if ($brackets_level > 1) {
1750 $foreign[$foreign_key_number]['ref_index_list'][] = $identifier;
1751 } else {
1752 // for MySQL 4.0.18, identifier is
1753 // `table` or `db`.`table`
1754 // the first pass will pick the db name
1755 // the next pass will execute the else and pick the
1756 // db name in $db_table[0]
1757 if ($arr[$i+1]['type'] == 'punct_qualifier') {
1758 $foreign[$foreign_key_number]['ref_db_name'] = $identifier;
1759 } else {
1760 // for MySQL 4.0.16, identifier is
1761 // `table` or `db.table`
1762 $db_table = explode('.',$identifier);
1763 if (isset($db_table[1])) {
1764 $foreign[$foreign_key_number]['ref_db_name'] = $db_table[0];
1765 $foreign[$foreign_key_number]['ref_table_name'] = $db_table[1];
1766 } else {
1767 $foreign[$foreign_key_number]['ref_table_name'] = $db_table[0];
1773 } // end for $i (loop #3)
1776 // Fill the $subresult array
1778 if (isset($create_table_fields)) {
1779 $subresult['create_table_fields'] = $create_table_fields;
1782 if (isset($foreign)) {
1783 $subresult['foreign_keys'] = $foreign;
1786 if (isset($select_expr_clause)) {
1787 $subresult['select_expr_clause'] = $select_expr_clause;
1789 if (isset($from_clause)) {
1790 $subresult['from_clause'] = $from_clause;
1792 if (isset($group_by_clause)) {
1793 $subresult['group_by_clause'] = $group_by_clause;
1795 if (isset($order_by_clause)) {
1796 $subresult['order_by_clause'] = $order_by_clause;
1798 if (isset($having_clause)) {
1799 $subresult['having_clause'] = $having_clause;
1801 if (isset($where_clause)) {
1802 $subresult['where_clause'] = $where_clause;
1804 if (isset($unsorted_query) && !empty($unsorted_query)) {
1805 $subresult['unsorted_query'] = $unsorted_query;
1807 if (isset($where_clause_identifiers)) {
1808 $subresult['where_clause_identifiers'] = $where_clause_identifiers;
1811 if (isset($position_of_first_select)) {
1812 $subresult['position_of_first_select'] = $position_of_first_select;
1813 $subresult['section_before_limit'] = $section_before_limit;
1814 $subresult['section_after_limit'] = $section_after_limit;
1817 // They are naughty and didn't have a trailing semi-colon,
1818 // then still handle it properly
1819 if ($subresult['querytype'] != '') {
1820 $result[] = $subresult;
1822 return $result;
1823 } // end of the "PMA_SQP_analyze()" function
1827 * Colorizes SQL queries html formatted
1829 * @param array The SQL queries html formatted
1831 * @return array The colorized SQL queries
1833 * @access public
1835 function PMA_SQP_formatHtml_colorize($arr)
1837 $i = $GLOBALS['PMA_strpos']($arr['type'], '_');
1838 $class = '';
1839 if ($i > 0) {
1840 $class = 'syntax_' . PMA_substr($arr['type'], 0, $i) . ' ';
1843 $class .= 'syntax_' . $arr['type'];
1845 //TODO: check why adding a "\n" after the </span> would cause extra
1846 // blanks to be displayed:
1847 // SELECT p . person_name
1849 return '<span class="' . $class . '">' . htmlspecialchars($arr['data']) . '</span>';
1850 } // end of the "PMA_SQP_formatHtml_colorize()" function
1854 * Formats SQL queries to html
1856 * @param array The SQL queries
1857 * @param string mode
1858 * @param integer starting token
1859 * @param integer number of tokens to format, -1 = all
1861 * @return string The formatted SQL queries
1863 * @access public
1865 function PMA_SQP_formatHtml($arr, $mode='color', $start_token=0,
1866 $number_of_tokens=-1)
1868 // then check for an array
1869 if (!is_array($arr)) {
1870 return htmlspecialchars($arr);
1872 // first check for the SQL parser having hit an error
1873 if (PMA_SQP_isError()) {
1874 return htmlspecialchars($arr['raw']);
1876 // else do it properly
1877 switch ($mode) {
1878 case 'color':
1879 $str = '<span class="syntax">';
1880 $html_line_break = '<br />';
1881 break;
1882 case 'query_only':
1883 $str = '';
1884 $html_line_break = "\n";
1885 break;
1886 case 'text':
1887 $str = '';
1888 $html_line_break = '<br />';
1889 break;
1890 } // end switch
1891 $indent = 0;
1892 $bracketlevel = 0;
1893 $functionlevel = 0;
1894 $infunction = FALSE;
1895 $space_punct_listsep = ' ';
1896 $space_punct_listsep_function_name = ' ';
1897 // $space_alpha_reserved_word = '<br />'."\n";
1898 $space_alpha_reserved_word = ' ';
1900 $keywords_with_brackets_1before = array(
1901 'INDEX',
1902 'KEY',
1903 'ON',
1904 'USING'
1906 $keywords_with_brackets_1before_cnt = 4;
1908 $keywords_with_brackets_2before = array(
1909 'IGNORE',
1910 'INDEX',
1911 'INTO',
1912 'KEY',
1913 'PRIMARY',
1914 'PROCEDURE',
1915 'REFERENCES',
1916 'UNIQUE',
1917 'USE'
1919 // $keywords_with_brackets_2before_cnt = count($keywords_with_brackets_2before);
1920 $keywords_with_brackets_2before_cnt = 9;
1922 // These reserved words do NOT get a newline placed near them.
1923 $keywords_no_newline = array(
1924 'AS',
1925 'ASC',
1926 'DESC',
1927 'DISTINCT',
1928 'HOUR',
1929 'INTERVAL',
1930 'IS',
1931 'LIKE',
1932 'NOT',
1933 'NULL',
1934 'ON',
1935 'REGEXP'
1937 $keywords_no_newline_cnt = 12;
1939 // These reserved words introduce a privilege list
1940 $keywords_priv_list = array(
1941 'GRANT',
1942 'REVOKE'
1944 $keywords_priv_list_cnt = 2;
1946 if ($number_of_tokens == -1) {
1947 $arraysize = $arr['len'];
1948 } else {
1949 $arraysize = $number_of_tokens;
1951 $typearr = array();
1952 if ($arraysize >= 0) {
1953 $typearr[0] = '';
1954 $typearr[1] = '';
1955 $typearr[2] = '';
1956 //$typearr[3] = $arr[0]['type'];
1957 $typearr[3] = $arr[$start_token]['type'];
1960 $in_priv_list = FALSE;
1961 for ($i = $start_token; $i < $arraysize; $i++) {
1962 // DEBUG echo "<b>" . $arr[$i]['data'] . "</b> " . $arr[$i]['type'] . "<br />";
1963 $before = '';
1964 $after = '';
1965 $indent = 0;
1966 // array_shift($typearr);
1968 0 prev2
1969 1 prev
1970 2 current
1971 3 next
1973 if (($i + 1) < $arraysize) {
1974 // array_push($typearr, $arr[$i + 1]['type']);
1975 $typearr[4] = $arr[$i + 1]['type'];
1976 } else {
1977 //array_push($typearr, NULL);
1978 $typearr[4] = '';
1981 for ($j=0; $j<4; $j++) {
1982 $typearr[$j] = $typearr[$j + 1];
1985 switch ($typearr[2]) {
1986 case 'white_newline':
1987 $before = '';
1988 break;
1989 case 'punct_bracket_open_round':
1990 $bracketlevel++;
1991 $infunction = FALSE;
1992 // Make sure this array is sorted!
1993 if (($typearr[1] == 'alpha_functionName') || ($typearr[1] == 'alpha_columnType') || ($typearr[1] == 'punct')
1994 || ($typearr[3] == 'digit_integer') || ($typearr[3] == 'digit_hex') || ($typearr[3] == 'digit_float')
1995 || (($typearr[0] == 'alpha_reservedWord')
1996 && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 2]['data']), $keywords_with_brackets_2before, $keywords_with_brackets_2before_cnt))
1997 || (($typearr[1] == 'alpha_reservedWord')
1998 && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 1]['data']), $keywords_with_brackets_1before, $keywords_with_brackets_1before_cnt))
2000 $functionlevel++;
2001 $infunction = TRUE;
2002 $after .= ' ';
2003 } else {
2004 $indent++;
2005 $after .= ($mode != 'query_only' ? '<div class="syntax_indent' . $indent . '">' : ' ');
2007 break;
2008 case 'alpha_identifier':
2009 if (($typearr[1] == 'punct_qualifier') || ($typearr[3] == 'punct_qualifier')) {
2010 $after = '';
2011 $before = '';
2013 if (($typearr[3] == 'alpha_columnType') || ($typearr[3] == 'alpha_identifier')) {
2014 $after .= ' ';
2016 break;
2017 case 'punct_qualifier':
2018 $before = '';
2019 $after = '';
2020 break;
2021 case 'punct_listsep':
2022 if ($infunction == TRUE) {
2023 $after .= $space_punct_listsep_function_name;
2024 } else {
2025 $after .= $space_punct_listsep;
2027 break;
2028 case 'punct_queryend':
2029 if (($typearr[3] != 'comment_mysql') && ($typearr[3] != 'comment_ansi') && $typearr[3] != 'comment_c') {
2030 $after .= $html_line_break;
2031 $after .= $html_line_break;
2033 $space_punct_listsep = ' ';
2034 $space_punct_listsep_function_name = ' ';
2035 $space_alpha_reserved_word = ' ';
2036 $in_priv_list = FALSE;
2037 break;
2038 case 'comment_mysql':
2039 case 'comment_ansi':
2040 $after .= $html_line_break;
2041 break;
2042 case 'punct':
2043 $before .= ' ';
2044 // workaround for
2045 // select * from mytable limit 0,-1
2046 // (a side effect of this workaround is that
2047 // select 20 - 9
2048 // becomes
2049 // select 20 -9
2050 // )
2051 if ($typearr[3] != 'digit_integer') {
2052 $after .= ' ';
2054 break;
2055 case 'punct_bracket_close_round':
2056 $bracketlevel--;
2057 if ($infunction == TRUE) {
2058 $functionlevel--;
2059 $after .= ' ';
2060 $before .= ' ';
2061 } else {
2062 $indent--;
2063 $before .= ($mode != 'query_only' ? '</div>' : ' ');
2065 $infunction = ($functionlevel > 0) ? TRUE : FALSE;
2066 break;
2067 case 'alpha_columnType':
2068 if ($typearr[3] == 'alpha_columnAttrib') {
2069 $after .= ' ';
2071 if ($typearr[1] == 'alpha_columnType') {
2072 $before .= ' ';
2074 break;
2075 case 'alpha_columnAttrib':
2077 // ALTER TABLE tbl_name AUTO_INCREMENT = 1
2078 // COLLATE LATIN1_GENERAL_CI DEFAULT
2079 if ($typearr[1] == 'alpha_identifier' || $typearr[1] == 'alpha_charset') {
2080 $before .= ' ';
2082 if (($typearr[3] == 'alpha_columnAttrib') || ($typearr[3] == 'quote_single') || ($typearr[3] == 'digit_integer')) {
2083 $after .= ' ';
2085 // workaround for
2086 // select * from mysql.user where binary user="root"
2087 // binary is marked as alpha_columnAttrib
2088 // but should be marked as a reserved word
2089 if (strtoupper($arr[$i]['data']) == 'BINARY'
2090 && $typearr[3] == 'alpha_identifier') {
2091 $after .= ' ';
2093 break;
2094 case 'alpha_reservedWord':
2095 // do not uppercase the reserved word if we are calling
2096 // this function in query_only mode, because we need
2097 // the original query (otherwise we get problems with
2098 // semi-reserved words like "storage" which is legal
2099 // as an identifier name)
2101 if ($mode != 'query_only') {
2102 $arr[$i]['data'] = strtoupper($arr[$i]['data']);
2105 if ((($typearr[1] != 'alpha_reservedWord')
2106 || (($typearr[1] == 'alpha_reservedWord')
2107 && PMA_STR_binarySearchInArr(strtoupper($arr[$i - 1]['data']), $keywords_no_newline, $keywords_no_newline_cnt)))
2108 && ($typearr[1] != 'punct_level_plus')
2109 && (!PMA_STR_binarySearchInArr($arr[$i]['data'], $keywords_no_newline, $keywords_no_newline_cnt))) {
2110 // do not put a space before the first token, because
2111 // we use a lot of eregi() checking for the first
2112 // reserved word at beginning of query
2113 // so do not put a newline before
2115 // also we must not be inside a privilege list
2116 if ($i > 0) {
2117 // the alpha_identifier exception is there to
2118 // catch cases like
2119 // GRANT SELECT ON mydb.mytable TO myuser@localhost
2120 // (else, we get mydb.mytableTO )
2122 // the quote_single exception is there to
2123 // catch cases like
2124 // GRANT ... TO 'marc'@'domain.com' IDENTIFIED...
2126 // TODO: fix all cases and find why this happens
2128 if (!$in_priv_list || $typearr[1] == 'alpha_identifier' || $typearr[1] == 'quote_single' || $typearr[1] == 'white_newline') {
2129 $before .= $space_alpha_reserved_word;
2131 } else {
2132 // on first keyword, check if it introduces a
2133 // privilege list
2134 if (PMA_STR_binarySearchInArr($arr[$i]['data'], $keywords_priv_list, $keywords_priv_list_cnt)) {
2135 $in_priv_list = TRUE;
2138 } else {
2139 $before .= ' ';
2142 switch ($arr[$i]['data']) {
2143 case 'CREATE':
2144 if (!$in_priv_list) {
2145 $space_punct_listsep = $html_line_break;
2146 $space_alpha_reserved_word = ' ';
2148 break;
2149 case 'EXPLAIN':
2150 case 'DESCRIBE':
2151 case 'SET':
2152 case 'ALTER':
2153 case 'DELETE':
2154 case 'SHOW':
2155 case 'DROP':
2156 case 'UPDATE':
2157 case 'TRUNCATE':
2158 case 'ANALYZE':
2159 case 'ANALYSE':
2160 if (!$in_priv_list) {
2161 $space_punct_listsep = $html_line_break;
2162 $space_alpha_reserved_word = ' ';
2164 break;
2165 case 'INSERT':
2166 case 'REPLACE':
2167 if (!$in_priv_list) {
2168 $space_punct_listsep = $html_line_break;
2169 $space_alpha_reserved_word = $html_line_break;
2171 break;
2172 case 'VALUES':
2173 $space_punct_listsep = ' ';
2174 $space_alpha_reserved_word = $html_line_break;
2175 break;
2176 case 'SELECT':
2177 $space_punct_listsep = ' ';
2178 $space_alpha_reserved_word = $html_line_break;
2179 break;
2180 default:
2181 break;
2182 } // end switch ($arr[$i]['data'])
2184 $after .= ' ';
2185 break;
2186 case 'digit_integer':
2187 case 'digit_float':
2188 case 'digit_hex':
2189 //TODO: could there be other types preceding a digit?
2190 if ($typearr[1] == 'alpha_reservedWord') {
2191 $after .= ' ';
2193 if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
2194 $after .= ' ';
2196 if ($typearr[1] == 'alpha_columnAttrib') {
2197 $before .= ' ';
2199 break;
2200 case 'alpha_variable':
2201 // other workaround for a problem similar to the one
2202 // explained below for quote_single
2203 if (!$in_priv_list) {
2204 $after = ' ';
2206 break;
2207 case 'quote_double':
2208 case 'quote_single':
2209 // workaround: for the query
2210 // REVOKE SELECT ON `base2\_db`.* FROM 'user'@'%'
2211 // the @ is incorrectly marked as alpha_variable
2212 // in the parser, and here, the '%' gets a blank before,
2213 // which is a syntax error
2214 if ($typearr[1] !='alpha_variable') {
2215 $before .= ' ';
2217 if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
2218 $after .= ' ';
2220 break;
2221 case 'quote_backtick':
2222 if ($typearr[3] != 'punct_qualifier') {
2223 $after .= ' ';
2225 if ($typearr[1] != 'punct_qualifier') {
2226 $before .= ' ';
2228 break;
2229 default:
2230 break;
2231 } // end switch ($typearr[2])
2234 if ($typearr[3] != 'punct_qualifier') {
2235 $after .= ' ';
2237 $after .= "\n";
2239 $str .= $before . ($mode=='color' ? PMA_SQP_formatHTML_colorize($arr[$i]) : $arr[$i]['data']). $after;
2240 } // end for
2241 if ($mode=='color') {
2242 $str .= '</span>';
2245 return $str;
2246 } // end of the "PMA_SQP_formatHtml()" function
2250 * Builds a CSS rule used for html formatted SQL queries
2252 * @param string The class name
2253 * @param string The property name
2254 * @param string The property value
2256 * @return string The CSS rule
2258 * @access public
2260 * @see PMA_SQP_buildCssData()
2262 function PMA_SQP_buildCssRule($classname, $property, $value)
2264 $str = '.' . $classname . ' {';
2265 if ($value != '') {
2266 $str .= $property . ': ' . $value . ';';
2268 $str .= '}' . "\n";
2270 return $str;
2271 } // end of the "PMA_SQP_buildCssRule()" function
2275 * Builds CSS rules used for html formatted SQL queries
2277 * @return string The CSS rules set
2279 * @access public
2281 * @global array The current PMA configuration
2283 * @see PMA_SQP_buildCssRule()
2285 function PMA_SQP_buildCssData()
2287 global $cfg;
2289 $css_string = '';
2290 foreach ($cfg['SQP']['fmtColor'] AS $key => $col) {
2291 $css_string .= PMA_SQP_buildCssRule('syntax_' . $key, 'color', $col);
2294 for ($i = 0; $i < 8; $i++) {
2295 $css_string .= PMA_SQP_buildCssRule('syntax_indent' . $i, 'margin-left', ($i * $cfg['SQP']['fmtInd']) . $cfg['SQP']['fmtIndUnit']);
2298 return $css_string;
2299 } // end of the "PMA_SQP_buildCssData()" function
2301 if ( ! defined( 'PMA_MINIMUM_COMMON' ) ) {
2303 * Gets SQL queries with no format
2305 * @param array The SQL queries list
2307 * @return string The SQL queries with no format
2309 * @access public
2311 function PMA_SQP_formatNone($arr)
2313 $formatted_sql = htmlspecialchars($arr['raw']);
2314 $formatted_sql = preg_replace("@((\015\012)|(\015)|(\012)){3,}@", "\n\n", $formatted_sql);
2316 return $formatted_sql;
2317 } // end of the "PMA_SQP_formatNone()" function
2321 * Gets SQL queries in text format
2323 * @param array The SQL queries list
2325 * @return string The SQL queries in text format
2327 * @access public
2329 function PMA_SQP_formatText($arr)
2332 * TODO WRITE THIS!
2334 return PMA_SQP_formatNone($arr);
2335 } // end of the "PMA_SQP_formatText()" function
2336 } // end if: minimal common.lib needed?