Merge branch 'QA_4_6' of github.com:phpmyadmin/phpmyadmin into QA_4_6
Conflicts: ChangeLog
This commit is contained in:
commit
3a054c82d0
@ -52,6 +52,13 @@ phpMyAdmin - ChangeLog
|
||||
- issue #11834 Adjust privileges fails if database name contains underscores
|
||||
- issue #11906 'Loading...' banner shows on login screen
|
||||
- issue #11930 Fixed changing of table parameters, eg. AUTO_INCREMENT
|
||||
- issue #11885 Call to undefined function SqlParser\ctype_alnum()
|
||||
- issue #11879 4.5.3.1 - NOW() function not recognized by parser
|
||||
- issue #11867 Gracefully handle the DESC statement
|
||||
- issue #11843 Fractional timestamp causes corrupted SQL export
|
||||
- issue #11836 Static analysis error for valid WHERE condition with IF keyword
|
||||
- issue #11800 Syntax Verifier error using REGEXP in SQL statement
|
||||
- issue #11799 Backslashes in comments are being interpreted as escape characters
|
||||
- issue #11909 Can't insert row into table that contains generated column
|
||||
|
||||
4.5.4.1 (2016-01-29)
|
||||
|
||||
@ -63,20 +63,32 @@ class ArrayObj extends Component
|
||||
$ret = empty($options['type']) ? new ArrayObj() : array();
|
||||
|
||||
/**
|
||||
* The state of the parser.
|
||||
* The last raw expression.
|
||||
*
|
||||
* Below are the states of the parser.
|
||||
*
|
||||
* 0 -----------------------[ ( ]------------------------> 1
|
||||
*
|
||||
* 1 ------------------[ array element ]-----------------> 2
|
||||
*
|
||||
* 2 ------------------------[ , ]-----------------------> 1
|
||||
* 2 ------------------------[ ) ]-----------------------> (END)
|
||||
*
|
||||
* @var int $state
|
||||
* @var string $lastRaw
|
||||
*/
|
||||
$state = 0;
|
||||
$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) {
|
||||
/**
|
||||
@ -92,49 +104,72 @@ class ArrayObj extends Component
|
||||
}
|
||||
|
||||
// Skipping whitespaces and comments.
|
||||
if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
|
||||
if (($token->type === Token::TYPE_WHITESPACE)
|
||||
|| ($token->type === Token::TYPE_COMMENT)
|
||||
) {
|
||||
$lastRaw .= $token->token;
|
||||
$lastValue = trim($lastValue) .' ';
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state === 0) {
|
||||
if (($token->type !== Token::TYPE_OPERATOR) || ($token->value !== '(')) {
|
||||
$parser->error(
|
||||
__('An opening bracket was expected.'),
|
||||
$token
|
||||
);
|
||||
break;
|
||||
}
|
||||
$state = 1;
|
||||
} elseif ($state === 1) {
|
||||
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === ')')) {
|
||||
// Empty array.
|
||||
break;
|
||||
}
|
||||
if (empty($options['type'])) {
|
||||
$ret->values[] = $token->value;
|
||||
$ret->raw[] = $token->token;
|
||||
} else {
|
||||
$ret[] = $options['type']::parse(
|
||||
$parser,
|
||||
$list,
|
||||
empty($options['typeOptions']) ? array() : $options['typeOptions']
|
||||
);
|
||||
}
|
||||
$state = 2;
|
||||
} elseif ($state === 2) {
|
||||
if (($token->type !== Token::TYPE_OPERATOR) || (($token->value !== ',') && ($token->value !== ')'))) {
|
||||
$parser->error(
|
||||
__('A comma or a closing bracket was expected'),
|
||||
$token
|
||||
);
|
||||
break;
|
||||
}
|
||||
if ($token->value === ',') {
|
||||
$state = 1;
|
||||
} else { // )
|
||||
break;
|
||||
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;
|
||||
|
||||
@ -30,17 +30,18 @@ class Condition extends Component
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $DELIMITERS = array('&&', 'AND', 'OR', 'XOR', '||');
|
||||
public static $DELIMITERS = array('&&', '||', 'AND', 'OR', 'XOR');
|
||||
|
||||
/**
|
||||
* Hash map containing reserved keywords that are also operators.
|
||||
* List of allowed reserved keywords in conditions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $OPERATORS = array(
|
||||
public static $ALLOWED_KEYWORDS = array(
|
||||
'AND' => 1,
|
||||
'BETWEEN' => 1,
|
||||
'EXISTS' => 1,
|
||||
'IF' => 1,
|
||||
'IN' => 1,
|
||||
'IS' => 1,
|
||||
'LIKE' => 1,
|
||||
@ -106,6 +107,7 @@ class Condition extends Component
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@ -115,6 +117,7 @@ class Condition extends Component
|
||||
$betweenBefore = false;
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
@ -142,11 +145,12 @@ class Condition extends Component
|
||||
// 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)) {
|
||||
// Adding the condition that is delimited by this operator.
|
||||
$ret[] = $expr;
|
||||
}
|
||||
|
||||
@ -155,11 +159,21 @@ class Condition extends Component
|
||||
$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)) {
|
||||
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;
|
||||
@ -168,23 +182,16 @@ class Condition extends Component
|
||||
}
|
||||
}
|
||||
|
||||
// No keyword is expected.
|
||||
if (($token->type === Token::TYPE_KEYWORD) && ($token->flags & Token::FLAG_KEYWORD_RESERVED)) {
|
||||
if ($token->value === 'BETWEEN') {
|
||||
$betweenBefore = true;
|
||||
}
|
||||
if (($brackets === 0) && (empty(static::$OPERATORS[$token->value]))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$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_KEYWORD)
|
||||
&& (!($token->flags & Token::FLAG_KEYWORD_RESERVED)))
|
||||
|| ($token->type === Token::TYPE_STRING)
|
||||
|| ($token->type === Token::TYPE_SYMBOL)
|
||||
) {
|
||||
$expr->identifiers[] = $token->value;
|
||||
if (!in_array($token->value, $expr->identifiers)) {
|
||||
$expr->identifiers[] = $token->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ class CreateDefinition extends Component
|
||||
|
||||
'NOT NULL' => 1,
|
||||
'NULL' => 1,
|
||||
'DEFAULT' => array(2, 'var'),
|
||||
'DEFAULT' => array(2, 'expr'),
|
||||
'AUTO_INCREMENT' => 3,
|
||||
'PRIMARY' => 4,
|
||||
'PRIMARY KEY' => 4,
|
||||
|
||||
@ -28,6 +28,15 @@ use SqlParser\TokensList;
|
||||
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
|
||||
);
|
||||
|
||||
/**
|
||||
* The name of this database.
|
||||
*
|
||||
@ -137,9 +146,9 @@ class Expression extends Component
|
||||
/**
|
||||
* Whether an alias is expected. Is 2 if `AS` keyword was found.
|
||||
*
|
||||
* @var int $alias
|
||||
* @var bool $alias
|
||||
*/
|
||||
$alias = 0;
|
||||
$alias = false;
|
||||
|
||||
/**
|
||||
* Counts brackets.
|
||||
@ -149,17 +158,14 @@ class Expression extends Component
|
||||
$brackets = 0;
|
||||
|
||||
/**
|
||||
* Keeps track of the previous token.
|
||||
* Possible values:
|
||||
* string, if function was previously found;
|
||||
* true, if opening bracket was previously found;
|
||||
* null, in any other case.
|
||||
* Keeps track of the last two previous tokens.
|
||||
*
|
||||
* @var string|bool $prev
|
||||
* @var Token[] $prev
|
||||
*/
|
||||
$prev = null;
|
||||
$prev = array(null, null);
|
||||
|
||||
for (; $list->idx < $list->count; ++$list->idx) {
|
||||
|
||||
/**
|
||||
* Token parsed at this moment.
|
||||
*
|
||||
@ -173,36 +179,48 @@ class Expression extends Component
|
||||
}
|
||||
|
||||
// Skipping whitespaces and comments.
|
||||
if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
|
||||
if (($isExpr) && (!$alias)) {
|
||||
if (($token->type === Token::TYPE_WHITESPACE)
|
||||
|| ($token->type === Token::TYPE_COMMENT)
|
||||
) {
|
||||
if ($isExpr) {
|
||||
$ret->expr .= $token->token;
|
||||
}
|
||||
if (($alias === 0) && (empty($options['noAlias'])) && (!$isExpr) && (!$dot) && (!empty($ret->expr))) {
|
||||
$alias = 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($token->type === Token::TYPE_KEYWORD)
|
||||
&& ($token->flags & Token::FLAG_KEYWORD_RESERVED)
|
||||
&& ($token->value !== 'DUAL')
|
||||
&& ($token->value !== 'NULL')
|
||||
) {
|
||||
// Keywords may be found only between brackets.
|
||||
if ($brackets === 0) {
|
||||
if ((empty($options['noAlias'])) && ($token->value === 'AS')) {
|
||||
$alias = 2;
|
||||
continue;
|
||||
}
|
||||
if (!($token->flags & Token::FLAG_KEYWORD_FUNCTION)) {
|
||||
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) {
|
||||
$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;
|
||||
}
|
||||
} elseif ($prev === true) {
|
||||
if ((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;
|
||||
if ($token->value === 'AS') {
|
||||
if (!empty($options['noAlias'])) {
|
||||
break;
|
||||
}
|
||||
if (!empty($ret->alias)) {
|
||||
$parser->error(
|
||||
__('An alias was previously found.'),
|
||||
$token
|
||||
);
|
||||
break;
|
||||
}
|
||||
$alias = true;
|
||||
continue;
|
||||
}
|
||||
$isExpr = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,22 +228,25 @@ class Expression extends Component
|
||||
if ((!empty($options['noBrackets']))
|
||||
&& (($token->value === '(') || ($token->value === ')'))
|
||||
) {
|
||||
// No brackets were expected.
|
||||
break;
|
||||
}
|
||||
if ($token->value === '(') {
|
||||
++$brackets;
|
||||
if ((empty($ret->function)) && ($prev !== null) && ($prev !== true)) {
|
||||
// A function name was previously found and now an open
|
||||
// bracket, so this is a function call.
|
||||
$ret->function = $prev;
|
||||
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;
|
||||
}
|
||||
$isExpr = true;
|
||||
} elseif ($token->value === ')') {
|
||||
--$brackets;
|
||||
if ($brackets === 0) {
|
||||
if (!empty($options['bracketsDelimited'])) {
|
||||
// The current token is the last brackets, the next
|
||||
// one will be outside.
|
||||
// The current token is the last bracket, the next
|
||||
// one will be outside the expression.
|
||||
$ret->expr .= $token->token;
|
||||
++$list->idx;
|
||||
break;
|
||||
@ -236,109 +257,100 @@ class Expression extends Component
|
||||
break;
|
||||
}
|
||||
} elseif ($token->value === ',') {
|
||||
// Expressions are comma-delimited.
|
||||
if ($brackets === 0) {
|
||||
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 (($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 !== '.'))
|
||||
) {
|
||||
// Numbers, booleans and operators are usually part of expressions.
|
||||
// Numbers, booleans and operators (except dot) are usually part
|
||||
// of expressions.
|
||||
$isExpr = true;
|
||||
}
|
||||
|
||||
// Saving the previous token.
|
||||
$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 = 0;
|
||||
} else {
|
||||
if (!$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;
|
||||
} else {
|
||||
// We found the name of a column (or table if column
|
||||
// field should be skipped; used to parse table names).
|
||||
$field = (!empty($options['skipColumn'])) ? 'table' : 'column';
|
||||
if (!empty($ret->$field)) {
|
||||
// No alias is expected.
|
||||
if (!empty($options['noAlias'])) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Parsing aliases without `AS` keyword and any
|
||||
// whitespace.
|
||||
// Example: SELECT 1`foo`
|
||||
if (($token->type === Token::TYPE_STRING)
|
||||
|| (($token->type === Token::TYPE_SYMBOL)
|
||||
&& ($token->flags & Token::FLAG_SYMBOL_BACKTICK))
|
||||
) {
|
||||
if (!empty($ret->alias)) {
|
||||
$parser->error(
|
||||
__('An alias was previously found.'),
|
||||
$token
|
||||
);
|
||||
}
|
||||
$ret->alias = $token->value;
|
||||
}
|
||||
} else {
|
||||
$ret->$field = $token->value;
|
||||
}
|
||||
$dot = false;
|
||||
$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 {
|
||||
// Parsing aliases without `AS` keyword.
|
||||
// Example: SELECT 'foo' `bar`
|
||||
if (($brackets === 0) && (empty($options['noAlias']))) {
|
||||
if (($token->type === Token::TYPE_NONE) || ($token->type === Token::TYPE_STRING)
|
||||
|| (($token->type === Token::TYPE_SYMBOL) && ($token->flags & Token::FLAG_SYMBOL_BACKTICK))
|
||||
) {
|
||||
if (!empty($ret->alias)) {
|
||||
$parser->error(
|
||||
__('An alias was previously found.'),
|
||||
$token
|
||||
);
|
||||
}
|
||||
$ret->alias = $token->value;
|
||||
continue;
|
||||
$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['skipColumn'])) ? 'table' : 'column';
|
||||
if (empty($ret->$field)) {
|
||||
$ret->$field = $token->value;
|
||||
$ret->expr .= $token->token;
|
||||
$dot = false;
|
||||
} else {
|
||||
// No alias is expected.
|
||||
if (!empty($options['noAlias'])) {
|
||||
break;
|
||||
}
|
||||
if (!empty($ret->alias)) {
|
||||
$parser->error(__('An alias was previously found.'), $token);
|
||||
break;
|
||||
}
|
||||
$ret->alias = $token->value;
|
||||
}
|
||||
}
|
||||
|
||||
$ret->expr .= $token->token;
|
||||
}
|
||||
|
||||
if (($token->type === Token::TYPE_KEYWORD) && ($token->flags & Token::FLAG_KEYWORD_FUNCTION)) {
|
||||
$prev = strtoupper($token->value);
|
||||
} elseif (($token->type === Token::TYPE_OPERATOR) || ($token->value === '(')) {
|
||||
$prev = true;
|
||||
} else {
|
||||
$prev = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($alias === 2) {
|
||||
if ($alias) {
|
||||
$parser->error(
|
||||
__('An alias was expected.'),
|
||||
$list->tokens[$list->idx - 1]
|
||||
);
|
||||
}
|
||||
|
||||
// Whitespaces might be added at the end.
|
||||
// White-spaces might be added at the end.
|
||||
$ret->expr = trim($ret->expr);
|
||||
|
||||
if (empty($ret->expr)) {
|
||||
|
||||
@ -92,7 +92,7 @@ class SetOperation extends Component
|
||||
if ($state === 0) {
|
||||
if ($token->token === '=') {
|
||||
$state = 1;
|
||||
} else if ($token->value !== ',') {
|
||||
} elseif ($token->value !== ',') {
|
||||
$expr->column .= $token->token;
|
||||
}
|
||||
} elseif ($state === 1) {
|
||||
|
||||
@ -107,7 +107,7 @@ abstract class Context
|
||||
':=' => 8,
|
||||
|
||||
// @see Token::FLAG_OPERATOR_SQL
|
||||
'(' => 16, ')' => 16, '.' => 16, ',' => 16,
|
||||
'(' => 16, ')' => 16, '.' => 16, ',' => 16, ';' => 16,
|
||||
);
|
||||
|
||||
/**
|
||||
@ -397,9 +397,12 @@ abstract class Context
|
||||
*/
|
||||
public static function isSeparator($str)
|
||||
{
|
||||
// NOTES: Only ASCII characters may be separators.
|
||||
// NOTES: Only non alphanumeric ASCII characters may be separators.
|
||||
// `~` is the last printable ASCII character.
|
||||
return ($str <= '~') && (!ctype_alnum($str)) && ($str !== '_');
|
||||
return ($str <= '~') && ($str !== '_')
|
||||
&& (($str < '0') || ($str > '9'))
|
||||
&& (($str < 'a') || ($str > 'z'))
|
||||
&& (($str < 'A') || ($str > 'Z'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -37,8 +37,8 @@ class ContextMySql50000 extends Context
|
||||
public static $KEYWORDS = array(
|
||||
|
||||
'DO' => 1, 'IO' => 1, 'NO' => 1, 'XA' => 1,
|
||||
'ANY' => 1, 'BDB' => 1, 'BIT' => 1, 'CPU' => 1, 'END' => 1, 'IPC' => 1,
|
||||
'NDB' => 1, 'NEW' => 1, 'ONE' => 1, 'ROW' => 1,
|
||||
'ANY' => 1, 'BDB' => 1, 'CPU' => 1, 'END' => 1, 'IPC' => 1, 'NDB' => 1,
|
||||
'NEW' => 1, 'ONE' => 1, 'ROW' => 1,
|
||||
'BOOL' => 1, 'BYTE' => 1, 'CODE' => 1, 'CUBE' => 1, 'DATA' => 1, 'FAST' => 1,
|
||||
'FILE' => 1, 'FULL' => 1, 'HASH' => 1, 'HELP' => 1, 'LAST' => 1, 'LOGS' => 1,
|
||||
'MODE' => 1, 'NAME' => 1, 'NEXT' => 1, 'NONE' => 1, 'OPEN' => 1, 'PAGE' => 1,
|
||||
@ -162,7 +162,7 @@ class ContextMySql50000 extends Context
|
||||
'DEFAULT CHARACTER SET' => 7,
|
||||
'WITH CONSISTENT SNAPSHOT' => 7,
|
||||
|
||||
'XML' => 9,
|
||||
'BIT' => 9, 'XML' => 9,
|
||||
'ENUM' => 9, 'JSON' => 9, 'TEXT' => 9,
|
||||
'ARRAY' => 9,
|
||||
'SERIAL' => 9,
|
||||
|
||||
@ -37,8 +37,8 @@ class ContextMySql50100 extends Context
|
||||
public static $KEYWORDS = array(
|
||||
|
||||
'AT' => 1, 'DO' => 1, 'IO' => 1, 'NO' => 1, 'XA' => 1,
|
||||
'ANY' => 1, 'BDB' => 1, 'BIT' => 1, 'CPU' => 1, 'END' => 1, 'IPC' => 1,
|
||||
'NDB' => 1, 'NEW' => 1, 'ONE' => 1, 'ROW' => 1,
|
||||
'ANY' => 1, 'BDB' => 1, 'CPU' => 1, 'END' => 1, 'IPC' => 1, 'NDB' => 1,
|
||||
'NEW' => 1, 'ONE' => 1, 'ROW' => 1,
|
||||
'BOOL' => 1, 'BYTE' => 1, 'CODE' => 1, 'CUBE' => 1, 'DATA' => 1, 'DISK' => 1,
|
||||
'ENDS' => 1, 'FAST' => 1, 'FILE' => 1, 'FULL' => 1, 'GOTO' => 1, 'HASH' => 1,
|
||||
'HELP' => 1, 'HOST' => 1, 'LAST' => 1, 'LESS' => 1, 'LIST' => 1, 'LOGS' => 1,
|
||||
@ -175,7 +175,7 @@ class ContextMySql50100 extends Context
|
||||
'DEFAULT CHARACTER SET' => 7,
|
||||
'WITH CONSISTENT SNAPSHOT' => 7,
|
||||
|
||||
'XML' => 9,
|
||||
'BIT' => 9, 'XML' => 9,
|
||||
'ENUM' => 9, 'JSON' => 9, 'TEXT' => 9,
|
||||
'ARRAY' => 9,
|
||||
'SERIAL' => 9,
|
||||
|
||||
@ -37,8 +37,8 @@ class ContextMySql50500 extends Context
|
||||
public static $KEYWORDS = array(
|
||||
|
||||
'AT' => 1, 'DO' => 1, 'IO' => 1, 'NO' => 1, 'XA' => 1,
|
||||
'ANY' => 1, 'BIT' => 1, 'CPU' => 1, 'END' => 1, 'IPC' => 1, 'NDB' => 1,
|
||||
'NEW' => 1, 'ONE' => 1, 'ROW' => 1,
|
||||
'ANY' => 1, 'CPU' => 1, 'END' => 1, 'IPC' => 1, 'NDB' => 1, 'NEW' => 1,
|
||||
'ONE' => 1, 'ROW' => 1,
|
||||
'BOOL' => 1, 'BYTE' => 1, 'CODE' => 1, 'CUBE' => 1, 'DATA' => 1, 'DISK' => 1,
|
||||
'ENDS' => 1, 'FAST' => 1, 'FILE' => 1, 'FULL' => 1, 'HASH' => 1, 'HELP' => 1,
|
||||
'HOST' => 1, 'LAST' => 1, 'LESS' => 1, 'LIST' => 1, 'LOGS' => 1, 'MODE' => 1,
|
||||
@ -180,7 +180,7 @@ class ContextMySql50500 extends Context
|
||||
'DEFAULT CHARACTER SET' => 7,
|
||||
'WITH CONSISTENT SNAPSHOT' => 7,
|
||||
|
||||
'XML' => 9,
|
||||
'BIT' => 9, 'XML' => 9,
|
||||
'ENUM' => 9, 'JSON' => 9, 'TEXT' => 9,
|
||||
'ARRAY' => 9,
|
||||
'SERIAL' => 9,
|
||||
|
||||
@ -37,8 +37,8 @@ class ContextMySql50600 extends Context
|
||||
public static $KEYWORDS = array(
|
||||
|
||||
'AT' => 1, 'DO' => 1, 'IO' => 1, 'NO' => 1, 'XA' => 1,
|
||||
'ANY' => 1, 'BIT' => 1, 'CPU' => 1, 'END' => 1, 'IPC' => 1, 'NDB' => 1,
|
||||
'NEW' => 1, 'ONE' => 1, 'ROW' => 1,
|
||||
'ANY' => 1, 'CPU' => 1, 'END' => 1, 'IPC' => 1, 'NDB' => 1, 'NEW' => 1,
|
||||
'ONE' => 1, 'ROW' => 1,
|
||||
'BOOL' => 1, 'BYTE' => 1, 'CODE' => 1, 'CUBE' => 1, 'DATA' => 1, 'DISK' => 1,
|
||||
'ENDS' => 1, 'FAST' => 1, 'FILE' => 1, 'FULL' => 1, 'HASH' => 1, 'HELP' => 1,
|
||||
'HOST' => 1, 'LAST' => 1, 'LESS' => 1, 'LIST' => 1, 'LOGS' => 1, 'MODE' => 1,
|
||||
@ -186,7 +186,7 @@ class ContextMySql50600 extends Context
|
||||
'DEFAULT CHARACTER SET' => 7,
|
||||
'WITH CONSISTENT SNAPSHOT' => 7,
|
||||
|
||||
'XML' => 9,
|
||||
'BIT' => 9, 'XML' => 9,
|
||||
'ENUM' => 9, 'JSON' => 9, 'TEXT' => 9,
|
||||
'ARRAY' => 9,
|
||||
'SERIAL' => 9,
|
||||
|
||||
@ -37,8 +37,8 @@ class ContextMySql50700 extends Context
|
||||
public static $KEYWORDS = array(
|
||||
|
||||
'AT' => 1, 'DO' => 1, 'IO' => 1, 'NO' => 1, 'XA' => 1,
|
||||
'ANY' => 1, 'BIT' => 1, 'CPU' => 1, 'END' => 1, 'IPC' => 1, 'NDB' => 1,
|
||||
'NEW' => 1, 'ONE' => 1, 'ROW' => 1, 'XID' => 1,
|
||||
'ANY' => 1, 'CPU' => 1, 'END' => 1, 'IPC' => 1, 'NDB' => 1, 'NEW' => 1,
|
||||
'ONE' => 1, 'ROW' => 1, 'XID' => 1,
|
||||
'BOOL' => 1, 'BYTE' => 1, 'CODE' => 1, 'CUBE' => 1, 'DATA' => 1, 'DISK' => 1,
|
||||
'ENDS' => 1, 'FAST' => 1, 'FILE' => 1, 'FULL' => 1, 'HASH' => 1, 'HELP' => 1,
|
||||
'HOST' => 1, 'LAST' => 1, 'LESS' => 1, 'LIST' => 1, 'LOGS' => 1, 'MODE' => 1,
|
||||
@ -193,7 +193,7 @@ class ContextMySql50700 extends Context
|
||||
'DEFAULT CHARACTER SET' => 7,
|
||||
'WITH CONSISTENT SNAPSHOT' => 7,
|
||||
|
||||
'XML' => 9,
|
||||
'BIT' => 9, 'XML' => 9,
|
||||
'ENUM' => 9, 'JSON' => 9, 'TEXT' => 9,
|
||||
'ARRAY' => 9,
|
||||
'SERIAL' => 9,
|
||||
|
||||
@ -36,6 +36,7 @@ class Parser
|
||||
|
||||
// MySQL Utility Statements
|
||||
'DESCRIBE' => 'SqlParser\\Statements\\ExplainStatement',
|
||||
'DESC' => 'SqlParser\\Statements\\ExplainStatement',
|
||||
'EXPLAIN' => 'SqlParser\\Statements\\ExplainStatement',
|
||||
'FLUSH' => '',
|
||||
'GRANT' => '',
|
||||
|
||||
@ -76,8 +76,8 @@ class AlterStatement extends Statement
|
||||
$parser,
|
||||
$list,
|
||||
array(
|
||||
'noAlias' => true,
|
||||
'noBrackets' => true,
|
||||
'noAlias' => true,
|
||||
'noBrackets' => true,
|
||||
)
|
||||
);
|
||||
++$list->idx; // Skipping field.
|
||||
|
||||
@ -29,12 +29,18 @@ class BufferedQuery
|
||||
{
|
||||
|
||||
// Constants that describe the current status of the parser.
|
||||
const STATUS_STRING_SINGLE_QUOTES = 1;
|
||||
const STATUS_STRING_DOUBLE_QUOTES = 2;
|
||||
const STATUS_STRING_BACKTICK = 3;
|
||||
const STATUS_COMMENT_BASH = 4;
|
||||
const STATUS_COMMENT_C = 5;
|
||||
const STATUS_COMMENT_SQL = 6;
|
||||
|
||||
// A string is being parsed.
|
||||
const STATUS_STRING = 16; // 0001 0000
|
||||
const STATUS_STRING_SINGLE_QUOTES = 17; // 0001 0001
|
||||
const STATUS_STRING_DOUBLE_QUOTES = 18; // 0001 0010
|
||||
const STATUS_STRING_BACKTICK = 20; // 0001 0100
|
||||
|
||||
// A comment is being parsed.
|
||||
const STATUS_COMMENT = 32; // 0010 0000
|
||||
const STATUS_COMMENT_BASH = 33; // 0010 0001
|
||||
const STATUS_COMMENT_C = 34; // 0010 0010
|
||||
const STATUS_COMMENT_SQL = 36; // 0010 0100
|
||||
|
||||
/**
|
||||
* The query that is being processed.
|
||||
@ -193,7 +199,7 @@ class BufferedQuery
|
||||
* treated differently, because of the preceding backslash, it will
|
||||
* be ignored.
|
||||
*/
|
||||
if ($this->query[$i] === '\\') {
|
||||
if (($this->status & static::STATUS_COMMENT == 0) && ($this->query[$i] === '\\')) {
|
||||
$this->current .= $this->query[$i] . $this->query[++$i];
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -305,8 +305,8 @@ class Formatter
|
||||
&& (!$formattedOptions)
|
||||
&& (empty(self::$INLINE_CLAUSES[$lastClause]))
|
||||
&& (($curr->type !== Token::TYPE_KEYWORD)
|
||||
|| (($curr->type === Token::TYPE_KEYWORD)
|
||||
&& ($curr->flags & Token::FLAG_KEYWORD_FUNCTION)))
|
||||
|| (($curr->type === Token::TYPE_KEYWORD)
|
||||
&& ($curr->flags & Token::FLAG_KEYWORD_FUNCTION)))
|
||||
) {
|
||||
$formattedOptions = true;
|
||||
$lineEnded = true;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user