Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/phpmyadmin/phpmyadmin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libraries/DisplayResults.class.php11
-rw-r--r--libraries/Table.class.php348
-rw-r--r--libraries/sql-parser/src/Builder.php87
-rw-r--r--libraries/sql-parser/src/Context.php46
-rw-r--r--libraries/sql-parser/src/Contexts/ContextMySql50000.php32
-rw-r--r--libraries/sql-parser/src/Contexts/ContextMySql50100.php32
-rw-r--r--libraries/sql-parser/src/Contexts/ContextMySql50500.php32
-rw-r--r--libraries/sql-parser/src/Contexts/ContextMySql50600.php32
-rw-r--r--libraries/sql-parser/src/Contexts/ContextMySql50700.php32
-rw-r--r--libraries/sql-parser/src/Fragments/AlterFragment.php201
-rw-r--r--libraries/sql-parser/src/Fragments/CreateDefFragment.php125
-rw-r--r--libraries/sql-parser/src/Fragments/DataTypeFragment.php38
-rw-r--r--libraries/sql-parser/src/Fragments/FieldDefFragment.php61
-rw-r--r--libraries/sql-parser/src/Fragments/FieldFragment.php20
-rw-r--r--libraries/sql-parser/src/Fragments/KeyFragment.php25
-rw-r--r--libraries/sql-parser/src/Fragments/OptionsFragment.php18
-rw-r--r--libraries/sql-parser/src/Fragments/ReferencesKeyword.php29
-rw-r--r--libraries/sql-parser/src/Lexer.php18
-rw-r--r--libraries/sql-parser/src/Parser.php5
-rw-r--r--libraries/sql-parser/src/Statement.php67
-rw-r--r--libraries/sql-parser/src/Statements/AlterStatement.php143
-rw-r--r--libraries/sql-parser/src/Statements/CreateStatement.php92
-rw-r--r--libraries/sql-parser/src/Statements/DropStatement.php15
-rw-r--r--libraries/sql-parser/src/Statements/NotImplementedStatement.php37
-rw-r--r--libraries/sql-parser/src/Statements/SelectStatement.php2
-rw-r--r--libraries/sql-parser/src/TokensList.php39
-rw-r--r--libraries/sql-parser/src/Utils/Query.php2
-rw-r--r--libraries/sql-parser/src/Utils/Table.php10
-rw-r--r--libraries/sql.lib.php10
-rw-r--r--test/classes/PMA_DisplayResults_test.php4
30 files changed, 996 insertions, 617 deletions
diff --git a/libraries/DisplayResults.class.php b/libraries/DisplayResults.class.php
index 175dcf0f12..ac00642b8c 100644
--- a/libraries/DisplayResults.class.php
+++ b/libraries/DisplayResults.class.php
@@ -3344,12 +3344,17 @@ class PMA_DisplayResults
}
$query = 'SELECT ' . SqlParser\Utils\Query::getClause(
- $parser->statements[0], $parser->list, 'SELECT'
+ $analyzed_sql_results['statement'],
+ $analyzed_sql_results['parser']->list,
+ 'SELECT'
);
$from_clause = SqlParser\Utils\Query::getClause(
- $parser->statements[0], $parser->list, 'FROM'
+ $analyzed_sql_results['statement'],
+ $analyzed_sql_results['parser']->list,
+ 'FROM'
);
+
if (!empty($from_clause)) {
$query .= ' FROM ' . $from_clause;
}
@@ -4345,7 +4350,7 @@ class PMA_DisplayResults
}
- if (($displayParts['nav_bar'] == '1') && (empty($statemet->limit))) {
+ if (($displayParts['nav_bar'] == '1') && (empty($statement->limit))) {
$table_html .= $this->_getPlacedTableNavigations(
$pos_next, $pos_prev, self::PLACE_TOP_DIRECTION_DROPDOWN,
$is_innodb
diff --git a/libraries/Table.class.php b/libraries/Table.class.php
index f6eff4c8bb..ae76bca900 100644
--- a/libraries/Table.class.php
+++ b/libraries/Table.class.php
@@ -714,9 +714,10 @@ class PMA_Table
static public function moveCopy($source_db, $source_table, $target_db,
$target_table, $what, $move, $mode
) {
+
global $err_url;
- /* Try moving table directly */
+ // Try moving the tables directly, using native `RENAME` statement.
if ($move && $what == 'data') {
$tbl = new PMA_Table($source_table, $source_db);
$result = $tbl->rename($target_table, $target_db);
@@ -726,11 +727,11 @@ class PMA_Table
}
}
- // set export settings we need
+ // Setting required export settings.
$GLOBALS['sql_backquotes'] = 1;
$GLOBALS['asfile'] = 1;
- // Ensure the target is valid
+ // Ensuring the target database is valid.
if (! $GLOBALS['pma']->databases->exists($source_db, $target_db)) {
if (! $GLOBALS['pma']->databases->exists($source_db)) {
$GLOBALS['message'] = PMA_Message::rawError(
@@ -751,24 +752,40 @@ class PMA_Table
return false;
}
+ /**
+ * The full name of source table, quoted.
+ * @var string
+ */
$source = PMA_Util::backquote($source_db)
. '.' . PMA_Util::backquote($source_table);
+
+ // If the target database is not specified, the operation is taking
+ // place in the same database.
if (! isset($target_db) || ! /*overload*/mb_strlen($target_db)) {
$target_db = $source_db;
}
- // Doing a select_db could avoid some problems with replicated databases,
- // when moving table from replicated one to not replicated one
+ // Selecting the database could avoid some problems with replicated
+ // databases, when moving table from replicated one to not replicated one.
$GLOBALS['dbi']->selectDb($target_db);
+ /**
+ * The full name of target table, quoted.
+ * @var string
+ */
$target = PMA_Util::backquote($target_db)
. '.' . PMA_Util::backquote($target_table);
- // do not create the table if dataonly
+ // No table is created when this is a data-only operation.
if ($what != 'dataonly') {
+
include_once "libraries/plugin_interface.lib.php";
- // get Export SQL instance
- /* @var $export_sql_plugin ExportSql */
+
+ /**
+ * Instance used for exporting the current structure of the table.
+ *
+ * @var ExportSql
+ */
$export_sql_plugin = PMA_getPlugin(
"export",
"sql",
@@ -786,141 +803,131 @@ class PMA_Table
$GLOBALS['sql_auto_increment'] = $_POST['sql_auto_increment'];
}
+ /**
+ * The old structure of the table..
+ * @var string
+ */
$sql_structure = $export_sql_plugin->getTableDef(
$source_db, $source_table, "\n", $err_url, false, false
);
+
unset($no_constraints_comments);
- $parsed_sql = PMA_SQP_parse($sql_structure);
- $analyzed_sql = PMA_SQP_analyze($parsed_sql);
- $i = 0;
- if (empty($analyzed_sql[0]['create_table_fields'])) {
- // this is not a CREATE TABLE, so find the first VIEW
- $target_for_view = PMA_Util::backquote($target_db);
- while (true) {
- if ($parsed_sql[$i]['type'] == 'alpha_reservedWord'
- && $parsed_sql[$i]['data'] == 'VIEW'
- ) {
- break;
- }
- $i++;
- }
- }
- unset($analyzed_sql);
- if (PMA_DRIZZLE) {
- $table_delimiter = 'quote_backtick';
- } else {
- $server_sql_mode = $GLOBALS['dbi']->fetchValue(
- "SHOW VARIABLES LIKE 'sql_mode'",
- 0,
- 1
- );
- // ANSI_QUOTES might be a subset of sql_mode, for example
- // REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ANSI
- if (false !== /*overload*/mb_strpos($server_sql_mode, 'ANSI_QUOTES')
- ) {
- $table_delimiter = 'quote_double';
- } else {
- $table_delimiter = 'quote_backtick';
- }
- unset($server_sql_mode);
- }
- /* Find table name in query and replace it */
- while ($parsed_sql[$i]['type'] != $table_delimiter) {
- $i++;
- }
+ // -----------------------------------------------------------------
+ // Phase 0: Preparing structures used.
- /* no need to backquote() */
- if (isset($target_for_view)) {
- // this a view definition; we just found the first db name
- // that follows DEFINER VIEW
- // so change it for the new db name
- $parsed_sql[$i]['data'] = $target_for_view;
- // then we have to find all references to the source db
- // and change them to the target db, ensuring we stay into
- // the $parsed_sql limits
- $last = $parsed_sql['len'] - 1;
- $backquoted_source_db = PMA_Util::backquote($source_db);
- for (++$i; $i <= $last; $i++) {
- if ($parsed_sql[$i]['type'] == $table_delimiter
- && $parsed_sql[$i]['data'] == $backquoted_source_db
- && $parsed_sql[$i - 1]['type'] != 'punct_qualifier'
- ) {
- $parsed_sql[$i]['data'] = $target_for_view;
- }
- }
- unset($last,$backquoted_source_db);
- } else {
- $parsed_sql[$i]['data'] = $target;
+ /**
+ * The destination where the table is moved or copied to.
+ * @var SqlParser\Fragments\FieldFragment
+ */
+ $destination = new SqlParser\Fragments\FieldFragment(
+ $target_db, $target_table, ''
+ );
+
+ // Find server's SQL mode so the builder can generate correct
+ // queries.
+ // One of the options that alters the behaviour is `ANSI_QUOTES`.
+ // This is not availabile for Drizzle.
+ if (!PMA_DRIZZLE) {
+ SqlParser\Context::setMode(
+ $GLOBALS['dbi']->fetchValue(
+ "SHOW VARIABLES LIKE 'sql_mode'", 0, 1
+ )
+ );
}
- /* Generate query back */
- $sql_structure = PMA_SQP_format($parsed_sql, 'query_only');
- // If table exists, and 'add drop table' is selected: Drop it!
+ // -----------------------------------------------------------------
+ // Phase 1: Dropping existent element of the same name (if exists
+ // and required).
+
if (isset($_REQUEST['drop_if_exists'])
&& $_REQUEST['drop_if_exists'] == 'true'
) {
- if (PMA_Table::isView($target_db, $target_table)) {
- $drop_query = 'DROP VIEW';
- } else {
- $drop_query = 'DROP TABLE';
- }
- $drop_query .= ' IF EXISTS '
- . PMA_Util::backquote($target_db) . '.'
- . PMA_Util::backquote($target_table);
- $GLOBALS['dbi']->query($drop_query);
- $GLOBALS['sql_query'] .= "\n" . $drop_query . ';';
+ /**
+ * Drop statement used for building the query.
+ * @var SqlParser\Statements\DropStatement
+ */
+ $statement = new SqlParser\Statements\DropStatement();
+
+ $statement->options = new SqlParser\Fragments\OptionsFragment(
+ array(
+ PMA_Table::isView($target_db, $target_table) ?
+ 'VIEW' : 'TABLE',
+ 'IF EXISTS',
+ )
+ );
+
+ $statement->fields = array($destination);
+
+ // Building the query.
+ $drop_query = $statement->build() . ';';
+
+ // Executing it.
+ $GLOBALS['dbi']->query($drop_query);
+ $GLOBALS['sql_query'] .= "\n" . $drop_query;
- // If an existing table gets deleted, maintain any
- // entries for the PMA_* tables
+ // If an existing table gets deleted, maintain any entries for
+ // the PMA_* tables.
$maintain_relations = true;
}
- @$GLOBALS['dbi']->query($sql_structure);
- $GLOBALS['sql_query'] .= "\n" . $sql_structure . ';';
+ // -----------------------------------------------------------------
+ // Phase 2: Generating the new query of this structure.
+
+ /**
+ * The parser responsible for parsing the old queries.
+ * @var SqlParser\Parser
+ */
+ $parser = new SqlParser\Parser($sql_structure);
+
+ /**
+ * The CREATE statement of this structure.
+ * @var SqlParser\Statements\CreateStatement
+ */
+ $statement = $parser->statements[0];
+
+ // Changing the destination.
+ $statement->name = $destination;
+
+ // Building back the query.
+ $sql_structure = $statement->build() . ';';
+
+ // Executing it.
+ $GLOBALS['dbi']->query($sql_structure);
+ $GLOBALS['sql_query'] .= "\n" . $sql_structure;
+
+ // -----------------------------------------------------------------
+ // Phase 3: Adding constraints.
+ // All constraint names are removed because they must be unique.
if (($move || isset($GLOBALS['add_constraints']))
&& !empty($GLOBALS['sql_constraints_query'])
) {
- $parsed_sql = PMA_SQP_parse($GLOBALS['sql_constraints_query']);
- $i = 0;
-
- // find the first $table_delimiter, it must be the source
- // table name
- while ($parsed_sql[$i]['type'] != $table_delimiter) {
- $i++;
- // maybe someday we should guard against going over limit
- //if ($i == $parsed_sql['len']) {
- // break;
- //}
- }
- // replace it by the target table name, no need
- // to backquote()
- $parsed_sql[$i]['data'] = $target;
+ $parser = new SqlParser\Parser($GLOBALS['sql_constraints_query']);
- // now we must remove all $table_delimiter that follow a
- // CONSTRAINT keyword, because a constraint name must be
- // unique in a db
+ /**
+ * The ALTER statement that generates the constraints.
+ * @var SqlParser\Statements\AlterStatement
+ */
+ $statement = $parser->statements[0];
- $cnt = $parsed_sql['len'] - 1;
+ // Changing the altered table to the destination.
+ $statement->table = $destination;
- for ($j = $i; $j < $cnt; $j++) {
- $dataUpper = /*overload*/mb_strtoupper($parsed_sql[$j]['data']);
- if ($parsed_sql[$j]['type'] == 'alpha_reservedWord'
- && $dataUpper == 'CONSTRAINT'
- ) {
- if ($parsed_sql[$j+1]['type'] == $table_delimiter) {
- $parsed_sql[$j+1]['data'] = '';
- }
+ // Removing the name of the constraints.
+ foreach ($statement->altered as $idx => $altered) {
+ // All constraint names are removed because they must be unique.
+ if ($altered->options->has('CONSTRAINT')) {
+ $altered->field = null;
}
}
- // Generate query back
- $GLOBALS['sql_constraints_query'] = PMA_SQP_format(
- $parsed_sql, 'query_only'
- );
+ // Building back the query.
+ $GLOBALS['sql_constraints_query'] = $statement->build() . ';';
+
+ // Executing it.
if ($mode == 'one_table') {
$GLOBALS['dbi']->query($GLOBALS['sql_constraints_query']);
}
@@ -930,95 +937,70 @@ class PMA_Table
}
}
- // add indexes to the table
- if (!empty($GLOBALS['sql_indexes'])) {
-
- $index_queries = array();
- $sql_indexes = $GLOBALS['sql_indexes'];
- $GLOBALS['sql_indexes'] = '';
-
- $parsed_sql = PMA_SQP_parse($sql_indexes);
- $cnt = $parsed_sql['len'] - 1;
- $k = 0;
-
- for ($j = 0; $j < $cnt; $j++) {
- if ($parsed_sql[$j]['type'] == 'punct_queryend') {
- $index_queries[] = substr(
- $sql_indexes, $k, $parsed_sql[$j]['pos'] - $k
- );
- $k = $parsed_sql[$j]['pos'];
- }
- }
+ // -----------------------------------------------------------------
+ // Phase 4: Adding indexes.
+ // View phase 3.
- foreach ($index_queries as $index_query) {
-
- $parsed_sql = PMA_SQP_parse($index_query);
- $i = 0;
+ if (!empty($GLOBALS['sql_indexes'])) {
- while ($parsed_sql[$i]['type'] != $table_delimiter) {
- $i++;
- }
+ $parser = new SqlParser\Parser($GLOBALS['sql_indexes']);
- $parsed_sql[$i]['data'] = $target;
+ /**
+ * The ALTER statement that generates the indexes.
+ * @var SqlParser\Statements\AlterStatement
+ */
+ $statement = $parser->statements[0];
- $cnt = $parsed_sql['len'] - 1;
+ // Changing the altered table to the destination.
+ $statement->table = $destination;
- for ($j = $i; $j < $cnt; $j++) {
- $dataUpper = /*overload*/mb_strtoupper($parsed_sql[$j]['data']);
- if ($parsed_sql[$j]['type'] == 'alpha_reservedWord'
- && $dataUpper == 'CONSTRAINT'
- ) {
- if ($parsed_sql[$j+1]['type'] == $table_delimiter) {
- $parsed_sql[$j+1]['data'] = '';
- }
- }
+ // Removing the name of the constraints.
+ foreach ($statement->altered as $idx => $altered) {
+ // All constraint names are removed because they must be unique.
+ if ($altered->options->has('CONSTRAINT')) {
+ $altered->field = null;
}
+ }
- $sql_index = PMA_SQP_format($parsed_sql, 'query_only');
- if ($mode == 'one_table' || $mode == 'db_copy') {
- $GLOBALS['dbi']->query($sql_index);
- }
+ // Building back the query.
+ $GLOBALS['sql_indexes'] = $statement->build() . ';';
- $GLOBALS['sql_indexes'] .= $sql_index;
+ // Executing it.
+ if ($mode == 'one_table' || $mode == 'db_copy') {
+ $GLOBALS['dbi']->query($GLOBALS['sql_indexes']);
}
-
$GLOBALS['sql_query'] .= "\n" . $GLOBALS['sql_indexes'];
if ($mode == 'one_table' || $mode == 'db_copy') {
unset($GLOBALS['sql_indexes']);
-
}
}
- /*
- * add AUTO_INCREMENT to the table
- *
- * @todo refactor with similar code above
- */
+ // -----------------------------------------------------------------
+ // Phase 5: Adding AUTO_INCREMENT.
+
if (! empty($GLOBALS['sql_auto_increments'])) {
if ($mode == 'one_table' || $mode == 'db_copy') {
- $parsed_sql = PMA_SQP_parse($GLOBALS['sql_auto_increments']);
- $i = 0;
- // find the first $table_delimiter, it must be the source
- // table name
- while ($parsed_sql[$i]['type'] != $table_delimiter) {
- $i++;
- }
+ $parser = new SqlParser\Parser($GLOBALS['sql_auto_increments']);
- // replace it by the target table name, no need
- // to backquote()
- $parsed_sql[$i]['data'] = $target;
+ /**
+ * The ALTER statement that alters the AUTO_INCREMENT value.
+ * @var SqlParser\Statements\AlterStatement
+ */
+ $statement = $parser->statements[0];
- // Generate query back
- $GLOBALS['sql_auto_increments'] = PMA_SQP_format(
- $parsed_sql, 'query_only'
- );
+ // Changing the altered table to the destination.
+ $statement->table = $destination;
+
+ // Building back the query.
+ $GLOBALS['sql_auto_increments'] = $statement->build() . ';';
+
+ // Executing it.
$GLOBALS['dbi']->query($GLOBALS['sql_auto_increments']);
$GLOBALS['sql_query'] .= "\n" . $GLOBALS['sql_auto_increments'];
unset($GLOBALS['sql_auto_increments']);
}
}
-
} else {
$GLOBALS['sql_query'] = '';
}
@@ -1036,7 +1018,7 @@ class PMA_Table
$sql_insert_data = 'INSERT INTO ' . $target
. ' SELECT * FROM ' . $source;
$GLOBALS['dbi']->query($sql_insert_data);
- $GLOBALS['sql_query'] .= "\n\n" . $sql_insert_data . ';';
+ $GLOBALS['sql_query'] .= "\n\n" . $sql_insert_data . ';';
}
$GLOBALS['cfgRelation'] = PMA_getRelationsParam();
@@ -1062,7 +1044,7 @@ class PMA_Table
$source_table, $target_table
);
- $GLOBALS['sql_query'] .= "\n\n" . $sql_drop_query . ';';
+ $GLOBALS['sql_query'] .= "\n\n" . $sql_drop_query . ';';
// end if ($move)
} else {
// we are copying
@@ -1240,6 +1222,8 @@ class PMA_Table
*/
}
}
+
+file_put_contents('/tmp/debug_table.txt', "-- GLOBALS[sql_query] (2) = \n" . $GLOBALS['sql_query'] . "\n", FILE_APPEND);
return true;
}
diff --git a/libraries/sql-parser/src/Builder.php b/libraries/sql-parser/src/Builder.php
deleted file mode 100644
index 5cb9d66fdb..0000000000
--- a/libraries/sql-parser/src/Builder.php
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-
-/**
- * This is one of the most important components, along with the lexer and the
- * parser.
- *
- * @package SqlParser
- */
-namespace SqlParser;
-
-use SqlParser\Exceptions\ParserException;
-
-/**
- * Builds the string representation of a Statement.
- *
- * @category Parser
- * @package SqlParser
- * @author Dan Ungureanu <udan1107@gmail.com>
- * @license http://opensource.org/licenses/GPL-2.0 GNU Public License
- */
-class Builder
-{
-
- /**
- * Statement to be build.
- *
- * @var Statement
- */
- public $statement;
-
- /**
- * Built query.
- *
- * @var string
- */
- public $query;
-
- /**
- * Constructor.
- *
- * @param Statement $statement
- */
- public function __construct($statement = null)
- {
- $this->statement = $statement;
-
- if ($this->statement != null) {
- $this->build();
- }
- }
-
- /**
- * Builds the statement.
- *
- * @return void
- */
- public function build()
- {
- $statement = $this->statement;
-
- foreach ($statement::$CLAUSES as $clause) {
- $name = $clause[0];
- $type = $clause[1];
-
- if (empty(Parser::$KEYWORD_PARSERS[$name])) {
- continue;
- }
-
- $class = Parser::$KEYWORD_PARSERS[$name]['class'];
- $field = Parser::$KEYWORD_PARSERS[$name]['field'];
-
- if (empty($statement->$field)) {
- continue;
- }
-
- if ($type & 2) {
- $this->query .= $name . ' ';
- }
-
- if ($type & 1) {
- $this->query .= $class::build($statement->$field) . ' ';
- }
- }
-
- return $this->qeury;
- }
-}
diff --git a/libraries/sql-parser/src/Context.php b/libraries/sql-parser/src/Context.php
index 699d800b87..d42211f639 100644
--- a/libraries/sql-parser/src/Context.php
+++ b/libraries/sql-parser/src/Context.php
@@ -110,6 +110,14 @@ abstract class Context
'(' => 16, ')' => 16, '.' => 16, ',' => 16,
);
+ /**
+ * The mode of the MySQL server that will be used in lexing, parsing and
+ * building the statements.
+ *
+ * @var int
+ */
+ public static $MODE = 0;
+
/*
* Server SQL Modes
* https://dev.mysql.com/doc/refman/5.0/en/sql-mode.html
@@ -413,6 +421,44 @@ abstract class Context
self::$loadedContext = $context;
self::$KEYWORDS = $context::$KEYWORDS;
}
+
+ /**
+ * Sets the SQL mode.
+ *
+ * @param string $mode The list of modes. If empty, the mode is reset.
+ */
+ public static function setMode($mode = '')
+ {
+ static::$MODE = 0;
+ if (empty($mode)) {
+ return;
+ }
+ $mode = explode(',', $mode);
+ foreach ($mode as $m) {
+ static::$MODE |= constant('static::' . $m);
+ }
+ }
+
+ /**
+ * Escapes the symbol by adding surrounding backticks.
+ *
+ * @param array|string $str The string to be escaped.
+ *
+ * @return string
+ */
+ public static function escape($str)
+ {
+ if (is_array($str)) {
+ foreach ($str as $key => $value) {
+ $str[$key] = static::escape($value);
+ }
+ return $str;
+ }
+ if (static::$MODE & Context::ANSI_QUOTES) {
+ return '"' . str_replace('"', '""', $str) . '"';
+ }
+ return '`' . str_replace('`', '``', $str) . '`';
+ }
}
// Initializng the default context.
diff --git a/libraries/sql-parser/src/Contexts/ContextMySql50000.php b/libraries/sql-parser/src/Contexts/ContextMySql50000.php
index 26afac1649..dc2759a30b 100644
--- a/libraries/sql-parser/src/Contexts/ContextMySql50000.php
+++ b/libraries/sql-parser/src/Contexts/ContextMySql50000.php
@@ -143,14 +143,14 @@ class ContextMySql50000 extends Context
'MINUTE_MICROSECOND' => 3, 'NO_WRITE_TO_BINLOG' => 3, 'SECOND_MICROSECOND' => 3,
'SQL_CALC_FOUND_ROWS' => 3,
- 'NOT NULL' => 5, 'SET NULL' => 5,
- 'NO ACTION' => 5, 'ON DELETE' => 5, 'ON UPDATE' => 5,
- 'CHARACTER SET' => 5, 'IF NOT EXISTS' => 5,
- 'DATA DIRECTORY' => 5,
- 'DEFAULT COLLATE' => 5, 'INDEX DIRECTORY' => 5,
- 'DEFAULT CHARACTER SET' => 5,
-
- 'GROUP BY' => 7, 'ORDER BY' => 7,
+ 'GROUP BY' => 7, 'NOT NULL' => 7, 'ORDER BY' => 7, 'SET NULL' => 7,
+ 'IF EXISTS' => 7, 'NO ACTION' => 7, 'ON DELETE' => 7, 'ON UPDATE' => 7,
+ 'OR REPLACE' => 7,
+ 'SQL SECURITY' => 7,
+ 'CHARACTER SET' => 7, 'IF NOT EXISTS' => 7,
+ 'DATA DIRECTORY' => 7,
+ 'DEFAULT COLLATE' => 7, 'INDEX DIRECTORY' => 7,
+ 'DEFAULT CHARACTER SET' => 7,
'XML' => 9,
'ENUM' => 9, 'TEXT' => 9,
@@ -168,18 +168,18 @@ class ContextMySql50000 extends Context
'CHARACTER' => 11, 'MEDIUMINT' => 11, 'VARBINARY' => 11,
'MEDIUMBLOB' => 11, 'MEDIUMTEXT' => 11,
- 'BINARY VARYING' => 13,
+ 'BINARY VARYING' => 15,
'KEY' => 19,
'INDEX' => 19,
'UNIQUE' => 19,
- 'INDEX KEY' => 21,
- 'UNIQUE KEY' => 21,
- 'FOREIGN KEY' => 21, 'PRIMARY KEY' => 21, 'SPATIAL KEY' => 21,
- 'FULLTEXT KEY' => 21, 'UNIQUE INDEX' => 21,
- 'SPATIAL INDEX' => 21,
- 'FULLTEXT INDEX' => 21,
+ 'INDEX KEY' => 23,
+ 'UNIQUE KEY' => 23,
+ 'FOREIGN KEY' => 23, 'PRIMARY KEY' => 23, 'SPATIAL KEY' => 23,
+ 'FULLTEXT KEY' => 23, 'UNIQUE INDEX' => 23,
+ 'SPATIAL INDEX' => 23,
+ 'FULLTEXT INDEX' => 23,
'X' => 33, 'Y' => 33,
'LN' => 33, 'PI' => 33,
@@ -258,7 +258,7 @@ class ContextMySql50000 extends Context
'LOCALTIMESTAMP' => 35,
'CURRENT_TIMESTAMP' => 35,
- 'NOT IN' => 37,
+ 'NOT IN' => 39,
'DATE' => 41, 'TIME' => 41, 'YEAR' => 41,
'TIMESTAMP' => 41,
diff --git a/libraries/sql-parser/src/Contexts/ContextMySql50100.php b/libraries/sql-parser/src/Contexts/ContextMySql50100.php
index 23c619b797..82db6843ed 100644
--- a/libraries/sql-parser/src/Contexts/ContextMySql50100.php
+++ b/libraries/sql-parser/src/Contexts/ContextMySql50100.php
@@ -154,14 +154,14 @@ class ContextMySql50100 extends Context
'SQL_CALC_FOUND_ROWS' => 3,
'MASTER_SSL_VERIFY_SERVER_CERT' => 3,
- 'NOT NULL' => 5, 'SET NULL' => 5,
- 'NO ACTION' => 5, 'ON DELETE' => 5, 'ON UPDATE' => 5,
- 'CHARACTER SET' => 5, 'IF NOT EXISTS' => 5,
- 'DATA DIRECTORY' => 5,
- 'DEFAULT COLLATE' => 5, 'INDEX DIRECTORY' => 5,
- 'DEFAULT CHARACTER SET' => 5,
-
- 'GROUP BY' => 7, 'ORDER BY' => 7,
+ 'GROUP BY' => 7, 'NOT NULL' => 7, 'ORDER BY' => 7, 'SET NULL' => 7,
+ 'IF EXISTS' => 7, 'NO ACTION' => 7, 'ON DELETE' => 7, 'ON UPDATE' => 7,
+ 'OR REPLACE' => 7,
+ 'SQL SECURITY' => 7,
+ 'CHARACTER SET' => 7, 'IF NOT EXISTS' => 7,
+ 'DATA DIRECTORY' => 7,
+ 'DEFAULT COLLATE' => 7, 'INDEX DIRECTORY' => 7,
+ 'DEFAULT CHARACTER SET' => 7,
'XML' => 9,
'ENUM' => 9, 'TEXT' => 9,
@@ -179,18 +179,18 @@ class ContextMySql50100 extends Context
'CHARACTER' => 11, 'MEDIUMINT' => 11, 'VARBINARY' => 11,
'MEDIUMBLOB' => 11, 'MEDIUMTEXT' => 11,
- 'BINARY VARYING' => 13,
+ 'BINARY VARYING' => 15,
'KEY' => 19,
'INDEX' => 19,
'UNIQUE' => 19,
- 'INDEX KEY' => 21,
- 'UNIQUE KEY' => 21,
- 'FOREIGN KEY' => 21, 'PRIMARY KEY' => 21, 'SPATIAL KEY' => 21,
- 'FULLTEXT KEY' => 21, 'UNIQUE INDEX' => 21,
- 'SPATIAL INDEX' => 21,
- 'FULLTEXT INDEX' => 21,
+ 'INDEX KEY' => 23,
+ 'UNIQUE KEY' => 23,
+ 'FOREIGN KEY' => 23, 'PRIMARY KEY' => 23, 'SPATIAL KEY' => 23,
+ 'FULLTEXT KEY' => 23, 'UNIQUE INDEX' => 23,
+ 'SPATIAL INDEX' => 23,
+ 'FULLTEXT INDEX' => 23,
'X' => 33, 'Y' => 33,
'LN' => 33, 'PI' => 33,
@@ -280,7 +280,7 @@ class ContextMySql50100 extends Context
'LOCALTIMESTAMP' => 35,
'CURRENT_TIMESTAMP' => 35,
- 'NOT IN' => 37,
+ 'NOT IN' => 39,
'DATE' => 41, 'TIME' => 41, 'YEAR' => 41,
'TIMESTAMP' => 41,
diff --git a/libraries/sql-parser/src/Contexts/ContextMySql50500.php b/libraries/sql-parser/src/Contexts/ContextMySql50500.php
index 03105abd1b..e338232eb0 100644
--- a/libraries/sql-parser/src/Contexts/ContextMySql50500.php
+++ b/libraries/sql-parser/src/Contexts/ContextMySql50500.php
@@ -159,14 +159,14 @@ class ContextMySql50500 extends Context
'SQL_CALC_FOUND_ROWS' => 3,
'MASTER_SSL_VERIFY_SERVER_CERT' => 3,
- 'NOT NULL' => 5, 'SET NULL' => 5,
- 'NO ACTION' => 5, 'ON DELETE' => 5, 'ON UPDATE' => 5,
- 'CHARACTER SET' => 5, 'IF NOT EXISTS' => 5,
- 'DATA DIRECTORY' => 5,
- 'DEFAULT COLLATE' => 5, 'INDEX DIRECTORY' => 5,
- 'DEFAULT CHARACTER SET' => 5,
-
- 'GROUP BY' => 7, 'ORDER BY' => 7,
+ 'GROUP BY' => 7, 'NOT NULL' => 7, 'ORDER BY' => 7, 'SET NULL' => 7,
+ 'IF EXISTS' => 7, 'NO ACTION' => 7, 'ON DELETE' => 7, 'ON UPDATE' => 7,
+ 'OR REPLACE' => 7,
+ 'SQL SECURITY' => 7,
+ 'CHARACTER SET' => 7, 'IF NOT EXISTS' => 7,
+ 'DATA DIRECTORY' => 7,
+ 'DEFAULT COLLATE' => 7, 'INDEX DIRECTORY' => 7,
+ 'DEFAULT CHARACTER SET' => 7,
'XML' => 9,
'ENUM' => 9, 'TEXT' => 9,
@@ -184,18 +184,18 @@ class ContextMySql50500 extends Context
'CHARACTER' => 11, 'MEDIUMINT' => 11, 'VARBINARY' => 11,
'MEDIUMBLOB' => 11, 'MEDIUMTEXT' => 11,
- 'BINARY VARYING' => 13,
+ 'BINARY VARYING' => 15,
'KEY' => 19,
'INDEX' => 19,
'UNIQUE' => 19,
- 'INDEX KEY' => 21,
- 'UNIQUE KEY' => 21,
- 'FOREIGN KEY' => 21, 'PRIMARY KEY' => 21, 'SPATIAL KEY' => 21,
- 'FULLTEXT KEY' => 21, 'UNIQUE INDEX' => 21,
- 'SPATIAL INDEX' => 21,
- 'FULLTEXT INDEX' => 21,
+ 'INDEX KEY' => 23,
+ 'UNIQUE KEY' => 23,
+ 'FOREIGN KEY' => 23, 'PRIMARY KEY' => 23, 'SPATIAL KEY' => 23,
+ 'FULLTEXT KEY' => 23, 'UNIQUE INDEX' => 23,
+ 'SPATIAL INDEX' => 23,
+ 'FULLTEXT INDEX' => 23,
'X' => 33, 'Y' => 33,
'LN' => 33, 'PI' => 33,
@@ -284,7 +284,7 @@ class ContextMySql50500 extends Context
'LOCALTIMESTAMP' => 35,
'CURRENT_TIMESTAMP' => 35,
- 'NOT IN' => 37,
+ 'NOT IN' => 39,
'DATE' => 41, 'TIME' => 41, 'YEAR' => 41,
'TIMESTAMP' => 41,
diff --git a/libraries/sql-parser/src/Contexts/ContextMySql50600.php b/libraries/sql-parser/src/Contexts/ContextMySql50600.php
index 91978cbd82..780ea5208b 100644
--- a/libraries/sql-parser/src/Contexts/ContextMySql50600.php
+++ b/libraries/sql-parser/src/Contexts/ContextMySql50600.php
@@ -165,14 +165,14 @@ class ContextMySql50600 extends Context
'SQL_CALC_FOUND_ROWS' => 3,
'MASTER_SSL_VERIFY_SERVER_CERT' => 3,
- 'NOT NULL' => 5, 'SET NULL' => 5,
- 'NO ACTION' => 5, 'ON DELETE' => 5, 'ON UPDATE' => 5,
- 'CHARACTER SET' => 5, 'IF NOT EXISTS' => 5,
- 'DATA DIRECTORY' => 5,
- 'DEFAULT COLLATE' => 5, 'INDEX DIRECTORY' => 5,
- 'DEFAULT CHARACTER SET' => 5,
-
- 'GROUP BY' => 7, 'ORDER BY' => 7,
+ 'GROUP BY' => 7, 'NOT NULL' => 7, 'ORDER BY' => 7, 'SET NULL' => 7,
+ 'IF EXISTS' => 7, 'NO ACTION' => 7, 'ON DELETE' => 7, 'ON UPDATE' => 7,
+ 'OR REPLACE' => 7,
+ 'SQL SECURITY' => 7,
+ 'CHARACTER SET' => 7, 'IF NOT EXISTS' => 7,
+ 'DATA DIRECTORY' => 7,
+ 'DEFAULT COLLATE' => 7, 'INDEX DIRECTORY' => 7,
+ 'DEFAULT CHARACTER SET' => 7,
'XML' => 9,
'ENUM' => 9, 'TEXT' => 9,
@@ -190,18 +190,18 @@ class ContextMySql50600 extends Context
'CHARACTER' => 11, 'MEDIUMINT' => 11, 'VARBINARY' => 11,
'MEDIUMBLOB' => 11, 'MEDIUMTEXT' => 11,
- 'BINARY VARYING' => 13,
+ 'BINARY VARYING' => 15,
'KEY' => 19,
'INDEX' => 19,
'UNIQUE' => 19,
- 'INDEX KEY' => 21,
- 'UNIQUE KEY' => 21,
- 'FOREIGN KEY' => 21, 'PRIMARY KEY' => 21, 'SPATIAL KEY' => 21,
- 'FULLTEXT KEY' => 21, 'UNIQUE INDEX' => 21,
- 'SPATIAL INDEX' => 21,
- 'FULLTEXT INDEX' => 21,
+ 'INDEX KEY' => 23,
+ 'UNIQUE KEY' => 23,
+ 'FOREIGN KEY' => 23, 'PRIMARY KEY' => 23, 'SPATIAL KEY' => 23,
+ 'FULLTEXT KEY' => 23, 'UNIQUE INDEX' => 23,
+ 'SPATIAL INDEX' => 23,
+ 'FULLTEXT INDEX' => 23,
'X' => 33, 'Y' => 33,
'LN' => 33, 'PI' => 33,
@@ -315,7 +315,7 @@ class ContextMySql50600 extends Context
'LOCALTIMESTAMP' => 35,
'CURRENT_TIMESTAMP' => 35,
- 'NOT IN' => 37,
+ 'NOT IN' => 39,
'DATE' => 41, 'TIME' => 41, 'YEAR' => 41,
'TIMESTAMP' => 41,
diff --git a/libraries/sql-parser/src/Contexts/ContextMySql50700.php b/libraries/sql-parser/src/Contexts/ContextMySql50700.php
index 083740996e..6a3cfd638b 100644
--- a/libraries/sql-parser/src/Contexts/ContextMySql50700.php
+++ b/libraries/sql-parser/src/Contexts/ContextMySql50700.php
@@ -173,14 +173,14 @@ class ContextMySql50700 extends Context
'SQL_CALC_FOUND_ROWS' => 3,
'MASTER_SSL_VERIFY_SERVER_CERT' => 3,
- 'NOT NULL' => 5, 'SET NULL' => 5,
- 'NO ACTION' => 5, 'ON DELETE' => 5, 'ON UPDATE' => 5,
- 'CHARACTER SET' => 5, 'IF NOT EXISTS' => 5,
- 'DATA DIRECTORY' => 5,
- 'DEFAULT COLLATE' => 5, 'INDEX DIRECTORY' => 5,
- 'DEFAULT CHARACTER SET' => 5,
-
- 'GROUP BY' => 7, 'ORDER BY' => 7,
+ 'GROUP BY' => 7, 'NOT NULL' => 7, 'ORDER BY' => 7, 'SET NULL' => 7,
+ 'IF EXISTS' => 7, 'NO ACTION' => 7, 'ON DELETE' => 7, 'ON UPDATE' => 7,
+ 'OR REPLACE' => 7,
+ 'SQL SECURITY' => 7,
+ 'CHARACTER SET' => 7, 'IF NOT EXISTS' => 7,
+ 'DATA DIRECTORY' => 7,
+ 'DEFAULT COLLATE' => 7, 'INDEX DIRECTORY' => 7,
+ 'DEFAULT CHARACTER SET' => 7,
'XML' => 9,
'ENUM' => 9, 'TEXT' => 9,
@@ -198,18 +198,18 @@ class ContextMySql50700 extends Context
'CHARACTER' => 11, 'MEDIUMINT' => 11, 'VARBINARY' => 11,
'MEDIUMBLOB' => 11, 'MEDIUMTEXT' => 11,
- 'BINARY VARYING' => 13,
+ 'BINARY VARYING' => 15,
'KEY' => 19,
'INDEX' => 19,
'UNIQUE' => 19,
- 'INDEX KEY' => 21,
- 'UNIQUE KEY' => 21,
- 'FOREIGN KEY' => 21, 'PRIMARY KEY' => 21, 'SPATIAL KEY' => 21,
- 'FULLTEXT KEY' => 21, 'UNIQUE INDEX' => 21,
- 'SPATIAL INDEX' => 21,
- 'FULLTEXT INDEX' => 21,
+ 'INDEX KEY' => 23,
+ 'UNIQUE KEY' => 23,
+ 'FOREIGN KEY' => 23, 'PRIMARY KEY' => 23, 'SPATIAL KEY' => 23,
+ 'FULLTEXT KEY' => 23, 'UNIQUE INDEX' => 23,
+ 'SPATIAL INDEX' => 23,
+ 'FULLTEXT INDEX' => 23,
'X' => 33, 'Y' => 33,
'LN' => 33, 'PI' => 33,
@@ -327,7 +327,7 @@ class ContextMySql50700 extends Context
'LOCALTIMESTAMP' => 35,
'CURRENT_TIMESTAMP' => 35,
- 'NOT IN' => 37,
+ 'NOT IN' => 39,
'DATE' => 41, 'TIME' => 41, 'YEAR' => 41,
'TIMESTAMP' => 41,
diff --git a/libraries/sql-parser/src/Fragments/AlterFragment.php b/libraries/sql-parser/src/Fragments/AlterFragment.php
new file mode 100644
index 0000000000..54338f9dab
--- /dev/null
+++ b/libraries/sql-parser/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/libraries/sql-parser/src/Fragments/CreateDefFragment.php b/libraries/sql-parser/src/Fragments/CreateDefFragment.php
deleted file mode 100644
index 933ea5ef5e..0000000000
--- a/libraries/sql-parser/src/Fragments/CreateDefFragment.php
+++ /dev/null
@@ -1,125 +0,0 @@
-<?php
-
-/**
- * Parses the definition that follows the `CREATE` keyword.
- *
- * @package SqlParser
- * @subpackage Fragments
- */
-namespace SqlParser\Fragments;
-
-use SqlParser\Fragment;
-use SqlParser\Parser;
-use SqlParser\Token;
-use SqlParser\TokensList;
-
-/**
- * Parses the definition that follows the `CREATE` keyword.
- *
- * @category Fragments
- * @package SqlParser
- * @subpackage Fragments
- * @author Dan Ungureanu <udan1107@gmail.com>
- * @license http://opensource.org/licenses/GPL-2.0 GNU Public License
- */
-class CreateDefFragment extends Fragment
-{
-
- /**
- * All table options.
- *
- * @var array
- */
- public static $TABLE_OPTIONS = array(
- 'ENGINE' => array(1, 'var'),
- 'AUTO_INCREMENT' => array(2, 'var'),
- 'AVG_ROW_LENGTH' => array(3, 'var'),
- 'DEFAULT CHARACTER SET' => array(4, 'var'),
- 'CHARACTER SET' => array(4, 'var'),
- 'CHECKSUM' => array(5, 'var'),
- 'DEFAULT COLLATE' => array(5, 'var'),
- 'COLLATE' => array(6, 'var'),
- 'COMMENT' => array(7, 'var'),
- 'CONNECTION' => array(8, 'var'),
- 'DATA DIRECTORY' => array(9, 'var'),
- 'DELAY_KEY_WRITE' => array(10, 'var'),
- 'INDEX DIRECTORY' => array(11, 'var'),
- 'INSERT_METHOD' => array(12, 'var'),
- 'KEY_BLOCK_SIZE' => array(13, 'var'),
- 'MAX_ROWS' => array(14, 'var'),
- 'MIN_ROWS' => array(15, 'var'),
- 'PACK_KEYS' => array(16, 'var'),
- 'PASSWORD' => array(17, 'var'),
- 'ROW_FORMAT' => array(18, 'var'),
- 'TABLESPACE' => array(19, 'var'),
- 'STORAGE' => array(20, 'var'),
- 'UNION' => array(21, 'var'),
- );
-
- /**
- * All function options.
- *
- * @var array
- */
- public static $FUNC_OPTIONS = array(
- 'COMMENT' => array(1, 'var'),
- 'LANGUAGE SQL' => 2,
- 'DETERMINISTIC' => 3,
- 'NOT DETERMINISTIC' => 3,
- 'CONSTAINS SQL' => 4,
- 'NO SQL' => 4,
- 'READS SQL DATA' => 4,
- 'MODIFIES SQL DATA' => 4,
- 'SQL SEQURITY DEFINER' => array(5, 'var'),
- );
-
- /**
- * The name of the new table.
- *
- * @var string
- */
- public $name;
-
- /**
- * @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 CreateDefFragment
- */
- public static function parse(Parser $parser, TokensList $list, array $options = array())
- {
- $ret = new CreateDefFragment();
-
- 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)
- ) {
- continue;
- }
-
- if (($token->type === Token::TYPE_OPERATOR)
- && ($token->value === '(')
- ) {
- break;
- }
-
- $ret->name .= $token->value;
- }
-
- --$list->idx;
- return $ret;
- }
-}
diff --git a/libraries/sql-parser/src/Fragments/DataTypeFragment.php b/libraries/sql-parser/src/Fragments/DataTypeFragment.php
index 0b9e1a81b2..6a44b78b48 100644
--- a/libraries/sql-parser/src/Fragments/DataTypeFragment.php
+++ b/libraries/sql-parser/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/libraries/sql-parser/src/Fragments/FieldDefFragment.php b/libraries/sql-parser/src/Fragments/FieldDefFragment.php
index 6c5771e95a..9f43fcc2fe 100644
--- a/libraries/sql-parser/src/Fragments/FieldDefFragment.php
+++ b/libraries/sql-parser/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/libraries/sql-parser/src/Fragments/FieldFragment.php b/libraries/sql-parser/src/Fragments/FieldFragment.php
index edd0a2bbba..c33a7d7ea3 100644
--- a/libraries/sql-parser/src/Fragments/FieldFragment.php
+++ b/libraries/sql-parser/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/libraries/sql-parser/src/Fragments/KeyFragment.php b/libraries/sql-parser/src/Fragments/KeyFragment.php
index d84c9f6565..1964cd2bc7 100644
--- a/libraries/sql-parser/src/Fragments/KeyFragment.php
+++ b/libraries/sql-parser/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/libraries/sql-parser/src/Fragments/OptionsFragment.php b/libraries/sql-parser/src/Fragments/OptionsFragment.php
index 00ac282c16..fa06b0d780 100644
--- a/libraries/sql-parser/src/Fragments/OptionsFragment.php
+++ b/libraries/sql-parser/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/libraries/sql-parser/src/Fragments/ReferencesKeyword.php b/libraries/sql-parser/src/Fragments/ReferencesKeyword.php
index 8de0233a71..ea10d20267 100644
--- a/libraries/sql-parser/src/Fragments/ReferencesKeyword.php
+++ b/libraries/sql-parser/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)
+ );
+ }
}
diff --git a/libraries/sql-parser/src/Lexer.php b/libraries/sql-parser/src/Lexer.php
index 0d0862d908..01d41d88c7 100644
--- a/libraries/sql-parser/src/Lexer.php
+++ b/libraries/sql-parser/src/Lexer.php
@@ -290,7 +290,25 @@ class Lexer
*/
$iEnd = $this->last;
+ /**
+ * Whether last parsed character is a whitespace.
+ * @var bool
+ */
+ $lastSpace = false;
+
for ($j = 1; $j < Context::KEYWORD_MAX_LENGTH && $this->last < $this->len; ++$j, ++$this->last) {
+ // Composed keywords shouldn't have more than one whitespace between
+ // keywords.
+ if (Context::isWhitespace($this->str[$this->last])) {
+ if ($lastSpace) {
+ --$j; // The size of the keyword didn't increase.
+ continue;
+ } else {
+ $lastSpace = true;
+ }
+ } else {
+ $lastSpace = false;
+ }
$token .= $this->str[$this->last];
if (($this->last + 1 === $this->len) || (Context::isSeparator($this->str[$this->last + 1]))) {
if (($flags = Context::isKeyword($token))) {
diff --git a/libraries/sql-parser/src/Parser.php b/libraries/sql-parser/src/Parser.php
index 11874fac61..ff55f5f7d1 100644
--- a/libraries/sql-parser/src/Parser.php
+++ b/libraries/sql-parser/src/Parser.php
@@ -337,10 +337,7 @@ class Parser
* Processed statement.
* @var Statement
*/
- $stmt = new $class();
-
- // Parsing the actual statement.
- $stmt->parse($this, $this->list);
+ $stmt = new $class($this, $this->list);
// The first token that is a part of this token is the next token
// unprocessed by the previous statement.
diff --git a/libraries/sql-parser/src/Statement.php b/libraries/sql-parser/src/Statement.php
index 41fba6e117..5ef7aec4a4 100644
--- a/libraries/sql-parser/src/Statement.php
+++ b/libraries/sql-parser/src/Statement.php
@@ -38,7 +38,7 @@ abstract class Statement
*
* @var array
*/
- public static $CLAUSES;
+ public static $CLAUSES = array();
/**
* The options of this query.
@@ -77,6 +77,71 @@ abstract class Statement
}
/**
+ * Builds the statement.
+ *
+ * @return string
+ */
+ public function build()
+ {
+ /**
+ * Query to be returned.
+ * @var string
+ */
+ $query = '';
+
+ foreach (static::$CLAUSES as $clause) {
+
+ /**
+ * The name of the clause.
+ * @var string
+ */
+ $name = $clause[0];
+
+ /**
+ * The type of the clause.
+ * @see self::$CLAUSES
+ * @var int
+ */
+ $type = $clause[1];
+
+ // Checking if there is any parser (builder) for this clause.
+ if (empty(Parser::$KEYWORD_PARSERS[$name])) {
+ continue;
+ }
+
+ /**
+ * The builder (parser) of this clause.
+ * @var string
+ */
+ $class = Parser::$KEYWORD_PARSERS[$name]['class'];
+
+ /**
+ * The name of the field that is used as source for the builder.
+ * Same field is used to store the result of parsing.
+ * @var string
+ */
+ $field = Parser::$KEYWORD_PARSERS[$name]['field'];
+
+ // The field is empty, there is nothing to be built.
+ if (empty($this->$field)) {
+ continue;
+ }
+
+ // Checking if the name of the clause should be added.
+ if ($type & 2) {
+ $query .= $name . ' ';
+ }
+
+ // Checking if the result of the builder should be added.
+ if ($type & 1) {
+ $query .= $class::build($this->$field) . ' ';
+ }
+ }
+
+ return $query;
+ }
+
+ /**
* Parses the statements defined by the tokens list.
*
* @param Parser $parser The instance that requests parsing.
diff --git a/libraries/sql-parser/src/Statements/AlterStatement.php b/libraries/sql-parser/src/Statements/AlterStatement.php
index 81bca056f9..92f3744a7d 100644
--- a/libraries/sql-parser/src/Statements/AlterStatement.php
+++ b/libraries/sql-parser/src/Statements/AlterStatement.php
@@ -9,11 +9,12 @@
namespace SqlParser\Statements;
use SqlParser\Parser;
+use SqlParser\Statement;
use SqlParser\Token;
use SqlParser\TokensList;
+use SqlParser\Fragments\AlterFragment;
use SqlParser\Fragments\FieldFragment;
use SqlParser\Fragments\OptionsFragment;
-use SqlParser\Statements\NotImplementedStatement;
/**
* `ALTER` statement.
@@ -24,7 +25,7 @@ use SqlParser\Statements\NotImplementedStatement;
* @author Dan Ungureanu <udan1107@gmail.com>
* @license http://opensource.org/licenses/GPL-2.0 GNU Public License
*/
-class AlterStatement extends NotImplementedStatement
+class AlterStatement extends Statement
{
/**
@@ -37,9 +38,9 @@ class AlterStatement extends NotImplementedStatement
/**
* Column affected by this statement.
*
- * @var FieldFragment
+ * @var AlterFragment[]
*/
- public $altered;
+ public $altered = array();
/**
* Options of this statement.
@@ -47,83 +48,91 @@ class AlterStatement extends NotImplementedStatement
* @var array
*/
public static $OPTIONS = array(
-
'ONLINE' => 1,
'OFFLINE' => 1,
-
'IGNORE' => 2,
-
- 'TABLE' => 3,
-
- 'ADD' => 4,
- 'ALTER' => 4,
- 'ANALYZE' => 4,
- 'CHANGE' => 4,
- 'CHECK' => 4,
- 'COALESCE' => 4,
- 'CONVERT' => 4,
- 'DISABLE' => 4,
- 'DISCARD' => 4,
- 'DROP' => 4,
- 'ENABLE' => 4,
- 'IMPORT' => 4,
- 'MODIFY' => 4,
- 'OPTIMIZE' => 4,
- 'ORDER' => 4,
- 'PARTITION' => 4,
- 'REBUILD' => 4,
- 'REMOVE' => 4,
- 'RENAME' => 4,
- 'REORGANIZE' => 4,
- 'REPAIR' => 4,
-
- 'COLUMN' => 5,
- 'CONSTRAINT' => 5,
- 'DEFAULT' => 5,
- 'TO' => 5,
- 'BY' => 5,
- 'FOREIGN' => 5,
- 'FULLTEXT' => 5,
- 'KEYS' => 5,
- 'PARTITIONING' => 5,
- 'PRIMARY KEY' => 5,
- 'SPATIAL' => 5,
- 'TABLESPACE' => 5,
- 'INDEX' => 5,
-
- 'DEFAULT CHARACTER SET' => array(6, 'var'),
-
- 'COLLATE' => array(7, 'var'),
);
/**
- * Function called after the token was processed.
- *
- * Extracts the name of affected column.
- *
* @param Parser $parser The instance that requests parsing.
* @param TokensList $list The list of tokens to be parsed.
- * @param Token $token The token that is being parsed.
*
* @return void
*/
- public function after(Parser $parser, TokensList $list, Token $token)
+ public function parse(Parser $parser, TokensList $list)
{
- // Parsing operation.
- ++$list->idx;
- $this->options->merge(
- OptionsFragment::parse(
- $parser,
- $list,
- static::$OPTIONS
- )
+ ++$list->idx; // Skipping `ALTER`.
+ $this->options = OptionsFragment::parse(
+ $parser,
+ $list,
+ static::$OPTIONS
);
- // Parsing affected field.
- ++$list->idx;
- $this->altered = FieldFragment::parse($parser, $list);
+ // Skipping `TABLE`.
+ $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'TABLE');
- //
- parent::after($parser, $list, $token);
+ // Parsing affected table.
+ $this->table = FieldFragment::parse(
+ $parser, $list, array(
+ 'noAlias' => true,
+ 'noBrackets' => true,
+ )
+ );
+ ++$list->idx; // Skipping field.
+
+ /**
+ * The state of the parser.
+ *
+ * Below are the states of the parser.
+ *
+ * 0 -----------------[ alter operation ]-----------------> 1
+ *
+ * 1 -------------------------[ , ]-----------------------> 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)) {
+ continue;
+ }
+
+ if ($state === 0) {
+ $this->altered[] = AlterFragment::parse($parser, $list);
+ $state = 1;
+ } else if ($state === 1) {
+ if (($token->type === Token::TYPE_OPERATOR) && ($token->value === ',')) {
+ $state = 0;
+ }
+ }
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function build()
+ {
+ $tmp = array();
+ foreach ($this->altered as $altered) {
+ $tmp[] = $altered::build($altered);
+ }
+
+ return 'ALTER ' . OptionsFragment::build($this->options)
+ . ' TABLE ' . FieldFragment::build($this->table)
+ . ' ' . implode(', ', $tmp);
}
}
diff --git a/libraries/sql-parser/src/Statements/CreateStatement.php b/libraries/sql-parser/src/Statements/CreateStatement.php
index 7fd8ef0501..306bfebceb 100644
--- a/libraries/sql-parser/src/Statements/CreateStatement.php
+++ b/libraries/sql-parser/src/Statements/CreateStatement.php
@@ -38,30 +38,30 @@ class CreateStatement extends Statement
*/
public static $OPTIONS = array(
- 'DATABASE' => 1,
- 'EVENT' => 1,
- 'FUNCTION' => 1,
- 'INDEX' => 1,
- 'PROCEDURE' => 1,
- 'SERVER' => 1,
- 'TABLE' => 1,
- 'TABLESPACE' => 1,
- 'TRIGGER' => 1,
- 'USER' => 1,
- 'VIEW' => 1,
-
// CREATE TABLE
- 'TEMPORARY' => 2,
- 'IF NOT EXISTS' => 3,
+ 'TEMPORARY' => 1,
+ 'IF NOT EXISTS' => 2,
// CREATE FUNCTION / PROCEDURE and CREATE VIEW
- 'DEFINER' => array(2, 'var'),
+ 'DEFINER' => array(1, 'var='),
// CREATE VIEW
- 'OR REPLACE' => array(3, 'var'),
- 'ALGORITHM' => array(4, 'var'),
- 'DEFINER' => array(5, 'var'),
- 'SQL SECURITY' => array(6, 'var'),
+ 'OR REPLACE' => array(2, 'var='),
+ 'ALGORITHM' => array(3, 'var='),
+ 'DEFINER' => array(4, 'var='),
+ 'SQL SECURITY' => array(5, 'var'),
+
+ 'DATABASE' => 6,
+ 'EVENT' => 6,
+ 'FUNCTION' => 6,
+ 'INDEX' => 6,
+ 'PROCEDURE' => 6,
+ 'SERVER' => 6,
+ 'TABLE' => 6,
+ 'TABLESPACE' => 6,
+ 'TRIGGER' => 6,
+ 'USER' => 6,
+ 'VIEW' => 6,
);
/**
@@ -70,8 +70,8 @@ class CreateStatement extends Statement
* @var array
*/
public static $TABLE_OPTIONS = array(
- 'ENGINE' => array(1, 'var'),
- 'AUTO_INCREMENT' => array(2, 'var'),
+ 'ENGINE' => array(1, 'var='),
+ 'AUTO_INCREMENT' => array(2, 'var='),
'AVG_ROW_LENGTH' => array(3, 'var'),
'DEFAULT CHARACTER SET' => array(4, 'var'),
'CHARACTER SET' => array(4, 'var'),
@@ -144,15 +144,6 @@ class CreateStatement extends Statement
public $fields;
/**
- * The `SELECT` statement that defines this view.
- *
- * Used by `CREATE VIEW`.
- *
- * @var SelectStatement
- */
- public $select;
-
- /**
* The return data type of this routine.
*
* Used by `CREATE FUNCTION`.
@@ -170,15 +161,36 @@ class CreateStatement extends Statement
*/
public $parameters;
-
/**
- * The body of this function or procedure.
+ * The body of this function or procedure. For views, it is the select
+ * statement that gets the
*
- * Used by `CREATE FUNCTION` and `CREATE PROCEDURE`.
+ * Used by `CREATE FUNCTION`, `CREATE PROCEDURE` and `CREATE VIEW`.
*
* @var Token[]
*/
- public $body;
+ public $body = array();
+
+ /**
+ * @return string
+ */
+ public function build()
+ {
+ $tmp = '';
+ if ($this->options->has('TABLE')) {
+ $tmp = FieldDefFragment::build($this->fields);
+ } elseif ($this->options->has('VIEW')) {
+ if (!empty($this->fields)) {
+ $tmp .= ArrayFragment::build($this->fields) . ' ';
+ }
+ $tmp .= 'AS ' . TokensList::build($this->body);
+ }
+ return 'CREATE '
+ . OptionsFragment::build($this->options) . ' '
+ . FieldFragment::build($this->name) . ' '
+ . $tmp . ' '
+ . OptionsFragment::build($this->entityOptions);
+ }
/**
* @param Parser $parser The instance that requests parsing.
@@ -258,11 +270,17 @@ class CreateStatement extends Statement
--$list->idx; // getNext() also goes forward one field.
$this->fields = ArrayFragment::parse($parser, $list);
++$list->idx; // Skipping last token from the array.
- $token = $list->getNext();
+ $list->getNext();
}
- // Parsing the 'SELECT' statement.
- $this->select = new SelectStatement($parser, $list);
+ // Parsing the `AS` keyword.
+ for (; $list->idx < $list->count; ++$list->idx) {
+ $token = $list->tokens[$list->idx];
+ if ($token->type === Token::TYPE_DELIMITER) {
+ break;
+ }
+ $this->body[] = $token;
+ }
}
}
}
diff --git a/libraries/sql-parser/src/Statements/DropStatement.php b/libraries/sql-parser/src/Statements/DropStatement.php
index babf18ac58..f0cd1ced87 100644
--- a/libraries/sql-parser/src/Statements/DropStatement.php
+++ b/libraries/sql-parser/src/Statements/DropStatement.php
@@ -46,6 +46,21 @@ class DropStatement extends Statement
);
/**
+ * The clauses of this statement, in order.
+ *
+ * @see Statement::$CLAUSES
+ *
+ * @var array
+ */
+ public static $CLAUSES = array(
+ 'DROP' => array('DROP', 2),
+ // Used for options.
+ '_OPTIONS' => array('_OPTIONS', 1),
+ // Used for select expressions.
+ 'DROP_' => array('DROP', 1),
+ );
+
+ /**
* Dropped elements.
*
* @var FieldFragment[]
diff --git a/libraries/sql-parser/src/Statements/NotImplementedStatement.php b/libraries/sql-parser/src/Statements/NotImplementedStatement.php
index b96c5583fd..f5f8b8f0a1 100644
--- a/libraries/sql-parser/src/Statements/NotImplementedStatement.php
+++ b/libraries/sql-parser/src/Statements/NotImplementedStatement.php
@@ -28,19 +28,40 @@ class NotImplementedStatement extends Statement
{
/**
- * Function called after the token was processed.
- *
- * Jump to the end of the delimiter.
- *
+ * The part of the statement that can't be parsed.
+ * @var Token[]
+ */
+ public $unknown = array();
+
+ /**
+ * @return string
+ */
+ public function build()
+ {
+ // Building the parsed part of the query (if any).
+ $query = parent::build() . ' ';
+
+ // Rebuilding the unknown part from tokens.
+ foreach ($this->unknown as $token) {
+ $query .= $token->token;
+ }
+
+ return $query;
+ }
+
+ /**
* @param Parser $parser The instance that requests parsing.
* @param TokensList $list The list of tokens to be parsed.
- * @param Token $token The token that is being parsed.
*
* @return void
*/
- public function after(Parser $parser, TokensList $list, Token $token)
+ public function parse(Parser $parser, TokensList $list)
{
- $list->getNextOfType(Token::TYPE_DELIMITER);
- --$list->idx;
+ for (; $list->idx < $list->count; ++$list->idx) {
+ if ($list->tokens[$list->idx]->type === Token::TYPE_DELIMITER) {
+ break;
+ }
+ $this->unknown[] = $list->tokens[$list->idx];
+ }
}
}
diff --git a/libraries/sql-parser/src/Statements/SelectStatement.php b/libraries/sql-parser/src/Statements/SelectStatement.php
index 84ec5a29f3..e376b47a0a 100644
--- a/libraries/sql-parser/src/Statements/SelectStatement.php
+++ b/libraries/sql-parser/src/Statements/SelectStatement.php
@@ -57,7 +57,7 @@ class SelectStatement extends Statement
'DISTINCT' => 1,
'DISTINCTROW' => 1,
'HIGH_PRIORITY' => 2,
- 'MAX_STATEMENT_TIME' => array(3, 'var'),
+ 'MAX_STATEMENT_TIME' => array(3, 'var='),
'STRAIGHT_JOIN' => 4,
'SQL_SMALL_RESULT' => 5,
'SQL_BIG_RESULT' => 6,
diff --git a/libraries/sql-parser/src/TokensList.php b/libraries/sql-parser/src/TokensList.php
index 52a464e624..229bebcec4 100644
--- a/libraries/sql-parser/src/TokensList.php
+++ b/libraries/sql-parser/src/TokensList.php
@@ -40,6 +40,42 @@ class TokensList implements \ArrayAccess
public $idx = 0;
/**
+ * Constructor.
+ *
+ * @param array $tokens The initial array of tokens.
+ */
+ public function __construct(array $tokens = array(), $count = -1)
+ {
+ if (!empty($tokens)) {
+ $this->tokens = $tokens;
+ if ($count === -1) {
+ $this->count = count($tokens);
+ }
+ }
+ }
+
+ /**
+ * Builds an array of tokens by merging their raw value.
+ *
+ * @param array $tokens
+ *
+ * @return string
+ */
+ public static function build($list)
+ {
+ if ($list instanceof TokensList) {
+ $list = $list->tokens;
+ }
+ $ret = '';
+ if (is_array($list)) {
+ foreach ($list as $tok) {
+ $ret .= $tok->token;
+ }
+ }
+ return $ret;
+ }
+
+ /**
* Adds a new token.
*
* @param Token $token Token to be added in list.
@@ -61,7 +97,8 @@ class TokensList implements \ArrayAccess
{
for (; $this->idx < $this->count; ++$this->idx) {
if (($this->tokens[$this->idx]->type !== Token::TYPE_WHITESPACE)
- && ($this->tokens[$this->idx]->type !== Token::TYPE_COMMENT)) {
+ && ($this->tokens[$this->idx]->type !== Token::TYPE_COMMENT)
+ ) {
return $this->tokens[$this->idx++];
}
}
diff --git a/libraries/sql-parser/src/Utils/Query.php b/libraries/sql-parser/src/Utils/Query.php
index 11a08d0212..f74162067f 100644
--- a/libraries/sql-parser/src/Utils/Query.php
+++ b/libraries/sql-parser/src/Utils/Query.php
@@ -531,7 +531,7 @@ class Query
if ($onlyType) {
return static::getClause($statement, $list, $old, -1, false) . ' ' .
- $new . ' ' . static::getCLause($statement, $list, $old, 0) . ' ' .
+ $new . ' ' . static::getClause($statement, $list, $old, 0) . ' ' .
static::getClause($statement, $list, $old, 1, false);
}
diff --git a/libraries/sql-parser/src/Utils/Table.php b/libraries/sql-parser/src/Utils/Table.php
index 86ccbfb4d1..ba7f82fd80 100644
--- a/libraries/sql-parser/src/Utils/Table.php
+++ b/libraries/sql-parser/src/Utils/Table.php
@@ -36,7 +36,10 @@ class Table
*/
public static function getForeignKeys($statement)
{
- if ((empty($statement->fields)) || (!$statement->options->has('TABLE'))) {
+ if ((empty($statement->fields))
+ || (!is_array($statement->fields))
+ || (!$statement->options->has('TABLE'))
+ ) {
return array();
}
@@ -85,7 +88,10 @@ class Table
*/
public static function getFields($statement)
{
- if ((empty($statement->fields)) || (!$statement->options->has('TABLE'))) {
+ if ((empty($statement->fields))
+ || (!is_array($statement->fields))
+ || (!$statement->options->has('TABLE'))
+ ) {
return array();
}
diff --git a/libraries/sql.lib.php b/libraries/sql.lib.php
index 71704d6da6..69dc46c1a9 100644
--- a/libraries/sql.lib.php
+++ b/libraries/sql.lib.php
@@ -1311,9 +1311,13 @@ function PMA_deleteTransformationInfo($db, $table, $analyzed_sql_results)
include_once 'libraries/transformations.lib.php';
$statement = $analyzed_sql_results['statement'];
if ($statement instanceof SqlParser\Statements\AlterStatement) {
- if ($statement->options->has('DROP')) {
- if (!empty($statement->altered->column)) {
- PMA_clearTransformations($db, $table, $statement->altered->column);
+ if ($statement->altered[0]->options->has('DROP')) {
+ if (!empty($statement->altered[0]->field->column)) {
+ PMA_clearTransformations(
+ $db,
+ $table,
+ $statement->altered[0]->field->column
+ );
}
}
} elseif ($statement instanceof SqlParser\Statements\DropStatement) {
diff --git a/test/classes/PMA_DisplayResults_test.php b/test/classes/PMA_DisplayResults_test.php
index 3485a80bf1..db8d10dcb5 100644
--- a/test/classes/PMA_DisplayResults_test.php
+++ b/test/classes/PMA_DisplayResults_test.php
@@ -101,9 +101,7 @@ class PMA_DisplayResults_Test extends PHPUnit_Framework_TestCase
array(
array(
'statement' => $parser->statements[0],
- 'queryflags' => array(
- 'select_from' => true,
- ),
+ 'select_from' => true,
),
)
)