PageRenderTime 58ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/phpMyAdmin/libraries/sqlparser.lib.php

https://bitbucket.org/izubizarreta/https-bitbucket.org-bityvip
PHP | 2759 lines | 1806 code | 241 blank | 712 comment | 805 complexity | 3d5c0861ba74d55b8b762cd32fd376c0 MD5 | raw file
Possible License(s): LGPL-3.0, LGPL-2.0, JSON, GPL-2.0, BSD-3-Clause, LGPL-2.1, MIT
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /** SQL Parser Functions for phpMyAdmin
  4. *
  5. * These functions define an SQL parser system, capable of understanding and
  6. * extracting data from a MySQL type SQL query.
  7. *
  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:
  11. *
  12. * ($sql contains the query)
  13. * $parsed_sql = PMA_SQP_parse($sql);
  14. *
  15. * If you want to extract data from it then, you just need to run
  16. * $sql_info = PMA_SQP_analyze($parsed_sql);
  17. *
  18. * See comments in PMA_SQP_analyze for the returned info
  19. * from the analyzer.
  20. *
  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.)
  26. *
  27. * @package PhpMyAdmin
  28. */
  29. if (! defined('PHPMYADMIN')) {
  30. exit;
  31. }
  32. /**
  33. * Minimum inclusion? (i.e. for the stylesheet builder)
  34. */
  35. if (! defined('PMA_MINIMUM_COMMON')) {
  36. /**
  37. * Include the string library as we use it heavily
  38. */
  39. include_once './libraries/string.lib.php';
  40. /**
  41. * Include data for the SQL Parser
  42. */
  43. include_once './libraries/sqlparser.data.php';
  44. if (!defined('TESTSUITE')) {
  45. include_once './libraries/mysql_charsets.lib.php';
  46. }
  47. if (! isset($mysql_charsets)) {
  48. $mysql_charsets = array();
  49. $mysql_collations_flat = array();
  50. }
  51. if (!defined('DEBUG_TIMING')) {
  52. /**
  53. * currently we don't need the $pos (token position in query)
  54. * for other purposes than LIMIT clause verification,
  55. * so many calls to this function do not include the 4th parameter
  56. */
  57. function PMA_SQP_arrayAdd(&$arr, $type, $data, &$arrsize, $pos = 0)
  58. {
  59. $arr[] = array('type' => $type, 'data' => $data, 'pos' => $pos);
  60. $arrsize++;
  61. } // end of the "PMA_SQP_arrayAdd()" function
  62. } else {
  63. /**
  64. * This is debug variant of above.
  65. * @ignore
  66. */
  67. function PMA_SQP_arrayAdd(&$arr, $type, $data, &$arrsize, $pos = 0)
  68. {
  69. global $timer;
  70. $t = $timer;
  71. $arr[] = array(
  72. 'type' => $type,
  73. 'data' => $data,
  74. 'pos' => $pos,
  75. 'time' => $t);
  76. $timer = microtime();
  77. $arrsize++;
  78. } // end of the "PMA_SQP_arrayAdd()" function
  79. } // end if... else...
  80. /**
  81. * Reset the error variable for the SQL parser
  82. *
  83. * @access public
  84. */
  85. function PMA_SQP_resetError()
  86. {
  87. global $SQP_errorString;
  88. $SQP_errorString = '';
  89. unset($SQP_errorString);
  90. }
  91. /**
  92. * Get the contents of the error variable for the SQL parser
  93. *
  94. * @return string Error string from SQL parser
  95. *
  96. * @access public
  97. */
  98. function PMA_SQP_getErrorString()
  99. {
  100. global $SQP_errorString;
  101. return isset($SQP_errorString) ? $SQP_errorString : '';
  102. }
  103. /**
  104. * Check if the SQL parser hit an error
  105. *
  106. * @return boolean error state
  107. *
  108. * @access public
  109. */
  110. function PMA_SQP_isError()
  111. {
  112. global $SQP_errorString;
  113. return isset($SQP_errorString) && !empty($SQP_errorString);
  114. }
  115. /**
  116. * Set an error message for the system
  117. *
  118. * @param string The error message
  119. * @param string The failing SQL query
  120. *
  121. * @access private
  122. * @scope SQL Parser internal
  123. */
  124. function PMA_SQP_throwError($message, $sql)
  125. {
  126. global $SQP_errorString;
  127. $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"
  128. . '<pre>' . "\n"
  129. . 'ERROR: ' . $message . "\n"
  130. . 'SQL: ' . htmlspecialchars($sql) . "\n"
  131. . '</pre>' . "\n";
  132. } // end of the "PMA_SQP_throwError()" function
  133. /**
  134. * Do display the bug report
  135. *
  136. * @param string The error message
  137. * @param string The failing SQL query
  138. *
  139. * @access public
  140. */
  141. function PMA_SQP_bug($message, $sql)
  142. {
  143. global $SQP_errorString;
  144. $debugstr = 'ERROR: ' . $message . "\n";
  145. $debugstr .= 'MySQL: '.PMA_MYSQL_STR_VERSION . "\n";
  146. $debugstr .= 'USR OS, AGENT, VER: ' . PMA_USR_OS . ' ';
  147. $debugstr .= PMA_USR_BROWSER_AGENT . ' ' . PMA_USR_BROWSER_VER . "\n";
  148. $debugstr .= 'PMA: ' . PMA_VERSION . "\n";
  149. $debugstr .= 'PHP VER,OS: ' . PMA_PHP_STR_VERSION . ' ' . PHP_OS . "\n";
  150. $debugstr .= 'LANG: ' . $GLOBALS['lang'] . "\n";
  151. $debugstr .= 'SQL: ' . htmlspecialchars($sql);
  152. $encodedstr = $debugstr;
  153. if (@function_exists('gzcompress')) {
  154. $encodedstr = gzcompress($debugstr, 9);
  155. }
  156. $encodedstr = preg_replace(
  157. "/(\015\012)|(\015)|(\012)/",
  158. '<br />' . "\n",
  159. chunk_split(base64_encode($encodedstr)));
  160. $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:')
  161. . '<br />' . "\n"
  162. . '----' . __('BEGIN CUT') . '----' . '<br />' . "\n"
  163. . $encodedstr . "\n"
  164. . '----' . __('END CUT') . '----' . '<br />' . "\n";
  165. $SQP_errorString .= '----' . __('BEGIN RAW') . '----<br />' . "\n"
  166. . '<pre>' . "\n"
  167. . $debugstr
  168. . '</pre>' . "\n"
  169. . '----' . __('END RAW') . '----<br />' . "\n";
  170. } // end of the "PMA_SQP_bug()" function
  171. /**
  172. * Parses the SQL queries
  173. *
  174. * @param string The SQL query list
  175. *
  176. * @return mixed Most of times, nothing...
  177. *
  178. * @global array The current PMA configuration
  179. * @global array MySQL column attributes
  180. * @global array MySQL reserved words
  181. * @global array MySQL column types
  182. * @global array MySQL function names
  183. * @global array List of available character sets
  184. * @global array List of available collations
  185. *
  186. * @access public
  187. */
  188. function PMA_SQP_parse($sql)
  189. {
  190. static $PMA_SQPdata_column_attrib, $PMA_SQPdata_reserved_word;
  191. static $PMA_SQPdata_column_type;
  192. static $PMA_SQPdata_function_name, $PMA_SQPdata_forbidden_word;
  193. global $mysql_charsets, $mysql_collations_flat;
  194. // Convert all line feeds to Unix style
  195. $sql = str_replace("\r\n", "\n", $sql);
  196. $sql = str_replace("\r", "\n", $sql);
  197. $len = PMA_strlen($sql);
  198. if ($len == 0) {
  199. return array();
  200. }
  201. // Create local hashtables
  202. if (!isset($PMA_SQPdata_column_attrib)) {
  203. $PMA_SQPdata_column_attrib = array_flip(
  204. $GLOBALS['PMA_SQPdata_column_attrib']
  205. );
  206. $PMA_SQPdata_function_name = array_flip(
  207. $GLOBALS['PMA_SQPdata_function_name']
  208. );
  209. $PMA_SQPdata_reserved_word = array_flip(
  210. $GLOBALS['PMA_SQPdata_reserved_word']
  211. );
  212. $PMA_SQPdata_forbidden_word = array_flip(
  213. $GLOBALS['PMA_SQPdata_forbidden_word']
  214. );
  215. $PMA_SQPdata_column_type = array_flip(
  216. $GLOBALS['PMA_SQPdata_column_type']
  217. );
  218. }
  219. $sql_array = array();
  220. $sql_array['raw'] = $sql;
  221. $count1 = 0;
  222. $count2 = 0;
  223. $punct_queryend = ';';
  224. $punct_qualifier = '.';
  225. $punct_listsep = ',';
  226. $punct_level_plus = '(';
  227. $punct_level_minus = ')';
  228. $punct_user = '@';
  229. $digit_floatdecimal = '.';
  230. $digit_hexset = 'x';
  231. $bracket_list = '()[]{}';
  232. $allpunct_list = '-,;:!?/.^~\*&%+<=>|';
  233. $allpunct_list_pair = array(
  234. '!=' => 1,
  235. '&&' => 1,
  236. ':=' => 1,
  237. '<<' => 1,
  238. '<=' => 1,
  239. '<=>' => 1,
  240. '<>' => 1,
  241. '>=' => 1,
  242. '>>' => 1,
  243. '||' => 1,
  244. '==' => 1
  245. );
  246. $quote_list = '\'"`';
  247. $arraysize = 0;
  248. $previous_was_space = false;
  249. $this_was_space = false;
  250. $previous_was_bracket = false;
  251. $this_was_bracket = false;
  252. $previous_was_punct = false;
  253. $this_was_punct = false;
  254. $previous_was_listsep = false;
  255. $this_was_listsep = false;
  256. $previous_was_quote = false;
  257. $this_was_quote = false;
  258. while ($count2 < $len) {
  259. $c = PMA_substr($sql, $count2, 1);
  260. $count1 = $count2;
  261. $previous_was_space = $this_was_space;
  262. $this_was_space = false;
  263. $previous_was_bracket = $this_was_bracket;
  264. $this_was_bracket = false;
  265. $previous_was_punct = $this_was_punct;
  266. $this_was_punct = false;
  267. $previous_was_listsep = $this_was_listsep;
  268. $this_was_listsep = false;
  269. $previous_was_quote = $this_was_quote;
  270. $this_was_quote = false;
  271. if (($c == "\n")) {
  272. $this_was_space = true;
  273. $count2++;
  274. PMA_SQP_arrayAdd($sql_array, 'white_newline', '', $arraysize);
  275. continue;
  276. }
  277. // Checks for white space
  278. if (PMA_STR_isSpace($c)) {
  279. $this_was_space = true;
  280. $count2++;
  281. continue;
  282. }
  283. // Checks for comment lines.
  284. // MySQL style #
  285. // C style /* */
  286. // ANSI style --
  287. $next_c = PMA_substr($sql, $count2 + 1, 1);
  288. if (($c == '#')
  289. || (($count2 + 1 < $len) && ($c == '/') && ($next_c == '*'))
  290. || (($count2 + 2 == $len) && ($c == '-') && ($next_c == '-'))
  291. || (($count2 + 2 < $len) && ($c == '-') && ($next_c == '-') && ((PMA_substr($sql, $count2 + 2, 1) <= ' ')))) {
  292. $count2++;
  293. $pos = 0;
  294. $type = 'bad';
  295. switch ($c) {
  296. case '#':
  297. $type = 'mysql';
  298. case '-':
  299. $type = 'ansi';
  300. $pos = PMA_strpos($sql, "\n", $count2);
  301. break;
  302. case '/':
  303. $type = 'c';
  304. $pos = PMA_strpos($sql, '*/', $count2);
  305. $pos += 2;
  306. break;
  307. default:
  308. break;
  309. } // end switch
  310. $count2 = ($pos < $count2) ? $len : $pos;
  311. $str = PMA_substr($sql, $count1, $count2 - $count1);
  312. PMA_SQP_arrayAdd($sql_array, 'comment_' . $type, $str, $arraysize);
  313. continue;
  314. } // end if
  315. // Checks for something inside quotation marks
  316. if (PMA_strpos($quote_list, $c) !== false) {
  317. $startquotepos = $count2;
  318. $quotetype = $c;
  319. $count2++;
  320. $escaped = false;
  321. $pos = $count2;
  322. $oldpos = 0;
  323. do {
  324. $oldpos = $pos;
  325. $pos = PMA_strpos(' ' . $sql, $quotetype, $oldpos + 1) - 1;
  326. // ($pos === false)
  327. if ($pos < 0) {
  328. if ($c == '`') {
  329. /*
  330. * Behave same as MySQL and accept end of query as end of backtick.
  331. * I know this is sick, but MySQL behaves like this:
  332. *
  333. * SELECT * FROM `table
  334. *
  335. * is treated like
  336. *
  337. * SELECT * FROM `table`
  338. */
  339. $pos_quote_separator = PMA_strpos(' ' . $sql, $GLOBALS['sql_delimiter'], $oldpos + 1) - 1;
  340. if ($pos_quote_separator < 0) {
  341. $len += 1;
  342. $sql .= '`';
  343. $sql_array['raw'] .= '`';
  344. $pos = $len;
  345. } else {
  346. $len += 1;
  347. $sql = PMA_substr($sql, 0, $pos_quote_separator) . '`' . PMA_substr($sql, $pos_quote_separator);
  348. $sql_array['raw'] = $sql;
  349. $pos = $pos_quote_separator;
  350. }
  351. if (class_exists('PMA_Message') && $GLOBALS['is_ajax_request'] != true) {
  352. PMA_Message::notice(__('Automatically appended backtick to the end of query!'))->display();
  353. }
  354. } else {
  355. $debugstr = __('Unclosed quote') . ' @ ' . $startquotepos. "\n"
  356. . 'STR: ' . htmlspecialchars($quotetype);
  357. PMA_SQP_throwError($debugstr, $sql);
  358. return $sql_array;
  359. }
  360. }
  361. // If the quote is the first character, it can't be
  362. // escaped, so don't do the rest of the code
  363. if ($pos == 0) {
  364. break;
  365. }
  366. // Checks for MySQL escaping using a \
  367. // And checks for ANSI escaping using the $quotetype character
  368. if (($pos < $len) && PMA_STR_charIsEscaped($sql, $pos) && $c != '`') {
  369. $pos ++;
  370. continue;
  371. } elseif (($pos + 1 < $len) && (PMA_substr($sql, $pos, 1) == $quotetype) && (PMA_substr($sql, $pos + 1, 1) == $quotetype)) {
  372. $pos = $pos + 2;
  373. continue;
  374. } else {
  375. break;
  376. }
  377. } while ($len > $pos); // end do
  378. $count2 = $pos;
  379. $count2++;
  380. $type = 'quote_';
  381. switch ($quotetype) {
  382. case '\'':
  383. $type .= 'single';
  384. $this_was_quote = true;
  385. break;
  386. case '"':
  387. $type .= 'double';
  388. $this_was_quote = true;
  389. break;
  390. case '`':
  391. $type .= 'backtick';
  392. $this_was_quote = true;
  393. break;
  394. default:
  395. break;
  396. } // end switch
  397. $data = PMA_substr($sql, $count1, $count2 - $count1);
  398. PMA_SQP_arrayAdd($sql_array, $type, $data, $arraysize);
  399. continue;
  400. }
  401. // Checks for brackets
  402. if (PMA_strpos($bracket_list, $c) !== false) {
  403. // All bracket tokens are only one item long
  404. $this_was_bracket = true;
  405. $count2++;
  406. $type_type = '';
  407. if (PMA_strpos('([{', $c) !== false) {
  408. $type_type = 'open';
  409. } else {
  410. $type_type = 'close';
  411. }
  412. $type_style = '';
  413. if (PMA_strpos('()', $c) !== false) {
  414. $type_style = 'round';
  415. } elseif (PMA_strpos('[]', $c) !== false) {
  416. $type_style = 'square';
  417. } else {
  418. $type_style = 'curly';
  419. }
  420. $type = 'punct_bracket_' . $type_type . '_' . $type_style;
  421. PMA_SQP_arrayAdd($sql_array, $type, $c, $arraysize);
  422. continue;
  423. }
  424. /* DEBUG
  425. echo '<pre>1';
  426. var_dump(PMA_STR_isSqlIdentifier($c, false));
  427. var_dump($c == '@');
  428. var_dump($c == '.');
  429. var_dump(PMA_STR_isDigit(PMA_substr($sql, $count2 + 1, 1)));
  430. var_dump($previous_was_space);
  431. var_dump($previous_was_bracket);
  432. var_dump($previous_was_listsep);
  433. echo '</pre>';
  434. */
  435. // Checks for identifier (alpha or numeric)
  436. if (PMA_STR_isSqlIdentifier($c, false)
  437. || $c == '@'
  438. || ($c == '.'
  439. && PMA_STR_isDigit(PMA_substr($sql, $count2 + 1, 1))
  440. && ($previous_was_space || $previous_was_bracket || $previous_was_listsep))) {
  441. /* DEBUG
  442. echo PMA_substr($sql, $count2);
  443. echo '<hr />';
  444. */
  445. $count2++;
  446. /**
  447. * @todo a @ can also be present in expressions like
  448. * FROM 'user'@'%' or TO 'user'@'%'
  449. * in this case, the @ is wrongly marked as alpha_variable
  450. */
  451. $is_identifier = $previous_was_punct;
  452. $is_sql_variable = $c == '@' && ! $previous_was_quote;
  453. $is_user = $c == '@' && $previous_was_quote;
  454. $is_digit = !$is_identifier && !$is_sql_variable && PMA_STR_isDigit($c);
  455. $is_hex_digit = $is_digit && $c == '0' && $count2 < $len && PMA_substr($sql, $count2, 1) == 'x';
  456. $is_float_digit = $c == '.';
  457. $is_float_digit_exponent = false;
  458. /* DEBUG
  459. echo '<pre>2';
  460. var_dump($is_identifier);
  461. var_dump($is_sql_variable);
  462. var_dump($is_digit);
  463. var_dump($is_float_digit);
  464. echo '</pre>';
  465. */
  466. // Fast skip is especially needed for huge BLOB data
  467. if ($is_hex_digit) {
  468. $count2++;
  469. $pos = strspn($sql, '0123456789abcdefABCDEF', $count2);
  470. if ($pos > $count2) {
  471. $count2 = $pos;
  472. }
  473. unset($pos);
  474. } elseif ($is_digit) {
  475. $pos = strspn($sql, '0123456789', $count2);
  476. if ($pos > $count2) {
  477. $count2 = $pos;
  478. }
  479. unset($pos);
  480. }
  481. while (($count2 < $len) && PMA_STR_isSqlIdentifier(PMA_substr($sql, $count2, 1), ($is_sql_variable || $is_digit))) {
  482. $c2 = PMA_substr($sql, $count2, 1);
  483. if ($is_sql_variable && ($c2 == '.')) {
  484. $count2++;
  485. continue;
  486. }
  487. if ($is_digit && (!$is_hex_digit) && ($c2 == '.')) {
  488. $count2++;
  489. if (!$is_float_digit) {
  490. $is_float_digit = true;
  491. continue;
  492. } else {
  493. $debugstr = __('Invalid Identifer') . ' @ ' . ($count1+1) . "\n"
  494. . 'STR: ' . htmlspecialchars(PMA_substr($sql, $count1, $count2 - $count1));
  495. PMA_SQP_throwError($debugstr, $sql);
  496. return $sql_array;
  497. }
  498. }
  499. if ($is_digit && (!$is_hex_digit) && (($c2 == 'e') || ($c2 == 'E'))) {
  500. if (!$is_float_digit_exponent) {
  501. $is_float_digit_exponent = true;
  502. $is_float_digit = true;
  503. $count2++;
  504. continue;
  505. } else {
  506. $is_digit = false;
  507. $is_float_digit = false;
  508. }
  509. }
  510. if (($is_hex_digit && PMA_STR_isHexDigit($c2)) || ($is_digit && PMA_STR_isDigit($c2))) {
  511. $count2++;
  512. continue;
  513. } else {
  514. $is_digit = false;
  515. $is_hex_digit = false;
  516. }
  517. $count2++;
  518. } // end while
  519. $l = $count2 - $count1;
  520. $str = PMA_substr($sql, $count1, $l);
  521. $type = '';
  522. if ($is_digit || $is_float_digit || $is_hex_digit) {
  523. $type = 'digit';
  524. if ($is_float_digit) {
  525. $type .= '_float';
  526. } elseif ($is_hex_digit) {
  527. $type .= '_hex';
  528. } else {
  529. $type .= '_integer';
  530. }
  531. } elseif ($is_user) {
  532. $type = 'punct_user';
  533. } elseif ($is_sql_variable != false) {
  534. $type = 'alpha_variable';
  535. } else {
  536. $type = 'alpha';
  537. } // end if... else....
  538. PMA_SQP_arrayAdd($sql_array, $type, $str, $arraysize, $count2);
  539. continue;
  540. }
  541. // Checks for punct
  542. if (PMA_strpos($allpunct_list, $c) !== false) {
  543. while (($count2 < $len) && PMA_strpos($allpunct_list, PMA_substr($sql, $count2, 1)) !== false) {
  544. $count2++;
  545. }
  546. $l = $count2 - $count1;
  547. if ($l == 1) {
  548. $punct_data = $c;
  549. } else {
  550. $punct_data = PMA_substr($sql, $count1, $l);
  551. }
  552. // Special case, sometimes, althought two characters are
  553. // adjectent directly, they ACTUALLY need to be seperate
  554. /* DEBUG
  555. echo '<pre>';
  556. var_dump($l);
  557. var_dump($punct_data);
  558. echo '</pre>';
  559. */
  560. if ($l == 1) {
  561. $t_suffix = '';
  562. switch ($punct_data) {
  563. case $punct_queryend:
  564. $t_suffix = '_queryend';
  565. break;
  566. case $punct_qualifier:
  567. $t_suffix = '_qualifier';
  568. $this_was_punct = true;
  569. break;
  570. case $punct_listsep:
  571. $this_was_listsep = true;
  572. $t_suffix = '_listsep';
  573. break;
  574. default:
  575. break;
  576. }
  577. PMA_SQP_arrayAdd($sql_array, 'punct' . $t_suffix, $punct_data, $arraysize);
  578. } elseif ($punct_data == $GLOBALS['sql_delimiter'] || isset($allpunct_list_pair[$punct_data])) {
  579. // Ok, we have one of the valid combined punct expressions
  580. PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize);
  581. } else {
  582. // Bad luck, lets split it up more
  583. $first = $punct_data[0];
  584. $first2 = $punct_data[0] . $punct_data[1];
  585. $last2 = $punct_data[$l - 2] . $punct_data[$l - 1];
  586. $last = $punct_data[$l - 1];
  587. if (($first == ',') || ($first == ';') || ($first == '.') || ($first == '*')) {
  588. $count2 = $count1 + 1;
  589. $punct_data = $first;
  590. } elseif (($last2 == '/*') || (($last2 == '--') && ($count2 == $len || PMA_substr($sql, $count2, 1) <= ' '))) {
  591. $count2 -= 2;
  592. $punct_data = PMA_substr($sql, $count1, $count2 - $count1);
  593. } elseif (($last == '-') || ($last == '+') || ($last == '!')) {
  594. $count2--;
  595. $punct_data = PMA_substr($sql, $count1, $count2 - $count1);
  596. } elseif ($last != '~') {
  597. /**
  598. * @todo for negation operator, split in 2 tokens ?
  599. * "select x&~1 from t"
  600. * becomes "select x & ~ 1 from t" ?
  601. */
  602. $debugstr = __('Unknown Punctuation String') . ' @ ' . ($count1+1) . "\n"
  603. . 'STR: ' . htmlspecialchars($punct_data);
  604. PMA_SQP_throwError($debugstr, $sql);
  605. return $sql_array;
  606. }
  607. PMA_SQP_arrayAdd($sql_array, 'punct', $punct_data, $arraysize);
  608. continue;
  609. } // end if... elseif... else
  610. continue;
  611. }
  612. // DEBUG
  613. $count2++;
  614. $debugstr = 'C1 C2 LEN: ' . $count1 . ' ' . $count2 . ' ' . $len . "\n"
  615. . 'STR: ' . PMA_substr($sql, $count1, $count2 - $count1) . "\n";
  616. PMA_SQP_bug($debugstr, $sql);
  617. return $sql_array;
  618. } // end while ($count2 < $len)
  619. /*
  620. echo '<pre>';
  621. print_r($sql_array);
  622. echo '</pre>';
  623. */
  624. if ($arraysize > 0) {
  625. $t_next = $sql_array[0]['type'];
  626. $t_prev = '';
  627. $t_bef_prev = '';
  628. $t_cur = '';
  629. $d_next = $sql_array[0]['data'];
  630. $d_prev = '';
  631. $d_bef_prev = '';
  632. $d_cur = '';
  633. $d_next_upper = $t_next == 'alpha' ? strtoupper($d_next) : $d_next;
  634. $d_prev_upper = '';
  635. $d_bef_prev_upper = '';
  636. $d_cur_upper = '';
  637. }
  638. for ($i = 0; $i < $arraysize; $i++) {
  639. $t_bef_prev = $t_prev;
  640. $t_prev = $t_cur;
  641. $t_cur = $t_next;
  642. $d_bef_prev = $d_prev;
  643. $d_prev = $d_cur;
  644. $d_cur = $d_next;
  645. $d_bef_prev_upper = $d_prev_upper;
  646. $d_prev_upper = $d_cur_upper;
  647. $d_cur_upper = $d_next_upper;
  648. if (($i + 1) < $arraysize) {
  649. $t_next = $sql_array[$i + 1]['type'];
  650. $d_next = $sql_array[$i + 1]['data'];
  651. $d_next_upper = $t_next == 'alpha' ? strtoupper($d_next) : $d_next;
  652. } else {
  653. $t_next = '';
  654. $d_next = '';
  655. $d_next_upper = '';
  656. }
  657. //DEBUG echo "[prev: <strong>".$d_prev."</strong> ".$t_prev."][cur: <strong>".$d_cur."</strong> ".$t_cur."][next: <strong>".$d_next."</strong> ".$t_next."]<br />";
  658. if ($t_cur == 'alpha') {
  659. $t_suffix = '_identifier';
  660. // for example: `thebit` bit(8) NOT NULL DEFAULT b'0'
  661. if ($t_prev == 'alpha' && $d_prev == 'DEFAULT' && $d_cur == 'b' && $t_next == 'quote_single') {
  662. $t_suffix = '_bitfield_constant_introducer';
  663. } elseif (($t_next == 'punct_qualifier') || ($t_prev == 'punct_qualifier')) {
  664. $t_suffix = '_identifier';
  665. } elseif (($t_next == 'punct_bracket_open_round')
  666. && isset($PMA_SQPdata_function_name[$d_cur_upper])) {
  667. /**
  668. * @todo 2005-10-16: in the case of a CREATE TABLE containing
  669. * a TIMESTAMP, since TIMESTAMP() is also a function, it's
  670. * found here and the token is wrongly marked as alpha_functionName.
  671. * But we compensate for this when analysing for timestamp_not_null
  672. * later in this script.
  673. *
  674. * Same applies to CHAR vs. CHAR() function.
  675. */
  676. $t_suffix = '_functionName';
  677. /* There are functions which might be as well column types */
  678. } elseif (isset($PMA_SQPdata_column_type[$d_cur_upper])) {
  679. $t_suffix = '_columnType';
  680. /**
  681. * Temporary fix for BUG #621357 and #2027720
  682. *
  683. * @todo FIX PROPERLY NEEDS OVERHAUL OF SQL TOKENIZER
  684. */
  685. if (($d_cur_upper == 'SET' || $d_cur_upper == 'BINARY') && $t_next != 'punct_bracket_open_round') {
  686. $t_suffix = '_reservedWord';
  687. }
  688. //END OF TEMPORARY FIX
  689. // CHARACTER is a synonym for CHAR, but can also be meant as
  690. // CHARACTER SET. In this case, we have a reserved word.
  691. if ($d_cur_upper == 'CHARACTER' && $d_next_upper == 'SET') {
  692. $t_suffix = '_reservedWord';
  693. }
  694. // experimental
  695. // current is a column type, so previous must not be
  696. // a reserved word but an identifier
  697. // CREATE TABLE SG_Persons (first varchar(64))
  698. //if ($sql_array[$i-1]['type'] =='alpha_reservedWord') {
  699. // $sql_array[$i-1]['type'] = 'alpha_identifier';
  700. //}
  701. } elseif (isset($PMA_SQPdata_reserved_word[$d_cur_upper])) {
  702. $t_suffix = '_reservedWord';
  703. } elseif (isset($PMA_SQPdata_column_attrib[$d_cur_upper])) {
  704. $t_suffix = '_columnAttrib';
  705. // INNODB is a MySQL table type, but in "SHOW INNODB STATUS",
  706. // it should be regarded as a reserved word.
  707. if ($d_cur_upper == 'INNODB' && $d_prev_upper == 'SHOW' && $d_next_upper == 'STATUS') {
  708. $t_suffix = '_reservedWord';
  709. }
  710. if ($d_cur_upper == 'DEFAULT' && $d_next_upper == 'CHARACTER') {
  711. $t_suffix = '_reservedWord';
  712. }
  713. // Binary as character set
  714. if ($d_cur_upper == 'BINARY' && (
  715. ($d_bef_prev_upper == 'CHARACTER' && $d_prev_upper == 'SET')
  716. || ($d_bef_prev_upper == 'SET' && $d_prev_upper == '=')
  717. || ($d_bef_prev_upper == 'CHARSET' && $d_prev_upper == '=')
  718. || $d_prev_upper == 'CHARSET'
  719. ) && in_array($d_cur, $mysql_charsets)) {
  720. $t_suffix = '_charset';
  721. }
  722. } elseif (in_array($d_cur, $mysql_charsets)
  723. || in_array($d_cur, $mysql_collations_flat)
  724. || ($d_cur{0} == '_' && in_array(substr($d_cur, 1), $mysql_charsets))) {
  725. $t_suffix = '_charset';
  726. } else {
  727. // Do nothing
  728. }
  729. // check if present in the list of forbidden words
  730. if ($t_suffix == '_reservedWord' && isset($PMA_SQPdata_forbidden_word[$d_cur_upper])) {
  731. $sql_array[$i]['forbidden'] = true;
  732. } else {
  733. $sql_array[$i]['forbidden'] = false;
  734. }
  735. $sql_array[$i]['type'] .= $t_suffix;
  736. }
  737. } // end for
  738. // Stores the size of the array inside the array, as count() is a slow
  739. // operation.
  740. $sql_array['len'] = $arraysize;
  741. // DEBUG echo 'After parsing<pre>'; print_r($sql_array); echo '</pre>';
  742. // Sends the data back
  743. return $sql_array;
  744. } // end of the "PMA_SQP_parse()" function
  745. /**
  746. * Checks for token types being what we want...
  747. *
  748. * @param string String of type that we have
  749. * @param string String of type that we want
  750. *
  751. * @return boolean result of check
  752. *
  753. * @access private
  754. */
  755. function PMA_SQP_typeCheck($toCheck, $whatWeWant)
  756. {
  757. $typeSeperator = '_';
  758. if (strcmp($whatWeWant, $toCheck) == 0) {
  759. return true;
  760. } else {
  761. if (strpos($whatWeWant, $typeSeperator) === false) {
  762. return strncmp($whatWeWant, $toCheck, strpos($toCheck, $typeSeperator)) == 0;
  763. } else {
  764. return false;
  765. }
  766. }
  767. }
  768. /**
  769. * Analyzes SQL queries
  770. *
  771. * @param array The SQL queries
  772. *
  773. * @return array The analyzed SQL queries
  774. *
  775. * @access public
  776. */
  777. function PMA_SQP_analyze($arr)
  778. {
  779. if ($arr == array() || ! isset($arr['len'])) {
  780. return array();
  781. }
  782. $result = array();
  783. $size = $arr['len'];
  784. $subresult = array(
  785. 'querytype' => '',
  786. 'select_expr_clause'=> '', // the whole stuff between SELECT and FROM , except DISTINCT
  787. 'position_of_first_select' => '', // the array index
  788. 'from_clause'=> '',
  789. 'group_by_clause'=> '',
  790. 'order_by_clause'=> '',
  791. 'having_clause' => '',
  792. 'limit_clause' => '',
  793. 'where_clause' => '',
  794. 'where_clause_identifiers' => array(),
  795. 'unsorted_query' => '',
  796. 'queryflags' => array(),
  797. 'select_expr' => array(),
  798. 'table_ref' => array(),
  799. 'foreign_keys' => array(),
  800. 'create_table_fields' => array()
  801. );
  802. $subresult_empty = $subresult;
  803. $seek_queryend = false;
  804. $seen_end_of_table_ref = false;
  805. $number_of_brackets_in_extract = 0;
  806. $number_of_brackets_in_group_concat = 0;
  807. $number_of_brackets = 0;
  808. $in_subquery = false;
  809. $seen_subquery = false;
  810. $seen_from = false;
  811. // for SELECT EXTRACT(YEAR_MONTH FROM CURDATE())
  812. // we must not use CURDATE as a table_ref
  813. // so we track whether we are in the EXTRACT()
  814. $in_extract = false;
  815. // for GROUP_CONCAT(...)
  816. $in_group_concat = false;
  817. /* Description of analyzer results
  818. *
  819. * db, table, column, alias
  820. * ------------------------
  821. *
  822. * Inside the $subresult array, we create ['select_expr'] and ['table_ref'] arrays.
  823. *
  824. * The SELECT syntax (simplified) is
  825. *
  826. * SELECT
  827. * select_expression,...
  828. * [FROM [table_references]
  829. *
  830. *
  831. * ['select_expr'] is filled with each expression, the key represents the
  832. * expression position in the list (0-based) (so we don't lose track of
  833. * multiple occurences of the same column).
  834. *
  835. * ['table_ref'] is filled with each table ref, same thing for the key.
  836. *
  837. * I create all sub-values empty, even if they are
  838. * not present (for example no select_expression alias).
  839. *
  840. * There is a debug section at the end of loop #1, if you want to
  841. * see the exact contents of select_expr and table_ref
  842. *
  843. * queryflags
  844. * ----------
  845. *
  846. * In $subresult, array 'queryflags' is filled, according to what we
  847. * find in the query.
  848. *
  849. * Currently, those are generated:
  850. *
  851. * ['queryflags']['need_confirm'] = 1; if the query needs confirmation
  852. * ['queryflags']['select_from'] = 1; if this is a real SELECT...FROM
  853. * ['queryflags']['distinct'] = 1; for a DISTINCT
  854. * ['queryflags']['union'] = 1; for a UNION
  855. * ['queryflags']['join'] = 1; for a JOIN
  856. * ['queryflags']['offset'] = 1; for the presence of OFFSET
  857. * ['queryflags']['procedure'] = 1; for the presence of PROCEDURE
  858. *
  859. * query clauses
  860. * -------------
  861. *
  862. * The select is splitted in those clauses:
  863. * ['select_expr_clause']
  864. * ['from_clause']
  865. * ['group_by_clause']
  866. * ['order_by_clause']
  867. * ['having_clause']
  868. * ['limit_clause']
  869. * ['where_clause']
  870. *
  871. * The identifiers of the WHERE clause are put into the array
  872. * ['where_clause_identifier']
  873. *
  874. * For a SELECT, the whole query without the ORDER BY clause is put into
  875. * ['unsorted_query']
  876. *
  877. * foreign keys
  878. * ------------
  879. * The CREATE TABLE may contain FOREIGN KEY clauses, so they get
  880. * analyzed and ['foreign_keys'] is an array filled with
  881. * the constraint name, the index list,
  882. * the REFERENCES table name and REFERENCES index list,
  883. * and ON UPDATE | ON DELETE clauses
  884. *
  885. * position_of_first_select
  886. * ------------------------
  887. *
  888. * The array index of the first SELECT we find. Will be used to
  889. * insert a SQL_CALC_FOUND_ROWS.
  890. *
  891. * create_table_fields
  892. * -------------------
  893. *
  894. * Used to detect the DEFAULT CURRENT_TIMESTAMP and
  895. * ON UPDATE CURRENT_TIMESTAMP clauses of the CREATE TABLE query.
  896. * Also used to store the default value of the field.
  897. * An array, each element is the identifier name.
  898. * Note that for now, the timestamp_not_null element is created
  899. * even for non-TIMESTAMP fields.
  900. *
  901. * Sub-elements: ['type'] which contains the column type
  902. * optional (currently they are never false but can be absent):
  903. * ['default_current_timestamp'] boolean
  904. * ['on_update_current_timestamp'] boolean
  905. * ['timestamp_not_null'] boolean
  906. *
  907. * section_before_limit, section_after_limit
  908. * -----------------------------------------
  909. *
  910. * Marks the point of the query where we can insert a LIMIT clause;
  911. * so the section_before_limit will contain the left part before
  912. * a possible LIMIT clause
  913. *
  914. *
  915. * End of description of analyzer results
  916. */
  917. // must be sorted
  918. // TODO: current logic checks for only one word, so I put only the
  919. // first word of the reserved expressions that end a table ref;
  920. // maybe this is not ok (the first word might mean something else)
  921. // $words_ending_table_ref = array(
  922. // 'FOR UPDATE',
  923. // 'GROUP BY',
  924. // 'HAVING',
  925. // 'LIMIT',
  926. // 'LOCK IN SHARE MODE',
  927. // 'ORDER BY',
  928. // 'PROCEDURE',
  929. // 'UNION',
  930. // 'WHERE'
  931. // );
  932. $words_ending_table_ref = array(
  933. 'FOR' => 1,
  934. 'GROUP' => 1,
  935. 'HAVING' => 1,
  936. 'LIMIT' => 1,
  937. 'LOCK' => 1,
  938. 'ORDER' => 1,
  939. 'PROCEDURE' => 1,
  940. 'UNION' => 1,
  941. 'WHERE' => 1
  942. );
  943. $words_ending_clauses = array(
  944. 'FOR' => 1,
  945. 'LIMIT' => 1,
  946. 'LOCK' => 1,
  947. 'PROCEDURE' => 1,
  948. 'UNION' => 1
  949. );
  950. $supported_query_types = array(
  951. 'SELECT' => 1,
  952. /*
  953. // Support for these additional query types will come later on.
  954. 'DELETE' => 1,
  955. 'INSERT' => 1,
  956. 'REPLACE' => 1,
  957. 'TRUNCATE' => 1,
  958. 'UPDATE' => 1,
  959. 'EXPLAIN' => 1,
  960. 'DESCRIBE' => 1,
  961. 'SHOW' => 1,
  962. 'CREATE' => 1,
  963. 'SET' => 1,
  964. 'ALTER' => 1
  965. */
  966. );
  967. // loop #1 for each token: select_expr, table_ref for SELECT
  968. for ($i = 0; $i < $size; $i++) {
  969. //DEBUG echo "Loop1 <strong>" . $arr[$i]['data'] . "</strong> (" . $arr[$i]['type'] . ")<br />";
  970. // High speed seek for locating the end of the current query
  971. if ($seek_queryend == true) {
  972. if ($arr[$i]['type'] == 'punct_queryend') {
  973. $seek_queryend = false;
  974. } else {
  975. continue;
  976. } // end if (type == punct_queryend)
  977. } // end if ($seek_queryend)
  978. /**
  979. * Note: do not split if this is a punct_queryend for the first and only query
  980. * @todo when we find a UNION, should we split in another subresult?
  981. */
  982. if ($arr[$i]['type'] == 'punct_queryend' && ($i + 1 != $size)) {
  983. $result[] = $subresult;
  984. $subresult = $subresult_empty;
  985. continue;
  986. } // end if (type == punct_queryend)
  987. // ==============================================================
  988. if ($arr[$i]['type'] == 'punct_bracket_open_round') {
  989. $number_of_brackets++;
  990. if ($in_extract) {
  991. $number_of_brackets_in_extract++;
  992. }
  993. if ($in_group_concat) {
  994. $number_of_brackets_in_group_concat++;
  995. }
  996. }
  997. // ==============================================================
  998. if ($arr[$i]['type'] == 'punct_bracket_close_round') {
  999. $number_of_brackets--;
  1000. if ($number_of_brackets == 0) {
  1001. $in_subquery = false;
  1002. }
  1003. if ($in_extract) {
  1004. $number_of_brackets_in_extract--;
  1005. if ($number_of_brackets_in_extract == 0) {
  1006. $in_extract = false;
  1007. }
  1008. }
  1009. if ($in_group_concat) {
  1010. $number_of_brackets_in_group_concat--;
  1011. if ($number_of_brackets_in_group_concat == 0) {
  1012. $in_group_concat = false;
  1013. }
  1014. }
  1015. }
  1016. if ($in_subquery) {
  1017. /**
  1018. * skip the subquery to avoid setting
  1019. * select_expr or table_ref with the contents
  1020. * of this subquery; this is to avoid a bug when
  1021. * trying to edit the results of
  1022. * select * from child where not exists (select id from
  1023. * parent where child.parent_id = parent.id);
  1024. */
  1025. continue;
  1026. }
  1027. // ==============================================================
  1028. if ($arr[$i]['type'] == 'alpha_functionName') {
  1029. $upper_data = strtoupper($arr[$i]['data']);
  1030. if ($upper_data =='EXTRACT') {
  1031. $in_extract = true;
  1032. $number_of_brackets_in_extract = 0;
  1033. }
  1034. if ($upper_data =='GROUP_CONCAT') {
  1035. $in_group_concat = true;
  1036. $number_of_brackets_in_group_concat = 0;
  1037. }
  1038. }
  1039. // ==============================================================
  1040. if ($arr[$i]['type'] == 'alpha_reservedWord'
  1041. //&& $arr[$i]['forbidden'] == false) {
  1042. ) {
  1043. // We don't know what type of query yet, so run this
  1044. if ($subresult['querytype'] == '') {
  1045. $subresult['querytype'] = strtoupper($arr[$i]['data']);
  1046. } // end if (querytype was empty)
  1047. // Check if we support this type of query
  1048. if (!isset($supported_query_types[$subresult['querytype']])) {
  1049. // Skip ahead to the next one if we don't
  1050. $seek_queryend = true;
  1051. continue;
  1052. } // end if (query not supported)
  1053. // upper once
  1054. $upper_data = strtoupper($arr[$i]['data']);
  1055. /**
  1056. * @todo reset for each query?
  1057. */
  1058. if ($upper_data == 'SELECT') {
  1059. if ($number_of_brackets > 0) {
  1060. $in_subquery = true;
  1061. $seen_subquery = true;
  1062. // this is a subquery so do not analyze inside it
  1063. continue;
  1064. }
  1065. $seen_from = false;
  1066. $previous_was_identifier = false;
  1067. $current_select_expr = -1;
  1068. $seen_end_of_table_ref = false;
  1069. } // end if (data == SELECT)
  1070. if ($upper_data =='FROM' && !$in_extract) {
  1071. $current_table_ref = -1;
  1072. $seen_from = true;
  1073. $previous_was_identifier = false;
  1074. $save_table_ref = true;
  1075. } // end if (data == FROM)
  1076. // here, do not 'continue' the loop, as we have more work for
  1077. // reserved words below
  1078. } // end if (type == alpha_reservedWord)
  1079. // ==============================
  1080. if ($arr[$i]['type'] == 'quote_backtick'
  1081. || $arr[$i]['type'] == 'quote_double'
  1082. || $arr[$i]['type'] == 'quote_single'
  1083. || $arr[$i]['type'] == 'alpha_identifier'
  1084. || ($arr[$i]['type'] == 'alpha_reservedWord'
  1085. && $arr[$i]['forbidden'] == false)) {
  1086. switch ($arr[$i]['type']) {
  1087. case 'alpha_identifier':
  1088. case 'alpha_reservedWord':
  1089. /**
  1090. * this is not a real reservedWord, because it's not
  1091. * present in the list of forbidden words, for example
  1092. * "storage" which can be used as an identifier
  1093. *
  1094. * @todo avoid the pretty printing in color in this case
  1095. */
  1096. $identifier = $arr[$i]['data'];
  1097. break;
  1098. case 'quote_backtick':
  1099. case 'quote_double':
  1100. case 'quote_single':
  1101. $identifier = PMA_unQuote($arr[$i]['data']);
  1102. break;
  1103. } // end switch
  1104. if ($subresult['querytype'] == 'SELECT'
  1105. && ! $in_group_concat
  1106. && ! ($seen_subquery && $arr[$i - 1]['type'] == 'punct_bracket_close_round')) {
  1107. if (!$seen_from) {
  1108. if ($previous_was_identifier && isset($chain)) {
  1109. // found alias for this select_expr, save it
  1110. // but only if we got something in $chain
  1111. // (for example, SELECT COUNT(*) AS cnt
  1112. // puts nothing in $chain, so we avoid
  1113. // setting the alias)
  1114. $alias_for_select_expr = $identifier;
  1115. } else {
  1116. $chain[] = $identifier;
  1117. $previous_was_identifier = true;
  1118. } // end if !$previous_was_identifier
  1119. } else {
  1120. // ($seen_from)
  1121. if ($save_table_ref && !$seen_end_of_table_ref) {
  1122. if ($previous_was_identifier) {
  1123. // found alias for table ref
  1124. // save it for later
  1125. $alias_for_table_ref = $identifier;
  1126. } else {
  1127. $chain[] = $identifier;
  1128. $previous_was_identifier = true;
  1129. } // end if ($previous_was_identifier)
  1130. } // end if ($save_table_ref &&!$seen_end_of_table_ref)
  1131. } // end if (!$seen_from)
  1132. } // end if (querytype SELECT)
  1133. } // end if (quote_backtick or double quote or alpha_identifier)
  1134. // ===================================
  1135. if ($arr[$i]['type'] == 'punct_qualifier') {
  1136. // to be able to detect an identifier following another
  1137. $previous_was_identifier = false;
  1138. continue;
  1139. } // end if (punct_qualifier)
  1140. /**
  1141. * @todo check if 3 identifiers following one another -> error
  1142. */
  1143. // s a v e a s e l e c t e x p r
  1144. // finding a list separator or FROM
  1145. // means that we must save the current chain of identifiers
  1146. // into a select expression
  1147. // for now, we only save a select expression if it contains
  1148. // at least one identifier, as we are interested in checking
  1149. // the columns and table names, so in "select * from persons",
  1150. // the "*" is not saved
  1151. if (isset($chain) && !$seen_end_of_table_ref
  1152. && ((!$seen_from && $arr[$i]['type'] == 'punct_listsep')
  1153. || ($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data == 'FROM'))) {
  1154. $size_chain = count($chain);
  1155. $current_select_expr++;
  1156. $subresult['select_expr'][$current_select_expr] = array(
  1157. 'expr' => '',
  1158. 'alias' => '',
  1159. 'db' => '',
  1160. 'table_name' => '',
  1161. 'table_true_name' => '',
  1162. 'column' => ''
  1163. );
  1164. if (isset($alias_for_select_expr) && strlen($alias_for_select_expr)) {
  1165. // we had found an alias for this select expression
  1166. $subresult['select_expr'][$current_select_expr]['alias'] = $alias_for_select_expr;
  1167. unset($alias_for_select_expr);
  1168. }
  1169. // there is at least a column
  1170. $subresult['select_expr'][$current_select_expr]['column'] = $chain[$size_chain - 1];
  1171. $subresult['select_expr'][$current_select_expr]['expr'] = $chain[$size_chain - 1];
  1172. // maybe a table
  1173. if ($size_chain > 1) {
  1174. $subresult['select_expr'][$current_select_expr]['table_name'] = $chain[$size_chain - 2];
  1175. // we assume for now that this is also the true name
  1176. $subresult['select_expr'][$current_select_expr]['table_true_name'] = $chain[$size_chain - 2];
  1177. $subresult['select_expr'][$current_select_expr]['expr']
  1178. = $subresult['select_expr'][$current_select_expr]['table_name']
  1179. . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
  1180. } // end if ($size_chain > 1)
  1181. // maybe a db
  1182. if ($size_chain > 2) {
  1183. $subresult['select_expr'][$current_select_expr]['db'] = $chain[$size_chain - 3];
  1184. $subresult['select_expr'][$current_select_expr]['expr']
  1185. = $subresult['select_expr'][$current_select_expr]['db']
  1186. . '.' . $subresult['select_expr'][$current_select_expr]['expr'];
  1187. } // end if ($size_chain > 2)
  1188. unset($chain);
  1189. /**
  1190. * @todo explain this:
  1191. */
  1192. if (($arr[$i]['type'] == 'alpha_reservedWord')
  1193. && ($upper_data != 'FROM')) {
  1194. $previous_was_identifier = true;
  1195. }
  1196. } // end if (save a select expr)
  1197. //======================================
  1198. // s a v e a t a b l e r e f
  1199. //======================================
  1200. // maybe we just saw the end of table refs
  1201. // but the last table ref has to be saved
  1202. // or we are at the last token
  1203. // or we just got a reserved word
  1204. /**
  1205. * @todo there could be another query after this one
  1206. */
  1207. if (isset($chain) && $seen_from && $save_table_ref
  1208. && ($arr[$i]['type'] == 'punct_listsep'
  1209. || ($arr[$i]['type'] == 'alpha_reservedWord' && $upper_data!="AS")
  1210. || $seen_end_of_table_ref
  1211. || $i==$size-1)) {
  1212. $size_chain = count($chain);
  1213. $current_table_ref++;
  1214. $subresult['table_ref'][$current_table_ref] = array(
  1215. 'expr' => '',
  1216. 'db' => '',
  1217. 'table_name' => '',
  1218. 'table_alias' => '',
  1219. 'table_true_name' => ''
  1220. );
  1221. if (isset($alias_for_table_ref) && strlen($alias_for_table_ref)) {
  1222. $subresult['table_ref'][$current_table_ref]['table_alias'] = $alias_for_table_ref;
  1223. unset($alias_for_table_ref);
  1224. }
  1225. $subresult['table_ref'][$current_table_ref]['table_name'] = $chain[$size_chain - 1];
  1226. // we assume for now that this is also the true name
  1227. $subresult['table_ref'][$current_table_ref]['table_true_name'] = $chain[$size_chain - 1];
  1228. $subresult['table_ref'][$current_table_ref]['expr']
  1229. = $subresult['table_ref'][$current_table_ref]['table_name'];
  1230. // maybe a db
  1231. if ($size_chain > 1) {
  1232. $subresult['table_ref'][$current_table_ref]['db'] = $chain[$size_chain - 2];
  1233. $subresult['table_ref'][$current_table_ref]['expr']
  1234. = $subresult['table_ref'][$current_table_ref]['db']
  1235. . '.' . $subresult['table_ref'][$current_table_ref]['expr'];
  1236. } // end if ($size_chain > 1)
  1237. // add the table alias into the whole expression
  1238. $subresult['table_ref'][$current_table_ref]['expr']
  1239. .= ' ' . $subresult['table_ref'][$current_table_ref]['table_alias'];
  1240. unset($chain);
  1241. $previous_was_identifier = true;
  1242. //continue;
  1243. } // end if (save a table ref)
  1244. // when we have found all table refs,
  1245. // for each table_ref alias, put the true name of the table
  1246. // in the corresponding select expressions
  1247. if (isset($current_table_ref) && ($seen_end_of_table_ref || $i == $size-1) && $subresult != $subresult_empty) {
  1248. for ($tr=0; $tr <= $current_table_ref; $tr++) {
  1249. $alias = $subresult['table_ref'][$tr]['table_alias'];
  1250. $truename = $subresult['table_ref'][$tr]['table_true_name'];
  1251. for ($se=0; $se <= $current_select_expr; $se++) {
  1252. if (isset($alias)
  1253. && strlen($alias)
  1254. && $subresult['select_expr'][$se]['table_true_name'] == $alias
  1255. ) {
  1256. $subresult['select_expr'][$se]['table_true_name'] = $truename;
  1257. } // end if (found the alias)
  1258. } // end for (select expressions)
  1259. } // end for (table refs)
  1260. } // end if (set the true names)
  1261. // e n d i n g l o o p #1
  1262. // set the $previous_was_identifier to false if the current
  1263. // token is not an identifier
  1264. if (($arr[$i]['type'] != 'alpha_identifier')
  1265. && ($arr[$i]['type'] != 'quote_double')
  1266. && ($arr[$i]['type'] != 'quote_single')
  1267. && ($arr[$i]['type'] != 'quote_backtick')) {
  1268. $previous_was_identifier = false;
  1269. } // end if
  1270. // however, if we are on AS, we must keep the $previous_was_identifier
  1271. if (($arr[$i]['type'] == 'alpha_reservedWord')
  1272. && ($upper_data == 'AS')) {
  1273. $previous_was_identifier = true;
  1274. }
  1275. if (($arr[$i]['type'] == 'alpha_reservedWord')
  1276. && ($upper_data =='ON' || $upper_data =='USING')) {
  1277. $save_table_ref = false;
  1278. } // end if (data == ON)
  1279. if (($arr[$i]['type'] == 'alpha_reservedWord')
  1280. && ($upper_data =='JOIN' || $upper_data =='FROM')) {
  1281. $save_table_ref = true;
  1282. } // end if (data == JOIN)
  1283. /**
  1284. * no need to check the end of table ref if we already did
  1285. *
  1286. * @todo maybe add "&& $seen_from"
  1287. */
  1288. if (!$seen_end_of_table_ref) {
  1289. // if this is the last token, it implies that we have
  1290. // seen the end of table references
  1291. // Check for the end of table references
  1292. //
  1293. // Note: if we are analyzing a GROUP_CONCAT clause,
  1294. // we might find a word that seems to indicate that
  1295. // we have found the end of table refs (like ORDER)
  1296. // but it's a modifier of the GROUP_CONCAT so
  1297. // it's not the real end of table refs
  1298. if (($i == $size-1)
  1299. || ($arr[$i]['type'] == 'alpha_reservedWord'
  1300. && !$in_group_concat
  1301. && isset($words_ending_table_ref[$upper_data]))) {
  1302. $seen_end_of_table_ref = true;
  1303. // to be able to save the last table ref, but do not
  1304. // set it true if we found a word like "ON" that has
  1305. // already set it to false
  1306. if (isset($save_table_ref) && $save_table_ref != false) {
  1307. $save_table_ref = true;
  1308. } //end if
  1309. } // end if (check for end of table ref)
  1310. } //end if (!$seen_end_of_table_ref)
  1311. if ($seen_end_of_table_ref) {
  1312. $save_table_ref = false;
  1313. } // end if
  1314. } // end for $i (loop #1)
  1315. //DEBUG
  1316. /*
  1317. if (isset($current_select_expr)) {
  1318. for ($trace=0; $trace<=$current_select_expr; $trace++) {
  1319. echo "<br />";
  1320. reset ($subresult['select_expr'][$trace]);
  1321. while (list ($key, $val) = each ($subresult['select_expr'][$trace]))
  1322. echo "sel expr $trace $key => $val<br />\n";
  1323. }
  1324. }
  1325. if (isset($current_table_ref)) {
  1326. echo "current_table_ref = " . $current_table_ref . "<br>";
  1327. for ($trace=0; $trace<=$current_table_ref; $trace++) {
  1328. echo "<br />";
  1329. reset ($subresult['table_ref'][$trace]);
  1330. while (list ($key, $val) = each ($subresult['table_ref'][$trace]))
  1331. echo "table ref $trace $key => $val<br />\n";
  1332. }
  1333. }
  1334. */
  1335. // -------------------------------------------------------
  1336. // loop #2: - queryflags
  1337. // - querytype (for queries != 'SELECT')
  1338. // - section_before_limit, section_after_limit
  1339. //
  1340. // we will also need this queryflag in loop 2
  1341. // so set it here
  1342. if (isset($current_table_ref) && $current_table_ref > -1) {
  1343. $subresult['queryflags']['select_from'] = 1;
  1344. }
  1345. $section_before_limit = '';
  1346. $section_after_limit = ''; // truly the section after the limit clause
  1347. $seen_reserved_word = false;
  1348. $seen_group = false;
  1349. $seen_order = false;
  1350. $seen_order_by = false;
  1351. $in_group_by = false; // true when we are inside the GROUP BY clause
  1352. $in_order_by = false; // true when we are inside the ORDER BY clause
  1353. $in_having = false; // true when we are inside the HAVING clause
  1354. $in_select_expr = false; // true when we are inside the select expr clause
  1355. $in_where = false; // true when we are inside the WHERE clause
  1356. $seen_limit = false; // true if we have seen a LIMIT clause
  1357. $in_limit = false; // true when we are inside the LIMIT clause
  1358. $after_limit = false; // true when we are after the LIMIT clause
  1359. $in_from = false; // true when we are in the FROM clause
  1360. $in_group_concat = false;
  1361. $first_reserved_word = '';
  1362. $current_identifier = '';
  1363. $unsorted_query = $arr['raw']; // in case there is no ORDER BY
  1364. $number_of_brackets = 0;
  1365. $in_subquery = false;
  1366. for ($i = 0; $i < $size; $i++) {
  1367. //DEBUG echo "Loop2 <strong>" . $arr[$i]['data'] . "</strong> (" . $arr[$i]['type'] . ")<br />";
  1368. // need_confirm
  1369. //
  1370. // check for reserved words that will have to generate
  1371. // a confirmation request later in sql.php
  1372. // the cases are:
  1373. // DROP TABLE
  1374. // DROP DATABASE
  1375. // ALTER TABLE... DROP
  1376. // DELETE FROM...
  1377. //
  1378. // this code is not used for confirmations coming from functions.js
  1379. if ($arr[$i]['type'] == 'punct_bracket_open_round') {
  1380. $number_of_brackets++;
  1381. }
  1382. if ($arr[$i]['type'] == 'punct_bracket_close_round') {
  1383. $number_of_brackets--;
  1384. if ($number_of_brackets == 0) {
  1385. $in_subquery = false;
  1386. }
  1387. }
  1388. if ($arr[$i]['type'] == 'alpha_reservedWord') {
  1389. $upper_data = strtoupper($arr[$i]['data']);
  1390. if ($upper_data == 'SELECT' && $number_of_brackets > 0) {
  1391. $in_subquery = true;
  1392. }
  1393. if (!$seen_reserved_word) {
  1394. $first_reserved_word = $upper_data;
  1395. $subresult['querytype'] = $upper_data;
  1396. $seen_reserved_word = true;
  1397. // if the first reserved word is DROP or DELETE,
  1398. // we know this is a query that needs to be confirmed
  1399. if ($first_reserved_word=='DROP'
  1400. || $first_reserved_word == 'DELETE'
  1401. || $first_reserved_word == 'TRUNCATE') {
  1402. $subresult['queryflags']['need_confirm'] = 1;
  1403. }
  1404. if ($first_reserved_word=='SELECT') {
  1405. $position_of_first_select = $i;
  1406. }
  1407. } else {
  1408. if ($upper_data == 'DROP' && $first_reserved_word == 'ALTER') {
  1409. $subresult['queryflags']['need_confirm'] = 1;
  1410. }
  1411. }
  1412. if ($upper_data == 'LIMIT' && ! $in_subquery) {
  1413. $section_before_limit = substr($arr['raw'], 0, $arr[$i]['pos'] - 5);
  1414. $in_limit = true;
  1415. $seen_limit = true;
  1416. $limit_clause = '';
  1417. $in_order_by = false; // @todo maybe others to set false
  1418. }
  1419. if ($upper_data == 'PROCEDURE') {
  1420. $subresult['queryflags']['procedure'] = 1;
  1421. $in_limit = false;
  1422. $after_limit = true;
  1423. }
  1424. /**
  1425. * @todo set also to false if we find FOR UPDATE or LOCK IN SHARE MODE
  1426. */
  1427. if ($upper_data == 'SELECT') {
  1428. $in_select_expr = true;
  1429. $select_expr_clause = '';
  1430. }
  1431. if ($upper_data == 'DISTINCT' && !$in_group_concat) {
  1432. $subresult['queryflags']['distinct'] = 1;
  1433. }
  1434. if ($upper_data == 'UNION') {
  1435. $subresult['queryflags']['union'] = 1;
  1436. }
  1437. if ($upper_data == 'JOIN') {
  1438. $subresult['queryflags']['join'] = 1;
  1439. }
  1440. if ($upper_data == 'OFFSET') {
  1441. $subresult['queryflags']['offset'] = 1;
  1442. }
  1443. // if this is a real SELECT...FROM
  1444. if ($upper_data == 'FROM' && isset($subresult['queryflags']['select_from']) && $subresult['queryflags']['select_from'] == 1) {
  1445. $in_from = true;
  1446. $from_clause = '';
  1447. $in_select_expr = false;
  1448. }
  1449. // (we could have less resetting of variables to false
  1450. // if we trust that the query respects the standard
  1451. // MySQL order for clauses)
  1452. // we use $seen_group and $seen_order because we are looking
  1453. // for the BY
  1454. if ($upper_data == 'GROUP') {
  1455. $seen_group = true;
  1456. $seen_order = false;
  1457. $in_having = false;
  1458. $in_order_by = false;
  1459. $in_where = false;
  1460. $in_select_expr = false;
  1461. $in_from = false;
  1462. }
  1463. if ($upper_data == 'ORDER' && !$in_group_concat) {
  1464. $seen_order = true;
  1465. $seen_group = false;
  1466. $in_having = false;
  1467. $in_group_by = false;
  1468. $in_where = false;
  1469. $in_select_expr = false;
  1470. $in_from = false;
  1471. }
  1472. if ($upper_data == 'HAVING') {
  1473. $in_having = true;
  1474. $having_clause = '';
  1475. $seen_group = false;
  1476. $seen_order = false;
  1477. $in_group_by = false;
  1478. $in_order_by = false;
  1479. $in_where = false;
  1480. $in_select_expr = false;
  1481. $in_from = false;
  1482. }
  1483. if ($upper_data == 'WHERE') {
  1484. $in_where = true;
  1485. $where_clause = '';
  1486. $where_clause_identifiers = array();
  1487. $seen_group = false;
  1488. $seen_order = false;
  1489. $in_group_by = false;
  1490. $in_order_by = false;
  1491. $in_having = false;
  1492. $in_select_expr = false;
  1493. $in_from = false;
  1494. }
  1495. if ($upper_data == 'BY') {
  1496. if ($seen_group) {
  1497. $in_group_by = true;
  1498. $group_by_clause = '';
  1499. }
  1500. if ($seen_order) {
  1501. $seen_order_by = true;
  1502. // Here we assume that the ORDER BY keywords took
  1503. // exactly 8 characters.
  1504. // We use PMA_substr() to be charset-safe; otherwise
  1505. // if the table name contains accents, the unsorted
  1506. // query would be missing some characters.
  1507. $unsorted_query = PMA_substr($arr['raw'], 0, $arr[$i]['pos'] - 8);
  1508. $in_order_by = true;
  1509. $order_by_clause = '';
  1510. }
  1511. }
  1512. // if we find one of the words that could end the clause
  1513. if (isset($words_ending_clauses[$upper_data])) {
  1514. $in_group_by = false;
  1515. $in_order_by = false;
  1516. $in_having = false;
  1517. $in_where = false;
  1518. $in_select_expr = false;
  1519. $in_from = false;
  1520. }
  1521. } // endif (reservedWord)
  1522. // do not add a space after a function name
  1523. /**
  1524. * @todo can we combine loop 2 and loop 1? some code is repeated here...
  1525. */
  1526. $sep = ' ';
  1527. if ($arr[$i]['type'] == 'alpha_functionName') {
  1528. $sep='';
  1529. $upper_data = strtoupper($arr[$i]['data']);
  1530. if ($upper_data =='GROUP_CONCAT') {
  1531. $in_group_concat = true;
  1532. $number_of_brackets_in_group_concat = 0;
  1533. }
  1534. }
  1535. if ($arr[$i]['type'] == 'punct_bracket_open_round') {
  1536. if ($in_group_concat) {
  1537. $number_of_brackets_in_group_concat++;
  1538. }
  1539. }
  1540. if ($arr[$i]['type'] == 'punct_bracket_close_round') {
  1541. if ($in_group_concat) {
  1542. $number_of_brackets_in_group_concat--;
  1543. if ($number_of_brackets_in_group_concat == 0) {
  1544. $in_group_concat = false;
  1545. }
  1546. }
  1547. }
  1548. // do not add a space after an identifier if followed by a dot
  1549. if ($arr[$i]['type'] == 'alpha_identifier' && $i < $size - 1 && $arr[$i + 1]['data'] == '.') {
  1550. $sep = '';
  1551. }
  1552. // do not add a space after a dot if followed by an identifier
  1553. if ($arr[$i]['data'] == '.' && $i < $size - 1 && $arr[$i + 1]['type'] == 'alpha_identifier') {
  1554. $sep = '';
  1555. }
  1556. if ($in_select_expr && $upper_data != 'SELECT' && $upper_data != 'DISTINCT') {
  1557. $select_expr_clause .= $arr[$i]['data'] . $sep;
  1558. }
  1559. if ($in_from && $upper_data != 'FROM') {
  1560. $from_clause .= $arr[$i]['data'] . $sep;
  1561. }
  1562. if ($in_group_by && $upper_data != 'GROUP' && $upper_data != 'BY') {
  1563. $group_by_clause .= $arr[$i]['data'] . $sep;
  1564. }
  1565. if ($in_order_by && $upper_data != 'ORDER' && $upper_data != 'BY') {
  1566. // add a space only before ASC or DESC
  1567. // not around the dot between dbname and tablename
  1568. if ($arr[$i]['type'] == 'alpha_reservedWord') {
  1569. $order_by_clause .= $sep;
  1570. }
  1571. $order_by_clause .= $arr[$i]['data'];
  1572. }
  1573. if ($in_having && $upper_data != 'HAVING') {
  1574. $having_clause .= $arr[$i]['data'] . $sep;
  1575. }
  1576. if ($in_where && $upper_data != 'WHERE') {
  1577. $where_clause .= $arr[$i]['data'] . $sep;
  1578. if (($arr[$i]['type'] == 'quote_backtick')
  1579. || ($arr[$i]['type'] == 'alpha_identifier')) {
  1580. $where_clause_identifiers[] = $arr[$i]['data'];
  1581. }
  1582. }
  1583. // to grab the rest of the query after the ORDER BY clause
  1584. if (isset($subresult['queryflags']['select_from'])
  1585. && $subresult['queryflags']['select_from'] == 1
  1586. && ! $in_order_by
  1587. && $seen_order_by
  1588. && $upper_data != 'BY') {
  1589. $unsorted_query .= $arr[$i]['data'];
  1590. if ($arr[$i]['type'] != 'punct_bracket_open_round'
  1591. && $arr[$i]['type'] != 'punct_bracket_close_round'
  1592. && $arr[$i]['type'] != 'punct') {
  1593. $unsorted_query .= $sep;
  1594. }
  1595. }
  1596. if ($in_limit) {
  1597. if ($upper_data == 'OFFSET') {
  1598. $limit_clause .= $sep;
  1599. }
  1600. $limit_clause .= $arr[$i]['data'];
  1601. if ($upper_data == 'LIMIT' || $upper_data == 'OFFSET') {
  1602. $limit_clause .= $sep;
  1603. }
  1604. }
  1605. if ($after_limit && $seen_limit) {
  1606. $section_after_limit .= $arr[$i]['data'] . $sep;
  1607. }
  1608. // clear $upper_data for next iteration
  1609. $upper_data='';
  1610. } // end for $i (loop #2)
  1611. if (empty($section_before_limit)) {
  1612. $section_before_limit = $arr['raw'];
  1613. }
  1614. // -----------------------------------------------------
  1615. // loop #3: foreign keys and MySQL 4.1.2+ TIMESTAMP options
  1616. // (for now, check only the first query)
  1617. // (for now, identifiers are assumed to be backquoted)
  1618. // If we find that we are dealing with a CREATE TABLE query,
  1619. // we look for the next punct_bracket_open_round, which
  1620. // introduces the fields list. Then, when we find a
  1621. // quote_backtick, it must be a field, so we put it into
  1622. // the create_table_fields array. Even if this field is
  1623. // not a timestamp, it will be useful when logic has been
  1624. // added for complete field attributes analysis.
  1625. $seen_foreign = false;
  1626. $seen_references = false;
  1627. $seen_constraint = false;
  1628. $foreign_key_number = -1;
  1629. $seen_create_table = false;
  1630. $seen_create = false;
  1631. $seen_alter = false;
  1632. $in_create_table_fields = false;
  1633. $brackets_level = 0;
  1634. $in_timestamp_options = false;
  1635. $seen_default = false;
  1636. for ($i = 0; $i < $size; $i++) {
  1637. // DEBUG echo "Loop 3 <strong>" . $arr[$i]['data'] . "</strong> " . $arr[$i]['type'] . "<br />";
  1638. if ($arr[$i]['type'] == 'alpha_reservedWord') {
  1639. $upper_data = strtoupper($arr[$i]['data']);
  1640. if ($upper_data == 'NOT' && $in_timestamp_options) {
  1641. $create_table_fields[$current_identifier]['timestamp_not_null'] = true;
  1642. }
  1643. if ($upper_data == 'CREATE') {
  1644. $seen_create = true;
  1645. }
  1646. if ($upper_data == 'ALTER') {
  1647. $seen_alter = true;
  1648. }
  1649. if ($upper_data == 'TABLE' && $seen_create) {
  1650. $seen_create_table = true;
  1651. $create_table_fields = array();
  1652. }
  1653. if ($upper_data == 'CURRENT_TIMESTAMP') {
  1654. if ($in_timestamp_options) {
  1655. if ($seen_default) {
  1656. $create_table_fields[$current_identifier]['default_current_timestamp'] = true;
  1657. }
  1658. }
  1659. }
  1660. if ($upper_data == 'CONSTRAINT') {
  1661. $foreign_key_number++;
  1662. $seen_foreign = false;
  1663. $seen_references = false;
  1664. $seen_constraint = true;
  1665. }
  1666. if ($upper_data == 'FOREIGN') {
  1667. $seen_foreign = true;
  1668. $seen_references = false;
  1669. $seen_constraint = false;
  1670. }
  1671. if ($upper_data == 'REFERENCES') {
  1672. $seen_foreign = false;
  1673. $seen_references = true;
  1674. $seen_constraint = false;
  1675. }
  1676. // Cases covered:
  1677. // [ON DELETE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
  1678. // [ON UPDATE {CASCADE | SET NULL | NO ACTION | RESTRICT}]
  1679. // but we set ['on_delete'] or ['on_cascade'] to
  1680. // CASCADE | SET_NULL | NO_ACTION | RESTRICT
  1681. // ON UPDATE CURRENT_TIMESTAMP
  1682. if ($upper_data == 'ON') {
  1683. if (isset($arr[$i+1]) && $arr[$i+1]['type'] == 'alpha_reservedWord') {
  1684. $second_upper_data = strtoupper($arr[$i+1]['data']);
  1685. if ($second_upper_data == 'DELETE') {
  1686. $clause = 'on_delete';
  1687. }
  1688. if ($second_upper_data == 'UPDATE') {
  1689. $clause = 'on_update';
  1690. }
  1691. if (isset($clause)
  1692. && ($arr[$i+2]['type'] == 'alpha_reservedWord'
  1693. // ugly workaround because currently, NO is not
  1694. // in the list of reserved words in sqlparser.data
  1695. // (we got a bug report about not being able to use
  1696. // 'no' as an identifier)
  1697. || ($arr[$i+2]['type'] == 'alpha_identifier'
  1698. && strtoupper($arr[$i+2]['data'])=='NO'))
  1699. ) {
  1700. $third_upper_data = strtoupper($arr[$i+2]['data']);
  1701. if ($third_upper_data == 'CASCADE'
  1702. || $third_upper_data == 'RESTRICT') {
  1703. $value = $third_upper_data;
  1704. } elseif ($third_upper_data == 'SET'
  1705. || $third_upper_data == 'NO') {
  1706. if ($arr[$i+3]['type'] == 'alpha_reservedWord') {
  1707. $value = $third_upper_data . '_' . strtoupper($arr[$i+3]['data']);
  1708. }
  1709. } elseif ($third_upper_data == 'CURRENT_TIMESTAMP') {
  1710. if ($clause == 'on_update'
  1711. && $in_timestamp_options) {
  1712. $create_table_fields[$current_identifier]['on_update_current_timestamp'] = true;
  1713. $seen_default = false;
  1714. }
  1715. } else {
  1716. $value = '';
  1717. }
  1718. if (!empty($value)) {
  1719. $foreign[$foreign_key_number][$clause] = $value;
  1720. }
  1721. unset($clause);
  1722. } // endif (isset($clause))
  1723. }
  1724. }
  1725. } // end of reserved words analysis
  1726. if ($arr[$i]['type'] == 'punct_bracket_open_round') {
  1727. $brackets_level++;
  1728. if ($seen_create_table && $brackets_level == 1) {
  1729. $in_create_table_fields = true;
  1730. }
  1731. }
  1732. if ($arr[$i]['type'] == 'punct_bracket_close_round') {
  1733. $brackets_level--;
  1734. if ($seen_references) {
  1735. $seen_references = false;
  1736. }
  1737. if ($seen_create_table && $brackets_level == 0) {
  1738. $in_create_table_fields = false;
  1739. }
  1740. }
  1741. if (($arr[$i]['type'] == 'alpha_columnAttrib')) {
  1742. $upper_data = strtoupper($arr[$i]['data']);
  1743. if ($seen_create_table && $in_create_table_fields) {
  1744. if ($upper_data == 'DEFAULT') {
  1745. $seen_default = true;
  1746. $create_table_fields[$current_identifier]['default_value'] = $arr[$i + 1]['data'];
  1747. }
  1748. }
  1749. }
  1750. /**
  1751. * @see @todo 2005-10-16 note: the "or" part here is a workaround for a bug
  1752. */
  1753. if (($arr[$i]['type'] == 'alpha_columnType') || ($arr[$i]['type'] == 'alpha_functionName' && $seen_create_table)) {
  1754. $upper_data = strtoupper($arr[$i]['data']);
  1755. if ($seen_create_table && $in_create_table_fields && isset($current_identifier)) {
  1756. $create_table_fields[$current_identifier]['type'] = $upper_data;
  1757. if ($upper_data == 'TIMESTAMP') {
  1758. $arr[$i]['type'] = 'alpha_columnType';
  1759. $in_timestamp_options = true;
  1760. } else {
  1761. $in_timestamp_options = false;
  1762. if ($upper_data == 'CHAR') {
  1763. $arr[$i]['type'] = 'alpha_columnType';
  1764. }
  1765. }
  1766. }
  1767. }
  1768. if ($arr[$i]['type'] == 'quote_backtick' || $arr[$i]['type'] == 'alpha_identifier') {
  1769. if ($arr[$i]['type'] == 'quote_backtick') {
  1770. // remove backquotes
  1771. $identifier = PMA_unQuote($arr[$i]['data']);
  1772. } else {
  1773. $identifier = $arr[$i]['data'];
  1774. }
  1775. if ($seen_create_table && $in_create_table_fields) {
  1776. $current_identifier = $identifier;
  1777. // we set this one even for non TIMESTAMP type
  1778. $create_table_fields[$current_identifier]['timestamp_not_null'] = false;
  1779. }
  1780. if ($seen_constraint) {
  1781. $foreign[$foreign_key_number]['constraint'] = $identifier;
  1782. }
  1783. if ($seen_foreign && $brackets_level > 0) {
  1784. $foreign[$foreign_key_number]['index_list'][] = $identifier;
  1785. }
  1786. if ($seen_references) {
  1787. if ($seen_alter && $brackets_level > 0) {
  1788. $foreign[$foreign_key_number]['ref_index_list'][] = $identifier;
  1789. // here, the first bracket level corresponds to the
  1790. // bracket of CREATE TABLE
  1791. // so if we are on level 2, it must be the index list
  1792. // of the foreign key REFERENCES
  1793. } elseif ($brackets_level > 1) {
  1794. $foreign[$foreign_key_number]['ref_index_list'][] = $identifier;
  1795. } elseif ($arr[$i+1]['type'] == 'punct_qualifier') {
  1796. // identifier is `db`.`table`
  1797. // the first pass will pick the db name
  1798. // the next pass will pick the table name
  1799. $foreign[$foreign_key_number]['ref_db_name'] = $identifier;
  1800. } else {
  1801. // identifier is `table`
  1802. $foreign[$foreign_key_number]['ref_table_name'] = $identifier;
  1803. }
  1804. }
  1805. }
  1806. } // end for $i (loop #3)
  1807. // Fill the $subresult array
  1808. if (isset($create_table_fields)) {
  1809. $subresult['create_table_fields'] = $create_table_fields;
  1810. }
  1811. if (isset($foreign)) {
  1812. $subresult['foreign_keys'] = $foreign;
  1813. }
  1814. if (isset($select_expr_clause)) {
  1815. $subresult['select_expr_clause'] = $select_expr_clause;
  1816. }
  1817. if (isset($from_clause)) {
  1818. $subresult['from_clause'] = $from_clause;
  1819. }
  1820. if (isset($group_by_clause)) {
  1821. $subresult['group_by_clause'] = $group_by_clause;
  1822. }
  1823. if (isset($order_by_clause)) {
  1824. $subresult['order_by_clause'] = $order_by_clause;
  1825. }
  1826. if (isset($having_clause)) {
  1827. $subresult['having_clause'] = $having_clause;
  1828. }
  1829. if (isset($limit_clause)) {
  1830. $subresult['limit_clause'] = $limit_clause;
  1831. }
  1832. if (isset($where_clause)) {
  1833. $subresult['where_clause'] = $where_clause;
  1834. }
  1835. if (isset($unsorted_query) && !empty($unsorted_query)) {
  1836. $subresult['unsorted_query'] = $unsorted_query;
  1837. }
  1838. if (isset($where_clause_identifiers)) {
  1839. $subresult['where_clause_identifiers'] = $where_clause_identifiers;
  1840. }
  1841. if (isset($position_of_first_select)) {
  1842. $subresult['position_of_first_select'] = $position_of_first_select;
  1843. $subresult['section_before_limit'] = $section_before_limit;
  1844. $subresult['section_after_limit'] = $section_after_limit;
  1845. }
  1846. // They are naughty and didn't have a trailing semi-colon,
  1847. // then still handle it properly
  1848. if ($subresult['querytype'] != '') {
  1849. $result[] = $subresult;
  1850. }
  1851. return $result;
  1852. } // end of the "PMA_SQP_analyze()" function
  1853. /**
  1854. * Colorizes SQL queries html formatted
  1855. *
  1856. * @todo check why adding a "\n" after the </span> would cause extra blanks
  1857. * to be displayed: SELECT p . person_name
  1858. * @param array The SQL queries html formatted
  1859. *
  1860. * @return array The colorized SQL queries
  1861. *
  1862. * @access public
  1863. */
  1864. function PMA_SQP_formatHtml_colorize($arr)
  1865. {
  1866. $i = PMA_strpos($arr['type'], '_');
  1867. $class = '';
  1868. if ($i > 0) {
  1869. $class = 'syntax_' . PMA_substr($arr['type'], 0, $i) . ' ';
  1870. }
  1871. $class .= 'syntax_' . $arr['type'];
  1872. return '<span class="' . $class . '">' . htmlspecialchars($arr['data']) . '</span>';
  1873. } // end of the "PMA_SQP_formatHtml_colorize()" function
  1874. /**
  1875. * Formats SQL queries to html
  1876. *
  1877. * @param array The SQL queries
  1878. * @param string mode
  1879. * @param integer starting token
  1880. * @param integer number of tokens to format, -1 = all
  1881. *
  1882. * @return string The formatted SQL queries
  1883. *
  1884. * @access public
  1885. */
  1886. function PMA_SQP_formatHtml($arr, $mode='color', $start_token=0,
  1887. $number_of_tokens=-1)
  1888. {
  1889. global $PMA_SQPdata_operators_docs, $PMA_SQPdata_functions_docs;
  1890. //DEBUG echo 'in Format<pre>'; print_r($arr); echo '</pre>';
  1891. // then check for an array
  1892. if (! is_array($arr)) {
  1893. return htmlspecialchars($arr);
  1894. }
  1895. // first check for the SQL parser having hit an error
  1896. if (PMA_SQP_isError()) {
  1897. return htmlspecialchars($arr['raw']);
  1898. }
  1899. // else do it properly
  1900. switch ($mode) {
  1901. case 'color':
  1902. $str = '<span class="syntax">';
  1903. $html_line_break = '<br />';
  1904. $docu = true;
  1905. break;
  1906. case 'query_only':
  1907. $str = '';
  1908. $html_line_break = "\n";
  1909. $docu = false;
  1910. break;
  1911. case 'text':
  1912. $str = '';
  1913. $html_line_break = '<br />';
  1914. $docu = true;
  1915. break;
  1916. } // end switch
  1917. // inner_sql is a span that exists for all cases, except query_only
  1918. // of $cfg['SQP']['fmtType'] to make possible a replacement
  1919. // for inline editing
  1920. if ($mode!='query_only') {
  1921. $str .= '<span class="inner_sql">';
  1922. }
  1923. $close_docu_link = false;
  1924. $indent = 0;
  1925. $bracketlevel = 0;
  1926. $functionlevel = 0;
  1927. $infunction = false;
  1928. $space_punct_listsep = ' ';
  1929. $space_punct_listsep_function_name = ' ';
  1930. // $space_alpha_reserved_word = '<br />'."\n";
  1931. $space_alpha_reserved_word = ' ';
  1932. $keywords_with_brackets_1before = array(
  1933. 'INDEX' => 1,
  1934. 'KEY' => 1,
  1935. 'ON' => 1,
  1936. 'USING' => 1
  1937. );
  1938. $keywords_with_brackets_2before = array(
  1939. 'IGNORE' => 1,
  1940. 'INDEX' => 1,
  1941. 'INTO' => 1,
  1942. 'KEY' => 1,
  1943. 'PRIMARY' => 1,
  1944. 'PROCEDURE' => 1,
  1945. 'REFERENCES' => 1,
  1946. 'UNIQUE' => 1,
  1947. 'USE' => 1
  1948. );
  1949. // These reserved words do NOT get a newline placed near them.
  1950. $keywords_no_newline = array(
  1951. 'AS' => 1,
  1952. 'ASC' => 1,
  1953. 'DESC' => 1,
  1954. 'DISTINCT' => 1,
  1955. 'DUPLICATE' => 1,
  1956. 'HOUR' => 1,
  1957. 'INTERVAL' => 1,
  1958. 'IS' => 1,
  1959. 'LIKE' => 1,
  1960. 'NOT' => 1,
  1961. 'NULL' => 1,
  1962. 'ON' => 1,
  1963. 'REGEXP' => 1
  1964. );
  1965. // These reserved words introduce a privilege list
  1966. $keywords_priv_list = array(
  1967. 'GRANT' => 1,
  1968. 'REVOKE' => 1
  1969. );
  1970. if ($number_of_tokens == -1) {
  1971. $number_of_tokens = $arr['len'];
  1972. }
  1973. $typearr = array();
  1974. if ($number_of_tokens >= 0) {
  1975. $typearr[0] = '';
  1976. $typearr[1] = '';
  1977. $typearr[2] = '';
  1978. $typearr[3] = $arr[$start_token]['type'];
  1979. }
  1980. $in_priv_list = false;
  1981. for ($i = $start_token; $i < $number_of_tokens; $i++) {
  1982. // DEBUG echo "Loop format <strong>" . $arr[$i]['data'] . "</strong> " . $arr[$i]['type'] . "<br />";
  1983. $before = '';
  1984. $after = '';
  1985. // array_shift($typearr);
  1986. /*
  1987. 0 prev2
  1988. 1 prev
  1989. 2 current
  1990. 3 next
  1991. */
  1992. if (($i + 1) < $number_of_tokens) {
  1993. $typearr[4] = $arr[$i + 1]['type'];
  1994. } else {
  1995. $typearr[4] = '';
  1996. }
  1997. for ($j=0; $j<4; $j++) {
  1998. $typearr[$j] = $typearr[$j + 1];
  1999. }
  2000. switch ($typearr[2]) {
  2001. case 'alpha_bitfield_constant_introducer':
  2002. $before = ' ';
  2003. $after = '';
  2004. break;
  2005. case 'white_newline':
  2006. $before = '';
  2007. break;
  2008. case 'punct_bracket_open_round':
  2009. $bracketlevel++;
  2010. $infunction = false;
  2011. // Make sure this array is sorted!
  2012. if (($typearr[1] == 'alpha_functionName') || ($typearr[1] == 'alpha_columnType') || ($typearr[1] == 'punct')
  2013. || ($typearr[3] == 'digit_integer') || ($typearr[3] == 'digit_hex') || ($typearr[3] == 'digit_float')
  2014. || (($typearr[0] == 'alpha_reservedWord')
  2015. && isset($keywords_with_brackets_2before[strtoupper($arr[$i - 2]['data'])]))
  2016. || (($typearr[1] == 'alpha_reservedWord')
  2017. && isset($keywords_with_brackets_1before[strtoupper($arr[$i - 1]['data'])]))
  2018. ) {
  2019. $functionlevel++;
  2020. $infunction = true;
  2021. $after .= ' ';
  2022. } else {
  2023. $indent++;
  2024. $after .= ($mode != 'query_only' ? '<div class="syntax_indent' . $indent . '">' : ' ');
  2025. }
  2026. break;
  2027. case 'alpha_identifier':
  2028. if (($typearr[1] == 'punct_qualifier') || ($typearr[3] == 'punct_qualifier')) {
  2029. $after = '';
  2030. $before = '';
  2031. }
  2032. // for example SELECT 1 somealias
  2033. if ($typearr[1] == 'digit_integer') {
  2034. $before = ' ';
  2035. }
  2036. if (($typearr[3] == 'alpha_columnType') || ($typearr[3] == 'alpha_identifier')) {
  2037. $after .= ' ';
  2038. }
  2039. break;
  2040. case 'punct_user':
  2041. case 'punct_qualifier':
  2042. $before = '';
  2043. $after = '';
  2044. break;
  2045. case 'punct_listsep':
  2046. if ($infunction == true) {
  2047. $after .= $space_punct_listsep_function_name;
  2048. } else {
  2049. $after .= $space_punct_listsep;
  2050. }
  2051. break;
  2052. case 'punct_queryend':
  2053. if (($typearr[3] != 'comment_mysql') && ($typearr[3] != 'comment_ansi') && $typearr[3] != 'comment_c') {
  2054. $after .= $html_line_break;
  2055. $after .= $html_line_break;
  2056. }
  2057. $space_punct_listsep = ' ';
  2058. $space_punct_listsep_function_name = ' ';
  2059. $space_alpha_reserved_word = ' ';
  2060. $in_priv_list = false;
  2061. break;
  2062. case 'comment_mysql':
  2063. case 'comment_ansi':
  2064. $after .= $html_line_break;
  2065. break;
  2066. case 'punct':
  2067. $before .= ' ';
  2068. if ($docu && isset($PMA_SQPdata_operators_docs[$arr[$i]['data']]) &&
  2069. ($arr[$i]['data'] != '*' || in_array($arr[$i]['type'], array('digit_integer','digit_float','digit_hex')))) {
  2070. $before .= PMA_showMySQLDocu(
  2071. 'functions',
  2072. $PMA_SQPdata_operators_docs[$arr[$i]['data']]['link'],
  2073. false,
  2074. $PMA_SQPdata_operators_docs[$arr[$i]['data']]['anchor'],
  2075. true);
  2076. $after .= '</a>';
  2077. }
  2078. // workaround for
  2079. // select * from mytable limit 0,-1
  2080. // (a side effect of this workaround is that
  2081. // select 20 - 9
  2082. // becomes
  2083. // select 20 -9
  2084. // )
  2085. if ($typearr[3] != 'digit_integer') {
  2086. $after .= ' ';
  2087. }
  2088. break;
  2089. case 'punct_bracket_close_round':
  2090. // only close bracket level when it was opened before
  2091. if ($bracketlevel > 0) {
  2092. $bracketlevel--;
  2093. if ($infunction == true) {
  2094. $functionlevel--;
  2095. $after .= ' ';
  2096. $before .= ' ';
  2097. } else {
  2098. $indent--;
  2099. $before .= ($mode != 'query_only' ? '</div>' : ' ');
  2100. }
  2101. $infunction = ($functionlevel > 0) ? true : false;
  2102. }
  2103. break;
  2104. case 'alpha_columnType':
  2105. if ($docu) {
  2106. switch ($arr[$i]['data']) {
  2107. case 'tinyint':
  2108. case 'smallint':
  2109. case 'mediumint':
  2110. case 'int':
  2111. case 'bigint':
  2112. case 'decimal':
  2113. case 'float':
  2114. case 'double':
  2115. case 'real':
  2116. case 'bit':
  2117. case 'boolean':
  2118. case 'serial':
  2119. $before .= PMA_showMySQLDocu('data-types', 'numeric-types', false, '', true);
  2120. $after = '</a>' . $after;
  2121. break;
  2122. case 'date':
  2123. case 'datetime':
  2124. case 'timestamp':
  2125. case 'time':
  2126. case 'year':
  2127. $before .= PMA_showMySQLDocu('data-types', 'date-and-time-types', false, '', true);
  2128. $after = '</a>' . $after;
  2129. break;
  2130. case 'char':
  2131. case 'varchar':
  2132. case 'tinytext':
  2133. case 'text':
  2134. case 'mediumtext':
  2135. case 'longtext':
  2136. case 'binary':
  2137. case 'varbinary':
  2138. case 'tinyblob':
  2139. case 'mediumblob':
  2140. case 'blob':
  2141. case 'longblob':
  2142. case 'enum':
  2143. case 'set':
  2144. $before .= PMA_showMySQLDocu('data-types', 'string-types', false, '', true);
  2145. $after = '</a>' . $after;
  2146. break;
  2147. }
  2148. }
  2149. if ($typearr[3] == 'alpha_columnAttrib') {
  2150. $after .= ' ';
  2151. }
  2152. if ($typearr[1] == 'alpha_columnType') {
  2153. $before .= ' ';
  2154. }
  2155. break;
  2156. case 'alpha_columnAttrib':
  2157. // ALTER TABLE tbl_name AUTO_INCREMENT = 1
  2158. // COLLATE LATIN1_GENERAL_CI DEFAULT
  2159. if ($typearr[1] == 'alpha_identifier' || $typearr[1] == 'alpha_charset') {
  2160. $before .= ' ';
  2161. }
  2162. if (($typearr[3] == 'alpha_columnAttrib') || ($typearr[3] == 'quote_single') || ($typearr[3] == 'digit_integer')) {
  2163. $after .= ' ';
  2164. }
  2165. // workaround for
  2166. // AUTO_INCREMENT = 31DEFAULT_CHARSET = utf-8
  2167. if ($typearr[2] == 'alpha_columnAttrib' && $typearr[3] == 'alpha_reservedWord') {
  2168. $before .= ' ';
  2169. }
  2170. // workaround for
  2171. // select * from mysql.user where binary user="root"
  2172. // binary is marked as alpha_columnAttrib
  2173. // but should be marked as a reserved word
  2174. if (strtoupper($arr[$i]['data']) == 'BINARY'
  2175. && $typearr[3] == 'alpha_identifier') {
  2176. $after .= ' ';
  2177. }
  2178. break;
  2179. case 'alpha_functionName':
  2180. $funcname = strtoupper($arr[$i]['data']);
  2181. if ($docu && isset($PMA_SQPdata_functions_docs[$funcname])) {
  2182. $before .= PMA_showMySQLDocu(
  2183. 'functions',
  2184. $PMA_SQPdata_functions_docs[$funcname]['link'],
  2185. false,
  2186. $PMA_SQPdata_functions_docs[$funcname]['anchor'],
  2187. true);
  2188. $after .= '</a>';
  2189. }
  2190. break;
  2191. case 'alpha_reservedWord':
  2192. // do not uppercase the reserved word if we are calling
  2193. // this function in query_only mode, because we need
  2194. // the original query (otherwise we get problems with
  2195. // semi-reserved words like "storage" which is legal
  2196. // as an identifier name)
  2197. if ($mode != 'query_only') {
  2198. $arr[$i]['data'] = strtoupper($arr[$i]['data']);
  2199. }
  2200. if ((($typearr[1] != 'alpha_reservedWord')
  2201. || (($typearr[1] == 'alpha_reservedWord')
  2202. && isset($keywords_no_newline[strtoupper($arr[$i - 1]['data'])])))
  2203. && ($typearr[1] != 'punct_level_plus')
  2204. && (!isset($keywords_no_newline[$arr[$i]['data']]))) {
  2205. // do not put a space before the first token, because
  2206. // we use a lot of pattern matching checking for the
  2207. // first reserved word at beginning of query
  2208. // so do not put a newline before
  2209. //
  2210. // also we must not be inside a privilege list
  2211. if ($i > 0) {
  2212. // the alpha_identifier exception is there to
  2213. // catch cases like
  2214. // GRANT SELECT ON mydb.mytable TO myuser@localhost
  2215. // (else, we get mydb.mytableTO)
  2216. //
  2217. // the quote_single exception is there to
  2218. // catch cases like
  2219. // GRANT ... TO 'marc'@'domain.com' IDENTIFIED...
  2220. /**
  2221. * @todo fix all cases and find why this happens
  2222. */
  2223. if (!$in_priv_list || $typearr[1] == 'alpha_identifier' || $typearr[1] == 'quote_single' || $typearr[1] == 'white_newline') {
  2224. $before .= $space_alpha_reserved_word;
  2225. }
  2226. } else {
  2227. // on first keyword, check if it introduces a
  2228. // privilege list
  2229. if (isset($keywords_priv_list[$arr[$i]['data']])) {
  2230. $in_priv_list = true;
  2231. }
  2232. }
  2233. } else {
  2234. $before .= ' ';
  2235. }
  2236. switch ($arr[$i]['data']) {
  2237. case 'CREATE':
  2238. case 'ALTER':
  2239. case 'DROP':
  2240. case 'RENAME';
  2241. case 'TRUNCATE':
  2242. case 'ANALYZE':
  2243. case 'ANALYSE':
  2244. case 'OPTIMIZE':
  2245. if ($docu) {
  2246. switch ($arr[$i + 1]['data']) {
  2247. case 'EVENT':
  2248. case 'TABLE':
  2249. case 'TABLESPACE':
  2250. case 'FUNCTION':
  2251. case 'INDEX':
  2252. case 'PROCEDURE':
  2253. case 'TRIGGER':
  2254. case 'SERVER':
  2255. case 'DATABASE':
  2256. case 'VIEW':
  2257. $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'] . '_' . $arr[$i + 1]['data'], false, '', true);
  2258. $close_docu_link = true;
  2259. break;
  2260. }
  2261. if ($arr[$i + 1]['data'] == 'LOGFILE' && $arr[$i + 2]['data'] == 'GROUP') {
  2262. $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'] . '_LOGFILE_GROUP', false, '', true);
  2263. $close_docu_link = true;
  2264. }
  2265. }
  2266. if (!$in_priv_list) {
  2267. $space_punct_listsep = $html_line_break;
  2268. $space_alpha_reserved_word = ' ';
  2269. }
  2270. break;
  2271. case 'EVENT':
  2272. case 'TABLESPACE':
  2273. case 'TABLE':
  2274. case 'FUNCTION':
  2275. case 'INDEX':
  2276. case 'PROCEDURE':
  2277. case 'SERVER':
  2278. case 'TRIGGER':
  2279. case 'DATABASE':
  2280. case 'VIEW':
  2281. case 'GROUP':
  2282. if ($close_docu_link) {
  2283. $after = '</a>' . $after;
  2284. $close_docu_link = false;
  2285. }
  2286. break;
  2287. case 'SET':
  2288. if ($docu && ($i == 0 || $arr[$i - 1]['data'] != 'CHARACTER')) {
  2289. $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
  2290. $after = '</a>' . $after;
  2291. }
  2292. if (!$in_priv_list) {
  2293. $space_punct_listsep = $html_line_break;
  2294. $space_alpha_reserved_word = ' ';
  2295. }
  2296. break;
  2297. case 'EXPLAIN':
  2298. case 'DESCRIBE':
  2299. case 'DELETE':
  2300. case 'SHOW':
  2301. case 'UPDATE':
  2302. if ($docu) {
  2303. $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
  2304. $after = '</a>' . $after;
  2305. }
  2306. if (!$in_priv_list) {
  2307. $space_punct_listsep = $html_line_break;
  2308. $space_alpha_reserved_word = ' ';
  2309. }
  2310. break;
  2311. case 'INSERT':
  2312. case 'REPLACE':
  2313. if ($docu) {
  2314. $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
  2315. $after = '</a>' . $after;
  2316. }
  2317. if (!$in_priv_list) {
  2318. $space_punct_listsep = $html_line_break;
  2319. $space_alpha_reserved_word = $html_line_break;
  2320. }
  2321. break;
  2322. case 'VALUES':
  2323. $space_punct_listsep = ' ';
  2324. $space_alpha_reserved_word = $html_line_break;
  2325. break;
  2326. case 'SELECT':
  2327. if ($docu) {
  2328. $before .= PMA_showMySQLDocu('SQL-Syntax', 'SELECT', false, '', true);
  2329. $after = '</a>' . $after;
  2330. }
  2331. $space_punct_listsep = ' ';
  2332. $space_alpha_reserved_word = $html_line_break;
  2333. break;
  2334. case 'CALL':
  2335. case 'DO':
  2336. case 'HANDLER':
  2337. if ($docu) {
  2338. $before .= PMA_showMySQLDocu('SQL-Syntax', $arr[$i]['data'], false, '', true);
  2339. $after = '</a>' . $after;
  2340. }
  2341. break;
  2342. default:
  2343. if ($close_docu_link && in_array($arr[$i]['data'], array('LIKE', 'NOT', 'IN', 'REGEXP', 'NULL'))) {
  2344. $after .= '</a>';
  2345. $close_docu_link = false;
  2346. } else if ($docu && isset($PMA_SQPdata_functions_docs[$arr[$i]['data']])) {
  2347. /* Handle multi word statements first */
  2348. 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'])])) {
  2349. $tempname = strtoupper($arr[$i]['data'] . '_' . $arr[$i + 1]['data'] . '_' . $arr[$i + 2]['data']);
  2350. $before .= PMA_showMySQLDocu('functions', $PMA_SQPdata_functions_docs[$tempname]['link'], false, $PMA_SQPdata_functions_docs[$tempname]['anchor'], true);
  2351. $close_docu_link = true;
  2352. } else if (isset($typearr[3]) && $typearr[3] == 'alpha_reservedWord' && isset($PMA_SQPdata_functions_docs[strtoupper($arr[$i]['data'] . '_' . $arr[$i + 1]['data'])])) {
  2353. $tempname = strtoupper($arr[$i]['data'] . '_' . $arr[$i + 1]['data']);
  2354. $before .= PMA_showMySQLDocu('functions', $PMA_SQPdata_functions_docs[$tempname]['link'], false, $PMA_SQPdata_functions_docs[$tempname]['anchor'], true);
  2355. $close_docu_link = true;
  2356. } else {
  2357. $before .= PMA_showMySQLDocu('functions', $PMA_SQPdata_functions_docs[$arr[$i]['data']]['link'], false, $PMA_SQPdata_functions_docs[$arr[$i]['data']]['anchor'], true);
  2358. $after .= '</a>';
  2359. }
  2360. }
  2361. break;
  2362. } // end switch ($arr[$i]['data'])
  2363. $after .= ' ';
  2364. break;
  2365. case 'digit_integer':
  2366. case 'digit_float':
  2367. case 'digit_hex':
  2368. /**
  2369. * @todo could there be other types preceding a digit?
  2370. */
  2371. if ($typearr[1] == 'alpha_reservedWord') {
  2372. $after .= ' ';
  2373. }
  2374. if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
  2375. $after .= ' ';
  2376. }
  2377. if ($typearr[1] == 'alpha_columnAttrib') {
  2378. $before .= ' ';
  2379. }
  2380. break;
  2381. case 'alpha_variable':
  2382. $after = ' ';
  2383. break;
  2384. case 'quote_double':
  2385. case 'quote_single':
  2386. // workaround: for the query
  2387. // REVOKE SELECT ON `base2\_db`.* FROM 'user'@'%'
  2388. // the @ is incorrectly marked as alpha_variable
  2389. // in the parser, and here, the '%' gets a blank before,
  2390. // which is a syntax error
  2391. if ($typearr[1] != 'punct_user' && $typearr[1] != 'alpha_bitfield_constant_introducer') {
  2392. $before .= ' ';
  2393. }
  2394. if ($infunction && $typearr[3] == 'punct_bracket_close_round') {
  2395. $after .= ' ';
  2396. }
  2397. break;
  2398. case 'quote_backtick':
  2399. // here we check for punct_user to handle correctly
  2400. // DEFINER = `username`@`%`
  2401. // where @ is the punct_user and `%` is the quote_backtick
  2402. if ($typearr[3] != 'punct_qualifier' && $typearr[3] != 'alpha_variable' && $typearr[3] != 'punct_user') {
  2403. $after .= ' ';
  2404. }
  2405. if ($typearr[1] != 'punct_qualifier' && $typearr[1] != 'alpha_variable' && $typearr[1] != 'punct_user') {
  2406. $before .= ' ';
  2407. }
  2408. break;
  2409. default:
  2410. break;
  2411. } // end switch ($typearr[2])
  2412. /*
  2413. if ($typearr[3] != 'punct_qualifier') {
  2414. $after .= ' ';
  2415. }
  2416. $after .= "\n";
  2417. */
  2418. $str .= $before;
  2419. if ($mode=='color') {
  2420. $str .= PMA_SQP_formatHTML_colorize($arr[$i]);
  2421. } elseif ($mode == 'text') {
  2422. $str .= htmlspecialchars($arr[$i]['data']);
  2423. } else {
  2424. $str .= $arr[$i]['data'];
  2425. }
  2426. $str .= $after;
  2427. } // end for
  2428. // close unclosed indent levels
  2429. while ($indent > 0) {
  2430. $indent--;
  2431. $str .= ($mode != 'query_only' ? '</div>' : ' ');
  2432. }
  2433. /* End possibly unclosed documentation link */
  2434. if ($close_docu_link) {
  2435. $str .= '</a>';
  2436. $close_docu_link = false;
  2437. }
  2438. if ($mode!='query_only') {
  2439. // close inner_sql span
  2440. $str .= '</span>';
  2441. }
  2442. if ($mode=='color') {
  2443. // close syntax span
  2444. $str .= '</span>';
  2445. }
  2446. return $str;
  2447. } // end of the "PMA_SQP_formatHtml()" function
  2448. }
  2449. /**
  2450. * Builds a CSS rule used for html formatted SQL queries
  2451. *
  2452. * @param string The class name
  2453. * @param string The property name
  2454. * @param string The property value
  2455. *
  2456. * @return string The CSS rule
  2457. *
  2458. * @access public
  2459. *
  2460. * @see PMA_SQP_buildCssData()
  2461. */
  2462. function PMA_SQP_buildCssRule($classname, $property, $value)
  2463. {
  2464. $str = '.' . $classname . ' {';
  2465. if ($value != '') {
  2466. $str .= $property . ': ' . $value . ';';
  2467. }
  2468. $str .= '}' . "\n";
  2469. return $str;
  2470. } // end of the "PMA_SQP_buildCssRule()" function
  2471. /**
  2472. * Builds CSS rules used for html formatted SQL queries
  2473. *
  2474. * @return string The CSS rules set
  2475. *
  2476. * @access public
  2477. *
  2478. * @global array The current PMA configuration
  2479. *
  2480. * @see PMA_SQP_buildCssRule()
  2481. */
  2482. function PMA_SQP_buildCssData()
  2483. {
  2484. global $cfg;
  2485. $css_string = '';
  2486. foreach ($cfg['SQP']['fmtColor'] AS $key => $col) {
  2487. $css_string .= PMA_SQP_buildCssRule('syntax_' . $key, 'color', $col);
  2488. }
  2489. for ($i = 0; $i < 8; $i++) {
  2490. $css_string .= PMA_SQP_buildCssRule(
  2491. 'syntax_indent' . $i, 'margin-left',
  2492. ($i * $cfg['SQP']['fmtInd']) . $cfg['SQP']['fmtIndUnit']);
  2493. }
  2494. return $css_string;
  2495. } // end of the "PMA_SQP_buildCssData()" function
  2496. if (! defined('PMA_MINIMUM_COMMON')) {
  2497. /**
  2498. * Gets SQL queries with no format
  2499. *
  2500. * @param array The SQL queries list
  2501. *
  2502. * @return string The SQL queries with no format
  2503. *
  2504. * @access public
  2505. */
  2506. function PMA_SQP_formatNone($arr)
  2507. {
  2508. $formatted_sql = htmlspecialchars($arr['raw']);
  2509. $formatted_sql = preg_replace(
  2510. "@((\015\012)|(\015)|(\012)){3,}@",
  2511. "\n\n",
  2512. $formatted_sql);
  2513. return $formatted_sql;
  2514. } // end of the "PMA_SQP_formatNone()" function
  2515. } // end if: minimal common.lib needed?
  2516. ?>