init web ems all

This commit is contained in:
agtuser
2024-09-27 17:13:36 +08:00
parent 81c97acbe9
commit 5cc56f8078
4263 changed files with 798779 additions and 0 deletions

View File

@@ -0,0 +1,153 @@
<?php
/**
* `ALTER` statement.
*/
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;
/**
* `ALTER` statement.
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class AlterStatement extends Statement
{
/**
* Table affected.
*
* @var Expression
*/
public $table;
/**
* Column affected by this statement.
*
* @var AlterOperation[]
*/
public $altered = array();
/**
* Options of this statement.
*
* @var array
*/
public static $OPTIONS = array(
'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,
array(
'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.
*
* @var Token
*/
$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 = array();
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;
}
$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 = array();
foreach ($this->altered as $altered) {
$tmp[] = $altered::build($altered);
}
return 'ALTER ' . OptionsArray::build($this->options)
. ' ' . Expression::build($this->table)
. ' ' . implode(', ', $tmp);
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* `ANALYZE` statement.
*/
namespace PhpMyAdmin\SqlParser\Statements;
use PhpMyAdmin\SqlParser\Components\Expression;
use PhpMyAdmin\SqlParser\Statement;
/**
* `ANALYZE` statement.
*
* ANALYZE array(NO_WRITE_TO_BINLOG | LOCAL] TABLE
* tbl_name array(, tbl_name] ...
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class AnalyzeStatement extends Statement
{
/**
* Options of this statement.
*
* @var array
*/
public static $OPTIONS = array(
'TABLE' => 1,
'NO_WRITE_TO_BINLOG' => 2,
'LOCAL' => 3
);
/**
* Analyzed tables.
*
* @var Expression[]
*/
public $tables;
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* `BACKUP` statement.
*/
namespace PhpMyAdmin\SqlParser\Statements;
/**
* `BACKUP` statement.
*
* BACKUP TABLE tbl_name array(, tbl_name] ... TO '/path/to/backup/directory'
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class BackupStatement extends MaintenanceStatement
{
/**
* Options of this statement.
*
* @var array
*/
public static $OPTIONS = array(
'TABLE' => 1,
'NO_WRITE_TO_BINLOG' => 2,
'LOCAL' => 3,
'TO' => array(
4,
'var',
)
);
}

View File

@@ -0,0 +1,43 @@
<?php
/**
* `CALL` statement.
*/
namespace PhpMyAdmin\SqlParser\Statements;
use PhpMyAdmin\SqlParser\Components\FunctionCall;
use PhpMyAdmin\SqlParser\Statement;
/**
* `CALL` statement.
*
* CALL sp_name([parameter[,...]])
*
* or
*
* CALL sp_name[()]
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class CallStatement extends Statement
{
/**
* The name of the function and its parameters.
*
* @var FunctionCall
*/
public $call;
/**
* Build statement for CALL.
*
* @return string
*/
public function build()
{
return "CALL " . $this->call->name . "(" . ($this->call->parameters ? implode(",", $this->call->parameters->raw) : "") . ")";
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* `CHECK` statement.
*/
namespace PhpMyAdmin\SqlParser\Statements;
/**
* `CHECK` statement.
*
* CHECK TABLE tbl_name array(, tbl_name] ... array(option] ...
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class CheckStatement extends MaintenanceStatement
{
/**
* Options of this statement.
*
* @var array
*/
public static $OPTIONS = array(
'TABLE' => 1,
'FOR UPGRADE' => 2,
'QUICK' => 3,
'FAST' => 4,
'MEDIUM' => 5,
'EXTENDED' => 6,
'CHANGED' => 7
);
}

View File

@@ -0,0 +1,31 @@
<?php
/**
* `CHECKSUM` statement.
*/
namespace PhpMyAdmin\SqlParser\Statements;
/**
* `CHECKSUM` statement.
*
* CHECKSUM TABLE tbl_name array(, tbl_name] ... array( QUICK | EXTENDED ]
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class ChecksumStatement extends MaintenanceStatement
{
/**
* Options of this statement.
*
* @var array
*/
public static $OPTIONS = array(
'TABLE' => 1,
'QUICK' => 2,
'EXTENDED' => 3
);
}

View File

