PluginProbe
SQLite Database Integration / 2.2.2
SQLite Database Integration v2.2.2
3.0.2 3.0.1 trunk 2.1.13 2.1.14 2.1.15 2.1.16 2.2.0 2.2.1 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.17 2.2.18 2.2.19 2.2.2 2.2.20 2.2.21 2.2.22 2.2.23 2.2.3 All 32 releases
sqlite-database-integration / wp-includes / sqlite / class-wp-sqlite-lexer.php

class-wp-sqlite-lexer.php in SQLite Database Integration 2.2.2, at wp-includes/sqlite/class-wp-sqlite-lexer.php

2,576 lines 85.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * This file is a port of the Lexer & TokensList classes from the PHPMyAdmin/sql-parser library.
4 *
5 * @package wp-sqlite-integration
6 * @see https://github.com/phpmyadmin/sql-parser
7 */
8
9 /**
10 * Performs lexical analysis over a SQL statement and splits it in multiple tokens.
11 */
12 class WP_SQLite_Lexer {
13
14 /**
15 * The maximum length of a keyword.
16 */
17 const KEYWORD_MAX_LENGTH = 30;
18
19 /**
20 * The maximum length of a label.
21 *
22 * Ref: https://dev.mysql.com/doc/refman/5.7/en/statement-labels.html
23 */
24 const LABEL_MAX_LENGTH = 16;
25
26 /**
27 * The maximum length of an operator.
28 */
29 const OPERATOR_MAX_LENGTH = 4;
30
31 /**
32 * A list of methods that are used in lexing the SQL query.
33 *
34 * @var string[]
35 */
36 const PARSER_METHODS = array(
37 // It is best to put the parsers in order of their complexity
38 // (ascending) and their occurrence rate (descending).
39 //
40 // Conflicts:
41 //
42 // 1. `parse_delimiter`, `parse_unknown`, `parse_keyword`, `parse_number`
43 // They fight over delimiter. The delimiter may be a keyword, a
44 // number or almost any character which makes the delimiter one of
45 // the first tokens that must be parsed.
46 //
47 // 1. `parse_number` and `parse_operator`
48 // They fight over `+` and `-`.
49 //
50 // 2. `parse_comment` and `parse_operator`
51 // They fight over `/` (as in ```/*comment*/``` or ```a / b```)
52 //
53 // 3. `parse_bool` and `parse_keyword`
54 // They fight over `TRUE` and `FALSE`.
55 //
56 // 4. `parse_keyword` and `parse_unknown`
57 // They fight over words. `parse_unknown` does not know about
58 // keywords.
59
60 'parse_delimiter',
61 'parse_whitespace',
62 'parse_number',
63 'parse_comment',
64 'parse_operator',
65 'parse_bool',
66 'parse_string',
67 'parse_symbol',
68 'parse_keyword',
69 'parse_label',
70 'parse_unknown',
71 );
72
73
74 /**
75 * A list of keywords that indicate that the function keyword
76 * is not used as a function.
77 *
78 * @var string[]
79 */
80 const KEYWORD_NAME_INDICATORS = array(
81 'FROM',
82 'SET',
83 'WHERE',
84 );
85
86 /**
87 * A list of operators that indicate that the function keyword
88 * is not used as a function.
89 *
90 * @var string[]
91 */
92 const OPERATOR_NAME_INDICATORS = array(
93 ',',
94 '.',
95 );
96
97 /**
98 * The string to be parsed.
99 *
100 * @var string
101 */
102 public $str = '';
103
104 /**
105 * The length of `$str`.
106 *
107 * By storing its length, a lot of time is saved, because parsing methods
108 * would call `strlen` everytime.
109 *
110 * @var int
111 */
112 public $string_length = 0;
113
114 /**
115 * The index of the last parsed character.
116 *
117 * @var int
118 */
119 public $last = 0;
120
121 /**
122 * The default delimiter. This is used, by default, in all new instances.
123 *
124 * @var string
125 */
126 public static $default_delimiter = ';';
127
128 /**
129 * Statements delimiter.
130 * This may change during lexing.
131 *
132 * @var string
133 */
134 public $delimiter;
135
136 /**
137 * The length of the delimiter.
138 *
139 * Because `parse_delimiter` can be called a lot, it would perform a lot of
140 * calls to `strlen`, which might affect performance when the delimiter is
141 * big.
142 *
143 * @var int
144 */
145 public $delimiter_length;
146
147 /**
148 * List of operators and their flags.
149 *
150 * @var array<string, int>
151 */
152 public static $operators = array(
153
154 /*
155 * Some operators (*, =) may have ambiguous flags, because they depend on
156 * the context they are being used in.
157 * For example: 1. SELECT * FROM table; # SQL specific (wildcard)
158 * SELECT 2 * 3; # arithmetic
159 * 2. SELECT * FROM table WHERE foo = 'bar';
160 * SET @i = 0;
161 */
162
163 // @see WP_SQLite_Token::FLAG_OPERATOR_ARITHMETIC
164 '%' => 1,
165 '*' => 1,
166 '+' => 1,
167 '-' => 1,
168 '/' => 1,
169
170 // @see WP_SQLite_Token::FLAG_OPERATOR_LOGICAL
171 '!' => 2,
172 '!=' => 2,
173 '&&' => 2,
174 '<' => 2,
175 '<=' => 2,
176 '<=>' => 2,
177 '<>' => 2,
178 '=' => 2,
179 '>' => 2,
180 '>=' => 2,
181 '||' => 2,
182
183 // @see WP_SQLite_Token::FLAG_OPERATOR_BITWISE
184 '&' => 4,
185 '<<' => 4,
186 '>>' => 4,
187 '^' => 4,
188 '|' => 4,
189 '~' => 4,
190
191 // @see WP_SQLite_Token::FLAG_OPERATOR_ASSIGNMENT
192 ':=' => 8,
193
194 // @see WP_SQLite_Token::FLAG_OPERATOR_SQL
195 '(' => 16,
196 ')' => 16,
197 '.' => 16,
198 ',' => 16,
199 ';' => 16,
200 );
201
202 /**
203 * List of keywords.
204 *
205 * The value associated to each keyword represents its flags.
206 *
207 * @see WP_SQLite_Token::FLAG_KEYWORD_RESERVED
208 * WP_SQLite_Token::FLAG_KEYWORD_COMPOSED
209 * WP_SQLite_Token::FLAG_KEYWORD_DATA_TYPE
210 * ∂WP_SQLite_Token::FLAG_KEYWORD_KEY
211 * WP_SQLite_Token::FLAG_KEYWORD_FUNCTION
212 *
213 * @var array<string,int>
214 */
215 public static $keywords = array(
216 'AT' => 1,
217 'DO' => 1,
218 'IO' => 1,
219 'NO' => 1,
220 'XA' => 1,
221 'ANY' => 1,
222 'CPU' => 1,
223 'END' => 1,
224 'IPC' => 1,
225 'NDB' => 1,
226 'NEW' => 1,
227 'ONE' => 1,
228 'ROW' => 1,
229 'XID' => 1,
230 'BOOL' => 1,
231 'BYTE' => 1,
232 'CODE' => 1,
233 'CUBE' => 1,
234 'DATA' => 1,
235 'DISK' => 1,
236 'ENDS' => 1,
237 'FAST' => 1,
238 'FILE' => 1,
239 'FULL' => 1,
240 'HASH' => 1,
241 'HELP' => 1,
242 'HOST' => 1,
243 'LAST' => 1,
244 'LESS' => 1,
245 'LIST' => 1,
246 'LOGS' => 1,
247 'MODE' => 1,
248 'NAME' => 1,
249 'NEXT' => 1,
250 'NONE' => 1,
251 'ONLY' => 1,
252 'OPEN' => 1,
253 'PAGE' => 1,
254 'PORT' => 1,
255 'PREV' => 1,
256 'ROWS' => 1,
257 'SLOW' => 1,
258 'SOME' => 1,
259 'STOP' => 1,
260 'THAN' => 1,
261 'TYPE' => 1,
262 'VIEW' => 1,
263 'WAIT' => 1,
264 'WORK' => 1,
265 'X509' => 1,
266 'AFTER' => 1,
267 'BEGIN' => 1,
268 'BLOCK' => 1,
269 'BTREE' => 1,
270 'CACHE' => 1,
271 'CHAIN' => 1,
272 'CLOSE' => 1,
273 'ERROR' => 1,
274 'EVENT' => 1,
275 'EVERY' => 1,
276 'FIRST' => 1,
277 'FIXED' => 1,
278 'FLUSH' => 1,
279 'FOUND' => 1,
280 'HOSTS' => 1,
281 'LEVEL' => 1,
282 'LOCAL' => 1,
283 'LOCKS' => 1,
284 'MERGE' => 1,
285 'MUTEX' => 1,
286 'NAMES' => 1,
287 'NCHAR' => 1,
288 'NEVER' => 1,
289 'OWNER' => 1,
290 'PHASE' => 1,
291 'PROXY' => 1,
292 'QUERY' => 1,
293 'QUICK' => 1,
294 'RELAY' => 1,
295 'RESET' => 1,
296 'RTREE' => 1,
297 'SHARE' => 1,
298 'SLAVE' => 1,
299 'START' => 1,
300 'SUPER' => 1,
301 'SWAPS' => 1,
302 'TYPES' => 1,
303 'UNTIL' => 1,
304 'VALUE' => 1,
305 'ACTION' => 1,
306 'ALWAYS' => 1,
307 'BACKUP' => 1,
308 'BINLOG' => 1,
309 'CIPHER' => 1,
310 'CLIENT' => 1,
311 'COMMIT' => 1,
312 'ENABLE' => 1,
313 'ENGINE' => 1,
314 'ERRORS' => 1,
315 'ESCAPE' => 1,
316 'EVENTS' => 1,
317 'EXPIRE' => 1,
318 'EXPORT' => 1,
319 'FAULTS' => 1,
320 'FIELDS' => 1,
321 'FILTER' => 1,
322 'GLOBAL' => 1,
323 'GRANTS' => 1,
324 'IMPORT' => 1,
325 'ISSUER' => 1,
326 'LEAVES' => 1,
327 'MASTER' => 1,
328 'MEDIUM' => 1,
329 'MEMORY' => 1,
330 'MODIFY' => 1,
331 'NUMBER' => 1,
332 'OFFSET' => 1,
333 'PARSER' => 1,
334 'PLUGIN' => 1,
335 'RELOAD' => 1,
336 'REMOVE' => 1,
337 'REPAIR' => 1,
338 'RESUME' => 1,
339 'ROLLUP' => 1,
340 'SERVER' => 1,
341 'SIGNED' => 1,
342 'SIMPLE' => 1,
343 'SOCKET' => 1,
344 'SONAME' => 1,
345 'SOUNDS' => 1,
346 'SOURCE' => 1,
347 'STARTS' => 1,
348 'STATUS' => 1,
349 'STRING' => 1,
350 'TABLES' => 1,
351 'ACCOUNT' => 1,
352 'ANALYSE' => 1,
353 'CHANGED' => 1,
354 'CHANNEL' => 1,
355 'COLUMNS' => 1,
356 'COMMENT' => 1,
357 'COMPACT' => 1,
358 'CONTEXT' => 1,
359 'CURRENT' => 1,
360 'DEFINER' => 1,
361 'DISABLE' => 1,
362 'DISCARD' => 1,
363 'DYNAMIC' => 1,
364 'ENGINES' => 1,
365 'EXECUTE' => 1,
366 'FOLLOWS' => 1,
367 'GENERAL' => 1,
368 'HANDLER' => 1,
369 'INDEXES' => 1,
370 'INSTALL' => 1,
371 'INVOKER' => 1,
372 'LOGFILE' => 1,
373 'MIGRATE' => 1,
374 'NO_WAIT' => 1,
375 'OPTIONS' => 1,
376 'PARTIAL' => 1,
377 'PERSIST' => 1,
378 'PLUGINS' => 1,
379 'PREPARE' => 1,
380 'PROFILE' => 1,
381 'REBUILD' => 1,
382 'RECOVER' => 1,
383 'RESTORE' => 1,
384 'RETURNS' => 1,
385 'ROUTINE' => 1,
386 'SESSION' => 1,
387 'STACKED' => 1,
388 'STORAGE' => 1,
389 'SUBJECT' => 1,
390 'SUSPEND' => 1,
391 'UNICODE' => 1,
392 'UNKNOWN' => 1,
393 'UPGRADE' => 1,
394 'USE_FRM' => 1,
395 'WITHOUT' => 1,
396 'WRAPPER' => 1,
397 'CASCADED' => 1,
398 'CHECKSUM' => 1,
399 'DATAFILE' => 1,
400 'DUMPFILE' => 1,
401 'EXCHANGE' => 1,
402 'EXTENDED' => 1,
403 'FUNCTION' => 1,
404 'LANGUAGE' => 1,
405 'MAX_ROWS' => 1,
406 'MAX_SIZE' => 1,
407 'MIN_ROWS' => 1,
408 'NATIONAL' => 1,
409 'NVARCHAR' => 1,
410 'PRECEDES' => 1,
411 'PRESERVE' => 1,
412 'PROFILES' => 1,
413 'REDOFILE' => 1,
414 'RELAYLOG' => 1,
415 'ROLLBACK' => 1,
416 'SCHEDULE' => 1,
417 'SECURITY' => 1,
418 'SEQUENCE' => 1,
419 'SHUTDOWN' => 1,
420 'SNAPSHOT' => 1,
421 'SWITCHES' => 1,
422 'TRIGGERS' => 1,
423 'UNDOFILE' => 1,
424 'WARNINGS' => 1,
425 'AGGREGATE' => 1,
426 'ALGORITHM' => 1,
427 'COMMITTED' => 1,
428 'DIRECTORY' => 1,
429 'DUPLICATE' => 1,
430 'EXPANSION' => 1,
431 'INVISIBLE' => 1,
432 'IO_THREAD' => 1,
433 'ISOLATION' => 1,
434 'NODEGROUP' => 1,
435 'PACK_KEYS' => 1,
436 'READ_ONLY' => 1,
437 'REDUNDANT' => 1,
438 'SAVEPOINT' => 1,
439 'SQL_CACHE' => 1,
440 'TEMPORARY' => 1,
441 'TEMPTABLE' => 1,
442 'UNDEFINED' => 1,
443 'UNINSTALL' => 1,
444 'VARIABLES' => 1,
445 'COMPLETION' => 1,
446 'COMPRESSED' => 1,
447 'CONCURRENT' => 1,
448 'CONNECTION' => 1,
449 'CONSISTENT' => 1,
450 'DEALLOCATE' => 1,
451 'IDENTIFIED' => 1,
452 'MASTER_SSL' => 1,
453 'NDBCLUSTER' => 1,
454 'PARTITIONS' => 1,
455 'PERSISTENT' => 1,
456 'PLUGIN_DIR' => 1,
457 'PRIVILEGES' => 1,
458 'REORGANIZE' => 1,
459 'REPEATABLE' => 1,
460 'ROW_FORMAT' => 1,
461 'SQL_THREAD' => 1,
462 'TABLESPACE' => 1,
463 'TABLE_NAME' => 1,
464 'VALIDATION' => 1,
465 'COLUMN_NAME' => 1,
466 'COMPRESSION' => 1,
467 'CURSOR_NAME' => 1,
468 'DIAGNOSTICS' => 1,
469 'EXTENT_SIZE' => 1,
470 'MASTER_HOST' => 1,
471 'MASTER_PORT' => 1,
472 'MASTER_USER' => 1,
473 'MYSQL_ERRNO' => 1,
474 'NONBLOCKING' => 1,
475 'PROCESSLIST' => 1,
476 'REPLICATION' => 1,
477 'SCHEMA_NAME' => 1,
478 'SQL_TSI_DAY' => 1,
479 'TRANSACTION' => 1,
480 'UNCOMMITTED' => 1,
481 'CATALOG_NAME' => 1,
482 'CLASS_ORIGIN' => 1,
483 'DEFAULT_AUTH' => 1,
484 'DES_KEY_FILE' => 1,
485 'INITIAL_SIZE' => 1,
486 'MASTER_DELAY' => 1,
487 'MESSAGE_TEXT' => 1,
488 'PARTITIONING' => 1,
489 'PERSIST_ONLY' => 1,
490 'RELAY_THREAD' => 1,
491 'SERIALIZABLE' => 1,
492 'SQL_NO_CACHE' => 1,
493 'SQL_TSI_HOUR' => 1,
494 'SQL_TSI_WEEK' => 1,
495 'SQL_TSI_YEAR' => 1,
496 'SUBPARTITION' => 1,
497 'COLUMN_FORMAT' => 1,
498 'INSERT_METHOD' => 1,
499 'MASTER_SSL_CA' => 1,
500 'RELAY_LOG_POS' => 1,
501 'SQL_TSI_MONTH' => 1,
502 'SUBPARTITIONS' => 1,
503 'AUTO_INCREMENT' => 1,
504 'AVG_ROW_LENGTH' => 1,
505 'KEY_BLOCK_SIZE' => 1,
506 'MASTER_LOG_POS' => 1,
507 'MASTER_SSL_CRL' => 1,
508 'MASTER_SSL_KEY' => 1,
509 'RELAY_LOG_FILE' => 1,
510 'SQL_TSI_MINUTE' => 1,
511 'SQL_TSI_SECOND' => 1,
512 'TABLE_CHECKSUM' => 1,
513 'USER_RESOURCES' => 1,
514 'AUTOEXTEND_SIZE' => 1,
515 'CONSTRAINT_NAME' => 1,
516 'DELAY_KEY_WRITE' => 1,
517 'FILE_BLOCK_SIZE' => 1,
518 'MASTER_LOG_FILE' => 1,
519 'MASTER_PASSWORD' => 1,
520 'MASTER_SSL_CERT' => 1,
521 'PARSE_GCOL_EXPR' => 1,
522 'REPLICATE_DO_DB' => 1,
523 'SQL_AFTER_GTIDS' => 1,
524 'SQL_TSI_QUARTER' => 1,
525 'SUBCLASS_ORIGIN' => 1,
526 'MASTER_SERVER_ID' => 1,
527 'REDO_BUFFER_SIZE' => 1,
528 'SQL_BEFORE_GTIDS' => 1,
529 'STATS_PERSISTENT' => 1,
530 'UNDO_BUFFER_SIZE' => 1,
531 'CONSTRAINT_SCHEMA' => 1,
532 'GROUP_REPLICATION' => 1,
533 'IGNORE_SERVER_IDS' => 1,
534 'MASTER_SSL_CAPATH' => 1,
535 'MASTER_SSL_CIPHER' => 1,
536 'RETURNED_SQLSTATE' => 1,
537 'SQL_BUFFER_RESULT' => 1,
538 'STATS_AUTO_RECALC' => 1,
539 'CONSTRAINT_CATALOG' => 1,
540 'MASTER_RETRY_COUNT' => 1,
541 'MASTER_SSL_CRLPATH' => 1,
542 'MAX_STATEMENT_TIME' => 1,
543 'REPLICATE_DO_TABLE' => 1,
544 'SQL_AFTER_MTS_GAPS' => 1,
545 'STATS_SAMPLE_PAGES' => 1,
546 'REPLICATE_IGNORE_DB' => 1,
547 'MASTER_AUTO_POSITION' => 1,
548 'MASTER_CONNECT_RETRY' => 1,
549 'MAX_QUERIES_PER_HOUR' => 1,
550 'MAX_UPDATES_PER_HOUR' => 1,
551 'MAX_USER_CONNECTIONS' => 1,
552 'REPLICATE_REWRITE_DB' => 1,
553 'REPLICATE_IGNORE_TABLE' => 1,
554 'MASTER_HEARTBEAT_PERIOD' => 1,
555 'REPLICATE_WILD_DO_TABLE' => 1,
556 'MAX_CONNECTIONS_PER_HOUR' => 1,
557 'REPLICATE_WILD_IGNORE_TABLE' => 1,
558
559 'AS' => 3,
560 'BY' => 3,
561 'IS' => 3,
562 'ON' => 3,
563 'OR' => 3,
564 'TO' => 3,
565 'ADD' => 3,
566 'ALL' => 3,
567 'AND' => 3,
568 'ASC' => 3,
569 'DEC' => 3,
570 'DIV' => 3,
571 'FOR' => 3,
572 'GET' => 3,
573 'NOT' => 3,
574 'OUT' => 3,
575 'SQL' => 3,
576 'SSL' => 3,
577 'USE' => 3,
578 'XOR' => 3,
579 'BOTH' => 3,
580 'CALL' => 3,
581 'CASE' => 3,
582 'DESC' => 3,
583 'DROP' => 3,
584 'DUAL' => 3,
585 'EACH' => 3,
586 'ELSE' => 3,
587 'EXIT' => 3,
588 'FROM' => 3,
589 'INT1' => 3,
590 'INT2' => 3,
591 'INT3' => 3,
592 'INT4' => 3,
593 'INT8' => 3,
594 'INTO' => 3,
595 'JOIN' => 3,
596 'KEYS' => 3,
597 'KILL' => 3,
598 'LIKE' => 3,
599 'LOAD' => 3,
600 'LOCK' => 3,
601 'LONG' => 3,
602 'LOOP' => 3,
603 'NULL' => 3,
604 'OVER' => 3,
605 'READ' => 3,
606 'SHOW' => 3,
607 'THEN' => 3,
608 'TRUE' => 3,
609 'UNDO' => 3,
610 'WHEN' => 3,
611 'WITH' => 3,
612 'ALTER' => 3,
613 'CHECK' => 3,
614 'CROSS' => 3,
615 'FALSE' => 3,
616 'FETCH' => 3,
617 'FORCE' => 3,
618 'GRANT' => 3,
619 'GROUP' => 3,
620 'INNER' => 3,
621 'INOUT' => 3,
622 'LEAVE' => 3,
623 'LIMIT' => 3,
624 'LINES' => 3,
625 'ORDER' => 3,
626 'OUTER' => 3,
627 'PURGE' => 3,
628 'RANGE' => 3,
629 'READS' => 3,
630 'RLIKE' => 3,
631 'TABLE' => 3,
632 'UNION' => 3,
633 'USAGE' => 3,
634 'USING' => 3,
635 'WHERE' => 3,
636 'WHILE' => 3,
637 'WRITE' => 3,
638 'BEFORE' => 3,
639 'CHANGE' => 3,
640 'COLUMN' => 3,
641 'CREATE' => 3,
642 'CURSOR' => 3,
643 'DELETE' => 3,
644 'ELSEIF' => 3,
645 'EXCEPT' => 3,
646 'FLOAT4' => 3,
647 'FLOAT8' => 3,
648 'HAVING' => 3,
649 'IGNORE' => 3,
650 'INFILE' => 3,
651 'LINEAR' => 3,
652 'OPTION' => 3,
653 'REGEXP' => 3,
654 'RENAME' => 3,
655 'RETURN' => 3,
656 'REVOKE' => 3,
657 'SELECT' => 3,
658 'SIGNAL' => 3,
659 'STORED' => 3,
660 'UNLOCK' => 3,
661 'UPDATE' => 3,
662 'ANALYZE' => 3,
663 'BETWEEN' => 3,
664 'CASCADE' => 3,
665 'COLLATE' => 3,
666 'DECLARE' => 3,
667 'DELAYED' => 3,
668 'ESCAPED' => 3,
669 'EXPLAIN' => 3,
670 'FOREIGN' => 3,
671 'ITERATE' => 3,
672 'LEADING' => 3,
673 'NATURAL' => 3,
674 'OUTFILE' => 3,
675 'PRIMARY' => 3,
676 'RELEASE' => 3,
677 'REQUIRE' => 3,
678 'SCHEMAS' => 3,
679 'TRIGGER' => 3,
680 'VARYING' => 3,
681 'VIRTUAL' => 3,
682 'CONTINUE' => 3,
683 'DAY_HOUR' => 3,
684 'DESCRIBE' => 3,
685 'DISTINCT' => 3,
686 'ENCLOSED' => 3,
687 'MAXVALUE' => 3,
688 'MODIFIES' => 3,
689 'OPTIMIZE' => 3,
690 'RESIGNAL' => 3,
691 'RESTRICT' => 3,
692 'SPECIFIC' => 3,
693 'SQLSTATE' => 3,
694 'STARTING' => 3,
695 'TRAILING' => 3,
696 'UNSIGNED' => 3,
697 'ZEROFILL' => 3,
698 'CONDITION' => 3,
699 'DATABASES' => 3,
700 'GENERATED' => 3,
701 'INTERSECT' => 3,
702 'MIDDLEINT' => 3,
703 'PARTITION' => 3,
704 'PRECISION' => 3,
705 'PROCEDURE' => 3,
706 'RECURSIVE' => 3,
707 'SENSITIVE' => 3,
708 'SEPARATOR' => 3,
709 'ACCESSIBLE' => 3,
710 'ASENSITIVE' => 3,
711 'CONSTRAINT' => 3,
712 'DAY_MINUTE' => 3,
713 'DAY_SECOND' => 3,
714 'OPTIONALLY' => 3,
715 'READ_WRITE' => 3,
716 'REFERENCES' => 3,
717 'SQLWARNING' => 3,
718 'TERMINATED' => 3,
719 'YEAR_MONTH' => 3,
720 'DISTINCTROW' => 3,
721 'HOUR_MINUTE' => 3,
722 'HOUR_SECOND' => 3,
723 'INSENSITIVE' => 3,
724 'MASTER_BIND' => 3,
725 'LOW_PRIORITY' => 3,
726 'SQLEXCEPTION' => 3,
727 'VARCHARACTER' => 3,
728 'DETERMINISTIC' => 3,
729 'HIGH_PRIORITY' => 3,
730 'MINUTE_SECOND' => 3,
731 'STRAIGHT_JOIN' => 3,
732 'IO_AFTER_GTIDS' => 3,
733 'SQL_BIG_RESULT' => 3,
734 'DAY_MICROSECOND' => 3,
735 'IO_BEFORE_GTIDS' => 3,
736 'OPTIMIZER_COSTS' => 3,
737 'HOUR_MICROSECOND' => 3,
738 'SQL_SMALL_RESULT' => 3,
739 'MINUTE_MICROSECOND' => 3,
740 'NO_WRITE_TO_BINLOG' => 3,
741 'SECOND_MICROSECOND' => 3,
742 'SQL_CALC_FOUND_ROWS' => 3,
743 'MASTER_SSL_VERIFY_SERVER_CERT' => 3,
744
745 'NO SQL' => 7,
746 'GROUP BY' => 7,
747 'NOT NULL' => 7,
748 'ORDER BY' => 7,
749 'SET NULL' => 7,
750 'AND CHAIN' => 7,
751 'FULL JOIN' => 7,
752 'IF EXISTS' => 7,
753 'LEFT JOIN' => 7,
754 'LESS THAN' => 7,
755 'LOAD DATA' => 7,
756 'NO ACTION' => 7,
757 'ON DELETE' => 7,
758 'ON UPDATE' => 7,
759 'UNION ALL' => 7,
760 'CROSS JOIN' => 7,
761 'ESCAPED BY' => 7,
762 'FOR UPDATE' => 7,
763 'INNER JOIN' => 7,
764 'LINEAR KEY' => 7,
765 'NO RELEASE' => 7,
766 'OR REPLACE' => 7,
767 'RIGHT JOIN' => 7,
768 'ENCLOSED BY' => 7,
769 'LINEAR HASH' => 7,
770 'ON SCHEDULE' => 7,
771 'STARTING BY' => 7,
772 'AND NO CHAIN' => 7,
773 'CONTAINS SQL' => 7,
774 'FOR EACH ROW' => 7,
775 'NATURAL JOIN' => 7,
776 'PARTITION BY' => 7,
777 'SET PASSWORD' => 7,
778 'SQL SECURITY' => 7,
779 'CHARACTER SET' => 7,
780 'IF NOT EXISTS' => 7,
781 'TERMINATED BY' => 7,
782 'DATA DIRECTORY' => 7,
783 'READS SQL DATA' => 7,
784 'UNION DISTINCT' => 7,
785 'DEFAULT CHARSET' => 7,
786 'DEFAULT COLLATE' => 7,
787 'FULL OUTER JOIN' => 7,
788 'INDEX DIRECTORY' => 7,
789 'LEFT OUTER JOIN' => 7,
790 'SUBPARTITION BY' => 7,
791 'DISABLE ON SLAVE' => 7,
792 'GENERATED ALWAYS' => 7,
793 'RIGHT OUTER JOIN' => 7,
794 'MODIFIES SQL DATA' => 7,
795 'NATURAL LEFT JOIN' => 7,
796 'START TRANSACTION' => 7,
797 'LOCK IN SHARE MODE' => 7,
798 'NATURAL RIGHT JOIN' => 7,
799 'SELECT TRANSACTION' => 7,
800 'DEFAULT CHARACTER SET' => 7,
801 'ON COMPLETION PRESERVE' => 7,
802 'NATURAL LEFT OUTER JOIN' => 7,
803 'NATURAL RIGHT OUTER JOIN' => 7,
804 'WITH CONSISTENT SNAPSHOT' => 7,
805 'ON COMPLETION NOT PRESERVE' => 7,
806
807 'BIT' => 9,
808 'XML' => 9,
809 'ENUM' => 9,
810 'JSON' => 9,
811 'TEXT' => 9,
812 'ARRAY' => 9,
813 'SERIAL' => 9,
814 'BOOLEAN' => 9,
815 'DATETIME' => 9,
816 'GEOMETRY' => 9,
817 'MULTISET' => 9,
818 'MULTILINEPOINT' => 9,
819 'MULTILINEPOLYGON' => 9,
820
821 'INT' => 11,
822 'SET' => 11,
823 'BLOB' => 11,
824 'REAL' => 11,
825 'FLOAT' => 11,
826 'BIGINT' => 11,
827 'DOUBLE' => 11,
828 'DECIMAL' => 11,
829 'INTEGER' => 11,
830 'NUMERIC' => 11,
831 'TINYINT' => 11,
832 'VARCHAR' => 11,
833 'LONGBLOB' => 11,
834 'LONGTEXT' => 11,
835 'SMALLINT' => 11,
836 'TINYBLOB' => 11,
837 'TINYTEXT' => 11,
838 'CHARACTER' => 11,
839 'MEDIUMINT' => 11,
840 'VARBINARY' => 11,
841 'MEDIUMBLOB' => 11,
842 'MEDIUMTEXT' => 11,
843
844 'BINARY VARYING' => 15,
845
846 'KEY' => 19,
847 'INDEX' => 19,
848 'UNIQUE' => 19,
849 'SPATIAL' => 19,
850 'FULLTEXT' => 19,
851
852 'INDEX KEY' => 23,
853 'UNIQUE KEY' => 23,
854 'FOREIGN KEY' => 23,
855 'PRIMARY KEY' => 23,
856 'SPATIAL KEY' => 23,
857 'FULLTEXT KEY' => 23,
858 'UNIQUE INDEX' => 23,
859 'SPATIAL INDEX' => 23,
860 'FULLTEXT INDEX' => 23,
861
862 'X' => 33,
863 'Y' => 33,
864 'LN' => 33,
865 'PI' => 33,
866 'ABS' => 33,
867 'AVG' => 33,
868 'BIN' => 33,
869 'COS' => 33,
870 'COT' => 33,
871 'DAY' => 33,
872 'ELT' => 33,
873 'EXP' => 33,
874 'HEX' => 33,
875 'LOG' => 33,
876 'MAX' => 33,
877 'MD5' => 33,
878 'MID' => 33,
879 'MIN' => 33,
880 'NOW' => 33,
881 'OCT' => 33,
882 'ORD' => 33,
883 'POW' => 33,
884 'SHA' => 33,
885 'SIN' => 33,
886 'STD' => 33,
887 'SUM' => 33,
888 'TAN' => 33,
889 'ACOS' => 33,
890 'AREA' => 33,
891 'ASIN' => 33,
892 'ATAN' => 33,
893 'CAST' => 33,
894 'CEIL' => 33,
895 'CONV' => 33,
896 'HOUR' => 33,
897 'LOG2' => 33,
898 'LPAD' => 33,
899 'RAND' => 33,
900 'RPAD' => 33,
901 'SHA1' => 33,
902 'SHA2' => 33,
903 'SIGN' => 33,
904 'SQRT' => 33,
905 'SRID' => 33,
906 'ST_X' => 33,
907 'ST_Y' => 33,
908 'TRIM' => 33,
909 'USER' => 33,
910 'UUID' => 33,
911 'WEEK' => 33,
912 'ASCII' => 33,
913 'ASWKB' => 33,
914 'ASWKT' => 33,
915 'ATAN2' => 33,
916 'COUNT' => 33,
917 'CRC32' => 33,
918 'FIELD' => 33,
919 'FLOOR' => 33,
920 'INSTR' => 33,
921 'LCASE' => 33,
922 'LEAST' => 33,
923 'LOG10' => 33,
924 'LOWER' => 33,
925 'LTRIM' => 33,
926 'MONTH' => 33,
927 'POWER' => 33,
928 'QUOTE' => 33,
929 'ROUND' => 33,
930 'RTRIM' => 33,
931 'SLEEP' => 33,
932 'SPACE' => 33,
933 'UCASE' => 33,
934 'UNHEX' => 33,
935 'UPPER' => 33,
936 'ASTEXT' => 33,
937 'BIT_OR' => 33,
938 'BUFFER' => 33,
939 'CONCAT' => 33,
940 'DECODE' => 33,
941 'ENCODE' => 33,
942 'EQUALS' => 33,
943 'FORMAT' => 33,
944 'IFNULL' => 33,
945 'ISNULL' => 33,
946 'LENGTH' => 33,
947 'LOCATE' => 33,
948 'MINUTE' => 33,
949 'NULLIF' => 33,
950 'POINTN' => 33,
951 'SECOND' => 33,
952 'STDDEV' => 33,
953 'STRCMP' => 33,
954 'SUBSTR' => 33,
955 'WITHIN' => 33,
956 'ADDDATE' => 33,
957 'ADDTIME' => 33,
958 'AGAINST' => 33,
959 'BIT_AND' => 33,
960 'BIT_XOR' => 33,
961 'CEILING' => 33,
962 'CHARSET' => 33,
963 'CROSSES' => 33,
964 'CURDATE' => 33,
965 'CURTIME' => 33,
966 'DAYNAME' => 33,
967 'DEGREES' => 33,
968 'ENCRYPT' => 33,
969 'EXTRACT' => 33,
970 'GLENGTH' => 33,
971 'ISEMPTY' => 33,
972 'IS_IPV4' => 33,
973 'IS_IPV6' => 33,
974 'IS_UUID' => 33,
975 'QUARTER' => 33,
976 'RADIANS' => 33,
977 'REVERSE' => 33,
978 'SOUNDEX' => 33,
979 'ST_AREA' => 33,
980 'ST_SRID' => 33,
981 'SUBDATE' => 33,
982 'SUBTIME' => 33,
983 'SYSDATE' => 33,
984 'TOUCHES' => 33,
985 'TO_DAYS' => 33,
986 'VAR_POP' => 33,
987 'VERSION' => 33,
988 'WEEKDAY' => 33,
989 'ASBINARY' => 33,
990 'CENTROID' => 33,
991 'COALESCE' => 33,
992 'COMPRESS' => 33,
993 'CONTAINS' => 33,
994 'DATEDIFF' => 33,
995 'DATE_ADD' => 33,
996 'DATE_SUB' => 33,
997 'DISJOINT' => 33,
998 'DISTANCE' => 33,
999 'ENDPOINT' => 33,
1000 'ENVELOPE' => 33,
1001 'GET_LOCK' => 33,
1002 'GREATEST' => 33,
1003 'ISCLOSED' => 33,
1004 'ISSIMPLE' => 33,
1005 'JSON_SET' => 33,
1006 'MAKEDATE' => 33,
1007 'MAKETIME' => 33,
1008 'MAKE_SET' => 33,
1009 'MBREQUAL' => 33,
1010 'OVERLAPS' => 33,
1011 'PASSWORD' => 33,
1012 'POSITION' => 33,
1013 'ST_ASWKB' => 33,
1014 'ST_ASWKT' => 33,
1015 'ST_UNION' => 33,
1016 'TIMEDIFF' => 33,
1017 'TRUNCATE' => 33,
1018 'VARIANCE' => 33,
1019 'VAR_SAMP' => 33,
1020 'YEARWEEK' => 33,
1021 'ANY_VALUE' => 33,
1022 'BENCHMARK' => 33,
1023 'BIT_COUNT' => 33,
1024 'COLLATION' => 33,
1025 'CONCAT_WS' => 33,
1026 'DAYOFWEEK' => 33,
1027 'DAYOFYEAR' => 33,
1028 'DIMENSION' => 33,
1029 'FROM_DAYS' => 33,
1030 'GEOMETRYN' => 33,
1031 'INET_ATON' => 33,
1032 'INET_NTOA' => 33,
1033 'JSON_KEYS' => 33,
1034 'JSON_TYPE' => 33,
1035 'LOAD_FILE' => 33,
1036 'MBRCOVERS' => 33,
1037 'MBREQUALS' => 33,
1038 'MBRWITHIN' => 33,
1039 'MONTHNAME' => 33,
1040 'NUMPOINTS' => 33,
1041 'ROW_COUNT' => 33,
1042 'ST_ASTEXT' => 33,
1043 'ST_BUFFER' => 33,
1044 'ST_EQUALS' => 33,
1045 'ST_LENGTH' => 33,
1046 'ST_POINTN' => 33,
1047 'ST_WITHIN' => 33,
1048 'SUBSTRING' => 33,
1049 'TO_BASE64' => 33,
1050 'UPDATEXML' => 33,
1051 'BIT_LENGTH' => 33,
1052 'CONVERT_TZ' => 33,
1053 'CONVEXHULL' => 33,
1054 'DAYOFMONTH' => 33,
1055 'EXPORT_SET' => 33,
1056 'FOUND_ROWS' => 33,
1057 'GET_FORMAT' => 33,
1058 'INET6_ATON' => 33,
1059 'INET6_NTOA' => 33,
1060 'INTERSECTS' => 33,
1061 'JSON_ARRAY' => 33,
1062 'JSON_DEPTH' => 33,
1063 'JSON_MERGE' => 33,
1064 'JSON_QUOTE' => 33,
1065 'JSON_VALID' => 33,
1066 'MBRTOUCHES' => 33,
1067 'NAME_CONST' => 33,
1068 'PERIOD_ADD' => 33,
1069 'STARTPOINT' => 33,
1070 'STDDEV_POP' => 33,
1071 'ST_CROSSES' => 33,
1072 'ST_GEOHASH' => 33,
1073 'ST_ISEMPTY' => 33,
1074 'ST_ISVALID' => 33,
1075 'ST_TOUCHES' => 33,
1076 'TO_SECONDS' => 33,
1077 'UNCOMPRESS' => 33,
1078 'UUID_SHORT' => 33,
1079 'WEEKOFYEAR' => 33,
1080 'AES_DECRYPT' => 33,
1081 'AES_ENCRYPT' => 33,
1082 'BIN_TO_UUID' => 33,
1083 'CHAR_LENGTH' => 33,
1084 'DATE_FORMAT' => 33,
1085 'DES_DECRYPT' => 33,
1086 'DES_ENCRYPT' => 33,
1087 'FIND_IN_SET' => 33,
1088 'FROM_BASE64' => 33,
1089 'GEOMFROMWKB' => 33,
1090 'GTID_SUBSET' => 33,
1091 'JSON_INSERT' => 33,
1092 'JSON_LENGTH' => 33,
1093 'JSON_OBJECT' => 33,
1094 'JSON_PRETTY' => 33,
1095 'JSON_REMOVE' => 33,
1096 'JSON_SEARCH' => 33,
1097 'LINEFROMWKB' => 33,
1098 'MBRCONTAINS' => 33,
1099 'MBRDISJOINT' => 33,
1100 'MBROVERLAPS' => 33,
1101 'MICROSECOND' => 33,
1102 'PERIOD_DIFF' => 33,
1103 'POLYFROMWKB' => 33,
1104 'SEC_TO_TIME' => 33,
1105 'STDDEV_SAMP' => 33,
1106 'STR_TO_DATE' => 33,
1107 'ST_ASBINARY' => 33,
1108 'ST_CENTROID' => 33,
1109 'ST_CONTAINS' => 33,
1110 'ST_DISJOINT' => 33,
1111 'ST_DISTANCE' => 33,
1112 'ST_ENDPOINT' => 33,
1113 'ST_ENVELOPE' => 33,
1114 'ST_ISCLOSED' => 33,
1115 'ST_ISSIMPLE' => 33,
1116 'ST_OVERLAPS' => 33,
1117 'ST_SIMPLIFY' => 33,
1118 'ST_VALIDATE' => 33,
1119 'SYSTEM_USER' => 33,
1120 'TIME_FORMAT' => 33,
1121 'TIME_TO_SEC' => 33,
1122 'UUID_TO_BIN' => 33,
1123 'COERCIBILITY' => 33,
1124 'EXTERIORRING' => 33,
1125 'EXTRACTVALUE' => 33,
1126 'GEOMETRYTYPE' => 33,
1127 'GEOMFROMTEXT' => 33,
1128 'GROUP_CONCAT' => 33,
1129 'IS_FREE_LOCK' => 33,
1130 'IS_USED_LOCK' => 33,
1131 'JSON_EXTRACT' => 33,
1132 'JSON_REPLACE' => 33,
1133 'JSON_UNQUOTE' => 33,
1134 'LINEFROMTEXT' => 33,
1135 'MBRCOVEREDBY' => 33,
1136 'MLINEFROMWKB' => 33,
1137 'MPOLYFROMWKB' => 33,
1138 'OCTET_LENGTH' => 33,
1139 'OLD_PASSWORD' => 33,
1140 'POINTFROMWKB' => 33,
1141 'POLYFROMTEXT' => 33,
1142 'RANDOM_BYTES' => 33,
1143 'RELEASE_LOCK' => 33,
1144 'SESSION_USER' => 33,
1145 'ST_ASGEOJSON' => 33,
1146 'ST_DIMENSION' => 33,
1147 'ST_GEOMETRYN' => 33,
1148 'ST_NUMPOINTS' => 33,
1149 'TIMESTAMPADD' => 33,
1150 'CONNECTION_ID' => 33,
1151 'FROM_UNIXTIME' => 33,
1152 'GTID_SUBTRACT' => 33,
1153 'INTERIORRINGN' => 33,
1154 'JSON_CONTAINS' => 33,
1155 'MBRINTERSECTS' => 33,
1156 'MLINEFROMTEXT' => 33,
1157 'MPOINTFROMWKB' => 33,
1158 'MPOLYFROMTEXT' => 33,
1159 'NUMGEOMETRIES' => 33,
1160 'POINTFROMTEXT' => 33,
1161 'ST_CONVEXHULL' => 33,
1162 'ST_DIFFERENCE' => 33,
1163 'ST_INTERSECTS' => 33,
1164 'ST_STARTPOINT' => 33,
1165 'TIMESTAMPDIFF' => 33,
1166 'WEIGHT_STRING' => 33,
1167 'IS_IPV4_COMPAT' => 33,
1168 'IS_IPV4_MAPPED' => 33,
1169 'LAST_INSERT_ID' => 33,
1170 'MPOINTFROMTEXT' => 33,
1171 'POLYGONFROMWKB' => 33,
1172 'ST_GEOMFROMWKB' => 33,
1173 'ST_LINEFROMWKB' => 33,
1174 'ST_POLYFROMWKB' => 33,
1175 'UNIX_TIMESTAMP' => 33,
1176 'GEOMCOLLFROMWKB' => 33,
1177 'MASTER_POS_WAIT' => 33,
1178 'POLYGONFROMTEXT' => 33,
1179 'ST_EXTERIORRING' => 33,
1180 'ST_GEOMETRYTYPE' => 33,
1181 'ST_GEOMFROMTEXT' => 33,
1182 'ST_INTERSECTION' => 33,
1183 'ST_LINEFROMTEXT' => 33,
1184 'ST_MAKEENVELOPE' => 33,
1185 'ST_MLINEFROMWKB' => 33,
1186 'ST_MPOLYFROMWKB' => 33,
1187 'ST_POINTFROMWKB' => 33,
1188 'ST_POLYFROMTEXT' => 33,
1189 'SUBSTRING_INDEX' => 33,
1190 'CHARACTER_LENGTH' => 33,
1191 'GEOMCOLLFROMTEXT' => 33,
1192 'GEOMETRYFROMTEXT' => 33,
1193 'JSON_MERGE_PATCH' => 33,
1194 'NUMINTERIORRINGS' => 33,
1195 'ST_INTERIORRINGN' => 33,
1196 'ST_MLINEFROMTEXT' => 33,
1197 'ST_MPOINTFROMWKB' => 33,
1198 'ST_MPOLYFROMTEXT' => 33,
1199 'ST_NUMGEOMETRIES' => 33,
1200 'ST_POINTFROMTEXT' => 33,
1201 'ST_SYMDIFFERENCE' => 33,
1202 'JSON_ARRAY_APPEND' => 33,
1203 'JSON_ARRAY_INSERT' => 33,
1204 'JSON_STORAGE_FREE' => 33,
1205 'JSON_STORAGE_SIZE' => 33,
1206 'LINESTRINGFROMWKB' => 33,
1207 'MULTIPOINTFROMWKB' => 33,
1208 'RELEASE_ALL_LOCKS' => 33,
1209 'ST_LATFROMGEOHASH' => 33,
1210 'ST_MPOINTFROMTEXT' => 33,
1211 'ST_POLYGONFROMWKB' => 33,
1212 'JSON_CONTAINS_PATH' => 33,
1213 'MULTIPOINTFROMTEXT' => 33,
1214 'ST_BUFFER_STRATEGY' => 33,
1215 'ST_DISTANCE_SPHERE' => 33,
1216 'ST_GEOMCOLLFROMTXT' => 33,
1217 'ST_GEOMCOLLFROMWKB' => 33,
1218 'ST_GEOMFROMGEOJSON' => 33,
1219 'ST_LONGFROMGEOHASH' => 33,
1220 'ST_POLYGONFROMTEXT' => 33,
1221 'JSON_MERGE_PRESERVE' => 33,
1222 'MULTIPOLYGONFROMWKB' => 33,
1223 'ST_GEOMCOLLFROMTEXT' => 33,
1224 'ST_GEOMETRYFROMTEXT' => 33,
1225 'ST_NUMINTERIORRINGS' => 33,
1226 'ST_POINTFROMGEOHASH' => 33,
1227 'UNCOMPRESSED_LENGTH' => 33,
1228 'MULTIPOLYGONFROMTEXT' => 33,
1229 'ST_LINESTRINGFROMWKB' => 33,
1230 'ST_MULTIPOINTFROMWKB' => 33,
1231 'ST_MULTIPOINTFROMTEXT' => 33,
1232 'MULTILINESTRINGFROMWKB' => 33,
1233 'ST_MULTIPOLYGONFROMWKB' => 33,
1234 'MULTILINESTRINGFROMTEXT' => 33,
1235 'ST_MULTIPOLYGONFROMTEXT' => 33,
1236 'GEOMETRYCOLLECTIONFROMWKB' => 33,
1237 'ST_MULTILINESTRINGFROMWKB' => 33,
1238 'GEOMETRYCOLLECTIONFROMTEXT' => 33,
1239 'ST_MULTILINESTRINGFROMTEXT' => 33,
1240 'VALIDATE_PASSWORD_STRENGTH' => 33,
1241 'WAIT_FOR_EXECUTED_GTID_SET' => 33,
1242 'ST_GEOMETRYCOLLECTIONFROMWKB' => 33,
1243 'ST_GEOMETRYCOLLECTIONFROMTEXT' => 33,
1244 'WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS' => 33,
1245
1246 'IF' => 35,
1247 'IN' => 35,
1248 'MOD' => 35,
1249 'LEFT' => 35,
1250 'MATCH' => 35,
1251 'RIGHT' => 35,
1252 'EXISTS' => 35,
1253 'INSERT' => 35,
1254 'REPEAT' => 35,
1255 'SCHEMA' => 35,
1256 'VALUES' => 35,
1257 'CONVERT' => 35,
1258 'DEFAULT' => 35,
1259 'REPLACE' => 35,
1260 'DATABASE' => 35,
1261 'UTC_DATE' => 35,
1262 'UTC_TIME' => 35,
1263 'LOCALTIME' => 35,
1264 'CURRENT_DATE' => 35,
1265 'CURRENT_TIME' => 35,
1266 'CURRENT_USER' => 35,
1267 'UTC_TIMESTAMP' => 35,
1268 'LOCALTIMESTAMP' => 35,
1269 'CURRENT_TIMESTAMP' => 35,
1270
1271 'NOT IN' => 39,
1272
1273 'DATE' => 41,
1274 'TIME' => 41,
1275 'YEAR' => 41,
1276 'POINT' => 41,
1277 'POLYGON' => 41,
1278 'TIMESTAMP' => 41,
1279 'LINESTRING' => 41,
1280 'MULTIPOINT' => 41,
1281 'MULTIPOLYGON' => 41,
1282 'MULTILINESTRING' => 41,
1283 'GEOMETRYCOLLECTION' => 41,
1284
1285 'CHAR' => 43,
1286 'BINARY' => 43,
1287 'INTERVAL' => 43,
1288 );
1289
1290 /**
1291 * All data type options.
1292 *
1293 * @var array<string, int|array<int, int|string>>
1294 */
1295 public static $data_type_options = array(
1296 'BINARY' => 1,
1297 'CHARACTER SET' => array(
1298 2,
1299 'var',
1300 ),
1301 'CHARSET' => array(
1302 2,
1303 'var',
1304 ),
1305 'COLLATE' => array(
1306 3,
1307 'var',
1308 ),
1309 'UNSIGNED' => 4,
1310 'ZEROFILL' => 5,
1311 );
1312
1313 /**
1314 * All field options.
1315 *
1316 * @var array<string, bool|int|array<int, int|string|array<string, bool>>>
1317 */
1318 public static $field_options = array(
1319
1320 /*
1321 * Tells the `OptionsArray` to not sort the options.
1322 * See the note below.
1323 */
1324 '_UNSORTED' => true,
1325
1326 'NOT NULL' => 1,
1327 'NULL' => 1,
1328 'DEFAULT' => array(
1329 2,
1330 'expr',
1331 array( 'breakOnAlias' => true ),
1332 ),
1333
1334 // Following are not according to grammar, but MySQL happily accepts these at any location.
1335 'CHARSET' => array(
1336 2,
1337 'var',
1338 ),
1339 'COLLATE' => array(
1340 3,
1341 'var',
1342 ),
1343 'AUTO_INCREMENT' => 3,
1344 'PRIMARY' => 4,
1345 'PRIMARY KEY' => 4,
1346 'UNIQUE' => 4,
1347 'UNIQUE KEY' => 4,
1348 'COMMENT' => array(
1349 5,
1350 'var',
1351 ),
1352 'COLUMN_FORMAT' => array(
1353 6,
1354 'var',
1355 ),
1356 'ON UPDATE' => array(
1357 7,
1358 'expr',
1359 ),
1360
1361 // Generated columns options.
1362 'GENERATED ALWAYS' => 8,
1363 'AS' => array(
1364 9,
1365 'expr',
1366 array( 'parenthesesDelimited' => true ),
1367 ),
1368 'VIRTUAL' => 10,
1369 'PERSISTENT' => 11,
1370 'STORED' => 11,
1371 'CHECK' => array(
1372 12,
1373 'expr',
1374 array( 'parenthesesDelimited' => true ),
1375 ),
1376 'INVISIBLE' => 13,
1377 'ENFORCED' => 14,
1378 'NOT' => 15,
1379 'COMPRESSED' => 16,
1380
1381 /*
1382 * Common entries.
1383 *
1384 * NOTE: Some of the common options are not in the same order which
1385 * causes troubles when checking if the options are in the right order.
1386 * I should find a way to define multiple sets of options and make the
1387 * parser select the right set.
1388 *
1389 * 'UNIQUE' => 4,
1390 * 'UNIQUE KEY' => 4,
1391 * 'COMMENT' => [5, 'var'],
1392 * 'NOT NULL' => 1,
1393 * 'NULL' => 1,
1394 * 'PRIMARY' => 4,
1395 * 'PRIMARY KEY' => 4,
1396 */
1397 );
1398
1399 /**
1400 * Quotes mode.
1401 *
1402 * @link https://dev.mysql.com/doc/refman/en/sql-mode.html#sqlmode_ansi_quotes
1403 * @link https://mariadb.com/kb/en/sql-mode/#ansi_quotes
1404 */
1405 const SQL_MODE_ANSI_QUOTES = 2;
1406
1407 /**
1408 * The array of tokens.
1409 *
1410 * @var stdClass[]
1411 */
1412 public $tokens = array();
1413
1414 /**
1415 * The count of tokens.
1416 *
1417 * @var int
1418 */
1419 public $tokens_count = 0;
1420
1421 /**
1422 * The index of the next token to be returned.
1423 *
1424 * @var int
1425 */
1426 public $tokens_index = 0;
1427
1428 /**
1429 * The object constructor.
1430 *
1431 * @param string $str The query to be lexed.
1432 * @param string $delimiter The delimiter to be used.
1433 */
1434 public function __construct( $str, $delimiter = null ) {
1435 $this->str = $str;
1436 // `strlen` is used instead of `mb_strlen` because the lexer needs to parse each byte of the input.
1437 $this->string_length = strlen( $str );
1438
1439 // Setting the delimiter.
1440 $this->set_delimiter( ! empty( $delimiter ) ? $delimiter : static::$default_delimiter );
1441
1442 $this->lex();
1443 }
1444
1445 /**
1446 * Sets the delimiter.
1447 *
1448 * @param string $delimiter The new delimiter.
1449 *
1450 * @return void
1451 */
1452 public function set_delimiter( $delimiter ) {
1453 $this->delimiter = $delimiter;
1454 $this->delimiter_length = strlen( $delimiter );
1455 }
1456
1457 /**
1458 * Parses the string and extracts lexemes.
1459 *
1460 * @return void
1461 */
1462 public function lex() {
1463 /*
1464 * TODO: Sometimes, static::parse* functions make unnecessary calls to
1465 * is* functions. For a better performance, some rules can be deduced
1466 * from context.
1467 * For example, in `parse_bool` there is no need to compare the token
1468 * every time with `true` and `false`. The first step would be to
1469 * compare with 'true' only and just after that add another letter from
1470 * context and compare again with `false`.
1471 * Another example is `parse_comment`.
1472 */
1473
1474 /**
1475 * Last processed token.
1476 *
1477 * @var WP_SQLite_Token
1478 */
1479 $last_token = null;
1480
1481 for ( $this->last = 0, $last_idx = 0; $this->last < $this->string_length; $last_idx = ++$this->last ) {
1482 /**
1483 * The new token.
1484 *
1485 * @var WP_SQLite_Token
1486 */
1487 $token = null;
1488
1489 foreach ( self::PARSER_METHODS as $method ) {
1490 $token = $this->$method();
1491
1492 if ( $token ) {
1493 break;
1494 }
1495 }
1496
1497 if ( null === $token ) {
1498 $token = new WP_SQLite_Token( $this->str[ $this->last ] );
1499 $this->error( 'Unexpected character.', $this->str[ $this->last ], $this->last );
1500 } elseif (
1501 null !== $last_token
1502 && WP_SQLite_Token::TYPE_SYMBOL === $token->type
1503 && $token->flags & WP_SQLite_Token::FLAG_SYMBOL_VARIABLE
1504 && (
1505 WP_SQLite_Token::TYPE_STRING === $last_token->type
1506 || (
1507 WP_SQLite_Token::TYPE_SYMBOL === $last_token->type
1508 && $last_token->flags & WP_SQLite_Token::FLAG_SYMBOL_BACKTICK
1509 )
1510 )
1511 ) {
1512 // Handles ```... FROM 'user'@'%' ...```.
1513 $last_token->token .= $token->token;
1514 $last_token->type = WP_SQLite_Token::TYPE_SYMBOL;
1515 $last_token->flags = WP_SQLite_Token::FLAG_SYMBOL_USER;
1516 $last_token->value .= '@' . $token->value;
1517 continue;
1518 } elseif (
1519 null !== $last_token
1520 && WP_SQLite_Token::TYPE_KEYWORD === $token->type
1521 && WP_SQLite_Token::TYPE_OPERATOR === $last_token->type
1522 && '.' === $last_token->value
1523 ) {
1524 // Handles ```... tbl.FROM ...```. In this case, FROM is not a reserved word.
1525 $token->type = WP_SQLite_Token::TYPE_NONE;
1526 $token->flags = 0;
1527 $token->value = $token->token;
1528 }
1529
1530 $token->position = $last_idx;
1531
1532 $this->tokens[ $this->tokens_count++ ] = $token;
1533
1534 // Handling delimiters.
1535 if ( WP_SQLite_Token::TYPE_NONE === $token->type && 'DELIMITER' === $token->value ) {
1536 if ( $this->last + 1 >= $this->string_length ) {
1537 $this->error( 'Expected whitespace(s) before delimiter.', '', $this->last + 1 );
1538 continue;
1539 }
1540
1541 /*
1542 * Skipping last R (from `delimiteR`) and whitespaces between
1543 * the keyword `DELIMITER` and the actual delimiter.
1544 */
1545 $pos = ++$this->last;
1546 $token = $this->parse_whitespace();
1547
1548 if ( null !== $token ) {
1549 $token->position = $pos;
1550 $this->tokens[ $this->tokens_count++ ] = $token;
1551 }
1552
1553 // Preparing the token that holds the new delimiter.
1554 if ( $this->last + 1 >= $this->string_length ) {
1555 $this->error( 'Expected delimiter.', '', $this->last + 1 );
1556 continue;
1557 }
1558
1559 $pos = $this->last + 1;
1560
1561 // Parsing the delimiter.
1562 $this->delimiter = null;
1563 $delimiter_length = 0;
1564 while (
1565 ++$this->last < $this->string_length
1566 && ! static::is_whitespace( $this->str[ $this->last ] )
1567 && $delimiter_length < 15
1568 ) {
1569 $this->delimiter .= $this->str[ $this->last ];
1570 ++$delimiter_length;
1571 }
1572
1573 if ( empty( $this->delimiter ) ) {
1574 $this->error( 'Expected delimiter.', '', $this->last );
1575 $this->delimiter = ';';
1576 }
1577
1578 --$this->last;
1579
1580 // Saving the delimiter and its token.
1581 $this->delimiter_length = strlen( $this->delimiter );
1582 $token = new WP_SQLite_Token( $this->delimiter, WP_SQLite_Token::TYPE_DELIMITER );
1583 $token->position = $pos;
1584 $this->tokens[ $this->tokens_count++ ] = $token;
1585 }
1586
1587 $last_token = $token;
1588 }
1589
1590 // Adding a final delimiter to mark the ending.
1591 $this->tokens[ $this->tokens_count++ ] = new WP_SQLite_Token( null, WP_SQLite_Token::TYPE_DELIMITER );
1592
1593 $this->solve_ambiguity_on_star_operator();
1594 $this->solve_ambiguity_on_function_keywords();
1595 }
1596
1597 /**
1598 * Resolves the ambiguity when dealing with the "*" operator.
1599 *
1600 * In SQL statements, the "*" operator can be an arithmetic operator (like in 2*3) or an SQL wildcard (like in
1601 * SELECT a.* FROM ...). To solve this ambiguity, the solution is to find the next token, excluding whitespaces and
1602 * comments, right after the "*" position. The "*" is for sure an SQL wildcard if the next token found is any of:
1603 * - "FROM" (the FROM keyword like in "SELECT * FROM...");
1604 * - "USING" (the USING keyword like in "DELETE table_name.* USING...");
1605 * - "," (a comma separator like in "SELECT *, field FROM...");
1606 * - ")" (a closing parenthesis like in "COUNT(*)").
1607 * This methods will change the flag of the "*" tokens when any of those condition above is true. Otherwise, the
1608 * default flag (arithmetic) will be kept.
1609 *
1610 * @return void
1611 */
1612 private function solve_ambiguity_on_star_operator() {
1613 $i_bak = $this->tokens_index;
1614 while ( true ) {
1615 $star_token = $this->tokens_get_next_of_type_and_value( WP_SQLite_Token::TYPE_OPERATOR, '*' );
1616 if ( null === $star_token ) {
1617 break;
1618 }
1619 // tokens_get_next() already gets rid of whitespaces and comments.
1620 $next = $this->tokens_get_next();
1621
1622 if ( null === $next ) {
1623 continue;
1624 }
1625
1626 if (
1627 ( WP_SQLite_Token::TYPE_KEYWORD !== $next->type || ! in_array( $next->value, array( 'FROM', 'USING' ), true ) )
1628 && ( WP_SQLite_Token::TYPE_OPERATOR !== $next->type || ! in_array( $next->value, array( ',', ')' ), true ) )
1629 ) {
1630 continue;
1631 }
1632
1633 $star_token->flags = WP_SQLite_Token::FLAG_OPERATOR_SQL;
1634 }
1635
1636 $this->tokens_index = $i_bak;
1637 }
1638
1639 /**
1640 * Resolves the ambiguity when dealing with the functions keywords.
1641 *
1642 * In SQL statements, the function keywords might be used as table names or columns names.
1643 * To solve this ambiguity, the solution is to find the next token, excluding whitespaces and
1644 * comments, right after the function keyword position. The function keyword is for sure used
1645 * as column name or table name if the next token found is any of:
1646 *
1647 * - "FROM" (the FROM keyword like in "SELECT Country x, AverageSalary avg FROM...");
1648 * - "WHERE" (the WHERE keyword like in "DELETE FROM emp x WHERE x.salary = 20");
1649 * - "SET" (the SET keyword like in "UPDATE Country x, City y set x.Name=x.Name");
1650 * - "," (a comma separator like 'x,' in "UPDATE Country x, City y set x.Name=x.Name");
1651 * - "." (a dot separator like in "x.asset_id FROM (SELECT evt.asset_id FROM evt)".
1652 * - "NULL" (when used as a table alias like in "avg.col FROM (SELECT ev.col FROM ev) avg").
1653 *
1654 * This method will change the flag of the function keyword tokens when any of those
1655 * condition above is true. Otherwise, the
1656 * default flag (function keyword) will be kept.
1657 *
1658 * @return void
1659 */
1660 private function solve_ambiguity_on_function_keywords() {
1661 $i_bak = $this->tokens_index;
1662 $keyword_function = WP_SQLite_Token::TYPE_KEYWORD | WP_SQLite_Token::FLAG_KEYWORD_FUNCTION;
1663 while ( true ) {
1664 $keyword_token = $this->tokens_get_next_of_type_and_flag( WP_SQLite_Token::TYPE_KEYWORD, $keyword_function );
1665 if ( null === $keyword_token ) {
1666 break;
1667 }
1668 $next = $this->tokens_get_next();
1669 if (
1670 ( WP_SQLite_Token::TYPE_KEYWORD !== $next->type
1671 || ! in_array( $next->value, self::KEYWORD_NAME_INDICATORS, true )
1672 )
1673 && ( WP_SQLite_Token::TYPE_OPERATOR !== $next->type
1674 || ! in_array( $next->value, self::OPERATOR_NAME_INDICATORS, true )
1675 )
1676 && ( null !== $next->value )
1677 ) {
1678 continue;
1679 }
1680
1681 $keyword_token->type = WP_SQLite_Token::TYPE_NONE;
1682 $keyword_token->flags = WP_SQLite_Token::TYPE_NONE;
1683 $keyword_token->keyword = $keyword_token->value;
1684 }
1685
1686 $this->tokens_index = $i_bak;
1687 }
1688
1689 /**
1690 * Creates a new error log.
1691 *
1692 * @param string $msg The error message.
1693 * @param string $str The character that produced the error.
1694 * @param int $pos The position of the character.
1695 * @param int $code The code of the error.
1696 *
1697 * @throws Exception The error log.
1698 * @return void
1699 */
1700 public function error( $msg, $str = '', $pos = 0, $code = 0 ) {
1701 throw new Exception(
1702 print_r(
1703 array(
1704 'query' => $this->str,
1705 'message' => $msg,
1706 'str' => $str,
1707 'position' => $pos,
1708 'code' => $code,
1709 ),
1710 true
1711 )
1712 );
1713 }
1714
1715 /**
1716 * Parses a keyword.
1717 *
1718 * @return WP_SQLite_Token|null
1719 */
1720 public function parse_keyword() {
1721 $token = '';
1722
1723 /**
1724 * Value to be returned.
1725 *
1726 * @var WP_SQLite_Token
1727 */
1728 $ret = null;
1729
1730 // The value of `$this->last` where `$token` ends in `$this->str`.
1731 $i_end = $this->last;
1732
1733 // Whether last parsed character is a whitespace.
1734 $last_space = false;
1735
1736 for ( $j = 1; $j < static::KEYWORD_MAX_LENGTH && $this->last < $this->string_length; ++$j, ++$this->last ) {
1737 $last_space = false;
1738 // Composed keywords shouldn't have more than one whitespace between keywords.
1739 if ( static::is_whitespace( $this->str[ $this->last ] ) ) {
1740 if ( $last_space ) {
1741 --$j; // The size of the keyword didn't increase.
1742 continue;
1743 }
1744
1745 $last_space = true;
1746 }
1747
1748 $token .= $this->str[ $this->last ];
1749 $flags = static::is_keyword( $token );
1750
1751 if ( ( $this->last + 1 !== $this->string_length && ! static::is_separator( $this->str[ $this->last + 1 ] ) ) || ! $flags ) {
1752 continue;
1753 }
1754
1755 $ret = new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_KEYWORD, $flags );
1756 $i_end = $this->last;
1757
1758 /*
1759 * We don't break so we find longest keyword.
1760 * For example, `OR` and `ORDER` have a common prefix `OR`.
1761 * If we stopped at `OR`, the parsing would be invalid.
1762 */
1763 }
1764
1765 $this->last = $i_end;
1766
1767 return $ret;
1768 }
1769
1770 /**
1771 * Parses a label.
1772 *
1773 * @return WP_SQLite_Token|null
1774 */
1775 public function parse_label() {
1776 $token = '';
1777
1778 /**
1779 * Value to be returned.
1780 *
1781 * @var WP_SQLite_Token
1782 */
1783 $ret = null;
1784
1785 // The value of `$this->last` where `$token` ends in `$this->str`.
1786 $i_end = $this->last;
1787 for ( $j = 1; $j < static::LABEL_MAX_LENGTH && $this->last < $this->string_length; ++$j, ++$this->last ) {
1788 if ( ':' === $this->str[ $this->last ] && $j > 1 ) {
1789 // End of label.
1790 $token .= $this->str[ $this->last ];
1791 $ret = new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_LABEL );
1792 $i_end = $this->last;
1793 break;
1794 }
1795
1796 if ( static::is_whitespace( $this->str[ $this->last ] ) && $j > 1 ) {
1797 /*
1798 * Whitespace between label and `:`.
1799 * The size of the keyword didn't increase.
1800 */
1801 --$j;
1802 } elseif ( static::is_separator( $this->str[ $this->last ] ) ) {
1803 // Any other separator.
1804 break;
1805 }
1806
1807 $token .= $this->str[ $this->last ];
1808 }
1809
1810 $this->last = $i_end;
1811
1812 return $ret;
1813 }
1814
1815 /**
1816 * Parses an operator.
1817 *
1818 * @return WP_SQLite_Token|null
1819 */
1820 public function parse_operator() {
1821 $token = '';
1822
1823 /**
1824 * Value to be returned.
1825 *
1826 * @var WP_SQLite_Token
1827 */
1828 $ret = null;
1829
1830 // The value of `$this->last` where `$token` ends in `$this->str`.
1831 $i_end = $this->last;
1832
1833 for ( $j = 1; $j < static::OPERATOR_MAX_LENGTH && $this->last < $this->string_length; ++$j, ++$this->last ) {
1834 $token .= $this->str[ $this->last ];
1835 $flags = static::is_operator( $token );
1836
1837 if ( ! $flags ) {
1838 continue;
1839 }
1840
1841 $ret = new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_OPERATOR, $flags );
1842 $i_end = $this->last;
1843 }
1844
1845 $this->last = $i_end;
1846
1847 return $ret;
1848 }
1849
1850 /**
1851 * Parses a whitespace.
1852 *
1853 * @return WP_SQLite_Token|null
1854 */
1855 public function parse_whitespace() {
1856 $token = $this->str[ $this->last ];
1857
1858 if ( ! static::is_whitespace( $token ) ) {
1859 return null;
1860 }
1861
1862 while ( ++$this->last < $this->string_length && static::is_whitespace( $this->str[ $this->last ] ) ) {
1863 $token .= $this->str[ $this->last ];
1864 }
1865
1866 --$this->last;
1867
1868 return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_WHITESPACE );
1869 }
1870
1871 /**
1872 * Parses a comment.
1873 *
1874 * @return WP_SQLite_Token|null
1875 */
1876 public function parse_comment() {
1877 $i_bak = $this->last;
1878 $token = $this->str[ $this->last ];
1879
1880 // Bash style comments (#comment\n).
1881 if ( static::is_comment( $token ) ) {
1882 while ( ++$this->last < $this->string_length && "\n" !== $this->str[ $this->last ] ) {
1883 $token .= $this->str[ $this->last ];
1884 }
1885
1886 // Include trailing \n as whitespace token.
1887 if ( $this->last < $this->string_length ) {
1888 --$this->last;
1889 }
1890
1891 return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_COMMENT, WP_SQLite_Token::FLAG_COMMENT_BASH );
1892 }
1893
1894 // C style comments (/*comment*\/).
1895 if ( ++$this->last < $this->string_length ) {
1896 $token .= $this->str[ $this->last ];
1897 if ( static::is_comment( $token ) ) {
1898 // There might be a conflict with "*" operator here, when string is "*/*".
1899 // This can occurs in the following statements:
1900 // - "SELECT */* comment */ FROM ..."
1901 // - "SELECT 2*/* comment */3 AS `six`;".
1902 $next = $this->last + 1;
1903 if ( ( $next < $this->string_length ) && '*' === $this->str[ $next ] ) {
1904 // Conflict in "*/*": first "*" was not for ending a comment.
1905 // Stop here and let other parsing method define the true behavior of that first star.
1906 $this->last = $i_bak;
1907
1908 return null;
1909 }
1910
1911 $flags = WP_SQLite_Token::FLAG_COMMENT_C;
1912
1913 // This comment already ended. It may be a part of a previous MySQL specific command.
1914 if ( '*/' === $token ) {
1915 return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_COMMENT, $flags );
1916 }
1917
1918 // Checking if this is a MySQL-specific command.
1919 if ( $this->last + 1 < $this->string_length && '!' === $this->str[ $this->last + 1 ] ) {
1920 $flags |= WP_SQLite_Token::FLAG_COMMENT_MYSQL_CMD;
1921 $token .= $this->str[ ++$this->last ];
1922
1923 while (
1924 ++$this->last < $this->string_length
1925 && $this->str[ $this->last ] >= '0'
1926 && $this->str[ $this->last ] <= '9'
1927 ) {
1928 $token .= $this->str[ $this->last ];
1929 }
1930
1931 --$this->last;
1932
1933 // We split this comment and parse only its beginning here.
1934 return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_COMMENT, $flags );
1935 }
1936
1937 // Parsing the comment.
1938 while (
1939 ++$this->last < $this->string_length
1940 && ( '*' !== $this->str[ $this->last - 1 ] || '/' !== $this->str[ $this->last ] )
1941 ) {
1942 $token .= $this->str[ $this->last ];
1943 }
1944
1945 // Adding the ending.
1946 if ( $this->last < $this->string_length ) {
1947 $token .= $this->str[ $this->last ];
1948 }
1949
1950 return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_COMMENT, $flags );
1951 }
1952 }
1953
1954 // SQL style comments (-- comment\n).
1955 if ( ++$this->last < $this->string_length ) {
1956 $token .= $this->str[ $this->last ];
1957 $end = false;
1958 } else {
1959 --$this->last;
1960 $end = true;
1961 }
1962
1963 if ( static::is_comment( $token, $end ) ) {
1964 // Checking if this comment did not end already (```--\n```).
1965 if ( "\n" !== $this->str[ $this->last ] ) {
1966 while ( ++$this->last < $this->string_length && "\n" !== $this->str[ $this->last ] ) {
1967 $token .= $this->str[ $this->last ];
1968 }
1969 }
1970
1971 // Include trailing \n as whitespace token.
1972 if ( $this->last < $this->string_length ) {
1973 --$this->last;
1974 }
1975
1976 return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_COMMENT, WP_SQLite_Token::FLAG_COMMENT_SQL );
1977 }
1978
1979 $this->last = $i_bak;
1980
1981 return null;
1982 }
1983
1984 /**
1985 * Parses a boolean.
1986 *
1987 * @return WP_SQLite_Token|null
1988 */
1989 public function parse_bool() {
1990 if ( $this->last + 3 >= $this->string_length ) {
1991 // At least `min(strlen('TRUE'), strlen('FALSE'))` characters are required.
1992 return null;
1993 }
1994
1995 $i_bak = $this->last;
1996 $token = $this->str[ $this->last ] . $this->str[ ++$this->last ]
1997 . $this->str[ ++$this->last ] . $this->str[ ++$this->last ]; // _TRUE_ or _FALS_e.
1998
1999 if ( static::is_bool( $token ) ) {
2000 return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_BOOL );
2001 }
2002
2003 if ( ++$this->last < $this->string_length ) {
2004 $token .= $this->str[ $this->last ]; // fals_E_.
2005 if ( static::is_bool( $token ) ) {
2006 return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_BOOL, 1 );
2007 }
2008 }
2009
2010 $this->last = $i_bak;
2011
2012 return null;
2013 }
2014
2015 /**
2016 * Parses a number.
2017 *
2018 * @return WP_SQLite_Token|null
2019 */
2020 public function parse_number() {
2021 /*
2022 * A rudimentary state machine is being used to parse numbers due to
2023 * the various forms of their notation.
2024 *
2025 * Below are the states of the machines and the conditions to change
2026 * the state.
2027 *
2028 * 1 --------------------[ + or - ]-------------------> 1
2029 * 1 -------------------[ 0x or 0X ]------------------> 2
2030 * 1 --------------------[ 0 to 9 ]-------------------> 3
2031 * 1 -----------------------[ . ]---------------------> 4
2032 * 1 -----------------------[ b ]---------------------> 7
2033 *
2034 * 2 --------------------[ 0 to F ]-------------------> 2
2035 *
2036 * 3 --------------------[ 0 to 9 ]-------------------> 3
2037 * 3 -----------------------[ . ]---------------------> 4
2038 * 3 --------------------[ e or E ]-------------------> 5
2039 *
2040 * 4 --------------------[ 0 to 9 ]-------------------> 4
2041 * 4 --------------------[ e or E ]-------------------> 5
2042 *
2043 * 5 ---------------[ + or - or 0 to 9 ]--------------> 6
2044 *
2045 * 7 -----------------------[ ' ]---------------------> 8
2046 *
2047 * 8 --------------------[ 0 or 1 ]-------------------> 8
2048 * 8 -----------------------[ ' ]---------------------> 9
2049 *
2050 * State 1 may be reached by negative numbers.
2051 * State 2 is reached only by hex numbers.
2052 * State 4 is reached only by float numbers.
2053 * State 5 is reached only by numbers in approximate form.
2054 * State 7 is reached only by numbers in bit representation.
2055 *
2056 * Valid final states are: 2, 3, 4 and 6. Any parsing that finished in a
2057 * state other than these is invalid.
2058 * Also, negative states are invalid states.
2059 */
2060 $i_bak = $this->last;
2061 $token = '';
2062 $flags = 0;
2063 $state = 1;
2064 for ( ; $this->last < $this->string_length; ++$this->last ) {
2065 if ( 1 === $state ) {
2066 if ( '-' === $this->str[ $this->last ] ) {
2067 $flags |= WP_SQLite_Token::FLAG_NUMBER_NEGATIVE;
2068 } elseif (
2069 $this->last + 1 < $this->string_length
2070 && '0' === $this->str[ $this->last ]
2071 && 'x' === $this->str[ $this->last + 1 ]
2072 ) {
2073 $token .= $this->str[ $this->last++ ];
2074 $state = 2;
2075 } elseif ( $this->str[ $this->last ] >= '0' && $this->str[ $this->last ] <= '9' ) {
2076 $state = 3;
2077 } elseif ( '.' === $this->str[ $this->last ] ) {
2078 $state = 4;
2079 } elseif ( 'b' === $this->str[ $this->last ] ) {
2080 $state = 7;
2081 } elseif ( '+' !== $this->str[ $this->last ] ) {
2082 // `+` is a valid character in a number.
2083 break;
2084 }
2085 } elseif ( 2 === $state ) {
2086 $flags |= WP_SQLite_Token::FLAG_NUMBER_HEX;
2087 if (
2088 ! (
2089 ( $this->str[ $this->last ] >= '0' && $this->str[ $this->last ] <= '9' )
2090 || ( $this->str[ $this->last ] >= 'A' && $this->str[ $this->last ] <= 'F' )
2091 || ( $this->str[ $this->last ] >= 'a' && $this->str[ $this->last ] <= 'f' )
2092 )
2093 ) {
2094 break;
2095 }
2096 } elseif ( 3 === $state ) {
2097 if ( '.' === $this->str[ $this->last ] ) {
2098 $state = 4;
2099 } elseif ( 'e' === $this->str[ $this->last ] || 'E' === $this->str[ $this->last ] ) {
2100 $state = 5;
2101 } elseif (
2102 ( $this->str[ $this->last ] >= 'a' && $this->str[ $this->last ] <= 'z' )
2103 || ( $this->str[ $this->last ] >= 'A' && $this->str[ $this->last ] <= 'Z' )
2104 ) {
2105 // A number can't be directly followed by a letter.
2106 $state = -$state;
2107 } elseif ( $this->str[ $this->last ] < '0' || $this->str[ $this->last ] > '9' ) {
2108 // Just digits and `.`, `e` and `E` are valid characters.
2109 break;
2110 }
2111 } elseif ( 4 === $state ) {
2112 $flags |= WP_SQLite_Token::FLAG_NUMBER_FLOAT;
2113 if ( 'e' === $this->str[ $this->last ] || 'E' === $this->str[ $this->last ] ) {
2114 $state = 5;
2115 } elseif (
2116 ( $this->str[ $this->last ] >= 'a' && $this->str[ $this->last ] <= 'z' )
2117 || ( $this->str[ $this->last ] >= 'A' && $this->str[ $this->last ] <= 'Z' )
2118 ) {
2119 // A number can't be directly followed by a letter.
2120 $state = -$state;
2121 } elseif ( $this->str[ $this->last ] < '0' || $this->str[ $this->last ] > '9' ) {
2122 // Just digits, `e` and `E` are valid characters.
2123 break;
2124 }
2125 } elseif ( 5 === $state ) {
2126 $flags |= WP_SQLite_Token::FLAG_NUMBER_APPROXIMATE;
2127 if (
2128 '+' === $this->str[ $this->last ] || '-' === $this->str[ $this->last ]
2129 || ( $this->str[ $this->last ] >= '0' && $this->str[ $this->last ] <= '9' )
2130 ) {
2131 $state = 6;
2132 } elseif (
2133 ( $this->str[ $this->last ] >= 'a' && $this->str[ $this->last ] <= 'z' )
2134 || ( $this->str[ $this->last ] >= 'A' && $this->str[ $this->last ] <= 'Z' )
2135 ) {
2136 // A number can't be directly followed by a letter.
2137 $state = -$state;
2138 } else {
2139 break;
2140 }
2141 } elseif ( 6 === $state ) {
2142 if ( $this->str[ $this->last ] < '0' || $this->str[ $this->last ] > '9' ) {
2143 // Just digits are valid characters.
2144 break;
2145 }
2146 } elseif ( 7 === $state ) {
2147 $flags |= WP_SQLite_Token::FLAG_NUMBER_BINARY;
2148 if ( '\'' !== $this->str[ $this->last ] ) {
2149 break;
2150 }
2151
2152 $state = 8;
2153 } elseif ( 8 === $state ) {
2154 if ( '\'' === $this->str[ $this->last ] ) {
2155 $state = 9;
2156 } elseif ( '0' !== $this->str[ $this->last ] && '1' !== $this->str[ $this->last ] ) {
2157 break;
2158 }
2159 } elseif ( 9 === $state ) {
2160 break;
2161 }
2162
2163 $token .= $this->str[ $this->last ];
2164 }
2165
2166 if ( 2 === $state || 3 === $state || ( '.' !== $token && 4 === $state ) || 6 === $state || 9 === $state ) {
2167 --$this->last;
2168
2169 return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_NUMBER, $flags );
2170 }
2171
2172 $this->last = $i_bak;
2173
2174 return null;
2175 }
2176
2177 /**
2178 * Parses a string.
2179 *
2180 * @param string $quote Additional starting symbol.
2181 *
2182 * @return WP_SQLite_Token|null
2183 */
2184 public function parse_string( $quote = '' ) {
2185 $token = $this->str[ $this->last ];
2186 $flags = static::is_string( $token );
2187
2188 if ( ! $flags && $token !== $quote ) {
2189 return null;
2190 }
2191
2192 $quote = $token;
2193
2194 while ( ++$this->last < $this->string_length ) {
2195 if (
2196 $this->last + 1 < $this->string_length
2197 && (
2198 ( $this->str[ $this->last ] === $quote && $this->str[ $this->last + 1 ] === $quote )
2199 || ( '\\' === $this->str[ $this->last ] && '`' !== $quote )
2200 )
2201 ) {
2202 $token .= $this->str[ $this->last ] . $this->str[ ++$this->last ];
2203 } else {
2204 if ( $this->str[ $this->last ] === $quote ) {
2205 break;
2206 }
2207
2208 $token .= $this->str[ $this->last ];
2209 }
2210 }
2211
2212 if ( $this->last >= $this->string_length || $this->str[ $this->last ] !== $quote ) {
2213 $this->error(
2214 sprintf(
2215 'Ending quote %1$s was expected.',
2216 $quote
2217 ),
2218 '',
2219 $this->last
2220 );
2221 } else {
2222 $token .= $this->str[ $this->last ];
2223 }
2224
2225 return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_STRING, $flags );
2226 }
2227
2228 /**
2229 * Parses a symbol.
2230 *
2231 * @return WP_SQLite_Token|null
2232 */
2233 public function parse_symbol() {
2234 $token = $this->str[ $this->last ];
2235 $flags = static::is_symbol( $token );
2236
2237 if ( ! $flags ) {
2238 return null;
2239 }
2240
2241 if ( $flags & WP_SQLite_Token::FLAG_SYMBOL_VARIABLE ) {
2242 if ( $this->last + 1 < $this->string_length && '@' === $this->str[ ++$this->last ] ) {
2243 // This is a system variable (e.g. `@@hostname`).
2244 $token .= $this->str[ $this->last++ ];
2245 $flags |= WP_SQLite_Token::FLAG_SYMBOL_SYSTEM;
2246 }
2247 } elseif ( $flags & WP_SQLite_Token::FLAG_SYMBOL_PARAMETER ) {
2248 if ( '?' !== $token && $this->last + 1 < $this->string_length ) {
2249 ++$this->last;
2250 }
2251 } else {
2252 $token = '';
2253 }
2254
2255 $str = null;
2256
2257 if ( $this->last < $this->string_length ) {
2258 $str = $this->parse_string( '`' );
2259
2260 if ( null === $str ) {
2261 $str = $this->parse_unknown();
2262
2263 if ( null === $str && ! ( $flags & WP_SQLite_Token::FLAG_SYMBOL_PARAMETER ) ) {
2264 $this->error( 'Variable name was expected.', $this->str[ $this->last ], $this->last );
2265 }
2266 }
2267 }
2268
2269 if ( null !== $str ) {
2270 $token .= $str->token;
2271 }
2272
2273 return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_SYMBOL, $flags );
2274 }
2275
2276 /**
2277 * Parses unknown parts of the query.
2278 *
2279 * @return WP_SQLite_Token|null
2280 */
2281 public function parse_unknown() {
2282 $token = $this->str[ $this->last ];
2283 if ( static::is_separator( $token ) ) {
2284 return null;
2285 }
2286
2287 while ( ++$this->last < $this->string_length && ! static::is_separator( $this->str[ $this->last ] ) ) {
2288 $token .= $this->str[ $this->last ];
2289
2290 // Test if end of token equals the current delimiter. If so, remove it from the token.
2291 if ( str_ends_with( $token, $this->delimiter ) ) {
2292 $token = substr( $token, 0, -$this->delimiter_length );
2293 $this->last -= $this->delimiter_length - 1;
2294 break;
2295 }
2296 }
2297
2298 --$this->last;
2299
2300 return new WP_SQLite_Token( $token );
2301 }
2302
2303 /**
2304 * Parses the delimiter of the query.
2305 *
2306 * @return WP_SQLite_Token|null
2307 */
2308 public function parse_delimiter() {
2309 $index = 0;
2310
2311 while ( $index < $this->delimiter_length && $this->last + $index < $this->string_length ) {
2312 if ( $this->delimiter[ $index ] !== $this->str[ $this->last + $index ] ) {
2313 return null;
2314 }
2315
2316 ++$index;
2317 }
2318
2319 $this->last += $this->delimiter_length - 1;
2320
2321 return new WP_SQLite_Token( $this->delimiter, WP_SQLite_Token::TYPE_DELIMITER );
2322 }
2323
2324 /**
2325 * Checks if the given string is a keyword.
2326 *
2327 * @param string $str String to be checked.
2328 * @param bool $is_reserved Checks if the keyword is reserved.
2329 *
2330 * @return int|null
2331 */
2332 public static function is_keyword( $str, $is_reserved = false ) {
2333 $str = strtoupper( $str );
2334
2335 if ( isset( static::$keywords[ $str ] ) ) {
2336 if ( $is_reserved && ! ( static::$keywords[ $str ] & WP_SQLite_Token::FLAG_KEYWORD_RESERVED ) ) {
2337 return null;
2338 }
2339
2340 return static::$keywords[ $str ];
2341 }
2342
2343 return null;
2344 }
2345
2346 /**
2347 * Checks if the given string is an operator.
2348 *
2349 * @param string $str String to be checked.
2350 *
2351 * @return int|null The appropriate flag for the operator.
2352 */
2353 public static function is_operator( $str ) {
2354 if ( ! isset( static::$operators[ $str ] ) ) {
2355 return null;
2356 }
2357
2358 return static::$operators[ $str ];
2359 }
2360
2361 /**
2362 * Checks if the given character is a whitespace.
2363 *
2364 * @param string $str String to be checked.
2365 *
2366 * @return bool
2367 */
2368 public static function is_whitespace( $str ) {
2369 return ( ' ' === $str ) || ( "\r" === $str ) || ( "\n" === $str ) || ( "\t" === $str );
2370 }
2371
2372 /**
2373 * Checks if the given string is the beginning of a whitespace.
2374 *
2375 * @param string $str String to be checked.
2376 * @param mixed $end Whether this is the end of the string.
2377 *
2378 * @return int|null The appropriate flag for the comment type.
2379 */
2380 public static function is_comment( $str, $end = false ) {
2381 $string_length = strlen( $str );
2382 if ( 0 === $string_length ) {
2383 return null;
2384 }
2385
2386 // If comment is Bash style (#).
2387 if ( '#' === $str[0] ) {
2388 return WP_SQLite_Token::FLAG_COMMENT_BASH;
2389 }
2390
2391 // If comment is opening C style (/*), warning, it could be a MySQL command (/*!).
2392 if ( ( $string_length > 1 ) && ( '/' === $str[0] ) && ( '*' === $str[1] ) ) {
2393 return ( $string_length > 2 ) && ( '!' === $str[2] ) ?
2394 WP_SQLite_Token::FLAG_COMMENT_MYSQL_CMD : WP_SQLite_Token::FLAG_COMMENT_C;
2395 }
2396
2397 // If comment is closing C style (*/), warning, it could conflicts with wildcard and a real opening C style.
2398 // It would looks like the following valid SQL statement: "SELECT */* comment */ FROM...".
2399 if ( ( $string_length > 1 ) && ( '*' === $str[0] ) && ( '/' === $str[1] ) ) {
2400 return WP_SQLite_Token::FLAG_COMMENT_C;
2401 }
2402
2403 // If comment is SQL style (--\s?).
2404 if ( ( $string_length > 2 ) && ( '-' === $str[0] ) && ( '-' === $str[1] ) && static::is_whitespace( $str[2] ) ) {
2405 return WP_SQLite_Token::FLAG_COMMENT_SQL;
2406 }
2407
2408 if ( ( 2 === $string_length ) && $end && ( '-' === $str[0] ) && ( '-' === $str[1] ) ) {
2409 return WP_SQLite_Token::FLAG_COMMENT_SQL;
2410 }
2411
2412 return null;
2413 }
2414
2415 /**
2416 * Checks if the given string is a boolean value.
2417 * This actually checks only for `TRUE` and `FALSE` because `1` or `0` are
2418 * numbers and are parsed by specific methods.
2419 *
2420 * @param string $str String to be checked.
2421 *
2422 * @return bool
2423 */
2424 public static function is_bool( $str ) {
2425 $str = strtoupper( $str );
2426
2427 return ( 'TRUE' === $str ) || ( 'FALSE' === $str );
2428 }
2429
2430 /**
2431 * Checks if the given character can be a part of a number.
2432 *
2433 * @param string $str String to be checked.
2434 *
2435 * @return bool
2436 */
2437 public static function is_number( $str ) {
2438 return ( $str >= '0' ) && ( $str <= '9' ) || ( '.' === $str )
2439 || ( '-' === $str ) || ( '+' === $str ) || ( 'e' === $str ) || ( 'E' === $str );
2440 }
2441
2442 /**
2443 * Checks if the given character is the beginning of a symbol. A symbol
2444 * can be either a variable or a field name.
2445 *
2446 * @param string $str String to be checked.
2447 *
2448 * @return int|null The appropriate flag for the symbol type.
2449 */
2450 public static function is_symbol( $str ) {
2451 if ( 0 === strlen( $str ) ) {
2452 return null;
2453 }
2454
2455 if ( '@' === $str[0] ) {
2456 return WP_SQLite_Token::FLAG_SYMBOL_VARIABLE;
2457 }
2458
2459 if ( '`' === $str[0] ) {
2460 return WP_SQLite_Token::FLAG_SYMBOL_BACKTICK;
2461 }
2462
2463 if ( ':' === $str[0] || '?' === $str[0] ) {
2464 return WP_SQLite_Token::FLAG_SYMBOL_PARAMETER;
2465 }
2466
2467 return null;
2468 }
2469
2470 /**
2471 * Checks if the given character is the beginning of a string.
2472 *
2473 * @param string $str String to be checked.
2474 *
2475 * @return int|null The appropriate flag for the string type.
2476 */
2477 public static function is_string( $str ) {
2478 if ( strlen( $str ) === 0 ) {
2479 return null;
2480 }
2481
2482 if ( '\'' === $str[0] ) {
2483 return WP_SQLite_Token::FLAG_STRING_SINGLE_QUOTES;
2484 }
2485
2486 if ( '"' === $str[0] ) {
2487 return WP_SQLite_Token::FLAG_STRING_DOUBLE_QUOTES;
2488 }
2489
2490 return null;
2491 }
2492
2493 /**
2494 * Checks if the given character can be a separator for two lexeme.
2495 *
2496 * @param string $str String to be checked.
2497 *
2498 * @return bool
2499 */
2500 public static function is_separator( $str ) {
2501 /*
2502 * NOTES: Only non alphanumeric ASCII characters may be separators.
2503 * `~` is the last printable ASCII character.
2504 */
2505 return ( $str <= '~' )
2506 && ( '_' !== $str )
2507 && ( '$' !== $str )
2508 && ( ( $str < '0' ) || ( $str > '9' ) )
2509 && ( ( $str < 'a' ) || ( $str > 'z' ) )
2510 && ( ( $str < 'A' ) || ( $str > 'Z' ) );
2511 }
2512
2513 /**
2514 * Constructor.
2515 *
2516 * @param stdClass[] $tokens The initial array of tokens.
2517 */
2518 public function tokens( array $tokens = array() ) {
2519 $this->tokens = $tokens;
2520 $this->tokens_count = count( $tokens );
2521 }
2522
2523 /**
2524 * Gets the next token.
2525 *
2526 * @param int $type The type of the token.
2527 * @param int $flag The flag of the token.
2528 */
2529 public function tokens_get_next_of_type_and_flag( $type, $flag ) {
2530 for ( ; $this->tokens_index < $this->tokens_count; ++$this->tokens_index ) {
2531 if ( ( $this->tokens[ $this->tokens_index ]->type === $type ) && ( $this->tokens[ $this->tokens_index ]->flags === $flag ) ) {
2532 return $this->tokens[ $this->tokens_index++ ];
2533 }
2534 }
2535
2536 return null;
2537 }
2538
2539 /**
2540 * Gets the next token.
2541 *
2542 * @param int $type The type of the token.
2543 * @param string $value The value of the token.
2544 *
2545 * @return stdClass|null
2546 */
2547 public function tokens_get_next_of_type_and_value( $type, $value ) {
2548 for ( ; $this->tokens_index < $this->tokens_count; ++$this->tokens_index ) {
2549 if ( ( $this->tokens[ $this->tokens_index ]->type === $type ) && ( $this->tokens[ $this->tokens_index ]->value === $value ) ) {
2550 return $this->tokens[ $this->tokens_index++ ];
2551 }
2552 }
2553
2554 return null;
2555 }
2556
2557 /**
2558 * Gets the next token. Skips any irrelevant token (whitespaces and
2559 * comments).
2560 *
2561 * @return stdClass|null
2562 */
2563 public function tokens_get_next() {
2564 for ( ; $this->tokens_index < $this->tokens_count; ++$this->tokens_index ) {
2565 if (
2566 ( WP_SQLite_Token::TYPE_WHITESPACE !== $this->tokens[ $this->tokens_index ]->type )
2567 && ( WP_SQLite_Token::TYPE_COMMENT !== $this->tokens[ $this->tokens_index ]->type )
2568 ) {
2569 return $this->tokens[ $this->tokens_index++ ];
2570 }
2571 }
2572
2573 return null;
2574 }
2575 }
2576