PageRenderTime 56ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/include/php-sql-parser.php

https://bitbucket.org/cviolette/sugarcrm
PHP | 1979 lines | 1705 code | 172 blank | 102 comment | 250 complexity | 4fd316e1251bd1134a46134ad7ddaf57 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. <?php
  2. /**
  3. * SQL Parser from: http://code.google.com/p/php-sql-parser/
  4. * License: New BSD
  5. */
  6. class PHPSQLParser
  7. {
  8. var $reserved = array();
  9. var $functions = array();
  10. function __construct($sql = false) {
  11. #LOAD THE LIST OF RESERVED WORDS
  12. $this->load_reserved_words();
  13. if($sql) $this->parse($sql);
  14. }
  15. function parse($sql) {
  16. $sql = trim($sql);
  17. #lex the SQL statement
  18. $in = $this->split_sql($sql);
  19. #sometimes the parser needs to skip ahead until a particular
  20. #token is found
  21. $skip_until = false;
  22. #this is the output tree which is being parsed
  23. $out = array();
  24. #This is the last type of union used (UNION or UNION ALL)
  25. #indicates a) presence of at least one union in this query
  26. # b) the type of union if this is the first or last query
  27. $union = false;
  28. #Sometimes a "query" consists of more than one query (like a UNION query)
  29. #this array holds all the queries
  30. $queries=array();
  31. #This is the highest level lexical analysis. This is the part of the
  32. #code which finds UNION and UNION ALL query parts
  33. foreach($in as $key => $token) {
  34. $token=trim($token);
  35. if($skip_until) {
  36. if($token) {
  37. if(strtoupper($token) == $skip_until) {
  38. $skip_until = false;
  39. continue;
  40. }
  41. } else {
  42. continue;
  43. }
  44. }
  45. if(strtoupper($token) == "UNION") {
  46. $union = 'UNION';
  47. for($i=$key+1;$i<count($in);++$i) {
  48. if(trim($in[$i]) == '') continue;
  49. if(strtoupper($in[$i]) == 'ALL') {
  50. $skip_until = 'ALL';
  51. $union = 'UNION ALL';
  52. continue ;
  53. } else {
  54. break;
  55. }
  56. }
  57. $queries[$union][] = $out;
  58. $out = array();
  59. } else {
  60. $out[]=$token;
  61. }
  62. }
  63. if(!empty($out)) {
  64. if ($union) {
  65. $queries[$union][] = $out;
  66. } else {
  67. $queries[] = $out;
  68. }
  69. }
  70. /*MySQL supports a special form of UNION:
  71. (select ...)
  72. union
  73. (select ...)
  74. This block handles this query syntax. Only one such subquery
  75. is supported in each UNION block. (select)(select)union(select) is not legal.
  76. The extra queries will be silently ignored.
  77. */
  78. $union_types = array('UNION','UNION ALL');
  79. foreach($union_types as $union_type) {
  80. if(!empty($queries[$union_type])) {
  81. foreach($queries[$union_type] as $i => $tok_list) {
  82. foreach($tok_list as $z => $tok) {
  83. $tok = trim($tok);
  84. if(!$tok) continue;
  85. if(preg_match('/^\\(\\s*select\\s*/i', $tok)) {
  86. $queries[$union_type][$i] = $this->parse(substr($tok,1,-1));
  87. break;
  88. } else {
  89. $queries[$union_type][$i] = $this->process_sql($queries[$union_type][$i]);
  90. break;
  91. }
  92. }
  93. }
  94. }
  95. }
  96. /* If there was no UNION or UNION ALL in the query, then the query is
  97. stored at $queries[0].
  98. */
  99. if(!empty($queries[0])) {
  100. $queries[0] = $this->process_sql($queries[0]);
  101. }
  102. if(count($queries) == 1 && !$union) {
  103. $queries = $queries[0];
  104. }
  105. $this->parsed = $queries;
  106. return $this->parsed;
  107. }
  108. #This function counts open and close parenthesis and
  109. #returns their location. This might be faster as a regex
  110. private function count_paren($token,$chars=array('(',')')) {
  111. $len = strlen($token);
  112. $open=array();
  113. $close=array();
  114. for($i=0;$i<$len;++$i){
  115. if($token[$i] == $chars[0]) {
  116. $open[] = $i;
  117. } elseif($token[$i] == $chars[1]) {
  118. $close[] = $i;
  119. }
  120. }
  121. return array('open' => $open, 'close' => $close, 'balanced' =>( count($close) - count($open)));
  122. }
  123. #This function counts open and close parenthesis and
  124. #returns their location. This might be faster as a regex
  125. private function count_backtick($token) {
  126. $len = strlen($token);
  127. $cnt=0;
  128. for($i=0;$i<$len;++$i){
  129. if($token[$i] == '`') ++$cnt;
  130. }
  131. return $cnt;
  132. }
  133. #This is the lexer
  134. #this function splits up a SQL statement into easy to "parse"
  135. #tokens for the SQL processor
  136. private function split_sql($sql) {
  137. if(!is_string($sql)) {
  138. return false;
  139. }
  140. $sql = str_replace(array('\\\'','\\"',"\r\n","\n","()"),array("''",'""'," "," "," "), $sql);
  141. $regex=<<<EOREGEX
  142. /(`(?:[^`]|``)`|[@A-Za-z0-9_.`-]+(?:\(\s*\)){0,1})
  143. |(\+|-|\*|\/|!=|>=|<=|<>|>|<|&&|\|\||=|\^)
  144. |(\(.*?\)) # Match FUNCTION(...) OR BAREWORDS
  145. |('(?:[^']|'')*'+)
  146. |("(?:[^"]|"")*"+)
  147. |([^ ,]+)
  148. /ix
  149. EOREGEX
  150. ;
  151. $tokens = preg_split($regex, $sql,-1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
  152. $token_count = count($tokens);
  153. /* The above regex has one problem, because the parenthetical match is not greedy.
  154. Thus, when matching grouped expressions such as ( (a and b) or c) the
  155. tokenizer will produce "( (a and b)", " ", "or", " " , "c,")"
  156. This block detects the number of open/close parentheses in the given token. If the parentheses are balanced
  157. (balanced == 0) then we don't need to do anything.
  158. otherwise, we need to balance the expression.
  159. */
  160. $reset = false;
  161. for($i=0;$i<$token_count;++$i) {
  162. if(empty($tokens[$i])) continue;
  163. $token = $tokens[$i];
  164. $trim = trim($token);
  165. if($trim) {
  166. if($trim[0] != '('
  167. && substr($trim,-1) == ')') {
  168. $trim=trim(substr($trim,0,
  169. strpos($trim,'(')));
  170. }
  171. $tokens[$i]=$trim;
  172. $token=$trim;
  173. }
  174. if($token && $token[0] == '(') {
  175. $info = $this->count_paren($token);
  176. if($info['balanced'] == 0) {
  177. continue;
  178. }
  179. // We need to find this many closing parentheses.
  180. $needed = abs($info['balanced']);
  181. $n = $i;
  182. while($needed > 0 && $n <$token_count-1) {
  183. ++$n;
  184. #echo "LOOKING FORWARD TO $n [ " . $tokens[$n] . "]\n";
  185. $token2 = $tokens[$n];
  186. $info2 = $this->count_paren($token2);
  187. $closes = count($info2['close']);
  188. if($closes != $needed) {
  189. $tokens[$i] .= $tokens[$n];
  190. unset($tokens[$n]);
  191. $reset = true;
  192. $info2 = $this->count_paren($tokens[$i]);
  193. $needed = abs($info2['balanced']);
  194. # echo "CLOSES LESS THAN NEEDED (still need $needed)\n";
  195. } else {
  196. /*get the string pos of the last close parenthesis we need*/
  197. $pos = $info2['close'][count($info2['close'])-1];
  198. $str1 = $str2 = "";
  199. if($pos == 0) {
  200. $str1 = ')';
  201. } else {
  202. $str1 = substr($tokens[$n],0,$pos) . ')';
  203. $str2 = substr($tokens[$n],$pos+1);
  204. }
  205. #echo "CLOSES FOUND AT $n, offset:$pos [$str1] [$str2]\n";
  206. if(strlen($str2) > 0) {
  207. $tokens[$n] = $str2;
  208. } else {
  209. unset($tokens[$n]);
  210. $reset = true;
  211. }
  212. $tokens[$i] .= $str1;
  213. $info2 = $this->count_paren($tokens[$i]);
  214. $needed = abs($info2['balanced']);
  215. }
  216. }
  217. }
  218. }
  219. #the same problem appears with backticks :(
  220. /* reset the array if we deleted any tokens above */
  221. if ($reset) $tokens = array_values($tokens);
  222. $token_count=count($tokens);
  223. for($i=0;$i<$token_count;++$i) {
  224. if(empty($tokens[$i])) continue;
  225. $token=$tokens[$i];
  226. $needed=true;
  227. $reset=false;
  228. if($needed && $token && strpos($token,'`') !== false) {
  229. $info = $this->count_backtick($token);
  230. if($info %2 == 0) { #even number of backticks means we are balanced
  231. continue;
  232. }
  233. $needed=1;
  234. $n = $i;
  235. while($needed && $n <$token_count-1) {
  236. $reset=true;
  237. #echo "BACKTICK COUNT[$i]: $info old: {$tokens[$i]}, new: ($token)\n";
  238. ++$n;
  239. $token .= $tokens[$n];
  240. unset($tokens[$n]);
  241. $needed = $this->count_backtick($token) % 2;
  242. }
  243. }
  244. if($reset) $tokens[$i] = $token;
  245. }
  246. /* reset the array if we deleted any tokens above */
  247. $tokens = array_values($tokens);
  248. return $tokens;
  249. }
  250. /* This function breaks up the SQL statement into logical sections.
  251. Some sections are then further handled by specialized functions.
  252. */
  253. private function process_sql(&$tokens,$start_at = 0, $stop_at = false) {
  254. $prev_category = "";
  255. $start = microtime(true);
  256. $token_category = "";
  257. $skip_next=false;
  258. $token_count = count($tokens);
  259. if(!$stop_at) {
  260. $stop_at = $token_count;
  261. }
  262. $out = false;
  263. for($token_number = $start_at;$token_number<$stop_at;++$token_number) {
  264. $token = trim($tokens[$token_number]);
  265. if($token && $token[0] == '(' && $token_category == "") {
  266. $token_category = 'SELECT';
  267. }
  268. /* If it isn't obvious, when $skip_next is set, then we ignore the next real
  269. token, that is we ignore whitespace.
  270. */
  271. if($skip_next) {
  272. #whitespace does not count as a next token
  273. if($token == "") {
  274. continue;
  275. }
  276. #to skip the token we replace it with whitespace
  277. $new_token = "";
  278. $skip_next = false;
  279. }
  280. $upper = strtoupper($token);
  281. switch($upper) {
  282. /* Tokens that get their own sections. These keywords have subclauses. */
  283. case 'SELECT':
  284. case 'ORDER':
  285. case 'LIMIT':
  286. case 'SET':
  287. case 'DUPLICATE':
  288. case 'VALUES':
  289. case 'GROUP':
  290. case 'ORDER':
  291. case 'HAVING':
  292. case 'INTO':
  293. case 'WHERE':
  294. case 'RENAME':
  295. case 'CALL':
  296. case 'PROCEDURE':
  297. case 'FUNCTION':
  298. case 'DATABASE':
  299. case 'SERVER':
  300. case 'LOGFILE':
  301. case 'DEFINER':
  302. case 'RETURNS':
  303. case 'EVENT':
  304. case 'TABLESPACE':
  305. case 'VIEW':
  306. case 'TRIGGER':
  307. case 'DATA':
  308. case 'DO':
  309. case 'PASSWORD':
  310. case 'USER':
  311. case 'PLUGIN':
  312. case 'FROM':
  313. case 'FLUSH':
  314. case 'KILL':
  315. case 'RESET':
  316. case 'START':
  317. case 'STOP':
  318. case 'PURGE':
  319. case 'EXECUTE':
  320. case 'PREPARE':
  321. case 'DEALLOCATE':
  322. if($token == 'DEALLOCATE') {
  323. $skip_next = true;
  324. }
  325. /* this FROM is different from FROM in other DML (not join related) */
  326. if($token_category == 'PREPARE' && $upper == 'FROM') {
  327. continue 2;
  328. }
  329. $token_category = $upper;
  330. #$join_type = 'JOIN';
  331. if($upper == 'FROM' && $token_category == 'FROM') {
  332. /* DO NOTHING*/
  333. } else {
  334. continue 2;
  335. }
  336. break;
  337. /* These tokens get their own section, but have no subclauses.
  338. These tokens identify the statement but have no specific subclauses of their own. */
  339. case 'DELETE':
  340. case 'ALTER':
  341. case 'INSERT':
  342. case 'REPLACE':
  343. case 'TRUNCATE':
  344. case 'CREATE':
  345. case 'TRUNCATE':
  346. case 'OPTIMIZE':
  347. case 'GRANT':
  348. case 'REVOKE':
  349. case 'SHOW':
  350. case 'HANDLER':
  351. case 'LOAD':
  352. case 'ROLLBACK':
  353. case 'SAVEPOINT':
  354. case 'UNLOCK':
  355. case 'INSTALL':
  356. case 'UNINSTALL':
  357. case 'ANALZYE':
  358. case 'BACKUP':
  359. case 'CHECK':
  360. case 'CHECKSUM':
  361. case 'REPAIR':
  362. case 'RESTORE':
  363. case 'CACHE':
  364. case 'DESCRIBE':
  365. case 'EXPLAIN':
  366. case 'USE':
  367. case 'HELP':
  368. $token_category = $upper; /* set the category in case these get subclauses
  369. in a future version of MySQL */
  370. $out[$upper][0] = $upper;
  371. continue 2;
  372. break;
  373. /* This is either LOCK TABLES or SELECT ... LOCK IN SHARE MODE*/
  374. case 'LOCK':
  375. if($token_category == "") {
  376. $token_category = $upper;
  377. $out[$upper][0] = $upper;
  378. } else {
  379. $token = 'LOCK IN SHARE MODE';
  380. $skip_next=true;
  381. $out['OPTIONS'][] = $token;
  382. }
  383. continue 2;
  384. break;
  385. case 'USING':
  386. /* USING in FROM clause is different from USING w/ prepared statement*/
  387. if($token_category == 'EXECUTE') {
  388. $token_category=$upper;
  389. continue 2;
  390. }
  391. if($token_category == 'FROM' && !empty($out['DELETE'])) {
  392. $token_category=$upper;
  393. continue 2;
  394. }
  395. break;
  396. /* DROP TABLE is different from ALTER TABLE DROP ... */
  397. case 'DROP':
  398. if($token_category != 'ALTER') {
  399. $token_category = $upper;
  400. $out[$upper][0] = $upper;
  401. continue 2;
  402. }
  403. break;
  404. case 'FOR':
  405. $skip_next=true;
  406. $out['OPTIONS'][] = 'FOR UPDATE';
  407. continue 2;
  408. break;
  409. case 'UPDATE':
  410. if($token_category == "" ) {
  411. $token_category = $upper;
  412. continue 2;
  413. }
  414. if($token_category == 'DUPLICATE') {
  415. continue 2;
  416. }
  417. break;
  418. break;
  419. case 'START':
  420. $token = "BEGIN";
  421. $out[$upper][0] = $upper;
  422. $skip_next = true;
  423. break;
  424. /* These tokens are ignored. */
  425. case 'BY':
  426. case 'ALL':
  427. case 'SHARE':
  428. case 'MODE':
  429. case 'TO':
  430. case ';':
  431. continue 2;
  432. break;
  433. case 'KEY':
  434. if($token_category == 'DUPLICATE') {
  435. continue 2;
  436. }
  437. break;
  438. /* These tokens set particular options for the statement. They never stand alone.*/
  439. case 'DISTINCTROW':
  440. $token='DISTINCT';
  441. case 'DISTINCT':
  442. case 'HIGH_PRIORITY':
  443. case 'LOW_PRIORITY':
  444. case 'DELAYED':
  445. case 'IGNORE':
  446. case 'FORCE':
  447. case 'STRAIGHT_JOIN':
  448. case 'SQL_SMALL_RESULT':
  449. case 'SQL_BIG_RESULT':
  450. case 'QUICK':
  451. case 'SQL_BUFFER_RESULT':
  452. case 'SQL_CACHE':
  453. case 'SQL_NO_CACHE':
  454. case 'SQL_CALC_FOUND_ROWS':
  455. $out['OPTIONS'][] = $upper;
  456. continue 2;
  457. break;
  458. case 'WITH':
  459. if($token_category == 'GROUP') {
  460. $skip_next=true;
  461. $out['OPTIONS'][] = 'WITH ROLLUP';
  462. continue 2;
  463. }
  464. break;
  465. case 'AS':
  466. break;
  467. case '':
  468. case ',':
  469. case ';':
  470. break;
  471. default:
  472. break;
  473. }
  474. if($prev_category == $token_category) {
  475. $out[$token_category][] = $token;
  476. }
  477. $prev_category = $token_category;
  478. }
  479. if(!$out) return false;
  480. #process the SELECT clause
  481. if(!empty($out['SELECT'])) $out['SELECT'] = $this->process_select($out['SELECT']);
  482. if(!empty($out['FROM'])) $out['FROM'] = $this->process_from($out['FROM']);
  483. if(!empty($out['USING'])) $out['USING'] = $this->process_from($out['USING']);
  484. if(!empty($out['UPDATE'])) $out['UPDATE'] = $this->process_from($out['UPDATE']);
  485. if(!empty($out['GROUP'])) $out['GROUP'] = $this->process_group($out['GROUP'], $out['SELECT']);
  486. if(!empty($out['ORDER'])) $out['ORDER'] = $this->process_group($out['ORDER'], $out['SELECT']);
  487. if(!empty($out['LIMIT'])) $out['LIMIT'] = $this->process_limit($out['LIMIT']);
  488. if(!empty($out['WHERE'])) $out['WHERE'] = $this->process_expr_list($out['WHERE']);
  489. if(!empty($out['HAVING'])) $out['HAVING'] = $this->process_expr_list($out['HAVING']);
  490. if(!empty($out['SET'])) $out['SET'] = $this->process_set_list($out['SET']);
  491. if(!empty($out['DUPLICATE'])) {
  492. $out['ON DUPLICATE KEY UPDATE'] = $this->process_set_list($out['DUPLICATE']);
  493. unset($out['DUPLICATE']);
  494. }
  495. if(!empty($out['INSERT'])) $out = $this->process_insert($out);
  496. if(!empty($out['REPLACE'])) $out = $this->process_insert($out,'REPLACE');
  497. if(!empty($out['DELETE'])) $out = $this->process_delete($out);
  498. return $out;
  499. }
  500. /* A SET list is simply a list of key = value expressions separated by comma (,).
  501. This function produces a list of the key/value expressions.
  502. */
  503. private function process_set_list($tokens) {
  504. $column="";
  505. $expression="";
  506. foreach($tokens as $token) {
  507. $token=trim($token);
  508. if(!$column) {
  509. if($token === false || empty($token)) continue;
  510. $column .= $token;
  511. continue;
  512. }
  513. if($token == '=') continue;
  514. if($token == ',') {
  515. $expr[] = array('column' => trim($column), 'expr' => trim($expression));
  516. $expression = $column = "";
  517. continue;
  518. }
  519. $expression .= $token;
  520. }
  521. if($expression) {
  522. $expr[] = array('column' => trim($column), 'expr' => trim($expression));
  523. }
  524. return $expr;
  525. }
  526. /* This function processes the LIMIT section.
  527. start,end are set. If only end is provided in the query
  528. then start is set to 0.
  529. */
  530. private function process_limit($tokens) {
  531. $start = 0;
  532. $end = 0;
  533. if($pos = array_search(',',$tokens)) {
  534. for($i=0;$i<$pos;++$i) {
  535. if($tokens[$i] != '') {
  536. $start = $tokens[$i];
  537. break;
  538. }
  539. }
  540. $pos = $pos + 1;
  541. } else {
  542. $pos = 0;
  543. }
  544. for($i=$pos;$i<count($tokens);++$i) {
  545. if($tokens[$i] != '') {
  546. $end = $tokens[$i];
  547. break;
  548. }
  549. }
  550. return array('start' => $start, 'end' => $end);
  551. }
  552. /* This function processes the SELECT section. It splits the clauses at the commas.
  553. Each clause is then processed by process_select_expr() and the results are added to
  554. the expression list.
  555. Finally, at the end, the expression list is returned.
  556. */
  557. private function process_select(&$tokens) {
  558. $expression = "";
  559. $expr = array();
  560. foreach($tokens as $token) {
  561. if($token == ',') {
  562. $expr[] = $this->process_select_expr(trim($expression));
  563. $expression = "";
  564. } else {
  565. if($token === "" || $token===false) $token=" ";
  566. $expression .= $token ;
  567. }
  568. }
  569. if($expression) $expr[] = $this->process_select_expr(trim($expression));
  570. return $expr;
  571. }
  572. /* This function processes each SELECT clause. We determine what (if any) alias
  573. is provided, and we set the type of expression.
  574. */
  575. private function process_select_expr($expression) {
  576. $capture = false;
  577. $alias = "";
  578. $base_expression = $expression;
  579. $upper = trim(strtoupper($expression));
  580. #if necessary, unpack the expression
  581. if($upper[0] == '(') {
  582. #$expression = substr($expression,1,-1);
  583. $base_expression = $expression;
  584. }
  585. $tokens = $this->split_sql($expression);
  586. $token_count = count($tokens);
  587. /* Determine if there is an explicit alias after the AS clause.
  588. If AS is found, then the next non-whitespace token is captured as the alias.
  589. The tokens after (and including) the AS are removed.
  590. */
  591. $base_expr = "";
  592. $stripped=array();
  593. $capture=false;
  594. $alias = "";
  595. $processed=false;
  596. for($i=0;$i<$token_count;++$i) {
  597. $token = strtoupper($tokens[$i]);
  598. if(trim($token)) {
  599. $stripped[] = $tokens[$i];
  600. }
  601. if($token == 'AS') {
  602. unset($tokens[$i]);
  603. $capture = true;
  604. continue;
  605. }
  606. if($capture) {
  607. if(trim($token)) {
  608. $alias .= $tokens[$i];
  609. }
  610. unset($tokens[$i]);
  611. continue;
  612. }
  613. $base_expr .= $tokens[$i];
  614. }
  615. $stripped = $this->process_expr_list($stripped);
  616. $last = array_pop($stripped);
  617. if(!$alias && $last['expr_type'] == 'colref') {
  618. $prev = array_pop($stripped);
  619. if($prev['expr_type'] == 'operator' ||
  620. $prev['expr_type'] == 'const' ||
  621. $prev['expr_type'] == 'function' ||
  622. $prev['expr_type'] == 'expression' ||
  623. #$prev['expr_type'] == 'aggregate_function' ||
  624. $prev['expr_type'] == 'subquery' ||
  625. $prev['expr_type'] == 'colref') {
  626. $alias = $last['base_expr'];
  627. #remove the last token
  628. array_pop($tokens);
  629. $base_expr = join("", $tokens);
  630. }
  631. }
  632. if(!$alias) {
  633. $base_expr=join("", $tokens);
  634. $alias = $base_expr;
  635. }
  636. /* Properly escape the alias if it is not escaped */
  637. if ($alias[0] != '`') {
  638. $alias = '`' . str_replace('`','``',$alias) . '`';
  639. }
  640. $processed = false;
  641. $type='expression';
  642. if(substr(trim($base_expr),0,1) == '(') {
  643. $base_expr = substr($expression,1,-1);
  644. if(preg_match('/^sel/i', $base_expr)) {
  645. $type='subquery';
  646. $processed = $this->parse($base_expr);
  647. }
  648. }
  649. if(!$processed) {
  650. $processed = $this->process_expr_list($tokens);
  651. }
  652. if(count($processed) == 1) {
  653. $type = $processed[0]['expr_type'];
  654. $processed = false;
  655. }
  656. return array('expr_type'=>$type,'alias' => $alias, 'base_expr' => $base_expr, 'sub_tree' => $processed);
  657. }
  658. private function trimSubquery($sq)
  659. {
  660. $sq = trim($sq);
  661. if(empty($sq)) return '';
  662. while($sq[0] == '(' && substr($sq, -1) == ')') {
  663. $sq = substr($sq, 1, -1);
  664. }
  665. return $sq;
  666. }
  667. private function process_from(&$tokens) {
  668. $expression = "";
  669. $expr = array();
  670. $token_count=0;
  671. $table = "";
  672. $alias = "";
  673. $skip_next=false;
  674. $i=0;
  675. $join_type = '';
  676. $ref_type="";
  677. $ref_expr="";
  678. $base_expr="";
  679. $sub_tree = false;
  680. $subquery = "";
  681. $first_join=true;
  682. $modifier="";
  683. $saved_join_type="";
  684. foreach($tokens as $token) {
  685. $base_expr = false;
  686. $upper = strtoupper(trim($token));
  687. if($skip_next && $token) {
  688. $token_count++;
  689. $skip_next = false;
  690. continue;
  691. } else {
  692. if($skip_next) {
  693. continue;
  694. }
  695. }
  696. if(preg_match("/^\\s*\\(\\s*select/i",$token)) {
  697. $type = 'subquery';
  698. $table = "DEPENDENT-SUBQUERY";
  699. $sub_tree = $this->parse($this->trimSubquery($token));
  700. $subquery = $token;
  701. }
  702. switch($upper) {
  703. case 'OUTER':
  704. case 'LEFT':
  705. case 'RIGHT':
  706. case 'NATURAL':
  707. case 'CROSS':
  708. case ',':
  709. case 'JOIN':
  710. break;
  711. default:
  712. $expression .= $token == '' ? " " : $token;
  713. if($ref_type) {
  714. $ref_expr .= $token == '' ? " " : $token;
  715. }
  716. break;
  717. }
  718. switch($upper) {
  719. case 'AS':
  720. $token_count++;
  721. $n=1;
  722. $alias = "";
  723. while($alias == "") {
  724. $alias = trim($tokens[$i+$n]);
  725. ++$n;
  726. }
  727. continue;
  728. break;
  729. case 'INDEX':
  730. if($token_category == 'CREATE') {
  731. $token_category = $upper;
  732. continue 2;
  733. }
  734. break;
  735. case 'USING':
  736. case 'ON':
  737. $ref_type = $upper;
  738. $ref_expr = "";
  739. case 'CROSS':
  740. case 'USE':
  741. case 'FORCE':
  742. case 'IGNORE':
  743. case 'INNER':
  744. case 'OUTER':
  745. # $expression .= $token;
  746. $token_count++;
  747. continue;
  748. break;
  749. case 'FOR':
  750. $token_count++;
  751. $skip_next = true;
  752. continue;
  753. break;
  754. case 'LEFT':
  755. case 'RIGHT':
  756. case 'STRAIGHT_JOIN':
  757. $join_type=$saved_join_type;
  758. $modifier = $upper . " ";
  759. break;
  760. case ',':
  761. $modifier = 'CROSS';
  762. case 'JOIN':
  763. if($first_join) {
  764. $join_type = 'JOIN';
  765. $saved_join_type = ($modifier ? $modifier : 'JOIN');
  766. } else {
  767. $new_join_type = ($modifier ? $modifier : 'JOIN');
  768. $join_type = $saved_join_type;
  769. $saved_join_type = $new_join_type;
  770. unset($new_join_type);
  771. }
  772. $first_join = false;
  773. if(!trim($alias)) $alias = $table;
  774. if($subquery) {
  775. $sub_tree = $this->parse(trim($subquery,'()'));
  776. $base_expr=$subquery;
  777. }
  778. if(substr(trim($table),0,1) == '(') {
  779. $base_expr=$this->trimSubquery($table);
  780. $join_type = 'JOIN';
  781. $sub_tree = $this->process_from($this->split_sql($base_expr));
  782. $alias="";
  783. }
  784. if($join_type == "") $join_type='JOIN';
  785. $expr[] = array('table'=>$table, 'alias'=>$alias,'join_type'=>$join_type,'ref_type'=> $ref_type,'ref_clause'=>$this->trimSubquery($ref_expr), 'base_expr' => $base_expr, 'sub_tree' => $sub_tree);
  786. $modifier = "";
  787. #$join_type=$saved_join_type;
  788. $token_count = 0;
  789. $table = $alias = $expression = $base_expr = $ref_type = $ref_expr = "";
  790. $sub_tree=false;
  791. $subquery = "";
  792. break;
  793. default:
  794. if($token === false || empty($token) || $token === "") continue;
  795. if($token_count == 0 ) {
  796. if(!$table) {
  797. $table = $token ;
  798. }
  799. } else if($token_count == 1) {
  800. $alias = $token;
  801. }
  802. $token_count++;
  803. break;
  804. }
  805. ++$i;
  806. }
  807. if(substr(trim($table),0,1) == '(') {
  808. $base_expr=$this->trimSubquery($table);
  809. $join_type = 'JOIN';
  810. $sub_tree = $this->process_from($this->split_sql($base_expr));
  811. $alias = "";
  812. } else {
  813. if(!trim($alias)) $alias = $table;
  814. }
  815. if($join_type == "") $saved_join_type='JOIN';
  816. $expr[] = array('table'=>$table, 'alias'=>$alias,'join_type'=>$saved_join_type,'ref_type'=> $ref_type,'ref_clause'=> $this->trimSubquery($ref_expr), 'base_expr' => $base_expr, 'sub_tree' => $sub_tree);
  817. return $expr;
  818. }
  819. private function process_group(&$tokens, &$select) {
  820. $out=array();
  821. $expression = "";
  822. $direction="ASC";
  823. $type = "expression";
  824. if(!$tokens) return false;
  825. foreach($tokens as $token) {
  826. switch(strtoupper($token)) {
  827. case ',':
  828. $expression = trim($expression);
  829. if($expression[0] != '`' || substr($expression,-1) != '`') {
  830. $escaped = str_replace('`','``',$expression);
  831. } else {
  832. $escaped = $expression;
  833. }
  834. $escaped = '`' . $escaped . '`';
  835. if(is_numeric(trim($expression))) {
  836. $type = 'pos';
  837. } else {
  838. #search to see if the expression matches an alias
  839. foreach($select as $clause) {
  840. if($clause['alias'] == $escaped) {
  841. $type = 'alias';
  842. }
  843. }
  844. if(!$type) $type = "expression";
  845. }
  846. $out[]=array('type'=>$type,'base_expr'=>$expression,'direction'=>$direction);
  847. $escaped = "";
  848. $expression = "";
  849. $direction = "ASC";
  850. $type = "";
  851. break;
  852. case 'ASC':
  853. $direction = "ASC";
  854. break;
  855. case 'DESC':
  856. $direction = "DESC";
  857. break;
  858. default:
  859. $expression .= $token == '' ? ' ' : $token;
  860. }
  861. }
  862. if($expression) {
  863. $expression = trim($expression);
  864. if($expression[0] != '`' || substr($expression,-1) != '`') {
  865. $escaped = str_replace('`','``',$expression);
  866. } else {
  867. $escaped = $expression;
  868. }
  869. $escaped = '`' . $escaped . '`';
  870. if(is_numeric(trim($expression))) {
  871. $type = 'pos';
  872. } else {
  873. #search to see if the expression matches an alias
  874. if(!$type && $select) {
  875. foreach($select as $clause) {
  876. if(!is_array($clause)) continue;
  877. if($clause['alias'] == $escaped) {
  878. $type = 'alias';
  879. }
  880. }
  881. } else {
  882. $type="expression";
  883. }
  884. if(!$type) $type = "expression";
  885. }
  886. $out[]=array('type'=>$type,'base_expr'=>$expression,'direction'=>$direction);
  887. }
  888. foreach($out as &$term) {
  889. if(!empty($term['base_expr'])) {
  890. $term['sub_tree'] = array($this->process_select_expr($term['base_expr']));
  891. }
  892. }
  893. return $out;
  894. }
  895. /* Some sections are just lists of expressions, like the WHERE and HAVING clauses. This function
  896. processes these sections. Recursive.
  897. */
  898. private function process_expr_list($tokens) {
  899. $expr = "";
  900. $type = "";
  901. $prev_token = "";
  902. $skip_next = false;
  903. $sub_expr = "";
  904. $in_lists = array();
  905. foreach($tokens as $key => $token) {
  906. if(strlen(trim($token))==0) continue;
  907. if($skip_next) {
  908. $skip_next = false;
  909. continue;
  910. }
  911. $processed = false;
  912. $upper = strtoupper(trim($token));
  913. if(trim($token)) $token=trim($token);
  914. /* is it a subquery?*/
  915. if(preg_match("/^\\s*\\(\\s*SELECT/i", $token)) {
  916. $type = 'subquery';
  917. #tokenize and parse the subquery.
  918. #we remove the enclosing parenthesis for the tokenizer
  919. $processed = $this->parse($this->trimSubquery($token));
  920. /* is it an inlist */
  921. } elseif( $upper[0] == '(' && substr($upper,-1) == ')' ) {
  922. if($prev_token == 'IN') {
  923. $type = "in-list";
  924. $processed = $this->split_sql(substr($token,1,-1));
  925. $list = array();
  926. foreach($processed as $v) {
  927. if($v == ',') continue;
  928. $list[]=$v;
  929. }
  930. $processed = $list;
  931. unset($list);
  932. $prev_token = "";
  933. }
  934. elseif($prev_token == 'AGAINST') {
  935. $type = "match-arguments";
  936. $list = $this->split_sql(substr($token,1,-1));
  937. if(count($list) > 1){
  938. $match_mode = implode('',array_slice($list,1));
  939. $processed = array($list[0], $match_mode);
  940. }
  941. else
  942. $processed = $list[0];
  943. $prev_token = "";
  944. }
  945. /* it is either an operator, a colref or a constant */
  946. } else {
  947. switch($upper) {
  948. case 'AND':
  949. case '&&':
  950. case 'BETWEEN':
  951. case 'AND':
  952. case 'BINARY':
  953. case '&':
  954. case '~':
  955. case '|':
  956. case '^':
  957. case 'CASE':
  958. case 'WHEN':
  959. case 'END':
  960. case 'DIV':
  961. case '/':
  962. case '<=>':
  963. case '=':
  964. case '>=':
  965. case '>':
  966. case 'IS':
  967. case 'NOT':
  968. case 'NULL':
  969. case '<<':
  970. case '<=':
  971. case '<':
  972. case 'LIKE':
  973. case '-':
  974. case '%':
  975. case '!=':
  976. case '<>':
  977. case 'REGEXP':
  978. case '!':
  979. case '||':
  980. case 'OR':
  981. case '+':
  982. case '>>':
  983. case 'RLIKE':
  984. case 'SOUNDS':
  985. case '*':
  986. case '-':
  987. case 'XOR':
  988. case 'IN':
  989. $processed = false;
  990. $type = "operator";
  991. break;
  992. default:
  993. switch($token[0]) {
  994. case "'":
  995. case '"':
  996. $type = 'const';
  997. break;
  998. case '`':
  999. $type = 'colref';
  1000. break;
  1001. default:
  1002. if(is_numeric($token)) {
  1003. $type = 'const';
  1004. } else {
  1005. $type = 'colref';
  1006. }
  1007. break;
  1008. }
  1009. #$processed = $token;
  1010. $processed = false;
  1011. }
  1012. }
  1013. /* is a reserved word? */
  1014. if(($type != 'operator' && $type != 'in-list' && $type != 'sub_expr') && in_array($upper, $this->reserved)) {
  1015. $token = $upper;
  1016. if(!in_array($upper,$this->functions)) {
  1017. $type = 'reserved';
  1018. } else {
  1019. switch($token) {
  1020. case 'AVG':
  1021. case 'SUM':
  1022. case 'COUNT':
  1023. case 'MIN':
  1024. case 'MAX':
  1025. case 'STDDEV':
  1026. case 'STDDEV_SAMP':
  1027. case 'STDDEV_POP':
  1028. case 'VARIANCE':
  1029. case 'VAR_SAMP':
  1030. case 'VAR_POP':
  1031. case 'GROUP_CONCAT':
  1032. case 'BIT_AND':
  1033. case 'BIT_OR':
  1034. case 'BIT_XOR':
  1035. $type = 'aggregate_function';
  1036. if(!empty($tokens[$key+1])) $sub_expr = $tokens[$key+1];
  1037. #$skip_next=true;
  1038. break;
  1039. default:
  1040. $type = 'function';
  1041. if(!empty($tokens[$key+1])) $sub_expr = $tokens[$key+1]; else $sub_expr="()";
  1042. #$skip_next=true;
  1043. break;
  1044. }
  1045. }
  1046. }
  1047. if(!$type) {
  1048. if($upper[0] == '(') {
  1049. $local_expr = substr(trim($token),1,-1);
  1050. } else {
  1051. $local_expr = $token;
  1052. }
  1053. $processed = $this->process_expr_list($this->split_sql($local_expr));
  1054. $type = 'expression';
  1055. // if(count($processed) == 1) {
  1056. // $type = $processed[0]['expr_type'];
  1057. // $base_expr = $processed[0]['base_expr'];
  1058. // $processed = $processed[0]['sub_tree'];
  1059. // }
  1060. }
  1061. $sub_expr=trim($sub_expr);
  1062. $sub_expr = "";
  1063. $expr[] = array( 'expr_type' => $type, 'base_expr' => $token, 'sub_tree' => $processed);
  1064. $prev_token = $upper;
  1065. $expr_type = "";
  1066. $type = "";
  1067. }
  1068. if($sub_expr) {
  1069. $processed['sub_tree'] = $this->process_expr_list($this->split_sql(substr($sub_expr,1,-1)));
  1070. }
  1071. if(!is_array($processed)) {
  1072. $processed = false;
  1073. }
  1074. if($expr_type) {
  1075. $expr[] = array( 'expr_type' => $type, 'base_expr' => $token, 'sub_tree' => $processed);
  1076. }
  1077. $mod = false;
  1078. /*
  1079. for($i=0;$i<count($expr);++$i){
  1080. if($expr[$i]['expr_type'] == 'function' ||
  1081. $expr[$i]['expr_type'] == 'aggregate_function') {
  1082. if(!empty($expr[$i+1])) {
  1083. $expr[$i]['sub_tree']=$expr[$i+1]['sub_tree'];
  1084. unset($expr[$i+1]);
  1085. $mod = 1;
  1086. ++$i; // BAD FORM TO MODIFY THE LOOP COUNTER
  1087. }
  1088. }
  1089. }
  1090. */
  1091. if($mod) $expr=array_values($expr);
  1092. return $expr;
  1093. }
  1094. private function process_update($tokens) {
  1095. }
  1096. private function process_delete($tokens) {
  1097. $tables = array();
  1098. $del = $tokens['DELETE'];
  1099. foreach($tokens['DELETE'] as $expression) {
  1100. if ($expression != 'DELETE' && trim($expression,' .*') != "" && $expression != ',') {
  1101. $tables[] = trim($expression,'.* ');
  1102. }
  1103. }
  1104. if(empty($tables)) {
  1105. foreach($tokens['FROM'] as $table) {
  1106. $tables[] = $table['table'];
  1107. }
  1108. }
  1109. $tokens['DELETE'] = array('TABLES' => $tables);
  1110. return $tokens;
  1111. }
  1112. private function process_insert($tokens, $token_category = 'INSERT') {
  1113. $table = "";
  1114. $cols = "";
  1115. $into = $tokens['INTO'];
  1116. foreach($into as $token) {
  1117. if(!trim($token)) continue;
  1118. if(!$table) {
  1119. $table = $token;
  1120. }elseif(!$cols) {
  1121. $cols = $token;
  1122. }
  1123. }
  1124. if(!$cols) {
  1125. $cols = 'ALL';
  1126. } else {
  1127. $cols = explode(",", $this->trimSubquery($cols));
  1128. }
  1129. unset($tokens['INTO']);
  1130. $tokens[$token_category] = array('table'=>$table, 'cols'=>$cols);
  1131. return $tokens;
  1132. }
  1133. private function load_reserved_words() {
  1134. $this->functions = array(
  1135. 'abs',
  1136. 'acos',
  1137. 'adddate',
  1138. 'addtime',
  1139. 'aes_encrypt',
  1140. 'aes_decrypt',
  1141. 'against',
  1142. 'ascii',
  1143. 'asin',
  1144. 'atan',
  1145. 'avg',
  1146. 'benchmark',
  1147. 'bin',
  1148. 'bit_and',
  1149. 'bit_or',
  1150. 'bitcount',
  1151. 'bitlength',
  1152. 'cast',
  1153. 'ceiling',
  1154. 'char',
  1155. 'char_length',
  1156. 'character_length',
  1157. 'charset',
  1158. 'coalesce',
  1159. 'coercibility',
  1160. 'collation',
  1161. 'compress',
  1162. 'concat',
  1163. 'concat_ws',
  1164. 'conection_id',
  1165. 'conv',
  1166. 'convert',
  1167. 'convert_tz',
  1168. 'cos',
  1169. 'cot',
  1170. 'count',
  1171. 'crc32',
  1172. 'curdate',
  1173. 'current_user',
  1174. 'currval',
  1175. 'curtime',
  1176. 'database',
  1177. 'date_add',
  1178. 'date_diff',
  1179. 'date_format',
  1180. 'date_sub',
  1181. 'day',
  1182. 'dayname',
  1183. 'dayofmonth',
  1184. 'dayofweek',
  1185. 'dayofyear',
  1186. 'decode',
  1187. 'default',
  1188. 'degrees',
  1189. 'des_decrypt',
  1190. 'des_encrypt',
  1191. 'elt',
  1192. 'encode',
  1193. 'encrypt',
  1194. 'exp',
  1195. 'export_set',
  1196. 'extract',
  1197. 'field',
  1198. 'find_in_set',
  1199. 'floor',
  1200. 'format',
  1201. 'found_rows',
  1202. 'from_days',
  1203. 'from_unixtime',
  1204. 'get_format',
  1205. 'get_lock',
  1206. 'group_concat',
  1207. 'greatest',
  1208. 'hex',
  1209. 'hour',
  1210. 'if',
  1211. 'ifnull',
  1212. 'in',
  1213. 'inet_aton',
  1214. 'inet_ntoa',
  1215. 'insert',
  1216. 'instr',
  1217. 'interval',
  1218. 'is_free_lock',
  1219. 'is_used_lock',
  1220. 'last_day',
  1221. 'last_insert_id',
  1222. 'lcase',
  1223. 'least',
  1224. 'left',
  1225. 'length',
  1226. 'ln',
  1227. 'load_file',
  1228. 'localtime',
  1229. 'localtimestamp',
  1230. 'locate',
  1231. 'log',
  1232. 'log2',
  1233. 'log10',
  1234. 'lower',
  1235. 'lpad',
  1236. 'ltrim',
  1237. 'make_set',
  1238. 'makedate',
  1239. 'maketime',
  1240. 'master_pos_wait',
  1241. 'match',
  1242. 'max',
  1243. 'md5',
  1244. 'microsecond',
  1245. 'mid',
  1246. 'min',
  1247. 'minute',
  1248. 'mod',
  1249. 'month',
  1250. 'monthname',
  1251. 'nextval',
  1252. 'now',
  1253. 'nullif',
  1254. 'oct',
  1255. 'octet_length',
  1256. 'old_password',
  1257. 'ord',
  1258. 'password',
  1259. 'period_add',
  1260. 'period_diff',
  1261. 'pi',
  1262. 'position',
  1263. 'pow',
  1264. 'power',
  1265. 'quarter',
  1266. 'quote',
  1267. 'radians',
  1268. 'rand',
  1269. 'release_lock',
  1270. 'repeat',
  1271. 'replace',
  1272. 'reverse',
  1273. 'right',
  1274. 'round',
  1275. 'row_count',
  1276. 'rpad',
  1277. 'rtrim',
  1278. 'sec_to_time',
  1279. 'second',
  1280. 'session_user',
  1281. 'sha',
  1282. 'sha1',
  1283. 'sign',
  1284. 'soundex',
  1285. 'space',
  1286. 'sqrt',
  1287. 'std',
  1288. 'stddev',
  1289. 'stddev_pop',
  1290. 'stddev_samp',
  1291. 'strcmp',
  1292. 'str_to_date',
  1293. 'subdate',
  1294. 'substr',
  1295. 'substring',
  1296. 'substring_index',
  1297. 'subtime',
  1298. 'sum',
  1299. 'sysdate',
  1300. 'system_user',
  1301. 'tan',
  1302. 'time',
  1303. 'timediff',
  1304. 'timestamp',
  1305. 'timestampadd',
  1306. 'timestampdiff',
  1307. 'time_format',
  1308. 'time_to_sec',
  1309. 'to_days',
  1310. 'trim',
  1311. 'truncate',
  1312. 'ucase',
  1313. 'uncompress',
  1314. 'uncompressed_length',
  1315. 'unhex',
  1316. 'unix_timestamp',
  1317. 'upper',
  1318. 'user',
  1319. 'utc_date',
  1320. 'utc_time',
  1321. 'utc_timestamp',
  1322. 'uuid',
  1323. 'var_pop',
  1324. 'var_samp',
  1325. 'variance',
  1326. 'version',
  1327. 'week',
  1328. 'weekday',
  1329. 'weekofyear',
  1330. 'year',
  1331. 'yearweek');
  1332. /* includes functions */
  1333. $this->reserved = array(
  1334. 'abs',
  1335. 'acos',
  1336. 'adddate',
  1337. 'addtime',
  1338. 'aes_encrypt',
  1339. 'aes_decrypt',
  1340. 'against',
  1341. 'ascii',
  1342. 'asin',
  1343. 'atan',
  1344. 'avg',
  1345. 'benchmark',
  1346. 'bin',
  1347. 'bit_and',
  1348. 'bit_or',
  1349. 'bitcount',
  1350. 'bitlength',
  1351. 'cast',
  1352. 'ceiling',
  1353. 'char',
  1354. 'char_length',
  1355. 'character_length',
  1356. 'charset',
  1357. 'coalesce',
  1358. 'coercibility',
  1359. 'collation',
  1360. 'compress',
  1361. 'concat',
  1362. 'concat_ws',
  1363. 'conection_id',
  1364. 'conv',
  1365. 'convert',
  1366. 'convert_tz',
  1367. 'cos',
  1368. 'cot',
  1369. 'count',
  1370. 'crc32',
  1371. 'curdate',
  1372. 'current_user',
  1373. 'currval',
  1374. 'curtime',
  1375. 'database',
  1376. 'date_add',
  1377. 'date_diff',
  1378. 'date_format',
  1379. 'date_sub',
  1380. 'day',
  1381. 'dayname',
  1382. 'dayofmonth',
  1383. 'dayofweek',
  1384. 'dayofyear',
  1385. 'decode',
  1386. 'default',
  1387. 'degrees',
  1388. 'des_decrypt',
  1389. 'des_encrypt',
  1390. 'elt',
  1391. 'encode',
  1392. 'encrypt',
  1393. 'exp',
  1394. 'export_set',
  1395. 'extract',
  1396. 'field',
  1397. 'find_in_set',
  1398. 'floor',
  1399. 'format',
  1400. 'found_rows',
  1401. 'from_days',
  1402. 'from_unixtime',
  1403. 'get_format',
  1404. 'get_lock',
  1405. 'group_concat',
  1406. 'greatest',
  1407. 'hex',
  1408. 'hour',
  1409. 'if',
  1410. 'ifnull',
  1411. 'in',
  1412. 'inet_aton',
  1413. 'inet_ntoa',
  1414. 'insert',
  1415. 'instr',
  1416. 'interval',
  1417. 'is_free_lock',
  1418. 'is_used_lock',
  1419. 'last_day',
  1420. 'last_insert_id',
  1421. 'lcase',
  1422. 'least',
  1423. 'left',
  1424. 'length',
  1425. 'ln',
  1426. 'load_file',
  1427. 'localtime',
  1428. 'localtimestamp',
  1429. 'locate',
  1430. 'log',
  1431. 'log2',
  1432. 'log10',
  1433. 'lower',
  1434. 'lpad',
  1435. 'ltrim',
  1436. 'make_set',
  1437. 'makedate',
  1438. 'maketime',
  1439. 'master_pos_wait',
  1440. 'match',
  1441. 'max',
  1442. 'md5',
  1443. 'microsecond',
  1444. 'mid',
  1445. 'min',
  1446. 'minute',
  1447. 'mod',
  1448. 'month',
  1449. 'monthname',
  1450. 'nextval',
  1451. 'now',
  1452. 'nullif',
  1453. 'oct',
  1454. 'octet_length',
  1455. 'old_password',
  1456. 'ord',
  1457. 'password',
  1458. 'period_add',
  1459. 'period_diff',
  1460. 'pi',
  1461. 'position',
  1462. 'pow',
  1463. 'power',
  1464. 'quarter',
  1465. 'quote',
  1466. 'radians',
  1467. 'rand',
  1468. 'release_lock',
  1469. 'repeat',
  1470. 'replace',
  1471. 'reverse',
  1472. 'right',
  1473. 'round',
  1474. 'row_count',
  1475. 'rpad',
  1476. 'rtrim',
  1477. 'sec_to_time',
  1478. 'second',
  1479. 'session_user',
  1480. 'sha',
  1481. 'sha1',
  1482. 'sign',
  1483. 'soundex',
  1484. 'space',
  1485. 'sqrt',
  1486. 'std',
  1487. 'stddev',
  1488. 'stddev_pop',
  1489. 'stddev_samp',
  1490. 'strcmp',
  1491. 'str_to_date',
  1492. 'subdate',
  1493. 'substring',
  1494. 'substring_index',
  1495. 'subtime',
  1496. 'sum',
  1497. 'sysdate',
  1498. 'system_user',
  1499. 'tan',
  1500. 'time',
  1501. 'timediff',
  1502. 'timestamp',
  1503. 'timestampadd',
  1504. 'timestampdiff',
  1505. 'time_format',
  1506. 'time_to_sec',
  1507. 'to_days',
  1508. 'trim',
  1509. 'truncate',
  1510. 'ucase',
  1511. 'uncompress',
  1512. 'uncompressed_length',
  1513. 'unhex',
  1514. 'unix_timestamp',
  1515. 'upper',
  1516. 'user',
  1517. 'utc_date',
  1518. 'utc_time',
  1519. 'utc_timestamp',
  1520. 'uuid',
  1521. 'var_pop',
  1522. 'var_samp',
  1523. 'variance',
  1524. 'version',
  1525. 'week',
  1526. 'weekday',
  1527. 'weekofyear',
  1528. 'year',
  1529. 'yearweek',
  1530. 'add',
  1531. 'all',
  1532. 'alter',
  1533. 'analyze',
  1534. 'and',
  1535. 'as',
  1536. 'asc',
  1537. 'asensitive',
  1538. 'auto_increment',
  1539. 'bdb',
  1540. 'before',
  1541. 'berkeleydb',
  1542. 'between',
  1543. 'bigint',
  1544. 'binary',
  1545. 'blob',
  1546. 'both',
  1547. 'by',
  1548. 'call',
  1549. 'cascade',
  1550. 'case',
  1551. 'change',
  1552. 'char',
  1553. 'character',
  1554. 'check',
  1555. 'collate',
  1556. 'column',
  1557. 'columns',
  1558. 'condition',
  1559. 'connection',
  1560. 'constraint',
  1561. 'continue',
  1562. 'create',
  1563. 'cross',
  1564. 'current_date',
  1565. 'current_time',
  1566. 'current_timestamp',
  1567. 'cursor',
  1568. 'database',
  1569. 'databases',
  1570. 'day_hour',
  1571. 'day_microsecond',
  1572. 'day_minute',
  1573. 'day_second',
  1574. 'dec',
  1575. 'decimal',
  1576. 'declare',
  1577. 'default',
  1578. 'delayed',
  1579. 'delete',
  1580. 'desc',
  1581. 'describe',
  1582. 'deterministic',
  1583. 'distinct',
  1584. 'distinctrow',
  1585. 'div',
  1586. 'double',
  1587. 'drop',
  1588. 'else',
  1589. 'elseif',
  1590. 'enclosed',
  1591. 'escaped',
  1592. 'exists',
  1593. 'exit',
  1594. 'explain',
  1595. 'false',
  1596. 'fetch',
  1597. 'fields',
  1598. 'float',
  1599. 'for',
  1600. 'force',
  1601. 'foreign',
  1602. 'found',
  1603. 'frac_second',
  1604. 'from',
  1605. 'fulltext',
  1606. 'grant',
  1607. 'group',
  1608. 'having',
  1609. 'high_priority',
  1610. 'hour_microsecond',
  1611. 'hour_minute',
  1612. 'hour_second',
  1613. 'if',
  1614. 'ignore',
  1615. 'in',
  1616. 'index',
  1617. 'infile',
  1618. 'inner',
  1619. 'innodb',
  1620. 'inout',
  1621. 'insensitive',
  1622. 'insert',
  1623. 'int',
  1624. 'integer',
  1625. 'interval',
  1626. 'into',
  1627. 'io_thread',
  1628. 'is',
  1629. 'iterate',
  1630. 'join',
  1631. 'key',
  1632. 'keys',
  1633. 'kill',
  1634. 'leading',
  1635. 'leave',
  1636. 'left',
  1637. 'like',
  1638. 'limit',
  1639. 'lines',
  1640. 'load',
  1641. 'localtime',
  1642. 'localtimestamp',
  1643. 'lock',
  1644. 'long',
  1645. 'longblob',
  1646. 'longtext',
  1647. 'loop',
  1648. 'low_priority',
  1649. 'master_server_id',
  1650. 'match',
  1651. 'mediumblob',
  1652. 'mediumint',
  1653. 'mediumtext',
  1654. 'middleint',
  1655. 'minute_microsecond',
  1656. 'minute_second',
  1657. 'mod',
  1658. 'natural',
  1659. 'not',
  1660. 'no_write_to_binlog',
  1661. 'null',
  1662. 'numeric',
  1663. 'on',
  1664. 'optimize',
  1665. 'option',
  1666. 'optionally',
  1667. 'or',
  1668. 'order',
  1669. 'out',
  1670. 'outer',
  1671. 'outfile',
  1672. 'precision',
  1673. 'primary',
  1674. 'privileges',
  1675. 'procedure',
  1676. 'purge',
  1677. 'read',
  1678. 'real',
  1679. 'references',
  1680. 'regexp',
  1681. 'rename',
  1682. 'repeat',
  1683. 'replace',
  1684. 'require',
  1685. 'restrict',
  1686. 'return',
  1687. 'revoke',
  1688. 'right',
  1689. 'rlike',
  1690. 'second_microsecond',
  1691. 'select',
  1692. 'sensitive',
  1693. 'separator',
  1694. 'set',
  1695. 'show',
  1696. 'smallint',
  1697. 'some',
  1698. 'soname',
  1699. 'spatial',
  1700. 'specific',
  1701. 'sql',
  1702. 'sqlexception',
  1703. 'sqlstate',
  1704. 'sqlwarning',
  1705. 'sql_big_result',
  1706. 'sql_calc_found_rows',
  1707. 'sql_small_result',
  1708. 'sql_tsi_day',
  1709. 'sql_tsi_frac_second',
  1710. 'sql_tsi_hour',
  1711. 'sql_tsi_minute',
  1712. 'sql_tsi_month',
  1713. 'sql_tsi_quarter',
  1714. 'sql_tsi_second',
  1715. 'sql_tsi_week',
  1716. 'sql_tsi_year',
  1717. 'ssl',
  1718. 'starting',
  1719. 'straight_join',
  1720. 'striped',
  1721. 'table',
  1722. 'tables',
  1723. 'terminated',
  1724. 'then',
  1725. 'timestampadd',
  1726. 'timestampdiff',
  1727. 'tinyblob',
  1728. 'tinyint',
  1729. 'tinytext',
  1730. 'to',
  1731. 'trailing',
  1732. 'true',
  1733. 'undo',
  1734. 'union',
  1735. 'unique',
  1736. 'unlock',
  1737. 'unsigned',
  1738. 'update',
  1739. 'usage',
  1740. 'use',
  1741. 'user_resources',
  1742. 'using',
  1743. 'utc_date',
  1744. 'utc_time',
  1745. 'utc_timestamp',
  1746. 'values',
  1747. 'varbinary',
  1748. 'varchar',
  1749. 'varcharacter',
  1750. 'varying',
  1751. 'when',
  1752. 'where',
  1753. 'while',
  1754. 'with',
  1755. 'write',
  1756. 'xor',
  1757. 'year_month',
  1758. 'zerofill'
  1759. );
  1760. for($i=0;$i<count($this->reserved);++$i) {
  1761. $this->reserved[$i]=strtoupper($this->reserved[$i]);
  1762. if(!empty($this->functions[$i])) $this->functions[$i] = strtoupper($this->functions[$i]);
  1763. }
  1764. }
  1765. } // END CLASS