summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDan Ungureanu <udan1107@gmail.com>2015-06-28 23:55:20 +0300
committerDan Ungureanu <udan1107@gmail.com>2015-06-28 23:55:20 +0300
commit5f15789bd325c4fcd6642ecfeea18a5b639acc99 (patch)
tree472815cc0139c5fbed907e8465de3cd0d129f9cf /src
parent7780defae68f630dd49dccd88f898093a62da510 (diff)
downloadsql-parser-5f15789bd325c4fcd6642ecfeea18a5b639acc99.zip
sql-parser-5f15789bd325c4fcd6642ecfeea18a5b639acc99.tar.gz
sql-parser-5f15789bd325c4fcd6642ecfeea18a5b639acc99.tar.bz2
Improved parsing system.
Better support for current statements. Improved query utilities. Fixed a bug in LIMIT's parser. Fixed a documentation issue.
Diffstat (limited to 'src')
-rw-r--r--src/Fragments/LimitKeyword.php4
-rw-r--r--src/Parser.php10
-rw-r--r--src/Statement.php11
-rw-r--r--src/Statements/AlterStatement.php105
-rw-r--r--src/Statements/DropStatement.php4
-rw-r--r--src/Statements/NotImplementedStatement.php4
-rw-r--r--src/Utils/Query.php99
7 files changed, 199 insertions, 38 deletions
diff --git a/src/Fragments/LimitKeyword.php b/src/Fragments/LimitKeyword.php
index 261ef3e..a23eb4c 100644
--- a/src/Fragments/LimitKeyword.php
+++ b/src/Fragments/LimitKeyword.php
@@ -70,6 +70,10 @@ class LimitKeyword extends Fragment
continue;
}
+ if (($token->type === Token::TYPE_KEYWORD) && ($token->flags & Token::FLAG_KEYWORD_RESERVED)) {
+ break;
+ }
+
if (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'OFFSET')) {
if ($offset) {
$parser->error('An offset was expected.');
diff --git a/src/Parser.php b/src/Parser.php
index 0a876b7..2a9bd47 100644
--- a/src/Parser.php
+++ b/src/Parser.php
@@ -79,7 +79,11 @@ class Parser
* @var array
*/
public static $KEYWORD_PARSERS = array(
-
+ 'ALTER' => array(
+ 'class' => 'SqlParser\\Fragments\\FieldFragment',
+ 'field' => 'table',
+ 'options' => array('skipColumn' => true),
+ ),
'ANALYZE' => array(
'class' => 'SqlParser\\Fragments\\FromKeyword',
'field' => 'tables',
@@ -100,6 +104,10 @@ class Parser
'class' => 'SqlParser\\Fragments\\FromKeyword',
'field' => 'tables',
),
+ 'DROP' => array(
+ 'class' => 'SqlParser\\Fragments\\FromKeyword',
+ 'field' => 'fields'
+ ),
'FROM' => array(
'class' => 'SqlParser\\Fragments\\FromKeyword',
'field' => 'from',
diff --git a/src/Statement.php b/src/Statement.php
index 74b2cc3..f10b5ee 100644
--- a/src/Statement.php
+++ b/src/Statement.php
@@ -100,9 +100,18 @@ abstract class Statement
*/
$field = null;
+ /**
+ * Parser's options.
+ * @var array
+ */
+ $options = array();
+
if (!empty(Parser::$KEYWORD_PARSERS[$token->value])) {
$class = Parser::$KEYWORD_PARSERS[$token->value]['class'];
$field = Parser::$KEYWORD_PARSERS[$token->value]['field'];
+ if (!empty(Parser::$KEYWORD_PARSERS[$token->value]['options'])) {
+ $options = Parser::$KEYWORD_PARSERS[$token->value]['options'];
+ }
}
if (!empty(Parser::$STATEMENT_PARSERS[$token->value])) {
@@ -130,7 +139,7 @@ abstract class Statement
// Parsing this keyword.
if ($class !== null) {
++$list->idx; // Skipping keyword.
- $this->$field = $class::parse($parser, $list, array());
+ $this->$field = $class::parse($parser, $list, $options);
}
$this->after($parser, $list, $token);
diff --git a/src/Statements/AlterStatement.php b/src/Statements/AlterStatement.php
index 1d6b44c..b6c18ed 100644
--- a/src/Statements/AlterStatement.php
+++ b/src/Statements/AlterStatement.php
@@ -8,6 +8,11 @@
*/
namespace SqlParser\Statements;
+use SqlParser\Parser;
+use SqlParser\Token;
+use SqlParser\TokensList;
+use SqlParser\Fragments\FieldFragment;
+use SqlParser\Fragments\OptionsFragment;
use SqlParser\Statements\NotImplementedStatement;
/**
@@ -23,23 +28,101 @@ class AlterStatement extends NotImplementedStatement
{
/**
+ * Table affected.
+ *
+ * @var FieldFragment
+ */
+ public $table;
+
+ /**
+ * Column affected by this statement.
+ *
+ * @var FieldFragment
+ */
+ public $altered;
+
+ /**
* Options of this statement.
*
* @var array
*/
public static $OPTIONS = array(
- 'DATABASE' => 1,
- 'EVENT' => 1,
- 'FUNCTION' => 1,
- 'INDEX' => 1,
- 'LOGFILE' => 1,
- 'PROCEDURE' => 1,
- 'SCHEMA' => 1,
- 'SERVER' => 1,
- 'TABLE' => 1,
- 'TABLESPACE' => 1,
- 'TRIGGER' => 1,
+ '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'),
);
+ /**
+ * 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)
+ {
+ // Parsing operation.
+ ++$list->idx;
+ $this->options->merge(
+ OptionsFragment::parse(
+ $parser,
+ $list,
+ static::$OPTIONS
+ )
+ );
+
+ // Parsing affected field.
+ ++$list->idx;
+ $this->altered = FieldFragment::parse($parser, $list);
+
+ //
+ parent::after($parser, $list, $token);
+ }
+
}
diff --git a/src/Statements/DropStatement.php b/src/Statements/DropStatement.php
index 716ca54..babf18a 100644
--- a/src/Statements/DropStatement.php
+++ b/src/Statements/DropStatement.php
@@ -48,7 +48,7 @@ class DropStatement extends Statement
/**
* Dropped elements.
*
- * @var FromKeyworrd[]
+ * @var FieldFragment[]
*/
- public $name;
+ public $fields;
}
diff --git a/src/Statements/NotImplementedStatement.php b/src/Statements/NotImplementedStatement.php
index 1ad5cc5..1689df2 100644
--- a/src/Statements/NotImplementedStatement.php
+++ b/src/Statements/NotImplementedStatement.php
@@ -16,7 +16,7 @@ use SqlParser\TokensList;
/**
* Not implemented (yet) statements.
*
- * The `before` function makes the parser jump straight to the first delimiter.
+ * The `after` function makes the parser jump straight to the first delimiter.
*
* @category Statements
* @package SqlParser
@@ -36,7 +36,7 @@ class NotImplementedStatement extends Statement
*
* @return void
*/
- public function before(Parser $parser, TokensList $list, Token $token)
+ public function after(Parser $parser, TokensList $list, Token $token)
{
$list->getNextOfType(Token::TYPE_DELIMITER);
--$list->idx;
diff --git a/src/Utils/Query.php b/src/Utils/Query.php
index 8cb9dfd..dde3304 100644
--- a/src/Utils/Query.php
+++ b/src/Utils/Query.php
@@ -373,6 +373,10 @@ class Query
if ($statement instanceof SelectStatement) {
$ret['select_tables'] = array();
$ret['select_expr'] = array();
+
+ // Trying to find selected tables only from the select expression.
+ // Sometimes, this is not possible because the tables aren't defined
+ // explicitly (e.g. SELECT * FROM film, SELECT film_id FROM film).
foreach ($statement->expr as $expr) {
if (!empty($expr->table)) {
$ret['select_tables'][] = array(
@@ -383,16 +387,37 @@ class Query
$ret['select_expr'][] = $expr->expr;
}
}
+
+ // If no tables names were found in the SELECT clause or if there
+ // are expressions like * or COUNT(*), etc. tables names should be
+ // extracted from the FROM clause as well.
+ if ((empty($ret['select_tables'])) || (!$ret['select_expr'])) {
+ foreach ($statement->from as $expr) {
+ if (!empty($expr->table)) {
+ $ret['select_tables'][] = array(
+ $expr->table,
+ !empty($expr->database) ? $expr->database : null
+ );
+ }
+ }
+ }
}
return $ret;
}
+ /**
+ * Gets the type of clause.
+ *
+ * @param string $clause The clause.
+ *
+ * @return string
+ */
public static function getClauseType($clause)
{
$type = '';
for ($i = 0, $len = strlen($clause); $i < $len; ++$i) {
- if ((empty($type)) && (ctype_space($type))) {
+ if ((empty($type)) && (ctype_space($clause[$i]))) {
// Skipping whitespaces if we haven't started determining the
// type.
continue;
@@ -408,18 +433,20 @@ class Query
}
/**
- * Replaces the clause in the query. If the clause does not exist, it is
- * inserted.
- *
- * It is a very basic version of a query builder.
+ * Gets a specific clause.
*
* @param Statement $statement The parsed query that has to be modified.
* @param TokensList $list The list of tokens.
- * @param string $clause The clause to be replaced.
+ * @param string $clause The clause to be returned.
+ * @param int $type The type of the search.
+ * -1 for everything that was before
+ * 0 only for the clause
+ * 1 for everything after
+ * @param bool $skipFirst Whether to skip the first keyword in clause.
*
* @return string
*/
- public static function replaceClause($statement, $list, $clause)
+ public static function getClause($statement, $list, $clause, $type = 0, $skipFirst = true)
{
/**
@@ -436,16 +463,10 @@ class Query
$brackets = 0;
/**
- * The sections before the clause
- * @var string
- */
- $before = '';
-
- /**
- * The sections after the clause.
+ * The string to be returned.
* @var string
*/
- $after = '';
+ $ret = '';
/**
* The place where the clause should be added.
@@ -473,21 +494,57 @@ class Query
// Checking if we changed sections.
if ($token->type === Token::TYPE_KEYWORD) {
if (isset($statement::$SECTIONS[$token->value])) {
- if ($statement::$SECTIONS[$token->value] > $currIdx) {
+ if ($statement::$SECTIONS[$token->value] >= $currIdx) {
$currIdx = $statement::$SECTIONS[$token->value];
+ if (($skipFirst) && ($currIdx == $clauseIdx)) {
+ // This token is skipped (not added to the old
+ // clause) because it will be replaced.
+ continue;
+ }
}
}
}
}
- if ($currIdx < $clauseIdx) {
- $before .= $token->token;
- } elseif ($currIdx > $clauseIdx) {
- $after .= $token->value;
+ if ((($type === -1) && ($currIdx < $clauseIdx))
+ || (($type === 0) && ($currIdx === $clauseIdx))
+ || (($type === 1) && ($currIdx > $clauseIdx))
+ ) {
+ $ret .= $token->token;
}
}
- return $before . ' ' . $clause . ' ' . $after;
+ return trim($ret);
+ }
+
+ /**
+ * Builds a query by rebuilding the statement from the tokens list supplied
+ * and replaces a clause.
+ *
+ * It is a very basic version of a query builder.
+ *
+ * @param Statement $statement The parsed query that has to be modified.
+ * @param TokensList $list The list of tokens.
+ * @param string $clause The clause to be replaced.
+ * @param bool $onlyType Whether only the type of the clause should
+ * be replaced or the entire clause.
+ *
+ * @return string
+ */
+ public static function replaceClause($statement, $list, $clause, $onlyType = false)
+ {
+ // TODO: Update the tokens list and the statement.
+
+ if ($onlyType) {
+ return static::getClause($statement, $list, $clause, -1, false) . ' ' .
+ $clause . ' ' .
+ static::getCLause($statement, $list, $clause, 0) . ' ' .
+ static::getClause($statement, $list, $clause, 1, false);
+ }
+
+ return static::getClause($statement, $list, $clause, -1, false) . ' ' .
+ $clause . ' ' .
+ static::getClause($statement, $list, $clause, 1, false);
}
}