@@ -0,0 +1,787 @@
<?php
/**
* `CREATE` statement.
*/
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;
/**
* `CREATE` statement.
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class CreateStatement extends Statement
{
/**
* Options for `CREATE` statements.
*
* @var array
*/
public static $OPTIONS = array(
// CREATE TABLE
'TEMPORARY' => 1,
// CREATE VIEW
'OR REPLACE' => 2,
'ALGORITHM' => array(
3,
'var=',
),
// `DEFINER` is also used for `CREATE FUNCTION / PROCEDURE`
'DEFINER' => array(
4,
'expr=',
),
// Used in `CREATE VIEW`
'SQL SECURITY' => array(
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
*/
public static $DB_OPTIONS = array(
'CHARACTER SET' => array(
1,
'var=',
),
'CHARSET' => array(
1,
'var=',
),
'DEFAULT CHARACTER SET' => array(
1,
'var=',
),
'DEFAULT CHARSET' => array(
1,
'var=',
),
'DEFAULT COLLATE' => array(
2,
'var=',
),
'COLLATE' => array(
2,
'var=',
)
);
/**
* All table options.
*
* @var array
*/
public static $TABLE_OPTIONS = array(
'ENGINE' => array(
1,
'var=',
),
'AUTO_INCREMENT' => array(
2,
'var=',
),
'AVG_ROW_LENGTH' => array(
3,
'var',
),
'CHARACTER SET' => array(
4,
'var=',
),
'CHARSET' => array(
4,
'var=',
),
'DEFAULT CHARACTER SET' => array(
4,
'var=',
),
'DEFAULT CHARSET' => array(
4,
'var=',
),
'CHECKSUM' => array(
5,
'var',
),
'DEFAULT COLLATE' => array(
6,
'var=',
),
'COLLATE' => array(
6,
'var=',
),
'COMMENT' => array(
7,
'var=',
),
'CONNECTION' => array(
8,
'var',
),
'DATA DIRECTORY' => array(
9,
'var',
),
'DELAY_KEY_WRITE' => array(
10,
'var',
),
'INDEX DIRECTORY' => array(
11,
'var',
),
'INSERT_METHOD' => array(
12,
'var',
),
'KEY_BLOCK_SIZE' => array(
13,
'var',
),
'MAX_ROWS' => array(
14,
'var',
),
'MIN_ROWS' => array(
15,
'var',
),
'PACK_KEYS' => array(
16,
'var',
),
'PASSWORD' => array(
17,
'var',
),
'ROW_FORMAT' => array(
18,
'var',
),
'TABLESPACE' => array(
19,
'var',
),
'STORAGE' => array(
20,
'var',
),
'UNION' => array(
21,
'var',
)
);
/**
* All function options.
*
* @var array
*/
public static $FUNC_OPTIONS = array(
'NOT' => array(
2,
'var',
),
'FUNCTION' => array(
3,
'var=',
),
'PROCEDURE' => array(
3,
'var=',
),
'CONTAINS' => array(
4,
'expr',
),
'NO' => array(
4,
'var',
),
'READS' => array(
4,
'var',
),
'MODIFIES' => array(
4,
'expr',
),
'SQL SECURITY' => array(
6,
'var',
),
'LANGUAGE' => array(
7,
'var',
),
'COMMENT' => array(
8,
'var',
),
'CREATE' => 1,
'DETERMINISTIC' => 2,
'DATA' => 5,
);
/**
* All trigger options.
*
* @var array
*/
public static $TRIGGER_OPTIONS = array(
'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
*/
public $name;
/**
* The options of the entity (table, procedure, function, etc.).
*
* Used by `CREATE TABLE`, `CREATE FUNCTION` and `CREATE PROCEDURE`.
*
* @var OptionsArray
*
* @see static::$TABLE_OPTIONS
* @see static::$FUNC_OPTIONS
* @see static::$TRIGGER_OPTIONS
*/
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
*/
public $fields;
/**
* 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
*/
public $like;
/**
* Expression used for partitioning.
*
* @var string
*/
public $partitionBy;
/**
* The number of partitions.
*
* @var int
*/
public $partitionsNum;
/**
* Expression used for subpartitioning.
*
* @var string
*/
public $subpartitionBy;
/**
* The number of subpartitions.
*
* @var int
*/
public $subpartitionsNum;
/**
* The partition of the new table.
*
* @var PartitionDefinition[]
*/
public $partitions;
/**
* If `CREATE TRIGGER` the name of the table.
*
* Used by `CREATE TRIGGER`.
*
* @var Expression
*/
public $table;
/**
* The return data type of this routine.
*
* Used by `CREATE FUNCTION`.
*
* @var DataType
*/
public $return;
/**
* The parameters of this routine.
*
* Used by `CREATE FUNCTION` and `CREATE PROCEDURE`.
*
* @var ParameterDefinition[]
*/
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 = array();
/**
* @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);
} elseif ($this->options->has('TABLE')) {
if (! is_null($this->select)) {
return 'CREATE '
. OptionsArray::build($this->options) . ' '
. Expression::build($this->name) . ' '
. $this->select->build();
} elseif (! is_null($this->like)) {
return 'CREATE '
. OptionsArray::build($this->options) . ' '
. Expression::build($this->name) . ' LIKE '
. Expression::build($this->like);
} else {
$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')) {
return 'CREATE '
. OptionsArray::build($this->options) . ' '
. Expression::build($this->name) . ' '
. $fields . ' AS ' . ($this->select ? $this->select->build() : '') . (! 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,
array(
'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.
*
* @var Token
*/
$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 === 'AS')
&& ($list->tokens[$nextidx]->type === Token::TYPE_KEYWORD)
&& ($list->tokens[$nextidx]->value === 'SELECT')) {
/* CREATE TABLE ... AS SELECT */
$list->idx = $nextidx;
$this->select = new SelectStatement($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,
array(
'parseField' => 'table',
'breakOnAlias' => true
)
);
// The 'LIKE' keyword was found, but no table_name was found next to it
if (is_null($this->like)) {
$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.
*
* @var Token
*/
$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 = $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,
array(
'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')) {
$prev_token = $token;
$token = $list->getNextOfType(Token::TYPE_KEYWORD);
if (is_null($token) || $token->keyword !== 'RETURNS') {
$parser->error(
'A "RETURNS" keyword was expected.',
is_null($token) ? $prev_token : $token
);
} 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
&& $list->tokens[$nextidx]->value === 'SELECT'
) {
$list->idx = $nextidx;
$this->select = new SelectStatement($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,
array(
'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;
}
}
}
}

View File

@@ -0,0 +1,380 @@
<?php
/**
* `DELETE` statement.
*/
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;
/**
* `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]
*
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class DeleteStatement extends Statement
{
/**
* Options for `DELETE` statements.
*
* @var array
*/
public static $OPTIONS = array(
'LOW_PRIORITY' => 1,
'QUICK' => 2,
'IGNORE' => 3
);
/**
* The clauses of this statement, in order.
*
* @see Statement::$CLAUSES
*
* @var array
*/
public static $CLAUSES = array(
'DELETE' => array(
'DELETE',
2,
),
// Used for options.
'_OPTIONS' => array(
'_OPTIONS',
1,
),
'FROM' => array(
'FROM',
3,
),
'PARTITION' => array(
'PARTITION',
3,
),
'USING' => array(
'USING',
3,
),
'WHERE' => array(
'WHERE',
3,
),
'ORDER BY' => array(
'ORDER BY',
3,
),
'LIMIT' => array(
'LIMIT',
3,
)
);
/**
* Table(s) used as sources for this statement.
*
* @var Expression[]
*/
public $from;
/**
* Joins.
*
* @var JoinKeyword[]
*/
public $join;
/**
* Tables used as sources for this statement.
*
* @var Expression[]
*/
public $using;
/**
* Columns used in this statement.
*
* @var Expression[]
*/
public $columns;
/**
* Partitions used as source for this statement.
*
* @var ArrayObj
*/
public $partition;
/**
* Conditions used for filtering each row of the result set.
*
* @var Condition[]
*/
public $where;
/**
* Specifies the order of the rows in the result set.
*
* @var OrderKeyword[]
*/
public $order;
/**
* Conditions used for limiting the size of the result set.
*
* @var Limit
*/
public $limit;
/**
* @return string
*/
public function build()
{
$ret = 'DELETE ' . OptionsArray::build($this->options);
if (! is_null($this->columns) && count($this->columns) > 0) {
$ret .= ' ' . ExpressionArray::build($this->columns);
}
if (! is_null($this->from) && count($this->from) > 0) {
$ret .= ' FROM ' . ExpressionArray::build($this->from);
}
if (! is_null($this->join) && count($this->join) > 0) {
$ret .= ' ' . JoinKeyword::build($this->join);
}
if (! is_null($this->using) && count($this->using) > 0) {
$ret .= ' USING ' . ExpressionArray::build($this->using);
}
if (! is_null($this->where) && count($this->where) > 0) {
$ret .= ' WHERE ' . Condition::build($this->where);
}
if (! is_null($this->order) && count($this->order) > 0) {
$ret .= ' ORDER BY ' . ExpressionArray::build($this->order);
}
if (! is_null($this->limit) && strlen($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.
*
* @var Token
*/
$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;
} else {
++$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) {
if ($token->keyword !== 'FROM') {
$parser->error('Unexpected keyword.', $token);
break;
} else {
++$list->idx; // Skip 'FROM'
$this->from = ExpressionArray::parse($parser, $list);
$state = 2;
}
} else {
$parser->error('Unexpected token.', $token);
break;
}
} 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) {
if ($token->keyword === 'WHERE') {
++$list->idx; // Skip 'WHERE'
$this->where = Condition::parse($parser, $list);
$state = 4;
} else {
$parser->error('Unexpected keyword.', $token);
break;
}
} else {
$parser->error('Unexpected token.', $token);
break;
}
} 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') {
++$list->idx; // Skip 'LIMIT'
$this->limit = Limit::parse($parser, $list);
$state = 6;
} else {
$parser->error('Unexpected keyword.', $token);
break;
}
}
}
}
if ($state >= 2) {
foreach ($this->from as $from_expr) {
$from_expr->database = $from_expr->table;
$from_expr->table = $from_expr->column;
$from_expr->column = null;
}
}
--$list->idx;
}
}

