Commit 084add0a authored by guilhermeblanco's avatar guilhermeblanco

[2.0] Fixed warning in semantical error reporting (strpos with length over dql...

[2.0] Fixed warning in semantical error reporting (strpos with length over dql length). Fixed wrong grammar rule. Fixed wrong token position on semantical error reporting. Added more semantical checks in UpdateItem
parent f64347d8
...@@ -320,14 +320,20 @@ class Parser ...@@ -320,14 +320,20 @@ class Parser
$token = $this->_lexer->lookahead; $token = $this->_lexer->lookahead;
} }
// Minimum exposed chars ahead of token
$distance = 12;
// Find a position of a final word to display in error string // Find a position of a final word to display in error string
$dql = $this->_query->getDql(); $dql = $this->_query->getDql();
$pos = strpos($dql, ' ', $token['position'] + 10); $length = strlen($dql);
$length = ($pos !== false) ? $pos - $token['position'] : 10; $pos = $token['position'] + $distance;
$pos = strpos($dql, ' ', ($length > $pos) ? $pos : $length);
$length = ($pos !== false) ? $pos - $token['position'] : $distance;
// Building informative message // Building informative message
$message = 'line 0, col ' . (isset($token['position']) ? $token['position'] : '-1') $message = 'line 0, col ' . (
. " near '" . substr($dql, $token['position'], $length) . "': Error: " . $message; (isset($token['position']) && $token['position'] > 0) ? $token['position'] : '-1'
) . " near '" . substr($dql, $token['position'], $length) . "': Error: " . $message;
throw \Doctrine\ORM\Query\QueryException::semanticalError($message); throw \Doctrine\ORM\Query\QueryException::semanticalError($message);
} }
...@@ -454,7 +460,9 @@ class Parser ...@@ -454,7 +460,9 @@ class Parser
$exprStack = array_pop($this->_deferredPathExpressionStacks); $exprStack = array_pop($this->_deferredPathExpressionStacks);
foreach ($exprStack as $item) { foreach ($exprStack as $item) {
$this->_validatePathExpression($item['pathExpression'], $item['nestingLevel']); $this->_validatePathExpression(
$item['pathExpression'], $item['nestingLevel'], $item['token']
);
} }
} }
...@@ -469,13 +477,16 @@ class Parser ...@@ -469,13 +477,16 @@ class Parser
* *
* @param PathExpression $pathExpression * @param PathExpression $pathExpression
* @param integer $nestingLevel * @param integer $nestingLevel
* @param array $token
* @return integer * @return integer
*/ */
private function _validatePathExpression(AST\PathExpression $pathExpression, $nestingLevel) private function _validatePathExpression(AST\PathExpression $pathExpression, $nestingLevel = null, $token = null)
{ {
$identificationVariable = $pathExpression->getIdentificationVariable(); $identificationVariable = $pathExpression->getIdentificationVariable();
$nestingLevel = ($nestingLevel !== null) ?: $this->_nestingLevel;
$token = ($token) ?: $this->_lexer->lookahead;
$this->_validateIdentificationVariable($identificationVariable, $nestingLevel); $this->_validateIdentificationVariable($identificationVariable, $nestingLevel, $token);
$class = $this->_queryComponents[$identificationVariable]['metadata']; $class = $this->_queryComponents[$identificationVariable]['metadata'];
$stateField = $collectionField = null; $stateField = $collectionField = null;
...@@ -483,17 +494,24 @@ class Parser ...@@ -483,17 +494,24 @@ class Parser
foreach ($pathExpression->getParts() as $field) { foreach ($pathExpression->getParts() as $field) {
// Check if it is not in a state field // Check if it is not in a state field
if ($stateField !== null) { if ($stateField !== null) {
$this->semanticalError('Cannot navigate through state field named ' . $stateField); $this->semanticalError(
'Cannot navigate through state field named ' . $stateField, $token
);
} }
// Check if it is not a collection field // Check if it is not a collection field
if ($collectionField !== null) { if ($collectionField !== null) {
$this->semanticalError('Can not navigate through collection-valued field named ' . $collectionField); $this->semanticalError(
'Cannot navigate through collection-valued field named ' . $collectionField,
$token
);
} }
// Check if field exists // Check if field exists
if ( ! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) { if ( ! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) {
$this->semanticalError('Class ' . $class->name . ' has no field named ' . $field); $this->semanticalError(
'Class ' . $class->name . ' has no field named ' . $field, $token
);
} }
if (isset($class->fieldMappings[$field])) { if (isset($class->fieldMappings[$field])) {
...@@ -547,7 +565,7 @@ class Parser ...@@ -547,7 +565,7 @@ class Parser
$semanticalError .= ' ' . implode(' or ', $expectedStringTypes) . ' expected.'; $semanticalError .= ' ' . implode(' or ', $expectedStringTypes) . ' expected.';
} }
$this->semanticalError($semanticalError); $this->semanticalError($semanticalError, $token);
} }
// We need to force the type in PathExpression // We need to force the type in PathExpression
...@@ -562,20 +580,27 @@ class Parser ...@@ -562,20 +580,27 @@ class Parser
* *
* @param string $idVariable * @param string $idVariable
* @param integer $nestingLevel * @param integer $nestingLevel
* @param array $token
* @return array Query Component * @return array Query Component
*/ */
private function _validateIdentificationVariable($idVariable, $nestingLevel) private function _validateIdentificationVariable($idVariable, $nestingLevel = null, $token = null)
{ {
if ( ! isset($this->_queryComponents[$idVariable])) { $nestingLevel = ($nestingLevel !== null) ?: $this->_nestingLevel;
$token = ($token) ?: $this->_lexer->lookahead;
if ( ! isset($this->_queryComponents[$idVariable])) {
echo '[Query Components: ' . var_export($this->_queryComponents, true) . ']';
$this->semanticalError( $this->semanticalError(
"Could not find '$idVariable' in query components" "Could not find '$idVariable' in query components", $token
); );
} }
// Validate if identification variable nesting level is lower or equal than the current one // Validate if identification variable nesting level is lower or equal than the current one
if ($this->_queryComponents[$idVariable]['nestingLevel'] > $nestingLevel) { if ($this->_queryComponents[$idVariable]['nestingLevel'] > $nestingLevel) {
$this->semanticalError( $this->semanticalError(
"Query component '$idVariable' is not in the same nesting level of its declaration" "Query component '$idVariable' is not in the same nesting level of its declaration",
$token
); );
} }
...@@ -732,13 +757,14 @@ class Parser ...@@ -732,13 +757,14 @@ class Parser
*/ */
public function JoinAssociationPathExpression() public function JoinAssociationPathExpression()
{ {
$token = $this->_lexer->lookahead;
$identificationVariable = $this->IdentificationVariable(); $identificationVariable = $this->IdentificationVariable();
$this->match('.'); $this->match('.');
$this->match(Lexer::T_IDENTIFIER); $this->match(Lexer::T_IDENTIFIER);
$field = $this->_lexer->token['value']; $field = $this->_lexer->token['value'];
// Validating IdentificationVariable (it was already defined previously) // Validating IdentificationVariable (it was already defined previously)
$this->_validateIdentificationVariable($identificationVariable, $this->_nestingLevel); $this->_validateIdentificationVariable($identificationVariable, null, $token);
// Validating association field (*-to-one or *-to-many) // Validating association field (*-to-one or *-to-many)
$class = $this->_queryComponents[$identificationVariable]['metadata']; $class = $this->_queryComponents[$identificationVariable]['metadata'];
...@@ -761,6 +787,7 @@ class Parser ...@@ -761,6 +787,7 @@ class Parser
*/ */
public function PathExpression($expectedType) public function PathExpression($expectedType)
{ {
$token = $this->_lexer->lookahead;
$identificationVariable = $this->IdentificationVariable(); $identificationVariable = $this->IdentificationVariable();
$parts = array(); $parts = array();
...@@ -780,6 +807,7 @@ class Parser ...@@ -780,6 +807,7 @@ class Parser
$exprStack[] = array( $exprStack[] = array(
'pathExpression' => $pathExpr, 'pathExpression' => $pathExpr,
'nestingLevel' => $this->_nestingLevel, 'nestingLevel' => $this->_nestingLevel,
'token' => $token,
); );
array_push($this->_deferredPathExpressionStacks, $exprStack); array_push($this->_deferredPathExpressionStacks, $exprStack);
...@@ -787,7 +815,7 @@ class Parser ...@@ -787,7 +815,7 @@ class Parser
} }
// Apply PathExpression validation normally (not in defer mode) // Apply PathExpression validation normally (not in defer mode)
$this->_validatePathExpression($pathExpr, $this->_nestingLevel); $this->_validatePathExpression($pathExpr, $this->_nestingLevel, $token);
return $pathExpr; return $pathExpr;
} }
...@@ -926,6 +954,7 @@ class Parser ...@@ -926,6 +954,7 @@ class Parser
public function UpdateClause() public function UpdateClause()
{ {
$this->match(Lexer::T_UPDATE); $this->match(Lexer::T_UPDATE);
$token = $this->_lexer->lookahead;
$abstractSchemaName = $this->AbstractSchemaName(); $abstractSchemaName = $this->AbstractSchemaName();
$aliasIdentificationVariable = null; $aliasIdentificationVariable = null;
...@@ -934,32 +963,34 @@ class Parser ...@@ -934,32 +963,34 @@ class Parser
} }
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) { if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$token = $this->_lexer->lookahead;
$aliasIdentificationVariable = $this->AliasIdentificationVariable(); $aliasIdentificationVariable = $this->AliasIdentificationVariable();
} else { } else {
$aliasIdentificationVariable = $abstractSchemaName; $aliasIdentificationVariable = $abstractSchemaName;
} }
$this->match(Lexer::T_SET); $class = $this->_em->getClassMetadata($abstractSchemaName);
$updateItems = array();
$updateItems[] = $this->UpdateItem();
while ($this->_lexer->isNextToken(',')) {
$this->match(',');
$updateItems[] = $this->UpdateItem();
}
$classMetadata = $this->_em->getClassMetadata($abstractSchemaName);
// Building queryComponent // Building queryComponent
$queryComponent = array( $queryComponent = array(
'metadata' => $classMetadata, 'metadata' => $class,
'parent' => null, 'parent' => null,
'relation' => null, 'relation' => null,
'map' => null, 'map' => null,
'nestingLevel' => $this->_nestingLevel, 'nestingLevel' => $this->_nestingLevel,
'token' => $token,
); );
$this->_queryComponents[$aliasIdentificationVariable] = $queryComponent; $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent;
$this->match(Lexer::T_SET);
$updateItems = array();
$updateItems[] = $this->UpdateItem();
while ($this->_lexer->isNextToken(',')) {
$this->match(',');
$updateItems[] = $this->UpdateItem();
}
$updateClause = new AST\UpdateClause($abstractSchemaName, $updateItems); $updateClause = new AST\UpdateClause($abstractSchemaName, $updateItems);
$updateClause->setAliasIdentificationVariable($aliasIdentificationVariable); $updateClause->setAliasIdentificationVariable($aliasIdentificationVariable);
...@@ -979,27 +1010,34 @@ class Parser ...@@ -979,27 +1010,34 @@ class Parser
$this->match(Lexer::T_FROM); $this->match(Lexer::T_FROM);
} }
$token = $this->_lexer->lookahead;
$deleteClause = new AST\DeleteClause($this->AbstractSchemaName()); $deleteClause = new AST\DeleteClause($this->AbstractSchemaName());
$aliasIdentificationVariable = null;
if ($this->_lexer->isNextToken(Lexer::T_AS)) { if ($this->_lexer->isNextToken(Lexer::T_AS)) {
$this->match(Lexer::T_AS); $this->match(Lexer::T_AS);
} }
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) { if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$deleteClause->setAliasIdentificationVariable($this->AliasIdentificationVariable()); $token = $this->_lexer->lookahead;
$aliasIdentificationVariable = $this->AliasIdentificationVariable();
} else { } else {
$deleteClause->setAliasIdentificationVariable($deleteClause->getAbstractSchemaName()); $aliasIdentificationVariable = $deleteClause->getAbstractSchemaName();
} }
$classMetadata = $this->_em->getClassMetadata($deleteClause->getAbstractSchemaName()); $deleteClause->setAliasIdentificationVariable($aliasIdentificationVariable);
$class = $this->_em->getClassMetadata($deleteClause->getAbstractSchemaName());
// Building queryComponent
$queryComponent = array( $queryComponent = array(
'metadata' => $classMetadata, 'metadata' => $class,
'parent' => null, 'parent' => null,
'relation' => null, 'relation' => null,
'map' => null, 'map' => null,
'nestingLevel' => $this->_nestingLevel, 'nestingLevel' => $this->_nestingLevel,
'token' => $token,
); );
$this->_queryComponents[$deleteClause->getAliasIdentificationVariable()] = $queryComponent; $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent;
return $deleteClause; return $deleteClause;
} }
...@@ -1151,25 +1189,31 @@ class Parser ...@@ -1151,25 +1189,31 @@ class Parser
*/ */
public function UpdateItem() public function UpdateItem()
{ {
$peek = $this->_lexer->glimpse(); $token = $this->_lexer->lookahead;
$identVariable = null; $identificationVariable = $this->IdentificationVariable();
if ($peek['value'] == '.') { // Validate if IdentificationVariable is defined
$identVariable = $this->IdentificationVariable(); $queryComponent = $this->_validateIdentificationVariable($identificationVariable, null, $token);
$this->match('.');
} else { $this->match('.');
throw QueryException::missingAliasQualifier();
}
$this->match(Lexer::T_IDENTIFIER); $this->match(Lexer::T_IDENTIFIER);
$field = $this->_lexer->token['value']; $field = $this->_lexer->token['value'];
// Check if field exists
$class = $queryComponent['metadata'];
if ( ! isset($class->associationMappings[$field]) && ! isset($class->fieldMappings[$field])) {
$this->semanticalError(
'Class ' . $class->name . ' has no field named ' . $field, $token
);
}
$this->match('='); $this->match('=');
$newValue = $this->NewValue(); $newValue = $this->NewValue();
$updateItem = new AST\UpdateItem($field, $newValue); $updateItem = new AST\UpdateItem($field, $newValue);
$updateItem->setIdentificationVariable($identVariable); $updateItem->setIdentificationVariable($identificationVariable);
return $updateItem; return $updateItem;
} }
...@@ -1185,10 +1229,11 @@ class Parser ...@@ -1185,10 +1229,11 @@ class Parser
$glimpse = $this->_lexer->glimpse(); $glimpse = $this->_lexer->glimpse();
if ($glimpse['value'] != '.') { if ($glimpse['value'] != '.') {
$token = $this->_lexer->lookahead;
$identificationVariable = $this->IdentificationVariable(); $identificationVariable = $this->IdentificationVariable();
// Validate if IdentificationVariable is defined // Validate if IdentificationVariable is defined
$this->_validateIdentificationVariable($identificationVariable, $this->_nestingLevel); $this->_validateIdentificationVariable($identificationVariable, null, $token);
return $identificationVariable; return $identificationVariable;
} }
...@@ -1210,10 +1255,11 @@ class Parser ...@@ -1210,10 +1255,11 @@ class Parser
$glimpse = $this->_lexer->glimpse(); $glimpse = $this->_lexer->glimpse();
if ($glimpse['value'] != '.') { if ($glimpse['value'] != '.') {
$token = $this->_lexer->lookahead;
$expr = $this->ResultVariable(); $expr = $this->ResultVariable();
// Check if ResultVariable is defined in query components // Check if ResultVariable is defined in query components
$queryComponent = $this->_validateIdentificationVariable($expr, $this->_nestingLevel); $queryComponent = $this->_validateIdentificationVariable($expr, null, $token);
// Outer defininition used in inner subselect is not enough. // Outer defininition used in inner subselect is not enough.
// ResultVariable exists in queryComponents, check nesting level // ResultVariable exists in queryComponents, check nesting level
...@@ -1342,6 +1388,7 @@ class Parser ...@@ -1342,6 +1388,7 @@ class Parser
$this->match(Lexer::T_AS); $this->match(Lexer::T_AS);
} }
$token = $this->_lexer->lookahead;
$aliasIdentificationVariable = $this->AliasIdentificationVariable(); $aliasIdentificationVariable = $this->AliasIdentificationVariable();
$classMetadata = $this->_em->getClassMetadata($abstractSchemaName); $classMetadata = $this->_em->getClassMetadata($abstractSchemaName);
...@@ -1352,6 +1399,7 @@ class Parser ...@@ -1352,6 +1399,7 @@ class Parser
'relation' => null, 'relation' => null,
'map' => null, 'map' => null,
'nestingLevel' => $this->_nestingLevel, 'nestingLevel' => $this->_nestingLevel,
'token' => $token,
); );
$this->_queryComponents[$aliasIdentificationVariable] = $queryComponent; $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent;
...@@ -1386,12 +1434,14 @@ class Parser ...@@ -1386,12 +1434,14 @@ class Parser
} }
$this->match(Lexer::T_JOIN); $this->match(Lexer::T_JOIN);
$joinPathExpression = $this->JoinAssociationPathExpression(); $joinPathExpression = $this->JoinAssociationPathExpression();
if ($this->_lexer->isNextToken(Lexer::T_AS)) { if ($this->_lexer->isNextToken(Lexer::T_AS)) {
$this->match(Lexer::T_AS); $this->match(Lexer::T_AS);
} }
$token = $this->_lexer->lookahead;
$aliasIdentificationVariable = $this->AliasIdentificationVariable(); $aliasIdentificationVariable = $this->AliasIdentificationVariable();
// Verify that the association exists. // Verify that the association exists.
...@@ -1413,6 +1463,7 @@ class Parser ...@@ -1413,6 +1463,7 @@ class Parser
'relation' => $parentClass->getAssociationMapping($assocField), 'relation' => $parentClass->getAssociationMapping($assocField),
'map' => null, 'map' => null,
'nestingLevel' => $this->_nestingLevel, 'nestingLevel' => $this->_nestingLevel,
'token' => $token,
); );
$this->_queryComponents[$aliasIdentificationVariable] = $joinQueryComponent; $this->_queryComponents[$aliasIdentificationVariable] = $joinQueryComponent;
...@@ -1487,12 +1538,14 @@ class Parser ...@@ -1487,12 +1538,14 @@ class Parser
} }
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) { if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$token = $this->_lexer->lookahead;
$resultVariable = $this->ResultVariable(); $resultVariable = $this->ResultVariable();
// Include ResultVariable in query components. // Include ResultVariable in query components.
$this->_queryComponents[$resultVariable] = array( $this->_queryComponents[$resultVariable] = array(
'resultvariable' => $expression, 'resultvariable' => $expression,
'nestingLevel' => $this->_nestingLevel, 'nestingLevel' => $this->_nestingLevel,
'token' => $token,
); );
} }
} else { } else {
...@@ -1538,6 +1591,7 @@ class Parser ...@@ -1538,6 +1591,7 @@ class Parser
} }
if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) { if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) {
$token = $this->_lexer->lookahead;
$resultVariable = $this->ResultVariable(); $resultVariable = $this->ResultVariable();
$expr->setFieldIdentificationVariable($resultVariable); $expr->setFieldIdentificationVariable($resultVariable);
...@@ -1545,6 +1599,7 @@ class Parser ...@@ -1545,6 +1599,7 @@ class Parser
$this->_queryComponents[$resultVariable] = array( $this->_queryComponents[$resultVariable] = array(
'resultvariable' => $expr, 'resultvariable' => $expr,
'nestingLevel' => $this->_nestingLevel, 'nestingLevel' => $this->_nestingLevel,
'token' => $token,
); );
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment