Merge pull request #6500 from SamKnows/feature/override-aggregate-functions
Allow internal functions to be overridden.
This commit is contained in:
commit
256091282e
14
UPGRADE.md
14
UPGRADE.md
@ -1,3 +1,17 @@
|
|||||||
|
# Upgrade to 2.6
|
||||||
|
|
||||||
|
## Minor BC BREAK: removed `Doctrine\ORM\Query\Parser#isInternalFunction`
|
||||||
|
|
||||||
|
Method `Doctrine\ORM\Query\Parser#isInternalFunction` was removed because
|
||||||
|
the distinction between internal function and user defined DQL was removed.
|
||||||
|
[#6500](https://github.com/doctrine/doctrine2/pull/6500)
|
||||||
|
|
||||||
|
## Minor BC BREAK: removed `Doctrine\ORM\ORMException#overwriteInternalDQLFunctionNotAllowed`
|
||||||
|
|
||||||
|
Method `Doctrine\ORM\Query\Parser#overwriteInternalDQLFunctionNotAllowed` was
|
||||||
|
removed because of the choice to allow users to overwrite internal functions, ie
|
||||||
|
`AVG`, `SUM`, `COUNT`, `MIN` and `MAX`. [#6500](https://github.com/doctrine/doctrine2/pull/6500)
|
||||||
|
|
||||||
# Upgrade to 2.5
|
# Upgrade to 2.5
|
||||||
|
|
||||||
## Minor BC BREAK: removed `Doctrine\ORM\Query\SqlWalker#walkCaseExpression()`
|
## Minor BC BREAK: removed `Doctrine\ORM\Query\SqlWalker#walkCaseExpression()`
|
||||||
|
@ -420,15 +420,9 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
|||||||
* @param string|callable $className Class name or a callable that returns the function.
|
* @param string|callable $className Class name or a callable that returns the function.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*
|
|
||||||
* @throws ORMException
|
|
||||||
*/
|
*/
|
||||||
public function addCustomStringFunction($name, $className)
|
public function addCustomStringFunction($name, $className)
|
||||||
{
|
{
|
||||||
if (Query\Parser::isInternalFunction($name)) {
|
|
||||||
throw ORMException::overwriteInternalDQLFunctionNotAllowed($name);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_attributes['customStringFunctions'][strtolower($name)] = $className;
|
$this->_attributes['customStringFunctions'][strtolower($name)] = $className;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,15 +472,9 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
|||||||
* @param string|callable $className Class name or a callable that returns the function.
|
* @param string|callable $className Class name or a callable that returns the function.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*
|
|
||||||
* @throws ORMException
|
|
||||||
*/
|
*/
|
||||||
public function addCustomNumericFunction($name, $className)
|
public function addCustomNumericFunction($name, $className)
|
||||||
{
|
{
|
||||||
if (Query\Parser::isInternalFunction($name)) {
|
|
||||||
throw ORMException::overwriteInternalDQLFunctionNotAllowed($name);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_attributes['customNumericFunctions'][strtolower($name)] = $className;
|
$this->_attributes['customNumericFunctions'][strtolower($name)] = $className;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,15 +524,9 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
|||||||
* @param string|callable $className Class name or a callable that returns the function.
|
* @param string|callable $className Class name or a callable that returns the function.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*
|
|
||||||
* @throws ORMException
|
|
||||||
*/
|
*/
|
||||||
public function addCustomDatetimeFunction($name, $className)
|
public function addCustomDatetimeFunction($name, $className)
|
||||||
{
|
{
|
||||||
if (Query\Parser::isInternalFunction($name)) {
|
|
||||||
throw ORMException::overwriteInternalDQLFunctionNotAllowed($name);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_attributes['customDatetimeFunctions'][strtolower($name)] = $className;
|
$this->_attributes['customDatetimeFunctions'][strtolower($name)] = $className;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,16 +323,6 @@ class ORMException extends Exception
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $functionName
|
|
||||||
*
|
|
||||||
* @return ORMException
|
|
||||||
*/
|
|
||||||
public static function overwriteInternalDQLFunctionNotAllowed($functionName)
|
|
||||||
{
|
|
||||||
return new self("It is not allowed to overwrite internal function '$functionName' in the DQL parser through user-defined functions.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return ORMException
|
* @return ORMException
|
||||||
*/
|
*/
|
||||||
|
54
lib/Doctrine/ORM/Query/AST/Functions/AvgFunction.php
Normal file
54
lib/Doctrine/ORM/Query/AST/Functions/AvgFunction.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many individuals
|
||||||
|
* and is licensed under the MIT license. For more information, see
|
||||||
|
* <http://www.doctrine-project.org>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Doctrine\ORM\Query\AST\Functions;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Query\Parser;
|
||||||
|
use Doctrine\ORM\Query\SqlWalker;
|
||||||
|
use Doctrine\ORM\Query\AST\AggregateExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "AVG" "(" ["DISTINCT"] StringPrimary ")"
|
||||||
|
*
|
||||||
|
* @since 2.6
|
||||||
|
* @author Mathew Davies <thepixeldeveloper@icloud.com>
|
||||||
|
*/
|
||||||
|
final class AvgFunction extends FunctionNode
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var AggregateExpression
|
||||||
|
*/
|
||||||
|
private $aggregateExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getSql(SqlWalker $sqlWalker): string
|
||||||
|
{
|
||||||
|
return $this->aggregateExpression->dispatch($sqlWalker);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function parse(Parser $parser): void
|
||||||
|
{
|
||||||
|
$this->aggregateExpression = $parser->AggregateExpression();
|
||||||
|
}
|
||||||
|
}
|
54
lib/Doctrine/ORM/Query/AST/Functions/CountFunction.php
Normal file
54
lib/Doctrine/ORM/Query/AST/Functions/CountFunction.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many individuals
|
||||||
|
* and is licensed under the MIT license. For more information, see
|
||||||
|
* <http://www.doctrine-project.org>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Doctrine\ORM\Query\AST\Functions;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Query\Parser;
|
||||||
|
use Doctrine\ORM\Query\SqlWalker;
|
||||||
|
use Doctrine\ORM\Query\AST\AggregateExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "COUNT" "(" ["DISTINCT"] StringPrimary ")"
|
||||||
|
*
|
||||||
|
* @since 2.6
|
||||||
|
* @author Mathew Davies <thepixeldeveloper@icloud.com>
|
||||||
|
*/
|
||||||
|
final class CountFunction extends FunctionNode
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var AggregateExpression
|
||||||
|
*/
|
||||||
|
private $aggregateExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getSql(SqlWalker $sqlWalker): string
|
||||||
|
{
|
||||||
|
return $this->aggregateExpression->dispatch($sqlWalker);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function parse(Parser $parser): void
|
||||||
|
{
|
||||||
|
$this->aggregateExpression = $parser->AggregateExpression();
|
||||||
|
}
|
||||||
|
}
|
54
lib/Doctrine/ORM/Query/AST/Functions/MaxFunction.php
Normal file
54
lib/Doctrine/ORM/Query/AST/Functions/MaxFunction.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many individuals
|
||||||
|
* and is licensed under the MIT license. For more information, see
|
||||||
|
* <http://www.doctrine-project.org>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Doctrine\ORM\Query\AST\Functions;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Query\Parser;
|
||||||
|
use Doctrine\ORM\Query\SqlWalker;
|
||||||
|
use Doctrine\ORM\Query\AST\AggregateExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "MAX" "(" ["DISTINCT"] StringPrimary ")"
|
||||||
|
*
|
||||||
|
* @since 2.6
|
||||||
|
* @author Mathew Davies <thepixeldeveloper@icloud.com>
|
||||||
|
*/
|
||||||
|
final class MaxFunction extends FunctionNode
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var AggregateExpression
|
||||||
|
*/
|
||||||
|
private $aggregateExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getSql(SqlWalker $sqlWalker): string
|
||||||
|
{
|
||||||
|
return $this->aggregateExpression->dispatch($sqlWalker);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function parse(Parser $parser): void
|
||||||
|
{
|
||||||
|
$this->aggregateExpression = $parser->AggregateExpression();
|
||||||
|
}
|
||||||
|
}
|
54
lib/Doctrine/ORM/Query/AST/Functions/MinFunction.php
Normal file
54
lib/Doctrine/ORM/Query/AST/Functions/MinFunction.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many individuals
|
||||||
|
* and is licensed under the MIT license. For more information, see
|
||||||
|
* <http://www.doctrine-project.org>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Doctrine\ORM\Query\AST\Functions;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Query\Parser;
|
||||||
|
use Doctrine\ORM\Query\SqlWalker;
|
||||||
|
use Doctrine\ORM\Query\AST\AggregateExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "MIN" "(" ["DISTINCT"] StringPrimary ")"
|
||||||
|
*
|
||||||
|
* @since 2.6
|
||||||
|
* @author Mathew Davies <thepixeldeveloper@icloud.com>
|
||||||
|
*/
|
||||||
|
final class MinFunction extends FunctionNode
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var AggregateExpression
|
||||||
|
*/
|
||||||
|
private $aggregateExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getSql(SqlWalker $sqlWalker): string
|
||||||
|
{
|
||||||
|
return $this->aggregateExpression->dispatch($sqlWalker);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function parse(Parser $parser): void
|
||||||
|
{
|
||||||
|
$this->aggregateExpression = $parser->AggregateExpression();
|
||||||
|
}
|
||||||
|
}
|
54
lib/Doctrine/ORM/Query/AST/Functions/SumFunction.php
Normal file
54
lib/Doctrine/ORM/Query/AST/Functions/SumFunction.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many individuals
|
||||||
|
* and is licensed under the MIT license. For more information, see
|
||||||
|
* <http://www.doctrine-project.org>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Doctrine\ORM\Query\AST\Functions;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Query\Parser;
|
||||||
|
use Doctrine\ORM\Query\SqlWalker;
|
||||||
|
use Doctrine\ORM\Query\AST\AggregateExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "SUM" "(" ["DISTINCT"] StringPrimary ")"
|
||||||
|
*
|
||||||
|
* @since 2.6
|
||||||
|
* @author Mathew Davies <thepixeldeveloper@icloud.com>
|
||||||
|
*/
|
||||||
|
final class SumFunction extends FunctionNode
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var AggregateExpression
|
||||||
|
*/
|
||||||
|
private $aggregateExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getSql(SqlWalker $sqlWalker): string
|
||||||
|
{
|
||||||
|
return $this->aggregateExpression->dispatch($sqlWalker);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function parse(Parser $parser): void
|
||||||
|
{
|
||||||
|
$this->aggregateExpression = $parser->AggregateExpression();
|
||||||
|
}
|
||||||
|
}
|
@ -65,6 +65,13 @@ class Parser
|
|||||||
'date_diff' => Functions\DateDiffFunction::class,
|
'date_diff' => Functions\DateDiffFunction::class,
|
||||||
'bit_and' => Functions\BitAndFunction::class,
|
'bit_and' => Functions\BitAndFunction::class,
|
||||||
'bit_or' => Functions\BitOrFunction::class,
|
'bit_or' => Functions\BitOrFunction::class,
|
||||||
|
|
||||||
|
// Aggregate functions
|
||||||
|
'min' => Functions\MinFunction::class,
|
||||||
|
'max' => Functions\MaxFunction::class,
|
||||||
|
'avg' => Functions\AvgFunction::class,
|
||||||
|
'sum' => Functions\SumFunction::class,
|
||||||
|
'count' => Functions\CountFunction::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,23 +178,6 @@ class Parser
|
|||||||
*/
|
*/
|
||||||
private $identVariableExpressions = [];
|
private $identVariableExpressions = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a function is internally defined. Used to prevent overwriting
|
|
||||||
* of built-in functions through user-defined functions.
|
|
||||||
*
|
|
||||||
* @param string $functionName
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
static public function isInternalFunction($functionName)
|
|
||||||
{
|
|
||||||
$functionName = strtolower($functionName);
|
|
||||||
|
|
||||||
return isset(self::$_STRING_FUNCTIONS[$functionName])
|
|
||||||
|| isset(self::$_DATETIME_FUNCTIONS[$functionName])
|
|
||||||
|| isset(self::$_NUMERIC_FUNCTIONS[$functionName]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new query parser object.
|
* Creates a new query parser object.
|
||||||
*
|
*
|
||||||
@ -1978,9 +1968,6 @@ class Parser
|
|||||||
// SUM(u.id) + COUNT(u.id)
|
// SUM(u.id) + COUNT(u.id)
|
||||||
return $this->SimpleArithmeticExpression();
|
return $this->SimpleArithmeticExpression();
|
||||||
|
|
||||||
case ($this->isAggregateFunction($this->lexer->lookahead['type'])):
|
|
||||||
return $this->AggregateExpression();
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// IDENTITY(u)
|
// IDENTITY(u)
|
||||||
return $this->FunctionDeclaration();
|
return $this->FunctionDeclaration();
|
||||||
@ -2209,11 +2196,6 @@ class Parser
|
|||||||
$expression = $this->ScalarExpression();
|
$expression = $this->ScalarExpression();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ($this->isAggregateFunction($lookaheadType)):
|
|
||||||
// COUNT(u.id)
|
|
||||||
$expression = $this->AggregateExpression();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// IDENTITY(u)
|
// IDENTITY(u)
|
||||||
$expression = $this->FunctionDeclaration();
|
$expression = $this->FunctionDeclaration();
|
||||||
@ -2858,10 +2840,6 @@ class Parser
|
|||||||
$peek = $this->lexer->glimpse();
|
$peek = $this->lexer->glimpse();
|
||||||
|
|
||||||
if ($peek['value'] == '(') {
|
if ($peek['value'] == '(') {
|
||||||
if ($this->isAggregateFunction($this->lexer->lookahead['type'])) {
|
|
||||||
return $this->AggregateExpression();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->FunctionDeclaration();
|
return $this->FunctionDeclaration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2932,11 +2910,6 @@ class Parser
|
|||||||
case Lexer::T_COALESCE:
|
case Lexer::T_COALESCE:
|
||||||
case Lexer::T_NULLIF:
|
case Lexer::T_NULLIF:
|
||||||
return $this->CaseExpression();
|
return $this->CaseExpression();
|
||||||
|
|
||||||
default:
|
|
||||||
if ($this->isAggregateFunction($lookaheadType)) {
|
|
||||||
return $this->AggregateExpression();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->syntaxError(
|
$this->syntaxError(
|
||||||
@ -3236,10 +3209,6 @@ class Parser
|
|||||||
$expr = $this->CoalesceExpression();
|
$expr = $this->CoalesceExpression();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case $this->isAggregateFunction($this->lexer->lookahead['type']):
|
|
||||||
$expr = $this->AggregateExpression();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case $this->isFunction():
|
case $this->isFunction():
|
||||||
$expr = $this->FunctionDeclaration();
|
$expr = $this->FunctionDeclaration();
|
||||||
break;
|
break;
|
||||||
@ -3376,8 +3345,13 @@ class Parser
|
|||||||
$token = $this->lexer->lookahead;
|
$token = $this->lexer->lookahead;
|
||||||
$funcName = strtolower($token['value']);
|
$funcName = strtolower($token['value']);
|
||||||
|
|
||||||
// Check for built-in functions first!
|
$customFunctionDeclaration = $this->CustomFunctionDeclaration();
|
||||||
|
|
||||||
|
// Check for custom functions functions first!
|
||||||
switch (true) {
|
switch (true) {
|
||||||
|
case $customFunctionDeclaration !== null:
|
||||||
|
return $customFunctionDeclaration;
|
||||||
|
|
||||||
case (isset(self::$_STRING_FUNCTIONS[$funcName])):
|
case (isset(self::$_STRING_FUNCTIONS[$funcName])):
|
||||||
return $this->FunctionsReturningStrings();
|
return $this->FunctionsReturningStrings();
|
||||||
|
|
||||||
@ -3388,7 +3362,7 @@ class Parser
|
|||||||
return $this->FunctionsReturningDatetime();
|
return $this->FunctionsReturningDatetime();
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return $this->CustomFunctionDeclaration();
|
$this->syntaxError('known function', $token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3416,7 +3390,7 @@ class Parser
|
|||||||
return $this->CustomFunctionsReturningDatetime();
|
return $this->CustomFunctionsReturningDatetime();
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$this->syntaxError('known function', $token);
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1579,12 +1579,6 @@ class SqlWalker implements TreeWalker
|
|||||||
$sql .= $this->walkPathExpression($expr);
|
$sql .= $this->walkPathExpression($expr);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ($expr instanceof AST\AggregateExpression):
|
|
||||||
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
|
|
||||||
|
|
||||||
$sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ($expr instanceof AST\Subselect):
|
case ($expr instanceof AST\Subselect):
|
||||||
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
|
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
|
||||||
|
|
||||||
|
@ -265,8 +265,6 @@ class ConfigurationTest extends TestCase
|
|||||||
$this->assertSame(null, $this->configuration->getCustomStringFunction('NonExistingFunction'));
|
$this->assertSame(null, $this->configuration->getCustomStringFunction('NonExistingFunction'));
|
||||||
$this->configuration->setCustomStringFunctions(['OtherFunctionName' => __CLASS__]);
|
$this->configuration->setCustomStringFunctions(['OtherFunctionName' => __CLASS__]);
|
||||||
$this->assertSame(__CLASS__, $this->configuration->getCustomStringFunction('OtherFunctionName'));
|
$this->assertSame(__CLASS__, $this->configuration->getCustomStringFunction('OtherFunctionName'));
|
||||||
$this->expectException(ORMException::class);
|
|
||||||
$this->configuration->addCustomStringFunction('concat', __CLASS__);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAddGetCustomNumericFunction()
|
public function testAddGetCustomNumericFunction()
|
||||||
@ -276,8 +274,6 @@ class ConfigurationTest extends TestCase
|
|||||||
$this->assertSame(null, $this->configuration->getCustomNumericFunction('NonExistingFunction'));
|
$this->assertSame(null, $this->configuration->getCustomNumericFunction('NonExistingFunction'));
|
||||||
$this->configuration->setCustomNumericFunctions(['OtherFunctionName' => __CLASS__]);
|
$this->configuration->setCustomNumericFunctions(['OtherFunctionName' => __CLASS__]);
|
||||||
$this->assertSame(__CLASS__, $this->configuration->getCustomNumericFunction('OtherFunctionName'));
|
$this->assertSame(__CLASS__, $this->configuration->getCustomNumericFunction('OtherFunctionName'));
|
||||||
$this->expectException(ORMException::class);
|
|
||||||
$this->configuration->addCustomNumericFunction('abs', __CLASS__);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAddGetCustomDatetimeFunction()
|
public function testAddGetCustomDatetimeFunction()
|
||||||
@ -287,8 +283,6 @@ class ConfigurationTest extends TestCase
|
|||||||
$this->assertSame(null, $this->configuration->getCustomDatetimeFunction('NonExistingFunction'));
|
$this->assertSame(null, $this->configuration->getCustomDatetimeFunction('NonExistingFunction'));
|
||||||
$this->configuration->setCustomDatetimeFunctions(['OtherFunctionName' => __CLASS__]);
|
$this->configuration->setCustomDatetimeFunctions(['OtherFunctionName' => __CLASS__]);
|
||||||
$this->assertSame(__CLASS__, $this->configuration->getCustomDatetimeFunction('OtherFunctionName'));
|
$this->assertSame(__CLASS__, $this->configuration->getCustomDatetimeFunction('OtherFunctionName'));
|
||||||
$this->expectException(ORMException::class);
|
|
||||||
$this->configuration->addCustomDatetimeFunction('date_add', __CLASS__);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAddGetCustomHydrationMode()
|
public function testAddGetCustomHydrationMode()
|
||||||
|
@ -47,6 +47,23 @@ class CustomFunctionsTest extends OrmFunctionalTestCase
|
|||||||
$this->assertEquals(1, count($users));
|
$this->assertEquals(1, count($users));
|
||||||
$this->assertSame($user, $users[0]);
|
$this->assertSame($user, $users[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCustomFunctionOverride()
|
||||||
|
{
|
||||||
|
$user = new CmsUser();
|
||||||
|
$user->name = 'Bob';
|
||||||
|
$user->username = 'Dylan';
|
||||||
|
$this->_em->persist($user);
|
||||||
|
$this->_em->flush();
|
||||||
|
|
||||||
|
$this->_em->getConfiguration()->addCustomStringFunction('COUNT', 'Doctrine\Tests\ORM\Functional\CustomCount');
|
||||||
|
|
||||||
|
$query = $this->_em->createQuery('SELECT COUNT(DISTINCT u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u');
|
||||||
|
|
||||||
|
$usersCount = $query->getSingleScalarResult();
|
||||||
|
|
||||||
|
$this->assertEquals(1, $usersCount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NoOp extends FunctionNode
|
class NoOp extends FunctionNode
|
||||||
@ -70,3 +87,20 @@ class NoOp extends FunctionNode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CustomCount extends FunctionNode
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Query\AST\AggregateExpression
|
||||||
|
*/
|
||||||
|
private $aggregateExpression;
|
||||||
|
|
||||||
|
public function parse(Parser $parser): void
|
||||||
|
{
|
||||||
|
$this->aggregateExpression = $parser->AggregateExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSql(SqlWalker $sqlWalker): string
|
||||||
|
{
|
||||||
|
return $this->aggregateExpression->dispatch($sqlWalker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1031,7 +1031,7 @@ class SelectSqlGenerationTest extends OrmTestCase
|
|||||||
{
|
{
|
||||||
$this->assertSqlGeneration(
|
$this->assertSqlGeneration(
|
||||||
"SELECT u.name, (SELECT COUNT(p.phonenumber) FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234) pcount FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'",
|
"SELECT u.name, (SELECT COUNT(p.phonenumber) FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234) pcount FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'",
|
||||||
"SELECT c0_.name AS name_0, (SELECT COUNT(c1_.phonenumber) AS dctrn__1 FROM cms_phonenumbers c1_ WHERE c1_.phonenumber = 1234) AS sclr_1 FROM cms_users c0_ WHERE c0_.name = 'jon'"
|
"SELECT c0_.name AS name_0, (SELECT COUNT(c1_.phonenumber) AS sclr_2 FROM cms_phonenumbers c1_ WHERE c1_.phonenumber = 1234) AS sclr_1 FROM cms_users c0_ WHERE c0_.name = 'jon'"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1198,7 +1198,7 @@ class SelectSqlGenerationTest extends OrmTestCase
|
|||||||
{
|
{
|
||||||
$this->assertSqlGeneration(
|
$this->assertSqlGeneration(
|
||||||
"SELECT u.name, (SELECT COUNT(cfc.id) total FROM Doctrine\Tests\Models\Company\CompanyFixContract cfc) as cfc_count FROM Doctrine\Tests\Models\CMS\CmsUser u",
|
"SELECT u.name, (SELECT COUNT(cfc.id) total FROM Doctrine\Tests\Models\Company\CompanyFixContract cfc) as cfc_count FROM Doctrine\Tests\Models\CMS\CmsUser u",
|
||||||
"SELECT c0_.name AS name_0, (SELECT COUNT(c1_.id) AS dctrn__total FROM company_contracts c1_ WHERE c1_.discr IN ('fix')) AS sclr_1 FROM cms_users c0_"
|
"SELECT c0_.name AS name_0, (SELECT COUNT(c1_.id) AS sclr_2 FROM company_contracts c1_ WHERE c1_.discr IN ('fix')) AS sclr_1 FROM cms_users c0_"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1750,7 +1750,7 @@ class SelectSqlGenerationTest extends OrmTestCase
|
|||||||
);
|
);
|
||||||
$this->assertSqlGeneration(
|
$this->assertSqlGeneration(
|
||||||
'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE COUNT(u1.id) = ( SELECT SUM(u2.id) FROM Doctrine\Tests\Models\CMS\CmsUser u2 )',
|
'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE COUNT(u1.id) = ( SELECT SUM(u2.id) FROM Doctrine\Tests\Models\CMS\CmsUser u2 )',
|
||||||
'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE COUNT(c0_.id) = (SELECT SUM(c1_.id) AS dctrn__1 FROM cms_users c1_)'
|
'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE COUNT(c0_.id) = (SELECT SUM(c1_.id) AS sclr_4 FROM cms_users c1_)'
|
||||||
);
|
);
|
||||||
$this->assertSqlGeneration(
|
$this->assertSqlGeneration(
|
||||||
'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE COUNT(u1.id) <= ( SELECT SUM(u2.id) + COUNT(u2.email) FROM Doctrine\Tests\Models\CMS\CmsUser u2 )',
|
'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE COUNT(u1.id) <= ( SELECT SUM(u2.id) + COUNT(u2.email) FROM Doctrine\Tests\Models\CMS\CmsUser u2 )',
|
||||||
|
@ -330,7 +330,7 @@ ORDER BY b.id DESC'
|
|||||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, b0_.author_id AS author_id_1, b0_.category_id AS category_id_2 FROM BlogPost b0_ WHERE ((SELECT COUNT(b1_.id) AS dctrn__1 FROM BlogPost b1_) = 1)) dctrn_result ORDER BY id_0 DESC',
|
'SELECT DISTINCT id_0 FROM (SELECT b0_.id AS id_0, b0_.author_id AS author_id_1, b0_.category_id AS category_id_2 FROM BlogPost b0_ WHERE ((SELECT COUNT(b1_.id) AS sclr_3 FROM BlogPost b1_) = 1)) dctrn_result ORDER BY id_0 DESC',
|
||||||
$query->getSQL()
|
$query->getSQL()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -346,7 +346,7 @@ ORDER BY b.id DESC'
|
|||||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'SELECT DISTINCT id_0, MIN(sclr_1) AS dctrn_minrownum FROM (SELECT b0_.id AS id_0, ROW_NUMBER() OVER(ORDER BY b0_.id DESC) AS sclr_1, b0_.author_id AS author_id_2, b0_.category_id AS category_id_3 FROM BlogPost b0_ WHERE ((SELECT COUNT(b1_.id) AS dctrn__1 FROM BlogPost b1_) = 1)) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC',
|
'SELECT DISTINCT id_0, MIN(sclr_1) AS dctrn_minrownum FROM (SELECT b0_.id AS id_0, ROW_NUMBER() OVER(ORDER BY b0_.id DESC) AS sclr_1, b0_.author_id AS author_id_2, b0_.category_id AS category_id_3 FROM BlogPost b0_ WHERE ((SELECT COUNT(b1_.id) AS sclr_4 FROM BlogPost b1_) = 1)) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC',
|
||||||
$query->getSQL()
|
$query->getSQL()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -390,7 +390,7 @@ ORDER BY b.id DESC'
|
|||||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'SELECT DISTINCT id_0 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, (SELECT MIN(m1_.title) AS dctrn__1 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS sclr_2 FROM Author a0_) dctrn_result ORDER BY sclr_2 DESC',
|
'SELECT DISTINCT id_0 FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, (SELECT MIN(m1_.title) AS sclr_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS sclr_2 FROM Author a0_) dctrn_result ORDER BY sclr_2 DESC',
|
||||||
$query->getSQL()
|
$query->getSQL()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -415,7 +415,7 @@ ORDER BY b.id DESC'
|
|||||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'SELECT DISTINCT id_0, MIN(sclr_3) AS dctrn_minrownum FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, (SELECT MIN(m1_.title) AS dctrn__1 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS sclr_2, ROW_NUMBER() OVER(ORDER BY (SELECT MIN(m1_.title) AS dctrn__2 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) DESC) AS sclr_3 FROM Author a0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC',
|
'SELECT DISTINCT id_0, MIN(sclr_4) AS dctrn_minrownum FROM (SELECT a0_.id AS id_0, a0_.name AS name_1, (SELECT MIN(m1_.title) AS sclr_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS sclr_2, ROW_NUMBER() OVER(ORDER BY (SELECT MIN(m1_.title) AS sclr_5 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) DESC) AS sclr_4 FROM Author a0_) dctrn_result GROUP BY id_0 ORDER BY dctrn_minrownum ASC',
|
||||||
$query->getSQL()
|
$query->getSQL()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -440,7 +440,7 @@ ORDER BY b.id DESC'
|
|||||||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class);
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'SELECT DISTINCT ID_0, MIN(SCLR_3) AS dctrn_minrownum FROM (SELECT a0_.id AS ID_0, a0_.name AS NAME_1, (SELECT MIN(m1_.title) AS dctrn__1 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS SCLR_2, ROW_NUMBER() OVER(ORDER BY (SELECT MIN(m1_.title) AS dctrn__2 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) DESC) AS SCLR_3 FROM Author a0_) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC',
|
'SELECT DISTINCT ID_0, MIN(SCLR_4) AS dctrn_minrownum FROM (SELECT a0_.id AS ID_0, a0_.name AS NAME_1, (SELECT MIN(m1_.title) AS SCLR_3 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) AS SCLR_2, ROW_NUMBER() OVER(ORDER BY (SELECT MIN(m1_.title) AS SCLR_5 FROM MyBlogPost m1_ WHERE m1_.author_id = a0_.id) DESC) AS SCLR_4 FROM Author a0_) dctrn_result GROUP BY ID_0 ORDER BY dctrn_minrownum ASC',
|
||||||
$query->getSQL()
|
$query->getSQL()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user