View File

@@ -0,0 +1,86 @@
<?php
/**
* `DROP` statement.
*/
namespace PhpMyAdmin\SqlParser\Statements;
use PhpMyAdmin\SqlParser\Components\Expression;
use PhpMyAdmin\SqlParser\Statement;
/**
* `DROP` statement.
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class DropStatement extends Statement
{
/**
* Options of this statement.
*
* @var array
*/
public static $OPTIONS = array(
'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
*/
public static $CLAUSES = array(
'DROP' => array(
'DROP',
2,
),
// Used for options.
'_OPTIONS' => array(
'_OPTIONS',
1,
),
// Used for select expressions.
'DROP_' => array(
'DROP',
1,
),
'ON' => array(
'ON',
3,
)
);
/**
* Dropped elements.
*
* @var Expression[]
*/
public $fields;
/**
* Table of the dropped index.
*
* @var Expression
*/
public $table;
}

View File

@@ -0,0 +1,18 @@
<?php
/**
* `EXPLAIN` statement.
*/
namespace PhpMyAdmin\SqlParser\Statements;
/**
* `EXPLAIN` statement.
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class ExplainStatement extends NotImplementedStatement
{
}

View File

@@ -0,0 +1,263 @@
<?php
/**
* `INSERT` statement.
*/
namespace PhpMyAdmin\SqlParser\Statements;
use PhpMyAdmin\SqlParser\Components\ArrayObj;
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;
/**
* `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] ... ]
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class InsertStatement extends Statement
{
/**
* Options for `INSERT` statements.
*
* @var array
*/
public static $OPTIONS = array(
'LOW_PRIORITY' => 1,
'DELAYED' => 2,
'HIGH_PRIORITY' => 3,
'IGNORE' => 4
);
/**
* Tables used as target for this statement.
*
* @var IntoKeyword
*/
public $into;
/**
* Values to be inserted.
*
* @var ArrayObj[]|null
*/
public $values;
/**
* If SET clause is present
* holds the SetOperation.
*
* @var SetOperation[]
*/
public $set;
/**
* If SELECT clause is present
* holds the SelectStatement.
*
* @var SelectStatement
*/
public $select;
/**
* If ON DUPLICATE KEY UPDATE clause is present
* holds the SetOperation.
*
* @var SetOperation[]
*/
public $onDuplicateSet;
/**
* @return string
*/
public function build()
{
$ret = 'INSERT ' . $this->options;
$ret = trim($ret) . ' INTO ' . $this->into;
if (! is_null($this->values) && count($this->values) > 0) {
$ret .= ' VALUES ' . Array2d::build($this->values);
} elseif (! is_null($this->set) && count($this->set) > 0) {
$ret .= ' SET ' . SetOperation::build($this->set);
} elseif (! is_null($this->select) && strlen($this->select) > 0) {
$ret .= ' ' . $this->select->build();
}
if (! is_null($this->onDuplicateSet) && 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.
*
* @var Token
*/
$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,
array('fromInsert' => true)
);
$state = 1;
} elseif ($state === 1) {
if ($token->type === Token::TYPE_KEYWORD) {
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;
$miniState = 1;
} else {
$parser->error(
'Unexpected token.',
$token
);
break;
}
} 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;
}
}

