Merge pull request #11319 from udan11/update
Updated sql-parser library.
This commit is contained in:
commit
2a63cbc43f
@ -10,64 +10,84 @@
|
||||
*
|
||||
* @package SqlParser
|
||||
*/
|
||||
namespace SqlParser;
|
||||
|
||||
/**
|
||||
* A component (of a statement) is a part of a statement that is common to
|
||||
* multiple query types.
|
||||
*
|
||||
* @category Components
|
||||
* @package SqlParser
|
||||
* @author Dan Ungureanu <udan1107@gmail.com>
|
||||
* @license http://opensource.org/licenses/GPL-2.0 GNU Public License
|
||||
*/
|
||||
abstract class Component
|
||||
{
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Parses the tokens contained in the given list in the context of the given
|
||||
* parser.
|
||||
*
|
||||
* @param Parser $parser The parser that serves as context.
|
||||
* @param TokensList $list The list of tokens that are being parsed.
|
||||
* @param array $options Parameters for parsing.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function parse(
|
||||
Parser $parser, TokensList $list, array $options = array()
|
||||
) {
|
||||
// This method should be abstract, but it can't be both static and
|
||||
// abstract.
|
||||
return null;
|
||||
}
|
||||
if (!function_exists('__')) {
|
||||
|
||||
/**
|
||||
* Builds the string representation of a component of this type.
|
||||
*
|
||||
* In other words, this function represents the inverse function of
|
||||
* `static::parse`.
|
||||
*
|
||||
* @param mixed $component The component to be built.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component)
|
||||
{
|
||||
// This method should be abstract, but it can't be both static and
|
||||
// abstract.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the string representation of a component of this type.
|
||||
*
|
||||
* @see static::build
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return static::build($this);
|
||||
/**
|
||||
* Translates the given string.
|
||||
*
|
||||
* @param string $str String to be translated.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function __($str)
|
||||
{
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace SqlParser {
|
||||
|
||||
/**
|
||||
* A component (of a statement) is a part of a statement that is common to
|
||||
* multiple query types.
|
||||
*
|
||||
* @category Components
|
||||
* @package SqlParser
|
||||
* @author Dan Ungureanu <udan1107@gmail.com>
|
||||
* @license http://opensource.org/licenses/GPL-2.0 GNU Public License
|
||||
*/
|
||||
abstract class Component
|
||||
{
|
||||
|
||||
/**
|
||||
* Parses the tokens contained in the given list in the context of the given
|
||||
* parser.
|
||||
*
|
||||
* @param Parser $parser The parser that serves as context.
|
||||
* @param TokensList $list The list of tokens that are being parsed.
|
||||
* @param array $options Parameters for parsing.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function parse(
|
||||
Parser $parser, TokensList $list, array $options = array()
|
||||
) {
|
||||
// This method should be abstract, but it can't be both static and
|
||||
// abstract.
|
||||
throw new \Exception(\__('Not implemented yet.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the string representation of a component of this type.
|
||||
*
|
||||
* In other words, this function represents the inverse function of
|
||||
* `static::parse`.
|
||||
*
|
||||
* @param mixed $component The component to be built.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component)
|
||||
{
|
||||
// This method should be abstract, but it can't be both static and
|
||||
// abstract.
|
||||
throw new \Exception(\__('Not implemented yet.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the string representation of a component of this type.
|
||||
*
|
||||
* @see static::build
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return static::build($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,7 +175,10 @@ class Expression extends Component
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($token->type === Token::TYPE_KEYWORD) && ($token->flags & Token::FLAG_KEYWORD_RESERVED)) {
|
||||
if (($token->type === Token::TYPE_KEYWORD)
|
||||
&& ($token->flags & Token::FLAG_KEYWORD_RESERVED)
|
||||
&& ($token->value !== 'DUAL')
|
||||
) {
|
||||
// Keywords may be found only between brackets.
|
||||
if ($brackets === 0) {
|
||||
if ((empty($options['noAlias'])) && ($token->value === 'AS')) {
|
||||
|
||||
@ -67,7 +67,10 @@ class ExpressionArray extends Component
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($token->type === Token::TYPE_KEYWORD) && ($token->flags & Token::FLAG_KEYWORD_RESERVED)) {
|
||||
if (($token->type === Token::TYPE_KEYWORD)
|
||||
&& ($token->flags & Token::FLAG_KEYWORD_RESERVED)
|
||||
&& ($token->value !== 'DUAL')
|
||||
) {
|
||||
// No keyword is expected.
|
||||
break;
|
||||
}
|
||||
|
||||
@ -241,6 +241,13 @@ class FieldDefinition extends Component
|
||||
$state = 6;
|
||||
++$list->idx;
|
||||
break;
|
||||
} else {
|
||||
$parser->error(
|
||||
__('A comma or a closing bracket was expected.'),
|
||||
$token
|
||||
);
|
||||
$state = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,7 +37,19 @@ class OrderKeyword extends Component
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $type = 'ASC';
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Expression $field The field that we are sorting by.
|
||||
* @param string $type The sorting type.
|
||||
*/
|
||||
public function __construct($field = null, $type = 'ASC')
|
||||
{
|
||||
$this->field = $field;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Parser $parser The parser that serves as context.
|
||||
@ -110,4 +122,22 @@ class OrderKeyword extends Component
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OrderKeyword $component The component to be built.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component)
|
||||
{
|
||||
if (is_array($component)) {
|
||||
$ret = array();
|
||||
foreach ($component as $c) {
|
||||
$ret[] = static::build($c);
|
||||
}
|
||||
return implode(", ", $ret);
|
||||
} else {
|
||||
return Expression::build($component->field) . ' ' . $component->type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,4 +117,22 @@ class SetOperation extends Component
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SetOperation|SetOperation[] $component The component to be built.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component)
|
||||
{
|
||||
if (is_array($component)) {
|
||||
$ret = array();
|
||||
foreach ($component as $c) {
|
||||
$ret[] = static::build($c);
|
||||
}
|
||||
return implode(", ", $ret);
|
||||
} else {
|
||||
return $component->column . ' = ' . $component->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
41
libraries/sql-parser/src/Components/UnionKeyword.php
Normal file
41
libraries/sql-parser/src/Components/UnionKeyword.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* `UNION` keyword builder.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* `UNION` keyword builder.
|
||||
*
|
||||
* @category Keywords
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @author Dan Ungureanu <udan1107@gmail.com>
|
||||
* @license http://opensource.org/licenses/GPL-2.0 GNU Public License
|
||||
*/
|
||||
class UnionKeyword extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* @param SelectStatement[] $component The component to be built.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component)
|
||||
{
|
||||
$ret = array();
|
||||
foreach ($component as $c) {
|
||||
$ret[] = $c->build();
|
||||
}
|
||||
return implode(" UNION ", $ret);
|
||||
}
|
||||
}
|
||||
@ -144,15 +144,18 @@ class ContextMySql50000 extends Context
|
||||
'SQL_CALC_FOUND_ROWS' => 3,
|
||||
|
||||
'GROUP BY' => 7, 'NOT NULL' => 7, 'ORDER BY' => 7, 'SET NULL' => 7,
|
||||
'FULL JOIN' => 7, 'IF EXISTS' => 7, 'LEFT JOIN' => 7, 'NO ACTION' => 7,
|
||||
'ON DELETE' => 7, 'ON UPDATE' => 7,
|
||||
'INNER JOIN' => 7, 'OR REPLACE' => 7, 'RIGHT JOIN' => 7,
|
||||
'FOR EACH ROW' => 7, 'SQL SECURITY' => 7,
|
||||
'AND CHAIN' => 7, 'FULL JOIN' => 7, 'IF EXISTS' => 7, 'LEFT JOIN' => 7,
|
||||
'NO ACTION' => 7, 'ON DELETE' => 7, 'ON UPDATE' => 7,
|
||||
'INNER JOIN' => 7, 'NO RELEASE' => 7, 'OR REPLACE' => 7, 'RIGHT JOIN' => 7,
|
||||
'AND NO CHAIN' => 7, 'FOR EACH ROW' => 7, 'SQL SECURITY' => 7,
|
||||
'CHARACTER SET' => 7, 'IF NOT EXISTS' => 7,
|
||||
'DATA DIRECTORY' => 7,
|
||||
'DEFAULT CHARSET' => 7, 'DEFAULT COLLATE' => 7, 'INDEX DIRECTORY' => 7,
|
||||
'GENERATED ALWAYS' => 7,
|
||||
'START TRANSACTION' => 7,
|
||||
'SELECT TRANSACTION' => 7,
|
||||
'DEFAULT CHARACTER SET' => 7,
|
||||
'WITH CONSISTENT SNAPSHOT' => 7,
|
||||
|
||||
'XML' => 9,
|
||||
'ENUM' => 9, 'TEXT' => 9,
|
||||
|
||||
@ -156,15 +156,18 @@ class ContextMySql50100 extends Context
|
||||
'MASTER_SSL_VERIFY_SERVER_CERT' => 3,
|
||||
|
||||
'GROUP BY' => 7, 'NOT NULL' => 7, 'ORDER BY' => 7, 'SET NULL' => 7,
|
||||
'FULL JOIN' => 7, 'IF EXISTS' => 7, 'LEFT JOIN' => 7, 'NO ACTION' => 7,
|
||||
'ON DELETE' => 7, 'ON UPDATE' => 7,
|
||||
'INNER JOIN' => 7, 'OR REPLACE' => 7, 'RIGHT JOIN' => 7,
|
||||
'FOR EACH ROW' => 7, 'SQL SECURITY' => 7,
|
||||
'AND CHAIN' => 7, 'FULL JOIN' => 7, 'IF EXISTS' => 7, 'LEFT JOIN' => 7,
|
||||
'NO ACTION' => 7, 'ON DELETE' => 7, 'ON UPDATE' => 7,
|
||||
'INNER JOIN' => 7, 'NO RELEASE' => 7, 'OR REPLACE' => 7, 'RIGHT JOIN' => 7,
|
||||
'AND NO CHAIN' => 7, 'FOR EACH ROW' => 7, 'SQL SECURITY' => 7,
|
||||
'CHARACTER SET' => 7, 'IF NOT EXISTS' => 7,
|
||||
'DATA DIRECTORY' => 7,
|
||||
'DEFAULT CHARSET' => 7, 'DEFAULT COLLATE' => 7, 'INDEX DIRECTORY' => 7,
|
||||
'GENERATED ALWAYS' => 7,
|
||||
'START TRANSACTION' => 7,
|
||||
'SELECT TRANSACTION' => 7,
|
||||
'DEFAULT CHARACTER SET' => 7,
|
||||
'WITH CONSISTENT SNAPSHOT' => 7,
|
||||
|
||||
'XML' => 9,
|
||||
'ENUM' => 9, 'TEXT' => 9,
|
||||
|
||||
@ -161,15 +161,18 @@ class ContextMySql50500 extends Context
|
||||
'MASTER_SSL_VERIFY_SERVER_CERT' => 3,
|
||||
|
||||
'GROUP BY' => 7, 'NOT NULL' => 7, 'ORDER BY' => 7, 'SET NULL' => 7,
|
||||
'FULL JOIN' => 7, 'IF EXISTS' => 7, 'LEFT JOIN' => 7, 'NO ACTION' => 7,
|
||||
'ON DELETE' => 7, 'ON UPDATE' => 7,
|
||||
'INNER JOIN' => 7, 'OR REPLACE' => 7, 'RIGHT JOIN' => 7,
|
||||
'FOR EACH ROW' => 7, 'SQL SECURITY' => 7,
|
||||
'AND CHAIN' => 7, 'FULL JOIN' => 7, 'IF EXISTS' => 7, 'LEFT JOIN' => 7,
|
||||
'NO ACTION' => 7, 'ON DELETE' => 7, 'ON UPDATE' => 7,
|
||||
'INNER JOIN' => 7, 'NO RELEASE' => 7, 'OR REPLACE' => 7, 'RIGHT JOIN' => 7,
|
||||
'AND NO CHAIN' => 7, 'FOR EACH ROW' => 7, 'SQL SECURITY' => 7,
|
||||
'CHARACTER SET' => 7, 'IF NOT EXISTS' => 7,
|
||||
'DATA DIRECTORY' => 7,
|
||||
'DEFAULT CHARSET' => 7, 'DEFAULT COLLATE' => 7, 'INDEX DIRECTORY' => 7,
|
||||
'GENERATED ALWAYS' => 7,
|
||||
'START TRANSACTION' => 7,
|
||||
'SELECT TRANSACTION' => 7,
|
||||
'DEFAULT CHARACTER SET' => 7,
|
||||
'WITH CONSISTENT SNAPSHOT' => 7,
|
||||
|
||||
'XML' => 9,
|
||||
'ENUM' => 9, 'TEXT' => 9,
|
||||
|
||||
@ -166,15 +166,18 @@ class ContextMySql50600 extends Context
|
||||
'MASTER_SSL_VERIFY_SERVER_CERT' => 3,
|
||||
|
||||
'GROUP BY' => 7, 'NOT NULL' => 7, 'ORDER BY' => 7, 'SET NULL' => 7,
|
||||
'FULL JOIN' => 7, 'IF EXISTS' => 7, 'LEFT JOIN' => 7, 'NO ACTION' => 7,
|
||||
'ON DELETE' => 7, 'ON UPDATE' => 7,
|
||||
'INNER JOIN' => 7, 'OR REPLACE' => 7, 'RIGHT JOIN' => 7,
|
||||
'FOR EACH ROW' => 7, 'SQL SECURITY' => 7,
|
||||
'AND CHAIN' => 7, 'FULL JOIN' => 7, 'IF EXISTS' => 7, 'LEFT JOIN' => 7,
|
||||
'NO ACTION' => 7, 'ON DELETE' => 7, 'ON UPDATE' => 7,
|
||||
'INNER JOIN' => 7, 'NO RELEASE' => 7, 'OR REPLACE' => 7, 'RIGHT JOIN' => 7,
|
||||
'AND NO CHAIN' => 7, 'FOR EACH ROW' => 7, 'SQL SECURITY' => 7,
|
||||
'CHARACTER SET' => 7, 'IF NOT EXISTS' => 7,
|
||||
'DATA DIRECTORY' => 7,
|
||||
'DEFAULT CHARSET' => 7, 'DEFAULT COLLATE' => 7, 'INDEX DIRECTORY' => 7,
|
||||
'GENERATED ALWAYS' => 7,
|
||||
'START TRANSACTION' => 7,
|
||||
'SELECT TRANSACTION' => 7,
|
||||
'DEFAULT CHARACTER SET' => 7,
|
||||
'WITH CONSISTENT SNAPSHOT' => 7,
|
||||
|
||||
'XML' => 9,
|
||||
'ENUM' => 9, 'TEXT' => 9,
|
||||
|
||||
@ -174,15 +174,18 @@ class ContextMySql50700 extends Context
|
||||
'MASTER_SSL_VERIFY_SERVER_CERT' => 3,
|
||||
|
||||
'GROUP BY' => 7, 'NOT NULL' => 7, 'ORDER BY' => 7, 'SET NULL' => 7,
|
||||
'FULL JOIN' => 7, 'IF EXISTS' => 7, 'LEFT JOIN' => 7, 'NO ACTION' => 7,
|
||||
'ON DELETE' => 7, 'ON UPDATE' => 7,
|
||||
'INNER JOIN' => 7, 'OR REPLACE' => 7, 'RIGHT JOIN' => 7,
|
||||
'FOR EACH ROW' => 7, 'SQL SECURITY' => 7,
|
||||
'AND CHAIN' => 7, 'FULL JOIN' => 7, 'IF EXISTS' => 7, 'LEFT JOIN' => 7,
|
||||
'NO ACTION' => 7, 'ON DELETE' => 7, 'ON UPDATE' => 7,
|
||||
'INNER JOIN' => 7, 'NO RELEASE' => 7, 'OR REPLACE' => 7, 'RIGHT JOIN' => 7,
|
||||
'AND NO CHAIN' => 7, 'FOR EACH ROW' => 7, 'SQL SECURITY' => 7,
|
||||
'CHARACTER SET' => 7, 'IF NOT EXISTS' => 7,
|
||||
'DATA DIRECTORY' => 7,
|
||||
'DEFAULT CHARSET' => 7, 'DEFAULT COLLATE' => 7, 'INDEX DIRECTORY' => 7,
|
||||
'GENERATED ALWAYS' => 7,
|
||||
'START TRANSACTION' => 7,
|
||||
'SELECT TRANSACTION' => 7,
|
||||
'DEFAULT CHARACTER SET' => 7,
|
||||
'WITH CONSISTENT SNAPSHOT' => 7,
|
||||
|
||||
'XML' => 9,
|
||||
'ENUM' => 9, 'TEXT' => 9,
|
||||
|
||||
@ -28,8 +28,9 @@ namespace {
|
||||
|
||||
namespace SqlParser {
|
||||
|
||||
use SqlParser\Statements\SelectStatement;
|
||||
use SqlParser\Exceptions\ParserException;
|
||||
use SqlParser\Statements\SelectStatement;
|
||||
use SqlParser\Statements\TransactionStatement;
|
||||
|
||||
/**
|
||||
* Takes multiple tokens (contained in a Lexer instance) as input and builds a
|
||||
@ -50,47 +51,54 @@ namespace SqlParser {
|
||||
*/
|
||||
public static $STATEMENT_PARSERS = array(
|
||||
|
||||
'EXPLAIN' => 'SqlParser\\Statements\\ExplainStatement',
|
||||
'EXPLAIN' => 'SqlParser\\Statements\\ExplainStatement',
|
||||
|
||||
// Table Maintenance Statements
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/table-maintenance-sql.html
|
||||
'ANALYZE' => 'SqlParser\\Statements\\AnalyzeStatement',
|
||||
'BACKUP' => 'SqlParser\\Statements\\BackupStatement',
|
||||
'CHECK' => 'SqlParser\\Statements\\CheckStatement',
|
||||
'CHECKSUM' => 'SqlParser\\Statements\\ChecksumStatement',
|
||||
'OPTIMIZE' => 'SqlParser\\Statements\\OptimizeStatement',
|
||||
'REPAIR' => 'SqlParser\\Statements\\RepairStatement',
|
||||
'RESTORE' => 'SqlParser\\Statements\\RestoreStatement',
|
||||
'ANALYZE' => 'SqlParser\\Statements\\AnalyzeStatement',
|
||||
'BACKUP' => 'SqlParser\\Statements\\BackupStatement',
|
||||
'CHECK' => 'SqlParser\\Statements\\CheckStatement',
|
||||
'CHECKSUM' => 'SqlParser\\Statements\\ChecksumStatement',
|
||||
'OPTIMIZE' => 'SqlParser\\Statements\\OptimizeStatement',
|
||||
'REPAIR' => 'SqlParser\\Statements\\RepairStatement',
|
||||
'RESTORE' => 'SqlParser\\Statements\\RestoreStatement',
|
||||
|
||||
// Database Administration Statements
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-server-administration.html
|
||||
'SET' => '',
|
||||
'SHOW' => 'SqlParser\\Statements\\ShowStatement',
|
||||
'SET' => '',
|
||||
'SHOW' => 'SqlParser\\Statements\\ShowStatement',
|
||||
|
||||
// Data Definition Statements.
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-data-definition.html
|
||||
'ALTER' => 'SqlParser\\Statements\\AlterStatement',
|
||||
'CREATE' => 'SqlParser\\Statements\\CreateStatement',
|
||||
'DROP' => 'SqlParser\\Statements\\DropStatement',
|
||||
'RENAME' => 'SqlParser\\Statements\\RenameStatement',
|
||||
'TRUNCATE' => 'SqlParser\\Statements\\TruncateStatement',
|
||||
'ALTER' => 'SqlParser\\Statements\\AlterStatement',
|
||||
'CREATE' => 'SqlParser\\Statements\\CreateStatement',
|
||||
'DROP' => 'SqlParser\\Statements\\DropStatement',
|
||||
'RENAME' => 'SqlParser\\Statements\\RenameStatement',
|
||||
'TRUNCATE' => 'SqlParser\\Statements\\TruncateStatement',
|
||||
|
||||
// Data Manipulation Statements.
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-data-manipulation.html
|
||||
'CALL' => 'SqlParser\\Statements\\CallStatement',
|
||||
'DELETE' => 'SqlParser\\Statements\\DeleteStatement',
|
||||
'DO' => '',
|
||||
'HANDLER' => '',
|
||||
'INSERT' => 'SqlParser\\Statements\\InsertStatement',
|
||||
'LOAD' => '',
|
||||
'REPLACE' => 'SqlParser\\Statements\\ReplaceStatement',
|
||||
'SELECT' => 'SqlParser\\Statements\\SelectStatement',
|
||||
'UPDATE' => 'SqlParser\\Statements\\UpdateStatement',
|
||||
'CALL' => 'SqlParser\\Statements\\CallStatement',
|
||||
'DELETE' => 'SqlParser\\Statements\\DeleteStatement',
|
||||
'DO' => '',
|
||||
'HANDLER' => '',
|
||||
'INSERT' => 'SqlParser\\Statements\\InsertStatement',
|
||||
'LOAD' => '',
|
||||
'REPLACE' => 'SqlParser\\Statements\\ReplaceStatement',
|
||||
'SELECT' => 'SqlParser\\Statements\\SelectStatement',
|
||||
'UPDATE' => 'SqlParser\\Statements\\UpdateStatement',
|
||||
|
||||
// Prepared Statements.
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-prepared-statements.html
|
||||
'PREPARE' => '',
|
||||
'EXECUTE' => '',
|
||||
'PREPARE' => '',
|
||||
'EXECUTE' => '',
|
||||
|
||||
// Transactional and Locking Statements
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/commit.html
|
||||
'START TRANSACTION' => 'SqlParser\\Statements\\TransactionStatement',
|
||||
'BEGIN' => 'SqlParser\\Statements\\TransactionStatement',
|
||||
'COMMIT' => 'SqlParser\\Statements\\TransactionStatement',
|
||||
'ROLLBACK' => 'SqlParser\\Statements\\TransactionStatement',
|
||||
);
|
||||
|
||||
/**
|
||||
@ -106,6 +114,12 @@ namespace SqlParser {
|
||||
'field' => 'options',
|
||||
),
|
||||
|
||||
// This is used only for building.
|
||||
'UNION' => array(
|
||||
'class' => 'SqlParser\\Components\\UnionKeyword',
|
||||
'field' => 'union',
|
||||
),
|
||||
|
||||
'ALTER' => array(
|
||||
'class' => 'SqlParser\\Components\\Expression',
|
||||
'field' => 'table',
|
||||
@ -311,6 +325,12 @@ namespace SqlParser {
|
||||
public function parse()
|
||||
{
|
||||
|
||||
/**
|
||||
* Last transaction.
|
||||
* @var TransactionStatement
|
||||
*/
|
||||
$lastTransaction = null;
|
||||
|
||||
/**
|
||||
* Last parsed statement.
|
||||
* @var Statement $lastStatement
|
||||
@ -321,7 +341,7 @@ namespace SqlParser {
|
||||
* Whether a union is parsed or not.
|
||||
* @var bool $inUnion
|
||||
*/
|
||||
$inUnion = true;
|
||||
$inUnion = false;
|
||||
|
||||
/**
|
||||
* The index of the last token from the last statement.
|
||||
@ -400,21 +420,62 @@ namespace SqlParser {
|
||||
$statement->last = $list->idx;
|
||||
$prevLastIdx = $list->idx;
|
||||
|
||||
// Finally, storing the statement.
|
||||
// Handles unions.
|
||||
if (($inUnion)
|
||||
&& ($lastStatement instanceof SelectStatement)
|
||||
&& ($statement instanceof SelectStatement)
|
||||
) {
|
||||
|
||||
/**
|
||||
* Last SELECT statement.
|
||||
* @var SelectStatement $lastStatement
|
||||
*/
|
||||
$lastStatement->union[] = $statement;
|
||||
|
||||
// if there are no no delimiting brackets, the `ORDER` and
|
||||
// `LIMIT` keywords actually belong to the first statement.
|
||||
$lastStatement->order = $statement->order;
|
||||
$lastStatement->limit = $statement->limit;
|
||||
$statement->order = array();
|
||||
$statement->limit = null;
|
||||
|
||||
// The statement actually ends where the last statement in
|
||||
// union ends.
|
||||
$lastStatement->last = $statement->last;
|
||||
|
||||
$inUnion = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handles transactions.
|
||||
if ($statement instanceof TransactionStatement) {
|
||||
if ($statement->type === TransactionStatement::TYPE_BEGIN) {
|
||||
$lastTransaction = $statement;
|
||||
$this->statements[] = $statement;
|
||||
} elseif ($statement->type === TransactionStatement::TYPE_END) {
|
||||
if ($lastTransaction === null) {
|
||||
// Even though an error occurred, the query is being
|
||||
// saved.
|
||||
$this->statements[] = $statement;
|
||||
$this->error(
|
||||
__('No transaction was previously started.'),
|
||||
$token
|
||||
);
|
||||
} else {
|
||||
$lastTransaction->end = $statement;
|
||||
}
|
||||
$lastTransaction = null;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Finally, storing the statement.
|
||||
if ($lastTransaction !== null) {
|
||||
$lastTransaction->statements[] = $statement;
|
||||
} else {
|
||||
$this->statements[] = $statement;
|
||||
$lastStatement = $statement;
|
||||
}
|
||||
$lastStatement = $statement;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,11 +122,6 @@ abstract class Statement
|
||||
*/
|
||||
$type = $clause[1];
|
||||
|
||||
// Checking if there is any parser (builder) for this clause.
|
||||
if (empty(Parser::$KEYWORD_PARSERS[$name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* The builder (parser) of this clause.
|
||||
* @var string $class
|
||||
@ -169,6 +164,16 @@ abstract class Statement
|
||||
*/
|
||||
public function parse(Parser $parser, TokensList $list)
|
||||
{
|
||||
/**
|
||||
* Whether the beginning of this statement was previously parsed.
|
||||
*
|
||||
* This is used to delimit statements that don't use the usual
|
||||
* delimiters.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
$parsedBeginning = false;
|
||||
|
||||
// This may be corrected by the parser.
|
||||
$this->first = $list->idx;
|
||||
|
||||
@ -178,7 +183,7 @@ abstract class Statement
|
||||
* default.
|
||||
* @var bool $parsedOptions
|
||||
*/
|
||||
$parsedOptions = !empty(static::$OPTIONS) ? false : true;
|
||||
$parsedOptions = empty(static::$OPTIONS);
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
@ -236,8 +241,21 @@ abstract class Statement
|
||||
}
|
||||
|
||||
if (!empty(Parser::$STATEMENT_PARSERS[$token->value])) {
|
||||
if ($parsedBeginning) {
|
||||
// New statement has started. We let the parser construct a
|
||||
// new statement and parse that one
|
||||
$parser->error(
|
||||
__('A new statement was found, but no delimiter between them.'),
|
||||
$token
|
||||
);
|
||||
break;
|
||||
}
|
||||
$parsedBeginning = true;
|
||||
if (!$parsedOptions) {
|
||||
++$list->idx; // Skipping keyword.
|
||||
if (empty(static::$OPTIONS[$token->value])) {
|
||||
// Skipping keyword because if it is not a option.
|
||||
++$list->idx;
|
||||
}
|
||||
$this->options = OptionsArray::parse(
|
||||
$parser,
|
||||
$list,
|
||||
@ -256,7 +274,7 @@ abstract class Statement
|
||||
|
||||
// Parsing this keyword.
|
||||
if ($class !== null) {
|
||||
++$list->idx; // Skipping keyword.
|
||||
++$list->idx; // Skipping keyword or last option.
|
||||
$this->$field = $class::parse($parser, $list, $options);
|
||||
}
|
||||
|
||||
|
||||
@ -99,6 +99,9 @@ class SelectStatement extends Statement
|
||||
'PROCEDURE' => array('PROCEDURE', 3),
|
||||
'INTO' => array('INTO', 3),
|
||||
'UNION' => array('UNION', 3),
|
||||
// These are available only when `UNION` is present.
|
||||
// 'ORDER BY' => array('ORDER BY', 3),
|
||||
// 'LIMIT' => array('LIMIT', 3),
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
119
libraries/sql-parser/src/Statements/TransactionStatement.php
Normal file
119
libraries/sql-parser/src/Statements/TransactionStatement.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Transaction statement.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Statements
|
||||
*/
|
||||
namespace SqlParser\Statements;
|
||||
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Statement;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
use SqlParser\Components\Expression;
|
||||
use SqlParser\Components\OptionsArray;
|
||||
|
||||
/**
|
||||
* Transaction statement.
|
||||
*
|
||||
* @category Statements
|
||||
* @package SqlParser
|
||||
* @subpackage Statements
|
||||
* @author Dan Ungureanu <udan1107@gmail.com>
|
||||
* @license http://opensource.org/licenses/GPL-2.0 GNU Public License
|
||||
*/
|
||||
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 Statements[]
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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 = TransactionStatement::TYPE_BEGIN;
|
||||
} elseif (($this->options->has('COMMIT'))
|
||||
|| ($this->options->has('ROLLBACK'))
|
||||
) {
|
||||
$this->type = TransactionStatement::TYPE_END;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$ret = OptionsArray::build($this->options);
|
||||
if ($this->type === TransactionStatement::TYPE_BEGIN) {
|
||||
foreach ($this->statements as $statement) {
|
||||
$ret .= ';' . $statement->build();
|
||||
}
|
||||
$ret .= ';' . $this->end->build();
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
@ -533,6 +533,14 @@ class Query
|
||||
*/
|
||||
$clauses = array_flip(array_keys($statement::$CLAUSES));
|
||||
|
||||
// This is a cheap fix for `SELECT` statements that contain `UNION`.
|
||||
// Replacing the `ORDER BY` or `LIMIT` clauses should replace the last
|
||||
// clause.
|
||||
if (($statement instanceof SelectStatement) && (!empty($statement->union))) {
|
||||
$clauses['ORDER BY'] = count($clauses) + 1;
|
||||
$clauses['LIMIT'] = count($clauses) + 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lexer used for lexing the clause.
|
||||
* @var Lexer $lexer
|
||||
@ -552,10 +560,9 @@ class Query
|
||||
$clauseIdx = $clauses[$clauseType];
|
||||
|
||||
$firstClauseIdx = $clauseIdx;
|
||||
|
||||
$lastClauseIdx = $clauseIdx + 1;
|
||||
|
||||
// Determining the behaviour of this function.
|
||||
// Determining the behavior of this function.
|
||||
if ($type === -1) {
|
||||
$firstClauseIdx = -1; // Something small enough.
|
||||
$lastClauseIdx = $clauseIdx - 1;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user