summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Context.php9
-rw-r--r--src/Fragments/FieldFragment.php54
-rw-r--r--src/Fragments/JoinKeyword.php89
-rw-r--r--src/Fragments/SelectKeyword.php25
-rw-r--r--src/Parser.php1
-rw-r--r--src/Statements/CreateStatement.php7
-rw-r--r--src/Statements/SelectStatement.php7
-rw-r--r--src/Utils/Misc.php91
-rw-r--r--src/Utils/Routine.php2
9 files changed, 251 insertions, 34 deletions
diff --git a/src/Context.php b/src/Context.php
index 3d7f4ab..2b43e56 100644
--- a/src/Context.php
+++ b/src/Context.php
@@ -309,14 +309,19 @@ abstract class Context
* Checks if the given string is a keyword.
*
* @param string $str
+ * @param bool $strict
*
* @return bool
*/
- public static function isKeyword($str)
+ public static function isKeyword($str, $strict = false)
{
$str = strtoupper($str);
- return isset(static::$KEYWORDS[$str]);
+ if (isset(static::$KEYWORDS[$str])) {
+ return $strict ? static::$KEYWORDS[$str] === 1 : true;
+ }
+
+ return false;
}
// -------------------------------------------------------------------------
diff --git a/src/Fragments/FieldFragment.php b/src/Fragments/FieldFragment.php
index 897a4cc..71e1066 100644
--- a/src/Fragments/FieldFragment.php
+++ b/src/Fragments/FieldFragment.php
@@ -63,6 +63,12 @@ class FieldFragment extends Fragment
/** @var bool Whether current tokens make an expression or a table reference. */
$isExpr = false;
+ /** @var bool Whether a period was previously found. */
+ $period = false;
+
+ /** @var int Whether an alias is expected. Is 2 if `AS` keyword was found. */
+ $alias = 0;
+
/** @var int Counts brackets. */
$brackets = 0;
@@ -77,16 +83,23 @@ class FieldFragment extends Fragment
// Skipping whitespaces and comments.
if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
- if ($isExpr) {
+ if (($isExpr) && (!$alias)) {
$ret->expr .= $token->token;
$ret->tokens[] = $token;
}
+ if (($alias === 0) && (!$isExpr) && (!$period) && (!empty($ret->expr))) {
+ $alias = 1;
+ }
continue;
}
if ($token->type === Token::TYPE_KEYWORD) {
// Keywords may be found only between brackets.
if ($brackets === 0) {
+ if ($token->value === 'AS') {
+ $alias = 2;
+ continue;
+ }
break;
}
}
@@ -109,26 +122,45 @@ class FieldFragment extends Fragment
}
if (($token->type === Token::TYPE_NUMBER) || ($token->type === Token::TYPE_BOOL) ||
+ (($token->type === Token::TYPE_SYMBOL) && ($token->flags & Token::FLAG_SYMBOL_VARIABLE)) ||
(($token->type === Token::TYPE_OPERATOR)) && ($token->value !== '.')) {
// Numbers, booleans and operators are usually part of expressions.
$isExpr = true;
}
- if (!$isExpr) {
- if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '.')) {
- $ret->database = $ret->table;
- $ret->table = $ret->column;
- } else {
- if (!empty($options['skipColumn'])) {
- $ret->table = $token->value;
+ if ($alias) {
+ $ret->alias = $token->value;
+ $alias = 0;
+ } else {
+ if (!$isExpr) {
+ if (($token->type === Token::TYPE_OPERATOR) && ($token->value === '.')) {
+ $ret->database = $ret->table;
+ $ret->table = $ret->column;
+ $period = true;
} else {
- $ret->column = $token->value;
+ if (!empty($options['skipColumn'])) {
+ $ret->table = $token->value;
+ } else {
+ $ret->column = $token->value;
+ }
+ $period = false;
+ }
+ } else {
+ if ($brackets === 0) {
+ if (($token->type === Token::TYPE_NONE) || ($token->type === Token::TYPE_STRING) ||
+ (($token->type === Token::TYPE_SYMBOL) && ($token->flags & Token::FLAG_SYMBOL_BACKTICK))) {
+ $ret->alias = $token->value;
+ }
}
}
+
+ $ret->expr .= $token->token;
+ $ret->tokens[] = $token;
}
+ }
- $ret->expr .= $token->token;
- $ret->tokens[] = $token;
+ if ($alias === 2) {
+ $parser->error('Alias was expected.', $token);
}
if (empty($ret->tokens)) {
diff --git a/src/Fragments/JoinKeyword.php b/src/Fragments/JoinKeyword.php
new file mode 100644
index 0000000..62f9ecd
--- /dev/null
+++ b/src/Fragments/JoinKeyword.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace SqlParser\Fragments;
+
+use SqlParser\Fragment;
+use SqlParser\Lexer;
+use SqlParser\Parser;
+use SqlParser\Token;
+use SqlParser\TokensList;
+
+/**
+ * `JOIN` keyword parser.
+ */
+class JoinKeyword extends Fragment
+{
+
+ /**
+ * Join expression.
+ *
+ * @var FieldFragment
+ */
+ public $expr;
+
+ /**
+ * Join conditions.
+ *
+ * @var WhereKeyword
+ */
+ public $on;
+
+ /**
+ * @param Parser $parser
+ * @param TokensList $list
+ * @param array $options
+ *
+ * @return JoinKeyword
+ */
+ public static function parse(Parser $parser, TokensList $list, array $options = array())
+ {
+ $ret = new JoinKeyword();
+
+ /**
+ * The state of the parser.
+ *
+ * Below are the states of the parser.
+ *
+ * 0 -----------------------[ expr ]----------------------> 1
+ *
+ * 1 ------------------------[ ON ]-----------------------> 2
+ *
+ * 2 --------------------[ conditions ]-------------------> -1
+ *
+ * @var int
+ */
+ $state = 0;
+
+ for (; $list->idx < $list->count; ++$list->idx) {
+ /** @var Token Token parsed at this moment. */
+ $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) {
+ $ret->expr = FieldFragment::parse($parser, $list, array('skipColumn' => true));
+ $state = 1;
+ } elseif ($state === 1) {
+ if (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'ON')) {
+ $state = 2;
+ }
+ } elseif ($state === 2) {
+ $ret->on = WhereKeyword::parse($parser, $list);
+ ++$list->idx;
+ break;
+ }
+
+ }
+
+ --$list->idx;
+ return $ret;
+ }
+}
diff --git a/src/Fragments/SelectKeyword.php b/src/Fragments/SelectKeyword.php
index f86d05d..dc8db95 100644
--- a/src/Fragments/SelectKeyword.php
+++ b/src/Fragments/SelectKeyword.php
@@ -27,9 +27,6 @@ class SelectKeyword extends Fragment
$expr = null;
- /** @var bool Whether an alias is expected. */
- $alias = false;
-
for (; $list->idx < $list->count; ++$list->idx) {
/** @var Token Token parsed at this moment. */
$token = $list->tokens[$list->idx];
@@ -45,25 +42,16 @@ class SelectKeyword extends Fragment
}
if ($token->type === Token::TYPE_KEYWORD) {
- // No keyword is expected other than `AS`.
- if ($token->value !== 'AS') {
- break;
- }
+ // No keyword is expected.
+ break;
}
if (($token->type === Token::TYPE_OPERATOR) && ($token->value === ',')) {
$ret[] = $expr;
- } elseif (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'AS')) {
- $alias = true;
} else {
- if ($alias) {
- $expr->alias = $token->value;
- $alias = false;
- } else {
- $expr = FieldFragment::parse($parser, $list);
- if ($expr === null) {
- break;
- }
+ $expr = FieldFragment::parse($parser, $list);
+ if ($expr === null) {
+ break;
}
}
@@ -71,9 +59,6 @@ class SelectKeyword extends Fragment
// Last iteration was not processed.
if (!empty($expr->tokens)) {
- if ($alias) {
- $parser->error('Alias was expected.', $token);
- }
$ret[] = $expr;
}
diff --git a/src/Parser.php b/src/Parser.php
index c2f4ae2..95ecf6c 100644
--- a/src/Parser.php
+++ b/src/Parser.php
@@ -66,6 +66,7 @@ class Parser
'GROUP' => 'SqlParser\\Fragments\\GroupFragment',
'HAVING' => 'SqlParser\\Fragments\\WhereKeyword',
'INTO' => 'SqlParser\\Fragments\\IntoKeyword',
+ 'JOIN' => 'SqlParser\\Fragments\\JoinKeyword',
'LIMIT' => 'SqlParser\\Fragments\\LimitKeyword',
'ORDER' => 'SqlParser\\Fragments\\OrderKeyword',
'PARTITION' => 'SqlParser\\Fragments\\ArrayFragment',
diff --git a/src/Statements/CreateStatement.php b/src/Statements/CreateStatement.php
index 25b4a8f..00561f5 100644
--- a/src/Statements/CreateStatement.php
+++ b/src/Statements/CreateStatement.php
@@ -50,6 +50,13 @@ class CreateStatement extends Statement
public $options;
/**
+ * The parameters of this routine.
+ *
+ * @var ParamDefFragment[]
+ */
+ public $parameters;
+
+ /**
* The options of the table.
*
* @var OptionsFragment
diff --git a/src/Statements/SelectStatement.php b/src/Statements/SelectStatement.php
index b8f3e10..d17450e 100644
--- a/src/Statements/SelectStatement.php
+++ b/src/Statements/SelectStatement.php
@@ -120,4 +120,11 @@ class SelectStatement extends Statement
* @var LimitKeyword
*/
public $limit;
+
+ /**
+ * Joins.
+ *
+ * @var JoinKeyword
+ */
+ public $join;
}
diff --git a/src/Utils/Misc.php b/src/Utils/Misc.php
new file mode 100644
index 0000000..aca16fe
--- /dev/null
+++ b/src/Utils/Misc.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace SqlParser\Utils;
+
+use SqlParser\Statement;
+
+class Misc
+{
+
+ /**
+ * Gets a list of all aliases and their original names.
+ *
+ * @param Statement $tree
+ * @param string $db
+ *
+ * @return array
+ */
+ public static function getAliases($tree, $db)
+ {
+ $retval = array();
+
+ $tables = array();
+
+ if ((!empty($tree->join->expr->table)) && (!empty($tree->join->expr->alias))) {
+ $thisDb = empty($tree->join->expr->database) ?
+ $db : $tree->join->expr->database;
+
+ $retval = array(
+ $thisDb => array(
+ 'alias' => null,
+ 'tables' => array(
+ $tree->join->expr->table => array(
+ 'alias' => $tree->join->expr->alias,
+ 'columns' => array(),
+ ),
+ ),
+ ),
+ );
+
+ $tables[$thisDb][$tree->join->expr->alias] = $tree->join->expr->table;
+ }
+
+ foreach ($tree->from as $expr) {
+ if (empty($expr->table)) {
+ continue;
+ }
+
+ $thisDb = empty($expr->database) ? $db : $expr->database;
+
+ if (!isset($retval[$thisDb])) {
+ $retval[$thisDb] = array(
+ 'alias' => null,
+ 'tables' => array(),
+ );
+ }
+
+ if (!isset($retval[$thisDb]['tables'][$expr->table])) {
+ $retval[$thisDb]['tables'][$expr->table] = array(
+ 'alias' => empty($expr->alias) ? null : $expr->alias,
+ 'columns' => array(),
+ );
+ }
+
+ if (!isset($tables[$thisDb])) {
+ $tables[$thisDb] = array();
+ }
+ $tables[$thisDb][$expr->alias] = $expr->table;
+ }
+
+ foreach ($tree->expr as $expr) {
+ if ((empty($expr->column)) || (empty($expr->alias)) ){
+ continue;
+ }
+
+ $thisDb = empty($expr->database) ? $db : $expr->database;
+
+ if (empty($expr->table)) {
+ foreach ($retval[$thisDb]['tables'] as &$table) {
+ $table['columns'][$expr->column] = $expr->alias;
+ }
+ } else {
+ $thisTable = isset($tables[$thisDb][$expr->table]) ?
+ $tables[$thisDb][$expr->table] : $expr->table;
+ $retval[$thisDb]['tables'][$thisTable]['columns'][$expr->column] = $expr->alias;
+ }
+ }
+
+ return $retval;
+ }
+
+}
diff --git a/src/Utils/Routine.php b/src/Utils/Routine.php
index 3015a3f..1b694e2 100644
--- a/src/Utils/Routine.php
+++ b/src/Utils/Routine.php
@@ -8,7 +8,7 @@ class Routine
/**
* Gets the parameters of a routine from the parse tree.
*
- * @param array $tree
+ * @param Statement $tree
*
* @return array
*/