2576 lines
86 KiB
PHP
2576 lines
86 KiB
PHP
|
|
<?php
|
|||
|
|
/**
|
|||
|
|
* This file is a port of the Lexer & TokensList classes from the PHPMyAdmin/sql-parser library.
|
|||
|
|
*
|
|||
|
|
* @package wp-sqlite-integration
|
|||
|
|
* @see https://github.com/phpmyadmin/sql-parser
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Performs lexical analysis over a SQL statement and splits it in multiple tokens.
|
|||
|
|
*/
|
|||
|
|
class WP_SQLite_Lexer {
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* The maximum length of a keyword.
|
|||
|
|
*/
|
|||
|
|
const KEYWORD_MAX_LENGTH = 30;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* The maximum length of a label.
|
|||
|
|
*
|
|||
|
|
* Ref: https://dev.mysql.com/doc/refman/5.7/en/statement-labels.html
|
|||
|
|
*/
|
|||
|
|
const LABEL_MAX_LENGTH = 16;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* The maximum length of an operator.
|
|||
|
|
*/
|
|||
|
|
const OPERATOR_MAX_LENGTH = 4;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* A list of methods that are used in lexing the SQL query.
|
|||
|
|
*
|
|||
|
|
* @var string[]
|
|||
|
|
*/
|
|||
|
|
const PARSER_METHODS = array(
|
|||
|
|
// It is best to put the parsers in order of their complexity
|
|||
|
|
// (ascending) and their occurrence rate (descending).
|
|||
|
|
//
|
|||
|
|
// Conflicts:
|
|||
|
|
//
|
|||
|
|
// 1. `parse_delimiter`, `parse_unknown`, `parse_keyword`, `parse_number`
|
|||
|
|
// They fight over delimiter. The delimiter may be a keyword, a
|
|||
|
|
// number or almost any character which makes the delimiter one of
|
|||
|
|
// the first tokens that must be parsed.
|
|||
|
|
//
|
|||
|
|
// 1. `parse_number` and `parse_operator`
|
|||
|
|
// They fight over `+` and `-`.
|
|||
|
|
//
|
|||
|
|
// 2. `parse_comment` and `parse_operator`
|
|||
|
|
// They fight over `/` (as in ```/*comment*/``` or ```a / b```)
|
|||
|
|
//
|
|||
|
|
// 3. `parse_bool` and `parse_keyword`
|
|||
|
|
// They fight over `TRUE` and `FALSE`.
|
|||
|
|
//
|
|||
|
|
// 4. `parse_keyword` and `parse_unknown`
|
|||
|
|
// They fight over words. `parse_unknown` does not know about
|
|||
|
|
// keywords.
|
|||
|
|
|
|||
|
|
'parse_delimiter',
|
|||
|
|
'parse_whitespace',
|
|||
|
|
'parse_number',
|
|||
|
|
'parse_comment',
|
|||
|
|
'parse_operator',
|
|||
|
|
'parse_bool',
|
|||
|
|
'parse_string',
|
|||
|
|
'parse_symbol',
|
|||
|
|
'parse_keyword',
|
|||
|
|
'parse_label',
|
|||
|
|
'parse_unknown',
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* A list of keywords that indicate that the function keyword
|
|||
|
|
* is not used as a function.
|
|||
|
|
*
|
|||
|
|
* @var string[]
|
|||
|
|
*/
|
|||
|
|
const KEYWORD_NAME_INDICATORS = array(
|
|||
|
|
'FROM',
|
|||
|
|
'SET',
|
|||
|
|
'WHERE',
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* A list of operators that indicate that the function keyword
|
|||
|
|
* is not used as a function.
|
|||
|
|
*
|
|||
|
|
* @var string[]
|
|||
|
|
*/
|
|||
|
|
const OPERATOR_NAME_INDICATORS = array(
|
|||
|
|
',',
|
|||
|
|
'.',
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* The string to be parsed.
|
|||
|
|
*
|
|||
|
|
* @var string
|
|||
|
|
*/
|
|||
|
|
public $str = '';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* The length of `$str`.
|
|||
|
|
*
|
|||
|
|
* By storing its length, a lot of time is saved, because parsing methods
|
|||
|
|
* would call `strlen` everytime.
|
|||
|
|
*
|
|||
|
|
* @var int
|
|||
|
|
*/
|
|||
|
|
public $string_length = 0;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* The index of the last parsed character.
|
|||
|
|
*
|
|||
|
|
* @var int
|
|||
|
|
*/
|
|||
|
|
public $last = 0;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* The default delimiter. This is used, by default, in all new instances.
|
|||
|
|
*
|
|||
|
|
* @var string
|
|||
|
|
*/
|
|||
|
|
public static $default_delimiter = ';';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Statements delimiter.
|
|||
|
|
* This may change during lexing.
|
|||
|
|
*
|
|||
|
|
* @var string
|
|||
|
|
*/
|
|||
|
|
public $delimiter;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* The length of the delimiter.
|
|||
|
|
*
|
|||
|
|
* Because `parse_delimiter` can be called a lot, it would perform a lot of
|
|||
|
|
* calls to `strlen`, which might affect performance when the delimiter is
|
|||
|
|
* big.
|
|||
|
|
*
|
|||
|
|
* @var int
|
|||
|
|
*/
|
|||
|
|
public $delimiter_length;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* List of operators and their flags.
|
|||
|
|
*
|
|||
|
|
* @var array<string, int>
|
|||
|
|
*/
|
|||
|
|
public static $operators = array(
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
* Some operators (*, =) may have ambiguous flags, because they depend on
|
|||
|
|
* the context they are being used in.
|
|||
|
|
* For example: 1. SELECT * FROM table; # SQL specific (wildcard)
|
|||
|
|
* SELECT 2 * 3; # arithmetic
|
|||
|
|
* 2. SELECT * FROM table WHERE foo = 'bar';
|
|||
|
|
* SET @i = 0;
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// @see WP_SQLite_Token::FLAG_OPERATOR_ARITHMETIC
|
|||
|
|
'%' => 1,
|
|||
|
|
'*' => 1,
|
|||
|
|
'+' => 1,
|
|||
|
|
'-' => 1,
|
|||
|
|
'/' => 1,
|
|||
|
|
|
|||
|
|
// @see WP_SQLite_Token::FLAG_OPERATOR_LOGICAL
|
|||
|
|
'!' => 2,
|
|||
|
|
'!=' => 2,
|
|||
|
|
'&&' => 2,
|
|||
|
|
'<' => 2,
|
|||
|
|
'<=' => 2,
|
|||
|
|
'<=>' => 2,
|
|||
|
|
'<>' => 2,
|
|||
|
|
'=' => 2,
|
|||
|
|
'>' => 2,
|
|||
|
|
'>=' => 2,
|
|||
|
|
'||' => 2,
|
|||
|
|
|
|||
|
|
// @see WP_SQLite_Token::FLAG_OPERATOR_BITWISE
|
|||
|
|
'&' => 4,
|
|||
|
|
'<<' => 4,
|
|||
|
|
'>>' => 4,
|
|||
|
|
'^' => 4,
|
|||
|
|
'|' => 4,
|
|||
|
|
'~' => 4,
|
|||
|
|
|
|||
|
|
// @see WP_SQLite_Token::FLAG_OPERATOR_ASSIGNMENT
|
|||
|
|
':=' => 8,
|
|||
|
|
|
|||
|
|
// @see WP_SQLite_Token::FLAG_OPERATOR_SQL
|
|||
|
|
'(' => 16,
|
|||
|
|
')' => 16,
|
|||
|
|
'.' => 16,
|
|||
|
|
',' => 16,
|
|||
|
|
';' => 16,
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* List of keywords.
|
|||
|
|
*
|
|||
|
|
* The value associated to each keyword represents its flags.
|
|||
|
|
*
|
|||
|
|
* @see WP_SQLite_Token::FLAG_KEYWORD_RESERVED
|
|||
|
|
* WP_SQLite_Token::FLAG_KEYWORD_COMPOSED
|
|||
|
|
* WP_SQLite_Token::FLAG_KEYWORD_DATA_TYPE
|
|||
|
|
* ∂WP_SQLite_Token::FLAG_KEYWORD_KEY
|
|||
|
|
* WP_SQLite_Token::FLAG_KEYWORD_FUNCTION
|
|||
|
|
*
|
|||
|
|
* @var array<string,int>
|
|||
|
|
*/
|
|||
|
|
public static $keywords = array(
|
|||
|
|
'AT' => 1,
|
|||
|
|
'DO' => 1,
|
|||
|
|
'IO' => 1,
|
|||
|
|
'NO' => 1,
|
|||
|
|
'XA' => 1,
|
|||
|
|
'ANY' => 1,
|
|||
|
|
'CPU' => 1,
|
|||
|
|
'END' => 1,
|
|||
|
|
'IPC' => 1,
|
|||
|
|
'NDB' => 1,
|
|||
|
|
'NEW' => 1,
|
|||
|
|
'ONE' => 1,
|
|||
|
|
'ROW' => 1,
|
|||
|
|
'XID' => 1,
|
|||
|
|
'BOOL' => 1,
|
|||
|
|
'BYTE' => 1,
|
|||
|
|
'CODE' => 1,
|
|||
|
|
'CUBE' => 1,
|
|||
|
|
'DATA' => 1,
|
|||
|
|
'DISK' => 1,
|
|||
|
|
'ENDS' => 1,
|
|||
|
|
'FAST' => 1,
|
|||
|
|
'FILE' => 1,
|
|||
|
|
'FULL' => 1,
|
|||
|
|
'HASH' => 1,
|
|||
|
|
'HELP' => 1,
|
|||
|
|
'HOST' => 1,
|
|||
|
|
'LAST' => 1,
|
|||
|
|
'LESS' => 1,
|
|||
|
|
'LIST' => 1,
|
|||
|
|
'LOGS' => 1,
|
|||
|
|
'MODE' => 1,
|
|||
|
|
'NAME' => 1,
|
|||
|
|
'NEXT' => 1,
|
|||
|
|
'NONE' => 1,
|
|||
|
|
'ONLY' => 1,
|
|||
|
|
'OPEN' => 1,
|
|||
|
|
'PAGE' => 1,
|
|||
|
|
'PORT' => 1,
|
|||
|
|
'PREV' => 1,
|
|||
|
|
'ROWS' => 1,
|
|||
|
|
'SLOW' => 1,
|
|||
|
|
'SOME' => 1,
|
|||
|
|
'STOP' => 1,
|
|||
|
|
'THAN' => 1,
|
|||
|
|
'TYPE' => 1,
|
|||
|
|
'VIEW' => 1,
|
|||
|
|
'WAIT' => 1,
|
|||
|
|
'WORK' => 1,
|
|||
|
|
'X509' => 1,
|
|||
|
|
'AFTER' => 1,
|
|||
|
|
'BEGIN' => 1,
|
|||
|
|
'BLOCK' => 1,
|
|||
|
|
'BTREE' => 1,
|
|||
|
|
'CACHE' => 1,
|
|||
|
|
'CHAIN' => 1,
|
|||
|
|
'CLOSE' => 1,
|
|||
|
|
'ERROR' => 1,
|
|||
|
|
'EVENT' => 1,
|
|||
|
|
'EVERY' => 1,
|
|||
|
|
'FIRST' => 1,
|
|||
|
|
'FIXED' => 1,
|
|||
|
|
'FLUSH' => 1,
|
|||
|
|
'FOUND' => 1,
|
|||
|
|
'HOSTS' => 1,
|
|||
|
|
'LEVEL' => 1,
|
|||
|
|
'LOCAL' => 1,
|
|||
|
|
'LOCKS' => 1,
|
|||
|
|
'MERGE' => 1,
|
|||
|
|
'MUTEX' => 1,
|
|||
|
|
'NAMES' => 1,
|
|||
|
|
'NCHAR' => 1,
|
|||
|
|
'NEVER' => 1,
|
|||
|
|
'OWNER' => 1,
|
|||
|
|
'PHASE' => 1,
|
|||
|
|
'PROXY' => 1,
|
|||
|
|
'QUERY' => 1,
|
|||
|
|
'QUICK' => 1,
|
|||
|
|
'RELAY' => 1,
|
|||
|
|
'RESET' => 1,
|
|||
|
|
'RTREE' => 1,
|
|||
|
|
'SHARE' => 1,
|
|||
|
|
'SLAVE' => 1,
|
|||
|
|
'START' => 1,
|
|||
|
|
'SUPER' => 1,
|
|||
|
|
'SWAPS' => 1,
|
|||
|
|
'TYPES' => 1,
|
|||
|
|
'UNTIL' => 1,
|
|||
|
|
'VALUE' => 1,
|
|||
|
|
'ACTION' => 1,
|
|||
|
|
'ALWAYS' => 1,
|
|||
|
|
'BACKUP' => 1,
|
|||
|
|
'BINLOG' => 1,
|
|||
|
|
'CIPHER' => 1,
|
|||
|
|
'CLIENT' => 1,
|
|||
|
|
'COMMIT' => 1,
|
|||
|
|
'ENABLE' => 1,
|
|||
|
|
'ENGINE' => 1,
|
|||
|
|
'ERRORS' => 1,
|
|||
|
|
'ESCAPE' => 1,
|
|||
|
|
'EVENTS' => 1,
|
|||
|
|
'EXPIRE' => 1,
|
|||
|
|
'EXPORT' => 1,
|
|||
|
|
'FAULTS' => 1,
|
|||
|
|
'FIELDS' => 1,
|
|||
|
|
'FILTER' => 1,
|
|||
|
|
'GLOBAL' => 1,
|
|||
|
|
'GRANTS' => 1,
|
|||
|
|
'IMPORT' => 1,
|
|||
|
|
'ISSUER' => 1,
|
|||
|
|
'LEAVES' => 1,
|
|||
|
|
'MASTER' => 1,
|
|||
|
|
'MEDIUM' => 1,
|
|||
|
|
'MEMORY' => 1,
|
|||
|
|
'MODIFY' => 1,
|
|||
|
|
'NUMBER' => 1,
|
|||
|
|
'OFFSET' => 1,
|
|||
|
|
'PARSER' => 1,
|
|||
|
|
'PLUGIN' => 1,
|
|||
|
|
'RELOAD' => 1,
|
|||
|
|
'REMOVE' => 1,
|
|||
|
|
'REPAIR' => 1,
|
|||
|
|
'RESUME' => 1,
|
|||
|
|
'ROLLUP' => 1,
|
|||
|
|
'SERVER' => 1,
|
|||
|
|
'SIGNED' => 1,
|
|||
|
|
'SIMPLE' => 1,
|
|||
|
|
'SOCKET' => 1,
|
|||
|
|
'SONAME' => 1,
|
|||
|
|
'SOUNDS' => 1,
|
|||
|
|
'SOURCE' => 1,
|
|||
|
|
'STARTS' => 1,
|
|||
|
|
'STATUS' => 1,
|
|||
|
|
'STRING' => 1,
|
|||
|
|
'TABLES' => 1,
|
|||
|
|
'ACCOUNT' => 1,
|
|||
|
|
'ANALYSE' => 1,
|
|||
|
|
'CHANGED' => 1,
|
|||
|
|
'CHANNEL' => 1,
|
|||
|
|
'COLUMNS' => 1,
|
|||
|
|
'COMMENT' => 1,
|
|||
|
|
'COMPACT' => 1,
|
|||
|
|
'CONTEXT' => 1,
|
|||
|
|
'CURRENT' => 1,
|
|||
|
|
'DEFINER' => 1,
|
|||
|
|
'DISABLE' => 1,
|
|||
|
|
'DISCARD' => 1,
|
|||
|
|
'DYNAMIC' => 1,
|
|||
|
|
'ENGINES' => 1,
|
|||
|
|
'EXECUTE' => 1,
|
|||
|
|
'FOLLOWS' => 1,
|
|||
|
|
'GENERAL' => 1,
|
|||
|
|
'HANDLER' => 1,
|
|||
|
|
'INDEXES' => 1,
|
|||
|
|
'INSTALL' => 1,
|
|||
|
|
'INVOKER' => 1,
|
|||
|
|
'LOGFILE' => 1,
|
|||
|
|
'MIGRATE' => 1,
|
|||
|
|
'NO_WAIT' => 1,
|
|||
|
|
'OPTIONS' => 1,
|
|||
|
|
'PARTIAL' => 1,
|
|||
|
|
'PERSIST' => 1,
|
|||
|
|
'PLUGINS' => 1,
|
|||
|
|
'PREPARE' => 1,
|
|||
|
|
'PROFILE' => 1,
|
|||
|
|
'REBUILD' => 1,
|
|||
|
|
'RECOVER' => 1,
|
|||
|
|
'RESTORE' => 1,
|
|||
|
|
'RETURNS' => 1,
|
|||
|
|
'ROUTINE' => 1,
|
|||
|
|
'SESSION' => 1,
|
|||
|
|
'STACKED' => 1,
|
|||
|
|
'STORAGE' => 1,
|
|||
|
|
'SUBJECT' => 1,
|
|||
|
|
'SUSPEND' => 1,
|
|||
|
|
'UNICODE' => 1,
|
|||
|
|
'UNKNOWN' => 1,
|
|||
|
|
'UPGRADE' => 1,
|
|||
|
|
'USE_FRM' => 1,
|
|||
|
|
'WITHOUT' => 1,
|
|||
|
|
'WRAPPER' => 1,
|
|||
|
|
'CASCADED' => 1,
|
|||
|
|
'CHECKSUM' => 1,
|
|||
|
|
'DATAFILE' => 1,
|
|||
|
|
'DUMPFILE' => 1,
|
|||
|
|
'EXCHANGE' => 1,
|
|||
|
|
'EXTENDED' => 1,
|
|||
|
|
'FUNCTION' => 1,
|
|||
|
|
'LANGUAGE' => 1,
|
|||
|
|
'MAX_ROWS' => 1,
|
|||
|
|
'MAX_SIZE' => 1,
|
|||
|
|
'MIN_ROWS' => 1,
|
|||
|
|
'NATIONAL' => 1,
|
|||
|
|
'NVARCHAR' => 1,
|
|||
|
|
'PRECEDES' => 1,
|
|||
|
|
'PRESERVE' => 1,
|
|||
|
|
'PROFILES' => 1,
|
|||
|
|
'REDOFILE' => 1,
|
|||
|
|
'RELAYLOG' => 1,
|
|||
|
|
'ROLLBACK' => 1,
|
|||
|
|
'SCHEDULE' => 1,
|
|||
|
|
'SECURITY' => 1,
|
|||
|
|
'SEQUENCE' => 1,
|
|||
|
|
'SHUTDOWN' => 1,
|
|||
|
|
'SNAPSHOT' => 1,
|
|||
|
|
'SWITCHES' => 1,
|
|||
|
|
'TRIGGERS' => 1,
|
|||
|
|
'UNDOFILE' => 1,
|
|||
|
|
'WARNINGS' => 1,
|
|||
|
|
'AGGREGATE' => 1,
|
|||
|
|
'ALGORITHM' => 1,
|
|||
|
|
'COMMITTED' => 1,
|
|||
|
|
'DIRECTORY' => 1,
|
|||
|
|
'DUPLICATE' => 1,
|
|||
|
|
'EXPANSION' => 1,
|
|||
|
|
'INVISIBLE' => 1,
|
|||
|
|
'IO_THREAD' => 1,
|
|||
|
|
'ISOLATION' => 1,
|
|||
|
|
'NODEGROUP' => 1,
|
|||
|
|
'PACK_KEYS' => 1,
|
|||
|
|
'READ_ONLY' => 1,
|
|||
|
|
'REDUNDANT' => 1,
|
|||
|
|
'SAVEPOINT' => 1,
|
|||
|
|
'SQL_CACHE' => 1,
|
|||
|
|
'TEMPORARY' => 1,
|
|||
|
|
'TEMPTABLE' => 1,
|
|||
|
|
'UNDEFINED' => 1,
|
|||
|
|
'UNINSTALL' => 1,
|
|||
|
|
'VARIABLES' => 1,
|
|||
|
|
'COMPLETION' => 1,
|
|||
|
|
'COMPRESSED' => 1,
|
|||
|
|
'CONCURRENT' => 1,
|
|||
|
|
'CONNECTION' => 1,
|
|||
|
|
'CONSISTENT' => 1,
|
|||
|
|
'DEALLOCATE' => 1,
|
|||
|
|
'IDENTIFIED' => 1,
|
|||
|
|
'MASTER_SSL' => 1,
|
|||
|
|
'NDBCLUSTER' => 1,
|
|||
|
|
'PARTITIONS' => 1,
|
|||
|
|
'PERSISTENT' => 1,
|
|||
|
|
'PLUGIN_DIR' => 1,
|
|||
|
|
'PRIVILEGES' => 1,
|
|||
|
|
'REORGANIZE' => 1,
|
|||
|
|
'REPEATABLE' => 1,
|
|||
|
|
'ROW_FORMAT' => 1,
|
|||
|
|
'SQL_THREAD' => 1,
|
|||
|
|
'TABLESPACE' => 1,
|
|||
|
|
'TABLE_NAME' => 1,
|
|||
|
|
'VALIDATION' => 1,
|
|||
|
|
'COLUMN_NAME' => 1,
|
|||
|
|
'COMPRESSION' => 1,
|
|||
|
|
'CURSOR_NAME' => 1,
|
|||
|
|
'DIAGNOSTICS' => 1,
|
|||
|
|
'EXTENT_SIZE' => 1,
|
|||
|
|
'MASTER_HOST' => 1,
|
|||
|
|
'MASTER_PORT' => 1,
|
|||
|
|
'MASTER_USER' => 1,
|
|||
|
|
'MYSQL_ERRNO' => 1,
|
|||
|
|
'NONBLOCKING' => 1,
|
|||
|
|
'PROCESSLIST' => 1,
|
|||
|
|
'REPLICATION' => 1,
|
|||
|
|
'SCHEMA_NAME' => 1,
|
|||
|
|
'SQL_TSI_DAY' => 1,
|
|||
|
|
'TRANSACTION' => 1,
|
|||
|
|
'UNCOMMITTED' => 1,
|
|||
|
|
'CATALOG_NAME' => 1,
|
|||
|
|
'CLASS_ORIGIN' => 1,
|
|||
|
|
'DEFAULT_AUTH' => 1,
|
|||
|
|
'DES_KEY_FILE' => 1,
|
|||
|
|
'INITIAL_SIZE' => 1,
|
|||
|
|
'MASTER_DELAY' => 1,
|
|||
|
|
'MESSAGE_TEXT' => 1,
|
|||
|
|
'PARTITIONING' => 1,
|
|||
|
|
'PERSIST_ONLY' => 1,
|
|||
|
|
'RELAY_THREAD' => 1,
|
|||
|
|
'SERIALIZABLE' => 1,
|
|||
|
|
'SQL_NO_CACHE' => 1,
|
|||
|
|
'SQL_TSI_HOUR' => 1,
|
|||
|
|
'SQL_TSI_WEEK' => 1,
|
|||
|
|
'SQL_TSI_YEAR' => 1,
|
|||
|
|
'SUBPARTITION' => 1,
|
|||
|
|
'COLUMN_FORMAT' => 1,
|
|||
|
|
'INSERT_METHOD' => 1,
|
|||
|
|
'MASTER_SSL_CA' => 1,
|
|||
|
|
'RELAY_LOG_POS' => 1,
|
|||
|
|
'SQL_TSI_MONTH' => 1,
|
|||
|
|
'SUBPARTITIONS' => 1,
|
|||
|
|
'AUTO_INCREMENT' => 1,
|
|||
|
|
'AVG_ROW_LENGTH' => 1,
|
|||
|
|
'KEY_BLOCK_SIZE' => 1,
|
|||
|
|
'MASTER_LOG_POS' => 1,
|
|||
|
|
'MASTER_SSL_CRL' => 1,
|
|||
|
|
'MASTER_SSL_KEY' => 1,
|
|||
|
|
'RELAY_LOG_FILE' => 1,
|
|||
|
|
'SQL_TSI_MINUTE' => 1,
|
|||
|
|
'SQL_TSI_SECOND' => 1,
|
|||
|
|
'TABLE_CHECKSUM' => 1,
|
|||
|
|
'USER_RESOURCES' => 1,
|
|||
|
|
'AUTOEXTEND_SIZE' => 1,
|
|||
|
|
'CONSTRAINT_NAME' => 1,
|
|||
|
|
'DELAY_KEY_WRITE' => 1,
|
|||
|
|
'FILE_BLOCK_SIZE' => 1,
|
|||
|
|
'MASTER_LOG_FILE' => 1,
|
|||
|
|
'MASTER_PASSWORD' => 1,
|
|||
|
|
'MASTER_SSL_CERT' => 1,
|
|||
|
|
'PARSE_GCOL_EXPR' => 1,
|
|||
|
|
'REPLICATE_DO_DB' => 1,
|
|||
|
|
'SQL_AFTER_GTIDS' => 1,
|
|||
|
|
'SQL_TSI_QUARTER' => 1,
|
|||
|
|
'SUBCLASS_ORIGIN' => 1,
|
|||
|
|
'MASTER_SERVER_ID' => 1,
|
|||
|
|
'REDO_BUFFER_SIZE' => 1,
|
|||
|
|
'SQL_BEFORE_GTIDS' => 1,
|
|||
|
|
'STATS_PERSISTENT' => 1,
|
|||
|
|
'UNDO_BUFFER_SIZE' => 1,
|
|||
|
|
'CONSTRAINT_SCHEMA' => 1,
|
|||
|
|
'GROUP_REPLICATION' => 1,
|
|||
|
|
'IGNORE_SERVER_IDS' => 1,
|
|||
|
|
'MASTER_SSL_CAPATH' => 1,
|
|||
|
|
'MASTER_SSL_CIPHER' => 1,
|
|||
|
|
'RETURNED_SQLSTATE' => 1,
|
|||
|
|
'SQL_BUFFER_RESULT' => 1,
|
|||
|
|
'STATS_AUTO_RECALC' => 1,
|
|||
|
|
'CONSTRAINT_CATALOG' => 1,
|
|||
|
|
'MASTER_RETRY_COUNT' => 1,
|
|||
|
|
'MASTER_SSL_CRLPATH' => 1,
|
|||
|
|
'MAX_STATEMENT_TIME' => 1,
|
|||
|
|
'REPLICATE_DO_TABLE' => 1,
|
|||
|
|
'SQL_AFTER_MTS_GAPS' => 1,
|
|||
|
|
'STATS_SAMPLE_PAGES' => 1,
|
|||
|
|
'REPLICATE_IGNORE_DB' => 1,
|
|||
|
|
'MASTER_AUTO_POSITION' => 1,
|
|||
|
|
'MASTER_CONNECT_RETRY' => 1,
|
|||
|
|
'MAX_QUERIES_PER_HOUR' => 1,
|
|||
|
|
'MAX_UPDATES_PER_HOUR' => 1,
|
|||
|
|
'MAX_USER_CONNECTIONS' => 1,
|
|||
|
|
'REPLICATE_REWRITE_DB' => 1,
|
|||
|
|
'REPLICATE_IGNORE_TABLE' => 1,
|
|||
|
|
'MASTER_HEARTBEAT_PERIOD' => 1,
|
|||
|
|
'REPLICATE_WILD_DO_TABLE' => 1,
|
|||
|
|
'MAX_CONNECTIONS_PER_HOUR' => 1,
|
|||
|
|
'REPLICATE_WILD_IGNORE_TABLE' => 1,
|
|||
|
|
|
|||
|
|
'AS' => 3,
|
|||
|
|
'BY' => 3,
|
|||
|
|
'IS' => 3,
|
|||
|
|
'ON' => 3,
|
|||
|
|
'OR' => 3,
|
|||
|
|
'TO' => 3,
|
|||
|
|
'ADD' => 3,
|
|||
|
|
'ALL' => 3,
|
|||
|
|
'AND' => 3,
|
|||
|
|
'ASC' => 3,
|
|||
|
|
'DEC' => 3,
|
|||
|
|
'DIV' => 3,
|
|||
|
|
'FOR' => 3,
|
|||
|
|
'GET' => 3,
|
|||
|
|
'NOT' => 3,
|
|||
|
|
'OUT' => 3,
|
|||
|
|
'SQL' => 3,
|
|||
|
|
'SSL' => 3,
|
|||
|
|
'USE' => 3,
|
|||
|
|
'XOR' => 3,
|
|||
|
|
'BOTH' => 3,
|
|||
|
|
'CALL' => 3,
|
|||
|
|
'CASE' => 3,
|
|||
|
|
'DESC' => 3,
|
|||
|
|
'DROP' => 3,
|
|||
|
|
'DUAL' => 3,
|
|||
|
|
'EACH' => 3,
|
|||
|
|
'ELSE' => 3,
|
|||
|
|
'EXIT' => 3,
|
|||
|
|
'FROM' => 3,
|
|||
|
|
'INT1' => 3,
|
|||
|
|
'INT2' => 3,
|
|||
|
|
'INT3' => 3,
|
|||
|
|
'INT4' => 3,
|
|||
|
|
'INT8' => 3,
|
|||
|
|
'INTO' => 3,
|
|||
|
|
'JOIN' => 3,
|
|||
|
|
'KEYS' => 3,
|
|||
|
|
'KILL' => 3,
|
|||
|
|
'LIKE' => 3,
|
|||
|
|
'LOAD' => 3,
|
|||
|
|
'LOCK' => 3,
|
|||
|
|
'LONG' => 3,
|
|||
|
|
'LOOP' => 3,
|
|||
|
|
'NULL' => 3,
|
|||
|
|
'OVER' => 3,
|
|||
|
|
'READ' => 3,
|
|||
|
|
'SHOW' => 3,
|
|||
|
|
'THEN' => 3,
|
|||
|
|
'TRUE' => 3,
|
|||
|
|
'UNDO' => 3,
|
|||
|
|
'WHEN' => 3,
|
|||
|
|
'WITH' => 3,
|
|||
|
|
'ALTER' => 3,
|
|||
|
|
'CHECK' => 3,
|
|||
|
|
'CROSS' => 3,
|
|||
|
|
'FALSE' => 3,
|
|||
|
|
'FETCH' => 3,
|
|||
|
|
'FORCE' => 3,
|
|||
|
|
'GRANT' => 3,
|
|||
|
|
'GROUP' => 3,
|
|||
|
|
'INNER' => 3,
|
|||
|
|
'INOUT' => 3,
|
|||
|
|
'LEAVE' => 3,
|
|||
|
|
'LIMIT' => 3,
|
|||
|
|
'LINES' => 3,
|
|||
|
|
'ORDER' => 3,
|
|||
|
|
'OUTER' => 3,
|
|||
|
|
'PURGE' => 3,
|
|||
|
|
'RANGE' => 3,
|
|||
|
|
'READS' => 3,
|
|||
|
|
'RLIKE' => 3,
|
|||
|
|
'TABLE' => 3,
|
|||
|
|
'UNION' => 3,
|
|||
|
|
'USAGE' => 3,
|
|||
|
|
'USING' => 3,
|
|||
|
|
'WHERE' => 3,
|
|||
|
|
'WHILE' => 3,
|
|||
|
|
'WRITE' => 3,
|
|||
|
|
'BEFORE' => 3,
|
|||
|
|
'CHANGE' => 3,
|
|||
|
|
'COLUMN' => 3,
|
|||
|
|
'CREATE' => 3,
|
|||
|
|
'CURSOR' => 3,
|
|||
|
|
'DELETE' => 3,
|
|||
|
|
'ELSEIF' => 3,
|
|||
|
|
'EXCEPT' => 3,
|
|||
|
|
'FLOAT4' => 3,
|
|||
|
|
'FLOAT8' => 3,
|
|||
|
|
'HAVING' => 3,
|
|||
|
|
'IGNORE' => 3,
|
|||
|
|
'INFILE' => 3,
|
|||
|
|
'LINEAR' => 3,
|
|||
|
|
'OPTION' => 3,
|
|||
|
|
'REGEXP' => 3,
|
|||
|
|
'RENAME' => 3,
|
|||
|
|
'RETURN' => 3,
|
|||
|
|
'REVOKE' => 3,
|
|||
|
|
'SELECT' => 3,
|
|||
|
|
'SIGNAL' => 3,
|
|||
|
|
'STORED' => 3,
|
|||
|
|
'UNLOCK' => 3,
|
|||
|
|
'UPDATE' => 3,
|
|||
|
|
'ANALYZE' => 3,
|
|||
|
|
'BETWEEN' => 3,
|
|||
|
|
'CASCADE' => 3,
|
|||
|
|
'COLLATE' => 3,
|
|||
|
|
'DECLARE' => 3,
|
|||
|
|
'DELAYED' => 3,
|
|||
|
|
'ESCAPED' => 3,
|
|||
|
|
'EXPLAIN' => 3,
|
|||
|
|
'FOREIGN' => 3,
|
|||
|
|
'ITERATE' => 3,
|
|||
|
|
'LEADING' => 3,
|
|||
|
|
'NATURAL' => 3,
|
|||
|
|
'OUTFILE' => 3,
|
|||
|
|
'PRIMARY' => 3,
|
|||
|
|
'RELEASE' => 3,
|
|||
|
|
'REQUIRE' => 3,
|
|||
|
|
'SCHEMAS' => 3,
|
|||
|
|
'TRIGGER' => 3,
|
|||
|
|
'VARYING' => 3,
|
|||
|
|
'VIRTUAL' => 3,
|
|||
|
|
'CONTINUE' => 3,
|
|||
|
|
'DAY_HOUR' => 3,
|
|||
|
|
'DESCRIBE' => 3,
|
|||
|
|
'DISTINCT' => 3,
|
|||
|
|
'ENCLOSED' => 3,
|
|||
|
|
'MAXVALUE' => 3,
|
|||
|
|
'MODIFIES' => 3,
|
|||
|
|
'OPTIMIZE' => 3,
|
|||
|
|
'RESIGNAL' => 3,
|
|||
|
|
'RESTRICT' => 3,
|
|||
|
|
'SPECIFIC' => 3,
|
|||
|
|
'SQLSTATE' => 3,
|
|||
|
|
'STARTING' => 3,
|
|||
|
|
'TRAILING' => 3,
|
|||
|
|
'UNSIGNED' => 3,
|
|||
|
|
'ZEROFILL' => 3,
|
|||
|
|
'CONDITION' => 3,
|
|||
|
|
'DATABASES' => 3,
|
|||
|
|
'GENERATED' => 3,
|
|||
|
|
'INTERSECT' => 3,
|
|||
|
|
'MIDDLEINT' => 3,
|
|||
|
|
'PARTITION' => 3,
|
|||
|
|
'PRECISION' => 3,
|
|||
|
|
'PROCEDURE' => 3,
|
|||
|
|
'RECURSIVE' => 3,
|
|||
|
|
'SENSITIVE' => 3,
|
|||
|
|
'SEPARATOR' => 3,
|
|||
|
|
'ACCESSIBLE' => 3,
|
|||
|
|
'ASENSITIVE' => 3,
|
|||
|
|
'CONSTRAINT' => 3,
|
|||
|
|
'DAY_MINUTE' => 3,
|
|||
|
|
'DAY_SECOND' => 3,
|
|||
|
|
'OPTIONALLY' => 3,
|
|||
|
|
'READ_WRITE' => 3,
|
|||
|
|
'REFERENCES' => 3,
|
|||
|
|
'SQLWARNING' => 3,
|
|||
|
|
'TERMINATED' => 3,
|
|||
|
|
'YEAR_MONTH' => 3,
|
|||
|
|
'DISTINCTROW' => 3,
|
|||
|
|
'HOUR_MINUTE' => 3,
|
|||
|
|
'HOUR_SECOND' => 3,
|
|||
|
|
'INSENSITIVE' => 3,
|
|||
|
|
'MASTER_BIND' => 3,
|
|||
|
|
'LOW_PRIORITY' => 3,
|
|||
|
|
'SQLEXCEPTION' => 3,
|
|||
|
|
'VARCHARACTER' => 3,
|
|||
|
|
'DETERMINISTIC' => 3,
|
|||
|
|
'HIGH_PRIORITY' => 3,
|
|||
|
|
'MINUTE_SECOND' => 3,
|
|||
|
|
'STRAIGHT_JOIN' => 3,
|
|||
|
|
'IO_AFTER_GTIDS' => 3,
|
|||
|
|
'SQL_BIG_RESULT' => 3,
|
|||
|
|
'DAY_MICROSECOND' => 3,
|
|||
|
|
'IO_BEFORE_GTIDS' => 3,
|
|||
|
|
'OPTIMIZER_COSTS' => 3,
|
|||
|
|
'HOUR_MICROSECOND' => 3,
|
|||
|
|
'SQL_SMALL_RESULT' => 3,
|
|||
|
|
'MINUTE_MICROSECOND' => 3,
|
|||
|
|
'NO_WRITE_TO_BINLOG' => 3,
|
|||
|
|
'SECOND_MICROSECOND' => 3,
|
|||
|
|
'SQL_CALC_FOUND_ROWS' => 3,
|
|||
|
|
'MASTER_SSL_VERIFY_SERVER_CERT' => 3,
|
|||
|
|
|
|||
|
|
'NO SQL' => 7,
|
|||
|
|
'GROUP BY' => 7,
|
|||
|
|
'NOT NULL' => 7,
|
|||
|
|
'ORDER BY' => 7,
|
|||
|
|
'SET NULL' => 7,
|
|||
|
|
'AND CHAIN' => 7,
|
|||
|
|
'FULL JOIN' => 7,
|
|||
|
|
'IF EXISTS' => 7,
|
|||
|
|
'LEFT JOIN' => 7,
|
|||
|
|
'LESS THAN' => 7,
|
|||
|
|
'LOAD DATA' => 7,
|
|||
|
|
'NO ACTION' => 7,
|
|||
|
|
'ON DELETE' => 7,
|
|||
|
|
'ON UPDATE' => 7,
|
|||
|
|
'UNION ALL' => 7,
|
|||
|
|
'CROSS JOIN' => 7,
|
|||
|
|
'ESCAPED BY' => 7,
|
|||
|
|
'FOR UPDATE' => 7,
|
|||
|
|
'INNER JOIN' => 7,
|
|||
|
|
'LINEAR KEY' => 7,
|
|||
|
|
'NO RELEASE' => 7,
|
|||
|
|
'OR REPLACE' => 7,
|
|||
|
|
'RIGHT JOIN' => 7,
|
|||
|
|
'ENCLOSED BY' => 7,
|
|||
|
|
'LINEAR HASH' => 7,
|
|||
|
|
'ON SCHEDULE' => 7,
|
|||
|
|
'STARTING BY' => 7,
|
|||
|
|
'AND NO CHAIN' => 7,
|
|||
|
|
'CONTAINS SQL' => 7,
|
|||
|
|
'FOR EACH ROW' => 7,
|
|||
|
|
'NATURAL JOIN' => 7,
|
|||
|
|
'PARTITION BY' => 7,
|
|||
|
|
'SET PASSWORD' => 7,
|
|||
|
|
'SQL SECURITY' => 7,
|
|||
|
|
'CHARACTER SET' => 7,
|
|||
|
|
'IF NOT EXISTS' => 7,
|
|||
|
|
'TERMINATED BY' => 7,
|
|||
|
|
'DATA DIRECTORY' => 7,
|
|||
|
|
'READS SQL DATA' => 7,
|
|||
|
|
'UNION DISTINCT' => 7,
|
|||
|
|
'DEFAULT CHARSET' => 7,
|
|||
|
|
'DEFAULT COLLATE' => 7,
|
|||
|
|
'FULL OUTER JOIN' => 7,
|
|||
|
|
'INDEX DIRECTORY' => 7,
|
|||
|
|
'LEFT OUTER JOIN' => 7,
|
|||
|
|
'SUBPARTITION BY' => 7,
|
|||
|
|
'DISABLE ON SLAVE' => 7,
|
|||
|
|
'GENERATED ALWAYS' => 7,
|
|||
|
|
'RIGHT OUTER JOIN' => 7,
|
|||
|
|
'MODIFIES SQL DATA' => 7,
|
|||
|
|
'NATURAL LEFT JOIN' => 7,
|
|||
|
|
'START TRANSACTION' => 7,
|
|||
|
|
'LOCK IN SHARE MODE' => 7,
|
|||
|
|
'NATURAL RIGHT JOIN' => 7,
|
|||
|
|
'SELECT TRANSACTION' => 7,
|
|||
|
|
'DEFAULT CHARACTER SET' => 7,
|
|||
|
|
'ON COMPLETION PRESERVE' => 7,
|
|||
|
|
'NATURAL LEFT OUTER JOIN' => 7,
|
|||
|
|
'NATURAL RIGHT OUTER JOIN' => 7,
|
|||
|
|
'WITH CONSISTENT SNAPSHOT' => 7,
|
|||
|
|
'ON COMPLETION NOT PRESERVE' => 7,
|
|||
|
|
|
|||
|
|
'BIT' => 9,
|
|||
|
|
'XML' => 9,
|
|||
|
|
'ENUM' => 9,
|
|||
|
|
'JSON' => 9,
|
|||
|
|
'TEXT' => 9,
|
|||
|
|
'ARRAY' => 9,
|
|||
|
|
'SERIAL' => 9,
|
|||
|
|
'BOOLEAN' => 9,
|
|||
|
|
'DATETIME' => 9,
|
|||
|
|
'GEOMETRY' => 9,
|
|||
|
|
'MULTISET' => 9,
|
|||
|
|
'MULTILINEPOINT' => 9,
|
|||
|
|
'MULTILINEPOLYGON' => 9,
|
|||
|
|
|
|||
|
|
'INT' => 11,
|
|||
|
|
'SET' => 11,
|
|||
|
|
'BLOB' => 11,
|
|||
|
|
'REAL' => 11,
|
|||
|
|
'FLOAT' => 11,
|
|||
|
|
'BIGINT' => 11,
|
|||
|
|
'DOUBLE' => 11,
|
|||
|
|
'DECIMAL' => 11,
|
|||
|
|
'INTEGER' => 11,
|
|||
|
|
'NUMERIC' => 11,
|
|||
|
|
'TINYINT' => 11,
|
|||
|
|
'VARCHAR' => 11,
|
|||
|
|
'LONGBLOB' => 11,
|
|||
|
|
'LONGTEXT' => 11,
|
|||
|
|
'SMALLINT' => 11,
|
|||
|
|
'TINYBLOB' => 11,
|
|||
|
|
'TINYTEXT' => 11,
|
|||
|
|
'CHARACTER' => 11,
|
|||
|
|
'MEDIUMINT' => 11,
|
|||
|
|
'VARBINARY' => 11,
|
|||
|
|
'MEDIUMBLOB' => 11,
|
|||
|
|
'MEDIUMTEXT' => 11,
|
|||
|
|
|
|||
|
|
'BINARY VARYING' => 15,
|
|||
|
|
|
|||
|
|
'KEY' => 19,
|
|||
|
|
'INDEX' => 19,
|
|||
|
|
'UNIQUE' => 19,
|
|||
|
|
'SPATIAL' => 19,
|
|||
|
|
'FULLTEXT' => 19,
|
|||
|
|
|
|||
|
|
'INDEX KEY' => 23,
|
|||
|
|
'UNIQUE KEY' => 23,
|
|||
|
|
'FOREIGN KEY' => 23,
|
|||
|
|
'PRIMARY KEY' => 23,
|
|||
|
|
'SPATIAL KEY' => 23,
|
|||
|
|
'FULLTEXT KEY' => 23,
|
|||
|
|
'UNIQUE INDEX' => 23,
|
|||
|
|
'SPATIAL INDEX' => 23,
|
|||
|
|
'FULLTEXT INDEX' => 23,
|
|||
|
|
|
|||
|
|
'X' => 33,
|
|||
|
|
'Y' => 33,
|
|||
|
|
'LN' => 33,
|
|||
|
|
'PI' => 33,
|
|||
|
|
'ABS' => 33,
|
|||
|
|
'AVG' => 33,
|
|||
|
|
'BIN' => 33,
|
|||
|
|
'COS' => 33,
|
|||
|
|
'COT' => 33,
|
|||
|
|
'DAY' => 33,
|
|||
|
|
'ELT' => 33,
|
|||
|
|
'EXP' => 33,
|
|||
|
|
'HEX' => 33,
|
|||
|
|
'LOG' => 33,
|
|||
|
|
'MAX' => 33,
|
|||
|
|
'MD5' => 33,
|
|||
|
|
'MID' => 33,
|
|||
|
|
'MIN' => 33,
|
|||
|
|
'NOW' => 33,
|
|||
|
|
'OCT' => 33,
|
|||
|
|
'ORD' => 33,
|
|||
|
|
'POW' => 33,
|
|||
|
|
'SHA' => 33,
|
|||
|
|
'SIN' => 33,
|
|||
|
|
'STD' => 33,
|
|||
|
|
'SUM' => 33,
|
|||
|
|
'TAN' => 33,
|
|||
|
|
'ACOS' => 33,
|
|||
|
|
'AREA' => 33,
|
|||
|
|
'ASIN' => 33,
|
|||
|
|
'ATAN' => 33,
|
|||
|
|
'CAST' => 33,
|
|||
|
|
'CEIL' => 33,
|
|||
|
|
'CONV' => 33,
|
|||
|
|
'HOUR' => 33,
|
|||
|
|
'LOG2' => 33,
|
|||
|
|
'LPAD' => 33,
|
|||
|
|
'RAND' => 33,
|
|||
|
|
'RPAD' => 33,
|
|||
|
|
'SHA1' => 33,
|
|||
|
|
'SHA2' => 33,
|
|||
|
|
'SIGN' => 33,
|
|||
|
|
'SQRT' => 33,
|
|||
|
|
'SRID' => 33,
|
|||
|
|
'ST_X' => 33,
|
|||
|
|
'ST_Y' => 33,
|
|||
|
|
'TRIM' => 33,
|
|||
|
|
'USER' => 33,
|
|||
|
|
'UUID' => 33,
|
|||
|
|
'WEEK' => 33,
|
|||
|
|
'ASCII' => 33,
|
|||
|
|
'ASWKB' => 33,
|
|||
|
|
'ASWKT' => 33,
|
|||
|
|
'ATAN2' => 33,
|
|||
|
|
'COUNT' => 33,
|
|||
|
|
'CRC32' => 33,
|
|||
|
|
'FIELD' => 33,
|
|||
|
|
'FLOOR' => 33,
|
|||
|
|
'INSTR' => 33,
|
|||
|
|
'LCASE' => 33,
|
|||
|
|
'LEAST' => 33,
|
|||
|
|
'LOG10' => 33,
|
|||
|
|
'LOWER' => 33,
|
|||
|
|
'LTRIM' => 33,
|
|||
|
|
'MONTH' => 33,
|
|||
|
|
'POWER' => 33,
|
|||
|
|
'QUOTE' => 33,
|
|||
|
|
'ROUND' => 33,
|
|||
|
|
'RTRIM' => 33,
|
|||
|
|
'SLEEP' => 33,
|
|||
|
|
'SPACE' => 33,
|
|||
|
|
'UCASE' => 33,
|
|||
|
|
'UNHEX' => 33,
|
|||
|
|
'UPPER' => 33,
|
|||
|
|
'ASTEXT' => 33,
|
|||
|
|
'BIT_OR' => 33,
|
|||
|
|
'BUFFER' => 33,
|
|||
|
|
'CONCAT' => 33,
|
|||
|
|
'DECODE' => 33,
|
|||
|
|
'ENCODE' => 33,
|
|||
|
|
'EQUALS' => 33,
|
|||
|
|
'FORMAT' => 33,
|
|||
|
|
'IFNULL' => 33,
|
|||
|
|
'ISNULL' => 33,
|
|||
|
|
'LENGTH' => 33,
|
|||
|
|
'LOCATE' => 33,
|
|||
|
|
'MINUTE' => 33,
|
|||
|
|
'NULLIF' => 33,
|
|||
|
|
'POINTN' => 33,
|
|||
|
|
'SECOND' => 33,
|
|||
|
|
'STDDEV' => 33,
|
|||
|
|
'STRCMP' => 33,
|
|||
|
|
'SUBSTR' => 33,
|
|||
|
|
'WITHIN' => 33,
|
|||
|
|
'ADDDATE' => 33,
|
|||
|
|
'ADDTIME' => 33,
|
|||
|
|
'AGAINST' => 33,
|
|||
|
|
'BIT_AND' => 33,
|
|||
|
|
'BIT_XOR' => 33,
|
|||
|
|
'CEILING' => 33,
|
|||
|
|
'CHARSET' => 33,
|
|||
|
|
'CROSSES' => 33,
|
|||
|
|
'CURDATE' => 33,
|
|||
|
|
'CURTIME' => 33,
|
|||
|
|
'DAYNAME' => 33,
|
|||
|
|
'DEGREES' => 33,
|
|||
|
|
'ENCRYPT' => 33,
|
|||
|
|
'EXTRACT' => 33,
|
|||
|
|
'GLENGTH' => 33,
|
|||
|
|
'ISEMPTY' => 33,
|
|||
|
|
'IS_IPV4' => 33,
|
|||
|
|
'IS_IPV6' => 33,
|
|||
|
|
'IS_UUID' => 33,
|
|||
|
|
'QUARTER' => 33,
|
|||
|
|
'RADIANS' => 33,
|
|||
|
|
'REVERSE' => 33,
|
|||
|
|
'SOUNDEX' => 33,
|
|||
|
|
'ST_AREA' => 33,
|
|||
|
|
'ST_SRID' => 33,
|
|||
|
|
'SUBDATE' => 33,
|
|||
|
|
'SUBTIME' => 33,
|
|||
|
|
'SYSDATE' => 33,
|
|||
|
|
'TOUCHES' => 33,
|
|||
|
|
'TO_DAYS' => 33,
|
|||
|
|
'VAR_POP' => 33,
|
|||
|
|
'VERSION' => 33,
|
|||
|
|
'WEEKDAY' => 33,
|
|||
|
|
'ASBINARY' => 33,
|
|||
|
|
'CENTROID' => 33,
|
|||
|
|
'COALESCE' => 33,
|
|||
|
|
'COMPRESS' => 33,
|
|||
|
|
'CONTAINS' => 33,
|
|||
|
|
'DATEDIFF' => 33,
|
|||
|
|
'DATE_ADD' => 33,
|
|||
|
|
'DATE_SUB' => 33,
|
|||
|
|
'DISJOINT' => 33,
|
|||
|
|
'DISTANCE' => 33,
|
|||
|
|
'ENDPOINT' => 33,
|
|||
|
|
'ENVELOPE' => 33,
|
|||
|
|
'GET_LOCK' => 33,
|
|||
|
|
'GREATEST' => 33,
|
|||
|
|
'ISCLOSED' => 33,
|
|||
|
|
'ISSIMPLE' => 33,
|
|||
|
|
'JSON_SET' => 33,
|
|||
|
|
'MAKEDATE' => 33,
|
|||
|
|
'MAKETIME' => 33,
|
|||
|
|
'MAKE_SET' => 33,
|
|||
|
|
'MBREQUAL' => 33,
|
|||
|
|
'OVERLAPS' => 33,
|
|||
|
|
'PASSWORD' => 33,
|
|||
|
|
'POSITION' => 33,
|
|||
|
|
'ST_ASWKB' => 33,
|
|||
|
|
'ST_ASWKT' => 33,
|
|||
|
|
'ST_UNION' => 33,
|
|||
|
|
'TIMEDIFF' => 33,
|
|||
|
|
'TRUNCATE' => 33,
|
|||
|
|
'VARIANCE' => 33,
|
|||
|
|
'VAR_SAMP' => 33,
|
|||
|
|
'YEARWEEK' => 33,
|
|||
|
|
'ANY_VALUE' => 33,
|
|||
|
|
'BENCHMARK' => 33,
|
|||
|
|
'BIT_COUNT' => 33,
|
|||
|
|
'COLLATION' => 33,
|
|||
|
|
'CONCAT_WS' => 33,
|
|||
|
|
'DAYOFWEEK' => 33,
|
|||
|
|
'DAYOFYEAR' => 33,
|
|||
|
|
'DIMENSION' => 33,
|
|||
|
|
'FROM_DAYS' => 33,
|
|||
|
|
'GEOMETRYN' => 33,
|
|||
|
|
'INET_ATON' => 33,
|
|||
|
|
'INET_NTOA' => 33,
|
|||
|
|
'JSON_KEYS' => 33,
|
|||
|
|
'JSON_TYPE' => 33,
|
|||
|
|
'LOAD_FILE' => 33,
|
|||
|
|
'MBRCOVERS' => 33,
|
|||
|
|
'MBREQUALS' => 33,
|
|||
|
|
'MBRWITHIN' => 33,
|
|||
|
|
'MONTHNAME' => 33,
|
|||
|
|
'NUMPOINTS' => 33,
|
|||
|
|
'ROW_COUNT' => 33,
|
|||
|
|
'ST_ASTEXT' => 33,
|
|||
|
|
'ST_BUFFER' => 33,
|
|||
|
|
'ST_EQUALS' => 33,
|
|||
|
|
'ST_LENGTH' => 33,
|
|||
|
|
'ST_POINTN' => 33,
|
|||
|
|
'ST_WITHIN' => 33,
|
|||
|
|
'SUBSTRING' => 33,
|
|||
|
|
'TO_BASE64' => 33,
|
|||
|
|
'UPDATEXML' => 33,
|
|||
|
|
'BIT_LENGTH' => 33,
|
|||
|
|
'CONVERT_TZ' => 33,
|
|||
|
|
'CONVEXHULL' => 33,
|
|||
|
|
'DAYOFMONTH' => 33,
|
|||
|
|
'EXPORT_SET' => 33,
|
|||
|
|
'FOUND_ROWS' => 33,
|
|||
|
|
'GET_FORMAT' => 33,
|
|||
|
|
'INET6_ATON' => 33,
|
|||
|
|
'INET6_NTOA' => 33,
|
|||
|
|
'INTERSECTS' => 33,
|
|||
|
|
'JSON_ARRAY' => 33,
|
|||
|
|
'JSON_DEPTH' => 33,
|
|||
|
|
'JSON_MERGE' => 33,
|
|||
|
|
'JSON_QUOTE' => 33,
|
|||
|
|
'JSON_VALID' => 33,
|
|||
|
|
'MBRTOUCHES' => 33,
|
|||
|
|
'NAME_CONST' => 33,
|
|||
|
|
'PERIOD_ADD' => 33,
|
|||
|
|
'STARTPOINT' => 33,
|
|||
|
|
'STDDEV_POP' => 33,
|
|||
|
|
'ST_CROSSES' => 33,
|
|||
|
|
'ST_GEOHASH' => 33,
|
|||
|
|
'ST_ISEMPTY' => 33,
|
|||
|
|
'ST_ISVALID' => 33,
|
|||
|
|
'ST_TOUCHES' => 33,
|
|||
|
|
'TO_SECONDS' => 33,
|
|||
|
|
'UNCOMPRESS' => 33,
|
|||
|
|
'UUID_SHORT' => 33,
|
|||
|
|
'WEEKOFYEAR' => 33,
|
|||
|
|
'AES_DECRYPT' => 33,
|
|||
|
|
'AES_ENCRYPT' => 33,
|
|||
|
|
'BIN_TO_UUID' => 33,
|
|||
|
|
'CHAR_LENGTH' => 33,
|
|||
|
|
'DATE_FORMAT' => 33,
|
|||
|
|
'DES_DECRYPT' => 33,
|
|||
|
|
'DES_ENCRYPT' => 33,
|
|||
|
|
'FIND_IN_SET' => 33,
|
|||
|
|
'FROM_BASE64' => 33,
|
|||
|
|
'GEOMFROMWKB' => 33,
|
|||
|
|
'GTID_SUBSET' => 33,
|
|||
|
|
'JSON_INSERT' => 33,
|
|||
|
|
'JSON_LENGTH' => 33,
|
|||
|
|
'JSON_OBJECT' => 33,
|
|||
|
|
'JSON_PRETTY' => 33,
|
|||
|
|
'JSON_REMOVE' => 33,
|
|||
|
|
'JSON_SEARCH' => 33,
|
|||
|
|
'LINEFROMWKB' => 33,
|
|||
|
|
'MBRCONTAINS' => 33,
|
|||
|
|
'MBRDISJOINT' => 33,
|
|||
|
|
'MBROVERLAPS' => 33,
|
|||
|
|
'MICROSECOND' => 33,
|
|||
|
|
'PERIOD_DIFF' => 33,
|
|||
|
|
'POLYFROMWKB' => 33,
|
|||
|
|
'SEC_TO_TIME' => 33,
|
|||
|
|
'STDDEV_SAMP' => 33,
|
|||
|
|
'STR_TO_DATE' => 33,
|
|||
|
|
'ST_ASBINARY' => 33,
|
|||
|
|
'ST_CENTROID' => 33,
|
|||
|
|
'ST_CONTAINS' => 33,
|
|||
|
|
'ST_DISJOINT' => 33,
|
|||
|
|
'ST_DISTANCE' => 33,
|
|||
|
|
'ST_ENDPOINT' => 33,
|
|||
|
|
'ST_ENVELOPE' => 33,
|
|||
|
|
'ST_ISCLOSED' => 33,
|
|||
|
|
'ST_ISSIMPLE' => 33,
|
|||
|
|
'ST_OVERLAPS' => 33,
|
|||
|
|
'ST_SIMPLIFY' => 33,
|
|||
|
|
'ST_VALIDATE' => 33,
|
|||
|
|
'SYSTEM_USER' => 33,
|
|||
|
|
'TIME_FORMAT' => 33,
|
|||
|
|
'TIME_TO_SEC' => 33,
|
|||
|
|
'UUID_TO_BIN' => 33,
|
|||
|
|
'COERCIBILITY' => 33,
|
|||
|
|
'EXTERIORRING' => 33,
|
|||
|
|
'EXTRACTVALUE' => 33,
|
|||
|
|
'GEOMETRYTYPE' => 33,
|
|||
|
|
'GEOMFROMTEXT' => 33,
|
|||
|
|
'GROUP_CONCAT' => 33,
|
|||
|
|
'IS_FREE_LOCK' => 33,
|
|||
|
|
'IS_USED_LOCK' => 33,
|
|||
|
|
'JSON_EXTRACT' => 33,
|
|||
|
|
'JSON_REPLACE' => 33,
|
|||
|
|
'JSON_UNQUOTE' => 33,
|
|||
|
|
'LINEFROMTEXT' => 33,
|
|||
|
|
'MBRCOVEREDBY' => 33,
|
|||
|
|
'MLINEFROMWKB' => 33,
|
|||
|
|
'MPOLYFROMWKB' => 33,
|
|||
|
|
'OCTET_LENGTH' => 33,
|
|||
|
|
'OLD_PASSWORD' => 33,
|
|||
|
|
'POINTFROMWKB' => 33,
|
|||
|
|
'POLYFROMTEXT' => 33,
|
|||
|
|
'RANDOM_BYTES' => 33,
|
|||
|
|
'RELEASE_LOCK' => 33,
|
|||
|
|
'SESSION_USER' => 33,
|
|||
|
|
'ST_ASGEOJSON' => 33,
|
|||
|
|
'ST_DIMENSION' => 33,
|
|||
|
|
'ST_GEOMETRYN' => 33,
|
|||
|
|
'ST_NUMPOINTS' => 33,
|
|||
|
|
'TIMESTAMPADD' => 33,
|
|||
|
|
'CONNECTION_ID' => 33,
|
|||
|
|
'FROM_UNIXTIME' => 33,
|
|||
|
|
'GTID_SUBTRACT' => 33,
|
|||
|
|
'INTERIORRINGN' => 33,
|
|||
|
|
'JSON_CONTAINS' => 33,
|
|||
|
|
'MBRINTERSECTS' => 33,
|
|||
|
|
'MLINEFROMTEXT' => 33,
|
|||
|
|
'MPOINTFROMWKB' => 33,
|
|||
|
|
'MPOLYFROMTEXT' => 33,
|
|||
|
|
'NUMGEOMETRIES' => 33,
|
|||
|
|
'POINTFROMTEXT' => 33,
|
|||
|
|
'ST_CONVEXHULL' => 33,
|
|||
|
|
'ST_DIFFERENCE' => 33,
|
|||
|
|
'ST_INTERSECTS' => 33,
|
|||
|
|
'ST_STARTPOINT' => 33,
|
|||
|
|
'TIMESTAMPDIFF' => 33,
|
|||
|
|
'WEIGHT_STRING' => 33,
|
|||
|
|
'IS_IPV4_COMPAT' => 33,
|
|||
|
|
'IS_IPV4_MAPPED' => 33,
|
|||
|
|
'LAST_INSERT_ID' => 33,
|
|||
|
|
'MPOINTFROMTEXT' => 33,
|
|||
|
|
'POLYGONFROMWKB' => 33,
|
|||
|
|
'ST_GEOMFROMWKB' => 33,
|
|||
|
|
'ST_LINEFROMWKB' => 33,
|
|||
|
|
'ST_POLYFROMWKB' => 33,
|
|||
|
|
'UNIX_TIMESTAMP' => 33,
|
|||
|
|
'GEOMCOLLFROMWKB' => 33,
|
|||
|
|
'MASTER_POS_WAIT' => 33,
|
|||
|
|
'POLYGONFROMTEXT' => 33,
|
|||
|
|
'ST_EXTERIORRING' => 33,
|
|||
|
|
'ST_GEOMETRYTYPE' => 33,
|
|||
|
|
'ST_GEOMFROMTEXT' => 33,
|
|||
|
|
'ST_INTERSECTION' => 33,
|
|||
|
|
'ST_LINEFROMTEXT' => 33,
|
|||
|
|
'ST_MAKEENVELOPE' => 33,
|
|||
|
|
'ST_MLINEFROMWKB' => 33,
|
|||
|
|
'ST_MPOLYFROMWKB' => 33,
|
|||
|
|
'ST_POINTFROMWKB' => 33,
|
|||
|
|
'ST_POLYFROMTEXT' => 33,
|
|||
|
|
'SUBSTRING_INDEX' => 33,
|
|||
|
|
'CHARACTER_LENGTH' => 33,
|
|||
|
|
'GEOMCOLLFROMTEXT' => 33,
|
|||
|
|
'GEOMETRYFROMTEXT' => 33,
|
|||
|
|
'JSON_MERGE_PATCH' => 33,
|
|||
|
|
'NUMINTERIORRINGS' => 33,
|
|||
|
|
'ST_INTERIORRINGN' => 33,
|
|||
|
|
'ST_MLINEFROMTEXT' => 33,
|
|||
|
|
'ST_MPOINTFROMWKB' => 33,
|
|||
|
|
'ST_MPOLYFROMTEXT' => 33,
|
|||
|
|
'ST_NUMGEOMETRIES' => 33,
|
|||
|
|
'ST_POINTFROMTEXT' => 33,
|
|||
|
|
'ST_SYMDIFFERENCE' => 33,
|
|||
|
|
'JSON_ARRAY_APPEND' => 33,
|
|||
|
|
'JSON_ARRAY_INSERT' => 33,
|
|||
|
|
'JSON_STORAGE_FREE' => 33,
|
|||
|
|
'JSON_STORAGE_SIZE' => 33,
|
|||
|
|
'LINESTRINGFROMWKB' => 33,
|
|||
|
|
'MULTIPOINTFROMWKB' => 33,
|
|||
|
|
'RELEASE_ALL_LOCKS' => 33,
|
|||
|
|
'ST_LATFROMGEOHASH' => 33,
|
|||
|
|
'ST_MPOINTFROMTEXT' => 33,
|
|||
|
|
'ST_POLYGONFROMWKB' => 33,
|
|||
|
|
'JSON_CONTAINS_PATH' => 33,
|
|||
|
|
'MULTIPOINTFROMTEXT' => 33,
|
|||
|
|
'ST_BUFFER_STRATEGY' => 33,
|
|||
|
|
'ST_DISTANCE_SPHERE' => 33,
|
|||
|
|
'ST_GEOMCOLLFROMTXT' => 33,
|
|||
|
|
'ST_GEOMCOLLFROMWKB' => 33,
|
|||
|
|
'ST_GEOMFROMGEOJSON' => 33,
|
|||
|
|
'ST_LONGFROMGEOHASH' => 33,
|
|||
|
|
'ST_POLYGONFROMTEXT' => 33,
|
|||
|
|
'JSON_MERGE_PRESERVE' => 33,
|
|||
|
|
'MULTIPOLYGONFROMWKB' => 33,
|
|||
|
|
'ST_GEOMCOLLFROMTEXT' => 33,
|
|||
|
|
'ST_GEOMETRYFROMTEXT' => 33,
|
|||
|
|
'ST_NUMINTERIORRINGS' => 33,
|
|||
|
|
'ST_POINTFROMGEOHASH' => 33,
|
|||
|
|
'UNCOMPRESSED_LENGTH' => 33,
|
|||
|
|
'MULTIPOLYGONFROMTEXT' => 33,
|
|||
|
|
'ST_LINESTRINGFROMWKB' => 33,
|
|||
|
|
'ST_MULTIPOINTFROMWKB' => 33,
|
|||
|
|
'ST_MULTIPOINTFROMTEXT' => 33,
|
|||
|
|
'MULTILINESTRINGFROMWKB' => 33,
|
|||
|
|
'ST_MULTIPOLYGONFROMWKB' => 33,
|
|||
|
|
'MULTILINESTRINGFROMTEXT' => 33,
|
|||
|
|
'ST_MULTIPOLYGONFROMTEXT' => 33,
|
|||
|
|
'GEOMETRYCOLLECTIONFROMWKB' => 33,
|
|||
|
|
'ST_MULTILINESTRINGFROMWKB' => 33,
|
|||
|
|
'GEOMETRYCOLLECTIONFROMTEXT' => 33,
|
|||
|
|
'ST_MULTILINESTRINGFROMTEXT' => 33,
|
|||
|
|
'VALIDATE_PASSWORD_STRENGTH' => 33,
|
|||
|
|
'WAIT_FOR_EXECUTED_GTID_SET' => 33,
|
|||
|
|
'ST_GEOMETRYCOLLECTIONFROMWKB' => 33,
|
|||
|
|
'ST_GEOMETRYCOLLECTIONFROMTEXT' => 33,
|
|||
|
|
'WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS' => 33,
|
|||
|
|
|
|||
|
|
'IF' => 35,
|
|||
|
|
'IN' => 35,
|
|||
|
|
'MOD' => 35,
|
|||
|
|
'LEFT' => 35,
|
|||
|
|
'MATCH' => 35,
|
|||
|
|
'RIGHT' => 35,
|
|||
|
|
'EXISTS' => 35,
|
|||
|
|
'INSERT' => 35,
|
|||
|
|
'REPEAT' => 35,
|
|||
|
|
'SCHEMA' => 35,
|
|||
|
|
'VALUES' => 35,
|
|||
|
|
'CONVERT' => 35,
|
|||
|
|
'DEFAULT' => 35,
|
|||
|
|
'REPLACE' => 35,
|
|||
|
|
'DATABASE' => 35,
|
|||
|
|
'UTC_DATE' => 35,
|
|||
|
|
'UTC_TIME' => 35,
|
|||
|
|
'LOCALTIME' => 35,
|
|||
|
|
'CURRENT_DATE' => 35,
|
|||
|
|
'CURRENT_TIME' => 35,
|
|||
|
|
'CURRENT_USER' => 35,
|
|||
|
|
'UTC_TIMESTAMP' => 35,
|
|||
|
|
'LOCALTIMESTAMP' => 35,
|
|||
|
|
'CURRENT_TIMESTAMP' => 35,
|
|||
|
|
|
|||
|
|
'NOT IN' => 39,
|
|||
|
|
|
|||
|
|
'DATE' => 41,
|
|||
|
|
'TIME' => 41,
|
|||
|
|
'YEAR' => 41,
|
|||
|
|
'POINT' => 41,
|
|||
|
|
'POLYGON' => 41,
|
|||
|
|
'TIMESTAMP' => 41,
|
|||
|
|
'LINESTRING' => 41,
|
|||
|
|
'MULTIPOINT' => 41,
|
|||
|
|
'MULTIPOLYGON' => 41,
|
|||
|
|
'MULTILINESTRING' => 41,
|
|||
|
|
'GEOMETRYCOLLECTION' => 41,
|
|||
|
|
|
|||
|
|
'CHAR' => 43,
|
|||
|
|
'BINARY' => 43,
|
|||
|
|
'INTERVAL' => 43,
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* All data type options.
|
|||
|
|
*
|
|||
|
|
* @var array<string, int|array<int, int|string>>
|
|||
|
|
*/
|
|||
|
|
public static $data_type_options = array(
|
|||
|
|
'BINARY' => 1,
|
|||
|
|
'CHARACTER SET' => array(
|
|||
|
|
2,
|
|||
|
|
'var',
|
|||
|
|
),
|
|||
|
|
'CHARSET' => array(
|
|||
|
|
2,
|
|||
|
|
'var',
|
|||
|
|
),
|
|||
|
|
'COLLATE' => array(
|
|||
|
|
3,
|
|||
|
|
'var',
|
|||
|
|
),
|
|||
|
|
'UNSIGNED' => 4,
|
|||
|
|
'ZEROFILL' => 5,
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* All field options.
|
|||
|
|
*
|
|||
|
|
* @var array<string, bool|int|array<int, int|string|array<string, bool>>>
|
|||
|
|
*/
|
|||
|
|
public static $field_options = array(
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
* Tells the `OptionsArray` to not sort the options.
|
|||
|
|
* See the note below.
|
|||
|
|
*/
|
|||
|
|
'_UNSORTED' => true,
|
|||
|
|
|
|||
|
|
'NOT NULL' => 1,
|
|||
|
|
'NULL' => 1,
|
|||
|
|
'DEFAULT' => array(
|
|||
|
|
2,
|
|||
|
|
'expr',
|
|||
|
|
array( 'breakOnAlias' => true ),
|
|||
|
|
),
|
|||
|
|
|
|||
|
|
// Following are not according to grammar, but MySQL happily accepts these at any location.
|
|||
|
|
'CHARSET' => array(
|
|||
|
|
2,
|
|||
|
|
'var',
|
|||
|
|
),
|
|||
|
|
'COLLATE' => array(
|
|||
|
|
3,
|
|||
|
|
'var',
|
|||
|
|
),
|
|||
|
|
'AUTO_INCREMENT' => 3,
|
|||
|
|
'PRIMARY' => 4,
|
|||
|
|
'PRIMARY KEY' => 4,
|
|||
|
|
'UNIQUE' => 4,
|
|||
|
|
'UNIQUE KEY' => 4,
|
|||
|
|
'COMMENT' => array(
|
|||
|
|
5,
|
|||
|
|
'var',
|
|||
|
|
),
|
|||
|
|
'COLUMN_FORMAT' => array(
|
|||
|
|
6,
|
|||
|
|
'var',
|
|||
|
|
),
|
|||
|
|
'ON UPDATE' => array(
|
|||
|
|
7,
|
|||
|
|
'expr',
|
|||
|
|
),
|
|||
|
|
|
|||
|
|
// Generated columns options.
|
|||
|
|
'GENERATED ALWAYS' => 8,
|
|||
|
|
'AS' => array(
|
|||
|
|
9,
|
|||
|
|
'expr',
|
|||
|
|
array( 'parenthesesDelimited' => true ),
|
|||
|
|
),
|
|||
|
|
'VIRTUAL' => 10,
|
|||
|
|
'PERSISTENT' => 11,
|
|||
|
|
'STORED' => 11,
|
|||
|
|
'CHECK' => array(
|
|||
|
|
12,
|
|||
|
|
'expr',
|
|||
|
|
array( 'parenthesesDelimited' => true ),
|
|||
|
|
),
|
|||
|
|
'INVISIBLE' => 13,
|
|||
|
|
'ENFORCED' => 14,
|
|||
|
|
'NOT' => 15,
|
|||
|
|
'COMPRESSED' => 16,
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
* Common entries.
|
|||
|
|
*
|
|||
|
|
* NOTE: Some of the common options are not in the same order which
|
|||
|
|
* causes troubles when checking if the options are in the right order.
|
|||
|
|
* I should find a way to define multiple sets of options and make the
|
|||
|
|
* parser select the right set.
|
|||
|
|
*
|
|||
|
|
* 'UNIQUE' => 4,
|
|||
|
|
* 'UNIQUE KEY' => 4,
|
|||
|
|
* 'COMMENT' => [5, 'var'],
|
|||
|
|
* 'NOT NULL' => 1,
|
|||
|
|
* 'NULL' => 1,
|
|||
|
|
* 'PRIMARY' => 4,
|
|||
|
|
* 'PRIMARY KEY' => 4,
|
|||
|
|
*/
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Quotes mode.
|
|||
|
|
*
|
|||
|
|
* @link https://dev.mysql.com/doc/refman/en/sql-mode.html#sqlmode_ansi_quotes
|
|||
|
|
* @link https://mariadb.com/kb/en/sql-mode/#ansi_quotes
|
|||
|
|
*/
|
|||
|
|
const SQL_MODE_ANSI_QUOTES = 2;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* The array of tokens.
|
|||
|
|
*
|
|||
|
|
* @var stdClass[]
|
|||
|
|
*/
|
|||
|
|
public $tokens = array();
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* The count of tokens.
|
|||
|
|
*
|
|||
|
|
* @var int
|
|||
|
|
*/
|
|||
|
|
public $tokens_count = 0;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* The index of the next token to be returned.
|
|||
|
|
*
|
|||
|
|
* @var int
|
|||
|
|
*/
|
|||
|
|
public $tokens_index = 0;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* The object constructor.
|
|||
|
|
*
|
|||
|
|
* @param string $str The query to be lexed.
|
|||
|
|
* @param string $delimiter The delimiter to be used.
|
|||
|
|
*/
|
|||
|
|
public function __construct( $str, $delimiter = null ) {
|
|||
|
|
$this->str = $str;
|
|||
|
|
// `strlen` is used instead of `mb_strlen` because the lexer needs to parse each byte of the input.
|
|||
|
|
$this->string_length = strlen( $str );
|
|||
|
|
|
|||
|
|
// Setting the delimiter.
|
|||
|
|
$this->set_delimiter( ! empty( $delimiter ) ? $delimiter : static::$default_delimiter );
|
|||
|
|
|
|||
|
|
$this->lex();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Sets the delimiter.
|
|||
|
|
*
|
|||
|
|
* @param string $delimiter The new delimiter.
|
|||
|
|
*
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function set_delimiter( $delimiter ) {
|
|||
|
|
$this->delimiter = $delimiter;
|
|||
|
|
$this->delimiter_length = strlen( $delimiter );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Parses the string and extracts lexemes.
|
|||
|
|
*
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function lex() {
|
|||
|
|
/*
|
|||
|
|
* TODO: Sometimes, static::parse* functions make unnecessary calls to
|
|||
|
|
* is* functions. For a better performance, some rules can be deduced
|
|||
|
|
* from context.
|
|||
|
|
* For example, in `parse_bool` there is no need to compare the token
|
|||
|
|
* every time with `true` and `false`. The first step would be to
|
|||
|
|
* compare with 'true' only and just after that add another letter from
|
|||
|
|
* context and compare again with `false`.
|
|||
|
|
* Another example is `parse_comment`.
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Last processed token.
|
|||
|
|
*
|
|||
|
|
* @var WP_SQLite_Token
|
|||
|
|
*/
|
|||
|
|
$last_token = null;
|
|||
|
|
|
|||
|
|
for ( $this->last = 0, $last_idx = 0; $this->last < $this->string_length; $last_idx = ++$this->last ) {
|
|||
|
|
/**
|
|||
|
|
* The new token.
|
|||
|
|
*
|
|||
|
|
* @var WP_SQLite_Token
|
|||
|
|
*/
|
|||
|
|
$token = null;
|
|||
|
|
|
|||
|
|
foreach ( self::PARSER_METHODS as $method ) {
|
|||
|
|
$token = $this->$method();
|
|||
|
|
|
|||
|
|
if ( $token ) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( null === $token ) {
|
|||
|
|
$token = new WP_SQLite_Token( $this->str[ $this->last ] );
|
|||
|
|
$this->error( 'Unexpected character.', $this->str[ $this->last ], $this->last );
|
|||
|
|
} elseif (
|
|||
|
|
null !== $last_token
|
|||
|
|
&& WP_SQLite_Token::TYPE_SYMBOL === $token->type
|
|||
|
|
&& $token->flags & WP_SQLite_Token::FLAG_SYMBOL_VARIABLE
|
|||
|
|
&& (
|
|||
|
|
WP_SQLite_Token::TYPE_STRING === $last_token->type
|
|||
|
|
|| (
|
|||
|
|
WP_SQLite_Token::TYPE_SYMBOL === $last_token->type
|
|||
|
|
&& $last_token->flags & WP_SQLite_Token::FLAG_SYMBOL_BACKTICK
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
) {
|
|||
|
|
// Handles ```... FROM 'user'@'%' ...```.
|
|||
|
|
$last_token->token .= $token->token;
|
|||
|
|
$last_token->type = WP_SQLite_Token::TYPE_SYMBOL;
|
|||
|
|
$last_token->flags = WP_SQLite_Token::FLAG_SYMBOL_USER;
|
|||
|
|
$last_token->value .= '@' . $token->value;
|
|||
|
|
continue;
|
|||
|
|
} elseif (
|
|||
|
|
null !== $last_token
|
|||
|
|
&& WP_SQLite_Token::TYPE_KEYWORD === $token->type
|
|||
|
|
&& WP_SQLite_Token::TYPE_OPERATOR === $last_token->type
|
|||
|
|
&& '.' === $last_token->value
|
|||
|
|
) {
|
|||
|
|
// Handles ```... tbl.FROM ...```. In this case, FROM is not a reserved word.
|
|||
|
|
$token->type = WP_SQLite_Token::TYPE_NONE;
|
|||
|
|
$token->flags = 0;
|
|||
|
|
$token->value = $token->token;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$token->position = $last_idx;
|
|||
|
|
|
|||
|
|
$this->tokens[ $this->tokens_count++ ] = $token;
|
|||
|
|
|
|||
|
|
// Handling delimiters.
|
|||
|
|
if ( WP_SQLite_Token::TYPE_NONE === $token->type && 'DELIMITER' === $token->value ) {
|
|||
|
|
if ( $this->last + 1 >= $this->string_length ) {
|
|||
|
|
$this->error( 'Expected whitespace(s) before delimiter.', '', $this->last + 1 );
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
* Skipping last R (from `delimiteR`) and whitespaces between
|
|||
|
|
* the keyword `DELIMITER` and the actual delimiter.
|
|||
|
|
*/
|
|||
|
|
$pos = ++$this->last;
|
|||
|
|
$token = $this->parse_whitespace();
|
|||
|
|
|
|||
|
|
if ( null !== $token ) {
|
|||
|
|
$token->position = $pos;
|
|||
|
|
$this->tokens[ $this->tokens_count++ ] = $token;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Preparing the token that holds the new delimiter.
|
|||
|
|
if ( $this->last + 1 >= $this->string_length ) {
|
|||
|
|
$this->error( 'Expected delimiter.', '', $this->last + 1 );
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$pos = $this->last + 1;
|
|||
|
|
|
|||
|
|
// Parsing the delimiter.
|
|||
|
|
$this->delimiter = null;
|
|||
|
|
$delimiter_length = 0;
|
|||
|
|
while (
|
|||
|
|
++$this->last < $this->string_length
|
|||
|
|
&& ! static::is_whitespace( $this->str[ $this->last ] )
|
|||
|
|
&& $delimiter_length < 15
|
|||
|
|
) {
|
|||
|
|
$this->delimiter .= $this->str[ $this->last ];
|
|||
|
|
++$delimiter_length;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( empty( $this->delimiter ) ) {
|
|||
|
|
$this->error( 'Expected delimiter.', '', $this->last );
|
|||
|
|
$this->delimiter = ';';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
--$this->last;
|
|||
|
|
|
|||
|
|
// Saving the delimiter and its token.
|
|||
|
|
$this->delimiter_length = strlen( $this->delimiter );
|
|||
|
|
$token = new WP_SQLite_Token( $this->delimiter, WP_SQLite_Token::TYPE_DELIMITER );
|
|||
|
|
$token->position = $pos;
|
|||
|
|
$this->tokens[ $this->tokens_count++ ] = $token;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$last_token = $token;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Adding a final delimiter to mark the ending.
|
|||
|
|
$this->tokens[ $this->tokens_count++ ] = new WP_SQLite_Token( null, WP_SQLite_Token::TYPE_DELIMITER );
|
|||
|
|
|
|||
|
|
$this->solve_ambiguity_on_star_operator();
|
|||
|
|
$this->solve_ambiguity_on_function_keywords();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Resolves the ambiguity when dealing with the "*" operator.
|
|||
|
|
*
|
|||
|
|
* In SQL statements, the "*" operator can be an arithmetic operator (like in 2*3) or an SQL wildcard (like in
|
|||
|
|
* SELECT a.* FROM ...). To solve this ambiguity, the solution is to find the next token, excluding whitespaces and
|
|||
|
|
* comments, right after the "*" position. The "*" is for sure an SQL wildcard if the next token found is any of:
|
|||
|
|
* - "FROM" (the FROM keyword like in "SELECT * FROM...");
|
|||
|
|
* - "USING" (the USING keyword like in "DELETE table_name.* USING...");
|
|||
|
|
* - "," (a comma separator like in "SELECT *, field FROM...");
|
|||
|
|
* - ")" (a closing parenthesis like in "COUNT(*)").
|
|||
|
|
* This methods will change the flag of the "*" tokens when any of those condition above is true. Otherwise, the
|
|||
|
|
* default flag (arithmetic) will be kept.
|
|||
|
|
*
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
private function solve_ambiguity_on_star_operator() {
|
|||
|
|
$i_bak = $this->tokens_index;
|
|||
|
|
while ( true ) {
|
|||
|
|
$star_token = $this->tokens_get_next_of_type_and_value( WP_SQLite_Token::TYPE_OPERATOR, '*' );
|
|||
|
|
if ( null === $star_token ) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
// tokens_get_next() already gets rid of whitespaces and comments.
|
|||
|
|
$next = $this->tokens_get_next();
|
|||
|
|
|
|||
|
|
if ( null === $next ) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (
|
|||
|
|
( WP_SQLite_Token::TYPE_KEYWORD !== $next->type || ! in_array( $next->value, array( 'FROM', 'USING' ), true ) )
|
|||
|
|
&& ( WP_SQLite_Token::TYPE_OPERATOR !== $next->type || ! in_array( $next->value, array( ',', ')' ), true ) )
|
|||
|
|
) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$star_token->flags = WP_SQLite_Token::FLAG_OPERATOR_SQL;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$this->tokens_index = $i_bak;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Resolves the ambiguity when dealing with the functions keywords.
|
|||
|
|
*
|
|||
|
|
* In SQL statements, the function keywords might be used as table names or columns names.
|
|||
|
|
* To solve this ambiguity, the solution is to find the next token, excluding whitespaces and
|
|||
|
|
* comments, right after the function keyword position. The function keyword is for sure used
|
|||
|
|
* as column name or table name if the next token found is any of:
|
|||
|
|
*
|
|||
|
|
* - "FROM" (the FROM keyword like in "SELECT Country x, AverageSalary avg FROM...");
|
|||
|
|
* - "WHERE" (the WHERE keyword like in "DELETE FROM emp x WHERE x.salary = 20");
|
|||
|
|
* - "SET" (the SET keyword like in "UPDATE Country x, City y set x.Name=x.Name");
|
|||
|
|
* - "," (a comma separator like 'x,' in "UPDATE Country x, City y set x.Name=x.Name");
|
|||
|
|
* - "." (a dot separator like in "x.asset_id FROM (SELECT evt.asset_id FROM evt)".
|
|||
|
|
* - "NULL" (when used as a table alias like in "avg.col FROM (SELECT ev.col FROM ev) avg").
|
|||
|
|
*
|
|||
|
|
* This method will change the flag of the function keyword tokens when any of those
|
|||
|
|
* condition above is true. Otherwise, the
|
|||
|
|
* default flag (function keyword) will be kept.
|
|||
|
|
*
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
private function solve_ambiguity_on_function_keywords() {
|
|||
|
|
$i_bak = $this->tokens_index;
|
|||
|
|
$keyword_function = WP_SQLite_Token::TYPE_KEYWORD | WP_SQLite_Token::FLAG_KEYWORD_FUNCTION;
|
|||
|
|
while ( true ) {
|
|||
|
|
$keyword_token = $this->tokens_get_next_of_type_and_flag( WP_SQLite_Token::TYPE_KEYWORD, $keyword_function );
|
|||
|
|
if ( null === $keyword_token ) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
$next = $this->tokens_get_next();
|
|||
|
|
if (
|
|||
|
|
( WP_SQLite_Token::TYPE_KEYWORD !== $next->type
|
|||
|
|
|| ! in_array( $next->value, self::KEYWORD_NAME_INDICATORS, true )
|
|||
|
|
)
|
|||
|
|
&& ( WP_SQLite_Token::TYPE_OPERATOR !== $next->type
|
|||
|
|
|| ! in_array( $next->value, self::OPERATOR_NAME_INDICATORS, true )
|
|||
|
|
)
|
|||
|
|
&& ( null !== $next->value )
|
|||
|
|
) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$keyword_token->type = WP_SQLite_Token::TYPE_NONE;
|
|||
|
|
$keyword_token->flags = WP_SQLite_Token::TYPE_NONE;
|
|||
|
|
$keyword_token->keyword = $keyword_token->value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$this->tokens_index = $i_bak;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Creates a new error log.
|
|||
|
|
*
|
|||
|
|
* @param string $msg The error message.
|
|||
|
|
* @param string $str The character that produced the error.
|
|||
|
|
* @param int $pos The position of the character.
|
|||
|
|
* @param int $code The code of the error.
|
|||
|
|
*
|
|||
|
|
* @throws Exception The error log.
|
|||
|
|
* @return void
|
|||
|
|
*/
|
|||
|
|
public function error( $msg, $str = '', $pos = 0, $code = 0 ) {
|
|||
|
|
throw new Exception(
|
|||
|
|
print_r(
|
|||
|
|
array(
|
|||
|
|
'query' => $this->str,
|
|||
|
|
'message' => $msg,
|
|||
|
|
'str' => $str,
|
|||
|
|
'position' => $pos,
|
|||
|
|
'code' => $code,
|
|||
|
|
),
|
|||
|
|
true
|
|||
|
|
)
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Parses a keyword.
|
|||
|
|
*
|
|||
|
|
* @return WP_SQLite_Token|null
|
|||
|
|
*/
|
|||
|
|
public function parse_keyword() {
|
|||
|
|
$token = '';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Value to be returned.
|
|||
|
|
*
|
|||
|
|
* @var WP_SQLite_Token
|
|||
|
|
*/
|
|||
|
|
$ret = null;
|
|||
|
|
|
|||
|
|
// The value of `$this->last` where `$token` ends in `$this->str`.
|
|||
|
|
$i_end = $this->last;
|
|||
|
|
|
|||
|
|
// Whether last parsed character is a whitespace.
|
|||
|
|
$last_space = false;
|
|||
|
|
|
|||
|
|
for ( $j = 1; $j < static::KEYWORD_MAX_LENGTH && $this->last < $this->string_length; ++$j, ++$this->last ) {
|
|||
|
|
$last_space = false;
|
|||
|
|
// Composed keywords shouldn't have more than one whitespace between keywords.
|
|||
|
|
if ( static::is_whitespace( $this->str[ $this->last ] ) ) {
|
|||
|
|
if ( $last_space ) {
|
|||
|
|
--$j; // The size of the keyword didn't increase.
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$last_space = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
$flags = static::is_keyword( $token );
|
|||
|
|
|
|||
|
|
if ( ( $this->last + 1 !== $this->string_length && ! static::is_separator( $this->str[ $this->last + 1 ] ) ) || ! $flags ) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$ret = new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_KEYWORD, $flags );
|
|||
|
|
$i_end = $this->last;
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
* We don't break so we find longest keyword.
|
|||
|
|
* For example, `OR` and `ORDER` have a common prefix `OR`.
|
|||
|
|
* If we stopped at `OR`, the parsing would be invalid.
|
|||
|
|
*/
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$this->last = $i_end;
|
|||
|
|
|
|||
|
|
return $ret;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Parses a label.
|
|||
|
|
*
|
|||
|
|
* @return WP_SQLite_Token|null
|
|||
|
|
*/
|
|||
|
|
public function parse_label() {
|
|||
|
|
$token = '';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Value to be returned.
|
|||
|
|
*
|
|||
|
|
* @var WP_SQLite_Token
|
|||
|
|
*/
|
|||
|
|
$ret = null;
|
|||
|
|
|
|||
|
|
// The value of `$this->last` where `$token` ends in `$this->str`.
|
|||
|
|
$i_end = $this->last;
|
|||
|
|
for ( $j = 1; $j < static::LABEL_MAX_LENGTH && $this->last < $this->string_length; ++$j, ++$this->last ) {
|
|||
|
|
if ( ':' === $this->str[ $this->last ] && $j > 1 ) {
|
|||
|
|
// End of label.
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
$ret = new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_LABEL );
|
|||
|
|
$i_end = $this->last;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( static::is_whitespace( $this->str[ $this->last ] ) && $j > 1 ) {
|
|||
|
|
/*
|
|||
|
|
* Whitespace between label and `:`.
|
|||
|
|
* The size of the keyword didn't increase.
|
|||
|
|
*/
|
|||
|
|
--$j;
|
|||
|
|
} elseif ( static::is_separator( $this->str[ $this->last ] ) ) {
|
|||
|
|
// Any other separator.
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$this->last = $i_end;
|
|||
|
|
|
|||
|
|
return $ret;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Parses an operator.
|
|||
|
|
*
|
|||
|
|
* @return WP_SQLite_Token|null
|
|||
|
|
*/
|
|||
|
|
public function parse_operator() {
|
|||
|
|
$token = '';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Value to be returned.
|
|||
|
|
*
|
|||
|
|
* @var WP_SQLite_Token
|
|||
|
|
*/
|
|||
|
|
$ret = null;
|
|||
|
|
|
|||
|
|
// The value of `$this->last` where `$token` ends in `$this->str`.
|
|||
|
|
$i_end = $this->last;
|
|||
|
|
|
|||
|
|
for ( $j = 1; $j < static::OPERATOR_MAX_LENGTH && $this->last < $this->string_length; ++$j, ++$this->last ) {
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
$flags = static::is_operator( $token );
|
|||
|
|
|
|||
|
|
if ( ! $flags ) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$ret = new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_OPERATOR, $flags );
|
|||
|
|
$i_end = $this->last;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$this->last = $i_end;
|
|||
|
|
|
|||
|
|
return $ret;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Parses a whitespace.
|
|||
|
|
*
|
|||
|
|
* @return WP_SQLite_Token|null
|
|||
|
|
*/
|
|||
|
|
public function parse_whitespace() {
|
|||
|
|
$token = $this->str[ $this->last ];
|
|||
|
|
|
|||
|
|
if ( ! static::is_whitespace( $token ) ) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
while ( ++$this->last < $this->string_length && static::is_whitespace( $this->str[ $this->last ] ) ) {
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
--$this->last;
|
|||
|
|
|
|||
|
|
return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_WHITESPACE );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Parses a comment.
|
|||
|
|
*
|
|||
|
|
* @return WP_SQLite_Token|null
|
|||
|
|
*/
|
|||
|
|
public function parse_comment() {
|
|||
|
|
$i_bak = $this->last;
|
|||
|
|
$token = $this->str[ $this->last ];
|
|||
|
|
|
|||
|
|
// Bash style comments (#comment\n).
|
|||
|
|
if ( static::is_comment( $token ) ) {
|
|||
|
|
while ( ++$this->last < $this->string_length && "\n" !== $this->str[ $this->last ] ) {
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Include trailing \n as whitespace token.
|
|||
|
|
if ( $this->last < $this->string_length ) {
|
|||
|
|
--$this->last;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_COMMENT, WP_SQLite_Token::FLAG_COMMENT_BASH );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// C style comments (/*comment*\/).
|
|||
|
|
if ( ++$this->last < $this->string_length ) {
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
if ( static::is_comment( $token ) ) {
|
|||
|
|
// There might be a conflict with "*" operator here, when string is "*/*".
|
|||
|
|
// This can occurs in the following statements:
|
|||
|
|
// - "SELECT */* comment */ FROM ..."
|
|||
|
|
// - "SELECT 2*/* comment */3 AS `six`;".
|
|||
|
|
$next = $this->last + 1;
|
|||
|
|
if ( ( $next < $this->string_length ) && '*' === $this->str[ $next ] ) {
|
|||
|
|
// Conflict in "*/*": first "*" was not for ending a comment.
|
|||
|
|
// Stop here and let other parsing method define the true behavior of that first star.
|
|||
|
|
$this->last = $i_bak;
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$flags = WP_SQLite_Token::FLAG_COMMENT_C;
|
|||
|
|
|
|||
|
|
// This comment already ended. It may be a part of a previous MySQL specific command.
|
|||
|
|
if ( '*/' === $token ) {
|
|||
|
|
return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_COMMENT, $flags );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Checking if this is a MySQL-specific command.
|
|||
|
|
if ( $this->last + 1 < $this->string_length && '!' === $this->str[ $this->last + 1 ] ) {
|
|||
|
|
$flags |= WP_SQLite_Token::FLAG_COMMENT_MYSQL_CMD;
|
|||
|
|
$token .= $this->str[ ++$this->last ];
|
|||
|
|
|
|||
|
|
while (
|
|||
|
|
++$this->last < $this->string_length
|
|||
|
|
&& $this->str[ $this->last ] >= '0'
|
|||
|
|
&& $this->str[ $this->last ] <= '9'
|
|||
|
|
) {
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
--$this->last;
|
|||
|
|
|
|||
|
|
// We split this comment and parse only its beginning here.
|
|||
|
|
return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_COMMENT, $flags );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Parsing the comment.
|
|||
|
|
while (
|
|||
|
|
++$this->last < $this->string_length
|
|||
|
|
&& ( '*' !== $this->str[ $this->last - 1 ] || '/' !== $this->str[ $this->last ] )
|
|||
|
|
) {
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Adding the ending.
|
|||
|
|
if ( $this->last < $this->string_length ) {
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_COMMENT, $flags );
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// SQL style comments (-- comment\n).
|
|||
|
|
if ( ++$this->last < $this->string_length ) {
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
$end = false;
|
|||
|
|
} else {
|
|||
|
|
--$this->last;
|
|||
|
|
$end = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( static::is_comment( $token, $end ) ) {
|
|||
|
|
// Checking if this comment did not end already (```--\n```).
|
|||
|
|
if ( "\n" !== $this->str[ $this->last ] ) {
|
|||
|
|
while ( ++$this->last < $this->string_length && "\n" !== $this->str[ $this->last ] ) {
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Include trailing \n as whitespace token.
|
|||
|
|
if ( $this->last < $this->string_length ) {
|
|||
|
|
--$this->last;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_COMMENT, WP_SQLite_Token::FLAG_COMMENT_SQL );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$this->last = $i_bak;
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Parses a boolean.
|
|||
|
|
*
|
|||
|
|
* @return WP_SQLite_Token|null
|
|||
|
|
*/
|
|||
|
|
public function parse_bool() {
|
|||
|
|
if ( $this->last + 3 >= $this->string_length ) {
|
|||
|
|
// At least `min(strlen('TRUE'), strlen('FALSE'))` characters are required.
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$i_bak = $this->last;
|
|||
|
|
$token = $this->str[ $this->last ] . $this->str[ ++$this->last ]
|
|||
|
|
. $this->str[ ++$this->last ] . $this->str[ ++$this->last ]; // _TRUE_ or _FALS_e.
|
|||
|
|
|
|||
|
|
if ( static::is_bool( $token ) ) {
|
|||
|
|
return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_BOOL );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( ++$this->last < $this->string_length ) {
|
|||
|
|
$token .= $this->str[ $this->last ]; // fals_E_.
|
|||
|
|
if ( static::is_bool( $token ) ) {
|
|||
|
|
return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_BOOL, 1 );
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$this->last = $i_bak;
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Parses a number.
|
|||
|
|
*
|
|||
|
|
* @return WP_SQLite_Token|null
|
|||
|
|
*/
|
|||
|
|
public function parse_number() {
|
|||
|
|
/*
|
|||
|
|
* A rudimentary state machine is being used to parse numbers due to
|
|||
|
|
* the various forms of their notation.
|
|||
|
|
*
|
|||
|
|
* Below are the states of the machines and the conditions to change
|
|||
|
|
* the state.
|
|||
|
|
*
|
|||
|
|
* 1 --------------------[ + or - ]-------------------> 1
|
|||
|
|
* 1 -------------------[ 0x or 0X ]------------------> 2
|
|||
|
|
* 1 --------------------[ 0 to 9 ]-------------------> 3
|
|||
|
|
* 1 -----------------------[ . ]---------------------> 4
|
|||
|
|
* 1 -----------------------[ b ]---------------------> 7
|
|||
|
|
*
|
|||
|
|
* 2 --------------------[ 0 to F ]-------------------> 2
|
|||
|
|
*
|
|||
|
|
* 3 --------------------[ 0 to 9 ]-------------------> 3
|
|||
|
|
* 3 -----------------------[ . ]---------------------> 4
|
|||
|
|
* 3 --------------------[ e or E ]-------------------> 5
|
|||
|
|
*
|
|||
|
|
* 4 --------------------[ 0 to 9 ]-------------------> 4
|
|||
|
|
* 4 --------------------[ e or E ]-------------------> 5
|
|||
|
|
*
|
|||
|
|
* 5 ---------------[ + or - or 0 to 9 ]--------------> 6
|
|||
|
|
*
|
|||
|
|
* 7 -----------------------[ ' ]---------------------> 8
|
|||
|
|
*
|
|||
|
|
* 8 --------------------[ 0 or 1 ]-------------------> 8
|
|||
|
|
* 8 -----------------------[ ' ]---------------------> 9
|
|||
|
|
*
|
|||
|
|
* State 1 may be reached by negative numbers.
|
|||
|
|
* State 2 is reached only by hex numbers.
|
|||
|
|
* State 4 is reached only by float numbers.
|
|||
|
|
* State 5 is reached only by numbers in approximate form.
|
|||
|
|
* State 7 is reached only by numbers in bit representation.
|
|||
|
|
*
|
|||
|
|
* Valid final states are: 2, 3, 4 and 6. Any parsing that finished in a
|
|||
|
|
* state other than these is invalid.
|
|||
|
|
* Also, negative states are invalid states.
|
|||
|
|
*/
|
|||
|
|
$i_bak = $this->last;
|
|||
|
|
$token = '';
|
|||
|
|
$flags = 0;
|
|||
|
|
$state = 1;
|
|||
|
|
for ( ; $this->last < $this->string_length; ++$this->last ) {
|
|||
|
|
if ( 1 === $state ) {
|
|||
|
|
if ( '-' === $this->str[ $this->last ] ) {
|
|||
|
|
$flags |= WP_SQLite_Token::FLAG_NUMBER_NEGATIVE;
|
|||
|
|
} elseif (
|
|||
|
|
$this->last + 1 < $this->string_length
|
|||
|
|
&& '0' === $this->str[ $this->last ]
|
|||
|
|
&& 'x' === $this->str[ $this->last + 1 ]
|
|||
|
|
) {
|
|||
|
|
$token .= $this->str[ $this->last++ ];
|
|||
|
|
$state = 2;
|
|||
|
|
} elseif ( $this->str[ $this->last ] >= '0' && $this->str[ $this->last ] <= '9' ) {
|
|||
|
|
$state = 3;
|
|||
|
|
} elseif ( '.' === $this->str[ $this->last ] ) {
|
|||
|
|
$state = 4;
|
|||
|
|
} elseif ( 'b' === $this->str[ $this->last ] ) {
|
|||
|
|
$state = 7;
|
|||
|
|
} elseif ( '+' !== $this->str[ $this->last ] ) {
|
|||
|
|
// `+` is a valid character in a number.
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
} elseif ( 2 === $state ) {
|
|||
|
|
$flags |= WP_SQLite_Token::FLAG_NUMBER_HEX;
|
|||
|
|
if (
|
|||
|
|
! (
|
|||
|
|
( $this->str[ $this->last ] >= '0' && $this->str[ $this->last ] <= '9' )
|
|||
|
|
|| ( $this->str[ $this->last ] >= 'A' && $this->str[ $this->last ] <= 'F' )
|
|||
|
|
|| ( $this->str[ $this->last ] >= 'a' && $this->str[ $this->last ] <= 'f' )
|
|||
|
|
)
|
|||
|
|
) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
} elseif ( 3 === $state ) {
|
|||
|
|
if ( '.' === $this->str[ $this->last ] ) {
|
|||
|
|
$state = 4;
|
|||
|
|
} elseif ( 'e' === $this->str[ $this->last ] || 'E' === $this->str[ $this->last ] ) {
|
|||
|
|
$state = 5;
|
|||
|
|
} elseif (
|
|||
|
|
( $this->str[ $this->last ] >= 'a' && $this->str[ $this->last ] <= 'z' )
|
|||
|
|
|| ( $this->str[ $this->last ] >= 'A' && $this->str[ $this->last ] <= 'Z' )
|
|||
|
|
) {
|
|||
|
|
// A number can't be directly followed by a letter.
|
|||
|
|
$state = -$state;
|
|||
|
|
} elseif ( $this->str[ $this->last ] < '0' || $this->str[ $this->last ] > '9' ) {
|
|||
|
|
// Just digits and `.`, `e` and `E` are valid characters.
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
} elseif ( 4 === $state ) {
|
|||
|
|
$flags |= WP_SQLite_Token::FLAG_NUMBER_FLOAT;
|
|||
|
|
if ( 'e' === $this->str[ $this->last ] || 'E' === $this->str[ $this->last ] ) {
|
|||
|
|
$state = 5;
|
|||
|
|
} elseif (
|
|||
|
|
( $this->str[ $this->last ] >= 'a' && $this->str[ $this->last ] <= 'z' )
|
|||
|
|
|| ( $this->str[ $this->last ] >= 'A' && $this->str[ $this->last ] <= 'Z' )
|
|||
|
|
) {
|
|||
|
|
// A number can't be directly followed by a letter.
|
|||
|
|
$state = -$state;
|
|||
|
|
} elseif ( $this->str[ $this->last ] < '0' || $this->str[ $this->last ] > '9' ) {
|
|||
|
|
// Just digits, `e` and `E` are valid characters.
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
} elseif ( 5 === $state ) {
|
|||
|
|
$flags |= WP_SQLite_Token::FLAG_NUMBER_APPROXIMATE;
|
|||
|
|
if (
|
|||
|
|
'+' === $this->str[ $this->last ] || '-' === $this->str[ $this->last ]
|
|||
|
|
|| ( $this->str[ $this->last ] >= '0' && $this->str[ $this->last ] <= '9' )
|
|||
|
|
) {
|
|||
|
|
$state = 6;
|
|||
|
|
} elseif (
|
|||
|
|
( $this->str[ $this->last ] >= 'a' && $this->str[ $this->last ] <= 'z' )
|
|||
|
|
|| ( $this->str[ $this->last ] >= 'A' && $this->str[ $this->last ] <= 'Z' )
|
|||
|
|
) {
|
|||
|
|
// A number can't be directly followed by a letter.
|
|||
|
|
$state = -$state;
|
|||
|
|
} else {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
} elseif ( 6 === $state ) {
|
|||
|
|
if ( $this->str[ $this->last ] < '0' || $this->str[ $this->last ] > '9' ) {
|
|||
|
|
// Just digits are valid characters.
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
} elseif ( 7 === $state ) {
|
|||
|
|
$flags |= WP_SQLite_Token::FLAG_NUMBER_BINARY;
|
|||
|
|
if ( '\'' !== $this->str[ $this->last ] ) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$state = 8;
|
|||
|
|
} elseif ( 8 === $state ) {
|
|||
|
|
if ( '\'' === $this->str[ $this->last ] ) {
|
|||
|
|
$state = 9;
|
|||
|
|
} elseif ( '0' !== $this->str[ $this->last ] && '1' !== $this->str[ $this->last ] ) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
} elseif ( 9 === $state ) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( 2 === $state || 3 === $state || ( '.' !== $token && 4 === $state ) || 6 === $state || 9 === $state ) {
|
|||
|
|
--$this->last;
|
|||
|
|
|
|||
|
|
return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_NUMBER, $flags );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$this->last = $i_bak;
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Parses a string.
|
|||
|
|
*
|
|||
|
|
* @param string $quote Additional starting symbol.
|
|||
|
|
*
|
|||
|
|
* @return WP_SQLite_Token|null
|
|||
|
|
*/
|
|||
|
|
public function parse_string( $quote = '' ) {
|
|||
|
|
$token = $this->str[ $this->last ];
|
|||
|
|
$flags = static::is_string( $token );
|
|||
|
|
|
|||
|
|
if ( ! $flags && $token !== $quote ) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$quote = $token;
|
|||
|
|
|
|||
|
|
while ( ++$this->last < $this->string_length ) {
|
|||
|
|
if (
|
|||
|
|
$this->last + 1 < $this->string_length
|
|||
|
|
&& (
|
|||
|
|
( $this->str[ $this->last ] === $quote && $this->str[ $this->last + 1 ] === $quote )
|
|||
|
|
|| ( '\\' === $this->str[ $this->last ] && '`' !== $quote )
|
|||
|
|
)
|
|||
|
|
) {
|
|||
|
|
$token .= $this->str[ $this->last ] . $this->str[ ++$this->last ];
|
|||
|
|
} else {
|
|||
|
|
if ( $this->str[ $this->last ] === $quote ) {
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( $this->last >= $this->string_length || $this->str[ $this->last ] !== $quote ) {
|
|||
|
|
$this->error(
|
|||
|
|
sprintf(
|
|||
|
|
'Ending quote %1$s was expected.',
|
|||
|
|
$quote
|
|||
|
|
),
|
|||
|
|
'',
|
|||
|
|
$this->last
|
|||
|
|
);
|
|||
|
|
} else {
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_STRING, $flags );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Parses a symbol.
|
|||
|
|
*
|
|||
|
|
* @return WP_SQLite_Token|null
|
|||
|
|
*/
|
|||
|
|
public function parse_symbol() {
|
|||
|
|
$token = $this->str[ $this->last ];
|
|||
|
|
$flags = static::is_symbol( $token );
|
|||
|
|
|
|||
|
|
if ( ! $flags ) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( $flags & WP_SQLite_Token::FLAG_SYMBOL_VARIABLE ) {
|
|||
|
|
if ( $this->last + 1 < $this->string_length && '@' === $this->str[ ++$this->last ] ) {
|
|||
|
|
// This is a system variable (e.g. `@@hostname`).
|
|||
|
|
$token .= $this->str[ $this->last++ ];
|
|||
|
|
$flags |= WP_SQLite_Token::FLAG_SYMBOL_SYSTEM;
|
|||
|
|
}
|
|||
|
|
} elseif ( $flags & WP_SQLite_Token::FLAG_SYMBOL_PARAMETER ) {
|
|||
|
|
if ( '?' !== $token && $this->last + 1 < $this->string_length ) {
|
|||
|
|
++$this->last;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
$token = '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$str = null;
|
|||
|
|
|
|||
|
|
if ( $this->last < $this->string_length ) {
|
|||
|
|
$str = $this->parse_string( '`' );
|
|||
|
|
|
|||
|
|
if ( null === $str ) {
|
|||
|
|
$str = $this->parse_unknown();
|
|||
|
|
|
|||
|
|
if ( null === $str && ! ( $flags & WP_SQLite_Token::FLAG_SYMBOL_PARAMETER ) ) {
|
|||
|
|
$this->error( 'Variable name was expected.', $this->str[ $this->last ], $this->last );
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( null !== $str ) {
|
|||
|
|
$token .= $str->token;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return new WP_SQLite_Token( $token, WP_SQLite_Token::TYPE_SYMBOL, $flags );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Parses unknown parts of the query.
|
|||
|
|
*
|
|||
|
|
* @return WP_SQLite_Token|null
|
|||
|
|
*/
|
|||
|
|
public function parse_unknown() {
|
|||
|
|
$token = $this->str[ $this->last ];
|
|||
|
|
if ( static::is_separator( $token ) ) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
while ( ++$this->last < $this->string_length && ! static::is_separator( $this->str[ $this->last ] ) ) {
|
|||
|
|
$token .= $this->str[ $this->last ];
|
|||
|
|
|
|||
|
|
// Test if end of token equals the current delimiter. If so, remove it from the token.
|
|||
|
|
if ( str_ends_with( $token, $this->delimiter ) ) {
|
|||
|
|
$token = substr( $token, 0, -$this->delimiter_length );
|
|||
|
|
$this->last -= $this->delimiter_length - 1;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
--$this->last;
|
|||
|
|
|
|||
|
|
return new WP_SQLite_Token( $token );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Parses the delimiter of the query.
|
|||
|
|
*
|
|||
|
|
* @return WP_SQLite_Token|null
|
|||
|
|
*/
|
|||
|
|
public function parse_delimiter() {
|
|||
|
|
$index = 0;
|
|||
|
|
|
|||
|
|
while ( $index < $this->delimiter_length && $this->last + $index < $this->string_length ) {
|
|||
|
|
if ( $this->delimiter[ $index ] !== $this->str[ $this->last + $index ] ) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
++$index;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$this->last += $this->delimiter_length - 1;
|
|||
|
|
|
|||
|
|
return new WP_SQLite_Token( $this->delimiter, WP_SQLite_Token::TYPE_DELIMITER );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Checks if the given string is a keyword.
|
|||
|
|
*
|
|||
|
|
* @param string $str String to be checked.
|
|||
|
|
* @param bool $is_reserved Checks if the keyword is reserved.
|
|||
|
|
*
|
|||
|
|
* @return int|null
|
|||
|
|
*/
|
|||
|
|
public static function is_keyword( $str, $is_reserved = false ) {
|
|||
|
|
$str = strtoupper( $str );
|
|||
|
|
|
|||
|
|
if ( isset( static::$keywords[ $str ] ) ) {
|
|||
|
|
if ( $is_reserved && ! ( static::$keywords[ $str ] & WP_SQLite_Token::FLAG_KEYWORD_RESERVED ) ) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return static::$keywords[ $str ];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Checks if the given string is an operator.
|
|||
|
|
*
|
|||
|
|
* @param string $str String to be checked.
|
|||
|
|
*
|
|||
|
|
* @return int|null The appropriate flag for the operator.
|
|||
|
|
*/
|
|||
|
|
public static function is_operator( $str ) {
|
|||
|
|
if ( ! isset( static::$operators[ $str ] ) ) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return static::$operators[ $str ];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Checks if the given character is a whitespace.
|
|||
|
|
*
|
|||
|
|
* @param string $str String to be checked.
|
|||
|
|
*
|
|||
|
|
* @return bool
|
|||
|
|
*/
|
|||
|
|
public static function is_whitespace( $str ) {
|
|||
|
|
return ( ' ' === $str ) || ( "\r" === $str ) || ( "\n" === $str ) || ( "\t" === $str );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Checks if the given string is the beginning of a whitespace.
|
|||
|
|
*
|
|||
|
|
* @param string $str String to be checked.
|
|||
|
|
* @param mixed $end Whether this is the end of the string.
|
|||
|
|
*
|
|||
|
|
* @return int|null The appropriate flag for the comment type.
|
|||
|
|
*/
|
|||
|
|
public static function is_comment( $str, $end = false ) {
|
|||
|
|
$string_length = strlen( $str );
|
|||
|
|
if ( 0 === $string_length ) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// If comment is Bash style (#).
|
|||
|
|
if ( '#' === $str[0] ) {
|
|||
|
|
return WP_SQLite_Token::FLAG_COMMENT_BASH;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// If comment is opening C style (/*), warning, it could be a MySQL command (/*!).
|
|||
|
|
if ( ( $string_length > 1 ) && ( '/' === $str[0] ) && ( '*' === $str[1] ) ) {
|
|||
|
|
return ( $string_length > 2 ) && ( '!' === $str[2] ) ?
|
|||
|
|
WP_SQLite_Token::FLAG_COMMENT_MYSQL_CMD : WP_SQLite_Token::FLAG_COMMENT_C;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// If comment is closing C style (*/), warning, it could conflicts with wildcard and a real opening C style.
|
|||
|
|
// It would looks like the following valid SQL statement: "SELECT */* comment */ FROM...".
|
|||
|
|
if ( ( $string_length > 1 ) && ( '*' === $str[0] ) && ( '/' === $str[1] ) ) {
|
|||
|
|
return WP_SQLite_Token::FLAG_COMMENT_C;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// If comment is SQL style (--\s?).
|
|||
|
|
if ( ( $string_length > 2 ) && ( '-' === $str[0] ) && ( '-' === $str[1] ) && static::is_whitespace( $str[2] ) ) {
|
|||
|
|
return WP_SQLite_Token::FLAG_COMMENT_SQL;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( ( 2 === $string_length ) && $end && ( '-' === $str[0] ) && ( '-' === $str[1] ) ) {
|
|||
|
|
return WP_SQLite_Token::FLAG_COMMENT_SQL;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Checks if the given string is a boolean value.
|
|||
|
|
* This actually checks only for `TRUE` and `FALSE` because `1` or `0` are
|
|||
|
|
* numbers and are parsed by specific methods.
|
|||
|
|
*
|
|||
|
|
* @param string $str String to be checked.
|
|||
|
|
*
|
|||
|
|
* @return bool
|
|||
|
|
*/
|
|||
|
|
public static function is_bool( $str ) {
|
|||
|
|
$str = strtoupper( $str );
|
|||
|
|
|
|||
|
|
return ( 'TRUE' === $str ) || ( 'FALSE' === $str );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Checks if the given character can be a part of a number.
|
|||
|
|
*
|
|||
|
|
* @param string $str String to be checked.
|
|||
|
|
*
|
|||
|
|
* @return bool
|
|||
|
|
*/
|
|||
|
|
public static function is_number( $str ) {
|
|||
|
|
return ( $str >= '0' ) && ( $str <= '9' ) || ( '.' === $str )
|
|||
|
|
|| ( '-' === $str ) || ( '+' === $str ) || ( 'e' === $str ) || ( 'E' === $str );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Checks if the given character is the beginning of a symbol. A symbol
|
|||
|
|
* can be either a variable or a field name.
|
|||
|
|
*
|
|||
|
|
* @param string $str String to be checked.
|
|||
|
|
*
|
|||
|
|
* @return int|null The appropriate flag for the symbol type.
|
|||
|
|
*/
|
|||
|
|
public static function is_symbol( $str ) {
|
|||
|
|
if ( 0 === strlen( $str ) ) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( '@' === $str[0] ) {
|
|||
|
|
return WP_SQLite_Token::FLAG_SYMBOL_VARIABLE;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( '`' === $str[0] ) {
|
|||
|
|
return WP_SQLite_Token::FLAG_SYMBOL_BACKTICK;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( ':' === $str[0] || '?' === $str[0] ) {
|
|||
|
|
return WP_SQLite_Token::FLAG_SYMBOL_PARAMETER;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Checks if the given character is the beginning of a string.
|
|||
|
|
*
|
|||
|
|
* @param string $str String to be checked.
|
|||
|
|
*
|
|||
|
|
* @return int|null The appropriate flag for the string type.
|
|||
|
|
*/
|
|||
|
|
public static function is_string( $str ) {
|
|||
|
|
if ( strlen( $str ) === 0 ) {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( '\'' === $str[0] ) {
|
|||
|
|
return WP_SQLite_Token::FLAG_STRING_SINGLE_QUOTES;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ( '"' === $str[0] ) {
|
|||
|
|
return WP_SQLite_Token::FLAG_STRING_DOUBLE_QUOTES;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Checks if the given character can be a separator for two lexeme.
|
|||
|
|
*
|
|||
|
|
* @param string $str String to be checked.
|
|||
|
|
*
|
|||
|
|
* @return bool
|
|||
|
|
*/
|
|||
|
|
public static function is_separator( $str ) {
|
|||
|
|
/*
|
|||
|
|
* NOTES: Only non alphanumeric ASCII characters may be separators.
|
|||
|
|
* `~` is the last printable ASCII character.
|
|||
|
|
*/
|
|||
|
|
return ( $str <= '~' )
|
|||
|
|
&& ( '_' !== $str )
|
|||
|
|
&& ( '$' !== $str )
|
|||
|
|
&& ( ( $str < '0' ) || ( $str > '9' ) )
|
|||
|
|
&& ( ( $str < 'a' ) || ( $str > 'z' ) )
|
|||
|
|
&& ( ( $str < 'A' ) || ( $str > 'Z' ) );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Constructor.
|
|||
|
|
*
|
|||
|
|
* @param stdClass[] $tokens The initial array of tokens.
|
|||
|
|
*/
|
|||
|
|
public function tokens( array $tokens = array() ) {
|
|||
|
|
$this->tokens = $tokens;
|
|||
|
|
$this->tokens_count = count( $tokens );
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Gets the next token.
|
|||
|
|
*
|
|||
|
|
* @param int $type The type of the token.
|
|||
|
|
* @param int $flag The flag of the token.
|
|||
|
|
*/
|
|||
|
|
public function tokens_get_next_of_type_and_flag( $type, $flag ) {
|
|||
|
|
for ( ; $this->tokens_index < $this->tokens_count; ++$this->tokens_index ) {
|
|||
|
|
if ( ( $this->tokens[ $this->tokens_index ]->type === $type ) && ( $this->tokens[ $this->tokens_index ]->flags === $flag ) ) {
|
|||
|
|
return $this->tokens[ $this->tokens_index++ ];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Gets the next token.
|
|||
|
|
*
|
|||
|
|
* @param int $type The type of the token.
|
|||
|
|
* @param string $value The value of the token.
|
|||
|
|
*
|
|||
|
|
* @return stdClass|null
|
|||
|
|
*/
|
|||
|
|
public function tokens_get_next_of_type_and_value( $type, $value ) {
|
|||
|
|
for ( ; $this->tokens_index < $this->tokens_count; ++$this->tokens_index ) {
|
|||
|
|
if ( ( $this->tokens[ $this->tokens_index ]->type === $type ) && ( $this->tokens[ $this->tokens_index ]->value === $value ) ) {
|
|||
|
|
return $this->tokens[ $this->tokens_index++ ];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Gets the next token. Skips any irrelevant token (whitespaces and
|
|||
|
|
* comments).
|
|||
|
|
*
|
|||
|
|
* @return stdClass|null
|
|||
|
|
*/
|
|||
|
|
public function tokens_get_next() {
|
|||
|
|
for ( ; $this->tokens_index < $this->tokens_count; ++$this->tokens_index ) {
|
|||
|
|
if (
|
|||
|
|
( WP_SQLite_Token::TYPE_WHITESPACE !== $this->tokens[ $this->tokens_index ]->type )
|
|||
|
|
&& ( WP_SQLite_Token::TYPE_COMMENT !== $this->tokens[ $this->tokens_index ]->type )
|
|||
|
|
) {
|
|||
|
|
return $this->tokens[ $this->tokens_index++ ];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
}
|