1
0
mirror of synced 2024-12-14 15:16:04 +03:00

Implemented COALESCE and NULLIF support in DQL.

This commit is contained in:
Guilherme Blanco 2011-06-17 16:16:22 -03:00
parent 626e467a17
commit 699ccfddb6
5 changed files with 170 additions and 12 deletions

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -27,7 +25,6 @@ namespace Doctrine\ORM\Query\AST;
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
* @version $Revision: 3938 $
* @author Guilherme Blanco <guilhermeblanco@hotmail.com> * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com> * @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>

View File

@ -1644,6 +1644,10 @@ class Parser
return $this->StateFieldPathExpression(); return $this->StateFieldPathExpression();
} else if ($lookahead == Lexer::T_INTEGER || $lookahead == Lexer::T_FLOAT) { } else if ($lookahead == Lexer::T_INTEGER || $lookahead == Lexer::T_FLOAT) {
return $this->SimpleArithmeticExpression(); return $this->SimpleArithmeticExpression();
} else if ($lookahead == Lexer::T_CASE || $lookahead == Lexer::T_COALESCE || $lookahead == Lexer::T_NULLIF) {
// Since NULLIF and COALESCE can be identified as a function,
// we need to check if before check for FunctionDeclaration
return $this->CaseExpression();
} else if ($this->_isFunction() || $this->_isAggregateFunction($this->_lexer->lookahead['type'])) { } else if ($this->_isFunction() || $this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
// We may be in an ArithmeticExpression (find the matching ")" and inspect for Math operator) // We may be in an ArithmeticExpression (find the matching ")" and inspect for Math operator)
$this->_lexer->peek(); // "(" $this->_lexer->peek(); // "("
@ -1665,8 +1669,6 @@ class Parser
} else if ($lookahead == Lexer::T_TRUE || $lookahead == Lexer::T_FALSE) { } else if ($lookahead == Lexer::T_TRUE || $lookahead == Lexer::T_FALSE) {
$this->match($lookahead); $this->match($lookahead);
return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']); return new AST\Literal(AST\Literal::BOOLEAN, $this->_lexer->token['value']);
} else if ($lookahead == Lexer::T_CASE || $lookahead == Lexer::T_COALESCE || $lookahead == Lexer::T_NULLIF) {
return $this->CaseExpression();
} else { } else {
$this->syntaxError(); $this->syntaxError();
} }
@ -1674,11 +1676,66 @@ class Parser
public function CaseExpression() public function CaseExpression()
{ {
$lookahead = $this->_lexer->lookahead['type'];
// if "CASE" "WHEN" => GeneralCaseExpression // if "CASE" "WHEN" => GeneralCaseExpression
// else if "CASE" => SimpleCaseExpression // else if "CASE" => SimpleCaseExpression
// else if "COALESCE" => CoalesceExpression // [DONE] else if "COALESCE" => CoalesceExpression
// else if "NULLIF" => NullifExpression // [DONE] else if "NULLIF" => NullifExpression
switch ($lookahead) {
case Lexer::T_NULLIF:
return $this->NullIfExpression();
case Lexer::T_COALESCE:
return $this->CoalesceExpression();
default:
$this->semanticalError('CaseExpression not yet supported.'); $this->semanticalError('CaseExpression not yet supported.');
return null;
}
}
/**
* CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
*
* @return Doctrine\ORM\Query\AST\CoalesceExpression
*/
public function CoalesceExpression()
{
$this->match(Lexer::T_COALESCE);
$this->match(Lexer::T_OPEN_PARENTHESIS);
// Process ScalarExpressions (1..N)
$scalarExpressions = array();
$scalarExpressions[] = $this->ScalarExpression();
while ($this->_lexer->isNextToken(Lexer::T_COMMA)) {
$this->match(Lexer::T_COMMA);
$scalarExpressions[] = $this->ScalarExpression();
}
$this->match(Lexer::T_CLOSE_PARENTHESIS);
return new AST\CoalesceExpression($scalarExpressions);
}
/**
* NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
*
* @return Doctrine\ORM\Query\AST\ExistsExpression
*/
public function NullIfExpression()
{
$this->match(Lexer::T_NULLIF);
$this->match(Lexer::T_OPEN_PARENTHESIS);
$firstExpression = $this->ScalarExpression();
$this->match(Lexer::T_COMMA);
$secondExpression = $this->ScalarExpression();
$this->match(Lexer::T_CLOSE_PARENTHESIS);
return new AST\NullIfExpression($firstExpression, $secondExpression);
} }
/** /**
@ -1717,12 +1774,16 @@ class Parser
} }
} else if ($this->_isFunction()) { } else if ($this->_isFunction()) {
$this->_lexer->peek(); // "(" $this->_lexer->peek(); // "("
$lookaheadType = $this->_lexer->lookahead['type'];
$beyond = $this->_peekBeyondClosingParenthesis(); $beyond = $this->_peekBeyondClosingParenthesis();
if ($this->_isMathOperator($beyond)) { if ($this->_isMathOperator($beyond)) {
$expression = $this->ScalarExpression(); $expression = $this->ScalarExpression();
} else if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { } else if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) {
$expression = $this->AggregateExpression(); $expression = $this->AggregateExpression();
} else if (in_array ($lookaheadType, array(Lexer::T_CASE, Lexer::T_COALESCE, Lexer::T_NULLIF))) {
$expression = $this->CaseExpression();
} else { } else {
// Shortcut: ScalarExpression => Function // Shortcut: ScalarExpression => Function
$expression = $this->FunctionDeclaration(); $expression = $this->FunctionDeclaration();

View File

@ -874,6 +874,60 @@ class SqlWalker implements TreeWalker
return $sql; return $sql;
} }
/**
* Walks down a CoalesceExpression AST node and generates the corresponding SQL.
*
* @param CoalesceExpression $coalesceExpression
* @return string The SQL.
*/
public function walkCoalesceExpression($coalesceExpression)
{
$sql = 'COALESCE(';
$scalarExpressions = array();
foreach ($coalesceExpression->scalarExpressions as $scalarExpression) {
$scalarExpressions[] = $this->walkSimpleArithmeticExpression($scalarExpression);
}
$sql .= implode(', ', $scalarExpressions) . ')';
return $sql;
}
public function walkCaseExpression($expression)
{
switch (true) {
case ($expression instanceof AST\CoalesceExpression):
return $this->walkCoalesceExpression($expression);
case ($expression instanceof AST\NullIfExpression):
return $this->walkNullIfExpression($expression);
default:
return '';
}
}
/**
* Walks down a NullIfExpression AST node and generates the corresponding SQL.
*
* @param NullIfExpression $nullIfExpression
* @return string The SQL.
*/
public function walkNullIfExpression($nullIfExpression)
{
$firstExpression = is_string($nullIfExpression->firstExpression)
? $this->_conn->quote($nullIfExpression->firstExpression)
: $this->walkSimpleArithmeticExpression($nullIfExpression->firstExpression);
$secondExpression = is_string($nullIfExpression->secondExpression)
? $this->_conn->quote($nullIfExpression->secondExpression)
: $this->walkSimpleArithmeticExpression($nullIfExpression->secondExpression);
return 'NULLIF(' . $firstExpression . ', ' . $secondExpression . ')';
}
/** /**
* Walks down a SelectExpression AST node and generates the corresponding SQL. * Walks down a SelectExpression AST node and generates the corresponding SQL.
* *
@ -956,8 +1010,7 @@ class SqlWalker implements TreeWalker
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias); $this->_rsm->addScalarResult($columnAlias, $resultAlias);
} } else if (
else if (
$expr instanceof AST\SimpleArithmeticExpression || $expr instanceof AST\SimpleArithmeticExpression ||
$expr instanceof AST\ArithmeticTerm || $expr instanceof AST\ArithmeticTerm ||
$expr instanceof AST\ArithmeticFactor || $expr instanceof AST\ArithmeticFactor ||
@ -971,11 +1024,32 @@ class SqlWalker implements TreeWalker
} }
$columnAlias = 'sclr' . $this->_aliasCounter++; $columnAlias = 'sclr' . $this->_aliasCounter++;
if ($expr instanceof AST\Literal) { if ($expr instanceof AST\Literal) {
$sql .= $this->walkLiteral($expr) . ' AS ' .$columnAlias; $sql .= $this->walkLiteral($expr) . ' AS ' .$columnAlias;
} else { } else {
$sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias; $sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias;
} }
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias);
$this->_rsm->addScalarResult($columnAlias, $resultAlias);
} else if (
$expr instanceof AST\NullIfExpression ||
$expr instanceof AST\CoalesceExpression ||
$expr instanceof AST\CaseExpression
) {
if ( ! $selectExpression->fieldIdentificationVariable) {
$resultAlias = $this->_scalarResultCounter++;
} else {
$resultAlias = $selectExpression->fieldIdentificationVariable;
}
$columnAlias = 'sclr' . $this->_aliasCounter++;
$sql .= $this->walkCaseExpression($expr) . ' AS ' . $columnAlias;
$this->_scalarResultAliasMap[$resultAlias] = $columnAlias; $this->_scalarResultAliasMap[$resultAlias] = $columnAlias;
$columnAlias = $this->_platform->getSQLResultCasing($columnAlias); $columnAlias = $this->_platform->getSQLResultCasing($columnAlias);

View File

@ -570,6 +570,16 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
{ {
$this->assertValidDQL("SELECT e, t FROM Doctrine\Tests\Models\DDC117\DDC117Editor e JOIN e.reviewingTranslations t WHERE SIZE(e.reviewingTranslations) > 0"); $this->assertValidDQL("SELECT e, t FROM Doctrine\Tests\Models\DDC117\DDC117Editor e JOIN e.reviewingTranslations t WHERE SIZE(e.reviewingTranslations) > 0");
} }
public function testCaseSupportContainingNullIfExpression()
{
$this->assertValidDQL("SELECT u.id, NULLIF(u.name, u.name) AS shouldBeNull FROM Doctrine\Tests\Models\CMS\CmsUser u");
}
public function testCaseSupportContainingCoalesceExpression()
{
$this->assertValidDQL("select COALESCE(NULLIF(u.name, ''), u.username) as Display FROM Doctrine\Tests\Models\CMS\CmsUser u");
}
} }
/** @Entity */ /** @Entity */

