diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Context.php | 9 | ||||
-rw-r--r-- | src/Fragments/FieldFragment.php | 54 | ||||
-rw-r--r-- | src/Fragments/JoinKeyword.php | 89 | ||||
-rw-r--r-- | src/Fragments/SelectKeyword.php | 25 | ||||
-rw-r--r-- | src/Parser.php | 1 | ||||
-rw-r--r-- | src/Statements/CreateStatement.php | 7 | ||||
-rw-r--r-- | src/Statements/SelectStatement.php | 7 | ||||
-rw-r--r-- | src/Utils/Misc.php | 91 | ||||
-rw-r--r-- | src/Utils/Routine.php | 2 |
9 files changed, 251 insertions, 34 deletions
diff --git a/src/Context.php b/src/Context.php index 3d7f4ab..2b43e56 100644 --- a/src/Context.php +++ b/src/Context.php @@ -309,14 +309,19 @@ abstract class Context * Checks if the given string is a keyword. * * @param string $str + * @param bool $strict * * @return bool */ - public static function isKeyword($str) + public static function isKeyword($str, $strict = false) { $str = strtoupper($str); - return isset(static::$KEYWORDS[$str]); + if (isset(static::$KEYWORDS[$str])) { + return $strict ? static::$KEYWORDS[$str] === 1 : true; + } + + return false; } // ------------------------------------------------------------------------- diff --git a/src/Fragments/FieldFragment.php b/src/Fragments/FieldFragment.php index 897a4cc..71e1066 100644 --- a/src/Fragments/FieldFragment.php +++ b/src/Fragments/FieldFragment.php @@ -63,6 +63,12 @@ class FieldFragment extends Fragment /** @var bool Whether current tokens make an expression or a table reference. */ $isExpr = false; + /** @var bool Whether a period was previously found. */ + $period = false; + + /** @var int Whether an alias is expected. Is 2 if `AS` keyword was found. */ + $alias = 0; + /** @var int Counts brackets. */ $brackets = 0; @@ -77,16 +83,23 @@ class FieldFragment extends Fragment // Skipping whitespaces and comments. if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { - if ($isExpr) { + if (($isExpr) && (!$alias)) { $ret->expr .= $token->token; $ret->tokens[] = $token; } + if (($alias === 0) && (!$isExpr) && (!$period) && (!empty($ret->expr))) { + $alias = 1; + } continue; } if ($token->type === Token::TYPE_KEYWORD) { // Keywords may be found only between brackets. if ($brackets === 0) { + if ($token->value === 'AS') { + $alias = 2; + continue; + } break; } } @@ -109,26 +122,45 @@ class FieldFragment extends Fragment } 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. $isExpr = true; } - if (!$isExpr) { - if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '.')) { - $ret->database = $ret->table; - $ret->table = $ret->column; - } else { - if (!empty($options['skipColumn'])) { - $ret->table = $token->value; + if ($alias) { + $ret->alias = $token->value; + $alias = 0; + } else { + if (!$isExpr) { + if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '.')) { + $ret->database = $ret->table; + $ret->table = $ret->column; + $period = true; } else { - $ret->column = $token->value; + if (!empty($options['skipColumn'])) { + $ret->table = $token->value; + } else { + $ret->column = $token->value; + } + $period = false; + } + } else { + if ($brackets === 0) { + if (($token->type === Token::TYPE_NONE) || ($token->type === Token::TYPE_STRING) || + (($token->type === Token::TYPE_SYMBOL) && ($token->flags & Token::FLAG_SYMBOL_BACKTICK))) { + $ret->alias = $token->value; + } } } + + $ret->expr .= $token->token; + $ret->tokens[] = $token; } + } - $ret->expr .= $token->token; - $ret->tokens[] = $token; + if ($alias === 2) { + $parser->error('Alias was expected.', $token); } if (empty($ret->tokens)) { diff --git a/src/Fragments/JoinKeyword.php b/src/Fragments/JoinKeyword.php new file mode 100644 index 0000000..62f9ecd --- /dev/null +++ b/src/Fragments/JoinKeyword.php @@ -0,0 +1,89 @@ +<?php + +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Lexer; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * `JOIN` keyword parser. + */ +class JoinKeyword extends Fragment +{ + + /** + * Join expression. + * + * @var FieldFragment + */ + public $expr; + + /** + * Join conditions. + * + * @var WhereKeyword + */ + public $on; + + /** + * @param Parser $parser + * @param TokensList $list + * @param array $options + * + * @return JoinKeyword + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = new JoinKeyword(); + + /** + * The state of the parser. + * + * Below are the states of the parser. + * + * 0 -----------------------[ expr ]----------------------> 1 + * + * 1 ------------------------[ ON ]-----------------------> 2 + * + * 2 --------------------[ conditions ]-------------------> -1 + * + * @var int + */ + $state = 0; + + for (; $list->idx < $list->count; ++$list->idx) { + /** @var Token Token parsed at this moment. */ + $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->expr = FieldFragment::parse($parser, $list, array('skipColumn' => true)); + $state = 1; + } elseif ($state === 1) { + if (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'ON')) { + $state = 2; + } + } elseif ($state === 2) { + $ret->on = WhereKeyword::parse($parser, $list); + ++$list->idx; + break; + } + + } + + --$list->idx; + return $ret; + } +} diff --git a/src/Fragments/SelectKeyword.php b/src/Fragments/SelectKeyword.php index f86d05d..dc8db95 100644 --- a/src/Fragments/SelectKeyword.php +++ b/src/Fragments/SelectKeyword.php @@ -27,9 +27,6 @@ class SelectKeyword extends Fragment $expr = null; - /** @var bool Whether an alias is expected. */ - $alias = false; - for (; $list->idx < $list->count; ++$list->idx) { /** @var Token Token parsed at this moment. */ $token = $list->tokens[$list->idx]; @@ -45,25 +42,16 @@ class SelectKeyword extends Fragment } if ($token->type === Token::TYPE_KEYWORD) { - // No keyword is expected other than `AS`. - if ($token->value !== 'AS') { - break; - } + // No keyword is expected. + break; } if (($token->type === Token::TYPE_OPERATOR) && ($token->value === ',')) { $ret[] = $expr; - } elseif (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'AS')) { - $alias = true; } else { - if ($alias) { - $expr->alias = $token->value; - $alias = false; - } else { - $expr = FieldFragment::parse($parser, $list); - if ($expr === null) { - break; - } + $expr = FieldFragment::parse($parser, $list); + if ($expr === null) { + break; } } @@ -71,9 +59,6 @@ class SelectKeyword extends Fragment // Last iteration was not processed. if (!empty($expr->tokens)) { - if ($alias) { - $parser->error('Alias was expected.', $token); - } $ret[] = $expr; } diff --git a/src/Parser.php b/src/Parser.php index c2f4ae2..95ecf6c 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -66,6 +66,7 @@ class Parser 'GROUP' => 'SqlParser\\Fragments\\GroupFragment', 'HAVING' => 'SqlParser\\Fragments\\WhereKeyword', 'INTO' => 'SqlParser\\Fragments\\IntoKeyword', + 'JOIN' => 'SqlParser\\Fragments\\JoinKeyword', 'LIMIT' => 'SqlParser\\Fragments\\LimitKeyword', 'ORDER' => 'SqlParser\\Fragments\\OrderKeyword', 'PARTITION' => 'SqlParser\\Fragments\\ArrayFragment', diff --git a/src/Statements/CreateStatement.php b/src/Statements/CreateStatement.php index 25b4a8f..00561f5 100644 --- a/src/Statements/CreateStatement.php +++ b/src/Statements/CreateStatement.php @@ -50,6 +50,13 @@ class CreateStatement extends Statement public $options; /** + * The parameters of this routine. + * + * @var ParamDefFragment[] + */ + public $parameters; + + /** * The options of the table. * * @var OptionsFragment diff --git a/src/Statements/SelectStatement.php b/src/Statements/SelectStatement.php index b8f3e10..d17450e 100644 --- a/src/Statements/SelectStatement.php +++ b/src/Statements/SelectStatement.php @@ -120,4 +120,11 @@ class SelectStatement extends Statement * @var LimitKeyword */ public $limit; + + /** + * Joins. + * + * @var JoinKeyword + */ + public $join; } diff --git a/src/Utils/Misc.php b/src/Utils/Misc.php new file mode 100644 index 0000000..aca16fe --- /dev/null +++ b/src/Utils/Misc.php @@ -0,0 +1,91 @@ +<?php + +namespace SqlParser\Utils; + +use SqlParser\Statement; + +class Misc +{ + + /** + * Gets a list of all aliases and their original names. + * + * @param Statement $tree + * @param string $db + * + * @return array + */ + public static function getAliases($tree, $db) + { + $retval = array(); + + $tables = array(); + + if ((!empty($tree->join->expr->table)) && (!empty($tree->join->expr->alias))) { + $thisDb = empty($tree->join->expr->database) ? + $db : $tree->join->expr->database; + + $retval = array( + $thisDb => array( + 'alias' => null, + 'tables' => array( + $tree->join->expr->table => array( + 'alias' => $tree->join->expr->alias, + 'columns' => array(), + ), + ), + ), + ); + + $tables[$thisDb][$tree->join->expr->alias] = $tree->join->expr->table; + } + + foreach ($tree->from as $expr) { + if (empty($expr->table)) { + continue; + } + + $thisDb = empty($expr->database) ? $db : $expr->database; + + if (!isset($retval[$thisDb])) { + $retval[$thisDb] = array( + 'alias' => null, + 'tables' => array(), + ); + } + + if (!isset($retval[$thisDb]['tables'][$expr->table])) { + $retval[$thisDb]['tables'][$expr->table] = array( + 'alias' => empty($expr->alias) ? null : $expr->alias, + 'columns' => array(), + ); + } + + if (!isset($tables[$thisDb])) { + $tables[$thisDb] = array(); + } + $tables[$thisDb][$expr->alias] = $expr->table; + } + + foreach ($tree->expr as $expr) { + if ((empty($expr->column)) || (empty($expr->alias)) ){ + continue; + } + + $thisDb = empty($expr->database) ? $db : $expr->database; + + if (empty($expr->table)) { + foreach ($retval[$thisDb]['tables'] as &$table) { + $table['columns'][$expr->column] = $expr->alias; + } + } else { + $thisTable = isset($tables[$thisDb][$expr->table]) ? + $tables[$thisDb][$expr->table] : $expr->table; + $retval[$thisDb]['tables'][$thisTable]['columns'][$expr->column] = $expr->alias; + } + } + + return $retval; + } + +} diff --git a/src/Utils/Routine.php b/src/Utils/Routine.php index 3015a3f..1b694e2 100644 --- a/src/Utils/Routine.php +++ b/src/Utils/Routine.php @@ -8,7 +8,7 @@ class Routine /** * Gets the parameters of a routine from the parse tree. * - * @param array $tree + * @param Statement $tree * * @return array */ |