summaryrefslogtreecommitdiffstats
path: root/src/Fragments
diff options
context:
space:
mode:
authorDan Ungureanu <udan1107@gmail.com>2015-07-03 21:18:10 +0300
committerDan Ungureanu <udan1107@gmail.com>2015-07-04 23:41:52 +0300
commitb3eff80030f9bd6d90e65360eb89e18a1be298b2 (patch)
treedb420fef27f24856f4cfaeee008104fdfcb77942 /src/Fragments
parent4dabcc2ae266c022e44294bfbe3344b05e66e266 (diff)
downloadsql-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.php201
-rw-r--r--src/Fragments/DataTypeFragment.php38
-rw-r--r--src/Fragments/FieldDefFragment.php61
-rw-r--r--src/Fragments/FieldFragment.php20
-rw-r--r--src/Fragments/KeyFragment.php25
-rw-r--r--src/Fragments/OptionsFragment.php18
-rw-r--r--src/Fragments/ReferencesKeyword.php29
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)
+ );
+ }
}