View File

@ -906,6 +906,22 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
'SELECT c0_.id AS id0, c0_.name AS name1, count(c1_.id) AS sclr2 FROM cms_groups c0_ INNER JOIN cms_users_groups c2_ ON c0_.id = c2_.group_id INNER JOIN cms_users c1_ ON c1_.id = c2_.user_id GROUP BY c0_.id' 'SELECT c0_.id AS id0, c0_.name AS name1, count(c1_.id) AS sclr2 FROM cms_groups c0_ INNER JOIN cms_users_groups c2_ ON c0_.id = c2_.group_id INNER JOIN cms_users c1_ ON c1_.id = c2_.user_id GROUP BY c0_.id'
); );
} }
public function testCaseContainingNullIf()
{
$this->assertSqlGeneration(
"SELECT NULLIF(g.id, g.name) AS NullIfEqual FROM Doctrine\Tests\Models\CMS\CmsGroup g",
'SELECT NULLIF(c0_.id, c0_.name) AS sclr0 FROM cms_groups c0_'
);
}
public function testCaseContainingCoalesce()
{
$this->assertSqlGeneration(
"SELECT COALESCE(NULLIF(u.name, ''), u.username) as Display FROM Doctrine\Tests\Models\CMS\CmsUser u",
"SELECT COALESCE(NULLIF(c0_.name, ''), c0_.username) AS sclr0 FROM cms_users c0_"
);
}
} }