View File

@@ -0,0 +1,423 @@
<?php
/**
* `LOAD` statement.
*/
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;
/**
* `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,...]
*
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class LoadStatement extends Statement
{
/**
* Options for `LOAD` statements and their slot ID.
*
* @var array
*/
public static $OPTIONS = array(
'LOW_PRIORITY' => 1,
'CONCURRENT' => 1,
'LOCAL' => 2
);
/**
* FIELDS/COLUMNS Options for `LOAD DATA...INFILE` statements.
*
* @var array
*/
public static $FIELDS_OPTIONS = array(
'TERMINATED BY' => array(
1,
'expr',
),
'OPTIONALLY' => 2,
'ENCLOSED BY' => array(
3,
'expr',
),
'ESCAPED BY' => array(
4,
'expr',
)
);
/**
* LINES Options for `LOAD DATA...INFILE` statements.
*
* @var array
*/
public static $LINES_OPTIONS = array(
'STARTING BY' => array(
1,
'expr',
),
'TERMINATED BY' => array(
2,
'expr',
)
);
/**
* File name being used to load data.
*
* @var Expression
*/
public $file_name;
/**
* Table used as destination for this statement.
*
* @var Expression
*/
public $table;
/**
* Partitions used as source for this statement.
*
* @var ArrayObj
*/
public $partition;
/**
* Character set used in this statement.
*
* @var Expression
*/
public $charset_name;
/**
* Options for FIELDS/COLUMNS keyword.
*
* @var OptionsArray
*
* @see static::$FIELDS_OPTIONS
*/
public $fields_options;
/**
* Whether to use `FIELDS` or `COLUMNS` while building.
*
* @var string
*/
public $fields_keyword;
/**
* Options for OPTIONS keyword.
*
* @var OptionsArray
*
* @see static::$LINES_OPTIONS
*/
public $lines_options;
/**
* Column names or user variables.
*
* @var Expression[]
*/
public $col_name_or_user_var;
/**
* SET clause's updated values(optional).
*
* @var SetOperation[]
*/
public $set;
/**
* Ignore 'number' LINES/ROWS.
*
* @var Expression
*/
public $ignore_number;
/**
* REPLACE/IGNORE Keyword.
*
* @var string
*/
public $replace_ignore;
/**
* LINES/ROWS Keyword.
*
* @var string
*/
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($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($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.
*
* @var Token
*/
$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;
} elseif ($token->type !== Token::TYPE_KEYWORD) {
$parser->error('Unexpected token.', $token);
break;
}
++$list->idx;
$this->file_name = Expression::parse(
$parser,
$list,
array('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'
) {
++$list->idx;
$this->table = Expression::parse($parser, $list, array('parseField' => 'table'));
$state = 3;
} else {
$parser->error('Unexpected token.', $token);
break;
}
} 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;
}
public function parseFileOptions(Parser $parser, TokensList $list, $keyword = 'FIELDS')
{
++$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
);
}
}
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);
$state = 4;
return $state;
}
// no break
case 4:
if ($token->keyword === 'CHARACTER SET') {
++$list->idx;
$this->charset_name = Expression::parse($parser, $list);
$state = 5;
return $state;
}
// no break
case 5:
if ($token->keyword === 'FIELDS'
|| $token->keyword === 'COLUMNS'
|| $token->keyword === 'LINES'
) {
$this->parseFileOptions($parser, $list, $token->value);
$state = 6;
return $state;
}
// 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;
}
$state = 7;
return $state;
}
// no break
case 7:
if ($token->keyword === 'SET') {
++$list->idx;
$this->set = SetOperation::parse($parser, $list);
$state = 8;
return $state;
}
// no break
default:
}
return $state;
}
}

