diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Context.php | 2 | ||||
-rw-r--r-- | src/Fragments/DataTypeFragment.php | 39 | ||||
-rw-r--r-- | src/Fragments/OptionsFragment.php | 4 | ||||
-rw-r--r-- | src/Fragments/ParamDefFragment.php | 5 | ||||
-rw-r--r-- | src/Lexer.php | 75 | ||||
-rw-r--r-- | src/Parser.php | 13 | ||||
-rw-r--r-- | src/Statements/CreateStatement.php | 1 | ||||
-rw-r--r-- | src/Token.php | 16 | ||||
-rw-r--r-- | src/Utils/Routine.php | 46 |
9 files changed, 146 insertions, 55 deletions
diff --git a/src/Context.php b/src/Context.php index 8c2ac14..3d7f4ab 100644 --- a/src/Context.php +++ b/src/Context.php @@ -116,7 +116,7 @@ abstract class Context 'END' => 2, 'INDEX' => 2, 'VALUE' => 2, 'ENGINE' => 2, - 'COMMENT' => 2, 'RETURNS' => 2, 'STORAGE' => 2, + 'CHARSET' => 2, 'COMMENT' => 2, 'RETURNS' => 2, 'STORAGE' => 2, 'CHECKSUM' => 2, 'MAX_ROWS' => 2, 'MIN_ROWS' => 2, 'NOT NULL' => 2, 'PASSWORD' => 2, 'INDEX KEY' => 2, 'PACK_KEYS' => 2, diff --git a/src/Fragments/DataTypeFragment.php b/src/Fragments/DataTypeFragment.php index acf3933..a5c8eca 100644 --- a/src/Fragments/DataTypeFragment.php +++ b/src/Fragments/DataTypeFragment.php @@ -10,24 +10,40 @@ use SqlParser\Token; use SqlParser\TokensList; /** - * `RETURN` keyword parser. + * Parses a data type. */ class DataTypeFragment extends Fragment { + public static $OPTIONS = array( + 'BINARY' => 1, + 'CHARACTER SET' => array(2, 'var'), + 'CHARSET' => array(3, 'var'), + 'COLLATE' => 4, + 'UNSIGNED' => 5, + 'ZEROFILL' => 6, + ); + /** - * The data type returned. + * The name of the data type. * * @var string */ - public $type; + public $name; /** - * The size of this variable. + * The size of this data type. * * @var array */ - public $size; + public $size = array(); + + /** + * The options of this data type. + * + * @var OptionsFragment + */ + public $options = array(); /** * @param Parser $parser @@ -47,8 +63,7 @@ class DataTypeFragment extends Fragment * * 0 -------------------[ data type ]--------------------> 1 * - * 1 ------------------[ size (array) ]------------------> 4 - * 1 ----------------------[ else ]----------------------> -1 + * 1 ----------------[ size and options ]----------------> 2 * * @var int */ @@ -64,7 +79,7 @@ class DataTypeFragment extends Fragment } if ($state === 0) { - $ret->type = $token->value; + $ret->name = $token->value; $ret->tokens[] = $token; if (!isset(Context::$DATA_TYPES[$token->value])) { $parser->error('Unrecognized data type.', $token); @@ -75,18 +90,20 @@ class DataTypeFragment extends Fragment $size = ArrayFragment::parse($parser, $list); $ret->size = $size->array; $ret->tokens = array_merge($ret->tokens, $size->tokens); - } else { - --$list->idx; + ++$list->idx; } + $ret->options = OptionsFragment::parse($parser, $list, static::$OPTIONS); + ++$list->idx; break; } } - if (empty($ret->type)) { + if (empty($ret->name)) { return null; } + --$list->idx; return $ret; } } diff --git a/src/Fragments/OptionsFragment.php b/src/Fragments/OptionsFragment.php index ce52135..22661eb 100644 --- a/src/Fragments/OptionsFragment.php +++ b/src/Fragments/OptionsFragment.php @@ -19,7 +19,7 @@ class OptionsFragment extends Fragment * * @var array */ - public $options; + public $options = array(); /** * @param Parser $parser @@ -109,6 +109,8 @@ class OptionsFragment extends Fragment } + ksort($ret->options); + --$list->idx; return $ret; } diff --git a/src/Fragments/ParamDefFragment.php b/src/Fragments/ParamDefFragment.php index e6ecea9..05bad6e 100644 --- a/src/Fragments/ParamDefFragment.php +++ b/src/Fragments/ParamDefFragment.php @@ -23,7 +23,7 @@ class ParamDefFragment extends Fragment public $name; /** - * Parameter's type (IN, OUT or INOUT). + * Parameter's direction (IN, OUT or INOUT). * * @var string */ @@ -91,6 +91,9 @@ class ParamDefFragment extends Fragment if (($token->value === 'IN') || ($token->value === 'OUT') || ($token->value === 'INOUT')) { $expr->inOut = $token->value; ++$list->idx; + } else if ($token->value === ')') { + ++$list->idx; + break; } else { $expr->name = $token->value; $state = 2; diff --git a/src/Lexer.php b/src/Lexer.php index 416122c..238d63b 100644 --- a/src/Lexer.php +++ b/src/Lexer.php @@ -115,6 +115,7 @@ class Lexer $this->len = ($str instanceof UtfString) ? $str->length() : strlen($str); $this->strict = $strict; + $this->lex(); } /** @@ -134,6 +135,7 @@ class Lexer // Another example is `parseComment`. $tokens = new TokensList(); + $lastToken = NULL; for ($this->last = 0, $lastIdx = 0; $this->last < $this->len; $lastIdx = ++$this->last) { /** @var Token The new token. */ @@ -151,6 +153,17 @@ class Lexer if ($this->delimiter !== $this->str[$this->last]) { $this->error('Unexpected character.', $this->str[$this->last], $this->last); } + } else if (($token->type === Token::TYPE_SYMBOL) && ($token->flags & Token::FLAG_SYMBOL_VARIABLE) && + ($lastToken !== NULL)) { + // Handles ```... FROM 'user'@'%' ...```. + if ((($lastToken->type === Token::TYPE_SYMBOL) && ($lastToken->flags & Token::FLAG_SYMBOL_BACKTICK)) || + ($lastToken->type === Token::TYPE_STRING)) { + $lastToken->token .= $token->token; + $lastToken->type = Token::TYPE_SYMBOL; + $lastToken->flags = Token::FLAG_SYMBOL_USER; + $lastToken->value .= '@' . $token->value; + continue; + } } $token->position = $lastIdx; @@ -172,6 +185,8 @@ class Lexer $token->type = Token::TYPE_DELIMITER; $token->flags = 0; } + + $lastToken = $token; } // Adding a final delimite at the end to mark the ending. @@ -479,20 +494,31 @@ class Lexer /** * Parses a string. * + * @param string $quote Additional start symbol. + * * @return Token */ - public function parseString() + public function parseString($quote = '') { - $quote = $token = $this->str[$this->last]; - if (!($flags = Context::isString($token))) { + $token = $this->str[$this->last]; + if ((!($flags = Context::isString($token))) && ($token !== $quote)) { return null; } - while ((++$this->last < $this->len) && ($this->str[$this->last] !== $quote)) { - $token .= $this->str[$this->last]; - if (($this->str[$this->last] === '\\') && (++$this->last < $this->len)) { + $quote = $token; + + while (++$this->last < $this->len) { + if (($this->last + 1 < $this->len) && + ((($this->str[$this->last] === $quote) && ($this->str[$this->last + 1] === $quote)) || + (($this->str[$this->last] === '\\') && ($quote !== '`')))) { + $token .= $this->str[$this->last] . $this->str[++$this->last]; + } else { + if ($this->str[$this->last] === $quote) { + break; + } $token .= $this->str[$this->last]; } } + if (($this->last >= $this->len) || ($this->str[$this->last] !== $quote)) { $this->error('Ending quote ' . $quote . ' was expected.', '', $this->last); } else { @@ -508,36 +534,27 @@ class Lexer */ public function parseSymbol() { - // TODO: `@` can be used in queries like ```... FROM 'user'@'%' ...```. $token = $this->str[$this->last]; if (!($flags = Context::isSymbol($token))) { return null; } - if ($flags === Token::FLAG_SYMBOL_VARIABLE) { + + if ($flags & Token::FLAG_SYMBOL_VARIABLE) { ++$this->last; - if (($name = static::parseString($this->str, $this->last, $this->len))) { - $token .= $name->token; - } elseif (($name = static::parseSymbol($this->str, $this->last, $this->len))) { - $token .= $name->token; - } else { - $name = static::parseUnknown($this->str, $this->last, $this->len); - if ($name === null) { - $this->error('Variable name was expected.', $this->str[$this->last], $this->last); - return null; - } - $token .= $name->token; - } - } elseif ($flags === Token::FLAG_SYMBOL_BACKTICK) { - $token = $this->str[$this->last]; - while ((++$this->last < $this->len) && ($this->str[$this->last] !== '`')) { - $token .= $this->str[$this->last]; - } - if ($this->last >= $this->len) { - $this->error('Ending backtick ` was expected.', '', $this->last); - } else { - $token .= $this->str[$this->last]; + } else { + $token = ''; + } + + if (($str = $this->parseString('`')) === null) { + if (($str = static::parseUnknown()) === null) { + $this->error('Variable name was expected.', $this->str[$this->last], $this->last); } } + + if ($str !== null) { + $token .= $str->token; + } + return new Token($token, Token::TYPE_SYMBOL, $flags); } diff --git a/src/Parser.php b/src/Parser.php index 9fab5ac..c2f4ae2 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -115,14 +115,19 @@ class Parser /** * Constructor. * - * @param Parser $parser - * @param TokensList $list + * @param mixed $list * @param bool $strict */ - public function __construct(TokensList $list, $strict = false) + public function __construct($list, $strict = false) { - $this->list = $list; + if ((is_string($list)) || ($list instanceof UtfString)) { + $lexer = new Lexer($list, $strict); + $this->list = $lexer->tokens; + } elseif ($list instanceof TokensList) { + $this->list = $list; + } $this->strict = $strict; + $this->parse(); } /** diff --git a/src/Statements/CreateStatement.php b/src/Statements/CreateStatement.php index edf0dbd..25b4a8f 100644 --- a/src/Statements/CreateStatement.php +++ b/src/Statements/CreateStatement.php @@ -30,6 +30,7 @@ class CreateStatement extends Statement 'TEMPORARY' => 2, 'IF NOT EXISTS' => 3, + 'DEFINER' => array(4, 'var'), ); /** diff --git a/src/Token.php b/src/Token.php index e597713..61fb16b 100644 --- a/src/Token.php +++ b/src/Token.php @@ -136,6 +136,7 @@ class Token // Symbols related flags. const FLAG_SYMBOL_VARIABLE = 1; const FLAG_SYMBOL_BACKTICK = 2; + const FLAG_SYMBOL_USER = 4; /** * The token it its raw string representation. @@ -224,15 +225,14 @@ class Token case Token::TYPE_STRING: return mb_substr($this->token, 1, -1); // trims quotes case Token::TYPE_SYMBOL: - if ($this->flags & Token::FLAG_SYMBOL_VARIABLE) { - if ($this->token[1] === '`') { - return mb_substr($this->token, 2, -1); // trims @` and ` - } else { - return mb_substr($this->token, 1); // trims @ - } - } elseif ($this->flags & Token::FLAG_SYMBOL_BACKTICK) { - return mb_substr($this->token, 1, -1); // trims backticks + $str = $this->token; + if ((isset($str[0])) && ($str[0] === '@')) { + $str = mb_substr($str, 1); + } + if ((isset($str[0])) && (($str[0] === '`') || ($str[0] === '"') || ($str[0] === '\''))) { + $str = mb_substr($str, 1, -1); } + return $str; } return $this->token; } diff --git a/src/Utils/Routine.php b/src/Utils/Routine.php new file mode 100644 index 0000000..3015a3f --- /dev/null +++ b/src/Utils/Routine.php @@ -0,0 +1,46 @@ +<?php + +namespace SqlParser\Utils; + +class Routine +{ + + /** + * Gets the parameters of a routine from the parse tree. + * + * @param array $tree + * + * @return array + */ + public static function getParameters($tree) + { + $retval = array( + 'num' => 0, + 'dir' => array(), + 'name' => array(), + 'type' => array(), + 'length' => array(), + 'opts' => array(), + ); + + $idx = 0; + foreach ($tree->parameters as $param) { + $retval['dir'][$idx] = $param->inOut; + $retval['name'][$idx] = $param->name; + $retval['type'][$idx] = $param->type->name; + $retval['length'][$idx] = implode(',', $param->type->size); + $retval['opts'][$idx] = array(); + foreach ($param->type->options->options as $opt) { + $retval['opts'][$idx][] = is_string($opt) ? + $opt : $opt['value']; + } + $retval['opts'][$idx] = implode(' ', $retval['opts'][$idx]); + ++$idx; + } + + $retval['num'] = $idx; + + return $retval; + } + +} |