Total Complexity | 465 |
Total Lines | 1847 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like RDatabaseSqlparserSqlparser often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use RDatabaseSqlparserSqlparser, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
46 | class RDatabaseSqlparserSqlparser extends RDatabaseSqlparserSqlparserutils |
||
47 | { |
||
48 | /** |
||
49 | * @var RDatabaseSqlparserSqllexer |
||
50 | */ |
||
51 | private $lexer; |
||
52 | |||
53 | /** |
||
54 | * @var |
||
55 | */ |
||
56 | public $parsed; |
||
57 | |||
58 | /** |
||
59 | * RDatabaseSqlparserSqlparser constructor. |
||
60 | * |
||
61 | * @param boolean $sql |
||
62 | * @param boolean $calcPositions |
||
63 | */ |
||
64 | public function __construct($sql = false, $calcPositions = false) |
||
65 | { |
||
66 | $this->lexer = new RDatabaseSqlparserSqllexer; |
||
67 | |||
68 | if ($sql) |
||
69 | { |
||
70 | $this->parse($sql, $calcPositions); |
||
71 | } |
||
72 | } |
||
73 | |||
74 | /** |
||
75 | * @param $sql |
||
76 | * @param bool $calcPositions |
||
77 | * |
||
78 | * @return mixed |
||
79 | */ |
||
80 | public function parse($sql, $calcPositions = false) |
||
81 | { |
||
82 | // Lex the SQL statement |
||
83 | $inputArray = $this->split_sql($sql); |
||
84 | |||
85 | // This is the highest level lexical analysis. This is the part of the |
||
86 | // code which finds UNION and UNION ALL query parts |
||
87 | $queries = $this->processUnion($inputArray); |
||
88 | |||
89 | // If there was no UNION or UNION ALL in the query, then the query is |
||
90 | // stored at $queries[0]. |
||
91 | if (!$this->isUnion($queries)) |
||
92 | { |
||
93 | $queries = $this->processSQL($queries[0]); |
||
|
|||
94 | } |
||
95 | |||
96 | // Calc the positions of some important tokens |
||
97 | if ($calcPositions) |
||
98 | { |
||
99 | $calculator = new RDatabaseSqlparserPositioncalculator; |
||
100 | $queries = $calculator->setPositionsWithinSQL($sql, $queries); |
||
101 | } |
||
102 | |||
103 | // store the parsed queries |
||
104 | $this->parsed = $queries; |
||
105 | |||
106 | return $this->parsed; |
||
107 | } |
||
108 | |||
109 | private function processUnion($inputArray) |
||
110 | { |
||
111 | $outputArray = array(); |
||
112 | |||
113 | // sometimes the parser needs to skip ahead until a particular |
||
114 | // token is found |
||
115 | $skipUntilToken = false; |
||
116 | |||
117 | // This is the last type of union used (UNION or UNION ALL) |
||
118 | // indicates a) presence of at least one union in this query |
||
119 | // b) the type of union if this is the first or last query |
||
120 | $unionType = false; |
||
121 | |||
122 | // Sometimes a "query" consists of more than one query (like a UNION query) |
||
123 | // this array holds all the queries |
||
124 | $queries = array(); |
||
125 | |||
126 | foreach ($inputArray as $key => $token) |
||
127 | { |
||
128 | $trim = trim($token); |
||
129 | |||
130 | // overread all tokens till that given token |
||
131 | if ($skipUntilToken) |
||
132 | { |
||
133 | if ($trim === "") |
||
134 | { |
||
135 | continue; // read the next token |
||
136 | } |
||
137 | if (strtoupper($trim) === $skipUntilToken) |
||
138 | { |
||
139 | $skipUntilToken = false; |
||
140 | continue; // read the next token |
||
141 | } |
||
142 | } |
||
143 | |||
144 | if (strtoupper($trim) !== "UNION") |
||
145 | { |
||
146 | $outputArray[] = $token; // here we get empty tokens, if we remove these, we get problems in parse_sql() |
||
147 | continue; |
||
148 | } |
||
149 | |||
150 | $unionType = "UNION"; |
||
151 | |||
152 | // we are looking for an ALL token right after UNION |
||
153 | for ($i = $key + 1; $i < count($inputArray); ++$i) |
||
154 | { |
||
155 | if (trim($inputArray[$i]) === "") |
||
156 | { |
||
157 | continue; |
||
158 | } |
||
159 | if (strtoupper($inputArray[$i]) !== "ALL") |
||
160 | { |
||
161 | break; |
||
162 | } |
||
163 | // the other for-loop should overread till "ALL" |
||
164 | $skipUntilToken = "ALL"; |
||
165 | $unionType = "UNION ALL"; |
||
166 | } |
||
167 | |||
168 | // store the tokens related to the unionType |
||
169 | $queries[$unionType][] = $outputArray; |
||
170 | $outputArray = array(); |
||
171 | } |
||
172 | |||
173 | // the query tokens after the last UNION or UNION ALL |
||
174 | // or we don't have an UNION/UNION ALL |
||
175 | if (!empty($outputArray)) |
||
176 | { |
||
177 | if ($unionType) |
||
178 | { |
||
179 | $queries[$unionType][] = $outputArray; |
||
180 | } |
||
181 | else |
||
182 | { |
||
183 | $queries[] = $outputArray; |
||
184 | } |
||
185 | } |
||
186 | |||
187 | return $this->processMySQLUnion($queries); |
||
188 | } |
||
189 | |||
190 | /** MySQL supports a special form of UNION: |
||
191 | * (select ...) |
||
192 | * union |
||
193 | * (select ...) |
||
194 | * |
||
195 | * This function handles this query syntax. Only one such subquery |
||
196 | * is supported in each UNION block. (select)(select)union(select) is not legal. |
||
197 | * The extra queries will be silently ignored. |
||
198 | */ |
||
199 | private function processMySQLUnion($queries) |
||
200 | { |
||
201 | $unionTypes = array('UNION', 'UNION ALL'); |
||
202 | foreach ($unionTypes as $unionType) |
||
203 | { |
||
204 | |||
205 | if (empty($queries[$unionType])) |
||
206 | { |
||
207 | continue; |
||
208 | } |
||
209 | |||
210 | foreach ($queries[$unionType] as $key => $tokenList) |
||
211 | { |
||
212 | foreach ($tokenList as $token) |
||
213 | { |
||
214 | $token = trim($token); |
||
215 | if ($token === "") |
||
216 | { |
||
217 | continue; |
||
218 | } |
||
219 | |||
220 | // starts with "(select" |
||
221 | if (preg_match("/^\\(\\s*select\\s*/i", $token)) |
||
222 | { |
||
223 | $queries[$unionType][$key] = $this->parse($this->removeParenthesisFromStart($token)); |
||
224 | break; |
||
225 | } |
||
226 | |||
227 | $queries[$unionType][$key] = $this->processSQL($queries[$unionType][$key]); |
||
228 | break; |
||
229 | } |
||
230 | } |
||
231 | } |
||
232 | |||
233 | // it can be parsed or not |
||
234 | return $queries; |
||
235 | } |
||
236 | |||
237 | private function isUnion($queries) |
||
238 | { |
||
239 | $unionTypes = array('UNION', 'UNION ALL'); |
||
240 | foreach ($unionTypes as $unionType) |
||
241 | { |
||
242 | if (!empty($queries[$unionType])) |
||
243 | { |
||
244 | return true; |
||
245 | } |
||
246 | } |
||
247 | |||
248 | return false; |
||
249 | } |
||
250 | |||
251 | // this function splits up a SQL statement into easy to "parse" |
||
252 | // tokens for the SQL processor |
||
253 | private function split_sql($sql) |
||
254 | { |
||
255 | return $this->lexer->split($sql); |
||
256 | } |
||
257 | |||
258 | /** |
||
259 | * This function breaks up the SQL statement into logical sections. |
||
260 | * Some sections are then further handled by specialized functions. |
||
261 | * |
||
262 | * @param array $tokens Token |
||
263 | * |
||
264 | * @return void |
||
265 | */ |
||
266 | private function processSQL(&$tokens) |
||
267 | { |
||
268 | $prev_category = ""; |
||
269 | $token_category = ""; |
||
270 | $skip_next = false; |
||
271 | $out = false; |
||
272 | |||
273 | $tokenCount = count($tokens); |
||
274 | |||
275 | for ($tokenNumber = 0; $tokenNumber < $tokenCount; ++$tokenNumber) |
||
276 | { |
||
277 | $token = $tokens[$tokenNumber]; |
||
278 | $trim = trim($token); // this removes also \n and \t! |
||
279 | |||
280 | // if it starts with an "(", it should follow a SELECT |
||
281 | if ($trim !== "" && $trim[0] == "(" && $token_category == "") |
||
282 | { |
||
283 | $token_category = 'SELECT'; |
||
284 | } |
||
285 | |||
286 | /* If it isn't obvious, when $skip_next is set, then we ignore the next real |
||
287 | token, that is we ignore whitespace. |
||
288 | */ |
||
289 | if ($skip_next) |
||
290 | { |
||
291 | if ($trim === "") |
||
292 | { |
||
293 | if ($token_category !== "") |
||
294 | { // is this correct?? |
||
295 | $out[$token_category][] = $token; |
||
296 | } |
||
297 | continue; |
||
298 | } |
||
299 | // to skip the token we replace it with whitespace |
||
300 | $trim = ""; |
||
301 | $token = ""; |
||
302 | $skip_next = false; |
||
303 | } |
||
304 | |||
305 | $upper = strtoupper($trim); |
||
306 | switch ($upper) |
||
307 | { |
||
308 | |||
309 | /* Tokens that get their own sections. These keywords have subclauses. */ |
||
310 | case 'SELECT': |
||
311 | case 'ORDER': |
||
312 | case 'LIMIT': |
||
313 | case 'SET': |
||
314 | case 'DUPLICATE': |
||
315 | case 'VALUES': |
||
316 | case 'GROUP': |
||
317 | case 'HAVING': |
||
318 | case 'WHERE': |
||
319 | case 'RENAME': |
||
320 | case 'CALL': |
||
321 | case 'PROCEDURE': |
||
322 | case 'FUNCTION': |
||
323 | case 'DATABASE': |
||
324 | case 'SERVER': |
||
325 | case 'LOGFILE': |
||
326 | case 'DEFINER': |
||
327 | case 'RETURNS': |
||
328 | case 'TABLESPACE': |
||
329 | case 'TRIGGER': |
||
330 | case 'DO': |
||
331 | case 'PLUGIN': |
||
332 | case 'FROM': |
||
333 | case 'FLUSH': |
||
334 | case 'KILL': |
||
335 | case 'RESET': |
||
336 | case 'START': |
||
337 | case 'STOP': |
||
338 | case 'PURGE': |
||
339 | case 'EXECUTE': |
||
340 | case 'PREPARE': |
||
341 | case 'DEALLOCATE': |
||
342 | if ($trim === 'DEALLOCATE') |
||
343 | { |
||
344 | $skip_next = true; |
||
345 | } |
||
346 | /* this FROM is different from FROM in other DML (not join related) */ |
||
347 | if ($token_category === 'PREPARE' && $upper === 'FROM') |
||
348 | { |
||
349 | continue 2; |
||
350 | } |
||
351 | |||
352 | $token_category = $upper; |
||
353 | break; |
||
354 | |||
355 | case 'EVENT': |
||
356 | // issue 71 |
||
357 | if ($prev_category === 'DROP' || $prev_category === 'ALTER' || $prev_category === 'CREATE') |
||
358 | { |
||
359 | $token_category = $upper; |
||
360 | } |
||
361 | break; |
||
362 | |||
363 | case 'DATA': |
||
364 | // prevent wrong handling of DATA as keyword |
||
365 | if ($prev_category === 'LOAD') |
||
366 | { |
||
367 | $token_category = $upper; |
||
368 | } |
||
369 | break; |
||
370 | |||
371 | case 'PASSWORD': |
||
372 | // prevent wrong handling of PASSWORD as keyword |
||
373 | if ($prev_category === 'SET') |
||
374 | { |
||
375 | $token_category = $upper; |
||
376 | } |
||
377 | break; |
||
378 | |||
379 | case 'INTO': |
||
380 | // prevent wrong handling of CACHE within LOAD INDEX INTO CACHE... |
||
381 | if ($prev_category === 'LOAD') |
||
382 | { |
||
383 | $out[$prev_category][] = $upper; |
||
384 | continue 2; |
||
385 | } |
||
386 | $token_category = $upper; |
||
387 | break; |
||
388 | |||
389 | case 'USER': |
||
390 | // prevent wrong processing as keyword |
||
391 | if ($prev_category === 'CREATE' || $prev_category === 'RENAME' || $prev_category === 'DROP') |
||
392 | { |
||
393 | $token_category = $upper; |
||
394 | } |
||
395 | break; |
||
396 | |||
397 | case 'VIEW': |
||
398 | // prevent wrong processing as keyword |
||
399 | if ($prev_category === 'CREATE' || $prev_category === 'ALTER' || $prev_category === 'DROP') |
||
400 | { |
||
401 | $token_category = $upper; |
||
402 | } |
||
403 | break; |
||
404 | |||
405 | /* These tokens get their own section, but have no subclauses. |
||
406 | These tokens identify the statement but have no specific subclauses of their own. */ |
||
407 | case 'DELETE': |
||
408 | case 'ALTER': |
||
409 | case 'INSERT': |
||
410 | case 'REPLACE': |
||
411 | case 'TRUNCATE': |
||
412 | case 'CREATE': |
||
413 | case 'OPTIMIZE': |
||
414 | case 'GRANT': |
||
415 | case 'REVOKE': |
||
416 | case 'SHOW': |
||
417 | case 'HANDLER': |
||
418 | case 'LOAD': |
||
419 | case 'ROLLBACK': |
||
420 | case 'SAVEPOINT': |
||
421 | case 'UNLOCK': |
||
422 | case 'INSTALL': |
||
423 | case 'UNINSTALL': |
||
424 | case 'ANALZYE': |
||
425 | case 'BACKUP': |
||
426 | case 'CHECK': |
||
427 | case 'CHECKSUM': |
||
428 | case 'REPAIR': |
||
429 | case 'RESTORE': |
||
430 | case 'DESCRIBE': |
||
431 | case 'EXPLAIN': |
||
432 | case 'USE': |
||
433 | case 'HELP': |
||
434 | // We are using USE from FROM statement, not separate token category |
||
435 | if ($prev_category == 'FROM') |
||
436 | { |
||
437 | break; |
||
438 | } |
||
439 | $token_category = $upper; /* set the category in case these get subclauses |
||
440 | in a future version of MySQL */ |
||
441 | $out[$upper][0] = $upper; |
||
442 | continue 2; |
||
443 | |||
444 | case 'CACHE': |
||
445 | if ($prev_category === "" || $prev_category === 'RESET' || $prev_category === 'FLUSH' |
||
446 | || $prev_category === 'LOAD') |
||
447 | { |
||
448 | $token_category = $upper; |
||
449 | continue 2; |
||
450 | } |
||
451 | break; |
||
452 | |||
453 | /* This is either LOCK TABLES or SELECT ... LOCK IN SHARE MODE*/ |
||
454 | case 'LOCK': |
||
455 | if ($token_category === "") |
||
456 | { |
||
457 | $token_category = $upper; |
||
458 | $out[$upper][0] = $upper; |
||
459 | } |
||
460 | else |
||
461 | { |
||
462 | $trim = 'LOCK IN SHARE MODE'; |
||
463 | $skip_next = true; |
||
464 | $out['OPTIONS'][] = $trim; |
||
465 | } |
||
466 | continue 2; |
||
467 | |||
468 | case 'USING': /* USING in FROM clause is different from USING w/ prepared statement*/ |
||
469 | if ($token_category === 'EXECUTE') |
||
470 | { |
||
471 | $token_category = $upper; |
||
472 | continue 2; |
||
473 | } |
||
474 | if ($token_category === 'FROM' && !empty($out['DELETE'])) |
||
475 | { |
||
476 | $token_category = $upper; |
||
477 | continue 2; |
||
478 | } |
||
479 | break; |
||
480 | |||
481 | /* DROP TABLE is different from ALTER TABLE DROP ... */ |
||
482 | case 'DROP': |
||
483 | if ($token_category !== 'ALTER') |
||
484 | { |
||
485 | $token_category = $upper; |
||
486 | $out[$upper][0] = $upper; |
||
487 | continue 2; |
||
488 | } |
||
489 | break; |
||
490 | |||
491 | case 'FOR': |
||
492 | $skip_next = true; |
||
493 | $out['OPTIONS'][] = 'FOR UPDATE'; |
||
494 | continue 2; |
||
495 | |||
496 | case 'UPDATE': |
||
497 | if ($token_category === "") |
||
498 | { |
||
499 | $token_category = $upper; |
||
500 | continue 2; |
||
501 | |||
502 | } |
||
503 | if ($token_category === 'DUPLICATE') |
||
504 | { |
||
505 | continue 2; |
||
506 | } |
||
507 | break; |
||
508 | |||
509 | /*case 'START': |
||
510 | $trim = "BEGIN"; |
||
511 | $out[$upper][0] = $upper; |
||
512 | $skip_next = true; |
||
513 | break;*/ |
||
514 | |||
515 | /* These tokens are ignored. */ |
||
516 | case 'BY': |
||
517 | case 'ALL': |
||
518 | case 'SHARE': |
||
519 | case 'MODE': |
||
520 | case 'TO': |
||
521 | case ';': |
||
522 | continue 2; |
||
523 | |||
524 | case 'KEY': |
||
525 | if ($token_category === 'DUPLICATE') |
||
526 | { |
||
527 | continue 2; |
||
528 | } |
||
529 | break; |
||
530 | |||
531 | /* These tokens set particular options for the statement. They never stand alone.*/ |
||
532 | case 'HIGH_PRIORITY': |
||
533 | case 'LOW_PRIORITY': |
||
534 | case 'DELAYED': |
||
535 | case 'IGNORE': |
||
536 | case 'FORCE': |
||
537 | case 'STRAIGHT_JOIN': |
||
538 | case 'SQL_SMALL_RESULT': |
||
539 | case 'SQL_BIG_RESULT': |
||
540 | case 'QUICK': |
||
541 | case 'SQL_BUFFER_RESULT': |
||
542 | case 'SQL_CACHE': |
||
543 | case 'SQL_NO_CACHE': |
||
544 | case 'SQL_CALC_FOUND_ROWS': |
||
545 | $out['OPTIONS'][] = $upper; |
||
546 | continue 2; |
||
547 | |||
548 | case 'WITH': |
||
549 | if ($token_category === 'GROUP') |
||
550 | { |
||
551 | $skip_next = true; |
||
552 | $out['OPTIONS'][] = 'WITH ROLLUP'; |
||
553 | continue 2; |
||
554 | } |
||
555 | break; |
||
556 | |||
557 | case 'AS': |
||
558 | break; |
||
559 | |||
560 | case '': |
||
561 | case ',': |
||
562 | //case ';': |
||
563 | break; |
||
564 | |||
565 | default: |
||
566 | break; |
||
567 | } |
||
568 | |||
569 | // remove obsolete category after union (empty category because of |
||
570 | // empty token before select) |
||
571 | if ($token_category !== "" && ($prev_category === $token_category)) |
||
572 | { |
||
573 | $out[$token_category][] = $token; |
||
574 | } |
||
575 | |||
576 | $prev_category = $token_category; |
||
577 | } |
||
578 | |||
579 | return $this->processSQLParts($out); |
||
580 | } |
||
581 | |||
582 | private function processSQLParts($out) |
||
583 | { |
||
584 | if (!$out) |
||
585 | { |
||
586 | return false; |
||
587 | } |
||
588 | if (!empty($out['SELECT'])) |
||
589 | { |
||
590 | $out['SELECT'] = $this->process_select($out['SELECT']); |
||
591 | } |
||
592 | if (!empty($out['FROM'])) |
||
593 | { |
||
594 | $out['FROM'] = $this->process_from($out['FROM']); |
||
595 | } |
||
596 | if (!empty($out['USING'])) |
||
597 | { |
||
598 | $out['USING'] = $this->process_from($out['USING']); |
||
599 | } |
||
600 | if (!empty($out['UPDATE'])) |
||
601 | { |
||
602 | $out['UPDATE'] = $this->process_from($out['UPDATE']); |
||
603 | } |
||
604 | if (!empty($out['GROUP'])) |
||
605 | { |
||
606 | // set empty array if we have partial SQL statement |
||
607 | $out['GROUP'] = $this->process_group($out['GROUP'], isset($out['SELECT']) ? $out['SELECT'] : array()); |
||
608 | } |
||
609 | if (!empty($out['ORDER'])) |
||
610 | { |
||
611 | // set empty array if we have partial SQL statement |
||
612 | $out['ORDER'] = $this->process_order($out['ORDER'], isset($out['SELECT']) ? $out['SELECT'] : array()); |
||
613 | } |
||
614 | if (!empty($out['LIMIT'])) |
||
615 | { |
||
616 | $out['LIMIT'] = $this->process_limit($out['LIMIT']); |
||
617 | } |
||
618 | if (!empty($out['WHERE'])) |
||
619 | { |
||
620 | $out['WHERE'] = $this->process_expr_list($out['WHERE']); |
||
621 | } |
||
622 | if (!empty($out['HAVING'])) |
||
623 | { |
||
624 | $out['HAVING'] = $this->process_expr_list($out['HAVING']); |
||
625 | } |
||
626 | if (!empty($out['SET'])) |
||
627 | { |
||
628 | $out['SET'] = $this->process_set_list($out['SET'], isset($out['UPDATE'])); |
||
629 | } |
||
630 | if (!empty($out['DUPLICATE'])) |
||
631 | { |
||
632 | $out['ON DUPLICATE KEY UPDATE'] = $this->process_set_list($out['DUPLICATE'], isset($out['UPDATE'])); |
||
633 | unset($out['DUPLICATE']); |
||
634 | } |
||
635 | if (!empty($out['INSERT'])) |
||
636 | { |
||
637 | $out = $this->process_insert($out); |
||
638 | } |
||
639 | if (!empty($out['REPLACE'])) |
||
640 | { |
||
641 | $out = $this->process_insert($out, 'REPLACE'); |
||
642 | } |
||
643 | if (!empty($out['DELETE'])) |
||
644 | { |
||
645 | $out = $this->process_delete($out); |
||
646 | } |
||
647 | if (!empty($out['VALUES'])) |
||
648 | { |
||
649 | $out = $this->process_values($out); |
||
650 | } |
||
651 | if (!empty($out['INTO'])) |
||
652 | { |
||
653 | $out = $this->process_into($out); |
||
654 | } |
||
655 | |||
656 | return $out; |
||
657 | } |
||
658 | |||
659 | /* A SET list is simply a list of key = value expressions separated by comma (,). |
||
660 | This function produces a list of the key/value expressions. |
||
661 | */ |
||
662 | private function getAssignment($base_expr) |
||
663 | { |
||
664 | $assignment = $this->process_expr_list($this->split_sql($base_expr)); |
||
665 | |||
666 | return array('expr_type' => 'expression', 'base_expr' => trim($base_expr), 'sub_tree' => $assignment); |
||
667 | } |
||
668 | |||
669 | private function getVariableType($expression) |
||
670 | { |
||
671 | // $expression must contain only upper-case characters |
||
672 | if ($expression[1] !== "@") |
||
673 | { |
||
674 | return 'user_variable'; |
||
675 | } |
||
676 | |||
677 | $type = substr($expression, 2, strpos($expression, ".", 2)); |
||
678 | |||
679 | switch ($type) |
||
680 | { |
||
681 | case 'GLOBAL': |
||
682 | case 'LOCAL': |
||
683 | case 'SESSION': |
||
684 | $type = strtolower($type) . '_variable'; |
||
685 | break; |
||
686 | default: |
||
687 | $type = 'session_variable'; |
||
688 | break; |
||
689 | } |
||
690 | |||
691 | return $type; |
||
692 | } |
||
693 | |||
694 | private function process_set_list($tokens, $isUpdate) |
||
695 | { |
||
696 | $result = array(); |
||
697 | $baseExpr = ""; |
||
698 | $varType = false; |
||
699 | |||
700 | foreach ($tokens as $token) |
||
701 | { |
||
702 | $upper = strtoupper(trim($token)); |
||
703 | |||
704 | switch ($upper) |
||
705 | { |
||
706 | case 'LOCAL': |
||
707 | case 'SESSION': |
||
708 | case 'GLOBAL': |
||
709 | if (!$isUpdate) |
||
710 | { |
||
711 | $varType = strtolower($upper) . '_variable'; |
||
712 | $baseExpr = ""; |
||
713 | continue 2; |
||
714 | } |
||
715 | break; |
||
716 | |||
717 | case ',': |
||
718 | $assignment = $this->getAssignment($baseExpr); |
||
719 | if (!$isUpdate) |
||
720 | { |
||
721 | if ($varType !== false) |
||
722 | { |
||
723 | $assignment['sub_tree'][0]['expr_type'] = $varType; |
||
724 | } |
||
725 | } |
||
726 | $result[] = $assignment; |
||
727 | $baseExpr = ""; |
||
728 | $varType = false; |
||
729 | continue 2; |
||
730 | |||
731 | default: |
||
732 | } |
||
733 | $baseExpr .= $token; |
||
734 | } |
||
735 | |||
736 | if (trim($baseExpr) !== "") |
||
737 | { |
||
738 | $assignment = $this->getAssignment($baseExpr); |
||
739 | if (!$isUpdate) |
||
740 | { |
||
741 | if ($varType !== false) |
||
742 | { |
||
743 | $assignment['sub_tree'][0]['expr_type'] = $varType; |
||
744 | } |
||
745 | } |
||
746 | $result[] = $assignment; |
||
747 | } |
||
748 | |||
749 | return $result; |
||
750 | } |
||
751 | |||
752 | /* This function processes the LIMIT section. |
||
753 | start,end are set. If only end is provided in the query |
||
754 | then start is set to 0. |
||
755 | */ |
||
756 | private function process_limit($tokens) |
||
757 | { |
||
758 | $rowcount = ""; |
||
759 | $offset = ""; |
||
760 | |||
761 | $comma = -1; |
||
762 | $exchange = false; |
||
763 | |||
764 | for ($i = 0; $i < count($tokens); ++$i) |
||
765 | { |
||
766 | $trim = trim($tokens[$i]); |
||
767 | if ($trim === ",") |
||
768 | { |
||
769 | $comma = $i; |
||
770 | break; |
||
771 | } |
||
772 | if ($trim === "OFFSET") |
||
773 | { |
||
774 | $comma = $i; |
||
775 | $exchange = true; |
||
776 | break; |
||
777 | } |
||
778 | } |
||
779 | |||
780 | for ($i = 0; $i < $comma; ++$i) |
||
781 | { |
||
782 | if ($exchange) |
||
783 | { |
||
784 | $rowcount .= $tokens[$i]; |
||
785 | } |
||
786 | else |
||
787 | { |
||
788 | $offset .= $tokens[$i]; |
||
789 | } |
||
790 | } |
||
791 | |||
792 | for ($i = $comma + 1; $i < count($tokens); ++$i) |
||
793 | { |
||
794 | if ($exchange) |
||
795 | { |
||
796 | $offset .= $tokens[$i]; |
||
797 | } |
||
798 | else |
||
799 | { |
||
800 | $rowcount .= $tokens[$i]; |
||
801 | } |
||
802 | } |
||
803 | |||
804 | return array('offset' => trim($offset), 'rowcount' => trim($rowcount)); |
||
805 | } |
||
806 | |||
807 | /* This function processes the SELECT section. It splits the clauses at the commas. |
||
808 | Each clause is then processed by process_select_expr() and the results are added to |
||
809 | the expression list. |
||
810 | |||
811 | Finally, at the end, the epxression list is returned. |
||
812 | */ |
||
813 | private function process_select(&$tokens) |
||
814 | { |
||
815 | $expression = ""; |
||
816 | $expr = array(); |
||
817 | foreach ($tokens as $token) |
||
818 | { |
||
819 | if (trim($token) === ',') |
||
820 | { |
||
821 | $expr[] = $this->process_select_expr(trim($expression)); |
||
822 | $expression = ""; |
||
823 | } |
||
824 | else |
||
825 | { |
||
826 | $expression .= $token; |
||
827 | } |
||
828 | } |
||
829 | if ($expression) |
||
830 | { |
||
831 | $expr[] = $this->process_select_expr(trim($expression)); |
||
832 | } |
||
833 | |||
834 | return $expr; |
||
835 | } |
||
836 | |||
837 | private function revokeEscaping($sql) |
||
838 | { |
||
839 | $sql = trim($sql); |
||
840 | if (($sql[0] === '`') && ($sql[strlen($sql) - 1] === '`')) |
||
841 | { |
||
842 | $sql = substr($sql, 1, -1); |
||
843 | } |
||
844 | |||
845 | return str_replace('`', '', $sql); |
||
846 | } |
||
847 | |||
848 | private function isWhitespaceToken($token) |
||
849 | { |
||
850 | return (trim($token) === ""); |
||
851 | } |
||
852 | |||
853 | private function isCommentToken($token) |
||
854 | { |
||
855 | return isset($token[0]) && isset($token[1]) |
||
856 | && (($token[0] === '-' && $token[1] === '-') || ($token[0] === '/' && $token[1] === '*')); |
||
857 | } |
||
858 | |||
859 | private function isColumnReference($out) |
||
860 | { |
||
861 | return (isset($out['expr_type']) && $out['expr_type'] === 'colref'); |
||
862 | } |
||
863 | |||
864 | private function isReserved($out) |
||
865 | { |
||
866 | return (isset($out['expr_type']) && $out['expr_type'] === 'reserved'); |
||
867 | } |
||
868 | |||
869 | private function isConstant($out) |
||
870 | { |
||
871 | return (isset($out['expr_type']) && $out['expr_type'] === 'const'); |
||
872 | } |
||
873 | |||
874 | private function isAggregateFunction($out) |
||
875 | { |
||
876 | return (isset($out['expr_type']) && $out['expr_type'] === 'aggregate_function'); |
||
877 | } |
||
878 | |||
879 | private function isFunction($out) |
||
880 | { |
||
881 | return (isset($out['expr_type']) && $out['expr_type'] === 'function'); |
||
882 | } |
||
883 | |||
884 | private function isExpression($out) |
||
885 | { |
||
886 | return (isset($out['expr_type']) && $out['expr_type'] === 'expression'); |
||
887 | } |
||
888 | |||
889 | private function isBrackedExpression($out) |
||
890 | { |
||
891 | return (isset($out['expr_type']) && $out['expr_type'] === 'bracked_expression'); |
||
892 | } |
||
893 | |||
894 | private function isSubQuery($out) |
||
895 | { |
||
896 | return (isset($out['expr_type']) && $out['expr_type'] === 'subquery'); |
||
897 | } |
||
898 | |||
899 | /* This fuction processes each SELECT clause. We determine what (if any) alias |
||
900 | is provided, and we set the type of expression. |
||
901 | */ |
||
902 | private function process_select_expr($expression) |
||
1009 | } |
||
1010 | |||
1011 | private function process_from(&$tokens) |
||
1172 | } |
||
1173 | |||
1174 | private function initParseInfoForFrom($parseInfo = false) |
||
1175 | { |
||
1176 | // first init |
||
1177 | if ($parseInfo === false) |
||
1178 | { |
||
1179 | $parseInfo = array('join_type' => "", 'saved_join_type' => "JOIN"); |
||
1180 | } |
||
1181 | |||
1182 | // loop init |
||
1183 | return array('expression' => "", 'token_count' => 0, 'table' => "", 'alias' => false, 'join_type' => "", |
||
1184 | 'next_join_type' => "", 'saved_join_type' => $parseInfo['saved_join_type'], |
||
1185 | 'ref_type' => false, 'ref_expr' => false, 'base_expr' => false, 'sub_tree' => false, 'index_hints' => false, |
||
1186 | 'subquery' => ""); |
||
1187 | } |
||
1188 | |||
1189 | private function processFromExpression(&$parseInfo) |
||
1190 | { |
||
1191 | |||
1192 | $res = array(); |
||
1193 | |||
1194 | // exchange the join types (join_type is save now, saved_join_type holds the next one) |
||
1195 | $parseInfo['join_type'] = $parseInfo['saved_join_type']; // initialized with JOIN |
||
1196 | $parseInfo['saved_join_type'] = ($parseInfo['next_join_type'] ? $parseInfo['next_join_type'] : 'JOIN'); |
||
1197 | |||
1198 | // we have a reg_expr, so we have to parse it |
||
1199 | if ($parseInfo['ref_expr'] !== false) |
||
1200 | { |
||
1201 | $unparsed = $this->split_sql($this->removeParenthesisFromStart($parseInfo['ref_expr'])); |
||
1202 | |||
1203 | // here we can get a comma separated list |
||
1204 | foreach ($unparsed as $k => $v) |
||
1205 | { |
||
1206 | if (trim($v) === ',') |
||
1207 | { |
||
1208 | $unparsed[$k] = ""; |
||
1209 | } |
||
1210 | } |
||
1211 | $parseInfo['ref_expr'] = $this->process_expr_list($unparsed); |
||
1212 | } |
||
1213 | |||
1214 | // there is an expression, we have to parse it |
||
1215 | if (substr(trim($parseInfo['table']), 0, 1) == '(') |
||
1216 | { |
||
1217 | $parseInfo['expression'] = $this->removeParenthesisFromStart($parseInfo['table']); |
||
1218 | |||
1219 | if (preg_match("/^\\s*select/i", $parseInfo['expression'])) |
||
1220 | { |
||
1221 | $parseInfo['sub_tree'] = $this->parse($parseInfo['expression']); |
||
1222 | $res['expr_type'] = 'subquery'; |
||
1223 | } |
||
1224 | else |
||
1225 | { |
||
1226 | $tmp = $this->split_sql($parseInfo['expression']); |
||
1227 | $parseInfo['sub_tree'] = $this->process_from($tmp); |
||
1228 | $res['expr_type'] = 'table_expression'; |
||
1229 | $res['union_tree'] = $tmp; |
||
1230 | } |
||
1231 | } |
||
1232 | else |
||
1233 | { |
||
1234 | $res['expr_type'] = 'table'; |
||
1235 | $res['table'] = $parseInfo['table']; |
||
1236 | } |
||
1237 | |||
1238 | $res['alias'] = $parseInfo['alias']; |
||
1239 | $res['join_type'] = $parseInfo['join_type']; |
||
1240 | $res['ref_type'] = $parseInfo['ref_type']; |
||
1241 | $res['ref_clause'] = $parseInfo['ref_expr']; |
||
1242 | $res['base_expr'] = trim($parseInfo['expression']); |
||
1243 | $res['sub_tree'] = $parseInfo['sub_tree']; |
||
1244 | $res['index_hints'] = $parseInfo['index_hints']; |
||
1245 | |||
1246 | return $res; |
||
1247 | } |
||
1248 | |||
1249 | private function processOrderExpression(&$parseInfo, $select, $groupby = false) |
||
1250 | { |
||
1251 | $parseInfo['expr'] = trim($parseInfo['expr']); |
||
1252 | |||
1253 | if ($parseInfo['expr'] === "") |
||
1254 | { |
||
1255 | return false; |
||
1256 | } |
||
1257 | |||
1258 | if ($groupby) |
||
1259 | $parseInfo['expr'] = trim($parseInfo['expr']); |
||
1260 | else |
||
1261 | $parseInfo['expr'] = trim($this->revokeEscaping($parseInfo['expr'])); |
||
1262 | |||
1263 | if (is_numeric($parseInfo['expr'])) |
||
1264 | { |
||
1265 | $parseInfo['type'] = 'pos'; |
||
1266 | } |
||
1267 | else |
||
1268 | { |
||
1269 | // search to see if the expression matches an alias |
||
1270 | foreach ($select as $clause) |
||
1271 | { |
||
1272 | if (!$clause['alias']) |
||
1273 | { |
||
1274 | continue; |
||
1275 | } |
||
1276 | if ($clause['alias']['name'] === $parseInfo['expr']) |
||
1277 | { |
||
1278 | $parseInfo['type'] = 'alias'; |
||
1279 | } |
||
1280 | } |
||
1281 | } |
||
1282 | |||
1283 | if ($parseInfo['type'] === "expression") |
||
1284 | { |
||
1285 | $expr = $this->process_select_expr($parseInfo['expr']); |
||
1286 | $expr['direction'] = $parseInfo['dir']; |
||
1287 | unset($expr['alias']); |
||
1288 | |||
1289 | return $expr; |
||
1290 | } |
||
1291 | |||
1292 | return array('expr_type' => $parseInfo['type'], 'base_expr' => $parseInfo['expr'], |
||
1293 | 'direction' => $parseInfo['dir']); |
||
1294 | } |
||
1295 | |||
1296 | private function initParseInfoForOrder() |
||
1297 | { |
||
1298 | return array('base_expr' => "", 'expr' => "", 'dir' => "ASC", 'type' => 'expression'); |
||
1299 | } |
||
1300 | |||
1301 | private function process_order($tokens, $select) |
||
1302 | { |
||
1303 | $out = array(); |
||
1304 | $parseInfo = $this->initParseInfoForOrder(); |
||
1305 | |||
1306 | if (!$tokens) |
||
1307 | { |
||
1308 | return false; |
||
1309 | } |
||
1310 | |||
1311 | foreach ($tokens as $token) |
||
1312 | { |
||
1313 | $upper = strtoupper(trim($token)); |
||
1314 | switch ($upper) |
||
1315 | { |
||
1316 | case ',': |
||
1317 | $out[] = $this->processOrderExpression($parseInfo, $select); |
||
1318 | $parseInfo = $this->initParseInfoForOrder(); |
||
1319 | break; |
||
1320 | |||
1321 | case 'DESC': |
||
1322 | $parseInfo['dir'] = "DESC"; |
||
1323 | break; |
||
1324 | |||
1325 | case 'ASC': |
||
1326 | $parseInfo['dir'] = "ASC"; |
||
1327 | break; |
||
1328 | |||
1329 | default: |
||
1330 | $parseInfo['expr'] .= $token; |
||
1331 | |||
1332 | } |
||
1333 | } |
||
1334 | |||
1335 | $out[] = $this->processOrderExpression($parseInfo, $select); |
||
1336 | |||
1337 | return $out; |
||
1338 | } |
||
1339 | |||
1340 | private function process_group($tokens, $select) |
||
1373 | } |
||
1374 | |||
1375 | private function removeParenthesisFromStart($token) |
||
1376 | { |
||
1377 | |||
1378 | $parenthesisRemoved = 0; |
||
1379 | |||
1380 | $trim = trim($token); |
||
1381 | if ($trim !== "" && $trim[0] === "(") |
||
1382 | { // remove only one parenthesis pair now! |
||
1383 | $parenthesisRemoved++; |
||
1384 | $trim[0] = " "; |
||
1385 | $trim = trim($trim); |
||
1386 | } |
||
1387 | |||
1388 | $parenthesis = $parenthesisRemoved; |
||
1389 | $i = 0; |
||
1390 | $string = 0; |
||
1391 | while ($i < strlen($trim)) |
||
1392 | { |
||
1393 | |||
1394 | if ($trim[$i] === "\\") |
||
1395 | { |
||
1396 | $i += 2; // an escape character, the next character is irrelevant |
||
1397 | continue; |
||
1398 | } |
||
1399 | |||
1400 | if ($trim[$i] === "'" || $trim[$i] === '"') |
||
1401 | { |
||
1402 | $string++; |
||
1403 | } |
||
1404 | |||
1405 | if (($string % 2 === 0) && ($trim[$i] === "(")) |
||
1406 | { |
||
1407 | $parenthesis++; |
||
1408 | } |
||
1409 | |||
1410 | if (($string % 2 === 0) && ($trim[$i] === ")")) |
||
1411 | { |
||
1412 | if ($parenthesis == $parenthesisRemoved) |
||
1413 | { |
||
1414 | $trim[$i] = " "; |
||
1415 | $parenthesisRemoved--; |
||
1416 | } |
||
1417 | $parenthesis--; |
||
1418 | } |
||
1419 | $i++; |
||
1420 | } |
||
1421 | |||
1422 | return trim($trim); |
||
1423 | } |
||
1424 | |||
1425 | private function initParseInfoExprList($parseInfo = false) |
||
1426 | { |
||
1427 | if ($parseInfo === false) |
||
1428 | { |
||
1429 | return array( |
||
1430 | 'processed' => false, |
||
1431 | 'expr' => "", |
||
1432 | 'key' => false, |
||
1433 | 'token' => false, |
||
1434 | 'tokenType' => "", |
||
1435 | 'prevToken' => "", |
||
1436 | 'prevTokenType' => "", |
||
1437 | 'trim' => false, |
||
1438 | 'upper' => false |
||
1439 | ); |
||
1440 | } |
||
1441 | |||
1442 | $expr = empty($parseInfo['expr']) ? array() : $parseInfo['expr']; |
||
1443 | $expr[] = array('expr_type' => $parseInfo['tokenType'], 'base_expr' => $parseInfo['token'], |
||
1444 | 'sub_tree' => $parseInfo['processed']); |
||
1445 | |||
1446 | return array('processed' => false, 'expr' => $expr, 'key' => false, 'token' => false, 'tokenType' => "", |
||
1447 | 'prevToken' => $parseInfo['upper'], 'prevTokenType' => $parseInfo['tokenType'], |
||
1448 | 'trim' => false, 'upper' => false); |
||
1449 | } |
||
1450 | |||
1451 | /* Some sections are just lists of expressions, like the WHERE and HAVING clauses. This function |
||
1452 | processes these sections. Recursive. |
||
1453 | */ |
||
1454 | private function process_expr_list($tokens) |
||
1455 | { |
||
1456 | |||
1457 | $parseInfo = $this->initParseInfoExprList(); |
||
1458 | $skip_next = false; |
||
1459 | |||
1460 | foreach ($tokens as $parseInfo['key'] => $parseInfo['token']) |
||
1461 | { |
||
1462 | |||
1463 | $parseInfo['trim'] = trim($parseInfo['token']); |
||
1464 | |||
1465 | if ($parseInfo['trim'] === "") |
||
1466 | { |
||
1467 | continue; |
||
1468 | } |
||
1469 | |||
1470 | if ($skip_next) |
||
1471 | { |
||
1472 | // skip the next non-whitespace token |
||
1473 | $skip_next = false; |
||
1474 | continue; |
||
1475 | } |
||
1476 | |||
1477 | $parseInfo['upper'] = strtoupper($parseInfo['trim']); |
||
1478 | |||
1479 | /* is it a subquery?*/ |
||
1480 | if (preg_match("/^\\(\\s*SELECT/i", $parseInfo['trim'])) |
||
1481 | { |
||
1482 | // tokenize and parse the subquery. |
||
1483 | // we remove the enclosing parenthesis for the tokenizer |
||
1484 | $parseInfo['processed'] = $this->parse($this->removeParenthesisFromStart($parseInfo['trim'])); |
||
1485 | $parseInfo['tokenType'] = 'subquery'; |
||
1486 | } |
||
1487 | elseif ($parseInfo['upper'][0] === '(' && substr($parseInfo['upper'], -1) === ')') |
||
1488 | { |
||
1489 | /* is it an inlist (upper is derived from trim!) */ |
||
1490 | |||
1491 | // if we have a colref followed by a parenthesis pair, |
||
1492 | // it isn't a colref, it is a user-function |
||
1493 | if ($parseInfo['prevTokenType'] === 'colref' || $parseInfo['prevTokenType'] === 'function' |
||
1494 | || $parseInfo['prevTokenType'] === 'aggregate_function') |
||
1495 | { |
||
1496 | |||
1497 | $tmptokens = $this->split_sql($this->removeParenthesisFromStart($parseInfo['trim'])); |
||
1498 | foreach ($tmptokens as $k => $v) |
||
1499 | { |
||
1500 | if (trim($v) == ',') |
||
1501 | { |
||
1502 | unset($tmptokens[$k]); |
||
1503 | } |
||
1504 | } |
||
1505 | |||
1506 | $tmptokens = array_values($tmptokens); |
||
1507 | $parseInfo['processed'] = $this->process_expr_list($tmptokens); |
||
1508 | |||
1509 | $last = array_pop($parseInfo['expr']); |
||
1510 | $parseInfo['token'] = $last['base_expr']; |
||
1511 | $parseInfo['tokenType'] = ($parseInfo['prevTokenType'] === 'colref' ? 'function' |
||
1512 | : $parseInfo['prevTokenType']); |
||
1513 | $parseInfo['prevTokenType'] = $parseInfo['prevToken'] = ""; |
||
1514 | } |
||
1515 | |||
1516 | if ($parseInfo['prevToken'] == 'IN') |
||
1517 | { |
||
1518 | |||
1519 | $tmptokens = $this->split_sql($this->removeParenthesisFromStart($parseInfo['trim'])); |
||
1520 | foreach ($tmptokens as $k => $v) |
||
1521 | { |
||
1522 | if (trim($v) == ',') |
||
1523 | { |
||
1524 | unset($tmptokens[$k]); |
||
1525 | } |
||
1526 | } |
||
1527 | |||
1528 | $tmptokens = array_values($tmptokens); |
||
1529 | $parseInfo['processed'] = $this->process_expr_list($tmptokens); |
||
1530 | $parseInfo['prevTokenType'] = $parseInfo['prevToken'] = ""; |
||
1531 | $parseInfo['tokenType'] = "in-list"; |
||
1532 | } |
||
1533 | |||
1534 | if ($parseInfo['prevToken'] == 'AGAINST') |
||
1535 | { |
||
1536 | |||
1537 | $tmptokens = $this->split_sql($this->removeParenthesisFromStart($parseInfo['trim'])); |
||
1538 | // Todo: rewiew later this was not set |
||
1539 | $list[0] = null; |
||
1540 | if (count($tmptokens) > 1) |
||
1541 | { |
||
1542 | $match_mode = implode('', array_slice($tmptokens, 1)); |
||
1543 | $parseInfo['processed'] = array($list[0], $match_mode); |
||
1544 | } |
||
1545 | else |
||
1546 | { |
||
1547 | $parseInfo['processed'] = $list[0]; |
||
1548 | } |
||
1549 | |||
1550 | $parseInfo['prevTokenType'] = $parseInfo['prevToken'] = ""; |
||
1551 | $parseInfo['tokenType'] = "match-arguments"; |
||
1552 | } |
||
1553 | |||
1554 | } |
||
1555 | elseif ($parseInfo['upper'][0] === '@') |
||
1556 | { |
||
1557 | // a variable |
||
1558 | $parseInfo['tokenType'] = $this->getVariableType($parseInfo['upper']); |
||
1559 | $parseInfo['processed'] = false; |
||
1560 | } |
||
1561 | else |
||
1562 | { |
||
1563 | /* it is either an operator, a colref or a constant */ |
||
1564 | switch ($parseInfo['upper']) |
||
1565 | { |
||
1566 | |||
1567 | case '*': |
||
1568 | $parseInfo['processed'] = false; // no subtree |
||
1569 | |||
1570 | // last token is colref, const or expression |
||
1571 | // it is an operator, in all other cases it is an all-columns-alias |
||
1572 | // if the previous colref ends with a dot, the * is the all-columns-alias |
||
1573 | if (!is_array($parseInfo['expr'])) |
||
1574 | { |
||
1575 | $parseInfo['tokenType'] = "colref"; // single or first element of select -> * |
||
1576 | break; |
||
1577 | } |
||
1578 | |||
1579 | $last = array_pop($parseInfo['expr']); |
||
1580 | if ($last['expr_type'] !== 'colref' && $last['expr_type'] !== 'const' |
||
1581 | && $last['expr_type'] !== 'expression') |
||
1582 | { |
||
1583 | $parseInfo['expr'][] = $last; |
||
1584 | $parseInfo['tokenType'] = "colref"; |
||
1585 | break; |
||
1586 | } |
||
1587 | |||
1588 | if ($last['expr_type'] === 'colref' && substr($last['base_expr'], -1, 1) === ".") |
||
1589 | { |
||
1590 | $last['base_expr'] .= '*'; // tablealias dot * |
||
1591 | $parseInfo['expr'][] = $last; |
||
1592 | continue 2; |
||
1593 | } |
||
1594 | |||
1595 | $parseInfo['expr'][] = $last; |
||
1596 | $parseInfo['tokenType'] = "operator"; |
||
1597 | break; |
||
1598 | |||
1599 | case 'AND': |
||
1600 | case '&&': |
||
1601 | case 'BETWEEN': |
||
1602 | case 'BINARY': |
||
1603 | case '&': |
||
1604 | case '~': |
||
1605 | case '|': |
||
1606 | case '^': |
||
1607 | case 'DIV': |
||
1608 | case '/': |
||
1609 | case '<=>': |
||
1610 | case '=': |
||
1611 | case '>=': |
||
1612 | case '>': |
||
1613 | case 'IS': |
||
1614 | case 'NOT': |
||
1615 | case '<<': |
||
1616 | case '<=': |
||
1617 | case '<': |
||
1618 | case 'LIKE': |
||
1619 | case '%': |
||
1620 | case '!=': |
||
1621 | case '<>': |
||
1622 | case 'REGEXP': |
||
1623 | case '!': |
||
1624 | case '||': |
||
1625 | case 'OR': |
||
1626 | case '>>': |
||
1627 | case 'RLIKE': |
||
1628 | case 'SOUNDS': |
||
1629 | case 'XOR': |
||
1630 | case 'IN': |
||
1631 | $parseInfo['processed'] = false; |
||
1632 | $parseInfo['tokenType'] = "operator"; |
||
1633 | break; |
||
1634 | |||
1635 | case 'NULL': |
||
1636 | $parseInfo['processed'] = false; |
||
1637 | $parseInfo['tokenType'] = 'const'; |
||
1638 | break; |
||
1639 | |||
1640 | case '-': |
||
1641 | case '+': |
||
1642 | // differ between preceding sign and operator |
||
1643 | $parseInfo['processed'] = false; |
||
1644 | |||
1645 | if ($parseInfo['prevTokenType'] === 'colref' || $parseInfo['prevTokenType'] === 'function' |
||
1646 | || $parseInfo['prevTokenType'] === 'aggregate_function' |
||
1647 | || $parseInfo['prevTokenType'] === 'const' |
||
1648 | || $parseInfo['prevTokenType'] === 'subquery') |
||
1649 | { |
||
1650 | $parseInfo['tokenType'] = "operator"; |
||
1651 | } |
||
1652 | else |
||
1653 | { |
||
1654 | $parseInfo['tokenType'] = "sign"; |
||
1655 | } |
||
1656 | break; |
||
1657 | |||
1658 | default: |
||
1659 | switch ($parseInfo['token'][0]) |
||
1660 | { |
||
1661 | case "'": |
||
1662 | case '"': |
||
1663 | $parseInfo['tokenType'] = 'const'; |
||
1664 | break; |
||
1665 | case '`': |
||
1666 | $parseInfo['tokenType'] = 'colref'; |
||
1667 | break; |
||
1668 | |||
1669 | default: |
||
1670 | if (is_numeric($parseInfo['token'])) |
||
1671 | { |
||
1672 | $parseInfo['tokenType'] = 'const'; |
||
1673 | |||
1674 | if ($parseInfo['prevTokenType'] === 'sign') |
||
1675 | { |
||
1676 | array_pop($parseInfo['expr']); |
||
1677 | $parseInfo['token'] = $parseInfo['prevToken'] . $parseInfo['token']; |
||
1678 | } |
||
1679 | |||
1680 | } |
||
1681 | else |
||
1682 | { |
||
1683 | $parseInfo['tokenType'] = 'colref'; |
||
1684 | } |
||
1685 | break; |
||
1686 | |||
1687 | } |
||
1688 | $parseInfo['processed'] = false; |
||
1689 | } |
||
1690 | } |
||
1691 | |||
1692 | /* is a reserved word? */ |
||
1693 | if ($parseInfo['tokenType'] !== 'operator' && $parseInfo['tokenType'] !== 'in-list' |
||
1694 | && $parseInfo['tokenType'] !== 'function' && $parseInfo['tokenType'] !== 'aggregate_function' |
||
1695 | && in_array($parseInfo['upper'], parent::$reserved)) |
||
1696 | { |
||
1697 | |||
1698 | switch ($parseInfo['upper']) |
||
1699 | { |
||
1700 | case 'AVG': |
||
1701 | case 'SUM': |
||
1702 | case 'COUNT': |
||
1703 | case 'MIN': |
||
1704 | case 'MAX': |
||
1705 | case 'STDDEV': |
||
1706 | case 'STDDEV_SAMP': |
||
1707 | case 'STDDEV_POP': |
||
1708 | case 'VARIANCE': |
||
1709 | case 'VAR_SAMP': |
||
1710 | case 'VAR_POP': |
||
1711 | case 'GROUP_CONCAT': |
||
1712 | case 'BIT_AND': |
||
1713 | case 'BIT_OR': |
||
1714 | case 'BIT_XOR': |
||
1715 | $parseInfo['tokenType'] = 'aggregate_function'; |
||
1716 | break; |
||
1717 | |||
1718 | case 'NULL': |
||
1719 | // it is a reserved word, but we would like to have set it as constant |
||
1720 | $parseInfo['tokenType'] = 'const'; |
||
1721 | break; |
||
1722 | |||
1723 | default: |
||
1724 | if (in_array($parseInfo['upper'], parent::$functions)) |
||
1725 | { |
||
1726 | $parseInfo['tokenType'] = 'function'; |
||
1727 | } |
||
1728 | else |
||
1729 | { |
||
1730 | $parseInfo['tokenType'] = 'reserved'; |
||
1731 | } |
||
1732 | break; |
||
1733 | } |
||
1734 | } |
||
1735 | |||
1736 | if (!$parseInfo['tokenType']) |
||
1737 | { |
||
1738 | if ($parseInfo['upper'][0] === '(') |
||
1739 | { |
||
1740 | $local_expr = $this->removeParenthesisFromStart($parseInfo['trim']); |
||
1741 | $parseInfo['tokenType'] = 'bracket_expression'; |
||
1742 | } |
||
1743 | else |
||
1744 | { |
||
1745 | $local_expr = $parseInfo['trim']; |
||
1746 | $parseInfo['tokenType'] = 'expression'; |
||
1747 | } |
||
1748 | $parseInfo['processed'] = $this->process_expr_list($this->split_sql($local_expr)); |
||
1749 | } |
||
1750 | |||
1751 | $parseInfo = $this->initParseInfoExprList($parseInfo); |
||
1752 | } // end of for-loop |
||
1753 | |||
1754 | return (is_array($parseInfo['expr']) ? $parseInfo['expr'] : false); |
||
1755 | } |
||
1756 | |||
1757 | private function process_update($tokens) |
||
1759 | |||
1760 | } |
||
1761 | |||
1762 | private function process_delete($tokens) |
||
1763 | { |
||
1764 | $tables = array(); |
||
1765 | |||
1766 | foreach ($tokens['DELETE'] as $expression) |
||
1767 | { |
||
1768 | if ($expression != 'DELETE' && trim($expression, ' .*') != "" && $expression != ',') |
||
1769 | { |
||
1770 | $tables[] = trim($expression, '.* '); |
||
1771 | } |
||
1772 | } |
||
1773 | |||
1774 | if (empty($tables)) |
||
1775 | { |
||
1776 | foreach ($tokens['FROM'] as $table) |
||
1777 | { |
||
1778 | $tables[] = $table['table']; |
||
1779 | } |
||
1780 | } |
||
1781 | |||
1782 | $tokens['DELETE'] = array('TABLES' => $tables); |
||
1783 | |||
1784 | return $tokens; |
||
1785 | } |
||
1786 | |||
1787 | private function process_insert($tokens, $token_category = 'INSERT') |
||
1825 | } |
||
1826 | |||
1827 | private function process_record($unparsed) |
||
1828 | { |
||
1829 | |||
1830 | $unparsed = $this->removeParenthesisFromStart($unparsed); |
||
1831 | $values = $this->split_sql($unparsed); |
||
1832 | |||
1833 | foreach ($values as $k => $v) |
||
1834 | { |
||
1835 | if (trim($v) === ",") |
||
1836 | { |
||
1837 | $values[$k] = ""; |
||
1838 | } |
||
1839 | } |
||
1840 | |||
1841 | return $this->process_expr_list($values); |
||
1842 | } |
||
1843 | |||
1844 | private function process_values($tokens) |
||
1845 | { |
||
1846 | |||
1847 | $unparsed = ""; |
||
1848 | foreach ($tokens['VALUES'] as $k => $v) |
||
1849 | { |
||
1850 | if (trim($v) === "") |
||
1851 | { |
||
1852 | continue; |
||
1853 | } |
||
1854 | $unparsed .= $v; |
||
1855 | } |
||
1856 | |||
1857 | $values = $this->split_sql($unparsed); |
||
1858 | |||
1859 | foreach ($values as $k => $v) |
||
1860 | { |
||
1861 | if (trim($v) === ",") |
||
1862 | { |
||
1863 | unset($values[$k]); |
||
1864 | } |
||
1865 | else |
||
1866 | { |
||
1867 | $values[$k] = array('expr_type' => 'record', 'base_expr' => $v, 'data' => $this->process_record($v)); |
||
1868 | } |
||
1869 | } |
||
1870 | |||
1871 | $tokens['VALUES'] = array_values($values); |
||
1872 | |||
1873 | return $tokens; |
||
1874 | } |
||
1875 | |||
1876 | /** |
||
1877 | * TODO: This is a dummy function, we cannot parse INTO as part of SELECT |
||
1878 | * at the moment |
||
1879 | */ |
||
1880 | private function process_into($tokens) |
||
1893 | } |
||
1894 | } |
||
1895 |
This check looks for function or method calls that always return null and whose return value is assigned to a variable.
The method
getObject()
can return nothing but null, so it makes no sense to assign that value to a variable.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.