View File

@@ -0,0 +1,128 @@
<?php
/**
* `LOCK` statement.
*/
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;
/**
* `LOCK` statement.
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class LockStatement extends Statement
{
/**
* Tables with their Lock expressions.
*
* @var LockExpression[]
*/
public $locked = array();
/**
* Whether it's a LOCK statement
* if false, it's an UNLOCK statement
*/
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.
*
* @var Token
*/
$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;
} else {
$parser->error('Unexpected token.', $token);
break;
}
} elseif ($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) {
$parser->error('Unexpected end of LOCK statement.', $prevToken);
}
}
/**
* @return string
*/
public function build()
{
return trim(($this->isLock ? 'LOCK' : 'UNLOCK')
. ' TABLES ' . LockExpression::build($this->locked));
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* Maintenance statement.
*/
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]
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class MaintenanceStatement extends Statement
{
/**
* Tables maintained.
*
* @var Expression[]
*/
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
*/
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
)
);
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* Not implemented (yet) statements.
*/
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.
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class NotImplementedStatement extends Statement
{
/**
* The part of the statement that can't be parsed.
*
* @var Token[]
*/
public $unknown = array();
/**
* @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];
}
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* `OPTIMIZE` statement.
*/
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] ...
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class OptimizeStatement extends Statement
{
/**
* Options of this statement.
*
* @var array
*/
public static $OPTIONS = array(
'TABLE' => 1,
'NO_WRITE_TO_BINLOG' => 2,
'LOCAL' => 3
);
/**
* Optimized tables.
*
* @var Expression[]
*/
public $tables;
}

View File

