summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Parser.php9
-rw-r--r--src/Statement.php43
-rw-r--r--src/Utils/Query.php62
3 files changed, 114 insertions, 0 deletions
diff --git a/src/Parser.php b/src/Parser.php
index 0d48a41..d98e8dc 100644
--- a/src/Parser.php
+++ b/src/Parser.php
@@ -531,6 +531,9 @@ class Parser
$lastStatement->last = $statement->last;
$unionType = false;
+
+ // Validate clause order
+ $statement->validateClauseOrder($this, $list);
continue;
}
@@ -556,9 +559,15 @@ class Parser
}
$lastTransaction = null;
}
+
+ // Validate clause order
+ $statement->validateClauseOrder($this, $list);
continue;
}
+ // Validate clause order
+ $statement->validateClauseOrder($this, $list);
+
// Finally, storing the statement.
if ($lastTransaction !== null) {
$lastTransaction->statements[] = $statement;
diff --git a/src/Statement.php b/src/Statement.php
index c23db67..b4769ef 100644
--- a/src/Statement.php
+++ b/src/Statement.php
@@ -422,4 +422,47 @@ abstract class Statement
{
return $this->build();
}
+
+ /**
+ * Validates the order of the clauses in parsed statement
+ * Ideally this should be called after successfully
+ * completing the parsing of each statement
+ *
+ * @param Parser $parser The instance that requests parsing.
+ * @param TokensList $list The list of tokens to be parsed.
+ *
+ * @return boolean
+ */
+ public function validateClauseOrder($parser, $list)
+ {
+ $clauses = array_flip(array_keys($this->getClauses()));
+
+ if (empty($clauses)
+ || count($clauses) == 0
+ ) {
+ return true;
+ }
+
+ $minIdx = -1;
+ foreach ($clauses as $clauseType => $index) {
+ $clauseStartIdx = Utils\Query::getClauseStartOffset(
+ $this,
+ $list,
+ $clauseType
+ );
+
+ if ($clauseStartIdx != -1 && $clauseStartIdx < $minIdx) {
+ $token = $list->tokens[$clauseStartIdx];
+ $parser->error(
+ __('Unexpected ordering of clauses.'),
+ $token
+ );
+ return false;
+ } elseif ($clauseStartIdx != -1) {
+ $minIdx = $clauseStartIdx;
+ }
+ }
+
+ return true;
+ }
}
diff --git a/src/Utils/Query.php b/src/Utils/Query.php
index 2e0982a..c3167ff 100644
--- a/src/Utils/Query.php
+++ b/src/Utils/Query.php
@@ -783,4 +783,66 @@ class Query
return array(trim($statement), $query, $delimiter);
}
+
+ /**
+ * Gets a starting offset of 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 returned.
+ *
+ * @return int
+ */
+ public static function getClauseStartOffset($statement, $list, $clause)
+ {
+
+ /**
+ * The index of the current clause.
+ *
+ * @var int $currIdx
+ */
+ $currIdx = 0;
+
+ /**
+ * The count of brackets.
+ * We keep track of them so we won't insert the clause in a subquery.
+ *
+ * @var int $brackets
+ */
+ $brackets = 0;
+
+ /**
+ * The clauses of this type of statement and their index.
+ *
+ * @var array $clauses
+ */
+ $clauses = array_flip(array_keys($statement->getClauses()));
+
+ for ($i = $statement->first; $i <= $statement->last; ++$i) {
+ $token = $list->tokens[$i];
+
+ if ($token->type === Token::TYPE_COMMENT) {
+ continue;
+ }
+
+ if ($token->type === Token::TYPE_OPERATOR) {
+ if ($token->value === '(') {
+ ++$brackets;
+ } elseif ($token->value === ')') {
+ --$brackets;
+ }
+ }
+
+ if ($brackets == 0) {
+ if (($token->type === Token::TYPE_KEYWORD)
+ && (isset($clauses[$token->value]))
+ && ($clause === $token->value)
+ ) {
+ return $i;
+ }
+ }
+ }
+
+ return -1;
+ }
}