first commit
This commit is contained in:
146
pma/vendor/phpmyadmin/sql-parser/src/Statements/AlterStatement.php
vendored
Normal file
146
pma/vendor/phpmyadmin/sql-parser/src/Statements/AlterStatement.php
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\AlterOperation;
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
use function implode;
|
||||
|
||||
/**
|
||||
* `ALTER` statement.
|
||||
*/
|
||||
class AlterStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Table affected.
|
||||
*
|
||||
* @var Expression|null
|
||||
*/
|
||||
public $table;
|
||||
|
||||
/**
|
||||
* Column affected by this statement.
|
||||
*
|
||||
* @var AlterOperation[]|null
|
||||
*/
|
||||
public $altered = [];
|
||||
|
||||
/**
|
||||
* Options of this statement.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'ONLINE' => 1,
|
||||
'OFFLINE' => 1,
|
||||
'IGNORE' => 2,
|
||||
|
||||
'DATABASE' => 3,
|
||||
'EVENT' => 3,
|
||||
'FUNCTION' => 3,
|
||||
'PROCEDURE' => 3,
|
||||
'SERVER' => 3,
|
||||
'TABLE' => 3,
|
||||
'TABLESPACE' => 3,
|
||||
'USER' => 3,
|
||||
'VIEW' => 3,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param Parser $parser the instance that requests parsing
|
||||
* @param TokensList $list the list of tokens to be parsed
|
||||
*/
|
||||
public function parse(Parser $parser, TokensList $list)
|
||||
{
|
||||
++$list->idx; // Skipping `ALTER`.
|
||||
$this->options = OptionsArray::parse($parser, $list, static::$OPTIONS);
|
||||
++$list->idx;
|
||||
|
||||
// Parsing affected table.
|
||||
$this->table = Expression::parse(
|
||||
$parser,
|
||||
$list,
|
||||
[
|
||||
'parseField' => 'table',
|
||||
'breakOnAlias' => true,
|
||||
]
|
||||
);
|
||||
++$list->idx; // Skipping field.
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 -----------------[ alter operation ]-----------------> 1
|
||||
*
|
||||
* 1 -------------------------[ , ]-----------------------> 0
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
// End of statement.
|
||||
if ($token->type === Token::TYPE_DELIMITER) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Skipping whitespaces and comments.
|
||||
if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
$options = [];
|
||||
if ($this->options->has('DATABASE')) {
|
||||
$options = AlterOperation::$DB_OPTIONS;
|
||||
} elseif ($this->options->has('TABLE')) {
|
||||
$options = AlterOperation::$TABLE_OPTIONS;
|
||||
} elseif ($this->options->has('VIEW')) {
|
||||
$options = AlterOperation::$VIEW_OPTIONS;
|
||||
} elseif ($this->options->has('USER')) {
|
||||
$options = AlterOperation::$USER_OPTIONS;
|
||||
} elseif ($this->options->has('EVENT')) {
|
||||
$options = AlterOperation::$EVENT_OPTIONS;
|
||||
}
|
||||
|
||||
$this->altered[] = AlterOperation::parse($parser, $list, $options);
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === ',')) {
|
||||
$state = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$tmp = [];
|
||||
foreach ($this->altered as $altered) {
|
||||
$tmp[] = $altered::build($altered);
|
||||
}
|
||||
|
||||
return 'ALTER ' . OptionsArray::build($this->options)
|
||||
. ' ' . Expression::build($this->table)
|
||||
. ' ' . implode(', ', $tmp);
|
||||
}
|
||||
}
|
||||
37
pma/vendor/phpmyadmin/sql-parser/src/Statements/AnalyzeStatement.php
vendored
Normal file
37
pma/vendor/phpmyadmin/sql-parser/src/Statements/AnalyzeStatement.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
|
||||
/**
|
||||
* `ANALYZE` statement.
|
||||
*
|
||||
* ANALYZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE
|
||||
* tbl_name [, tbl_name] ...
|
||||
*/
|
||||
class AnalyzeStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Options of this statement.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'TABLE' => 1,
|
||||
|
||||
'NO_WRITE_TO_BINLOG' => 2,
|
||||
'LOCAL' => 3,
|
||||
];
|
||||
|
||||
/**
|
||||
* Analyzed tables.
|
||||
*
|
||||
* @var Expression[]|null
|
||||
*/
|
||||
public $tables;
|
||||
}
|
||||
31
pma/vendor/phpmyadmin/sql-parser/src/Statements/BackupStatement.php
vendored
Normal file
31
pma/vendor/phpmyadmin/sql-parser/src/Statements/BackupStatement.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
/**
|
||||
* `BACKUP` statement.
|
||||
*
|
||||
* BACKUP TABLE tbl_name [, tbl_name] ... TO '/path/to/backup/directory'
|
||||
*/
|
||||
class BackupStatement extends MaintenanceStatement
|
||||
{
|
||||
/**
|
||||
* Options of this statement.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'TABLE' => 1,
|
||||
|
||||
'NO_WRITE_TO_BINLOG' => 2,
|
||||
'LOCAL' => 3,
|
||||
|
||||
'TO' => [
|
||||
4,
|
||||
'var',
|
||||
],
|
||||
];
|
||||
}
|
||||
40
pma/vendor/phpmyadmin/sql-parser/src/Statements/CallStatement.php
vendored
Normal file
40
pma/vendor/phpmyadmin/sql-parser/src/Statements/CallStatement.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\FunctionCall;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
|
||||
use function implode;
|
||||
|
||||
/**
|
||||
* `CALL` statement.
|
||||
*
|
||||
* CALL sp_name([parameter[,...]])
|
||||
*
|
||||
* or
|
||||
*
|
||||
* CALL sp_name[()]
|
||||
*/
|
||||
class CallStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* The name of the function and its parameters.
|
||||
*
|
||||
* @var FunctionCall|null
|
||||
*/
|
||||
public $call;
|
||||
|
||||
/**
|
||||
* Build statement for CALL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return 'CALL ' . $this->call->name . '('
|
||||
. ($this->call->parameters ? implode(',', $this->call->parameters->raw) : '') . ')';
|
||||
}
|
||||
}
|
||||
30
pma/vendor/phpmyadmin/sql-parser/src/Statements/CheckStatement.php
vendored
Normal file
30
pma/vendor/phpmyadmin/sql-parser/src/Statements/CheckStatement.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
/**
|
||||
* `CHECK` statement.
|
||||
*
|
||||
* CHECK TABLE tbl_name [, tbl_name] ... [option] ...
|
||||
*/
|
||||
class CheckStatement extends MaintenanceStatement
|
||||
{
|
||||
/**
|
||||
* Options of this statement.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'TABLE' => 1,
|
||||
|
||||
'FOR UPGRADE' => 2,
|
||||
'QUICK' => 3,
|
||||
'FAST' => 4,
|
||||
'MEDIUM' => 5,
|
||||
'EXTENDED' => 6,
|
||||
'CHANGED' => 7,
|
||||
];
|
||||
}
|
||||
26
pma/vendor/phpmyadmin/sql-parser/src/Statements/ChecksumStatement.php
vendored
Normal file
26
pma/vendor/phpmyadmin/sql-parser/src/Statements/ChecksumStatement.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
/**
|
||||
* `CHECKSUM` statement.
|
||||
*
|
||||
* CHECKSUM TABLE tbl_name [, tbl_name] ... [ QUICK | EXTENDED ]
|
||||
*/
|
||||
class ChecksumStatement extends MaintenanceStatement
|
||||
{
|
||||
/**
|
||||
* Options of this statement.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'TABLE' => 1,
|
||||
|
||||
'QUICK' => 2,
|
||||
'EXTENDED' => 3,
|
||||
];
|
||||
}
|
||||
797
pma/vendor/phpmyadmin/sql-parser/src/Statements/CreateStatement.php
vendored
Normal file
797
pma/vendor/phpmyadmin/sql-parser/src/Statements/CreateStatement.php
vendored
Normal file
@@ -0,0 +1,797 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\ArrayObj;
|
||||
use PhpMyAdmin\SqlParser\Components\CreateDefinition;
|
||||
use PhpMyAdmin\SqlParser\Components\DataType;
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Components\ParameterDefinition;
|
||||
use PhpMyAdmin\SqlParser\Components\PartitionDefinition;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
use function is_array;
|
||||
use function trim;
|
||||
|
||||
/**
|
||||
* `CREATE` statement.
|
||||
*/
|
||||
class CreateStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Options for `CREATE` statements.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
// CREATE TABLE
|
||||
'TEMPORARY' => 1,
|
||||
|
||||
// CREATE VIEW
|
||||
'OR REPLACE' => 2,
|
||||
'ALGORITHM' => [
|
||||
3,
|
||||
'var=',
|
||||
],
|
||||
// `DEFINER` is also used for `CREATE FUNCTION / PROCEDURE`
|
||||
'DEFINER' => [
|
||||
4,
|
||||
'expr=',
|
||||
],
|
||||
// Used in `CREATE VIEW`
|
||||
'SQL SECURITY' => [
|
||||
5,
|
||||
'var',
|
||||
],
|
||||
|
||||
'DATABASE' => 6,
|
||||
'EVENT' => 6,
|
||||
'FUNCTION' => 6,
|
||||
'INDEX' => 6,
|
||||
'UNIQUE INDEX' => 6,
|
||||
'FULLTEXT INDEX' => 6,
|
||||
'SPATIAL INDEX' => 6,
|
||||
'PROCEDURE' => 6,
|
||||
'SERVER' => 6,
|
||||
'TABLE' => 6,
|
||||
'TABLESPACE' => 6,
|
||||
'TRIGGER' => 6,
|
||||
'USER' => 6,
|
||||
'VIEW' => 6,
|
||||
'SCHEMA' => 6,
|
||||
|
||||
// CREATE TABLE
|
||||
'IF NOT EXISTS' => 7,
|
||||
];
|
||||
|
||||
/**
|
||||
* All database options.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $DB_OPTIONS = [
|
||||
'CHARACTER SET' => [
|
||||
1,
|
||||
'var=',
|
||||
],
|
||||
'CHARSET' => [
|
||||
1,
|
||||
'var=',
|
||||
],
|
||||
'DEFAULT CHARACTER SET' => [
|
||||
1,
|
||||
'var=',
|
||||
],
|
||||
'DEFAULT CHARSET' => [
|
||||
1,
|
||||
'var=',
|
||||
],
|
||||
'DEFAULT COLLATE' => [
|
||||
2,
|
||||
'var=',
|
||||
],
|
||||
'COLLATE' => [
|
||||
2,
|
||||
'var=',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* All table options.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $TABLE_OPTIONS = [
|
||||
'ENGINE' => [
|
||||
1,
|
||||
'var=',
|
||||
],
|
||||
'AUTO_INCREMENT' => [
|
||||
2,
|
||||
'var=',
|
||||
],
|
||||
'AVG_ROW_LENGTH' => [
|
||||
3,
|
||||
'var',
|
||||
],
|
||||
'CHARACTER SET' => [
|
||||
4,
|
||||
'var=',
|
||||
],
|
||||
'CHARSET' => [
|
||||
4,
|
||||
'var=',
|
||||
],
|
||||
'DEFAULT CHARACTER SET' => [
|
||||
4,
|
||||
'var=',
|
||||
],
|
||||
'DEFAULT CHARSET' => [
|
||||
4,
|
||||
'var=',
|
||||
],
|
||||
'CHECKSUM' => [
|
||||
5,
|
||||
'var',
|
||||
],
|
||||
'DEFAULT COLLATE' => [
|
||||
6,
|
||||
'var=',
|
||||
],
|
||||
'COLLATE' => [
|
||||
6,
|
||||
'var=',
|
||||
],
|
||||
'COMMENT' => [
|
||||
7,
|
||||
'var=',
|
||||
],
|
||||
'CONNECTION' => [
|
||||
8,
|
||||
'var',
|
||||
],
|
||||
'DATA DIRECTORY' => [
|
||||
9,
|
||||
'var',
|
||||
],
|
||||
'DELAY_KEY_WRITE' => [
|
||||
10,
|
||||
'var',
|
||||
],
|
||||
'INDEX DIRECTORY' => [
|
||||
11,
|
||||
'var',
|
||||
],
|
||||
'INSERT_METHOD' => [
|
||||
12,
|
||||
'var',
|
||||
],
|
||||
'KEY_BLOCK_SIZE' => [
|
||||
13,
|
||||
'var',
|
||||
],
|
||||
'MAX_ROWS' => [
|
||||
14,
|
||||
'var',
|
||||
],
|
||||
'MIN_ROWS' => [
|
||||
15,
|
||||
'var',
|
||||
],
|
||||
'PACK_KEYS' => [
|
||||
16,
|
||||
'var',
|
||||
],
|
||||
'PASSWORD' => [
|
||||
17,
|
||||
'var',
|
||||
],
|
||||
'ROW_FORMAT' => [
|
||||
18,
|
||||
'var',
|
||||
],
|
||||
'TABLESPACE' => [
|
||||
19,
|
||||
'var',
|
||||
],
|
||||
'STORAGE' => [
|
||||
20,
|
||||
'var',
|
||||
],
|
||||
'UNION' => [
|
||||
21,
|
||||
'var',
|
||||
],
|
||||
'PAGE_COMPRESSED' => [
|
||||
22,
|
||||
'var',
|
||||
],
|
||||
'PAGE_COMPRESSION_LEVEL' => [
|
||||
23,
|
||||
'var',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* All function options.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $FUNC_OPTIONS = [
|
||||
'NOT' => [
|
||||
2,
|
||||
'var',
|
||||
],
|
||||
'FUNCTION' => [
|
||||
3,
|
||||
'var=',
|
||||
],
|
||||
'PROCEDURE' => [
|
||||
3,
|
||||
'var=',
|
||||
],
|
||||
'CONTAINS SQL' => 4,
|
||||
'NO SQL' => 4,
|
||||
'READS SQL DATA' => 4,
|
||||
'MODIFIES SQL DATA' => 4,
|
||||
'SQL SECURITY' => [
|
||||
6,
|
||||
'var',
|
||||
],
|
||||
'LANGUAGE' => [
|
||||
7,
|
||||
'var',
|
||||
],
|
||||
'COMMENT' => [
|
||||
8,
|
||||
'var',
|
||||
],
|
||||
|
||||
'CREATE' => 1,
|
||||
'DETERMINISTIC' => 2,
|
||||
];
|
||||
|
||||
/**
|
||||
* All trigger options.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $TRIGGER_OPTIONS = [
|
||||
'BEFORE' => 1,
|
||||
'AFTER' => 1,
|
||||
'INSERT' => 2,
|
||||
'UPDATE' => 2,
|
||||
'DELETE' => 2,
|
||||
];
|
||||
|
||||
/**
|
||||
* The name of the entity that is created.
|
||||
*
|
||||
* Used by all `CREATE` statements.
|
||||
*
|
||||
* @var Expression|null
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* The options of the entity (table, procedure, function, etc.).
|
||||
*
|
||||
* Used by `CREATE TABLE`, `CREATE FUNCTION` and `CREATE PROCEDURE`.
|
||||
*
|
||||
* @see static::$TABLE_OPTIONS
|
||||
* @see static::$FUNC_OPTIONS
|
||||
* @see static::$TRIGGER_OPTIONS
|
||||
*
|
||||
* @var OptionsArray|null
|
||||
*/
|
||||
public $entityOptions;
|
||||
|
||||
/**
|
||||
* If `CREATE TABLE`, a list of columns and keys.
|
||||
* If `CREATE VIEW`, a list of columns.
|
||||
*
|
||||
* Used by `CREATE TABLE` and `CREATE VIEW`.
|
||||
*
|
||||
* @var CreateDefinition[]|ArrayObj|null
|
||||
*/
|
||||
public $fields;
|
||||
|
||||
/**
|
||||
* If `CREATE TABLE WITH`.
|
||||
* If `CREATE TABLE AS WITH`.
|
||||
* If `CREATE VIEW AS WITH`.
|
||||
*
|
||||
* Used by `CREATE TABLE`, `CREATE VIEW`
|
||||
*
|
||||
* @var WithStatement|null
|
||||
*/
|
||||
public $with;
|
||||
|
||||
/**
|
||||
* If `CREATE TABLE ... SELECT`.
|
||||
* If `CREATE VIEW AS ` ... SELECT`.
|
||||
*
|
||||
* Used by `CREATE TABLE`, `CREATE VIEW`
|
||||
*
|
||||
* @var SelectStatement|null
|
||||
*/
|
||||
public $select;
|
||||
|
||||
/**
|
||||
* If `CREATE TABLE ... LIKE`.
|
||||
*
|
||||
* Used by `CREATE TABLE`
|
||||
*
|
||||
* @var Expression|null
|
||||
*/
|
||||
public $like;
|
||||
|
||||
/**
|
||||
* Expression used for partitioning.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $partitionBy;
|
||||
|
||||
/**
|
||||
* The number of partitions.
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
public $partitionsNum;
|
||||
|
||||
/**
|
||||
* Expression used for subpartitioning.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $subpartitionBy;
|
||||
|
||||
/**
|
||||
* The number of subpartitions.
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
public $subpartitionsNum;
|
||||
|
||||
/**
|
||||
* The partition of the new table.
|
||||
*
|
||||
* @var PartitionDefinition[]|null
|
||||
*/
|
||||
public $partitions;
|
||||
|
||||
/**
|
||||
* If `CREATE TRIGGER` the name of the table.
|
||||
*
|
||||
* Used by `CREATE TRIGGER`.
|
||||
*
|
||||
* @var Expression|null
|
||||
*/
|
||||
public $table;
|
||||
|
||||
/**
|
||||
* The return data type of this routine.
|
||||
*
|
||||
* Used by `CREATE FUNCTION`.
|
||||
*
|
||||
* @var DataType|null
|
||||
*/
|
||||
public $return;
|
||||
|
||||
/**
|
||||
* The parameters of this routine.
|
||||
*
|
||||
* Used by `CREATE FUNCTION` and `CREATE PROCEDURE`.
|
||||
*
|
||||
* @var ParameterDefinition[]|null
|
||||
*/
|
||||
public $parameters;
|
||||
|
||||
/**
|
||||
* The body of this function or procedure.
|
||||
* For views, it is the select statement that creates the view.
|
||||
* Used by `CREATE FUNCTION`, `CREATE PROCEDURE` and `CREATE VIEW`.
|
||||
*
|
||||
* @var Token[]|string
|
||||
*/
|
||||
public $body = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$fields = '';
|
||||
if (! empty($this->fields)) {
|
||||
if (is_array($this->fields)) {
|
||||
$fields = CreateDefinition::build($this->fields) . ' ';
|
||||
} elseif ($this->fields instanceof ArrayObj) {
|
||||
$fields = ArrayObj::build($this->fields);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->options->has('DATABASE') || $this->options->has('SCHEMA')) {
|
||||
return 'CREATE '
|
||||
. OptionsArray::build($this->options) . ' '
|
||||
. Expression::build($this->name) . ' '
|
||||
. OptionsArray::build($this->entityOptions);
|
||||
}
|
||||
|
||||
if ($this->options->has('TABLE')) {
|
||||
if ($this->select !== null) {
|
||||
return 'CREATE '
|
||||
. OptionsArray::build($this->options) . ' '
|
||||
. Expression::build($this->name) . ' '
|
||||
. $this->select->build();
|
||||
}
|
||||
|
||||
if ($this->like !== null) {
|
||||
return 'CREATE '
|
||||
. OptionsArray::build($this->options) . ' '
|
||||
. Expression::build($this->name) . ' LIKE '
|
||||
. Expression::build($this->like);
|
||||
}
|
||||
|
||||
if ($this->with !== null) {
|
||||
return 'CREATE '
|
||||
. OptionsArray::build($this->options) . ' '
|
||||
. Expression::build($this->name) . ' '
|
||||
. $this->with->build();
|
||||
}
|
||||
|
||||
$partition = '';
|
||||
|
||||
if (! empty($this->partitionBy)) {
|
||||
$partition .= "\nPARTITION BY " . $this->partitionBy;
|
||||
}
|
||||
|
||||
if (! empty($this->partitionsNum)) {
|
||||
$partition .= "\nPARTITIONS " . $this->partitionsNum;
|
||||
}
|
||||
|
||||
if (! empty($this->subpartitionBy)) {
|
||||
$partition .= "\nSUBPARTITION BY " . $this->subpartitionBy;
|
||||
}
|
||||
|
||||
if (! empty($this->subpartitionsNum)) {
|
||||
$partition .= "\nSUBPARTITIONS " . $this->subpartitionsNum;
|
||||
}
|
||||
|
||||
if (! empty($this->partitions)) {
|
||||
$partition .= "\n" . PartitionDefinition::build($this->partitions);
|
||||
}
|
||||
|
||||
return 'CREATE '
|
||||
. OptionsArray::build($this->options) . ' '
|
||||
. Expression::build($this->name) . ' '
|
||||
. $fields
|
||||
. OptionsArray::build($this->entityOptions)
|
||||
. $partition;
|
||||
} elseif ($this->options->has('VIEW')) {
|
||||
$builtStatement = '';
|
||||
if ($this->select !== null) {
|
||||
$builtStatement = $this->select->build();
|
||||
} elseif ($this->with !== null) {
|
||||
$builtStatement = $this->with->build();
|
||||
}
|
||||
|
||||
return 'CREATE '
|
||||
. OptionsArray::build($this->options) . ' '
|
||||
. Expression::build($this->name) . ' '
|
||||
. $fields . ' AS ' . $builtStatement
|
||||
. (! empty($this->body) ? TokensList::build($this->body) : '') . ' '
|
||||
. OptionsArray::build($this->entityOptions);
|
||||
} elseif ($this->options->has('TRIGGER')) {
|
||||
return 'CREATE '
|
||||
. OptionsArray::build($this->options) . ' '
|
||||
. Expression::build($this->name) . ' '
|
||||
. OptionsArray::build($this->entityOptions) . ' '
|
||||
. 'ON ' . Expression::build($this->table) . ' '
|
||||
. 'FOR EACH ROW ' . TokensList::build($this->body);
|
||||
} elseif ($this->options->has('PROCEDURE') || $this->options->has('FUNCTION')) {
|
||||
$tmp = '';
|
||||
if ($this->options->has('FUNCTION')) {
|
||||
$tmp = 'RETURNS ' . DataType::build($this->return);
|
||||
}
|
||||
|
||||
return 'CREATE '
|
||||
. OptionsArray::build($this->options) . ' '
|
||||
. Expression::build($this->name) . ' '
|
||||
. ParameterDefinition::build($this->parameters) . ' '
|
||||
. $tmp . ' ' . OptionsArray::build($this->entityOptions) . ' '
|
||||
. TokensList::build($this->body);
|
||||
}
|
||||
|
||||
return 'CREATE '
|
||||
. OptionsArray::build($this->options) . ' '
|
||||
. Expression::build($this->name) . ' '
|
||||
. TokensList::build($this->body);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Parser $parser the instance that requests parsing
|
||||
* @param TokensList $list the list of tokens to be parsed
|
||||
*/
|
||||
public function parse(Parser $parser, TokensList $list)
|
||||
{
|
||||
++$list->idx; // Skipping `CREATE`.
|
||||
|
||||
// Parsing options.
|
||||
$this->options = OptionsArray::parse($parser, $list, static::$OPTIONS);
|
||||
++$list->idx; // Skipping last option.
|
||||
|
||||
$isDatabase = $this->options->has('DATABASE') || $this->options->has('SCHEMA');
|
||||
$fieldName = $isDatabase ? 'database' : 'table';
|
||||
|
||||
// Parsing the field name.
|
||||
$this->name = Expression::parse(
|
||||
$parser,
|
||||
$list,
|
||||
[
|
||||
'parseField' => $fieldName,
|
||||
'breakOnAlias' => true,
|
||||
]
|
||||
);
|
||||
|
||||
if (! isset($this->name) || ($this->name === '')) {
|
||||
$parser->error('The name of the entity was expected.', $list->tokens[$list->idx]);
|
||||
} else {
|
||||
++$list->idx; // Skipping field.
|
||||
}
|
||||
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
$nextidx = $list->idx + 1;
|
||||
while ($nextidx < $list->count && $list->tokens[$nextidx]->type === Token::TYPE_WHITESPACE) {
|
||||
++$nextidx;
|
||||
}
|
||||
|
||||
if ($isDatabase) {
|
||||
$this->entityOptions = OptionsArray::parse($parser, $list, static::$DB_OPTIONS);
|
||||
} elseif ($this->options->has('TABLE')) {
|
||||
if (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'SELECT')) {
|
||||
/* CREATE TABLE ... SELECT */
|
||||
$this->select = new SelectStatement($parser, $list);
|
||||
} elseif ($token->type === Token::TYPE_KEYWORD && ($token->keyword === 'WITH')) {
|
||||
/* CREATE TABLE WITH */
|
||||
$this->with = new WithStatement($parser, $list);
|
||||
} elseif (
|
||||
($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'AS')
|
||||
&& ($list->tokens[$nextidx]->type === Token::TYPE_KEYWORD)
|
||||
) {
|
||||
if ($list->tokens[$nextidx]->value === 'SELECT') {
|
||||
/* CREATE TABLE ... AS SELECT */
|
||||
$list->idx = $nextidx;
|
||||
$this->select = new SelectStatement($parser, $list);
|
||||
} elseif ($list->tokens[$nextidx]->value === 'WITH') {
|
||||
/* CREATE TABLE WITH */
|
||||
$list->idx = $nextidx;
|
||||
$this->with = new WithStatement($parser, $list);
|
||||
}
|
||||
} elseif ($token->type === Token::TYPE_KEYWORD && $token->keyword === 'LIKE') {
|
||||
/* CREATE TABLE `new_tbl` LIKE 'orig_tbl' */
|
||||
$list->idx = $nextidx;
|
||||
$this->like = Expression::parse(
|
||||
$parser,
|
||||
$list,
|
||||
[
|
||||
'parseField' => 'table',
|
||||
'breakOnAlias' => true,
|
||||
]
|
||||
);
|
||||
// The 'LIKE' keyword was found, but no table_name was found next to it
|
||||
if ($this->like === null) {
|
||||
$parser->error('A table name was expected.', $list->tokens[$list->idx]);
|
||||
}
|
||||
} else {
|
||||
$this->fields = CreateDefinition::parse($parser, $list);
|
||||
if (empty($this->fields)) {
|
||||
$parser->error('At least one column definition was expected.', $list->tokens[$list->idx]);
|
||||
}
|
||||
|
||||
++$list->idx;
|
||||
|
||||
$this->entityOptions = OptionsArray::parse($parser, $list, static::$TABLE_OPTIONS);
|
||||
|
||||
/**
|
||||
* The field that is being filled (`partitionBy` or
|
||||
* `subpartitionBy`).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
$field = null;
|
||||
|
||||
/**
|
||||
* The number of brackets. `false` means no bracket was found
|
||||
* previously. At least one bracket is required to validate the
|
||||
* expression.
|
||||
*
|
||||
* @var int|bool
|
||||
*/
|
||||
$brackets = false;
|
||||
|
||||
/*
|
||||
* Handles partitions.
|
||||
*/
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
// End of statement.
|
||||
if ($token->type === Token::TYPE_DELIMITER) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Skipping comments.
|
||||
if ($token->type === Token::TYPE_COMMENT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'PARTITION BY')) {
|
||||
$field = 'partitionBy';
|
||||
$brackets = false;
|
||||
} elseif (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'SUBPARTITION BY')) {
|
||||
$field = 'subpartitionBy';
|
||||
$brackets = false;
|
||||
} elseif (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'PARTITIONS')) {
|
||||
$token = $list->getNextOfType(Token::TYPE_NUMBER);
|
||||
--$list->idx; // `getNextOfType` also advances one position.
|
||||
$this->partitionsNum = $token->value;
|
||||
} elseif (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'SUBPARTITIONS')) {
|
||||
$token = $list->getNextOfType(Token::TYPE_NUMBER);
|
||||
--$list->idx; // `getNextOfType` also advances one position.
|
||||
$this->subpartitionsNum = $token->value;
|
||||
} elseif (! empty($field)) {
|
||||
/*
|
||||
* Handling the content of `PARTITION BY` and `SUBPARTITION BY`.
|
||||
*/
|
||||
|
||||
// Counting brackets.
|
||||
if ($token->type === Token::TYPE_OPERATOR) {
|
||||
if ($token->value === '(') {
|
||||
// This is used instead of `++$brackets` because,
|
||||
// initially, `$brackets` is `false` cannot be
|
||||
// incremented.
|
||||
$brackets += 1;
|
||||
} elseif ($token->value === ')') {
|
||||
--$brackets;
|
||||
}
|
||||
}
|
||||
|
||||
// Building the expression used for partitioning.
|
||||
$this->$field .= $token->type === Token::TYPE_WHITESPACE ? ' ' : $token->token;
|
||||
|
||||
// Last bracket was read, the expression ended.
|
||||
// Comparing with `0` and not `false`, because `false` means
|
||||
// that no bracket was found and at least one must is
|
||||
// required.
|
||||
if ($brackets === 0) {
|
||||
$this->$field = trim($this->$field);
|
||||
$field = null;
|
||||
}
|
||||
} elseif (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
|
||||
if (! empty($this->partitionBy)) {
|
||||
$this->partitions = ArrayObj::parse(
|
||||
$parser,
|
||||
$list,
|
||||
['type' => 'PhpMyAdmin\\SqlParser\\Components\\PartitionDefinition']
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($this->options->has('PROCEDURE') || $this->options->has('FUNCTION')) {
|
||||
$this->parameters = ParameterDefinition::parse($parser, $list);
|
||||
if ($this->options->has('FUNCTION')) {
|
||||
$prevToken = $token;
|
||||
$token = $list->getNextOfType(Token::TYPE_KEYWORD);
|
||||
if ($token === null || $token->keyword !== 'RETURNS') {
|
||||
$parser->error('A "RETURNS" keyword was expected.', $token ?? $prevToken);
|
||||
} else {
|
||||
++$list->idx;
|
||||
$this->return = DataType::parse($parser, $list);
|
||||
}
|
||||
}
|
||||
|
||||
++$list->idx;
|
||||
|
||||
$this->entityOptions = OptionsArray::parse($parser, $list, static::$FUNC_OPTIONS);
|
||||
++$list->idx;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
$token = $list->tokens[$list->idx];
|
||||
$this->body[] = $token;
|
||||
}
|
||||
} elseif ($this->options->has('VIEW')) {
|
||||
/** @var Token $token */
|
||||
$token = $list->getNext(); // Skipping whitespaces and comments.
|
||||
|
||||
// Parsing columns list.
|
||||
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
|
||||
--$list->idx; // getNext() also goes forward one field.
|
||||
$this->fields = ArrayObj::parse($parser, $list);
|
||||
++$list->idx; // Skipping last token from the array.
|
||||
$list->getNext();
|
||||
}
|
||||
|
||||
// Parsing the SELECT expression if the view started with it.
|
||||
if (
|
||||
$token->type === Token::TYPE_KEYWORD
|
||||
&& $token->keyword === 'AS'
|
||||
&& $list->tokens[$nextidx]->type === Token::TYPE_KEYWORD
|
||||
) {
|
||||
if ($list->tokens[$nextidx]->value === 'SELECT') {
|
||||
$list->idx = $nextidx;
|
||||
$this->select = new SelectStatement($parser, $list);
|
||||
++$list->idx; // Skipping last token from the select.
|
||||
} elseif ($list->tokens[$nextidx]->value === 'WITH') {
|
||||
++$list->idx;
|
||||
$this->with = new WithStatement($parser, $list);
|
||||
}
|
||||
}
|
||||
|
||||
// Parsing all other tokens
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
$token = $list->tokens[$list->idx];
|
||||
if ($token->type === Token::TYPE_DELIMITER) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->body[] = $token;
|
||||
}
|
||||
} elseif ($this->options->has('TRIGGER')) {
|
||||
// Parsing the time and the event.
|
||||
$this->entityOptions = OptionsArray::parse($parser, $list, static::$TRIGGER_OPTIONS);
|
||||
++$list->idx;
|
||||
|
||||
$list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'ON');
|
||||
++$list->idx; // Skipping `ON`.
|
||||
|
||||
// Parsing the name of the table.
|
||||
$this->table = Expression::parse(
|
||||
$parser,
|
||||
$list,
|
||||
[
|
||||
'parseField' => 'table',
|
||||
'breakOnAlias' => true,
|
||||
]
|
||||
);
|
||||
++$list->idx;
|
||||
|
||||
$list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'FOR EACH ROW');
|
||||
++$list->idx; // Skipping `FOR EACH ROW`.
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
$token = $list->tokens[$list->idx];
|
||||
$this->body[] = $token;
|
||||
}
|
||||
} else {
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
$token = $list->tokens[$list->idx];
|
||||
if ($token->type === Token::TYPE_DELIMITER) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->body[] = $token;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
374
pma/vendor/phpmyadmin/sql-parser/src/Statements/DeleteStatement.php
vendored
Normal file
374
pma/vendor/phpmyadmin/sql-parser/src/Statements/DeleteStatement.php
vendored
Normal file
@@ -0,0 +1,374 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\ArrayObj;
|
||||
use PhpMyAdmin\SqlParser\Components\Condition;
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Components\ExpressionArray;
|
||||
use PhpMyAdmin\SqlParser\Components\JoinKeyword;
|
||||
use PhpMyAdmin\SqlParser\Components\Limit;
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Components\OrderKeyword;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
use function count;
|
||||
use function stripos;
|
||||
use function strlen;
|
||||
|
||||
/**
|
||||
* `DELETE` statement.
|
||||
*
|
||||
* DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM tbl_name
|
||||
* [PARTITION (partition_name,...)]
|
||||
* [WHERE where_condition]
|
||||
* [ORDER BY ...]
|
||||
* [LIMIT row_count]
|
||||
*
|
||||
* Multi-table syntax
|
||||
*
|
||||
* DELETE [LOW_PRIORITY] [QUICK] [IGNORE]
|
||||
* tbl_name[.*] [, tbl_name[.*]] ...
|
||||
* FROM table_references
|
||||
* [WHERE where_condition]
|
||||
*
|
||||
* OR
|
||||
*
|
||||
* DELETE [LOW_PRIORITY] [QUICK] [IGNORE]
|
||||
* FROM tbl_name[.*] [, tbl_name[.*]] ...
|
||||
* USING table_references
|
||||
* [WHERE where_condition]
|
||||
*/
|
||||
class DeleteStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Options for `DELETE` statements.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'LOW_PRIORITY' => 1,
|
||||
'QUICK' => 2,
|
||||
'IGNORE' => 3,
|
||||
];
|
||||
|
||||
/**
|
||||
* The clauses of this statement, in order.
|
||||
*
|
||||
* @see Statement::$CLAUSES
|
||||
*
|
||||
* @var array<string, array<int, int|string>>
|
||||
* @psalm-var array<string, array{non-empty-string, (1|2|3)}>
|
||||
*/
|
||||
public static $CLAUSES = [
|
||||
'DELETE' => [
|
||||
'DELETE',
|
||||
2,
|
||||
],
|
||||
// Used for options.
|
||||
'_OPTIONS' => [
|
||||
'_OPTIONS',
|
||||
1,
|
||||
],
|
||||
'FROM' => [
|
||||
'FROM',
|
||||
3,
|
||||
],
|
||||
'PARTITION' => [
|
||||
'PARTITION',
|
||||
3,
|
||||
],
|
||||
'USING' => [
|
||||
'USING',
|
||||
3,
|
||||
],
|
||||
'WHERE' => [
|
||||
'WHERE',
|
||||
3,
|
||||
],
|
||||
'ORDER BY' => [
|
||||
'ORDER BY',
|
||||
3,
|
||||
],
|
||||
'LIMIT' => [
|
||||
'LIMIT',
|
||||
3,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Table(s) used as sources for this statement.
|
||||
*
|
||||
* @var Expression[]|null
|
||||
*/
|
||||
public $from;
|
||||
|
||||
/**
|
||||
* Joins.
|
||||
*
|
||||
* @var JoinKeyword[]|null
|
||||
*/
|
||||
public $join;
|
||||
|
||||
/**
|
||||
* Tables used as sources for this statement.
|
||||
*
|
||||
* @var Expression[]|null
|
||||
*/
|
||||
public $using;
|
||||
|
||||
/**
|
||||
* Columns used in this statement.
|
||||
*
|
||||
* @var Expression[]|null
|
||||
*/
|
||||
public $columns;
|
||||
|
||||
/**
|
||||
* Partitions used as source for this statement.
|
||||
*
|
||||
* @var ArrayObj|null
|
||||
*/
|
||||
public $partition;
|
||||
|
||||
/**
|
||||
* Conditions used for filtering each row of the result set.
|
||||
*
|
||||
* @var Condition[]|null
|
||||
*/
|
||||
public $where;
|
||||
|
||||
/**
|
||||
* Specifies the order of the rows in the result set.
|
||||
*
|
||||
* @var OrderKeyword[]|null
|
||||
*/
|
||||
public $order;
|
||||
|
||||
/**
|
||||
* Conditions used for limiting the size of the result set.
|
||||
*
|
||||
* @var Limit|null
|
||||
*/
|
||||
public $limit;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$ret = 'DELETE ' . OptionsArray::build($this->options);
|
||||
|
||||
if ($this->columns !== null && count($this->columns) > 0) {
|
||||
$ret .= ' ' . ExpressionArray::build($this->columns);
|
||||
}
|
||||
|
||||
if ($this->from !== null && count($this->from) > 0) {
|
||||
$ret .= ' FROM ' . ExpressionArray::build($this->from);
|
||||
}
|
||||
|
||||
if ($this->join !== null && count($this->join) > 0) {
|
||||
$ret .= ' ' . JoinKeyword::build($this->join);
|
||||
}
|
||||
|
||||
if ($this->using !== null && count($this->using) > 0) {
|
||||
$ret .= ' USING ' . ExpressionArray::build($this->using);
|
||||
}
|
||||
|
||||
if ($this->where !== null && count($this->where) > 0) {
|
||||
$ret .= ' WHERE ' . Condition::build($this->where);
|
||||
}
|
||||
|
||||
if ($this->order !== null && count($this->order) > 0) {
|
||||
$ret .= ' ORDER BY ' . ExpressionArray::build($this->order);
|
||||
}
|
||||
|
||||
if ($this->limit !== null && strlen((string) $this->limit) > 0) {
|
||||
$ret .= ' LIMIT ' . Limit::build($this->limit);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Parser $parser the instance that requests parsing
|
||||
* @param TokensList $list the list of tokens to be parsed
|
||||
*/
|
||||
public function parse(Parser $parser, TokensList $list)
|
||||
{
|
||||
++$list->idx; // Skipping `DELETE`.
|
||||
|
||||
// parse any options if provided
|
||||
$this->options = OptionsArray::parse($parser, $list, static::$OPTIONS);
|
||||
++$list->idx;
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 ---------------------------------[ FROM ]----------------------------------> 2
|
||||
* 0 ------------------------------[ table[.*] ]--------------------------------> 1
|
||||
* 1 ---------------------------------[ FROM ]----------------------------------> 2
|
||||
* 2 --------------------------------[ USING ]----------------------------------> 3
|
||||
* 2 --------------------------------[ WHERE ]----------------------------------> 4
|
||||
* 2 --------------------------------[ ORDER ]----------------------------------> 5
|
||||
* 2 --------------------------------[ LIMIT ]----------------------------------> 6
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
/**
|
||||
* If the query is multi-table or not.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
$multiTable = false;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
// End of statement.
|
||||
if ($token->type === Token::TYPE_DELIMITER) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
if ($token->type === Token::TYPE_KEYWORD) {
|
||||
if ($token->keyword !== 'FROM') {
|
||||
$parser->error('Unexpected keyword.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
++$list->idx; // Skip 'FROM'
|
||||
$this->from = ExpressionArray::parse($parser, $list);
|
||||
|
||||
$state = 2;
|
||||
} else {
|
||||
$this->columns = ExpressionArray::parse($parser, $list);
|
||||
$state = 1;
|
||||
}
|
||||
} elseif ($state === 1) {
|
||||
if ($token->type !== Token::TYPE_KEYWORD) {
|
||||
$parser->error('Unexpected token.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($token->keyword !== 'FROM') {
|
||||
$parser->error('Unexpected keyword.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
++$list->idx; // Skip 'FROM'
|
||||
$this->from = ExpressionArray::parse($parser, $list);
|
||||
|
||||
$state = 2;
|
||||
} elseif ($state === 2) {
|
||||
if ($token->type === Token::TYPE_KEYWORD) {
|
||||
if (stripos($token->keyword, 'JOIN') !== false) {
|
||||
++$list->idx;
|
||||
$this->join = JoinKeyword::parse($parser, $list);
|
||||
|
||||
// remain in state = 2
|
||||
} else {
|
||||
switch ($token->keyword) {
|
||||
case 'USING':
|
||||
++$list->idx; // Skip 'USING'
|
||||
$this->using = ExpressionArray::parse($parser, $list);
|
||||
$state = 3;
|
||||
|
||||
$multiTable = true;
|
||||
break;
|
||||
case 'WHERE':
|
||||
++$list->idx; // Skip 'WHERE'
|
||||
$this->where = Condition::parse($parser, $list);
|
||||
$state = 4;
|
||||
break;
|
||||
case 'ORDER BY':
|
||||
++$list->idx; // Skip 'ORDER BY'
|
||||
$this->order = OrderKeyword::parse($parser, $list);
|
||||
$state = 5;
|
||||
break;
|
||||
case 'LIMIT':
|
||||
++$list->idx; // Skip 'LIMIT'
|
||||
$this->limit = Limit::parse($parser, $list);
|
||||
$state = 6;
|
||||
break;
|
||||
default:
|
||||
$parser->error('Unexpected keyword.', $token);
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($state === 3) {
|
||||
if ($token->type !== Token::TYPE_KEYWORD) {
|
||||
$parser->error('Unexpected token.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($token->keyword !== 'WHERE') {
|
||||
$parser->error('Unexpected keyword.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
++$list->idx; // Skip 'WHERE'
|
||||
$this->where = Condition::parse($parser, $list);
|
||||
$state = 4;
|
||||
} elseif ($state === 4) {
|
||||
if ($multiTable === true && $token->type === Token::TYPE_KEYWORD) {
|
||||
$parser->error('This type of clause is not valid in Multi-table queries.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($token->type === Token::TYPE_KEYWORD) {
|
||||
switch ($token->keyword) {
|
||||
case 'ORDER BY':
|
||||
++$list->idx; // Skip 'ORDER BY'
|
||||
$this->order = OrderKeyword::parse($parser, $list);
|
||||
$state = 5;
|
||||
break;
|
||||
case 'LIMIT':
|
||||
++$list->idx; // Skip 'LIMIT'
|
||||
$this->limit = Limit::parse($parser, $list);
|
||||
$state = 6;
|
||||
break;
|
||||
default:
|
||||
$parser->error('Unexpected keyword.', $token);
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
} elseif ($state === 5) {
|
||||
if ($token->type === Token::TYPE_KEYWORD) {
|
||||
if ($token->keyword !== 'LIMIT') {
|
||||
$parser->error('Unexpected keyword.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
++$list->idx; // Skip 'LIMIT'
|
||||
$this->limit = Limit::parse($parser, $list);
|
||||
$state = 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($state >= 2) {
|
||||
foreach ($this->from as $fromExpr) {
|
||||
$fromExpr->database = $fromExpr->table;
|
||||
$fromExpr->table = $fromExpr->column;
|
||||
$fromExpr->column = null;
|
||||
}
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
}
|
||||
}
|
||||
82
pma/vendor/phpmyadmin/sql-parser/src/Statements/DropStatement.php
vendored
Normal file
82
pma/vendor/phpmyadmin/sql-parser/src/Statements/DropStatement.php
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
|
||||
/**
|
||||
* `DROP` statement.
|
||||
*/
|
||||
class DropStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Options of this statement.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'DATABASE' => 1,
|
||||
'EVENT' => 1,
|
||||
'FUNCTION' => 1,
|
||||
'INDEX' => 1,
|
||||
'LOGFILE' => 1,
|
||||
'PROCEDURE' => 1,
|
||||
'SCHEMA' => 1,
|
||||
'SERVER' => 1,
|
||||
'TABLE' => 1,
|
||||
'VIEW' => 1,
|
||||
'TABLESPACE' => 1,
|
||||
'TRIGGER' => 1,
|
||||
'USER' => 1,
|
||||
|
||||
'TEMPORARY' => 2,
|
||||
'IF EXISTS' => 3,
|
||||
];
|
||||
|
||||
/**
|
||||
* The clauses of this statement, in order.
|
||||
*
|
||||
* @see Statement::$CLAUSES
|
||||
*
|
||||
* @var array<string, array<int, int|string>>
|
||||
* @psalm-var array<string, array{non-empty-string, (1|2|3)}>
|
||||
*/
|
||||
public static $CLAUSES = [
|
||||
'DROP' => [
|
||||
'DROP',
|
||||
2,
|
||||
],
|
||||
// Used for options.
|
||||
'_OPTIONS' => [
|
||||
'_OPTIONS',
|
||||
1,
|
||||
],
|
||||
// Used for select expressions.
|
||||
'DROP_' => [
|
||||
'DROP',
|
||||
1,
|
||||
],
|
||||
'ON' => [
|
||||
'ON',
|
||||
3,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Dropped elements.
|
||||
*
|
||||
* @var Expression[]|null
|
||||
*/
|
||||
public $fields;
|
||||
|
||||
/**
|
||||
* Table of the dropped index.
|
||||
*
|
||||
* @var Expression|null
|
||||
*/
|
||||
public $table;
|
||||
}
|
||||
207
pma/vendor/phpmyadmin/sql-parser/src/Statements/ExplainStatement.php
vendored
Normal file
207
pma/vendor/phpmyadmin/sql-parser/src/Statements/ExplainStatement.php
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
use function array_slice;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* `EXPLAIN` statement.
|
||||
*/
|
||||
class ExplainStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Options for `EXPLAIN` statements.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
|
||||
'EXTENDED' => 1,
|
||||
'PARTITIONS' => 1,
|
||||
'FORMAT' => [
|
||||
1,
|
||||
'var',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The parser of the statement to be explained
|
||||
*
|
||||
* @var Parser|null
|
||||
*/
|
||||
public $bodyParser = null;
|
||||
|
||||
/**
|
||||
* The statement alias, could be any of the following:
|
||||
* - {EXPLAIN | DESCRIBE | DESC}
|
||||
* - {EXPLAIN | DESCRIBE | DESC} ANALYZE
|
||||
* - ANALYZE
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $statementAlias;
|
||||
|
||||
/**
|
||||
* The connection identifier, if used.
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
public $connectionId = null;
|
||||
|
||||
/**
|
||||
* The explained table's name, if used.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $explainedTable = null;
|
||||
|
||||
/**
|
||||
* @param Parser $parser the instance that requests parsing
|
||||
* @param TokensList $list the list of tokens to be parsed
|
||||
*/
|
||||
public function parse(Parser $parser, TokensList $list)
|
||||
{
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 -------------------[ EXPLAIN/EXPLAIN ANALYZE/ANALYZE ]-----------------------> 1
|
||||
*
|
||||
* 1 ------------------------------[ OPTIONS ]------------------------------------> 2
|
||||
*
|
||||
* 2 --------------[ tablename / STATEMENT / FOR CONNECTION ]---------------------> 2
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
/**
|
||||
* To Differentiate between ANALYZE / EXPLAIN / EXPLAIN ANALYZE
|
||||
* 0 -> ANALYZE ( used by mariaDB https://mariadb.com/kb/en/analyze-statement)
|
||||
* 1 -> {EXPLAIN | DESCRIBE | DESC}
|
||||
* 2 -> {EXPLAIN | DESCRIBE | DESC} ANALYZE
|
||||
*/
|
||||
$miniState = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
// Skipping whitespaces and comments.
|
||||
if ($token->type === Token::TYPE_WHITESPACE || $token->type === Token::TYPE_COMMENT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
if ($token->keyword === 'ANALYZE' && $miniState === 0) {
|
||||
$state = 1;
|
||||
$this->statementAlias = 'ANALYZE';
|
||||
} elseif (
|
||||
$token->keyword === 'EXPLAIN'
|
||||
|| $token->keyword === 'DESC'
|
||||
|| $token->keyword === 'DESCRIBE'
|
||||
) {
|
||||
$miniState = 1;
|
||||
$this->statementAlias = $token->keyword;
|
||||
|
||||
$lastIdx = $list->idx;
|
||||
$nextKeyword = $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'ANALYZE');
|
||||
if ($nextKeyword && $nextKeyword->keyword !== null) {
|
||||
$miniState = 2;
|
||||
$this->statementAlias .= ' ANALYZE';
|
||||
} else {
|
||||
$list->idx = $lastIdx;
|
||||
}
|
||||
|
||||
$state = 1;
|
||||
}
|
||||
} elseif ($state === 1) {
|
||||
// Parsing options.
|
||||
$this->options = OptionsArray::parse($parser, $list, static::$OPTIONS);
|
||||
$state = 2;
|
||||
} elseif ($state === 2) {
|
||||
$currIdx = $list->idx;
|
||||
$list->idx++; // Ignore the current token
|
||||
$nextToken = $list->getNext();
|
||||
$list->idx = $currIdx;
|
||||
|
||||
if ($token->keyword === 'FOR' && $nextToken->keyword === 'CONNECTION') {
|
||||
$list->idx++; // Ignore the current token
|
||||
$list->getNext(); // CONNECTION
|
||||
$nextToken = $list->getNext(); // Identifier
|
||||
$this->connectionId = $nextToken->value;
|
||||
break;
|
||||
}
|
||||
|
||||
// To support EXPLAIN tablename
|
||||
if ($token->type === Token::TYPE_NONE) {
|
||||
$this->explainedTable = $token->value;
|
||||
break;
|
||||
}
|
||||
|
||||
if (
|
||||
$token->keyword !== 'SELECT'
|
||||
&& $token->keyword !== 'INSERT'
|
||||
&& $token->keyword !== 'UPDATE'
|
||||
&& $token->keyword !== 'DELETE'
|
||||
) {
|
||||
$parser->error('Unexpected token.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
// Index of the last parsed token by default would be the last token in the $list, because we're
|
||||
// assuming that all remaining tokens at state 2, are related to the to-be-explained statement.
|
||||
$idxOfLastParsedToken = $list->count - 1;
|
||||
$subList = new TokensList(array_slice($list->tokens, $list->idx));
|
||||
|
||||
$this->bodyParser = new Parser($subList);
|
||||
if (count($this->bodyParser->errors)) {
|
||||
foreach ($this->bodyParser->errors as $error) {
|
||||
$parser->errors[] = $error;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$list->idx = $idxOfLastParsedToken;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function build(): string
|
||||
{
|
||||
$str = $this->statementAlias;
|
||||
|
||||
if (count($this->options->options)) {
|
||||
$str .= ' ';
|
||||
}
|
||||
|
||||
$str .= OptionsArray::build($this->options) . ' ';
|
||||
|
||||
if ($this->bodyParser) {
|
||||
foreach ($this->bodyParser->statements as $statement) {
|
||||
$str .= $statement->build();
|
||||
}
|
||||
} elseif ($this->connectionId) {
|
||||
$str .= 'FOR CONNECTION ' . $this->connectionId;
|
||||
} elseif ($this->explainedTable) {
|
||||
$str .= $this->explainedTable;
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
254
pma/vendor/phpmyadmin/sql-parser/src/Statements/InsertStatement.php
vendored
Normal file
254
pma/vendor/phpmyadmin/sql-parser/src/Statements/InsertStatement.php
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Array2d;
|
||||
use PhpMyAdmin\SqlParser\Components\ArrayObj;
|
||||
use PhpMyAdmin\SqlParser\Components\IntoKeyword;
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Components\SetOperation;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
use function count;
|
||||
use function strlen;
|
||||
use function trim;
|
||||
|
||||
/**
|
||||
* `INSERT` statement.
|
||||
*
|
||||
* INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
|
||||
* [INTO] tbl_name
|
||||
* [PARTITION (partition_name,...)]
|
||||
* [(col_name,...)]
|
||||
* {VALUES | VALUE} ({expr | DEFAULT},...),(...),...
|
||||
* [ ON DUPLICATE KEY UPDATE
|
||||
* col_name=expr
|
||||
* [, col_name=expr] ... ]
|
||||
*
|
||||
* or
|
||||
*
|
||||
* INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
|
||||
* [INTO] tbl_name
|
||||
* [PARTITION (partition_name,...)]
|
||||
* SET col_name={expr | DEFAULT}, ...
|
||||
* [ ON DUPLICATE KEY UPDATE
|
||||
* col_name=expr
|
||||
* [, col_name=expr] ... ]
|
||||
*
|
||||
* or
|
||||
*
|
||||
* INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
|
||||
* [INTO] tbl_name
|
||||
* [PARTITION (partition_name,...)]
|
||||
* [(col_name,...)]
|
||||
* SELECT ...
|
||||
* [ ON DUPLICATE KEY UPDATE
|
||||
* col_name=expr
|
||||
* [, col_name=expr] ... ]
|
||||
*/
|
||||
class InsertStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Options for `INSERT` statements.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'LOW_PRIORITY' => 1,
|
||||
'DELAYED' => 2,
|
||||
'HIGH_PRIORITY' => 3,
|
||||
'IGNORE' => 4,
|
||||
];
|
||||
|
||||
/**
|
||||
* Tables used as target for this statement.
|
||||
*
|
||||
* @var IntoKeyword|null
|
||||
*/
|
||||
public $into;
|
||||
|
||||
/**
|
||||
* Values to be inserted.
|
||||
*
|
||||
* @var ArrayObj[]|null
|
||||
*/
|
||||
public $values;
|
||||
|
||||
/**
|
||||
* If SET clause is present
|
||||
* holds the SetOperation.
|
||||
*
|
||||
* @var SetOperation[]|null
|
||||
*/
|
||||
public $set;
|
||||
|
||||
/**
|
||||
* If SELECT clause is present
|
||||
* holds the SelectStatement.
|
||||
*
|
||||
* @var SelectStatement|null
|
||||
*/
|
||||
public $select;
|
||||
|
||||
/**
|
||||
* If WITH CTE is present
|
||||
* holds the WithStatement.
|
||||
*
|
||||
* @var WithStatement|null
|
||||
*/
|
||||
public $with;
|
||||
|
||||
/**
|
||||
* If ON DUPLICATE KEY UPDATE clause is present
|
||||
* holds the SetOperation.
|
||||
*
|
||||
* @var SetOperation[]|null
|
||||
*/
|
||||
public $onDuplicateSet;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$ret = 'INSERT ' . $this->options;
|
||||
$ret = trim($ret) . ' INTO ' . $this->into;
|
||||
|
||||
if ($this->values !== null && count($this->values) > 0) {
|
||||
$ret .= ' VALUES ' . Array2d::build($this->values);
|
||||
} elseif ($this->set !== null && count($this->set) > 0) {
|
||||
$ret .= ' SET ' . SetOperation::build($this->set);
|
||||
} elseif ($this->select !== null && strlen((string) $this->select) > 0) {
|
||||
$ret .= ' ' . $this->select->build();
|
||||
}
|
||||
|
||||
if ($this->onDuplicateSet !== null && count($this->onDuplicateSet) > 0) {
|
||||
$ret .= ' ON DUPLICATE KEY UPDATE ' . SetOperation::build($this->onDuplicateSet);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Parser $parser the instance that requests parsing
|
||||
* @param TokensList $list the list of tokens to be parsed
|
||||
*/
|
||||
public function parse(Parser $parser, TokensList $list)
|
||||
{
|
||||
++$list->idx; // Skipping `INSERT`.
|
||||
|
||||
// parse any options if provided
|
||||
$this->options = OptionsArray::parse($parser, $list, static::$OPTIONS);
|
||||
++$list->idx;
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 ---------------------------------[ INTO ]----------------------------------> 1
|
||||
*
|
||||
* 1 -------------------------[ VALUES/VALUE/SET/SELECT ]-----------------------> 2
|
||||
*
|
||||
* 2 -------------------------[ ON DUPLICATE KEY UPDATE ]-----------------------> 3
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
/**
|
||||
* For keeping track of semi-states on encountering
|
||||
* ON DUPLICATE KEY UPDATE ...
|
||||
*/
|
||||
$miniState = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
// End of statement.
|
||||
if ($token->type === Token::TYPE_DELIMITER) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Skipping whitespaces and comments.
|
||||
if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
if ($token->type === Token::TYPE_KEYWORD && $token->keyword !== 'INTO') {
|
||||
$parser->error('Unexpected keyword.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
++$list->idx;
|
||||
$this->into = IntoKeyword::parse(
|
||||
$parser,
|
||||
$list,
|
||||
['fromInsert' => true]
|
||||
);
|
||||
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
if ($token->type !== Token::TYPE_KEYWORD) {
|
||||
$parser->error('Unexpected token.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($token->keyword === 'VALUE' || $token->keyword === 'VALUES') {
|
||||
++$list->idx; // skip VALUES
|
||||
|
||||
$this->values = Array2d::parse($parser, $list);
|
||||
} elseif ($token->keyword === 'SET') {
|
||||
++$list->idx; // skip SET
|
||||
|
||||
$this->set = SetOperation::parse($parser, $list);
|
||||
} elseif ($token->keyword === 'SELECT') {
|
||||
$this->select = new SelectStatement($parser, $list);
|
||||
} elseif ($token->keyword === 'WITH') {
|
||||
$this->with = new WithStatement($parser, $list);
|
||||
} else {
|
||||
$parser->error('Unexpected keyword.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
$state = 2;
|
||||
$miniState = 1;
|
||||
} elseif ($state === 2) {
|
||||
$lastCount = $miniState;
|
||||
|
||||
if ($miniState === 1 && $token->keyword === 'ON') {
|
||||
++$miniState;
|
||||
} elseif ($miniState === 2 && $token->keyword === 'DUPLICATE') {
|
||||
++$miniState;
|
||||
} elseif ($miniState === 3 && $token->keyword === 'KEY') {
|
||||
++$miniState;
|
||||
} elseif ($miniState === 4 && $token->keyword === 'UPDATE') {
|
||||
++$miniState;
|
||||
}
|
||||
|
||||
if ($lastCount === $miniState) {
|
||||
$parser->error('Unexpected token.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($miniState === 5) {
|
||||
++$list->idx;
|
||||
$this->onDuplicateSet = SetOperation::parse($parser, $list);
|
||||
$state = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
}
|
||||
}
|
||||
410
pma/vendor/phpmyadmin/sql-parser/src/Statements/LoadStatement.php
vendored
Normal file
410
pma/vendor/phpmyadmin/sql-parser/src/Statements/LoadStatement.php
vendored
Normal file
@@ -0,0 +1,410 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\ArrayObj;
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Components\ExpressionArray;
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Components\SetOperation;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
use function count;
|
||||
use function strlen;
|
||||
use function trim;
|
||||
|
||||
/**
|
||||
* `LOAD` statement.
|
||||
*
|
||||
* LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'file_name'
|
||||
* [REPLACE | IGNORE]
|
||||
* INTO TABLE tbl_name
|
||||
* [PARTITION (partition_name,...)]
|
||||
* [CHARACTER SET charset_name]
|
||||
* [{FIELDS | COLUMNS}
|
||||
* [TERMINATED BY 'string']
|
||||
* [[OPTIONALLY] ENCLOSED BY 'char']
|
||||
* [ESCAPED BY 'char']
|
||||
* ]
|
||||
* [LINES
|
||||
* [STARTING BY 'string']
|
||||
* [TERMINATED BY 'string']
|
||||
* ]
|
||||
* [IGNORE number {LINES | ROWS}]
|
||||
* [(col_name_or_user_var,...)]
|
||||
* [SET col_name = expr,...]
|
||||
*/
|
||||
class LoadStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Options for `LOAD` statements and their slot ID.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'LOW_PRIORITY' => 1,
|
||||
'CONCURRENT' => 1,
|
||||
'LOCAL' => 2,
|
||||
];
|
||||
|
||||
/**
|
||||
* FIELDS/COLUMNS Options for `LOAD DATA...INFILE` statements.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $FIELDS_OPTIONS = [
|
||||
'TERMINATED BY' => [
|
||||
1,
|
||||
'expr',
|
||||
],
|
||||
'OPTIONALLY' => 2,
|
||||
'ENCLOSED BY' => [
|
||||
3,
|
||||
'expr',
|
||||
],
|
||||
'ESCAPED BY' => [
|
||||
4,
|
||||
'expr',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* LINES Options for `LOAD DATA...INFILE` statements.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $LINES_OPTIONS = [
|
||||
'STARTING BY' => [
|
||||
1,
|
||||
'expr',
|
||||
],
|
||||
'TERMINATED BY' => [
|
||||
2,
|
||||
'expr',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* File name being used to load data.
|
||||
*
|
||||
* @var Expression|null
|
||||
*/
|
||||
public $file_name;
|
||||
|
||||
/**
|
||||
* Table used as destination for this statement.
|
||||
*
|
||||
* @var Expression|null
|
||||
*/
|
||||
public $table;
|
||||
|
||||
/**
|
||||
* Partitions used as source for this statement.
|
||||
*
|
||||
* @var ArrayObj|null
|
||||
*/
|
||||
public $partition;
|
||||
|
||||
/**
|
||||
* Character set used in this statement.
|
||||
*
|
||||
* @var Expression|null
|
||||
*/
|
||||
public $charset_name;
|
||||
|
||||
/**
|
||||
* Options for FIELDS/COLUMNS keyword.
|
||||
*
|
||||
* @see static::$FIELDS_OPTIONS
|
||||
*
|
||||
* @var OptionsArray|null
|
||||
*/
|
||||
public $fields_options;
|
||||
|
||||
/**
|
||||
* Whether to use `FIELDS` or `COLUMNS` while building.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $fields_keyword;
|
||||
|
||||
/**
|
||||
* Options for OPTIONS keyword.
|
||||
*
|
||||
* @see static::$LINES_OPTIONS
|
||||
*
|
||||
* @var OptionsArray|null
|
||||
*/
|
||||
public $lines_options;
|
||||
|
||||
/**
|
||||
* Column names or user variables.
|
||||
*
|
||||
* @var Expression[]|null
|
||||
*/
|
||||
public $col_name_or_user_var;
|
||||
|
||||
/**
|
||||
* SET clause's updated values(optional).
|
||||
*
|
||||
* @var SetOperation[]|null
|
||||
*/
|
||||
public $set;
|
||||
|
||||
/**
|
||||
* Ignore 'number' LINES/ROWS.
|
||||
*
|
||||
* @var Expression|null
|
||||
*/
|
||||
public $ignore_number;
|
||||
|
||||
/**
|
||||
* REPLACE/IGNORE Keyword.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $replace_ignore;
|
||||
|
||||
/**
|
||||
* LINES/ROWS Keyword.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $lines_rows;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$ret = 'LOAD DATA ' . $this->options
|
||||
. ' INFILE ' . $this->file_name;
|
||||
|
||||
if ($this->replace_ignore !== null) {
|
||||
$ret .= ' ' . trim($this->replace_ignore);
|
||||
}
|
||||
|
||||
$ret .= ' INTO TABLE ' . $this->table;
|
||||
|
||||
if ($this->partition !== null && strlen((string) $this->partition) > 0) {
|
||||
$ret .= ' PARTITION ' . ArrayObj::build($this->partition);
|
||||
}
|
||||
|
||||
if ($this->charset_name !== null) {
|
||||
$ret .= ' CHARACTER SET ' . $this->charset_name;
|
||||
}
|
||||
|
||||
if ($this->fields_keyword !== null) {
|
||||
$ret .= ' ' . $this->fields_keyword . ' ' . $this->fields_options;
|
||||
}
|
||||
|
||||
if ($this->lines_options !== null && strlen((string) $this->lines_options) > 0) {
|
||||
$ret .= ' LINES ' . $this->lines_options;
|
||||
}
|
||||
|
||||
if ($this->ignore_number !== null) {
|
||||
$ret .= ' IGNORE ' . $this->ignore_number . ' ' . $this->lines_rows;
|
||||
}
|
||||
|
||||
if ($this->col_name_or_user_var !== null && count($this->col_name_or_user_var) > 0) {
|
||||
$ret .= ' ' . ExpressionArray::build($this->col_name_or_user_var);
|
||||
}
|
||||
|
||||
if ($this->set !== null && count($this->set) > 0) {
|
||||
$ret .= ' SET ' . SetOperation::build($this->set);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Parser $parser the instance that requests parsing
|
||||
* @param TokensList $list the list of tokens to be parsed
|
||||
*/
|
||||
public function parse(Parser $parser, TokensList $list)
|
||||
{
|
||||
++$list->idx; // Skipping `LOAD DATA`.
|
||||
|
||||
// parse any options if provided
|
||||
$this->options = OptionsArray::parse($parser, $list, static::$OPTIONS);
|
||||
++$list->idx;
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
// End of statement.
|
||||
if ($token->type === Token::TYPE_DELIMITER) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Skipping whitespaces and comments.
|
||||
if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
if ($token->type === Token::TYPE_KEYWORD && $token->keyword !== 'INFILE') {
|
||||
$parser->error('Unexpected keyword.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($token->type !== Token::TYPE_KEYWORD) {
|
||||
$parser->error('Unexpected token.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
++$list->idx;
|
||||
$this->file_name = Expression::parse(
|
||||
$parser,
|
||||
$list,
|
||||
['parseField' => 'file']
|
||||
);
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
if ($token->type === Token::TYPE_KEYWORD) {
|
||||
if ($token->keyword === 'REPLACE' || $token->keyword === 'IGNORE') {
|
||||
$this->replace_ignore = trim($token->keyword);
|
||||
} elseif ($token->keyword === 'INTO') {
|
||||
$state = 2;
|
||||
}
|
||||
}
|
||||
} elseif ($state === 2) {
|
||||
if ($token->type !== Token::TYPE_KEYWORD || $token->keyword !== 'TABLE') {
|
||||
$parser->error('Unexpected token.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
++$list->idx;
|
||||
$this->table = Expression::parse($parser, $list, ['parseField' => 'table']);
|
||||
$state = 3;
|
||||
} elseif ($state >= 3 && $state <= 7) {
|
||||
if ($token->type === Token::TYPE_KEYWORD) {
|
||||
$newState = $this->parseKeywordsAccordingToState($parser, $list, $state);
|
||||
if ($newState === $state) {
|
||||
// Avoid infinite loop
|
||||
break;
|
||||
}
|
||||
} elseif ($token->type === Token::TYPE_OPERATOR && $token->token === '(') {
|
||||
$this->col_name_or_user_var
|
||||
= ExpressionArray::parse($parser, $list);
|
||||
$state = 7;
|
||||
} else {
|
||||
$parser->error('Unexpected token.', $token);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Parser $parser The parser
|
||||
* @param TokensList $list A token list
|
||||
* @param string $keyword The keyword
|
||||
*/
|
||||
public function parseFileOptions(Parser $parser, TokensList $list, $keyword = 'FIELDS'): void
|
||||
{
|
||||
++$list->idx;
|
||||
|
||||
if ($keyword === 'FIELDS' || $keyword === 'COLUMNS') {
|
||||
// parse field options
|
||||
$this->fields_options = OptionsArray::parse($parser, $list, static::$FIELDS_OPTIONS);
|
||||
|
||||
$this->fields_keyword = $keyword;
|
||||
} else {
|
||||
// parse line options
|
||||
$this->lines_options = OptionsArray::parse($parser, $list, static::$LINES_OPTIONS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Parser $parser
|
||||
* @param TokensList $list
|
||||
* @param int $state
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function parseKeywordsAccordingToState($parser, $list, $state)
|
||||
{
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
switch ($state) {
|
||||
case 3:
|
||||
if ($token->keyword === 'PARTITION') {
|
||||
++$list->idx;
|
||||
$this->partition = ArrayObj::parse($parser, $list);
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
// no break
|
||||
case 4:
|
||||
if ($token->keyword === 'CHARACTER SET') {
|
||||
++$list->idx;
|
||||
$this->charset_name = Expression::parse($parser, $list);
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
// no break
|
||||
case 5:
|
||||
if ($token->keyword === 'FIELDS' || $token->keyword === 'COLUMNS' || $token->keyword === 'LINES') {
|
||||
$this->parseFileOptions($parser, $list, $token->value);
|
||||
|
||||
return 6;
|
||||
}
|
||||
|
||||
// no break
|
||||
case 6:
|
||||
if ($token->keyword === 'IGNORE') {
|
||||
++$list->idx;
|
||||
|
||||
$this->ignore_number = Expression::parse($parser, $list);
|
||||
$nextToken = $list->getNextOfType(Token::TYPE_KEYWORD);
|
||||
|
||||
if (
|
||||
$nextToken->type === Token::TYPE_KEYWORD
|
||||
&& (($nextToken->keyword === 'LINES')
|
||||
|| ($nextToken->keyword === 'ROWS'))
|
||||
) {
|
||||
$this->lines_rows = $nextToken->token;
|
||||
}
|
||||
|
||||
return 7;
|
||||
}
|
||||
|
||||
// no break
|
||||
case 7:
|
||||
if ($token->keyword === 'SET') {
|
||||
++$list->idx;
|
||||
$this->set = SetOperation::parse($parser, $list);
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
// no break
|
||||
default:
|
||||
}
|
||||
|
||||
return $state;
|
||||
}
|
||||
}
|
||||
131
pma/vendor/phpmyadmin/sql-parser/src/Statements/LockStatement.php
vendored
Normal file
131
pma/vendor/phpmyadmin/sql-parser/src/Statements/LockStatement.php
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\LockExpression;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
use function trim;
|
||||
|
||||
/**
|
||||
* `LOCK` statement.
|
||||
*/
|
||||
class LockStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Tables with their Lock expressions.
|
||||
*
|
||||
* @var LockExpression[]
|
||||
*/
|
||||
public $locked = [];
|
||||
|
||||
/**
|
||||
* Whether it's a LOCK statement
|
||||
* if false, it's an UNLOCK statement
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $isLock = true;
|
||||
|
||||
/**
|
||||
* @param Parser $parser the instance that requests parsing
|
||||
* @param TokensList $list the list of tokens to be parsed
|
||||
*/
|
||||
public function parse(Parser $parser, TokensList $list)
|
||||
{
|
||||
if ($list->tokens[$list->idx]->value === 'UNLOCK') {
|
||||
// this is in fact an UNLOCK statement
|
||||
$this->isLock = false;
|
||||
}
|
||||
|
||||
++$list->idx; // Skipping `LOCK`.
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 ---------------- [ TABLES ] -----------------> 1
|
||||
* 1 -------------- [ lock_expr ] ----------------> 2
|
||||
* 2 ------------------ [ , ] --------------------> 1
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
/**
|
||||
* Previous parsed token
|
||||
*/
|
||||
$prevToken = null;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
// End of statement.
|
||||
if ($token->type === Token::TYPE_DELIMITER) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Skipping whitespaces and comments.
|
||||
if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
if ($token->type === Token::TYPE_KEYWORD) {
|
||||
if ($token->keyword !== 'TABLES') {
|
||||
$parser->error('Unexpected keyword.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
$state = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
$parser->error('Unexpected token.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($state === 1) {
|
||||
if (! $this->isLock) {
|
||||
// UNLOCK statement should not have any more tokens
|
||||
$parser->error('Unexpected token.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
$this->locked[] = LockExpression::parse($parser, $list);
|
||||
$state = 2;
|
||||
} elseif ($state === 2) {
|
||||
if ($token->value === ',') {
|
||||
// move over to parsing next lock expression
|
||||
$state = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$prevToken = $token;
|
||||
}
|
||||
|
||||
if ($state === 2 || $prevToken === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parser->error('Unexpected end of LOCK statement.', $prevToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return trim(($this->isLock ? 'LOCK' : 'UNLOCK')
|
||||
. ' TABLES ' . LockExpression::build($this->locked));
|
||||
}
|
||||
}
|
||||
57
pma/vendor/phpmyadmin/sql-parser/src/Statements/MaintenanceStatement.php
vendored
Normal file
57
pma/vendor/phpmyadmin/sql-parser/src/Statements/MaintenanceStatement.php
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* Maintenance statement.
|
||||
*
|
||||
* They follow the syntax:
|
||||
* STMT [some options] tbl_name [, tbl_name] ... [some more options]
|
||||
*/
|
||||
class MaintenanceStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Tables maintained.
|
||||
*
|
||||
* @var Expression[]|null
|
||||
*/
|
||||
public $tables;
|
||||
|
||||
/**
|
||||
* Function called after the token was processed.
|
||||
*
|
||||
* Parses the additional options from the end.
|
||||
*
|
||||
* @param Parser $parser the instance that requests parsing
|
||||
* @param TokensList $list the list of tokens to be parsed
|
||||
* @param Token $token the token that is being parsed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function after(Parser $parser, TokensList $list, Token $token)
|
||||
{
|
||||
// [some options] is going to be parsed first.
|
||||
//
|
||||
// There is a parser specified in `Parser::$KEYWORD_PARSERS`
|
||||
// which parses the name of the tables.
|
||||
//
|
||||
// Finally, we parse here [some more options] and that's all.
|
||||
++$list->idx;
|
||||
$this->options->merge(
|
||||
OptionsArray::parse(
|
||||
$parser,
|
||||
$list,
|
||||
static::$OPTIONS
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
56
pma/vendor/phpmyadmin/sql-parser/src/Statements/NotImplementedStatement.php
vendored
Normal file
56
pma/vendor/phpmyadmin/sql-parser/src/Statements/NotImplementedStatement.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* Not implemented (yet) statements.
|
||||
*
|
||||
* The `after` function makes the parser jump straight to the first delimiter.
|
||||
*/
|
||||
class NotImplementedStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* The part of the statement that can't be parsed.
|
||||
*
|
||||
* @var Token[]
|
||||
*/
|
||||
public $unknown = [];
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
// Building the parsed part of the query (if any).
|
||||
$query = parent::build() . ' ';
|
||||
|
||||
// Rebuilding the unknown part from tokens.
|
||||
foreach ($this->unknown as $token) {
|
||||
$query .= $token->token;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Parser $parser the instance that requests parsing
|
||||
* @param TokensList $list the list of tokens to be parsed
|
||||
*/
|
||||
public function parse(Parser $parser, TokensList $list)
|
||||
{
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
if ($list->tokens[$list->idx]->type === Token::TYPE_DELIMITER) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->unknown[] = $list->tokens[$list->idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
37
pma/vendor/phpmyadmin/sql-parser/src/Statements/OptimizeStatement.php
vendored
Normal file
37
pma/vendor/phpmyadmin/sql-parser/src/Statements/OptimizeStatement.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
|
||||
/**
|
||||
* `OPTIMIZE` statement.
|
||||
*
|
||||
* OPTIMIZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE
|
||||
* tbl_name [, tbl_name] ...
|
||||
*/
|
||||
class OptimizeStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Options of this statement.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'TABLE' => 1,
|
||||
|
||||
'NO_WRITE_TO_BINLOG' => 2,
|
||||
'LOCAL' => 3,
|
||||
];
|
||||
|
||||
/**
|
||||
* Optimized tables.
|
||||
*
|
||||
* @var Expression[]|null
|
||||
*/
|
||||
public $tables;
|
||||
}
|
||||
145
pma/vendor/phpmyadmin/sql-parser/src/Statements/PurgeStatement.php
vendored
Normal file
145
pma/vendor/phpmyadmin/sql-parser/src/Statements/PurgeStatement.php
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
use function in_array;
|
||||
use function trim;
|
||||
|
||||
/**
|
||||
* `PURGE` statement.
|
||||
*
|
||||
* PURGE { BINARY | MASTER } LOGS
|
||||
* { TO 'log_name' | BEFORE datetime_expr }
|
||||
*/
|
||||
class PurgeStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* The type of logs
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $log_type;
|
||||
|
||||
/**
|
||||
* The end option of this query.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $end_option;
|
||||
|
||||
/**
|
||||
* The end expr of this query.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
public $end_expr;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$ret = 'PURGE ' . $this->log_type . ' LOGS '
|
||||
. ($this->end_option !== null ? ($this->end_option . ' ' . $this->end_expr) : '');
|
||||
|
||||
return trim($ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Parser $parser the instance that requests parsing
|
||||
* @param TokensList $list the list of tokens to be parsed
|
||||
*/
|
||||
public function parse(Parser $parser, TokensList $list)
|
||||
{
|
||||
++$list->idx; // Skipping `PURGE`.
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
$prevToken = null;
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
// End of statement.
|
||||
if ($token->type === Token::TYPE_DELIMITER) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Skipping whitespaces and comments.
|
||||
if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($state) {
|
||||
case 0:
|
||||
// parse `{ BINARY | MASTER }`
|
||||
$this->log_type = self::parseExpectedKeyword($parser, $token, ['BINARY', 'MASTER']);
|
||||
break;
|
||||
case 1:
|
||||
// parse `LOGS`
|
||||
self::parseExpectedKeyword($parser, $token, ['LOGS']);
|
||||
break;
|
||||
case 2:
|
||||
// parse `{ TO | BEFORE }`
|
||||
$this->end_option = self::parseExpectedKeyword($parser, $token, ['TO', 'BEFORE']);
|
||||
break;
|
||||
case 3:
|
||||
// parse `expr`
|
||||
$this->end_expr = Expression::parse($parser, $list, []);
|
||||
break;
|
||||
default:
|
||||
$parser->error('Unexpected token.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
$state++;
|
||||
$prevToken = $token;
|
||||
}
|
||||
|
||||
// Only one possible end state
|
||||
if ($state === 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parser->error('Unexpected token.', $prevToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse expected keyword (or throw relevant error)
|
||||
*
|
||||
* @param Parser $parser the instance that requests parsing
|
||||
* @param Token $token token to be parsed
|
||||
* @param string[] $expectedKeywords array of possibly expected keywords at this point
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
private static function parseExpectedKeyword($parser, $token, $expectedKeywords)
|
||||
{
|
||||
if ($token->type === Token::TYPE_KEYWORD) {
|
||||
if (in_array($token->keyword, $expectedKeywords)) {
|
||||
return $token->keyword;
|
||||
}
|
||||
|
||||
$parser->error('Unexpected keyword', $token);
|
||||
} else {
|
||||
$parser->error('Unexpected token.', $token);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
56
pma/vendor/phpmyadmin/sql-parser/src/Statements/RenameStatement.php
vendored
Normal file
56
pma/vendor/phpmyadmin/sql-parser/src/Statements/RenameStatement.php
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\RenameOperation;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* `RENAME` statement.
|
||||
*
|
||||
* RENAME TABLE tbl_name TO new_tbl_name
|
||||
* [, tbl_name2 TO new_tbl_name2] ...
|
||||
*/
|
||||
class RenameStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* The old and new names of the tables.
|
||||
*
|
||||
* @var RenameOperation[]|null
|
||||
*/
|
||||
public $renames;
|
||||
|
||||
/**
|
||||
* Function called before the token is processed.
|
||||
*
|
||||
* Skips the `TABLE` keyword after `RENAME`.
|
||||
*
|
||||
* @param Parser $parser the instance that requests parsing
|
||||
* @param TokensList $list the list of tokens to be parsed
|
||||
* @param Token $token the token that is being parsed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function before(Parser $parser, TokensList $list, Token $token)
|
||||
{
|
||||
if (($token->type !== Token::TYPE_KEYWORD) || ($token->keyword !== 'RENAME')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Checking if it is the beginning of the query.
|
||||
$list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'TABLE');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return 'RENAME TABLE ' . RenameOperation::build($this->renames);
|
||||
}
|
||||
}
|
||||
32
pma/vendor/phpmyadmin/sql-parser/src/Statements/RepairStatement.php
vendored
Normal file
32
pma/vendor/phpmyadmin/sql-parser/src/Statements/RepairStatement.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
/**
|
||||
* `REPAIR` statement.
|
||||
*
|
||||
* REPAIR [NO_WRITE_TO_BINLOG | LOCAL] TABLE
|
||||
* tbl_name [, tbl_name] ...
|
||||
* [QUICK] [EXTENDED] [USE_FRM]
|
||||
*/
|
||||
class RepairStatement extends MaintenanceStatement
|
||||
{
|
||||
/**
|
||||
* Options of this statement.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'TABLE' => 1,
|
||||
|
||||
'NO_WRITE_TO_BINLOG' => 2,
|
||||
'LOCAL' => 3,
|
||||
|
||||
'QUICK' => 4,
|
||||
'EXTENDED' => 5,
|
||||
'USE_FRM' => 6,
|
||||
];
|
||||
}
|
||||
188
pma/vendor/phpmyadmin/sql-parser/src/Statements/ReplaceStatement.php
vendored
Normal file
188
pma/vendor/phpmyadmin/sql-parser/src/Statements/ReplaceStatement.php
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Array2d;
|
||||
use PhpMyAdmin\SqlParser\Components\IntoKeyword;
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Components\SetOperation;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
use function count;
|
||||
use function strlen;
|
||||
use function trim;
|
||||
|
||||
/**
|
||||
* `REPLACE` statement.
|
||||
*
|
||||
* REPLACE [LOW_PRIORITY | DELAYED]
|
||||
* [INTO] tbl_name [(col_name,...)]
|
||||
* {VALUES | VALUE} ({expr | DEFAULT},...),(...),...
|
||||
*
|
||||
* or
|
||||
*
|
||||
* REPLACE [LOW_PRIORITY | DELAYED]
|
||||
* [INTO] tbl_name
|
||||
* SET col_name={expr | DEFAULT}, ...
|
||||
*
|
||||
* or
|
||||
*
|
||||
* REPLACE [LOW_PRIORITY | DELAYED]
|
||||
* [INTO] tbl_name
|
||||
* [PARTITION (partition_name,...)]
|
||||
* [(col_name,...)]
|
||||
* SELECT ...
|
||||
*/
|
||||
class ReplaceStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Options for `REPLACE` statements and their slot ID.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'LOW_PRIORITY' => 1,
|
||||
'DELAYED' => 1,
|
||||
];
|
||||
|
||||
/**
|
||||
* Tables used as target for this statement.
|
||||
*
|
||||
* @var IntoKeyword|null
|
||||
*/
|
||||
public $into;
|
||||
|
||||
/**
|
||||
* Values to be replaced.
|
||||
*
|
||||
* @var Array2d|null
|
||||
*/
|
||||
public $values;
|
||||
|
||||
/**
|
||||
* If SET clause is present
|
||||
* holds the SetOperation.
|
||||
*
|
||||
* @var SetOperation[]|null
|
||||
*/
|
||||
public $set;
|
||||
|
||||
/**
|
||||
* If SELECT clause is present
|
||||
* holds the SelectStatement.
|
||||
*
|
||||
* @var SelectStatement|null
|
||||
*/
|
||||
public $select;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$ret = 'REPLACE ' . $this->options;
|
||||
$ret = trim($ret) . ' INTO ' . $this->into;
|
||||
|
||||
if ($this->values !== null && count($this->values) > 0) {
|
||||
$ret .= ' VALUES ' . Array2d::build($this->values);
|
||||
} elseif ($this->set !== null && count($this->set) > 0) {
|
||||
$ret .= ' SET ' . SetOperation::build($this->set);
|
||||
} elseif ($this->select !== null && strlen((string) $this->select) > 0) {
|
||||
$ret .= ' ' . $this->select->build();
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Parser $parser the instance that requests parsing
|
||||
* @param TokensList $list the list of tokens to be parsed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function parse(Parser $parser, TokensList $list)
|
||||
{
|
||||
++$list->idx; // Skipping `REPLACE`.
|
||||
|
||||
// parse any options if provided
|
||||
$this->options = OptionsArray::parse($parser, $list, static::$OPTIONS);
|
||||
|
||||
++$list->idx;
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 ---------------------------------[ INTO ]----------------------------------> 1
|
||||
*
|
||||
* 1 -------------------------[ VALUES/VALUE/SET/SELECT ]-----------------------> 2
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
// End of statement.
|
||||
if ($token->type === Token::TYPE_DELIMITER) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Skipping whitespaces and comments.
|
||||
if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
if ($token->type === Token::TYPE_KEYWORD && $token->keyword !== 'INTO') {
|
||||
$parser->error('Unexpected keyword.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
++$list->idx;
|
||||
$this->into = IntoKeyword::parse(
|
||||
$parser,
|
||||
$list,
|
||||
['fromReplace' => true]
|
||||
);
|
||||
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
if ($token->type !== Token::TYPE_KEYWORD) {
|
||||
$parser->error('Unexpected token.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($token->keyword === 'VALUE' || $token->keyword === 'VALUES') {
|
||||
++$list->idx; // skip VALUES
|
||||
|
||||
$this->values = Array2d::parse($parser, $list);
|
||||
} elseif ($token->keyword === 'SET') {
|
||||
++$list->idx; // skip SET
|
||||
|
||||
$this->set = SetOperation::parse($parser, $list);
|
||||
} elseif ($token->keyword === 'SELECT') {
|
||||
$this->select = new SelectStatement($parser, $list);
|
||||
} else {
|
||||
$parser->error('Unexpected keyword.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
$state = 2;
|
||||
}
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
}
|
||||
}
|
||||
28
pma/vendor/phpmyadmin/sql-parser/src/Statements/RestoreStatement.php
vendored
Normal file
28
pma/vendor/phpmyadmin/sql-parser/src/Statements/RestoreStatement.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
/**
|
||||
* `RESTORE` statement.
|
||||
*
|
||||
* RESTORE TABLE tbl_name [, tbl_name] ... FROM '/path/to/backup/directory'
|
||||
*/
|
||||
class RestoreStatement extends MaintenanceStatement
|
||||
{
|
||||
/**
|
||||
* Options of this statement.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'TABLE' => 1,
|
||||
|
||||
'FROM' => [
|
||||
2,
|
||||
'var',
|
||||
],
|
||||
];
|
||||
}
|
||||
353
pma/vendor/phpmyadmin/sql-parser/src/Statements/SelectStatement.php
vendored
Normal file
353
pma/vendor/phpmyadmin/sql-parser/src/Statements/SelectStatement.php
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\ArrayObj;
|
||||
use PhpMyAdmin\SqlParser\Components\Condition;
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Components\FunctionCall;
|
||||
use PhpMyAdmin\SqlParser\Components\GroupKeyword;
|
||||
use PhpMyAdmin\SqlParser\Components\IndexHint;
|
||||
use PhpMyAdmin\SqlParser\Components\IntoKeyword;
|
||||
use PhpMyAdmin\SqlParser\Components\JoinKeyword;
|
||||
use PhpMyAdmin\SqlParser\Components\Limit;
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Components\OrderKeyword;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
|
||||
/**
|
||||
* `SELECT` statement.
|
||||
*
|
||||
* SELECT
|
||||
* [ALL | DISTINCT | DISTINCTROW ]
|
||||
* [HIGH_PRIORITY]
|
||||
* [MAX_STATEMENT_TIME = N]
|
||||
* [STRAIGHT_JOIN]
|
||||
* [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
|
||||
* [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
|
||||
* select_expr [, select_expr ...]
|
||||
* [FROM table_references
|
||||
* [PARTITION partition_list]
|
||||
* [WHERE where_condition]
|
||||
* [GROUP BY {col_name | expr | position}
|
||||
* [ASC | DESC], ... [WITH ROLLUP]]
|
||||
* [HAVING where_condition]
|
||||
* [ORDER BY {col_name | expr | position}
|
||||
* [ASC | DESC], ...]
|
||||
* [LIMIT {[offset,] row_count | row_count OFFSET offset}]
|
||||
* [PROCEDURE procedure_name(argument_list)]
|
||||
* [INTO OUTFILE 'file_name'
|
||||
* [CHARACTER SET charset_name]
|
||||
* export_options
|
||||
* | INTO DUMPFILE 'file_name'
|
||||
* | INTO var_name [, var_name]]
|
||||
* [FOR UPDATE | LOCK IN SHARE MODE]]
|
||||
*/
|
||||
class SelectStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Options for `SELECT` statements and their slot ID.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'ALL' => 1,
|
||||
'DISTINCT' => 1,
|
||||
'DISTINCTROW' => 1,
|
||||
'HIGH_PRIORITY' => 2,
|
||||
'MAX_STATEMENT_TIME' => [
|
||||
3,
|
||||
'var=',
|
||||
],
|
||||
'STRAIGHT_JOIN' => 4,
|
||||
'SQL_SMALL_RESULT' => 5,
|
||||
'SQL_BIG_RESULT' => 6,
|
||||
'SQL_BUFFER_RESULT' => 7,
|
||||
'SQL_CACHE' => 8,
|
||||
'SQL_NO_CACHE' => 8,
|
||||
'SQL_CALC_FOUND_ROWS' => 9,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $END_OPTIONS = [
|
||||
'FOR UPDATE' => 1,
|
||||
'LOCK IN SHARE MODE' => 1,
|
||||
];
|
||||
|
||||
/**
|
||||
* The clauses of this statement, in order.
|
||||
*
|
||||
* @see Statement::$CLAUSES
|
||||
*
|
||||
* @var array<string, array<int, int|string>>
|
||||
* @psalm-var array<string, array{non-empty-string, (1|2|3)}>
|
||||
*/
|
||||
public static $CLAUSES = [
|
||||
'SELECT' => [
|
||||
'SELECT',
|
||||
2,
|
||||
],
|
||||
// Used for options.
|
||||
'_OPTIONS' => [
|
||||
'_OPTIONS',
|
||||
1,
|
||||
],
|
||||
// Used for selected expressions.
|
||||
'_SELECT' => [
|
||||
'SELECT',
|
||||
1,
|
||||
],
|
||||
'INTO' => [
|
||||
'INTO',
|
||||
3,
|
||||
],
|
||||
'FROM' => [
|
||||
'FROM',
|
||||
3,
|
||||
],
|
||||
'FORCE' => [
|
||||
'FORCE',
|
||||
1,
|
||||
],
|
||||
'USE' => [
|
||||
'USE',
|
||||
1,
|
||||
],
|
||||
'IGNORE' => [
|
||||
'IGNORE',
|
||||
3,
|
||||
],
|
||||
'PARTITION' => [
|
||||
'PARTITION',
|
||||
3,
|
||||
],
|
||||
|
||||
'JOIN' => [
|
||||
'JOIN',
|
||||
1,
|
||||
],
|
||||
'FULL JOIN' => [
|
||||
'FULL JOIN',
|
||||
1,
|
||||
],
|
||||
'INNER JOIN' => [
|
||||
'INNER JOIN',
|
||||
1,
|
||||
],
|
||||
'LEFT JOIN' => [
|
||||
'LEFT JOIN',
|
||||
1,
|
||||
],
|
||||
'LEFT OUTER JOIN' => [
|
||||
'LEFT OUTER JOIN',
|
||||
1,
|
||||
],
|
||||
'RIGHT JOIN' => [
|
||||
'RIGHT JOIN',
|
||||
1,
|
||||
],
|
||||
'RIGHT OUTER JOIN' => [
|
||||
'RIGHT OUTER JOIN',
|
||||
1,
|
||||
],
|
||||
'NATURAL JOIN' => [
|
||||
'NATURAL JOIN',
|
||||
1,
|
||||
],
|
||||
'NATURAL LEFT JOIN' => [
|
||||
'NATURAL LEFT JOIN',
|
||||
1,
|
||||
],
|
||||
'NATURAL RIGHT JOIN' => [
|
||||
'NATURAL RIGHT JOIN',
|
||||
1,
|
||||
],
|
||||
'NATURAL LEFT OUTER JOIN' => [
|
||||
'NATURAL LEFT OUTER JOIN',
|
||||
1,
|
||||
],
|
||||
'NATURAL RIGHT OUTER JOIN' => [
|
||||
'NATURAL RIGHT JOIN',
|
||||
1,
|
||||
],
|
||||
|
||||
'WHERE' => [
|
||||
'WHERE',
|
||||
3,
|
||||
],
|
||||
'GROUP BY' => [
|
||||
'GROUP BY',
|
||||
3,
|
||||
],
|
||||
'HAVING' => [
|
||||
'HAVING',
|
||||
3,
|
||||
],
|
||||
'ORDER BY' => [
|
||||
'ORDER BY',
|
||||
3,
|
||||
],
|
||||
'LIMIT' => [
|
||||
'LIMIT',
|
||||
3,
|
||||
],
|
||||
'PROCEDURE' => [
|
||||
'PROCEDURE',
|
||||
3,
|
||||
],
|
||||
'UNION' => [
|
||||
'UNION',
|
||||
1,
|
||||
],
|
||||
'EXCEPT' => [
|
||||
'EXCEPT',
|
||||
1,
|
||||
],
|
||||
'INTERSECT' => [
|
||||
'INTERSECT',
|
||||
1,
|
||||
],
|
||||
'_END_OPTIONS' => [
|
||||
'_END_OPTIONS',
|
||||
1,
|
||||
],
|
||||
// These are available only when `UNION` is present.
|
||||
// 'ORDER BY' => ['ORDER BY', 3],
|
||||
// 'LIMIT' => ['LIMIT', 3],
|
||||
];
|
||||
|
||||
/**
|
||||
* Expressions that are being selected by this statement.
|
||||
*
|
||||
* @var Expression[]
|
||||
*/
|
||||
public $expr = [];
|
||||
|
||||
/**
|
||||
* Tables used as sources for this statement.
|
||||
*
|
||||
* @var Expression[]
|
||||
*/
|
||||
public $from = [];
|
||||
|
||||
/**
|
||||
* Index hints
|
||||
*
|
||||
* @var IndexHint[]|null
|
||||
*/
|
||||
public $index_hints;
|
||||
|
||||
/**
|
||||
* Partitions used as source for this statement.
|
||||
*
|
||||
* @var ArrayObj|null
|
||||
*/
|
||||
public $partition;
|
||||
|
||||
/**
|
||||
* Conditions used for filtering each row of the result set.
|
||||
*
|
||||
* @var Condition[]|null
|
||||
*/
|
||||
public $where;
|
||||
|
||||
/**
|
||||
* Conditions used for grouping the result set.
|
||||
*
|
||||
* @var GroupKeyword[]|null
|
||||
*/
|
||||
public $group;
|
||||
|
||||
/**
|
||||
* Conditions used for filtering the result set.
|
||||
*
|
||||
* @var Condition[]|null
|
||||
*/
|
||||
public $having;
|
||||
|
||||
/**
|
||||
* Specifies the order of the rows in the result set.
|
||||
*
|
||||
* @var OrderKeyword[]|null
|
||||
*/
|
||||
public $order;
|
||||
|
||||
/**
|
||||
* Conditions used for limiting the size of the result set.
|
||||
*
|
||||
* @var Limit|null
|
||||
*/
|
||||
public $limit;
|
||||
|
||||
/**
|
||||
* Procedure that should process the data in the result set.
|
||||
*
|
||||
* @var FunctionCall|null
|
||||
*/
|
||||
public $procedure;
|
||||
|
||||
/**
|
||||
* Destination of this result set.
|
||||
*
|
||||
* @var IntoKeyword|null
|
||||
*/
|
||||
public $into;
|
||||
|
||||
/**
|
||||
* Joins.
|
||||
*
|
||||
* @var JoinKeyword[]|null
|
||||
*/
|
||||
public $join;
|
||||
|
||||
/**
|
||||
* Unions.
|
||||
*
|
||||
* @var SelectStatement[]
|
||||
*/
|
||||
public $union = [];
|
||||
|
||||
/**
|
||||
* The end options of this query.
|
||||
*
|
||||
* @see static::$END_OPTIONS
|
||||
*
|
||||
* @var OptionsArray|null
|
||||
*/
|
||||
public $end_options;
|
||||
|
||||
/**
|
||||
* Gets the clauses of this statement.
|
||||
*
|
||||
* @return array<string, array<int, int|string>>
|
||||
* @psalm-return array<string, array{non-empty-string, (1|2|3)}>
|
||||
*/
|
||||
public function getClauses()
|
||||
{
|
||||
// This is a cheap fix for `SELECT` statements that contain `UNION`.
|
||||
// The `ORDER BY` and `LIMIT` clauses should be at the end of the
|
||||
// statement.
|
||||
if (! empty($this->union)) {
|
||||
$clauses = static::$CLAUSES;
|
||||
unset($clauses['ORDER BY'], $clauses['LIMIT']);
|
||||
$clauses['ORDER BY'] = [
|
||||
'ORDER BY',
|
||||
3,
|
||||
];
|
||||
$clauses['LIMIT'] = [
|
||||
'LIMIT',
|
||||
3,
|
||||
];
|
||||
|
||||
return $clauses;
|
||||
}
|
||||
|
||||
return static::$CLAUSES;
|
||||
}
|
||||
}
|
||||
116
pma/vendor/phpmyadmin/sql-parser/src/Statements/SetStatement.php
vendored
Normal file
116
pma/vendor/phpmyadmin/sql-parser/src/Statements/SetStatement.php
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Components\SetOperation;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
|
||||
use function trim;
|
||||
|
||||
/**
|
||||
* `SET` statement.
|
||||
*/
|
||||
class SetStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* The clauses of this statement, in order.
|
||||
*
|
||||
* @see Statement::$CLAUSES
|
||||
*
|
||||
* @var array<string, array<int, int|string>>
|
||||
* @psalm-var array<string, array{non-empty-string, (1|2|3)}>
|
||||
*/
|
||||
public static $CLAUSES = [
|
||||
'SET' => [
|
||||
'SET',
|
||||
3,
|
||||
],
|
||||
'_END_OPTIONS' => [
|
||||
'_END_OPTIONS',
|
||||
1,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Possible exceptions in SET statement.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'CHARSET' => [
|
||||
3,
|
||||
'var',
|
||||
],
|
||||
'CHARACTER SET' => [
|
||||
3,
|
||||
'var',
|
||||
],
|
||||
'NAMES' => [
|
||||
3,
|
||||
'var',
|
||||
],
|
||||
'PASSWORD' => [
|
||||
3,
|
||||
'expr',
|
||||
],
|
||||
'SESSION' => 3,
|
||||
'GLOBAL' => 3,
|
||||
'PERSIST' => 3,
|
||||
'PERSIST_ONLY' => 3,
|
||||
'@@SESSION' => 3,
|
||||
'@@GLOBAL' => 3,
|
||||
'@@PERSIST' => 3,
|
||||
'@@PERSIST_ONLY' => 3,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $END_OPTIONS = [
|
||||
'COLLATE' => [
|
||||
1,
|
||||
'var',
|
||||
],
|
||||
'DEFAULT' => 1,
|
||||
];
|
||||
|
||||
/**
|
||||
* Options used in current statement.
|
||||
*
|
||||
* @var OptionsArray[]|null
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* The end options of this query.
|
||||
*
|
||||
* @see static::$END_OPTIONS
|
||||
*
|
||||
* @var OptionsArray|null
|
||||
*/
|
||||
public $end_options;
|
||||
|
||||
/**
|
||||
* The updated values.
|
||||
*
|
||||
* @var SetOperation[]|null
|
||||
*/
|
||||
public $set;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$ret = 'SET ' . OptionsArray::build($this->options)
|
||||
. ' ' . SetOperation::build($this->set)
|
||||
. ' ' . OptionsArray::build($this->end_options);
|
||||
|
||||
return trim($ret);
|
||||
}
|
||||
}
|
||||
61
pma/vendor/phpmyadmin/sql-parser/src/Statements/ShowStatement.php
vendored
Normal file
61
pma/vendor/phpmyadmin/sql-parser/src/Statements/ShowStatement.php
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
/**
|
||||
* `SHOW` statement.
|
||||
*/
|
||||
class ShowStatement extends NotImplementedStatement
|
||||
{
|
||||
/**
|
||||
* Options of this statement.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'CREATE' => 1,
|
||||
'AUTHORS' => 2,
|
||||
'BINARY' => 2,
|
||||
'BINLOG' => 2,
|
||||
'CHARACTER' => 2,
|
||||
'CODE' => 2,
|
||||
'COLLATION' => 2,
|
||||
'COLUMNS' => 2,
|
||||
'CONTRIBUTORS' => 2,
|
||||
'DATABASE' => 2,
|
||||
'DATABASES' => 2,
|
||||
'ENGINE' => 2,
|
||||
'ENGINES' => 2,
|
||||
'ERRORS' => 2,
|
||||
'EVENT' => 2,
|
||||
'EVENTS' => 2,
|
||||
'FUNCTION' => 2,
|
||||
'GRANTS' => 2,
|
||||
'HOSTS' => 2,
|
||||
'INDEX' => 2,
|
||||
'INNODB' => 2,
|
||||
'LOGS' => 2,
|
||||
'MASTER' => 2,
|
||||
'OPEN' => 2,
|
||||
'PLUGINS' => 2,
|
||||
'PRIVILEGES' => 2,
|
||||
'PROCEDURE' => 2,
|
||||
'PROCESSLIST' => 2,
|
||||
'PROFILE' => 2,
|
||||
'PROFILES' => 2,
|
||||
'SCHEDULER' => 2,
|
||||
'SET' => 2,
|
||||
'SLAVE' => 2,
|
||||
'STATUS' => 2,
|
||||
'TABLE' => 2,
|
||||
'TABLES' => 2,
|
||||
'TRIGGER' => 2,
|
||||
'TRIGGERS' => 2,
|
||||
'VARIABLES' => 2,
|
||||
'VIEW' => 2,
|
||||
'WARNINGS' => 2,
|
||||
];
|
||||
}
|
||||
104
pma/vendor/phpmyadmin/sql-parser/src/Statements/TransactionStatement.php
vendored
Normal file
104
pma/vendor/phpmyadmin/sql-parser/src/Statements/TransactionStatement.php
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* Transaction statement.
|
||||
*/
|
||||
class TransactionStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* START TRANSACTION and BEGIN.
|
||||
*/
|
||||
public const TYPE_BEGIN = 1;
|
||||
|
||||
/**
|
||||
* COMMIT and ROLLBACK.
|
||||
*/
|
||||
public const TYPE_END = 2;
|
||||
|
||||
/**
|
||||
* The type of this query.
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* The list of statements in this transaction.
|
||||
*
|
||||
* @var Statement[]|null
|
||||
*/
|
||||
public $statements;
|
||||
|
||||
/**
|
||||
* The ending transaction statement which may be a `COMMIT` or a `ROLLBACK`.
|
||||
*
|
||||
* @var TransactionStatement|null
|
||||
*/
|
||||
public $end;
|
||||
|
||||
/**
|
||||
* Options for this query.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'START TRANSACTION' => 1,
|
||||
'BEGIN' => 1,
|
||||
'COMMIT' => 1,
|
||||
'ROLLBACK' => 1,
|
||||
'WITH CONSISTENT SNAPSHOT' => 2,
|
||||
'WORK' => 2,
|
||||
'AND NO CHAIN' => 3,
|
||||
'AND CHAIN' => 3,
|
||||
'RELEASE' => 4,
|
||||
'NO RELEASE' => 4,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param Parser $parser the instance that requests parsing
|
||||
* @param TokensList $list the list of tokens to be parsed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function parse(Parser $parser, TokensList $list)
|
||||
{
|
||||
parent::parse($parser, $list);
|
||||
|
||||
// Checks the type of this query.
|
||||
if ($this->options->has('START TRANSACTION') || $this->options->has('BEGIN')) {
|
||||
$this->type = self::TYPE_BEGIN;
|
||||
} elseif ($this->options->has('COMMIT') || $this->options->has('ROLLBACK')) {
|
||||
$this->type = self::TYPE_END;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$ret = OptionsArray::build($this->options);
|
||||
if ($this->type === self::TYPE_BEGIN) {
|
||||
foreach ($this->statements as $statement) {
|
||||
/*
|
||||
* @var SelectStatement $statement
|
||||
*/
|
||||
$ret .= ';' . $statement->build();
|
||||
}
|
||||
|
||||
$ret .= ';' . $this->end->build();
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
39
pma/vendor/phpmyadmin/sql-parser/src/Statements/TruncateStatement.php
vendored
Normal file
39
pma/vendor/phpmyadmin/sql-parser/src/Statements/TruncateStatement.php
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
|
||||
/**
|
||||
* `TRUNCATE` statement.
|
||||
*/
|
||||
class TruncateStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Options for `TRUNCATE` statements.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = ['TABLE' => 1];
|
||||
|
||||
/**
|
||||
* The name of the truncated table.
|
||||
*
|
||||
* @var Expression|null
|
||||
*/
|
||||
public $table;
|
||||
|
||||
/**
|
||||
* Special build method for truncate statement as Statement::build would return empty string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return 'TRUNCATE TABLE ' . $this->table . ';';
|
||||
}
|
||||
}
|
||||
117
pma/vendor/phpmyadmin/sql-parser/src/Statements/UpdateStatement.php
vendored
Normal file
117
pma/vendor/phpmyadmin/sql-parser/src/Statements/UpdateStatement.php
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Condition;
|
||||
use PhpMyAdmin\SqlParser\Components\Expression;
|
||||
use PhpMyAdmin\SqlParser\Components\Limit;
|
||||
use PhpMyAdmin\SqlParser\Components\OrderKeyword;
|
||||
use PhpMyAdmin\SqlParser\Components\SetOperation;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
|
||||
/**
|
||||
* `UPDATE` statement.
|
||||
*
|
||||
* UPDATE [LOW_PRIORITY] [IGNORE] table_reference
|
||||
* SET col_name1={expr1|DEFAULT} [, col_name2={expr2|DEFAULT}] ...
|
||||
* [WHERE where_condition]
|
||||
* [ORDER BY ...]
|
||||
* [LIMIT row_count]
|
||||
*
|
||||
* or
|
||||
*
|
||||
* UPDATE [LOW_PRIORITY] [IGNORE] table_references
|
||||
* SET col_name1={expr1|DEFAULT} [, col_name2={expr2|DEFAULT}] ...
|
||||
* [WHERE where_condition]
|
||||
*/
|
||||
class UpdateStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Options for `UPDATE` statements and their slot ID.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = [
|
||||
'LOW_PRIORITY' => 1,
|
||||
'IGNORE' => 2,
|
||||
];
|
||||
|
||||
/**
|
||||
* The clauses of this statement, in order.
|
||||
*
|
||||
* @see Statement::$CLAUSES
|
||||
*
|
||||
* @var array<string, array<int, int|string>>
|
||||
* @psalm-var array<string, array{non-empty-string, (1|2|3)}>
|
||||
*/
|
||||
public static $CLAUSES = [
|
||||
'UPDATE' => [
|
||||
'UPDATE',
|
||||
2,
|
||||
],
|
||||
// Used for options.
|
||||
'_OPTIONS' => [
|
||||
'_OPTIONS',
|
||||
1,
|
||||
],
|
||||
// Used for updated tables.
|
||||
'_UPDATE' => [
|
||||
'UPDATE',
|
||||
1,
|
||||
],
|
||||
'SET' => [
|
||||
'SET',
|
||||
3,
|
||||
],
|
||||
'WHERE' => [
|
||||
'WHERE',
|
||||
3,
|
||||
],
|
||||
'ORDER BY' => [
|
||||
'ORDER BY',
|
||||
3,
|
||||
],
|
||||
'LIMIT' => [
|
||||
'LIMIT',
|
||||
3,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Tables used as sources for this statement.
|
||||
*
|
||||
* @var Expression[]|null
|
||||
*/
|
||||
public $tables;
|
||||
|
||||
/**
|
||||
* The updated values.
|
||||
*
|
||||
* @var SetOperation[]|null
|
||||
*/
|
||||
public $set;
|
||||
|
||||
/**
|
||||
* Conditions used for filtering each row of the result set.
|
||||
*
|
||||
* @var Condition[]|null
|
||||
*/
|
||||
public $where;
|
||||
|
||||
/**
|
||||
* Specifies the order of the rows in the result set.
|
||||
*
|
||||
* @var OrderKeyword[]|null
|
||||
*/
|
||||
public $order;
|
||||
|
||||
/**
|
||||
* Conditions used for limiting the size of the result set.
|
||||
*
|
||||
* @var Limit|null
|
||||
*/
|
||||
public $limit;
|
||||
}
|
||||
337
pma/vendor/phpmyadmin/sql-parser/src/Statements/WithStatement.php
vendored
Normal file
337
pma/vendor/phpmyadmin/sql-parser/src/Statements/WithStatement.php
vendored
Normal file
@@ -0,0 +1,337 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\SqlParser\Statements;
|
||||
|
||||
use PhpMyAdmin\SqlParser\Components\Array2d;
|
||||
use PhpMyAdmin\SqlParser\Components\OptionsArray;
|
||||
use PhpMyAdmin\SqlParser\Components\WithKeyword;
|
||||
use PhpMyAdmin\SqlParser\Exceptions\ParserException;
|
||||
use PhpMyAdmin\SqlParser\Parser;
|
||||
use PhpMyAdmin\SqlParser\Statement;
|
||||
use PhpMyAdmin\SqlParser\Token;
|
||||
use PhpMyAdmin\SqlParser\TokensList;
|
||||
use PhpMyAdmin\SqlParser\Translator;
|
||||
|
||||
use function array_slice;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* `WITH` statement.
|
||||
|
||||
* WITH [RECURSIVE] query_name [ (column_name [,...]) ] AS (SELECT ...) [, ...]
|
||||
*/
|
||||
final class WithStatement extends Statement
|
||||
{
|
||||
/**
|
||||
* Options for `WITH` statements and their slot ID.
|
||||
*
|
||||
* @var array<string, int|array<int, int|string>>
|
||||
* @psalm-var array<string, (positive-int|array{positive-int, ('var'|'var='|'expr'|'expr=')})>
|
||||
*/
|
||||
public static $OPTIONS = ['RECURSIVE' => 1];
|
||||
|
||||
/**
|
||||
* The clauses of this statement, in order.
|
||||
*
|
||||
* @see Statement::$CLAUSES
|
||||
*
|
||||
* @var array<string, array<int, int|string>>
|
||||
* @psalm-var array<string, array{non-empty-string, (1|2|3)}>
|
||||
*/
|
||||
public static $CLAUSES = [
|
||||
'WITH' => [
|
||||
'WITH',
|
||||
2,
|
||||
],
|
||||
// Used for options.
|
||||
'_OPTIONS' => [
|
||||
'_OPTIONS',
|
||||
1,
|
||||
],
|
||||
'AS' => [
|
||||
'AS',
|
||||
2,
|
||||
],
|
||||
];
|
||||
|
||||
/** @var WithKeyword[] */
|
||||
public $withers = [];
|
||||
|
||||
/**
|
||||
* holds the CTE parser.
|
||||
*
|
||||
* @var Parser|null
|
||||
*/
|
||||
public $cteStatementParser;
|
||||
|
||||
/**
|
||||
* @param Parser $parser the instance that requests parsing
|
||||
* @param TokensList $list the list of tokens to be parsed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function parse(Parser $parser, TokensList $list)
|
||||
{
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 ---------------- [ name ] -----------------> 1
|
||||
*
|
||||
* 1 ------------------ [ ( ] ------------------> 2
|
||||
*
|
||||
* 2 ------------------ [ AS ] -----------------> 3
|
||||
*
|
||||
* 3 ------------------ [ ( ] ------------------> 4
|
||||
*
|
||||
* 4 ------------------ [ , ] ------------------> 1
|
||||
*
|
||||
* 4 ----- [ SELECT/UPDATE/DELETE/INSERT ] -----> 5
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
$state = 0;
|
||||
$wither = null;
|
||||
|
||||
++$list->idx; // Skipping `WITH`.
|
||||
|
||||
// parse any options if provided
|
||||
$this->options = OptionsArray::parse($parser, $list, static::$OPTIONS);
|
||||
++$list->idx;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
// Skipping whitespaces and comments.
|
||||
if ($token->type === Token::TYPE_WHITESPACE || $token->type === Token::TYPE_COMMENT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
if ($token->type !== Token::TYPE_NONE) {
|
||||
$parser->error('The name of the CTE was expected.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
$wither = $token->value;
|
||||
$this->withers[$wither] = new WithKeyword($wither);
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
if ($token->type === Token::TYPE_OPERATOR && $token->value === '(') {
|
||||
$this->withers[$wither]->columns = Array2d::parse($parser, $list);
|
||||
$state = 2;
|
||||
} elseif ($token->type === Token::TYPE_KEYWORD && $token->keyword === 'AS') {
|
||||
$state = 3;
|
||||
} else {
|
||||
$parser->error('Unexpected token.', $token);
|
||||
break;
|
||||
}
|
||||
} elseif ($state === 2) {
|
||||
if (! ($token->type === Token::TYPE_KEYWORD && $token->keyword === 'AS')) {
|
||||
$parser->error('AS keyword was expected.', $token);
|
||||
break;
|
||||
}
|
||||
|
||||
$state = 3;
|
||||
} elseif ($state === 3) {
|
||||
$idxBeforeGetNext = $list->idx;
|
||||
|
||||
$list->idx++; // Ignore the current token
|
||||
$nextKeyword = $list->getNext();
|
||||
|
||||
if (! ($token->value === '(' && ($nextKeyword && $nextKeyword->value === 'SELECT'))) {
|
||||
$parser->error('Subquery of the CTE was expected.', $token);
|
||||
$list->idx = $idxBeforeGetNext;
|
||||
break;
|
||||
}
|
||||
|
||||
// Restore the index
|
||||
$list->idx = $idxBeforeGetNext;
|
||||
|
||||
++$list->idx;
|
||||
$subList = $this->getSubTokenList($list);
|
||||
if ($subList instanceof ParserException) {
|
||||
$parser->errors[] = $subList;
|
||||
break;
|
||||
}
|
||||
|
||||
$subParser = new Parser($subList);
|
||||
|
||||
if (count($subParser->errors)) {
|
||||
foreach ($subParser->errors as $error) {
|
||||
$parser->errors[] = $error;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$this->withers[$wither]->statement = $subParser;
|
||||
|
||||
$state = 4;
|
||||
} elseif ($state === 4) {
|
||||
if ($token->value === ',') {
|
||||
// There's another WITH expression to parse, go back to state=0
|
||||
$state = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
$token->type === Token::TYPE_KEYWORD && (
|
||||
$token->value === 'SELECT'
|
||||
|| $token->value === 'INSERT'
|
||||
|| $token->value === 'UPDATE'
|
||||
|| $token->value === 'DELETE'
|
||||
)
|
||||
) {
|
||||
$state = 5;
|
||||
--$list->idx;
|
||||
continue;
|
||||
}
|
||||
|
||||
$parser->error('An expression was expected.', $token);
|
||||
break;
|
||||
} elseif ($state === 5) {
|
||||
/**
|
||||
* We need to parse all of the remaining tokens becuase mostly, they are only the CTE expression
|
||||
* which's mostly is SELECT, or INSERT, UPDATE, or delete statement.
|
||||
* e.g: INSERT .. ( SELECT 1 ) SELECT col1 FROM cte ON DUPLICATE KEY UPDATE col_name = 3.
|
||||
* The issue is that, `ON DUPLICATE KEY UPDATE col_name = 3` is related to the main INSERT query
|
||||
* not the cte expression (SELECT col1 FROM cte) we need to determine the end of the expression
|
||||
* to parse `ON DUPLICATE KEY UPDATE` from the InsertStatement parser instead.
|
||||
*/
|
||||
|
||||
// Index of the last parsed token by default would be the last token in the $list, because we're
|
||||
// assuming that all remaining tokens at state 4, are related to the expression.
|
||||
$idxOfLastParsedToken = $list->count - 1;
|
||||
// Index before search to be able to restore the index.
|
||||
$idxBeforeSearch = $list->idx;
|
||||
// Length of expression tokens is null by default, in order for the $subList to start
|
||||
// from $list->idx to the end of the $list.
|
||||
$lengthOfExpressionTokens = null;
|
||||
|
||||
if ($list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'ON')) {
|
||||
// (-1) because getNextOfTypeAndValue returned ON and increased the index.
|
||||
$idxOfOn = $list->idx - 1;
|
||||
// We want to make sure that it's `ON DUPLICATE KEY UPDATE`
|
||||
$dubplicateToken = $list->getNext();
|
||||
$keyToken = $list->getNext();
|
||||
$updateToken = $list->getNext();
|
||||
if (
|
||||
$dubplicateToken && $dubplicateToken->keyword === 'DUPLICATE'
|
||||
&& ($keyToken && $keyToken->keyword === 'KEY')
|
||||
&& ($updateToken && $updateToken->keyword === 'UPDATE')
|
||||
) {
|
||||
// Index of the last parsed token will be the token before the ON Keyword
|
||||
$idxOfLastParsedToken = $idxOfOn - 1;
|
||||
// The length of the expression tokens would be the difference
|
||||
// between the first unrelated token `ON` and the idx
|
||||
// before skipping the CTE tokens.
|
||||
$lengthOfExpressionTokens = $idxOfOn - $idxBeforeSearch;
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the index
|
||||
$list->idx = $idxBeforeSearch;
|
||||
|
||||
$subList = new TokensList(array_slice($list->tokens, $list->idx, $lengthOfExpressionTokens));
|
||||
$subParser = new Parser($subList);
|
||||
if (count($subParser->errors)) {
|
||||
foreach ($subParser->errors as $error) {
|
||||
$parser->errors[] = $error;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$this->cteStatementParser = $subParser;
|
||||
|
||||
$list->idx = $idxOfLastParsedToken;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 5 is the only valid end state
|
||||
if ($state !== 5) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
$parser->error('Unexpected end of the WITH CTE.', $token);
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$str = 'WITH ';
|
||||
|
||||
foreach ($this->withers as $wither) {
|
||||
$str .= $str === 'WITH ' ? '' : ', ';
|
||||
$str .= WithKeyword::build($wither);
|
||||
}
|
||||
|
||||
$str .= ' ';
|
||||
|
||||
if ($this->cteStatementParser) {
|
||||
foreach ($this->cteStatementParser->statements as $statement) {
|
||||
$str .= $statement->build();
|
||||
}
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tokens within the WITH expression to use them in another parser
|
||||
*
|
||||
* @return ParserException|TokensList
|
||||
*/
|
||||
private function getSubTokenList(TokensList $list)
|
||||
{
|
||||
$idx = $list->idx;
|
||||
$token = $list->tokens[$list->idx];
|
||||
$openParenthesis = 0;
|
||||
|
||||
while ($list->idx < $list->count) {
|
||||
if ($token->value === '(') {
|
||||
++$openParenthesis;
|
||||
} elseif ($token->value === ')') {
|
||||
if (--$openParenthesis === -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++$list->idx;
|
||||
if (! isset($list->tokens[$list->idx])) {
|
||||
break;
|
||||
}
|
||||
|
||||
$token = $list->tokens[$list->idx];
|
||||
}
|
||||
|
||||
// performance improvement: return the error to avoid a try/catch in the loop
|
||||
if ($list->idx === $list->count) {
|
||||
--$list->idx;
|
||||
|
||||
return new ParserException(
|
||||
Translator::gettext('A closing bracket was expected.'),
|
||||
$token
|
||||
);
|
||||
}
|
||||
|
||||
$length = $list->idx - $idx;
|
||||
|
||||
return new TokensList(array_slice($list->tokens, $idx, $length), $length);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user