@@ -0,0 +1,143 @@
<?php
/**
* `PURGE` statement.
*/
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;
/**
* `PURGE` statement.
*
* PURGE { BINARY | MASTER } LOGS
* { TO 'log_name' | BEFORE datetime_expr }
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class PurgeStatement extends Statement
{
/**
* The type of logs
*
* @var String
*/
public $log_type;
/**
* The end option of this query.
*
* @var String
*/
public $end_option;
/**
* The end expr of this query.
*
* @var String
*/
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;
for (; $list->idx < $list->count; ++$list->idx) {
/**
* Token parsed at this moment.
*
* @var Token
*/
$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, array('BINARY', 'MASTER'));
break;
case 1:
// parse `LOGS`
self::parseExpectedKeyword($parser, $token, array('LOGS'));
break;
case 2:
// parse `{ TO | BEFORE }`
$this->end_option = self::parseExpectedKeyword($parser, $token, array('TO', 'BEFORE'));
break;
case 3:
// parse `expr`
$this->end_expr = Expression::parse($parser, $list, array());
break;
default:
$parser->error('Unexpected token.', $token);
break;
}
$state++;
$prevToken = $token;
}
// Only one possible end state
if ($state != 4) {
$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 Array $expected_keywords array of possibly expected keywords at this point
*/
private static function parseExpectedKeyword($parser, $token, $expected_keywords)
{
if ($token->type === Token::TYPE_KEYWORD) {
if (in_array($token->keyword, $expected_keywords)) {
return $token->keyword;
} else {
$parser->error('Unexpected keyword', $token);
}
} else {
$parser->error('Unexpected token.', $token);
}
return null;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/**
* `RENAME` statement.
*/
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] ...
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class RenameStatement extends Statement
{
/**
* The old and new names of the tables.
*
* @var RenameOperation[]
*/
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
*/
public function before(Parser $parser, TokensList $list, Token $token)
{
if (($token->type === Token::TYPE_KEYWORD) && ($token->keyword === 'RENAME')) {
// 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);
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* `REPAIR` statement.
*/
namespace PhpMyAdmin\SqlParser\Statements;
/**
* `REPAIR` statement.
*
* REPAIR [NO_WRITE_TO_BINLOG | LOCAL] TABLE
* tbl_name [, tbl_name] ...
* [QUICK] [EXTENDED] [USE_FRM]
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class RepairStatement extends MaintenanceStatement
{
/**
* Options of this statement.
*
* @var array
*/
public static $OPTIONS = array(
'TABLE' => 1,
'NO_WRITE_TO_BINLOG' => 2,
'LOCAL' => 3,
'QUICK' => 4,
'EXTENDED' => 5,
'USE_FRM' => 6
);
}

View File

@@ -0,0 +1,201 @@
<?php
/**
* `REPLACE` statement.
*/
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;
/**
* `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 ...
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class ReplaceStatement extends Statement
{
/**
* Options for `REPLACE` statements and their slot ID.
*
* @var array
*/
public static $OPTIONS = array(
'LOW_PRIORITY' => 1,
'DELAYED' => 1
);
/**
* Tables used as target for this statement.
*
* @var IntoKeyword
*/
public $into;
/**
* Values to be replaced.
*
* @var Array2d
*/
public $values;
/**
* If SET clause is present
* holds the SetOperation.
*
* @var SetOperation[]
*/
public $set;
/**
* If SELECT clause is present
* holds the SelectStatement.
*
* @var SelectStatement
*/
public $select;
/**
* @return string
*/
public function build()
{
$ret = 'REPLACE ' . $this->options;
$ret = trim($ret) . ' INTO ' . $this->into;
if (! is_null($this->values) && count($this->values) > 0) {
$ret .= ' VALUES ' . Array2d::build($this->values);
} elseif (! is_null($this->set) && count($this->set) > 0) {
$ret .= ' SET ' . SetOperation::build($this->set);
} elseif (! is_null($this->select) && strlen($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
*/
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.
*
* @var Token
*/
$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,
array('fromReplace' => true)
);
$state = 1;
} elseif ($state === 1) {
if ($token->type === Token::TYPE_KEYWORD) {
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;
} else {
$parser->error(
'Unexpected token.',
$token
);
break;
}
}
}
--$list->idx;
}
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* `RESTORE` statement.
*/
namespace PhpMyAdmin\SqlParser\Statements;
/**
* `RESTORE` statement.
*
* RESTORE TABLE tbl_name [, tbl_name] ... FROM '/path/to/backup/directory'
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class RestoreStatement extends MaintenanceStatement
{
/**
* Options of this statement.
*
* @var array
*/
public static $OPTIONS = array(
'TABLE' => 1,
'FROM' => array(
2,
'var',
)
);
}

View File

@@ -0,0 +1,352 @@
<?php
/**
* `SELECT` statement.
*/
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\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\Components\GroupKeyword;
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]]
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class SelectStatement extends Statement
{
/**
* Options for `SELECT` statements and their slot ID.
*
* @var array
*/
public static $OPTIONS = array(
'ALL' => 1,
'DISTINCT' => 1,
'DISTINCTROW' => 1,
'HIGH_PRIORITY' => 2,
'MAX_STATEMENT_TIME' => array(
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
);
public static $END_OPTIONS = array(
'FOR UPDATE' => 1,
'LOCK IN SHARE MODE' => 1
);
/**
* The clauses of this statement, in order.
*
* @see Statement::$CLAUSES
*
* @var array
*/
public static $CLAUSES = array(
'SELECT' => array(
'SELECT',
2,
),
// Used for options.
'_OPTIONS' => array(
'_OPTIONS',
1,
),
// Used for selected expressions.
'_SELECT' => array(
'SELECT',
1,
),
'INTO' => array(
'INTO',
3,
),
'FROM' => array(
'FROM',
3,
),
'FORCE' => array(
'FORCE',
1,
),
'USE' => array(
'USE',
1,
),
'IGNORE' => array(
'IGNORE',
3,
),
'PARTITION' => array(
'PARTITION',
3,
),
'JOIN' => array(
'JOIN',
1,
),
'FULL JOIN' => array(
'FULL JOIN',
1,
),
'INNER JOIN' => array(
'INNER JOIN',
1,
),
'LEFT JOIN' => array(
'LEFT JOIN',
1,
),
'LEFT OUTER JOIN' => array(
'LEFT OUTER JOIN',
1,
),
'RIGHT JOIN' => array(
'RIGHT JOIN',
1,
),
'RIGHT OUTER JOIN' => array(
'RIGHT OUTER JOIN',
1,
),
'NATURAL JOIN' => array(
'NATURAL JOIN',
1,
),
'NATURAL LEFT JOIN' => array(
'NATURAL LEFT JOIN',
1,
),
'NATURAL RIGHT JOIN' => array(
'NATURAL RIGHT JOIN',
1,
),
'NATURAL LEFT OUTER JOIN' => array(
'NATURAL LEFT OUTER JOIN',
1,
),
'NATURAL RIGHT OUTER JOIN' => array(
'NATURAL RIGHT JOIN',
1,
),
'WHERE' => array(
'WHERE',
3,
),
'GROUP BY' => array(
'GROUP BY',
3,
),
'HAVING' => array(
'HAVING',
3,
),
'ORDER BY' => array(
'ORDER BY',
3,
),
'LIMIT' => array(
'LIMIT',
3,
),
'PROCEDURE' => array(
'PROCEDURE',
3,
),
'UNION' => array(
'UNION',
1,
),
'EXCEPT' => array(
'EXCEPT',
1,
),
'INTERSECT' => array(
'INTERSECT',
1,
),
'_END_OPTIONS' => array(
'_END_OPTIONS',
1,
),
// These are available only when `UNION` is present.
// 'ORDER BY' => array('ORDER BY', 3),
// 'LIMIT' => array('LIMIT', 3)
);
/**
* Expressions that are being selected by this statement.
*
* @var Expression[]
*/
public $expr = array();
/**
* Tables used as sources for this statement.
*
* @var Expression[]
*/
public $from = array();
/**
* Index hints
*
* @var IndexHint[]
*/
public $index_hints;
/**
* Partitions used as source for this statement.
*
* @var ArrayObj
*/
public $partition;
/**
* Conditions used for filtering each row of the result set.
*
* @var Condition[]
*/
public $where;
/**
* Conditions used for grouping the result set.
*
* @var GroupKeyword[]
*/
public $group;
/**
* Conditions used for filtering the result set.
*
* @var Condition[]
*/
public $having;
/**
* Specifies the order of the rows in the result set.
*
* @var OrderKeyword[]
*/
public $order;
/**
* Conditions used for limiting the size of the result set.
*
* @var Limit
*/
public $limit;
/**
* Procedure that should process the data in the result set.
*
* @var FunctionCall
*/
public $procedure;
/**
* Destination of this result set.
*
* @var IntoKeyword
*/
public $into;
/**
* Joins.
*
* @var JoinKeyword[]
*/
public $join;
/**
* Unions.
*
* @var SelectStatement[]
*/
public $union = array();
/**
* The end options of this query.
*
* @var OptionsArray
*
* @see static::$END_OPTIONS
*/
public $end_options;
/**
* Gets the clauses of this statement.
*
* @return array
*/
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'] = array(
'ORDER BY',
3
);
$clauses['LIMIT'] = array(
'LIMIT',
3
);
return $clauses;
}
return static::$CLAUSES;
}
}

