diff options
author | Deven Bansod <devenbansod.bits@gmail.com> | 2016-09-25 22:51:51 +0530 |
---|---|---|
committer | Deven Bansod <devenbansod.bits@gmail.com> | 2016-09-25 22:51:51 +0530 |
commit | 7f4702adeab4a852ffc724971a601e28668fa6fe (patch) | |
tree | b606f8eb4af49f84f8aa9069f9aafad0aa90c82a /src | |
parent | 5988855152a5c8adcc874e9868e97adcbfcd7d69 (diff) | |
download | sql-parser-7f4702adeab4a852ffc724971a601e28668fa6fe.zip sql-parser-7f4702adeab4a852ffc724971a601e28668fa6fe.tar.gz sql-parser-7f4702adeab4a852ffc724971a601e28668fa6fe.tar.bz2 |
Add parsing of CASE Expressions
Fix phpmyadmin/phpmyadmin#12100
Signed-off-by: Deven Bansod <devenbansod.bits@gmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/Components/CaseExpression.php | 284 | ||||
-rw-r--r-- | src/Components/ExpressionArray.php | 14 |
2 files changed, 295 insertions, 3 deletions
diff --git a/src/Components/CaseExpression.php b/src/Components/CaseExpression.php new file mode 100644 index 0000000..6195cd8 --- /dev/null +++ b/src/Components/CaseExpression.php @@ -0,0 +1,284 @@ +<?php + +/** + * Parses a reference to a CASE expression + * + * @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 a CASE expression + * + * @category Components + * @package SqlParser + * @subpackage Components + * @author Deven Bansod <devenbansod.bits@gmail.com> + * @license http://opensource.org/licenses/GPL-2.0 GNU Public License + */ +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) + { + $ret = new CaseExpression(); + + /** + * Counts brackets. + * + * @var int $brackets + */ + $brackets = 0; + + /** + * Keeps track of the last two previous tokens. + * + * @var Token[] $prev + */ + $prev = array(null, null); + + /** + * 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]; + + // End of statement. + if ($token->type === Token::TYPE_DELIMITER) { + break; + } + + // Skipping whitespaces and comments. + if (($token->type === Token::TYPE_WHITESPACE) + || ($token->type === Token::TYPE_COMMENT) + ) { + continue; + } + + if ($state === 0) { + if ($token->type === Token::TYPE_KEYWORD + && $token->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 { + $parser->error(__('Unexpected token'), $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; + } else { + $parser->error(__('Unexpected token'), $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; + } else { + $parser->error(__('Unexpected token'), $token); + break; + } + } else { + $parser->error(__('Unexpected token'), $token); + break; + } + } + } + + if ($state !== 3) { + $parser->error( + __('Unexpected end of CASE expression'), + $list->list[$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; + } +} diff --git a/src/Components/ExpressionArray.php b/src/Components/ExpressionArray.php index f108ad8..a64f64a 100644 --- a/src/Components/ExpressionArray.php +++ b/src/Components/ExpressionArray.php @@ -1,7 +1,7 @@ <?php /** - * Parses a a list of expressions delimited by a comma. + * Parses a list of expressions delimited by a comma. * * @package SqlParser * @subpackage Components @@ -14,7 +14,7 @@ use SqlParser\Token; use SqlParser\TokensList; /** - * Parses a a list of expressions delimited by a comma. + * Parses a list of expressions delimited by a comma. * * @category Keywords * @package SqlParser @@ -73,13 +73,21 @@ class ExpressionArray extends Component && ((~$token->flags & Token::FLAG_KEYWORD_FUNCTION)) && ($token->value !== 'DUAL') && ($token->value !== 'NULL') + && ($token->value !== 'CASE') ) { // No keyword is expected. break; } if ($state === 0) { - $expr = Expression::parse($parser, $list, $options); + 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; } |