diff options
author | Dan Ungureanu <udan1107@gmail.com> | 2015-07-03 21:18:10 +0300 |
---|---|---|
committer | Dan Ungureanu <udan1107@gmail.com> | 2015-07-04 23:41:52 +0300 |
commit | b3eff80030f9bd6d90e65360eb89e18a1be298b2 (patch) | |
tree | db420fef27f24856f4cfaeee008104fdfcb77942 /src/Fragments | |
parent | 4dabcc2ae266c022e44294bfbe3344b05e66e266 (diff) | |
download | sql-parser-b3eff80030f9bd6d90e65360eb89e18a1be298b2.zip sql-parser-b3eff80030f9bd6d90e65360eb89e18a1be298b2.tar.gz sql-parser-b3eff80030f9bd6d90e65360eb89e18a1be298b2.tar.bz2 |
The context depends on the SQL mode.
Implemented a few more builders.
Improved some fragments and statement types.
Fixed the noAlias option in FieldFragment.
Reordered CREATE statements's options.
Updated contexts definitions.
Fixed typos. Improved tests.
Diffstat (limited to 'src/Fragments')
-rw-r--r-- | src/Fragments/AlterFragment.php | 201 | ||||
-rw-r--r-- | src/Fragments/DataTypeFragment.php | 38 | ||||
-rw-r--r-- | src/Fragments/FieldDefFragment.php | 61 | ||||
-rw-r--r-- | src/Fragments/FieldFragment.php | 20 | ||||
-rw-r--r-- | src/Fragments/KeyFragment.php | 25 | ||||
-rw-r--r-- | src/Fragments/OptionsFragment.php | 18 | ||||
-rw-r--r-- | src/Fragments/ReferencesKeyword.php | 29 |
7 files changed, 380 insertions, 12 deletions
diff --git a/src/Fragments/AlterFragment.php b/src/Fragments/AlterFragment.php new file mode 100644 index 0000000..54338f9 --- /dev/null +++ b/src/Fragments/AlterFragment.php @@ -0,0 +1,201 @@ +<?php + +/** + * Parses a reference to a field. + * + * @package SqlParser + * @subpackage Fragments + */ +namespace SqlParser\Fragments; + +use SqlParser\Fragment; +use SqlParser\Parser; +use SqlParser\Token; +use SqlParser\TokensList; + +/** + * Parses a reference to a field. + * + * @category Fragments + * @package SqlParser + * @subpackage Fragments + * @author Dan Ungureanu <udan1107@gmail.com> + * @license http://opensource.org/licenses/GPL-2.0 GNU Public License + */ +class AlterFragment extends Fragment +{ + + /** + * All alter operations. + * + * @var array + */ + public static $OPTIONS = array( + 'ADD' => 3, + 'ALTER' => 3, + 'ANALYZE' => 3, + 'CHANGE' => 3, + 'CHECK' => 3, + 'COALESCE' => 3, + 'CONVERT' => 3, + 'DISABLE' => 3, + 'DISCARD' => 3, + 'DROP' => 3, + 'ENABLE' => 3, + 'IMPORT' => 3, + 'MODIFY' => 3, + 'OPTIMIZE' => 3, + 'ORDER' => 3, + 'PARTITION' => 3, + 'REBUILD' => 3, + 'REMOVE' => 3, + 'RENAME' => 3, + 'REORGANIZE' => 3, + 'REPAIR' => 3, + + 'COLUMN' => 4, + 'CONSTRAINT' => 4, + 'DEFAULT' => 4, + 'TO' => 4, + 'BY' => 4, + 'FOREIGN' => 4, + 'FULLTEXT' => 4, + 'KEY' => 4, + 'KEYS' => 4, + 'PARTITIONING' => 4, + 'PRIMARY KEY' => 4, + 'SPATIAL' => 4, + 'TABLESPACE' => 4, + 'INDEX' => 4, + + 'DEFAULT CHARACTER SET' => array(5, 'var'), + + 'COLLATE' => array(6, 'var'), + ); + + /** + * Options of this operation. + * + * @var OptionsFragment + */ + public $options; + + /** + * The altered field. + * + * @var FieldFragment + */ + public $field; + + /** + * Unparsed tokens. + * + * @var Token[] + */ + 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 AlterFragment + */ + public static function parse(Parser $parser, TokensList $list, array $options = array()) + { + $ret = new AlterFragment(); + + /** + * Counts brackets. + * @var int + */ + $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 = 0; + + for (; $list->idx < $list->count; ++$list->idx) { + /** + * Token parsed at this moment. + * @var 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 ($state !== 2) { + // State 2 parses the unknown part which must include whitespaces as well. + continue; + } + } + + if ($state === 0) { + $ret->options = OptionsFragment::parse($parser, $list, static::$OPTIONS); + $state = 1; + } elseif ($state === 1) { + $ret->field = FieldFragment::parse( + $parser, + $list, + array( + 'noAlias' => true, + 'noBrackets' => true, + ) + ); + if ($ret->field === null) { + // No field was read. We go back one token so the next + // iteration will parse the same on, 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 === ',') { + break; + } + } + $ret->unknown[] = $token; + } + } + + --$list->idx; + return $ret; + } + + /** + * @param AlterFragment $fragment The fragment to be built. + * + * @return string + */ + public static function build($fragment) + { + $ret = OptionsFragment::build($fragment->options) . ' '; + if (!empty($fragment->field)) { + $ret .= FieldFragment::build($fragment->field) . ' '; + } + foreach ($fragment->unknown as $token) { + $ret .= $token->token; + } + return $ret; + } +} diff --git a/src/Fragments/DataTypeFragment.php b/src/Fragments/DataTypeFragment.php index 0b9e1a8..6a44b78 100644 --- a/src/Fragments/DataTypeFragment.php +++ b/src/Fragments/DataTypeFragment.php @@ -35,7 +35,7 @@ class DataTypeFragment extends Fragment 'BINARY' => 1, 'CHARACTER SET' => array(2, 'var'), 'CHARSET' => array(2, 'var'), - 'COLLATE' => 3, + 'COLLATE' => array(3, 'var'), 'UNSIGNED' => 4, 'ZEROFILL' => 5, ); @@ -70,6 +70,21 @@ class DataTypeFragment extends Fragment public $options; /** + * Constructor. + * + * @param string $name The name of this data type. + * @param array $parameters The parameters (size or possible values). + * @param OptionsFragment $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. @@ -113,10 +128,10 @@ class DataTypeFragment extends Fragment $state = 1; } elseif ($state === 1) { if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '(')) { - $size = ArrayFragment::parse($parser, $list); + $parameters = ArrayFragment::parse($parser, $list); ++$list->idx; $ret->parameters = (($ret->name === 'ENUM') || ($ret->name === 'SET')) ? - $size->raw : $size->values; + $parameters->raw : $parameters->values; } $ret->options = OptionsFragment::parse($parser, $list, static::$DATA_TYPE_OPTIONS); ++$list->idx; @@ -132,4 +147,21 @@ class DataTypeFragment extends Fragment --$list->idx; return $ret; } + + /** + * @param DataTypeFragment $fragment The fragment to be built. + * + * @return string + */ + public static function build($fragment) + { + $tmp = ''; + if (!empty($fragment->parameters)) { + $tmp = '('. implode(', ', $fragment->parameters) . ')'; + } + return trim( + $fragment->name . ' ' . $tmp . ' ' + . OptionsFragment::build($fragment->options) + ); + } } diff --git a/src/Fragments/FieldDefFragment.php b/src/Fragments/FieldDefFragment.php index 6c5771e..9f43fcc 100644 --- a/src/Fragments/FieldDefFragment.php +++ b/src/Fragments/FieldDefFragment.php @@ -92,6 +92,29 @@ class FieldDefFragment extends Fragment public $options; /** + * Constructor. + * + * @param string $name The name of the field. + * @param OptionsFragment $options The options of this field. + * @param DataTypeFragment|KeyFragment $type The data type of this field or the key. + * @param bool $isConstraint Whether this field is a constraint or not. + * @param ReferencesKeyword $references References. + */ + public function __construct($name = null, $options = null, $type = null, + $isConstraint = false, $references = null + ) { + $this->name = $name; + $this->options = $options; + if ($type instanceof DataTypeFragment) { + $this->type = $type; + } elseif ($type instanceof KeyFragment) { + $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. @@ -200,4 +223,42 @@ class FieldDefFragment extends Fragment return $ret; } + + /** + * @param FieldDefFragment[] $fragment The fragment to be built. + * + * @return string + */ + public static function build($fragment) + { + $ret = array(); + foreach ($fragment as $f) { + $tmp = ''; + + if ($f->isConstraint) { + $tmp .= 'CONSTRAINT '; + } + + if (!empty($f->name)) { + $tmp .= Context::escape($f->name) . ' '; + } + + if (!empty($f->type)) { + $tmp .= DataTypeFragment::build($f->type) . ' '; + } + + if (!empty($f->key)) { + $tmp .= KeyFragment::build($f->key) . ' '; + } + + if (!empty($f->references)) { + $tmp .= 'REFERENCES ' . ReferencesKeyword::build($f->references) . ' '; + } + + $tmp .= OptionsFragment::build($f->options); + + $ret[] = trim($tmp); + } + return '(' . implode(', ', $ret) . ')'; + } } diff --git a/src/Fragments/FieldFragment.php b/src/Fragments/FieldFragment.php index edd0a2b..c33a7d7 100644 --- a/src/Fragments/FieldFragment.php +++ b/src/Fragments/FieldFragment.php @@ -8,6 +8,7 @@ */ namespace SqlParser\Fragments; +use SqlParser\Context; use SqlParser\Fragment; use SqlParser\Parser; use SqlParser\Token; @@ -168,7 +169,7 @@ class FieldFragment extends Fragment if (($isExpr) && (!$alias)) { $ret->expr .= $token->token; } - if (($alias === 0) && (!$isExpr) && (!$period) && (!empty($ret->expr))) { + if (($alias === 0) && (empty($options['noAlias'])) && (!$isExpr) && (!$period) && (!empty($ret->expr))) { $alias = 1; } continue; @@ -194,14 +195,14 @@ class FieldFragment extends Fragment } if ($token->type === Token::TYPE_OPERATOR) { - if ((!empty($options['noBrackets'])) && - (($token->value === '(') || ($token->value === ')')) + if ((!empty($options['noBrackets'])) + && (($token->value === '(') || ($token->value === ')')) ) { break; } if ($token->value === '(') { ++$brackets; - // We don't check to see if `$prev` is `true` (open bracke + // We don't check to see if `$prev` is `true` (open bracket // was found before) because the brackets count is one (the // only bracket we found is this one). if (($brackets === 1) && (empty($ret->function)) && ($prev !== null) && ($prev !== true)) { @@ -246,13 +247,20 @@ class FieldFragment extends Fragment } $ret->database = $ret->table; $ret->table = $ret->column; + $ret->column = null; $period = true; } else { // We found the name of a column (or table if column // field should be skipped; used to parse table names). if (!empty($options['skipColumn'])) { + if (!empty($ret->table)) { + break; + } $ret->table = $token->value; } else { + if (!empty($ret->column)) { + break; + } $ret->column = $token->value; } $period = false; @@ -317,11 +325,11 @@ class FieldFragment extends Fragment if (!empty($fragment->column)) { $fields[] = $fragment->column; } - $ret = implode('.', $fields); + $ret = implode('.', Context::escape($fields)); } if (!empty($fragment->alias)) { - $ret .= ' AS ' . $fragment->alias; + $ret .= ' AS ' . Context::escape($fragment->alias); } return $ret; diff --git a/src/Fragments/KeyFragment.php b/src/Fragments/KeyFragment.php index d84c9f6..1964cd2 100644 --- a/src/Fragments/KeyFragment.php +++ b/src/Fragments/KeyFragment.php @@ -67,6 +67,16 @@ class KeyFragment extends Fragment */ public $options; + + 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. @@ -131,6 +141,21 @@ class KeyFragment extends Fragment --$list->idx; return $ret; + } + /** + * @param KeyFragment $fragment The fragment to be built. + * + * @return string + */ + public static function build($fragment) + { + $ret = $fragment->type . ' '; + if (!empty($fragment->name)) { + $ret .= Context::escape($fragment->name) . ' '; + } + $ret .= '(' . implode(', ', Context::escape($fragment->columns)) . ')'; + $ret .= OptionsFragment::build($fragment->options); + return trim($ret); } } diff --git a/src/Fragments/OptionsFragment.php b/src/Fragments/OptionsFragment.php index 00ac282..fa06b0d 100644 --- a/src/Fragments/OptionsFragment.php +++ b/src/Fragments/OptionsFragment.php @@ -110,7 +110,12 @@ class OptionsFragment extends Fragment if (is_array($lastOption)) { if (empty($ret->options[$lastOptionId])) { - $ret->options[$lastOptionId] = array('name' => $token->value, 'value' => ''); + $ret->options[$lastOptionId] = array( + 'name' => $token->value, + 'equal' => $lastOption[1] === 'var=', + 'value' => '', + 'value_' => '', + ); } else { if ($token->value !== '=') { if ($token->value === '(') { @@ -118,7 +123,9 @@ class OptionsFragment extends Fragment } elseif ($token->value === ')') { --$brackets; } else { - $ret->options[$lastOptionId]['value'] .= $token->value; + // Raw and processed value. + $ret->options[$lastOptionId]['value'] .= $token->token; + $ret->options[$lastOptionId]['value_'] .= $token->value; } if ($brackets === 0) { $lastOption = null; @@ -144,10 +151,15 @@ class OptionsFragment extends Fragment */ public static function build($fragment) { + if ((empty($fragment)) || (!is_array($fragment->options))) { + return ''; + } $options = array(); foreach ($fragment->options as $option) { if (is_array($option)) { - $options[] = $option['name'] . '=' . $option['value']; + $options[] = $option['name'] + . (!empty($option['equal']) ? '=' : ' ') + . $option['value']; } else { $options[] = $option; } diff --git a/src/Fragments/ReferencesKeyword.php b/src/Fragments/ReferencesKeyword.php index 8de0233..ea10d20 100644 --- a/src/Fragments/ReferencesKeyword.php +++ b/src/Fragments/ReferencesKeyword.php @@ -8,6 +8,7 @@ */ namespace SqlParser\Fragments; +use SqlParser\Context; use SqlParser\Fragment; use SqlParser\Parser; use SqlParser\Token; @@ -58,6 +59,20 @@ class ReferencesKeyword extends Fragment public $options; /** + * Constructor. + * + * @param string $table The name of the table referenced. + * @param array $columns The columns referenced. + * @param OptionsFragment $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. @@ -117,4 +132,18 @@ class ReferencesKeyword extends Fragment --$list->idx; return $ret; } + + /** + * @param ReferencesKeyword $fragment The fragment to be built. + * + * @return string + */ + public static function build($fragment) + { + return trim( + Context::escape($fragment->table) + . ' (' . implode(', ', Context::escape($fragment->columns)) . ') ' + . OptionsFragment::build($fragment->options) + ); + } } |