View File

@@ -0,0 +1,114 @@
<?php
/**
* `SET` statement.
*/
namespace PhpMyAdmin\SqlParser\Statements;
use PhpMyAdmin\SqlParser\Components\OptionsArray;
use PhpMyAdmin\SqlParser\Components\SetOperation;
use PhpMyAdmin\SqlParser\Statement;
/**
* `SET` statement.
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class SetStatement extends Statement
{
/**
* The clauses of this statement, in order.
*
* @see Statement::$CLAUSES
*
* @var array
*/
public static $CLAUSES = array(
'SET' => array(
'SET',
3
),
'_END_OPTIONS' => array(
'_END_OPTIONS',
1
)
);
/**
* Possible exceptions in SET statment.
*
* @var array
*/
public static $OPTIONS = array(
'CHARSET' => array(
3,
'var',
),
'CHARACTER SET' => array(
3,
'var',
),
'NAMES' => array(
3,
'var',
),
'PASSWORD' => array(
3,
'expr',
),
'SESSION' => 3,
'GLOBAL' => 3,
'PERSIST' => 3,
'PERSIST_ONLY' => 3,
'@@SESSION' => 3,
'@@GLOBAL' => 3,
'@@PERSIST' => 3,
'@@PERSIST_ONLY' => 3,
);
public static $END_OPTIONS = array(
'COLLATE' => array(
1,
'var',
),
'DEFAULT' => 1
);
/**
* Options used in current statement.
*
* @var OptionsArray[]
*/
public $options;
/**
* The end options of this query.
*
* @var OptionsArray
*
* @see static::$END_OPTIONS
*/
public $end_options;
/**
* The updated values.
*
* @var SetOperation[]
*/
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);
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* `SHOW` statement.
*/
namespace PhpMyAdmin\SqlParser\Statements;
/**
* `SHOW` statement.
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class ShowStatement extends NotImplementedStatement
{
/**
* Options of this statement.
*
* @var array
*/
public static $OPTIONS = array(
'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
);
}

