Initial commit
This commit is contained in:
265
#pma/libraries/sql-parser/src/Components/AlterOperation.php
Normal file
265
#pma/libraries/sql-parser/src/Components/AlterOperation.php
Normal file
@@ -0,0 +1,265 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Parses an alter operation.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* Parses an alter operation.
|
||||
*
|
||||
* @category Components
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class AlterOperation extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* 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'),
|
||||
'UPGRADE' => array(1, 'var'),
|
||||
'COLLATE' => array(2, 'var'),
|
||||
'DEFAULT COLLATE' => array(2, 'var'),
|
||||
);
|
||||
|
||||
/**
|
||||
* All table options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $TABLE_OPTIONS = array(
|
||||
'ENGINE' => array(1, 'var='),
|
||||
'AUTO_INCREMENT' => array(1, 'var='),
|
||||
'AVG_ROW_LENGTH' => array(1, 'var'),
|
||||
'MAX_ROWS' => array(1, 'var'),
|
||||
'ROW_FORMAT' => array(1, 'var'),
|
||||
'COMMENT' => array(1, 'var'),
|
||||
'ADD' => 1,
|
||||
'ALTER' => 1,
|
||||
'ANALYZE' => 1,
|
||||
'CHANGE' => 1,
|
||||
'CHECK' => 1,
|
||||
'COALESCE' => 1,
|
||||
'CONVERT' => 1,
|
||||
'DISABLE' => 1,
|
||||
'DISCARD' => 1,
|
||||
'DROP' => 1,
|
||||
'ENABLE' => 1,
|
||||
'IMPORT' => 1,
|
||||
'MODIFY' => 1,
|
||||
'OPTIMIZE' => 1,
|
||||
'ORDER' => 1,
|
||||
'PARTITION' => 1,
|
||||
'REBUILD' => 1,
|
||||
'REMOVE' => 1,
|
||||
'RENAME' => 1,
|
||||
'REORGANIZE' => 1,
|
||||
'REPAIR' => 1,
|
||||
'UPGRADE' => 1,
|
||||
|
||||
'COLUMN' => 2,
|
||||
'CONSTRAINT' => 2,
|
||||
'DEFAULT' => 2,
|
||||
'TO' => 2,
|
||||
'BY' => 2,
|
||||
'FOREIGN' => 2,
|
||||
'FULLTEXT' => 2,
|
||||
'KEY' => 2,
|
||||
'KEYS' => 2,
|
||||
'PARTITIONING' => 2,
|
||||
'PRIMARY KEY' => 2,
|
||||
'SPATIAL' => 2,
|
||||
'TABLESPACE' => 2,
|
||||
'INDEX' => 2,
|
||||
);
|
||||
|
||||
/**
|
||||
* All view options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $VIEW_OPTIONS = array(
|
||||
'AS' => 1,
|
||||
);
|
||||
|
||||
/**
|
||||
* Options of this operation.
|
||||
*
|
||||
* @var OptionsArray
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* The altered field.
|
||||
*
|
||||
* @var Expression
|
||||
*/
|
||||
public $field;
|
||||
|
||||
/**
|
||||
* Unparsed tokens.
|
||||
*
|
||||
* @var Token[]|string
|
||||
*/
|
||||
public $unknown = array();
|
||||
|
||||
/**
|
||||
* @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 AlterOperation
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = new AlterOperation();
|
||||
|
||||
/**
|
||||
* Counts brackets.
|
||||
*
|
||||
* @var int $brackets
|
||||
*/
|
||||
$brackets = 0;
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 ---------------------[ options ]---------------------> 1
|
||||
*
|
||||
* 1 ----------------------[ field ]----------------------> 2
|
||||
*
|
||||
* 2 -------------------------[ , ]-----------------------> 0
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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;
|
||||
}
|
||||
|
||||
// Skipping whitespaces.
|
||||
if ($token->type === Token::TYPE_WHITESPACE) {
|
||||
if ($state === 2) {
|
||||
// When parsing the unknown part, the whitespaces are
|
||||
// included to not break anything.
|
||||
$ret->unknown[] = $token;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
$ret->options = OptionsArray::parse($parser, $list, $options);
|
||||
|
||||
if ($ret->options->has('AS')) {
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
if ($list->tokens[$list->idx]->type === Token::TYPE_DELIMITER) {
|
||||
break;
|
||||
}
|
||||
$ret->unknown[] = $list->tokens[$list->idx];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
$ret->field = Expression::parse(
|
||||
$parser,
|
||||
$list,
|
||||
array(
|
||||
'breakOnAlias' => true,
|
||||
'parseField' => 'column',
|
||||
)
|
||||
);
|
||||
if ($ret->field === null) {
|
||||
// No field was read. We go back one token so the next
|
||||
// iteration will parse the same token, but in state 2.
|
||||
--$list->idx;
|
||||
}
|
||||
$state = 2;
|
||||
} elseif ($state === 2) {
|
||||
if ($token->type === Token::TYPE_OPERATOR) {
|
||||
if ($token->value === '(') {
|
||||
++$brackets;
|
||||
} elseif ($token->value === ')') {
|
||||
--$brackets;
|
||||
} elseif (($token->value === ',') && ($brackets === 0)) {
|
||||
break;
|
||||
}
|
||||
} elseif (!empty(Parser::$STATEMENT_PARSERS[$token->value])) {
|
||||
// We have reached the end of ALTER operation and suddenly found
|
||||
// a start to new statement, but have not find a delimiter between them
|
||||
|
||||
if (! ($token->value == 'SET' && $list->tokens[$list->idx - 1]->value == 'CHARACTER')) {
|
||||
$parser->error(
|
||||
__('A new statement was found, but no delimiter between it and the previous one.'),
|
||||
$token
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
$ret->unknown[] = $token;
|
||||
}
|
||||
}
|
||||
|
||||
if ($ret->options->isEmpty()) {
|
||||
$parser->error(
|
||||
__('Unrecognized alter operation.'),
|
||||
$list->tokens[$list->idx]
|
||||
);
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AlterOperation $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
$ret = $component->options . ' ';
|
||||
if ((isset($component->field)) && ($component->field !== '')) {
|
||||
$ret .= $component->field . ' ';
|
||||
}
|
||||
$ret .= TokensList::build($component->unknown);
|
||||
return $ret;
|
||||
}
|
||||
}
|
133
#pma/libraries/sql-parser/src/Components/Array2d.php
Normal file
133
#pma/libraries/sql-parser/src/Components/Array2d.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* `VALUES` keyword parser.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* `VALUES` keyword parser.
|
||||
*
|
||||
* @category Keywords
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class Array2d extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* @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 ArrayObj[]
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = array();
|
||||
|
||||
/**
|
||||
* The number of values in each set.
|
||||
*
|
||||
* @var int $count
|
||||
*/
|
||||
$count = -1;
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 ----------------------[ array ]----------------------> 1
|
||||
*
|
||||
* 1 ------------------------[ , ]------------------------> 0
|
||||
* 1 -----------------------[ else ]----------------------> (END)
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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;
|
||||
}
|
||||
|
||||
// No keyword is expected.
|
||||
if (($token->type === Token::TYPE_KEYWORD) && ($token->flags & Token::FLAG_KEYWORD_RESERVED)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
if ($token->value === '(') {
|
||||
$arr = ArrayObj::parse($parser, $list, $options);
|
||||
$arrCount = count($arr->values);
|
||||
if ($count === -1) {
|
||||
$count = $arrCount;
|
||||
} elseif ($arrCount != $count) {
|
||||
$parser->error(
|
||||
sprintf(
|
||||
__('%1$d values were expected, but found %2$d.'),
|
||||
$count,
|
||||
$arrCount
|
||||
),
|
||||
$token
|
||||
);
|
||||
}
|
||||
$ret[] = $arr;
|
||||
$state = 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} elseif ($state === 1) {
|
||||
if ($token->value === ',') {
|
||||
$state = 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
$parser->error(
|
||||
__('An opening bracket followed by a set of values was expected.'),
|
||||
$list->tokens[$list->idx]
|
||||
);
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayObj[] $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
return ArrayObj::build($component);
|
||||
}
|
||||
}
|
193
#pma/libraries/sql-parser/src/Components/ArrayObj.php
Normal file
193
#pma/libraries/sql-parser/src/Components/ArrayObj.php
Normal file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Parses an array.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* Parses an array.
|
||||
*
|
||||
* @category Components
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class ArrayObj extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* The array that contains the unprocessed value of each token.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $raw = array();
|
||||
|
||||
/**
|
||||
* The array that contains the processed value of each token.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $values = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $raw The unprocessed values.
|
||||
* @param array $values The processed values.
|
||||
*/
|
||||
public function __construct(array $raw = array(), array $values = array())
|
||||
{
|
||||
$this->raw = $raw;
|
||||
$this->values = $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 ArrayObj|Component[]
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = empty($options['type']) ? new ArrayObj() : array();
|
||||
|
||||
/**
|
||||
* The last raw expression.
|
||||
*
|
||||
* @var string $lastRaw
|
||||
*/
|
||||
$lastRaw = '';
|
||||
|
||||
/**
|
||||
* The last value.
|
||||
*
|
||||
* @var string $lastValue
|
||||
*/
|
||||
$lastValue = '';
|
||||
|
||||
/**
|
||||
* Counts brackets.
|
||||
*
|
||||
* @var int $brackets
|
||||
*/
|
||||
$brackets = 0;
|
||||
|
||||
/**
|
||||
* Last separator (bracket or comma).
|
||||
*
|
||||
* @var boolean $isCommaLast
|
||||
*/
|
||||
$isCommaLast = false;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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)
|
||||
) {
|
||||
$lastRaw .= $token->token;
|
||||
$lastValue = trim($lastValue) . ' ';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($brackets === 0)
|
||||
&& (($token->type !== Token::TYPE_OPERATOR)
|
||||
|| ($token->value !== '('))
|
||||
) {
|
||||
$parser->error(__('An opening bracket was expected.'), $token);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($token->type === Token::TYPE_OPERATOR) {
|
||||
if ($token->value === '(') {
|
||||
if (++$brackets === 1) { // 1 is the base level.
|
||||
continue;
|
||||
}
|
||||
} elseif ($token->value === ')') {
|
||||
if (--$brackets === 0) { // Array ended.
|
||||
break;
|
||||
}
|
||||
} elseif ($token->value === ',') {
|
||||
if ($brackets === 1) {
|
||||
$isCommaLast = true;
|
||||
if (empty($options['type'])) {
|
||||
$ret->raw[] = trim($lastRaw);
|
||||
$ret->values[] = trim($lastValue);
|
||||
$lastRaw = $lastValue = '';
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($options['type'])) {
|
||||
$lastRaw .= $token->token;
|
||||
$lastValue .= $token->value;
|
||||
} else {
|
||||
$ret[] = $options['type']::parse(
|
||||
$parser,
|
||||
$list,
|
||||
empty($options['typeOptions']) ? array() : $options['typeOptions']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Handling last element.
|
||||
//
|
||||
// This is treated differently to treat the following cases:
|
||||
//
|
||||
// => array()
|
||||
// (,) => array('', '')
|
||||
// () => array()
|
||||
// (a,) => array('a', '')
|
||||
// (a) => array('a')
|
||||
//
|
||||
$lastRaw = trim($lastRaw);
|
||||
if ((empty($options['type']))
|
||||
&& ((strlen($lastRaw) > 0) || ($isCommaLast))
|
||||
) {
|
||||
$ret->raw[] = $lastRaw;
|
||||
$ret->values[] = trim($lastValue);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayObj|ArrayObj[] $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
if (is_array($component)) {
|
||||
return implode(', ', $component);
|
||||
} elseif (!empty($component->raw)) {
|
||||
return '(' . implode(', ', $component->raw) . ')';
|
||||
} else {
|
||||
return '(' . implode(', ', $component->values) . ')';
|
||||
}
|
||||
}
|
||||
}
|
249
#pma/libraries/sql-parser/src/Components/CaseExpression.php
Normal file
249
#pma/libraries/sql-parser/src/Components/CaseExpression.php
Normal file
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Parses a reference to a CASE expression
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
|
||||
/**
|
||||
* Parses a reference to a CASE expression
|
||||
*
|
||||
* @category Components
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class CaseExpression extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* The value to be compared
|
||||
*
|
||||
* @var Expression
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* The conditions in WHEN clauses
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $conditions;
|
||||
|
||||
/**
|
||||
* The results matching with the WHEN clauses
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $results;
|
||||
|
||||
/**
|
||||
* The values to be compared against
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $compare_values;
|
||||
|
||||
/**
|
||||
* The result in ELSE section of expr
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $else_result;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param Parser $parser The parser that serves as context.
|
||||
* @param TokensList $list The list of tokens that are being parsed.
|
||||
*
|
||||
* @return Expression
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = new CaseExpression();
|
||||
|
||||
/**
|
||||
* State of parser
|
||||
*
|
||||
* @var int $parser
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
/**
|
||||
* Syntax type (type 0 or type 1)
|
||||
*
|
||||
* @var int $type
|
||||
*/
|
||||
$type = 0;
|
||||
|
||||
++$list->idx; // Skip 'CASE'
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $token
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
// Skipping whitespaces and comments.
|
||||
if (($token->type === Token::TYPE_WHITESPACE)
|
||||
|| ($token->type === Token::TYPE_COMMENT)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
if ($token->type === Token::TYPE_KEYWORD
|
||||
&& $token->value === 'WHEN'
|
||||
) {
|
||||
++$list->idx; // Skip 'WHEN'
|
||||
$new_condition = Condition::parse($parser, $list);
|
||||
$type = 1;
|
||||
$state = 1;
|
||||
$ret->conditions[] = $new_condition;
|
||||
} elseif ($token->type === Token::TYPE_KEYWORD
|
||||
&& $token->value === 'ELSE'
|
||||
) {
|
||||
++$list->idx; // Skip 'ELSE'
|
||||
$ret->else_result = Expression::parse($parser, $list);
|
||||
$state = 0; // last clause of CASE expression
|
||||
} elseif ($token->type === Token::TYPE_KEYWORD
|
||||
&& ($token->value === 'END'
|
||||
|| $token->value === 'end')
|
||||
) {
|
||||
$state = 3; // end of CASE expression
|
||||
++$list->idx;
|
||||
break;
|
||||
} elseif ($token->type === Token::TYPE_KEYWORD) {
|
||||
$parser->error(__('Unexpected keyword.'), $token);
|
||||
break;
|
||||
} else {
|
||||
$ret->value = Expression::parse($parser, $list);
|
||||
$type = 0;
|
||||
$state = 1;
|
||||
}
|
||||
} elseif ($state === 1) {
|
||||
if ($type === 0) {
|
||||
if ($token->type === Token::TYPE_KEYWORD
|
||||
&& $token->value === 'WHEN'
|
||||
) {
|
||||
++$list->idx; // Skip 'WHEN'
|
||||
$new_value = Expression::parse($parser, $list);
|
||||
$state = 2;
|
||||
$ret->compare_values[] = $new_value;
|
||||
} elseif ($token->type === Token::TYPE_KEYWORD
|
||||
&& $token->value === 'ELSE'
|
||||
) {
|
||||
++$list->idx; // Skip 'ELSE'
|
||||
$ret->else_result = Expression::parse($parser, $list);
|
||||
$state = 0; // last clause of CASE expression
|
||||
} elseif ($token->type === Token::TYPE_KEYWORD
|
||||
&& ($token->value === 'END'
|
||||
|| $token->value === 'end')
|
||||
) {
|
||||
$state = 3; // end of CASE expression
|
||||
++$list->idx;
|
||||
break;
|
||||
} elseif ($token->type === Token::TYPE_KEYWORD) {
|
||||
$parser->error(__('Unexpected keyword.'), $token);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ($token->type === Token::TYPE_KEYWORD
|
||||
&& $token->value === 'THEN'
|
||||
) {
|
||||
++$list->idx; // Skip 'THEN'
|
||||
$new_result = Expression::parse($parser, $list);
|
||||
$state = 0;
|
||||
$ret->results[] = $new_result;
|
||||
} elseif ($token->type === Token::TYPE_KEYWORD) {
|
||||
$parser->error(__('Unexpected keyword.'), $token);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} elseif ($state === 2) {
|
||||
if ($type === 0) {
|
||||
if ($token->type === Token::TYPE_KEYWORD
|
||||
&& $token->value === 'THEN'
|
||||
) {
|
||||
++$list->idx; // Skip 'THEN'
|
||||
$new_result = Expression::parse($parser, $list);
|
||||
$ret->results[] = $new_result;
|
||||
$state = 1;
|
||||
} elseif ($token->type === Token::TYPE_KEYWORD) {
|
||||
$parser->error(__('Unexpected keyword.'), $token);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($state !== 3) {
|
||||
$parser->error(
|
||||
__('Unexpected end of CASE expression'),
|
||||
$list->tokens[$list->idx - 1]
|
||||
);
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Expression $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
$ret = 'CASE ';
|
||||
if (isset($component->value)) {
|
||||
// Syntax type 0
|
||||
$ret .= $component->value . ' ';
|
||||
for (
|
||||
$i = 0;
|
||||
$i < count($component->compare_values) && $i < count($component->results);
|
||||
++$i
|
||||
) {
|
||||
$ret .= 'WHEN ' . $component->compare_values[$i] . ' ';
|
||||
$ret .= 'THEN ' . $component->results[$i] . ' ';
|
||||
}
|
||||
} else {
|
||||
// Syntax type 1
|
||||
for (
|
||||
$i = 0;
|
||||
$i < count($component->conditions) && $i < count($component->results);
|
||||
++$i
|
||||
) {
|
||||
$ret .= 'WHEN ' . Condition::build($component->conditions[$i]) . ' ';
|
||||
$ret .= 'THEN ' . $component->results[$i] . ' ';
|
||||
}
|
||||
}
|
||||
if (isset($component->else_result)) {
|
||||
$ret .= 'ELSE ' . $component->else_result . ' ';
|
||||
}
|
||||
$ret .= 'END';
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
231
#pma/libraries/sql-parser/src/Components/Condition.php
Normal file
231
#pma/libraries/sql-parser/src/Components/Condition.php
Normal file
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* `WHERE` keyword parser.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* `WHERE` keyword parser.
|
||||
*
|
||||
* @category Keywords
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class Condition extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* Logical operators that can be used to delimit expressions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $DELIMITERS = array('&&', '||', 'AND', 'OR', 'XOR');
|
||||
|
||||
/**
|
||||
* List of allowed reserved keywords in conditions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $ALLOWED_KEYWORDS = array(
|
||||
'ALL' => 1,
|
||||
'AND' => 1,
|
||||
'BETWEEN' => 1,
|
||||
'EXISTS' => 1,
|
||||
'IF' => 1,
|
||||
'IN' => 1,
|
||||
'INTERVAL' => 1,
|
||||
'IS' => 1,
|
||||
'LIKE' => 1,
|
||||
'MATCH' => 1,
|
||||
'NOT IN' => 1,
|
||||
'NOT NULL' => 1,
|
||||
'NOT' => 1,
|
||||
'NULL' => 1,
|
||||
'OR' => 1,
|
||||
'REGEXP' => 1,
|
||||
'RLIKE' => 1,
|
||||
'XOR' => 1,
|
||||
);
|
||||
|
||||
/**
|
||||
* Identifiers recognized.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $identifiers = array();
|
||||
|
||||
/**
|
||||
* Whether this component is an operator.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $isOperator = false;
|
||||
|
||||
/**
|
||||
* The condition.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $expr;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $expr The condition or the operator.
|
||||
*/
|
||||
public function __construct($expr = null)
|
||||
{
|
||||
$this->expr = trim($expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 Condition[]
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = array();
|
||||
|
||||
$expr = new Condition();
|
||||
|
||||
/**
|
||||
* Counts brackets.
|
||||
*
|
||||
* @var int $brackets
|
||||
*/
|
||||
$brackets = 0;
|
||||
|
||||
/**
|
||||
* Whether there was a `BETWEEN` keyword before or not.
|
||||
*
|
||||
* It is required to keep track of them because their structure contains
|
||||
* the keyword `AND`, which is also an operator that delimits
|
||||
* expressions.
|
||||
*
|
||||
* @var bool $betweenBefore
|
||||
*/
|
||||
$betweenBefore = false;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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_COMMENT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Replacing all whitespaces (new lines, tabs, etc.) with a single
|
||||
// space character.
|
||||
if ($token->type === Token::TYPE_WHITESPACE) {
|
||||
$expr->expr .= ' ';
|
||||
continue;
|
||||
}
|
||||
|
||||
// Conditions are delimited by logical operators.
|
||||
if (in_array($token->value, static::$DELIMITERS, true)) {
|
||||
if (($betweenBefore) && ($token->value === 'AND')) {
|
||||
// The syntax of keyword `BETWEEN` is hard-coded.
|
||||
$betweenBefore = false;
|
||||
} else {
|
||||
// The expression ended.
|
||||
$expr->expr = trim($expr->expr);
|
||||
if (!empty($expr->expr)) {
|
||||
$ret[] = $expr;
|
||||
}
|
||||
|
||||
// Adding the operator.
|
||||
$expr = new Condition($token->value);
|
||||
$expr->isOperator = true;
|
||||
$ret[] = $expr;
|
||||
|
||||
// Preparing to parse another condition.
|
||||
$expr = new Condition();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (($token->type === Token::TYPE_KEYWORD)
|
||||
&& ($token->flags & Token::FLAG_KEYWORD_RESERVED)
|
||||
&& !($token->flags & Token::FLAG_KEYWORD_FUNCTION)
|
||||
) {
|
||||
if ($token->value === 'BETWEEN') {
|
||||
$betweenBefore = true;
|
||||
}
|
||||
if (($brackets === 0) && (empty(static::$ALLOWED_KEYWORDS[$token->value]))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($token->type === Token::TYPE_OPERATOR) {
|
||||
if ($token->value === '(') {
|
||||
++$brackets;
|
||||
} elseif ($token->value === ')') {
|
||||
if ($brackets == 0) {
|
||||
break;
|
||||
}
|
||||
--$brackets;
|
||||
}
|
||||
}
|
||||
|
||||
$expr->expr .= $token->token;
|
||||
if (($token->type === Token::TYPE_NONE)
|
||||
|| (($token->type === Token::TYPE_KEYWORD)
|
||||
&& (!($token->flags & Token::FLAG_KEYWORD_RESERVED)))
|
||||
|| ($token->type === Token::TYPE_STRING)
|
||||
|| ($token->type === Token::TYPE_SYMBOL)
|
||||
) {
|
||||
if (!in_array($token->value, $expr->identifiers)) {
|
||||
$expr->identifiers[] = $token->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Last iteration was not processed.
|
||||
$expr->expr = trim($expr->expr);
|
||||
if (!empty($expr->expr)) {
|
||||
$ret[] = $expr;
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Condition[] $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
if (is_array($component)) {
|
||||
return implode(' ', $component);
|
||||
} else {
|
||||
return $component->expr;
|
||||
}
|
||||
}
|
||||
}
|
339
#pma/libraries/sql-parser/src/Components/CreateDefinition.php
Normal file
339
#pma/libraries/sql-parser/src/Components/CreateDefinition.php
Normal file
@@ -0,0 +1,339 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Parses the create definition of a column or a key.
|
||||
*
|
||||
* Used for parsing `CREATE TABLE` statement.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Context;
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* Parses the create definition of a column or a key.
|
||||
*
|
||||
* Used for parsing `CREATE TABLE` statement.
|
||||
*
|
||||
* @category Components
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class CreateDefinition extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* All field options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $FIELD_OPTIONS = array(
|
||||
|
||||
// Tells the `OptionsArray` to not sort the options.
|
||||
// See the note below.
|
||||
'_UNSORTED' => true,
|
||||
|
||||
'NOT NULL' => 1,
|
||||
'NULL' => 1,
|
||||
'DEFAULT' => array(2, 'expr', array('breakOnAlias' => true)),
|
||||
'AUTO_INCREMENT' => 3,
|
||||
'PRIMARY' => 4,
|
||||
'PRIMARY KEY' => 4,
|
||||
'UNIQUE' => 4,
|
||||
'UNIQUE KEY' => 4,
|
||||
'COMMENT' => array(5, 'var'),
|
||||
'COLUMN_FORMAT' => array(6, 'var'),
|
||||
'ON UPDATE' => array(7, 'expr'),
|
||||
|
||||
// Generated columns options.
|
||||
'GENERATED ALWAYS' => 8,
|
||||
'AS' => array(9, 'expr', array('parenthesesDelimited' => true)),
|
||||
'VIRTUAL' => 10,
|
||||
'PERSISTENT' => 11,
|
||||
'STORED' => 11,
|
||||
// Common entries.
|
||||
//
|
||||
// NOTE: Some of the common options are not in the same order which
|
||||
// causes troubles when checking if the options are in the right order.
|
||||
// I should find a way to define multiple sets of options and make the
|
||||
// parser select the right set.
|
||||
//
|
||||
// 'UNIQUE' => 4,
|
||||
// 'UNIQUE KEY' => 4,
|
||||
// 'COMMENT' => array(5, 'var'),
|
||||
// 'NOT NULL' => 1,
|
||||
// 'NULL' => 1,
|
||||
// 'PRIMARY' => 4,
|
||||
// 'PRIMARY KEY' => 4,
|
||||
);
|
||||
|
||||
/**
|
||||
* The name of the new column.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Whether this field is a constraint or not.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $isConstraint;
|
||||
|
||||
/**
|
||||
* The data type of thew new column.
|
||||
*
|
||||
* @var DataType
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* The key.
|
||||
*
|
||||
* @var Key
|
||||
*/
|
||||
public $key;
|
||||
|
||||
/**
|
||||
* The table that is referenced.
|
||||
*
|
||||
* @var Reference
|
||||
*/
|
||||
public $references;
|
||||
|
||||
/**
|
||||
* The options of this field.
|
||||
*
|
||||
* @var OptionsArray
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $name The name of the field.
|
||||
* @param OptionsArray $options The options of this field.
|
||||
* @param DataType|Key $type The data type of this field or the key.
|
||||
* @param bool $isConstraint Whether this field is a constraint or not.
|
||||
* @param Reference $references References.
|
||||
*/
|
||||
public function __construct(
|
||||
$name = null,
|
||||
$options = null,
|
||||
$type = null,
|
||||
$isConstraint = false,
|
||||
$references = null
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->options = $options;
|
||||
if ($type instanceof DataType) {
|
||||
$this->type = $type;
|
||||
} elseif ($type instanceof Key) {
|
||||
$this->key = $type;
|
||||
$this->isConstraint = $isConstraint;
|
||||
$this->references = $references;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 CreateDefinition[]
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = array();
|
||||
|
||||
$expr = new CreateDefinition();
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 -----------------------[ ( ]------------------------> 1
|
||||
*
|
||||
* 1 --------------------[ CONSTRAINT ]------------------> 1
|
||||
* 1 -----------------------[ key ]----------------------> 2
|
||||
* 1 -------------[ constraint / column name ]-----------> 2
|
||||
*
|
||||
* 2 --------------------[ data type ]-------------------> 3
|
||||
*
|
||||
* 3 ---------------------[ options ]--------------------> 4
|
||||
*
|
||||
* 4 --------------------[ REFERENCES ]------------------> 4
|
||||
*
|
||||
* 5 ------------------------[ , ]-----------------------> 1
|
||||
* 5 ------------------------[ ) ]-----------------------> 6 (-1)
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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_OPERATOR) && ($token->value === '(')) {
|
||||
$state = 1;
|
||||
} else {
|
||||
$parser->error(
|
||||
__('An opening bracket was expected.'),
|
||||
$token
|
||||
);
|
||||
break;
|
||||
}
|
||||
} elseif ($state === 1) {
|
||||
if (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'CONSTRAINT')) {
|
||||
$expr->isConstraint = true;
|
||||
} elseif (($token->type === Token::TYPE_KEYWORD) && ($token->flags & Token::FLAG_KEYWORD_KEY)) {
|
||||
$expr->key = Key::parse($parser, $list);
|
||||
$state = 4;
|
||||
} elseif ($token->type === Token::TYPE_SYMBOL || $token->type === Token::TYPE_NONE) {
|
||||
$expr->name = $token->value;
|
||||
if (!$expr->isConstraint) {
|
||||
$state = 2;
|
||||
}
|
||||
} else if ($token->type === Token::TYPE_KEYWORD) {
|
||||
if ($token->flags & Token::FLAG_KEYWORD_RESERVED) {
|
||||
// Reserved keywords can't be used
|
||||
// as field names without backquotes
|
||||
$parser->error(
|
||||
__('A symbol name was expected! '
|
||||
. 'A reserved keyword can not be used '
|
||||
. 'as a column name without backquotes.'
|
||||
),
|
||||
$token
|
||||
);
|
||||
return $ret;
|
||||
} else {
|
||||
// Non-reserved keywords are allowed without backquotes
|
||||
$expr->name = $token->value;
|
||||
$state = 2;
|
||||
}
|
||||
} else {
|
||||
$parser->error(
|
||||
__('A symbol name was expected!'),
|
||||
$token
|
||||
);
|
||||
return $ret;
|
||||
}
|
||||
} elseif ($state === 2) {
|
||||
$expr->type = DataType::parse($parser, $list);
|
||||
$state = 3;
|
||||
} elseif ($state === 3) {
|
||||
$expr->options = OptionsArray::parse($parser, $list, static::$FIELD_OPTIONS);
|
||||
$state = 4;
|
||||
} elseif ($state === 4) {
|
||||
if (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'REFERENCES')) {
|
||||
++$list->idx; // Skipping keyword 'REFERENCES'.
|
||||
$expr->references = Reference::parse($parser, $list);
|
||||
} else {
|
||||
--$list->idx;
|
||||
}
|
||||
$state = 5;
|
||||
} elseif ($state === 5) {
|
||||
if ((!empty($expr->type)) || (!empty($expr->key))) {
|
||||
$ret[] = $expr;
|
||||
}
|
||||
$expr = new CreateDefinition();
|
||||
if ($token->value === ',') {
|
||||
$state = 1;
|
||||
} elseif ($token->value === ')') {
|
||||
$state = 6;
|
||||
++$list->idx;
|
||||
break;
|
||||
} else {
|
||||
$parser->error(
|
||||
__('A comma or a closing bracket was expected.'),
|
||||
$token
|
||||
);
|
||||
$state = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Last iteration was not saved.
|
||||
if ((!empty($expr->type)) || (!empty($expr->key))) {
|
||||
$ret[] = $expr;
|
||||
}
|
||||
|
||||
if (($state !== 0) && ($state !== 6)) {
|
||||
$parser->error(
|
||||
__('A closing bracket was expected.'),
|
||||
$list->tokens[$list->idx - 1]
|
||||
);
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CreateDefinition|CreateDefinition[] $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
if (is_array($component)) {
|
||||
return "(\n " . implode(",\n ", $component) . "\n)";
|
||||
} else {
|
||||
$tmp = '';
|
||||
|
||||
if ($component->isConstraint) {
|
||||
$tmp .= 'CONSTRAINT ';
|
||||
}
|
||||
|
||||
if ((isset($component->name)) && ($component->name !== '')) {
|
||||
$tmp .= Context::escape($component->name) . ' ';
|
||||
}
|
||||
|
||||
if (!empty($component->type)) {
|
||||
$tmp .= DataType::build(
|
||||
$component->type,
|
||||
array('lowercase' => true)
|
||||
) . ' ';
|
||||
}
|
||||
|
||||
if (!empty($component->key)) {
|
||||
$tmp .= $component->key . ' ';
|
||||
}
|
||||
|
||||
if (!empty($component->references)) {
|
||||
$tmp .= 'REFERENCES ' . $component->references . ' ';
|
||||
}
|
||||
|
||||
$tmp .= $component->options;
|
||||
|
||||
return trim($tmp);
|
||||
}
|
||||
}
|
||||
}
|
170
#pma/libraries/sql-parser/src/Components/DataType.php
Normal file
170
#pma/libraries/sql-parser/src/Components/DataType.php
Normal file
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Parses a data type.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* Parses a data type.
|
||||
*
|
||||
* @category Components
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class DataType extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* All data type options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $DATA_TYPE_OPTIONS = array(
|
||||
'BINARY' => 1,
|
||||
'CHARACTER SET' => array(2, 'var'),
|
||||
'CHARSET' => array(2, 'var'),
|
||||
'COLLATE' => array(3, 'var'),
|
||||
'UNSIGNED' => 4,
|
||||
'ZEROFILL' => 5,
|
||||
);
|
||||
|
||||
/**
|
||||
* The name of the data type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* The parameters of this data type.
|
||||
*
|
||||
* Some data types have no parameters.
|
||||
* Numeric types might have parameters for the maximum number of digits,
|
||||
* precision, etc.
|
||||
* String types might have parameters for the maximum length stored.
|
||||
* `ENUM` and `SET` have parameters for possible values.
|
||||
*
|
||||
* For more information, check the MySQL manual.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $parameters = array();
|
||||
|
||||
/**
|
||||
* The options of this data type.
|
||||
*
|
||||
* @var OptionsArray
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $name The name of this data type.
|
||||
* @param array $parameters The parameters (size or possible values).
|
||||
* @param OptionsArray $options The options of this data type.
|
||||
*/
|
||||
public function __construct(
|
||||
$name = null,
|
||||
array $parameters = array(),
|
||||
$options = null
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->parameters = $parameters;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 DataType
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = new DataType();
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 -------------------[ data type ]--------------------> 1
|
||||
*
|
||||
* 1 ----------------[ size and options ]----------------> 2
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $token
|
||||
*/
|
||||
$token = $list->tokens[$list->idx];
|
||||
|
||||
// Skipping whitespaces and comments.
|
||||
if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
$ret->name = strtoupper($token->value);
|
||||
if (($token->type !== Token::TYPE_KEYWORD) || (!($token->flags & Token::FLAG_KEYWORD_DATA_TYPE))) {
|
||||
$parser->error(__('Unrecognized data type.'), $token);
|
||||
}
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
|
||||
$parameters = ArrayObj::parse($parser, $list);
|
||||
++$list->idx;
|
||||
$ret->parameters = (($ret->name === 'ENUM') || ($ret->name === 'SET')) ?
|
||||
$parameters->raw : $parameters->values;
|
||||
}
|
||||
$ret->options = OptionsArray::parse($parser, $list, static::$DATA_TYPE_OPTIONS);
|
||||
++$list->idx;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (empty($ret->name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DataType $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
$name = (empty($options['lowercase'])) ?
|
||||
$component->name : strtolower($component->name);
|
||||
|
||||
$parameters = '';
|
||||
if (!empty($component->parameters)) {
|
||||
$parameters = '(' . implode(',', $component->parameters) . ')';
|
||||
}
|
||||
|
||||
return trim($name . $parameters . ' ' . $component->options);
|
||||
}
|
||||
}
|
448
#pma/libraries/sql-parser/src/Components/Expression.php
Normal file
448
#pma/libraries/sql-parser/src/Components/Expression.php
Normal file
@@ -0,0 +1,448 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Parses a reference to an expression (column, table or database name, function
|
||||
* call, mathematical expression, etc.).
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Context;
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* Parses a reference to an expression (column, table or database name, function
|
||||
* call, mathematical expression, etc.).
|
||||
*
|
||||
* @category Components
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class Expression extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* List of allowed reserved keywords in expressions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $ALLOWED_KEYWORDS = array(
|
||||
'AS' => 1, 'DUAL' => 1, 'NULL' => 1, 'REGEXP' => 1, 'CASE' => 1
|
||||
);
|
||||
|
||||
/**
|
||||
* The name of this database.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $database;
|
||||
|
||||
/**
|
||||
* The name of this table.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $table;
|
||||
|
||||
/**
|
||||
* The name of the column.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $column;
|
||||
|
||||
/**
|
||||
* The sub-expression.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $expr = '';
|
||||
|
||||
/**
|
||||
* The alias of this expression.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $alias;
|
||||
|
||||
/**
|
||||
* The name of the function.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $function;
|
||||
|
||||
/**
|
||||
* The type of subquery.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $subquery;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Syntax:
|
||||
* new Expression('expr')
|
||||
* new Expression('expr', 'alias')
|
||||
* new Expression('database', 'table', 'column')
|
||||
* new Expression('database', 'table', 'column', 'alias')
|
||||
*
|
||||
* If the database, table or column name is not required, pass an empty
|
||||
* string.
|
||||
*
|
||||
* @param string $database The name of the database or the the expression.
|
||||
* the the expression.
|
||||
* @param string $table The name of the table or the alias of the expression.
|
||||
* the alias of the expression.
|
||||
* @param string $column The name of the column.
|
||||
* @param string $alias The name of the alias.
|
||||
*/
|
||||
public function __construct($database = null, $table = null, $column = null, $alias = null)
|
||||
{
|
||||
if (($column === null) && ($alias === null)) {
|
||||
$this->expr = $database; // case 1
|
||||
$this->alias = $table; // case 2
|
||||
} else {
|
||||
$this->database = $database; // case 3
|
||||
$this->table = $table; // case 3
|
||||
$this->column = $column; // case 3
|
||||
$this->alias = $alias; // case 4
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Possible options:
|
||||
*
|
||||
* `field`
|
||||
*
|
||||
* First field to be filled.
|
||||
* If this is not specified, it takes the value of `parseField`.
|
||||
*
|
||||
* `parseField`
|
||||
*
|
||||
* Specifies the type of the field parsed. It may be `database`,
|
||||
* `table` or `column`. These expressions may not include
|
||||
* parentheses.
|
||||
*
|
||||
* `breakOnAlias`
|
||||
*
|
||||
* If not empty, breaks when the alias occurs (it is not included).
|
||||
*
|
||||
* `breakOnParentheses`
|
||||
*
|
||||
* If not empty, breaks when the first parentheses occurs.
|
||||
*
|
||||
* `parenthesesDelimited`
|
||||
*
|
||||
* If not empty, breaks after last parentheses occurred.
|
||||
*
|
||||
* @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 Expression
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = new Expression();
|
||||
|
||||
/**
|
||||
* Whether current tokens make an expression or a table reference.
|
||||
*
|
||||
* @var bool $isExpr
|
||||
*/
|
||||
$isExpr = false;
|
||||
|
||||
/**
|
||||
* Whether a period was previously found.
|
||||
*
|
||||
* @var bool $dot
|
||||
*/
|
||||
$dot = false;
|
||||
|
||||
/**
|
||||
* Whether an alias is expected. Is 2 if `AS` keyword was found.
|
||||
*
|
||||
* @var bool $alias
|
||||
*/
|
||||
$alias = false;
|
||||
|
||||
/**
|
||||
* Counts brackets.
|
||||
*
|
||||
* @var int $brackets
|
||||
*/
|
||||
$brackets = 0;
|
||||
|
||||
/**
|
||||
* Keeps track of the last two previous tokens.
|
||||
*
|
||||
* @var Token[] $prev
|
||||
*/
|
||||
$prev = array(null, null);
|
||||
|
||||
// When a field is parsed, no parentheses are expected.
|
||||
if (!empty($options['parseField'])) {
|
||||
$options['breakOnParentheses'] = true;
|
||||
$options['field'] = $options['parseField'];
|
||||
}
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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)
|
||||
) {
|
||||
if ($isExpr) {
|
||||
$ret->expr .= $token->token;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($token->type === Token::TYPE_KEYWORD) {
|
||||
if (($brackets > 0) && (empty($ret->subquery))
|
||||
&& (!empty(Parser::$STATEMENT_PARSERS[$token->value]))
|
||||
) {
|
||||
// A `(` was previously found and this keyword is the
|
||||
// beginning of a statement, so this is a subquery.
|
||||
$ret->subquery = $token->value;
|
||||
} elseif (($token->flags & Token::FLAG_KEYWORD_FUNCTION)
|
||||
&& (empty($options['parseField'])
|
||||
&& ! $alias)
|
||||
) {
|
||||
$isExpr = true;
|
||||
} elseif (($token->flags & Token::FLAG_KEYWORD_RESERVED)
|
||||
&& ($brackets === 0)
|
||||
) {
|
||||
if (empty(self::$ALLOWED_KEYWORDS[$token->value])) {
|
||||
// A reserved keyword that is not allowed in the
|
||||
// expression was found so the expression must have
|
||||
// ended and a new clause is starting.
|
||||
break;
|
||||
}
|
||||
if ($token->value === 'AS') {
|
||||
if (!empty($options['breakOnAlias'])) {
|
||||
break;
|
||||
}
|
||||
if ($alias) {
|
||||
$parser->error(
|
||||
__('An alias was expected.'),
|
||||
$token
|
||||
);
|
||||
break;
|
||||
}
|
||||
$alias = true;
|
||||
continue;
|
||||
} elseif ($token->value === 'CASE') {
|
||||
// For a use of CASE like
|
||||
// 'SELECT a = CASE .... END, b=1, `id`, ... FROM ...'
|
||||
$tempCaseExpr = CaseExpression::parse($parser, $list);
|
||||
$ret->expr .= CaseExpression::build($tempCaseExpr);
|
||||
$isExpr = true;
|
||||
continue;
|
||||
}
|
||||
$isExpr = true;
|
||||
} elseif ($brackets === 0 && count($ret->expr) > 0 && ! $alias) {
|
||||
/* End of expression */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (($token->type === Token::TYPE_NUMBER)
|
||||
|| ($token->type === Token::TYPE_BOOL)
|
||||
|| (($token->type === Token::TYPE_SYMBOL)
|
||||
&& ($token->flags & Token::FLAG_SYMBOL_VARIABLE))
|
||||
|| (($token->type === Token::TYPE_OPERATOR)
|
||||
&& ($token->value !== '.'))
|
||||
) {
|
||||
if (!empty($options['parseField'])) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Numbers, booleans and operators (except dot) are usually part
|
||||
// of expressions.
|
||||
$isExpr = true;
|
||||
}
|
||||
|
||||
if ($token->type === Token::TYPE_OPERATOR) {
|
||||
if ((!empty($options['breakOnParentheses']))
|
||||
&& (($token->value === '(') || ($token->value === ')'))
|
||||
) {
|
||||
// No brackets were expected.
|
||||
break;
|
||||
}
|
||||
if ($token->value === '(') {
|
||||
++$brackets;
|
||||
if ((empty($ret->function)) && ($prev[1] !== null)
|
||||
&& (($prev[1]->type === Token::TYPE_NONE)
|
||||
|| ($prev[1]->type === Token::TYPE_SYMBOL)
|
||||
|| (($prev[1]->type === Token::TYPE_KEYWORD)
|
||||
&& ($prev[1]->flags & Token::FLAG_KEYWORD_FUNCTION)))
|
||||
) {
|
||||
$ret->function = $prev[1]->value;
|
||||
}
|
||||
} elseif ($token->value === ')' && $brackets == 0) {
|
||||
// Not our bracket
|
||||
break;
|
||||
} elseif ($token->value === ')') {
|
||||
--$brackets;
|
||||
if ($brackets === 0) {
|
||||
if (!empty($options['parenthesesDelimited'])) {
|
||||
// The current token is the last bracket, the next
|
||||
// one will be outside the expression.
|
||||
$ret->expr .= $token->token;
|
||||
++$list->idx;
|
||||
break;
|
||||
}
|
||||
} elseif ($brackets < 0) {
|
||||
// $parser->error(__('Unexpected closing bracket.'), $token);
|
||||
// $brackets = 0;
|
||||
break;
|
||||
}
|
||||
} elseif ($token->value === ',') {
|
||||
// Expressions are comma-delimited.
|
||||
if ($brackets === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Saving the previous tokens.
|
||||
$prev[0] = $prev[1];
|
||||
$prev[1] = $token;
|
||||
|
||||
if ($alias) {
|
||||
// An alias is expected (the keyword `AS` was previously found).
|
||||
if (!empty($ret->alias)) {
|
||||
$parser->error(__('An alias was previously found.'), $token);
|
||||
break;
|
||||
}
|
||||
$ret->alias = $token->value;
|
||||
$alias = false;
|
||||
} elseif ($isExpr) {
|
||||
// Handling aliases.
|
||||
if (/* (empty($ret->alias)) && */ ($brackets === 0)
|
||||
&& (($prev[0] === null)
|
||||
|| ((($prev[0]->type !== Token::TYPE_OPERATOR)
|
||||
|| ($prev[0]->token === ')'))
|
||||
&& (($prev[0]->type !== Token::TYPE_KEYWORD)
|
||||
|| (!($prev[0]->flags & Token::FLAG_KEYWORD_RESERVED)))))
|
||||
&& (($prev[1]->type === Token::TYPE_STRING)
|
||||
|| (($prev[1]->type === Token::TYPE_SYMBOL)
|
||||
&& (!($prev[1]->flags & Token::FLAG_SYMBOL_VARIABLE)))
|
||||
|| ($prev[1]->type === Token::TYPE_NONE))
|
||||
) {
|
||||
if (!empty($ret->alias)) {
|
||||
$parser->error(__('An alias was previously found.'), $token);
|
||||
break;
|
||||
}
|
||||
$ret->alias = $prev[1]->value;
|
||||
} else {
|
||||
$ret->expr .= $token->token;
|
||||
}
|
||||
} elseif (!$isExpr) {
|
||||
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '.')) {
|
||||
// Found a `.` which means we expect a column name and
|
||||
// the column name we parsed is actually the table name
|
||||
// and the table name is actually a database name.
|
||||
if ((!empty($ret->database)) || ($dot)) {
|
||||
$parser->error(__('Unexpected dot.'), $token);
|
||||
}
|
||||
$ret->database = $ret->table;
|
||||
$ret->table = $ret->column;
|
||||
$ret->column = null;
|
||||
$dot = true;
|
||||
$ret->expr .= $token->token;
|
||||
} else {
|
||||
$field = empty($options['field']) ? 'column' : $options['field'];
|
||||
if (empty($ret->$field)) {
|
||||
$ret->$field = $token->value;
|
||||
$ret->expr .= $token->token;
|
||||
$dot = false;
|
||||
} else {
|
||||
// No alias is expected.
|
||||
if (!empty($options['breakOnAlias'])) {
|
||||
break;
|
||||
}
|
||||
if (!empty($ret->alias)) {
|
||||
$parser->error(__('An alias was previously found.'), $token);
|
||||
break;
|
||||
}
|
||||
$ret->alias = $token->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($alias) {
|
||||
$parser->error(
|
||||
__('An alias was expected.'),
|
||||
$list->tokens[$list->idx - 1]
|
||||
);
|
||||
}
|
||||
|
||||
// White-spaces might be added at the end.
|
||||
$ret->expr = trim($ret->expr);
|
||||
|
||||
if ($ret->expr === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Expression|Expression[] $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
if (is_array($component)) {
|
||||
return implode($component, ', ');
|
||||
} else {
|
||||
if ($component->expr !== '' && !is_null($component->expr)) {
|
||||
$ret = $component->expr;
|
||||
} else {
|
||||
$fields = array();
|
||||
if ((isset($component->database)) && ($component->database !== '')) {
|
||||
$fields[] = $component->database;
|
||||
}
|
||||
if ((isset($component->table)) && ($component->table !== '')) {
|
||||
$fields[] = $component->table;
|
||||
}
|
||||
if ((isset($component->column)) && ($component->column !== '')) {
|
||||
$fields[] = $component->column;
|
||||
}
|
||||
$ret = implode('.', Context::escape($fields));
|
||||
}
|
||||
|
||||
if (!empty($component->alias)) {
|
||||
$ret .= ' AS ' . Context::escape($component->alias);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
}
|
129
#pma/libraries/sql-parser/src/Components/ExpressionArray.php
Normal file
129
#pma/libraries/sql-parser/src/Components/ExpressionArray.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Parses a list of expressions delimited by a comma.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* Parses a list of expressions delimited by a comma.
|
||||
*
|
||||
* @category Keywords
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class ExpressionArray extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* @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 Expression[]
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = array();
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 ----------------------[ array ]---------------------> 1
|
||||
*
|
||||
* 1 ------------------------[ , ]------------------------> 0
|
||||
* 1 -----------------------[ else ]----------------------> (END)
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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 (($token->type === Token::TYPE_KEYWORD)
|
||||
&& ($token->flags & Token::FLAG_KEYWORD_RESERVED)
|
||||
&& ((~$token->flags & Token::FLAG_KEYWORD_FUNCTION))
|
||||
&& ($token->value !== 'DUAL')
|
||||
&& ($token->value !== 'NULL')
|
||||
&& ($token->value !== 'CASE')
|
||||
) {
|
||||
// No keyword is expected.
|
||||
break;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
if ($token->type === Token::TYPE_KEYWORD
|
||||
&& $token->value === 'CASE'
|
||||
) {
|
||||
$expr = CaseExpression::parse($parser, $list, $options);
|
||||
} else {
|
||||
$expr = Expression::parse($parser, $list, $options);
|
||||
}
|
||||
|
||||
if ($expr === null) {
|
||||
break;
|
||||
}
|
||||
$ret[] = $expr;
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
if ($token->value === ',') {
|
||||
$state = 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
$parser->error(
|
||||
__('An expression was expected.'),
|
||||
$list->tokens[$list->idx]
|
||||
);
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Expression[] $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
$ret = array();
|
||||
foreach ($component as $frag) {
|
||||
$ret[] = $frag::build($frag);
|
||||
}
|
||||
return implode($ret, ', ');
|
||||
}
|
||||
}
|
124
#pma/libraries/sql-parser/src/Components/FunctionCall.php
Normal file
124
#pma/libraries/sql-parser/src/Components/FunctionCall.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Parses a function call.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* Parses a function call.
|
||||
*
|
||||
* @category Keywords
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class FunctionCall extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* The name of this function.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* The list of parameters
|
||||
*
|
||||
* @var ArrayObj
|
||||
*/
|
||||
public $parameters;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $name The name of the function to be called.
|
||||
* @param array|ArrayObj $parameters The parameters of this function.
|
||||
*/
|
||||
public function __construct($name = null, $parameters = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
if (is_array($parameters)) {
|
||||
$this->parameters = new ArrayObj($parameters);
|
||||
} elseif ($parameters instanceof ArrayObj) {
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 FunctionCall
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = new FunctionCall();
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 ----------------------[ name ]-----------------------> 1
|
||||
*
|
||||
* 1 --------------------[ parameters ]-------------------> (END)
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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) {
|
||||
$ret->name = $token->value;
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
|
||||
$ret->parameters = ArrayObj::parse($parser, $list);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FunctionCall $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
return $component->name . $component->parameters;
|
||||
}
|
||||
}
|
164
#pma/libraries/sql-parser/src/Components/IntoKeyword.php
Normal file
164
#pma/libraries/sql-parser/src/Components/IntoKeyword.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* `INTO` keyword parser.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* `INTO` keyword parser.
|
||||
*
|
||||
* @category Keywords
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class IntoKeyword extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* Type of target (OUTFILE or SYMBOL).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* The destination, which can be a table or a file.
|
||||
*
|
||||
* @var string|Expression
|
||||
*/
|
||||
public $dest;
|
||||
|
||||
/**
|
||||
* The name of the columns.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $columns;
|
||||
|
||||
/**
|
||||
* The values to be selected into (SELECT .. INTO @var1)
|
||||
*
|
||||
* @var ExpressionArray
|
||||
*/
|
||||
public $values;
|
||||
|
||||
/**
|
||||
* @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 IntoKeyword
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = new IntoKeyword();
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 -----------------------[ name ]----------------------> 1
|
||||
* 0 ---------------------[ OUTFILE ]---------------------> 2
|
||||
*
|
||||
* 1 ------------------------[ ( ]------------------------> (END)
|
||||
*
|
||||
* 2 ---------------------[ filename ]--------------------> 1
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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 (($token->type === Token::TYPE_KEYWORD) && ($token->flags & Token::FLAG_KEYWORD_RESERVED)) {
|
||||
if (($state === 0) && ($token->value === 'OUTFILE')) {
|
||||
$ret->type = 'OUTFILE';
|
||||
$state = 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// No other keyword is expected.
|
||||
break;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
if ((isset($options['fromInsert'])
|
||||
&& $options['fromInsert'])
|
||||
|| (isset($options['fromReplace'])
|
||||
&& $options['fromReplace'])
|
||||
) {
|
||||
$ret->dest = Expression::parse(
|
||||
$parser,
|
||||
$list,
|
||||
array(
|
||||
'parseField' => 'table',
|
||||
'breakOnAlias' => true,
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$ret->values = ExpressionArray::parse($parser, $list);
|
||||
}
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
|
||||
$ret->columns = ArrayObj::parse($parser, $list)->values;
|
||||
++$list->idx;
|
||||
}
|
||||
break;
|
||||
} elseif ($state === 2) {
|
||||
$ret->dest = $token->value;
|
||||
++$list->idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IntoKeyword $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
if ($component->dest instanceof Expression) {
|
||||
$columns = !empty($component->columns) ?
|
||||
'(`' . implode('`, `', $component->columns) . '`)' : '';
|
||||
return $component->dest . $columns;
|
||||
} elseif (isset($component->values)) {
|
||||
return ExpressionArray::build($component->values);
|
||||
} else {
|
||||
return 'OUTFILE "' . $component->dest . '"';
|
||||
}
|
||||
}
|
||||
}
|
210
#pma/libraries/sql-parser/src/Components/JoinKeyword.php
Normal file
210
#pma/libraries/sql-parser/src/Components/JoinKeyword.php
Normal file
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* `JOIN` keyword parser.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* `JOIN` keyword parser.
|
||||
*
|
||||
* @category Keywords
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class JoinKeyword extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* Types of join.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $JOINS = array(
|
||||
'CROSS JOIN' => 'CROSS',
|
||||
'FULL JOIN' => 'FULL',
|
||||
'FULL OUTER JOIN' => 'FULL',
|
||||
'INNER JOIN' => 'INNER',
|
||||
'JOIN' => 'JOIN',
|
||||
'LEFT JOIN' => 'LEFT',
|
||||
'LEFT OUTER JOIN' => 'LEFT',
|
||||
'RIGHT JOIN' => 'RIGHT',
|
||||
'RIGHT OUTER JOIN' => 'RIGHT',
|
||||
'NATURAL JOIN' => 'NATURAL',
|
||||
'NATURAL LEFT JOIN' => 'NATURAL LEFT',
|
||||
'NATURAL LEFT JOIN' => 'NATURAL LEFT',
|
||||
'NATURAL RIGHT JOIN' => 'NATURAL RIGHT',
|
||||
'NATURAL LEFT OUTER JOIN' => 'NATURAL LEFT OUTER',
|
||||
'NATURAL RIGHT OUTER JOIN' => 'NATURAL RIGHT OUTER',
|
||||
'STRAIGHT_JOIN' => 'STRAIGHT',
|
||||
);
|
||||
|
||||
/**
|
||||
* Type of this join.
|
||||
*
|
||||
* @see static::$JOINS
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* Join expression.
|
||||
*
|
||||
* @var Expression
|
||||
*/
|
||||
public $expr;
|
||||
|
||||
/**
|
||||
* Join conditions.
|
||||
*
|
||||
* @var Condition[]
|
||||
*/
|
||||
public $on;
|
||||
|
||||
/**
|
||||
* Columns in Using clause
|
||||
*
|
||||
* @var ArrayObj
|
||||
*/
|
||||
public $using;
|
||||
|
||||
/**
|
||||
* @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 JoinKeyword[]
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = array();
|
||||
|
||||
$expr = new JoinKeyword();
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 -----------------------[ JOIN ]----------------------> 1
|
||||
*
|
||||
* 1 -----------------------[ expr ]----------------------> 2
|
||||
*
|
||||
* 2 ------------------------[ ON ]-----------------------> 3
|
||||
* 2 -----------------------[ USING ]---------------------> 4
|
||||
*
|
||||
* 3 --------------------[ conditions ]-------------------> 0
|
||||
*
|
||||
* 4 ----------------------[ columns ]--------------------> 0
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
// By design, the parser will parse first token after the keyword.
|
||||
// In this case, the keyword must be analyzed too, in order to determine
|
||||
// the type of this join.
|
||||
if ($list->idx > 0) {
|
||||
--$list->idx;
|
||||
}
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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)
|
||||
&& (!empty(static::$JOINS[$token->value]))
|
||||
) {
|
||||
$expr->type = static::$JOINS[$token->value];
|
||||
$state = 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} elseif ($state === 1) {
|
||||
$expr->expr = Expression::parse($parser, $list, array('field' => 'table'));
|
||||
$state = 2;
|
||||
} elseif ($state === 2) {
|
||||
if ($token->type === Token::TYPE_KEYWORD) {
|
||||
if ($token->value === 'ON') {
|
||||
$state = 3;
|
||||
} elseif ($token->value === 'USING') {
|
||||
$state = 4;
|
||||
} else {
|
||||
if (($token->type === Token::TYPE_KEYWORD)
|
||||
&& (!empty(static::$JOINS[$token->value]))
|
||||
) {
|
||||
$ret[] = $expr;
|
||||
$expr = new JoinKeyword();
|
||||
$expr->type = static::$JOINS[$token->value];
|
||||
$state = 1;
|
||||
} else {
|
||||
/* Next clause is starting */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($state === 3) {
|
||||
$expr->on = Condition::parse($parser, $list);
|
||||
$ret[] = $expr;
|
||||
$expr = new JoinKeyword();
|
||||
$state = 0;
|
||||
} elseif ($state === 4) {
|
||||
$expr->using = ArrayObj::parse($parser, $list);
|
||||
$ret[] = $expr;
|
||||
$expr = new JoinKeyword();
|
||||
$state = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!empty($expr->type)) {
|
||||
$ret[] = $expr;
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param JoinKeyword[] $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
$ret = array();
|
||||
foreach ($component as $c) {
|
||||
$ret[] = array_search($c->type, static::$JOINS) . ' ' . $c->expr
|
||||
. (!empty($c->on)
|
||||
? ' ON ' . Condition::build($c->on) : '')
|
||||
. (!empty($c->using)
|
||||
? ' USING ' . ArrayObj::build($c->using) : '');
|
||||
}
|
||||
return implode(' ', $ret);
|
||||
}
|
||||
}
|
207
#pma/libraries/sql-parser/src/Components/Key.php
Normal file
207
#pma/libraries/sql-parser/src/Components/Key.php
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Parses the definition of a key.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Context;
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* Parses the definition of a key.
|
||||
*
|
||||
* Used for parsing `CREATE TABLE` statement.
|
||||
*
|
||||
* @category Components
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class Key extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* All key options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $KEY_OPTIONS = array(
|
||||
'KEY_BLOCK_SIZE' => array(1, 'var'),
|
||||
'USING' => array(2, 'var'),
|
||||
'WITH PARSER' => array(3, 'var'),
|
||||
'COMMENT' => array(4, 'var='),
|
||||
);
|
||||
|
||||
/**
|
||||
* The name of this key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Columns.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $columns;
|
||||
|
||||
/**
|
||||
* The type of this key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* The options of this key.
|
||||
*
|
||||
* @var OptionsArray
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $name The name of the key.
|
||||
* @param array $columns The columns covered by this key.
|
||||
* @param string $type The type of this key.
|
||||
* @param OptionsArray $options The options of this key.
|
||||
*/
|
||||
public function __construct(
|
||||
$name = null,
|
||||
array $columns = array(),
|
||||
$type = null,
|
||||
$options = null
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->columns = $columns;
|
||||
$this->type = $type;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 Key
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = new Key();
|
||||
|
||||
/**
|
||||
* Last parsed column.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
$lastColumn = array();
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 ----------------------[ type ]-----------------------> 1
|
||||
*
|
||||
* 1 ----------------------[ name ]-----------------------> 1
|
||||
* 1 ---------------------[ columns ]---------------------> 2
|
||||
*
|
||||
* 2 ---------------------[ options ]---------------------> 3
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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) {
|
||||
$ret->type = $token->value;
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
|
||||
$state = 2;
|
||||
} else {
|
||||
$ret->name = $token->value;
|
||||
}
|
||||
} elseif ($state === 2) {
|
||||
if ($token->type === Token::TYPE_OPERATOR) {
|
||||
if ($token->value === '(') {
|
||||
$state = 3;
|
||||
} elseif (($token->value === ',') || ($token->value === ')')) {
|
||||
$state = ($token->value === ',') ? 2 : 4;
|
||||
if (!empty($lastColumn)) {
|
||||
$ret->columns[] = $lastColumn;
|
||||
$lastColumn = array();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$lastColumn['name'] = $token->value;
|
||||
}
|
||||
} elseif ($state === 3) {
|
||||
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === ')')) {
|
||||
$state = 2;
|
||||
} else {
|
||||
$lastColumn['length'] = $token->value;
|
||||
}
|
||||
} elseif ($state === 4) {
|
||||
$ret->options = OptionsArray::parse($parser, $list, static::$KEY_OPTIONS);
|
||||
++$list->idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Key $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
$ret = $component->type . ' ';
|
||||
if (!empty($component->name)) {
|
||||
$ret .= Context::escape($component->name) . ' ';
|
||||
}
|
||||
|
||||
$columns = array();
|
||||
foreach ($component->columns as $column) {
|
||||
$tmp = Context::escape($column['name']);
|
||||
if (isset($column['length'])) {
|
||||
$tmp .= '(' . $column['length'] . ')';
|
||||
}
|
||||
$columns[] = $tmp;
|
||||
}
|
||||
|
||||
$ret .= '(' . implode(',', $columns) . ') ' . $component->options;
|
||||
return trim($ret);
|
||||
}
|
||||
}
|
131
#pma/libraries/sql-parser/src/Components/Limit.php
Normal file
131
#pma/libraries/sql-parser/src/Components/Limit.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* `LIMIT` keyword parser.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* `LIMIT` keyword parser.
|
||||
*
|
||||
* @category Keywords
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class Limit extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* The number of rows skipped.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $offset;
|
||||
|
||||
/**
|
||||
* The number of rows to be returned.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $rowCount;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param int $rowCount The row count.
|
||||
* @param int $offset The offset.
|
||||
*/
|
||||
public function __construct($rowCount = 0, $offset = 0)
|
||||
{
|
||||
$this->rowCount = $rowCount;
|
||||
$this->offset = $offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 Limit
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = new Limit();
|
||||
|
||||
$offset = false;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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 (($token->type === Token::TYPE_KEYWORD) && ($token->flags & Token::FLAG_KEYWORD_RESERVED)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'OFFSET')) {
|
||||
if ($offset) {
|
||||
$parser->error(__('An offset was expected.'), $token);
|
||||
}
|
||||
$offset = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === ',')) {
|
||||
$ret->offset = $ret->rowCount;
|
||||
$ret->rowCount = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($offset) {
|
||||
$ret->offset = $token->value;
|
||||
$offset = false;
|
||||
} else {
|
||||
$ret->rowCount = $token->value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($offset) {
|
||||
$parser->error(
|
||||
__('An offset was expected.'),
|
||||
$list->tokens[$list->idx - 1]
|
||||
);
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Limit $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
return $component->offset . ', ' . $component->rowCount;
|
||||
}
|
||||
}
|
372
#pma/libraries/sql-parser/src/Components/OptionsArray.php
Normal file
372
#pma/libraries/sql-parser/src/Components/OptionsArray.php
Normal file
@@ -0,0 +1,372 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Parses a list of options.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* Parses a list of options.
|
||||
*
|
||||
* @category Components
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class OptionsArray extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* ArrayObj of selected options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $options = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $options The array of options. Options that have a value
|
||||
* must be an array with at least two keys `name` and
|
||||
* `expr` or `value`.
|
||||
*/
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 OptionsArray
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = new OptionsArray();
|
||||
|
||||
/**
|
||||
* The ID that will be assigned to duplicate options.
|
||||
*
|
||||
* @var int $lastAssignedId
|
||||
*/
|
||||
$lastAssignedId = count($options) + 1;
|
||||
|
||||
/**
|
||||
* The option that was processed last time.
|
||||
*
|
||||
* @var array $lastOption
|
||||
*/
|
||||
$lastOption = null;
|
||||
|
||||
/**
|
||||
* The index of the option that was processed last time.
|
||||
*
|
||||
* @var int $lastOptionId
|
||||
*/
|
||||
$lastOptionId = 0;
|
||||
|
||||
/**
|
||||
* Counts brackets.
|
||||
*
|
||||
* @var int $brackets
|
||||
*/
|
||||
$brackets = 0;
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 ---------------------[ option ]----------------------> 1
|
||||
*
|
||||
* 1 -------------------[ = (optional) ]------------------> 2
|
||||
*
|
||||
* 2 ----------------------[ value ]----------------------> 0
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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;
|
||||
}
|
||||
|
||||
// Skipping whitespace if not parsing value.
|
||||
if (($token->type === Token::TYPE_WHITESPACE) && ($brackets === 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($lastOption === null) {
|
||||
$upper = strtoupper($token->token);
|
||||
if (isset($options[$upper])) {
|
||||
$lastOption = $options[$upper];
|
||||
$lastOptionId = is_array($lastOption) ?
|
||||
$lastOption[0] : $lastOption;
|
||||
$state = 0;
|
||||
|
||||
// Checking for option conflicts.
|
||||
// For example, in `SELECT` statements the keywords `ALL`
|
||||
// and `DISTINCT` conflict and if used together, they
|
||||
// produce an invalid query.
|
||||
//
|
||||
// Usually, tokens can be identified in the array by the
|
||||
// option ID, but if conflicts occur, a generated option ID
|
||||
// is used.
|
||||
//
|
||||
// The first pseudo duplicate ID is the maximum value of the
|
||||
// real options (e.g. if there are 5 options, the first
|
||||
// fake ID is 6).
|
||||
if (isset($ret->options[$lastOptionId])) {
|
||||
$parser->error(
|
||||
sprintf(
|
||||
__('This option conflicts with "%1$s".'),
|
||||
is_array($ret->options[$lastOptionId])
|
||||
? $ret->options[$lastOptionId]['name']
|
||||
: $ret->options[$lastOptionId]
|
||||
),
|
||||
$token
|
||||
);
|
||||
$lastOptionId = $lastAssignedId++;
|
||||
}
|
||||
} else {
|
||||
// There is no option to be processed.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
if (!is_array($lastOption)) {
|
||||
// This is a just keyword option without any value.
|
||||
// This is the beginning and the end of it.
|
||||
$ret->options[$lastOptionId] = $token->value;
|
||||
$lastOption = null;
|
||||
$state = 0;
|
||||
} elseif (($lastOption[1] === 'var') || ($lastOption[1] === 'var=')) {
|
||||
// This is a keyword that is followed by a value.
|
||||
// This is only the beginning. The value is parsed in state
|
||||
// 1 and 2. State 1 is used to skip the first equals sign
|
||||
// and state 2 to parse the actual value.
|
||||
$ret->options[$lastOptionId] = array(
|
||||
// @var string The name of the option.
|
||||
'name' => $token->value,
|
||||
// @var bool Whether it contains an equal sign.
|
||||
// This is used by the builder to rebuild it.
|
||||
'equals' => $lastOption[1] === 'var=',
|
||||
// @var string Raw value.
|
||||
'expr' => '',
|
||||
// @var string Processed value.
|
||||
'value' => '',
|
||||
);
|
||||
$state = 1;
|
||||
} elseif ($lastOption[1] === 'expr' || $lastOption[1] === 'expr=') {
|
||||
// This is a keyword that is followed by an expression.
|
||||
// The expression is used by the specialized parser.
|
||||
|
||||
// Skipping this option in order to parse the expression.
|
||||
++$list->idx;
|
||||
$ret->options[$lastOptionId] = array(
|
||||
// @var string The name of the option.
|
||||
'name' => $token->value,
|
||||
// @var bool Whether it contains an equal sign.
|
||||
// This is used by the builder to rebuild it.
|
||||
'equals' => $lastOption[1] === 'expr=',
|
||||
// @var Expression The parsed expression.
|
||||
'expr' => '',
|
||||
);
|
||||
$state = 1;
|
||||
}
|
||||
} elseif ($state === 1) {
|
||||
$state = 2;
|
||||
if ($token->token === '=') {
|
||||
$ret->options[$lastOptionId]['equals'] = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// This is outside the `elseif` group above because the change might
|
||||
// change this iteration.
|
||||
if ($state === 2) {
|
||||
if ($lastOption[1] === 'expr' || $lastOption[1] === 'expr=') {
|
||||
$ret->options[$lastOptionId]['expr'] = Expression::parse(
|
||||
$parser,
|
||||
$list,
|
||||
empty($lastOption[2]) ? array() : $lastOption[2]
|
||||
);
|
||||
$ret->options[$lastOptionId]['value']
|
||||
= $ret->options[$lastOptionId]['expr']->expr;
|
||||
$lastOption = null;
|
||||
$state = 0;
|
||||
} else {
|
||||
if ($token->token === '(') {
|
||||
++$brackets;
|
||||
} elseif ($token->token === ')') {
|
||||
--$brackets;
|
||||
}
|
||||
|
||||
$ret->options[$lastOptionId]['expr'] .= $token->token;
|
||||
|
||||
if (!((($token->token === '(') && ($brackets === 1))
|
||||
|| (($token->token === ')') && ($brackets === 0)))
|
||||
) {
|
||||
// First pair of brackets is being skipped.
|
||||
$ret->options[$lastOptionId]['value'] .= $token->value;
|
||||
}
|
||||
|
||||
// Checking if we finished parsing.
|
||||
if ($brackets === 0) {
|
||||
$lastOption = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We reached the end of statement without getting a value
|
||||
* for an option for which a value was required
|
||||
*/
|
||||
if ($state === 1
|
||||
&& $lastOption
|
||||
&& ($lastOption[1] == 'expr'
|
||||
|| $lastOption[1] == 'var'
|
||||
|| $lastOption[1] == 'var='
|
||||
|| $lastOption[1] == 'expr=')
|
||||
) {
|
||||
$parser->error(
|
||||
sprintf(
|
||||
__('Value/Expression for the option %1$s was expected'),
|
||||
$ret->options[$lastOptionId]['name']
|
||||
),
|
||||
$list->tokens[$list->idx - 1]
|
||||
);
|
||||
}
|
||||
|
||||
if (empty($options['_UNSORTED'])) {
|
||||
ksort($ret->options);
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OptionsArray $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
if (empty($component->options)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$options = array();
|
||||
foreach ($component->options as $option) {
|
||||
if (!is_array($option)) {
|
||||
$options[] = $option;
|
||||
} else {
|
||||
$options[] = $option['name']
|
||||
. ((!empty($option['equals']) && $option['equals']) ? '=' : ' ')
|
||||
. (!empty($option['expr']) ? $option['expr'] : $option['value']);
|
||||
}
|
||||
}
|
||||
return implode(' ', $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if it has the specified option and returns it value or true.
|
||||
*
|
||||
* @param string $key The key to be checked.
|
||||
* @param bool $getExpr Gets the expression instead of the value.
|
||||
* The value is the processed form of the expression.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function has($key, $getExpr = false)
|
||||
{
|
||||
foreach ($this->options as $option) {
|
||||
if (is_array($option)) {
|
||||
if (!strcasecmp($key, $option['name'])) {
|
||||
return $getExpr ? $option['expr'] : $option['value'];
|
||||
}
|
||||
} elseif (!strcasecmp($key, $option)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the option from the array.
|
||||
*
|
||||
* @param string $key The key to be removed.
|
||||
*
|
||||
* @return bool Whether the key was found and deleted or not.
|
||||
*/
|
||||
public function remove($key)
|
||||
{
|
||||
foreach ($this->options as $idx => $option) {
|
||||
if (is_array($option)) {
|
||||
if (!strcasecmp($key, $option['name'])) {
|
||||
unset($this->options[$idx]);
|
||||
return true;
|
||||
}
|
||||
} elseif (!strcasecmp($key, $option)) {
|
||||
unset($this->options[$idx]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the specified options with these ones. Values with same ID will be
|
||||
* replaced.
|
||||
*
|
||||
* @param array|OptionsArray $options The options to be merged.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function merge($options)
|
||||
{
|
||||
if (is_array($options)) {
|
||||
$this->options = array_merge_recursive($this->options, $options);
|
||||
} elseif ($options instanceof OptionsArray) {
|
||||
$this->options = array_merge_recursive($this->options, $options->options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks tf there are no options set.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->options);
|
||||
}
|
||||
}
|
144
#pma/libraries/sql-parser/src/Components/OrderKeyword.php
Normal file
144
#pma/libraries/sql-parser/src/Components/OrderKeyword.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* `ORDER BY` keyword parser.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* `ORDER BY` keyword parser.
|
||||
*
|
||||
* @category Keywords
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class OrderKeyword extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* The expression that is used for ordering.
|
||||
*
|
||||
* @var Expression
|
||||
*/
|
||||
public $expr;
|
||||
|
||||
/**
|
||||
* The order type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Expression $expr The expression that we are sorting by.
|
||||
* @param string $type The sorting type.
|
||||
*/
|
||||
public function __construct($expr = null, $type = 'ASC')
|
||||
{
|
||||
$this->expr = $expr;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 OrderKeyword[]
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = array();
|
||||
|
||||
$expr = new OrderKeyword();
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 --------------------[ expression ]-------------------> 1
|
||||
*
|
||||
* 1 ------------------------[ , ]------------------------> 0
|
||||
* 1 -------------------[ ASC / DESC ]--------------------> 1
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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) {
|
||||
$expr->expr = Expression::parse($parser, $list);
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
if (($token->type === Token::TYPE_KEYWORD)
|
||||
&& (($token->value === 'ASC') || ($token->value === 'DESC'))
|
||||
) {
|
||||
$expr->type = $token->value;
|
||||
} elseif (($token->type === Token::TYPE_OPERATOR)
|
||||
&& ($token->value === ',')
|
||||
) {
|
||||
if (!empty($expr->expr)) {
|
||||
$ret[] = $expr;
|
||||
}
|
||||
$expr = new OrderKeyword();
|
||||
$state = 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Last iteration was not processed.
|
||||
if (!empty($expr->expr)) {
|
||||
$ret[] = $expr;
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param OrderKeyword|OrderKeyword[] $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
if (is_array($component)) {
|
||||
return implode(', ', $component);
|
||||
} else {
|
||||
return $component->expr . ' ' . $component->type;
|
||||
}
|
||||
}
|
||||
}
|
160
#pma/libraries/sql-parser/src/Components/ParameterDefinition.php
Normal file
160
#pma/libraries/sql-parser/src/Components/ParameterDefinition.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* The definition of a parameter of a function or procedure.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Context;
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* The definition of a parameter of a function or procedure.
|
||||
*
|
||||
* @category Components
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class ParameterDefinition extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* The name of the new column.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Parameter's direction (IN, OUT or INOUT).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $inOut;
|
||||
|
||||
/**
|
||||
* The data type of thew new column.
|
||||
*
|
||||
* @var DataType
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @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 ParameterDefinition[]
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = array();
|
||||
|
||||
$expr = new ParameterDefinition();
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 -----------------------[ ( ]------------------------> 1
|
||||
*
|
||||
* 1 ----------------[ IN / OUT / INOUT ]----------------> 1
|
||||
* 1 ----------------------[ name ]----------------------> 2
|
||||
*
|
||||
* 2 -------------------[ data type ]--------------------> 3
|
||||
*
|
||||
* 3 ------------------------[ , ]-----------------------> 1
|
||||
* 3 ------------------------[ ) ]-----------------------> (END)
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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_OPERATOR) && ($token->value === '(')) {
|
||||
$state = 1;
|
||||
}
|
||||
continue;
|
||||
} elseif ($state === 1) {
|
||||
if (($token->value === 'IN') || ($token->value === 'OUT') || ($token->value === 'INOUT')) {
|
||||
$expr->inOut = $token->value;
|
||||
++$list->idx;
|
||||
} elseif ($token->value === ')') {
|
||||
++$list->idx;
|
||||
break;
|
||||
} else {
|
||||
$expr->name = $token->value;
|
||||
$state = 2;
|
||||
}
|
||||
} elseif ($state === 2) {
|
||||
$expr->type = DataType::parse($parser, $list);
|
||||
$state = 3;
|
||||
} elseif ($state === 3) {
|
||||
$ret[] = $expr;
|
||||
$expr = new ParameterDefinition();
|
||||
if ($token->value === ',') {
|
||||
$state = 1;
|
||||
} elseif ($token->value === ')') {
|
||||
++$list->idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Last iteration was not saved.
|
||||
if ((isset($expr->name)) && ($expr->name !== '')) {
|
||||
$ret[] = $expr;
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ParameterDefinition[] $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
if (is_array($component)) {
|
||||
return '(' . implode(', ', $component) . ')';
|
||||
} else {
|
||||
$tmp = '';
|
||||
if (!empty($component->inOut)) {
|
||||
$tmp .= $component->inOut . ' ';
|
||||
}
|
||||
|
||||
return trim(
|
||||
$tmp . Context::escape($component->name) . ' ' . $component->type
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
223
#pma/libraries/sql-parser/src/Components/PartitionDefinition.php
Normal file
223
#pma/libraries/sql-parser/src/Components/PartitionDefinition.php
Normal file
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Parses the create definition of a partition.
|
||||
*
|
||||
* Used for parsing `CREATE TABLE` statement.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* Parses the create definition of a partition.
|
||||
*
|
||||
* Used for parsing `CREATE TABLE` statement.
|
||||
*
|
||||
* @category Components
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class PartitionDefinition extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* All field options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $OPTIONS = array(
|
||||
'STORAGE ENGINE' => array(1, 'var'),
|
||||
'ENGINE' => array(1, 'var'),
|
||||
'COMMENT' => array(2, 'var'),
|
||||
'DATA DIRECTORY' => array(3, 'var'),
|
||||
'INDEX DIRECTORY' => array(4, 'var'),
|
||||
'MAX_ROWS' => array(5, 'var'),
|
||||
'MIN_ROWS' => array(6, 'var'),
|
||||
'TABLESPACE' => array(7, 'var'),
|
||||
'NODEGROUP' => array(8, 'var'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Whether this entry is a subpartition or a partition.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $isSubpartition;
|
||||
|
||||
/**
|
||||
* The name of this partition.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* The type of this partition (what follows the `VALUES` keyword).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* The expression used to defined this partition.
|
||||
*
|
||||
* @var Expression|string
|
||||
*/
|
||||
public $expr;
|
||||
|
||||
/**
|
||||
* The subpartitions of this partition.
|
||||
*
|
||||
* @var PartitionDefinition[]
|
||||
*/
|
||||
public $subpartitions;
|
||||
|
||||
/**
|
||||
* The options of this field.
|
||||
*
|
||||
* @var OptionsArray
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* @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 PartitionDefinition
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = new PartitionDefinition();
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 -------------[ PARTITION | SUBPARTITION ]------------> 1
|
||||
*
|
||||
* 1 -----------------------[ name ]----------------------> 2
|
||||
*
|
||||
* 2 ----------------------[ VALUES ]---------------------> 3
|
||||
*
|
||||
* 3 ---------------------[ LESS THAN ]-------------------> 4
|
||||
* 3 ------------------------[ IN ]-----------------------> 4
|
||||
*
|
||||
* 4 -----------------------[ expr ]----------------------> 5
|
||||
*
|
||||
* 5 ----------------------[ options ]--------------------> 6
|
||||
*
|
||||
* 6 ------------------[ subpartitions ]------------------> (END)
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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) {
|
||||
$ret->isSubpartition = ($token->type === Token::TYPE_KEYWORD) && ($token->value === 'SUBPARTITION');
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
$ret->name = $token->value;
|
||||
|
||||
// Looking ahead for a 'VALUES' keyword.
|
||||
$idx = $list->idx;
|
||||
$list->getNext();
|
||||
$nextToken = $list->getNext();
|
||||
$list->idx = $idx;
|
||||
|
||||
$state = ($nextToken->type === Token::TYPE_KEYWORD)
|
||||
&& ($nextToken->value === 'VALUES')
|
||||
? 2 : 5;
|
||||
} elseif ($state === 2) {
|
||||
$state = 3;
|
||||
} elseif ($state === 3) {
|
||||
$ret->type = $token->value;
|
||||
$state = 4;
|
||||
} elseif ($state === 4) {
|
||||
if ($token->value === 'MAXVALUE') {
|
||||
$ret->expr = $token->value;
|
||||
} else {
|
||||
$ret->expr = Expression::parse(
|
||||
$parser,
|
||||
$list,
|
||||
array(
|
||||
'parenthesesDelimited' => true,
|
||||
'breakOnAlias' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
$state = 5;
|
||||
} elseif ($state === 5) {
|
||||
$ret->options = OptionsArray::parse($parser, $list, static::$OPTIONS);
|
||||
$state = 6;
|
||||
} elseif ($state === 6) {
|
||||
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) {
|
||||
$ret->subpartitions = ArrayObj::parse(
|
||||
$parser,
|
||||
$list,
|
||||
array(
|
||||
'type' => 'SqlParser\\Components\\PartitionDefinition'
|
||||
)
|
||||
);
|
||||
++$list->idx;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PartitionDefinition|PartitionDefinition[] $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
if (is_array($component)) {
|
||||
return "(\n" . implode(",\n", $component) . "\n)";
|
||||
} else {
|
||||
if ($component->isSubpartition) {
|
||||
return trim('SUBPARTITION ' . $component->name . ' ' . $component->options);
|
||||
} else {
|
||||
$subpartitions = empty($component->subpartitions)
|
||||
? '' : ' ' . PartitionDefinition::build($component->subpartitions);
|
||||
return trim(
|
||||
'PARTITION ' . $component->name
|
||||
. (empty($component->type) ? '' : ' VALUES ' . $component->type . ' ' . $component->expr . ' ')
|
||||
. $component->options . $subpartitions
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
157
#pma/libraries/sql-parser/src/Components/Reference.php
Normal file
157
#pma/libraries/sql-parser/src/Components/Reference.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* `REFERENCES` keyword parser.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Context;
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* `REFERENCES` keyword parser.
|
||||
*
|
||||
* @category Keywords
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class Reference extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* All references options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $REFERENCES_OPTIONS = array(
|
||||
'MATCH' => array(1, 'var'),
|
||||
'ON DELETE' => array(2, 'var'),
|
||||
'ON UPDATE' => array(3, 'var'),
|
||||
);
|
||||
|
||||
/**
|
||||
* The referenced table.
|
||||
*
|
||||
* @var Expression
|
||||
*/
|
||||
public $table;
|
||||
|
||||
/**
|
||||
* The referenced columns.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $columns;
|
||||
|
||||
/**
|
||||
* The options of the referencing.
|
||||
*
|
||||
* @var OptionsArray
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Expression $table The name of the table referenced.
|
||||
* @param array $columns The columns referenced.
|
||||
* @param OptionsArray $options The options.
|
||||
*/
|
||||
public function __construct($table = null, array $columns = array(), $options = null)
|
||||
{
|
||||
$this->table = $table;
|
||||
$this->columns = $columns;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 Reference
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = new Reference();
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 ----------------------[ table ]---------------------> 1
|
||||
*
|
||||
* 1 ---------------------[ columns ]--------------------> 2
|
||||
*
|
||||
* 2 ---------------------[ options ]--------------------> (END)
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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) {
|
||||
$ret->table = Expression::parse(
|
||||
$parser,
|
||||
$list,
|
||||
array(
|
||||
'parseField' => 'table',
|
||||
'breakOnAlias' => true,
|
||||
)
|
||||
);
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
$ret->columns = ArrayObj::parse($parser, $list)->values;
|
||||
$state = 2;
|
||||
} elseif ($state === 2) {
|
||||
$ret->options = OptionsArray::parse($parser, $list, static::$REFERENCES_OPTIONS);
|
||||
++$list->idx;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Reference $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
return trim(
|
||||
$component->table
|
||||
. ' (' . implode(', ', Context::escape($component->columns)) . ') '
|
||||
. $component->options
|
||||
);
|
||||
}
|
||||
}
|
173
#pma/libraries/sql-parser/src/Components/RenameOperation.php
Normal file
173
#pma/libraries/sql-parser/src/Components/RenameOperation.php
Normal file
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* `RENAME TABLE` keyword parser.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* `RENAME TABLE` keyword parser.
|
||||
*
|
||||
* @category Keywords
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class RenameOperation extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* The old table name.
|
||||
*
|
||||
* @var Expression
|
||||
*/
|
||||
public $old;
|
||||
|
||||
/**
|
||||
* The new table name.
|
||||
*
|
||||
* @var Expression
|
||||
*/
|
||||
public $new;
|
||||
|
||||
/**
|
||||
* @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 RenameOperation[]
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = array();
|
||||
|
||||
$expr = new RenameOperation();
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 ---------------------[ old name ]--------------------> 1
|
||||
*
|
||||
* 1 ------------------------[ TO ]-----------------------> 2
|
||||
*
|
||||
* 2 ---------------------[ old name ]--------------------> 3
|
||||
*
|
||||
* 3 ------------------------[ , ]------------------------> 0
|
||||
* 3 -----------------------[ else ]----------------------> (END)
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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) {
|
||||
$expr->old = Expression::parse(
|
||||
$parser,
|
||||
$list,
|
||||
array(
|
||||
'breakOnAlias' => true,
|
||||
'parseField' => 'table',
|
||||
)
|
||||
);
|
||||
if (empty($expr->old)) {
|
||||
$parser->error(
|
||||
__('The old name of the table was expected.'),
|
||||
$token
|
||||
);
|
||||
}
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
if (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'TO')) {
|
||||
$state = 2;
|
||||
} else {
|
||||
$parser->error(
|
||||
__('Keyword "TO" was expected.'),
|
||||
$token
|
||||
);
|
||||
break;
|
||||
}
|
||||
} elseif ($state === 2) {
|
||||
$expr->new = Expression::parse(
|
||||
$parser,
|
||||
$list,
|
||||
array(
|
||||
'breakOnAlias' => true,
|
||||
'parseField' => 'table',
|
||||
)
|
||||
);
|
||||
if (empty($expr->new)) {
|
||||
$parser->error(
|
||||
__('The new name of the table was expected.'),
|
||||
$token
|
||||
);
|
||||
}
|
||||
$state = 3;
|
||||
} elseif ($state === 3) {
|
||||
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === ',')) {
|
||||
$ret[] = $expr;
|
||||
$expr = new RenameOperation();
|
||||
$state = 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($state !== 3) {
|
||||
$parser->error(
|
||||
__('A rename operation was expected.'),
|
||||
$list->tokens[$list->idx - 1]
|
||||
);
|
||||
}
|
||||
|
||||
// Last iteration was not saved.
|
||||
if (!empty($expr->old)) {
|
||||
$ret[] = $expr;
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RenameOperation $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
if (is_array($component)) {
|
||||
return implode(', ', $component);
|
||||
} else {
|
||||
return $component->old . ' TO ' . $component->new;
|
||||
}
|
||||
}
|
||||
}
|
137
#pma/libraries/sql-parser/src/Components/SetOperation.php
Normal file
137
#pma/libraries/sql-parser/src/Components/SetOperation.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* `SET` keyword parser.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Parser;
|
||||
use SqlParser\Token;
|
||||
use SqlParser\TokensList;
|
||||
|
||||
/**
|
||||
* `SET` keyword parser.
|
||||
*
|
||||
* @category Keywords
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class SetOperation extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* The name of the column that is being updated.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $column;
|
||||
|
||||
/**
|
||||
* The new value.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* @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 SetOperation[]
|
||||
*/
|
||||
public static function parse(Parser $parser, TokensList $list, array $options = array())
|
||||
{
|
||||
$ret = array();
|
||||
|
||||
$expr = new SetOperation();
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 -------------------[ column name ]-------------------> 1
|
||||
*
|
||||
* 1 ------------------------[ , ]------------------------> 0
|
||||
* 1 ----------------------[ value ]----------------------> 1
|
||||
*
|
||||
* @var int $state
|
||||
*/
|
||||
$state = 0;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
* @var Token $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;
|
||||
}
|
||||
|
||||
// No keyword is expected.
|
||||
if (($token->type === Token::TYPE_KEYWORD)
|
||||
&& ($token->flags & Token::FLAG_KEYWORD_RESERVED)
|
||||
&& ($state == 0)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
if ($token->token === '=') {
|
||||
$state = 1;
|
||||
} elseif ($token->value !== ',') {
|
||||
$expr->column .= $token->token;
|
||||
}
|
||||
} elseif ($state === 1) {
|
||||
$tmp = Expression::parse(
|
||||
$parser,
|
||||
$list,
|
||||
array(
|
||||
'breakOnAlias' => true,
|
||||
)
|
||||
);
|
||||
if ($tmp == null) {
|
||||
break;
|
||||
}
|
||||
$expr->column = trim($expr->column);
|
||||
$expr->value = $tmp->expr;
|
||||
$ret[] = $expr;
|
||||
$expr = new SetOperation();
|
||||
$state = 0;
|
||||
}
|
||||
}
|
||||
|
||||
--$list->idx;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SetOperation|SetOperation[] $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
if (is_array($component)) {
|
||||
return implode(', ', $component);
|
||||
} else {
|
||||
return $component->column . ' = ' . $component->value;
|
||||
}
|
||||
}
|
||||
}
|
39
#pma/libraries/sql-parser/src/Components/UnionKeyword.php
Normal file
39
#pma/libraries/sql-parser/src/Components/UnionKeyword.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* `UNION` keyword builder.
|
||||
*
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
*/
|
||||
namespace SqlParser\Components;
|
||||
|
||||
use SqlParser\Component;
|
||||
use SqlParser\Statements\SelectStatement;
|
||||
|
||||
/**
|
||||
* `UNION` keyword builder.
|
||||
*
|
||||
* @category Keywords
|
||||
* @package SqlParser
|
||||
* @subpackage Components
|
||||
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
|
||||
*/
|
||||
class UnionKeyword extends Component
|
||||
{
|
||||
|
||||
/**
|
||||
* @param SelectStatement[] $component The component to be built.
|
||||
* @param array $options Parameters for building.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function build($component, array $options = array())
|
||||
{
|
||||
$tmp = array();
|
||||
foreach ($component as $component) {
|
||||
$tmp[] = $component[0] . ' ' . $component[1];
|
||||
}
|
||||
return implode(' ', $tmp);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user