View File

@@ -0,0 +1,114 @@
<?php
/**
* Transaction statement.
*/
namespace PhpMyAdmin\SqlParser\Statements;
use PhpMyAdmin\SqlParser\Components\OptionsArray;
use PhpMyAdmin\SqlParser\Parser;
use PhpMyAdmin\SqlParser\Statement;
use PhpMyAdmin\SqlParser\TokensList;
/**
* Transaction statement.
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class TransactionStatement extends Statement
{
/**
* START TRANSACTION and BEGIN.
*
* @var int
*/
const TYPE_BEGIN = 1;
/**
* COMMIT and ROLLBACK.
*
* @var int
*/
const TYPE_END = 2;
/**
* The type of this query.
*
* @var int
*/
public $type;
/**
* The list of statements in this transaction.
*
* @var Statement[]
*/
public $statements;
/**
* The ending transaction statement which may be a `COMMIT` or a `ROLLBACK`.
*
* @var TransactionStatement
*/
public $end;
/**
* Options for this query.
*
* @var array
*/
public static $OPTIONS = array(
'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
*/
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;
}
}

View File

@@ -0,0 +1,46 @@
<?php
/**
* `TRUNCATE` statement.
*/
namespace PhpMyAdmin\SqlParser\Statements;
use PhpMyAdmin\SqlParser\Components\Expression;
use PhpMyAdmin\SqlParser\Statement;
/**
* `TRUNCATE` statement.
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class TruncateStatement extends Statement
{
/**
* Options for `TRUNCATE` statements.
*
* @var array
*/
public static $OPTIONS = array(
'TABLE' => 1
);
/**
* The name of the truncated table.
*
* @var Expression
*/
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 . ';';
}
}

View File

@@ -0,0 +1,121 @@
<?php
/**
* `UPDATE` statement.
*/
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]
*
* @category Statements
*
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
*/
class UpdateStatement extends Statement
{
/**
* Options for `UPDATE` statements and their slot ID.
*
* @var array
*/
public static $OPTIONS = array(
'LOW_PRIORITY' => 1,
'IGNORE' => 2
);
/**
* The clauses of this statement, in order.
*
* @see Statement::$CLAUSES
*
* @var array
*/
public static $CLAUSES = array(
'UPDATE' => array(
'UPDATE',
2,
),
// Used for options.
'_OPTIONS' => array(
'_OPTIONS',
1,
),
// Used for updated tables.
'_UPDATE' => array(
'UPDATE',
1,
),
'SET' => array(
'SET',
3,
),
'WHERE' => array(
'WHERE',
3,
),
'ORDER BY' => array(
'ORDER BY',
3,
),
'LIMIT' => array(
'LIMIT',
3,
)
);
/**
* Tables used as sources for this statement.
*
* @var Expression[]
*/
public $tables;
/**
* The updated values.
*
* @var SetOperation[]
*/
public $set;
/**
* Conditions used for filtering each row of the result set.
*
* @var Condition[]
*/
public $where;
/**
* Specifies the order of the rows in the result set.
*
* @var OrderKeyword[]
*/
public $order;
/**
* Conditions used for limiting the size of the result set.
*
* @var Limit
*/
public $limit;
}