welcome back to dyb-tech

This commit is contained in:
Daniel Guzman
2024-05-18 02:28:01 +02:00
parent 9513cdba09
commit 9f30bc98c7
6149 changed files with 668407 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
use Doctrine\ORM\Query\QueryException;
use function get_debug_type;
/**
* Base exception class for AST exceptions.
*/
class ASTException extends QueryException
{
/**
* @param Node $node
*
* @return ASTException
*/
public static function noDispatchForNode($node)
{
return new self('Double-dispatch for node ' . get_debug_type($node) . ' is not supported.');
}
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
class AggregateExpression extends Node
{
/** @var string */
public $functionName;
/** @var PathExpression|SimpleArithmeticExpression */
public $pathExpression;
/**
* Some aggregate expressions support distinct, eg COUNT.
*
* @var bool
*/
public $isDistinct = false;
/**
* @param string $functionName
* @param PathExpression|SimpleArithmeticExpression $pathExpression
* @param bool $isDistinct
*/
public function __construct($functionName, $pathExpression, $isDistinct)
{
$this->functionName = $functionName;
$this->pathExpression = $pathExpression;
$this->isDistinct = $isDistinct;
}
/**
* {@inheritDoc}
*/
public function dispatch($walker)
{
return $walker->walkAggregateExpression($this);
}
}
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* ArithmeticExpression ::= SimpleArithmeticExpression | "(" Subselect ")"
*
* @link www.doctrine-project.org
*/
class ArithmeticExpression extends Node
{
/** @var SimpleArithmeticExpression|null */
public $simpleArithmeticExpression;
/** @var Subselect|null */
public $subselect;
/** @return bool */
public function isSimpleArithmeticExpression()
{
return (bool) $this->simpleArithmeticExpression;
}
/** @return bool */
public function isSubselect()
{
return (bool) $this->subselect;
}
/**
* {@inheritDoc}
*/
public function dispatch($walker)
{
return $walker->walkArithmeticExpression($this);
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* ArithmeticFactor ::= [("+" | "-")] ArithmeticPrimary
*
* @link www.doctrine-project.org
*/
class ArithmeticFactor extends Node
{
/** @var mixed */
public $arithmeticPrimary;
/**
* NULL represents no sign, TRUE means positive and FALSE means negative sign.
*
* @var bool|null
*/
public $sign;
/**
* @param mixed $arithmeticPrimary
* @param bool|null $sign
*/
public function __construct($arithmeticPrimary, $sign = null)
{
$this->arithmeticPrimary = $arithmeticPrimary;
$this->sign = $sign;
}
/** @return bool */
public function isPositiveSigned()
{
return $this->sign === true;
}
/** @return bool */
public function isNegativeSigned()
{
return $this->sign === false;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkArithmeticFactor($this);
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* ArithmeticTerm ::= ArithmeticFactor {("*" | "/") ArithmeticFactor}*
*
* @link www.doctrine-project.org
*/
class ArithmeticTerm extends Node
{
/** @var mixed[] */
public $arithmeticFactors;
/** @param mixed[] $arithmeticFactors */
public function __construct(array $arithmeticFactors)
{
$this->arithmeticFactors = $arithmeticFactors;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkArithmeticTerm($this);
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
class BetweenExpression extends Node
{
/** @var ArithmeticExpression */
public $expression;
/** @var ArithmeticExpression */
public $leftBetweenExpression;
/** @var ArithmeticExpression */
public $rightBetweenExpression;
/** @var bool */
public $not;
/**
* @param ArithmeticExpression $expr
* @param ArithmeticExpression $leftExpr
* @param ArithmeticExpression $rightExpr
*/
public function __construct($expr, $leftExpr, $rightExpr, bool $not = false)
{
$this->expression = $expr;
$this->leftBetweenExpression = $leftExpr;
$this->rightBetweenExpression = $rightExpr;
$this->not = $not;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkBetweenExpression($this);
}
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* CoalesceExpression ::= "COALESCE" "(" ScalarExpression {"," ScalarExpression}* ")"
*
* @link www.doctrine-project.org
*/
class CoalesceExpression extends Node
{
/** @var mixed[] */
public $scalarExpressions = [];
/** @param mixed[] $scalarExpressions */
public function __construct(array $scalarExpressions)
{
$this->scalarExpressions = $scalarExpressions;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkCoalesceExpression($this);
}
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* CollectionMemberExpression ::= EntityExpression ["NOT"] "MEMBER" ["OF"] CollectionValuedPathExpression
*
* @link www.doctrine-project.org
*/
class CollectionMemberExpression extends Node
{
/** @var mixed */
public $entityExpression;
/** @var PathExpression */
public $collectionValuedPathExpression;
/** @var bool */
public $not;
/**
* @param mixed $entityExpr
* @param PathExpression $collValuedPathExpr
*/
public function __construct($entityExpr, $collValuedPathExpr, bool $not = false)
{
$this->entityExpression = $entityExpr;
$this->collectionValuedPathExpression = $collValuedPathExpr;
$this->not = $not;
}
/**
* {@inheritDoc}
*/
public function dispatch($walker)
{
return $walker->walkCollectionMemberExpression($this);
}
}
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* ComparisonExpression ::= ArithmeticExpression ComparisonOperator ( QuantifiedExpression | ArithmeticExpression ) |
* StringExpression ComparisonOperator (StringExpression | QuantifiedExpression) |
* BooleanExpression ("=" | "<>" | "!=") (BooleanExpression | QuantifiedExpression) |
* EnumExpression ("=" | "<>" | "!=") (EnumExpression | QuantifiedExpression) |
* DatetimeExpression ComparisonOperator (DatetimeExpression | QuantifiedExpression) |
* EntityExpression ("=" | "<>") (EntityExpression | QuantifiedExpression)
*
* @link www.doctrine-project.org
*/
class ComparisonExpression extends Node
{
/** @var Node|string */
public $leftExpression;
/** @var Node|string */
public $rightExpression;
/** @var string */
public $operator;
/**
* @param Node|string $leftExpr
* @param string $operator
* @param Node|string $rightExpr
*/
public function __construct($leftExpr, $operator, $rightExpr)
{
$this->leftExpression = $leftExpr;
$this->rightExpression = $rightExpr;
$this->operator = $operator;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkComparisonExpression($this);
}
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}*
*
* @link www.doctrine-project.org
*/
class ConditionalExpression extends Node
{
/** @var mixed[] */
public $conditionalTerms = [];
/** @param mixed[] $conditionalTerms */
public function __construct(array $conditionalTerms)
{
$this->conditionalTerms = $conditionalTerms;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkConditionalExpression($this);
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* ConditionalFactor ::= ["NOT"] ConditionalPrimary
*
* @link www.doctrine-project.org
*/
class ConditionalFactor extends Node implements Phase2OptimizableConditional
{
/** @var bool */
public $not = false;
/** @var ConditionalPrimary */
public $conditionalPrimary;
/** @param ConditionalPrimary $conditionalPrimary */
public function __construct($conditionalPrimary, bool $not = false)
{
$this->conditionalPrimary = $conditionalPrimary;
$this->not = $not;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkConditionalFactor($this);
}
}
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")"
*
* @link www.doctrine-project.org
*/
class ConditionalPrimary extends Node implements Phase2OptimizableConditional
{
/** @var Node|null */
public $simpleConditionalExpression;
/** @var ConditionalExpression|Phase2OptimizableConditional|null */
public $conditionalExpression;
/** @return bool */
public function isSimpleConditionalExpression()
{
return (bool) $this->simpleConditionalExpression;
}
/** @return bool */
public function isConditionalExpression()
{
return (bool) $this->conditionalExpression;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkConditionalPrimary($this);
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}*
*
* @link www.doctrine-project.org
*/
class ConditionalTerm extends Node implements Phase2OptimizableConditional
{
/** @var mixed[] */
public $conditionalFactors = [];
/** @param mixed[] $conditionalFactors */
public function __construct(array $conditionalFactors)
{
$this->conditionalFactors = $conditionalFactors;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkConditionalTerm($this);
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName [["AS"] AliasIdentificationVariable]
*
* @link www.doctrine-project.org
*/
class DeleteClause extends Node
{
/** @var string */
public $abstractSchemaName;
/** @var string */
public $aliasIdentificationVariable;
/** @param string $abstractSchemaName */
public function __construct($abstractSchemaName)
{
$this->abstractSchemaName = $abstractSchemaName;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkDeleteClause($this);
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* DeleteStatement = DeleteClause [WhereClause]
*
* @link www.doctrine-project.org
*/
class DeleteStatement extends Node
{
/** @var DeleteClause */
public $deleteClause;
/** @var WhereClause|null */
public $whereClause;
/** @param DeleteClause $deleteClause */
public function __construct($deleteClause)
{
$this->deleteClause = $deleteClause;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkDeleteStatement($this);
}
}
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY"
*
* @link www.doctrine-project.org
*/
class EmptyCollectionComparisonExpression extends Node
{
/** @var PathExpression */
public $expression;
/** @var bool */
public $not;
/** @param PathExpression $expression */
public function __construct($expression, bool $not = false)
{
$this->expression = $expression;
$this->not = $not;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkEmptyCollectionComparisonExpression($this);
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* ExistsExpression ::= ["NOT"] "EXISTS" "(" Subselect ")"
*
* @link www.doctrine-project.org
*/
class ExistsExpression extends Node
{
/** @var bool */
public $not;
/** @var Subselect */
public $subselect;
/** @param Subselect $subselect */
public function __construct($subselect, bool $not = false)
{
$this->subselect = $subselect;
$this->not = $not;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkExistsExpression($this);
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}
*
* @link www.doctrine-project.org
*/
class FromClause extends Node
{
/** @var mixed[] */
public $identificationVariableDeclarations = [];
/** @param mixed[] $identificationVariableDeclarations */
public function __construct(array $identificationVariableDeclarations)
{
$this->identificationVariableDeclarations = $identificationVariableDeclarations;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkFromClause($this);
}
}
@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "ABS" "(" SimpleArithmeticExpression ")"
*
* @link www.doctrine-project.org
*/
class AbsFunction extends FunctionNode
{
/** @var SimpleArithmeticExpression */
public $simpleArithmeticExpression;
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
return 'ABS(' . $sqlWalker->walkSimpleArithmeticExpression(
$this->simpleArithmeticExpression
) . ')';
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\AggregateExpression;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "AVG" "(" ["DISTINCT"] StringPrimary ")"
*/
final class AvgFunction extends FunctionNode
{
/** @var AggregateExpression */
private $aggregateExpression;
public function getSql(SqlWalker $sqlWalker): string
{
return $this->aggregateExpression->dispatch($sqlWalker);
}
public function parse(Parser $parser): void
{
$this->aggregateExpression = $parser->AggregateExpression();
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "BIT_AND" "(" ArithmeticPrimary "," ArithmeticPrimary ")"
*
* @link www.doctrine-project.org
*/
class BitAndFunction extends FunctionNode
{
/** @var Node */
public $firstArithmetic;
/** @var Node */
public $secondArithmetic;
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
$platform = $sqlWalker->getConnection()->getDatabasePlatform();
return $platform->getBitAndComparisonExpression(
$this->firstArithmetic->dispatch($sqlWalker),
$this->secondArithmetic->dispatch($sqlWalker)
);
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->firstArithmetic = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$this->secondArithmetic = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "BIT_OR" "(" ArithmeticPrimary "," ArithmeticPrimary ")"
*
* @link www.doctrine-project.org
*/
class BitOrFunction extends FunctionNode
{
/** @var Node */
public $firstArithmetic;
/** @var Node */
public $secondArithmetic;
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
$platform = $sqlWalker->getConnection()->getDatabasePlatform();
return $platform->getBitOrComparisonExpression(
$this->firstArithmetic->dispatch($sqlWalker),
$this->secondArithmetic->dispatch($sqlWalker)
);
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->firstArithmetic = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$this->secondArithmetic = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "CONCAT" "(" StringPrimary "," StringPrimary {"," StringPrimary }* ")"
*
* @link www.doctrine-project.org
*/
class ConcatFunction extends FunctionNode
{
/** @var Node */
public $firstStringPrimary;
/** @var Node */
public $secondStringPrimary;
/** @psalm-var list<Node> */
public $concatExpressions = [];
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
$platform = $sqlWalker->getConnection()->getDatabasePlatform();
$args = [];
foreach ($this->concatExpressions as $expression) {
$args[] = $sqlWalker->walkStringPrimary($expression);
}
return $platform->getConcatExpression(...$args);
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->firstStringPrimary = $parser->StringPrimary();
$this->concatExpressions[] = $this->firstStringPrimary;
$parser->match(Lexer::T_COMMA);
$this->secondStringPrimary = $parser->StringPrimary();
$this->concatExpressions[] = $this->secondStringPrimary;
while ($parser->getLexer()->isNextToken(Lexer::T_COMMA)) {
$parser->match(Lexer::T_COMMA);
$this->concatExpressions[] = $parser->StringPrimary();
}
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\AST\AggregateExpression;
use Doctrine\ORM\Query\AST\TypedExpression;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "COUNT" "(" ["DISTINCT"] StringPrimary ")"
*/
final class CountFunction extends FunctionNode implements TypedExpression
{
/** @var AggregateExpression */
private $aggregateExpression;
public function getSql(SqlWalker $sqlWalker): string
{
return $this->aggregateExpression->dispatch($sqlWalker);
}
public function parse(Parser $parser): void
{
$this->aggregateExpression = $parser->AggregateExpression();
}
public function getReturnType(): Type
{
return Type::getType(Types::INTEGER);
}
}
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "CURRENT_DATE"
*
* @link www.doctrine-project.org
*/
class CurrentDateFunction extends FunctionNode
{
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentDateSQL();
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "CURRENT_TIME"
*
* @link www.doctrine-project.org
*/
class CurrentTimeFunction extends FunctionNode
{
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentTimeSQL();
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "CURRENT_TIMESTAMP"
*
* @link www.doctrine-project.org
*/
class CurrentTimestampFunction extends FunctionNode
{
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
return $sqlWalker->getConnection()->getDatabasePlatform()->getCurrentTimestampSQL();
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\SqlWalker;
use function strtolower;
/**
* "DATE_ADD" "(" ArithmeticPrimary "," ArithmeticPrimary "," StringPrimary ")"
*
* @link www.doctrine-project.org
*/
class DateAddFunction extends FunctionNode
{
/** @var Node */
public $firstDateExpression = null;
/** @var Node */
public $intervalExpression = null;
/** @var Node */
public $unit = null;
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
switch (strtolower($this->unit->value)) {
case 'second':
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddSecondsExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
case 'minute':
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddMinutesExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
case 'hour':
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddHourExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
case 'day':
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddDaysExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
case 'week':
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddWeeksExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
case 'month':
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddMonthExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
case 'year':
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateAddYearsExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
default:
throw QueryException::semanticalError(
'DATE_ADD() only supports units of type second, minute, hour, day, week, month and year.'
);
}
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->firstDateExpression = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$this->intervalExpression = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$this->unit = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "DATE_DIFF" "(" ArithmeticPrimary "," ArithmeticPrimary ")"
*
* @link www.doctrine-project.org
*/
class DateDiffFunction extends FunctionNode
{
/** @var Node */
public $date1;
/** @var Node */
public $date2;
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateDiffExpression(
$this->date1->dispatch($sqlWalker),
$this->date2->dispatch($sqlWalker)
);
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->date1 = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$this->date2 = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\SqlWalker;
use function strtolower;
/**
* "DATE_SUB(date1, interval, unit)"
*
* @link www.doctrine-project.org
*/
class DateSubFunction extends DateAddFunction
{
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
switch (strtolower($this->unit->value)) {
case 'second':
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubSecondsExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
case 'minute':
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubMinutesExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
case 'hour':
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubHourExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
case 'day':
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubDaysExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
case 'week':
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubWeeksExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
case 'month':
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubMonthExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
case 'year':
return $sqlWalker->getConnection()->getDatabasePlatform()->getDateSubYearsExpression(
$this->firstDateExpression->dispatch($sqlWalker),
$this->intervalExpression->dispatch($sqlWalker)
);
default:
throw QueryException::semanticalError(
'DATE_SUB() only supports units of type second, minute, hour, day, week, month and year.'
);
}
}
}
@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* Abstract Function Node.
*
* @link www.doctrine-project.org
*
* @psalm-consistent-constructor
*/
abstract class FunctionNode extends Node
{
/** @var string */
public $name;
/** @param string $name */
public function __construct($name)
{
$this->name = $name;
}
/** @return string */
abstract public function getSql(SqlWalker $sqlWalker);
/**
* @param SqlWalker $sqlWalker
*
* @return string
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkFunction($this);
}
/** @return void */
abstract public function parse(Parser $parser);
}
@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\PathExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\SqlWalker;
use function assert;
use function reset;
use function sprintf;
/**
* "IDENTITY" "(" SingleValuedAssociationPathExpression {"," string} ")"
*
* @link www.doctrine-project.org
*/
class IdentityFunction extends FunctionNode
{
/** @var PathExpression */
public $pathExpression;
/** @var string|null */
public $fieldMapping;
/**
* {@inheritDoc}
*/
public function getSql(SqlWalker $sqlWalker)
{
assert($this->pathExpression->field !== null);
$entityManager = $sqlWalker->getEntityManager();
$platform = $entityManager->getConnection()->getDatabasePlatform();
$quoteStrategy = $entityManager->getConfiguration()->getQuoteStrategy();
$dqlAlias = $this->pathExpression->identificationVariable;
$assocField = $this->pathExpression->field;
$assoc = $sqlWalker->getMetadataForDqlAlias($dqlAlias)->associationMappings[$assocField];
$targetEntity = $entityManager->getClassMetadata($assoc['targetEntity']);
$joinColumn = reset($assoc['joinColumns']);
if ($this->fieldMapping !== null) {
if (! isset($targetEntity->fieldMappings[$this->fieldMapping])) {
throw new QueryException(sprintf('Undefined reference field mapping "%s"', $this->fieldMapping));
}
$field = $targetEntity->fieldMappings[$this->fieldMapping];
$joinColumn = null;
foreach ($assoc['joinColumns'] as $mapping) {
if ($mapping['referencedColumnName'] === $field['columnName']) {
$joinColumn = $mapping;
break;
}
}
if ($joinColumn === null) {
throw new QueryException(sprintf('Unable to resolve the reference field mapping "%s"', $this->fieldMapping));
}
}
// The table with the relation may be a subclass, so get the table name from the association definition
$tableName = $entityManager->getClassMetadata($assoc['sourceEntity'])->getTableName();
$tableAlias = $sqlWalker->getSQLTableAlias($tableName, $dqlAlias);
$columnName = $quoteStrategy->getJoinColumnName($joinColumn, $targetEntity, $platform);
return $tableAlias . '.' . $columnName;
}
/**
* {@inheritDoc}
*/
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->pathExpression = $parser->SingleValuedAssociationPathExpression();
if ($parser->getLexer()->isNextToken(Lexer::T_COMMA)) {
$parser->match(Lexer::T_COMMA);
$parser->match(Lexer::T_STRING);
$token = $parser->getLexer()->token;
assert($token !== null);
$this->fieldMapping = $token->value;
}
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\AST\TypedExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "LENGTH" "(" StringPrimary ")"
*
* @link www.doctrine-project.org
*/
class LengthFunction extends FunctionNode implements TypedExpression
{
/** @var Node */
public $stringPrimary;
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
return $sqlWalker->getConnection()->getDatabasePlatform()->getLengthExpression(
$sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary)
);
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->stringPrimary = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
public function getReturnType(): Type
{
return Type::getType(Types::INTEGER);
}
}
@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")"
*
* @link www.doctrine-project.org
*/
class LocateFunction extends FunctionNode
{
/** @var Node */
public $firstStringPrimary;
/** @var Node */
public $secondStringPrimary;
/** @var SimpleArithmeticExpression|bool */
public $simpleArithmeticExpression = false;
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
$platform = $sqlWalker->getConnection()->getDatabasePlatform();
$firstString = $sqlWalker->walkStringPrimary($this->firstStringPrimary);
$secondString = $sqlWalker->walkStringPrimary($this->secondStringPrimary);
if ($this->simpleArithmeticExpression) {
return $platform->getLocateExpression(
$secondString,
$firstString,
$sqlWalker->walkSimpleArithmeticExpression($this->simpleArithmeticExpression)
);
}
return $platform->getLocateExpression($secondString, $firstString);
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->firstStringPrimary = $parser->StringPrimary();
$parser->match(Lexer::T_COMMA);
$this->secondStringPrimary = $parser->StringPrimary();
$lexer = $parser->getLexer();
if ($lexer->isNextToken(Lexer::T_COMMA)) {
$parser->match(Lexer::T_COMMA);
$this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
}
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use function sprintf;
/**
* "LOWER" "(" StringPrimary ")"
*
* @link www.doctrine-project.org
*/
class LowerFunction extends FunctionNode
{
/** @var Node */
public $stringPrimary;
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
return sprintf(
'LOWER(%s)',
$sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary)
);
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->stringPrimary = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\AggregateExpression;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "MAX" "(" ["DISTINCT"] StringPrimary ")"
*/
final class MaxFunction extends FunctionNode
{
/** @var AggregateExpression */
private $aggregateExpression;
public function getSql(SqlWalker $sqlWalker): string
{
return $this->aggregateExpression->dispatch($sqlWalker);
}
public function parse(Parser $parser): void
{
$this->aggregateExpression = $parser->AggregateExpression();
}
}
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\AggregateExpression;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "MIN" "(" ["DISTINCT"] StringPrimary ")"
*/
final class MinFunction extends FunctionNode
{
/** @var AggregateExpression */
private $aggregateExpression;
public function getSql(SqlWalker $sqlWalker): string
{
return $this->aggregateExpression->dispatch($sqlWalker);
}
public function parse(Parser $parser): void
{
$this->aggregateExpression = $parser->AggregateExpression();
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")"
*
* @link www.doctrine-project.org
*/
class ModFunction extends FunctionNode
{
/** @var SimpleArithmeticExpression */
public $firstSimpleArithmeticExpression;
/** @var SimpleArithmeticExpression */
public $secondSimpleArithmeticExpression;
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
return $sqlWalker->getConnection()->getDatabasePlatform()->getModExpression(
$sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression),
$sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression)
);
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();
$parser->match(Lexer::T_COMMA);
$this->secondSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\AST\PathExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use function assert;
/**
* "SIZE" "(" CollectionValuedPathExpression ")"
*
* @link www.doctrine-project.org
*/
class SizeFunction extends FunctionNode
{
/** @var PathExpression */
public $collectionPathExpression;
/**
* @inheritDoc
* @todo If the collection being counted is already joined, the SQL can be simpler (more efficient).
*/
public function getSql(SqlWalker $sqlWalker)
{
assert($this->collectionPathExpression->field !== null);
$entityManager = $sqlWalker->getEntityManager();
$platform = $entityManager->getConnection()->getDatabasePlatform();
$quoteStrategy = $entityManager->getConfiguration()->getQuoteStrategy();
$dqlAlias = $this->collectionPathExpression->identificationVariable;
$assocField = $this->collectionPathExpression->field;
$class = $sqlWalker->getMetadataForDqlAlias($dqlAlias);
$assoc = $class->associationMappings[$assocField];
$sql = 'SELECT COUNT(*) FROM ';
if ($assoc['type'] === ClassMetadata::ONE_TO_MANY) {
$targetClass = $entityManager->getClassMetadata($assoc['targetEntity']);
$targetTableAlias = $sqlWalker->getSQLTableAlias($targetClass->getTableName());
$sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias);
$sql .= $quoteStrategy->getTableName($targetClass, $platform) . ' ' . $targetTableAlias . ' WHERE ';
$owningAssoc = $targetClass->associationMappings[$assoc['mappedBy']];
$first = true;
foreach ($owningAssoc['targetToSourceKeyColumns'] as $targetColumn => $sourceColumn) {
if ($first) {
$first = false;
} else {
$sql .= ' AND ';
}
$sql .= $targetTableAlias . '.' . $sourceColumn
. ' = '
. $sourceTableAlias . '.' . $quoteStrategy->getColumnName($class->fieldNames[$targetColumn], $class, $platform);
}
} else { // many-to-many
$targetClass = $entityManager->getClassMetadata($assoc['targetEntity']);
$owningAssoc = $assoc['isOwningSide'] ? $assoc : $targetClass->associationMappings[$assoc['mappedBy']];
$joinTable = $owningAssoc['joinTable'];
// SQL table aliases
$joinTableAlias = $sqlWalker->getSQLTableAlias($joinTable['name']);
$sourceTableAlias = $sqlWalker->getSQLTableAlias($class->getTableName(), $dqlAlias);
// join to target table
$sql .= $quoteStrategy->getJoinTableName($owningAssoc, $targetClass, $platform) . ' ' . $joinTableAlias . ' WHERE ';
$joinColumns = $assoc['isOwningSide']
? $joinTable['joinColumns']
: $joinTable['inverseJoinColumns'];
$first = true;
foreach ($joinColumns as $joinColumn) {
if ($first) {
$first = false;
} else {
$sql .= ' AND ';
}
$sourceColumnName = $quoteStrategy->getColumnName(
$class->fieldNames[$joinColumn['referencedColumnName']],
$class,
$platform
);
$sql .= $joinTableAlias . '.' . $joinColumn['name']
. ' = '
. $sourceTableAlias . '.' . $sourceColumnName;
}
}
return '(' . $sql . ')';
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->collectionPathExpression = $parser->CollectionValuedPathExpression();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use function sprintf;
/**
* "SQRT" "(" SimpleArithmeticExpression ")"
*
* @link www.doctrine-project.org
*/
class SqrtFunction extends FunctionNode
{
/** @var SimpleArithmeticExpression */
public $simpleArithmeticExpression;
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
return sprintf(
'SQRT(%s)',
$sqlWalker->walkSimpleArithmeticExpression($this->simpleArithmeticExpression)
);
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")"
*
* @link www.doctrine-project.org
*/
class SubstringFunction extends FunctionNode
{
/** @var Node */
public $stringPrimary;
/** @var SimpleArithmeticExpression */
public $firstSimpleArithmeticExpression;
/** @var SimpleArithmeticExpression|null */
public $secondSimpleArithmeticExpression = null;
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
$optionalSecondSimpleArithmeticExpression = null;
if ($this->secondSimpleArithmeticExpression !== null) {
$optionalSecondSimpleArithmeticExpression = $sqlWalker->walkSimpleArithmeticExpression($this->secondSimpleArithmeticExpression);
}
return $sqlWalker->getConnection()->getDatabasePlatform()->getSubstringExpression(
$sqlWalker->walkStringPrimary($this->stringPrimary),
$sqlWalker->walkSimpleArithmeticExpression($this->firstSimpleArithmeticExpression),
$optionalSecondSimpleArithmeticExpression
);
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->stringPrimary = $parser->StringPrimary();
$parser->match(Lexer::T_COMMA);
$this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();
$lexer = $parser->getLexer();
if ($lexer->isNextToken(Lexer::T_COMMA)) {
$parser->match(Lexer::T_COMMA);
$this->secondSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();
}
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\AggregateExpression;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
/**
* "SUM" "(" ["DISTINCT"] StringPrimary ")"
*/
final class SumFunction extends FunctionNode
{
/** @var AggregateExpression */
private $aggregateExpression;
public function getSql(SqlWalker $sqlWalker): string
{
return $this->aggregateExpression->dispatch($sqlWalker);
}
public function parse(Parser $parser): void
{
$this->aggregateExpression = $parser->AggregateExpression();
}
}
@@ -0,0 +1,134 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\DBAL\Platforms\TrimMode;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use function assert;
use function strcasecmp;
/**
* "TRIM" "(" [["LEADING" | "TRAILING" | "BOTH"] [char] "FROM"] StringPrimary ")"
*
* @link www.doctrine-project.org
*/
class TrimFunction extends FunctionNode
{
/** @var bool */
public $leading;
/** @var bool */
public $trailing;
/** @var bool */
public $both;
/** @var string|false */
public $trimChar = false;
/** @var Node */
public $stringPrimary;
/**
* {@inheritDoc}
*/
public function getSql(SqlWalker $sqlWalker)
{
$stringPrimary = $sqlWalker->walkStringPrimary($this->stringPrimary);
$platform = $sqlWalker->getConnection()->getDatabasePlatform();
$trimMode = $this->getTrimMode();
if ($this->trimChar !== false) {
return $platform->getTrimExpression(
$stringPrimary,
$trimMode,
$platform->quoteStringLiteral($this->trimChar)
);
}
return $platform->getTrimExpression($stringPrimary, $trimMode);
}
/**
* {@inheritDoc}
*/
public function parse(Parser $parser)
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->parseTrimMode($parser);
if ($lexer->isNextToken(Lexer::T_STRING)) {
$parser->match(Lexer::T_STRING);
assert($lexer->token !== null);
$this->trimChar = $lexer->token->value;
}
if ($this->leading || $this->trailing || $this->both || $this->trimChar) {
$parser->match(Lexer::T_FROM);
}
$this->stringPrimary = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
/** @psalm-return TrimMode::* */
private function getTrimMode(): int
{
if ($this->leading) {
return TrimMode::LEADING;
}
if ($this->trailing) {
return TrimMode::TRAILING;
}
if ($this->both) {
return TrimMode::BOTH;
}
return TrimMode::UNSPECIFIED;
}
private function parseTrimMode(Parser $parser): void
{
$lexer = $parser->getLexer();
assert($lexer->lookahead !== null);
$value = $lexer->lookahead->value;
if (strcasecmp('leading', $value) === 0) {
$parser->match(Lexer::T_LEADING);
$this->leading = true;
return;
}
if (strcasecmp('trailing', $value) === 0) {
$parser->match(Lexer::T_TRAILING);
$this->trailing = true;
return;
}
if (strcasecmp('both', $value) === 0) {
$parser->match(Lexer::T_BOTH);
$this->both = true;
return;
}
}
}
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use function sprintf;
/**
* "UPPER" "(" StringPrimary ")"
*
* @link www.doctrine-project.org
*/
class UpperFunction extends FunctionNode
{
/** @var Node */
public $stringPrimary;
/** @inheritDoc */
public function getSql(SqlWalker $sqlWalker)
{
return sprintf(
'UPPER(%s)',
$sqlWalker->walkSimpleArithmeticExpression($this->stringPrimary)
);
}
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->stringPrimary = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* GeneralCaseExpression ::= "CASE" WhenClause {WhenClause}* "ELSE" ScalarExpression "END"
*
* @link www.doctrine-project.org
*/
class GeneralCaseExpression extends Node
{
/** @var mixed[] */
public $whenClauses = [];
/** @var mixed */
public $elseScalarExpression = null;
/**
* @param mixed[] $whenClauses
* @param mixed $elseScalarExpression
*/
public function __construct(array $whenClauses, $elseScalarExpression)
{
$this->whenClauses = $whenClauses;
$this->elseScalarExpression = $elseScalarExpression;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkGeneralCaseExpression($this);
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
class GroupByClause extends Node
{
/** @var mixed[] */
public $groupByItems = [];
/** @param mixed[] $groupByItems */
public function __construct(array $groupByItems)
{
$this->groupByItems = $groupByItems;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkGroupByClause($this);
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
class HavingClause extends Node
{
/** @var ConditionalExpression|Phase2OptimizableConditional */
public $conditionalExpression;
/** @param ConditionalExpression|Phase2OptimizableConditional $conditionalExpression */
public function __construct($conditionalExpression)
{
$this->conditionalExpression = $conditionalExpression;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkHavingClause($this);
}
}
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}*
*
* @link www.doctrine-project.org
*/
class IdentificationVariableDeclaration extends Node
{
/** @var RangeVariableDeclaration|null */
public $rangeVariableDeclaration = null;
/** @var IndexBy|null */
public $indexBy = null;
/** @var mixed[] */
public $joins = [];
/**
* @param RangeVariableDeclaration|null $rangeVariableDecl
* @param IndexBy|null $indexBy
* @param mixed[] $joins
*/
public function __construct($rangeVariableDecl, $indexBy, array $joins)
{
$this->rangeVariableDeclaration = $rangeVariableDecl;
$this->indexBy = $indexBy;
$this->joins = $joins;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkIdentificationVariableDeclaration($this);
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
use Doctrine\Deprecations\Deprecation;
/**
* InExpression ::= ArithmeticExpression ["NOT"] "IN" "(" (Literal {"," Literal}* | Subselect) ")"
*
* @deprecated Use {@see InListExpression} or {@see InSubselectExpression} instead.
*/
class InExpression extends Node
{
/** @var bool */
public $not;
/** @var ArithmeticExpression */
public $expression;
/** @var mixed[] */
public $literals = [];
/** @var Subselect|null */
public $subselect;
/** @param ArithmeticExpression $expression */
public function __construct($expression)
{
if (! $this instanceof InListExpression && ! $this instanceof InSubselectExpression) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/10267',
'%s is deprecated, use %s or %s instead.',
self::class,
InListExpression::class,
InSubselectExpression::class
);
}
$this->expression = $expression;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
// We still call the deprecated method in order to not break existing custom SQL walkers.
return $sqlWalker->walkInExpression($this);
}
}
+20
View File
@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
class InListExpression extends InExpression
{
/** @var non-empty-list<mixed> */
public $literals;
/** @param non-empty-list<mixed> $literals */
public function __construct(ArithmeticExpression $expression, array $literals, bool $not = false)
{
$this->literals = $literals;
$this->not = $not;
parent::__construct($expression);
}
}
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
class InSubselectExpression extends InExpression
{
/** @var Subselect */
public $subselect;
public function __construct(ArithmeticExpression $expression, Subselect $subselect, bool $not = false)
{
$this->subselect = $subselect;
$this->not = $not;
parent::__construct($expression);
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* IndexBy ::= "INDEX" "BY" SingleValuedPathExpression
*
* @link www.doctrine-project.org
*/
class IndexBy extends Node
{
/** @var PathExpression */
public $singleValuedPathExpression = null;
/**
* @deprecated
*
* @var PathExpression
*/
public $simpleStateFieldPathExpression = null;
public function __construct(PathExpression $singleValuedPathExpression)
{
$this->singleValuedPathExpression = $this->simpleStateFieldPathExpression = $singleValuedPathExpression;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkIndexBy($this);
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
use Doctrine\ORM\Query\QueryException;
use function is_numeric;
use function strlen;
use function substr;
class InputParameter extends Node
{
/** @var bool */
public $isNamed;
/** @var string */
public $name;
/**
* @param string $value
*
* @throws QueryException
*/
public function __construct($value)
{
if (strlen($value) === 1) {
throw QueryException::invalidParameterFormat($value);
}
$param = substr($value, 1);
$this->isNamed = ! is_numeric($param);
$this->name = $param;
}
/**
* {@inheritDoc}
*/
public function dispatch($walker)
{
return $walker->walkInputParameter($this);
}
}
@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
use Doctrine\Deprecations\Deprecation;
use function func_num_args;
/**
* InstanceOfExpression ::= IdentificationVariable ["NOT"] "INSTANCE" ["OF"] (InstanceOfParameter | "(" InstanceOfParameter {"," InstanceOfParameter}* ")")
* InstanceOfParameter ::= AbstractSchemaName | InputParameter
*
* @link www.doctrine-project.org
*/
class InstanceOfExpression extends Node
{
/** @var bool */
public $not;
/** @var string */
public $identificationVariable;
/** @var non-empty-list<InputParameter|string> */
public $value;
/**
* @param string $identVariable
* @param non-empty-list<InputParameter|string> $value
*/
public function __construct($identVariable, array $value = [], bool $not = false)
{
if (func_num_args() < 2) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/10267',
'Not passing a value for $value to %s() is deprecated.',
__METHOD__
);
}
$this->identificationVariable = $identVariable;
$this->value = $value;
$this->not = $not;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkInstanceOfExpression($this);
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" JoinAssociationPathExpression
* ["AS"] AliasIdentificationVariable [("ON" | "WITH") ConditionalExpression]
*
* @link www.doctrine-project.org
*/
class Join extends Node
{
public const JOIN_TYPE_LEFT = 1;
public const JOIN_TYPE_LEFTOUTER = 2;
public const JOIN_TYPE_INNER = 3;
/**
* @var int
* @psalm-var self::JOIN_TYPE_*
*/
public $joinType = self::JOIN_TYPE_INNER;
/** @var Node|null */
public $joinAssociationDeclaration = null;
/** @var ConditionalExpression|Phase2OptimizableConditional|null */
public $conditionalExpression = null;
/**
* @param int $joinType
* @param Node $joinAssociationDeclaration
* @psalm-param self::JOIN_TYPE_* $joinType
*/
public function __construct($joinType, $joinAssociationDeclaration)
{
$this->joinType = $joinType;
$this->joinAssociationDeclaration = $joinAssociationDeclaration;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkJoin($this);
}
}
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable
*
* @link www.doctrine-project.org
*/
class JoinAssociationDeclaration extends Node
{
/** @var JoinAssociationPathExpression */
public $joinAssociationPathExpression;
/** @var string */
public $aliasIdentificationVariable;
/** @var IndexBy|null */
public $indexBy;
/**
* @param JoinAssociationPathExpression $joinAssociationPathExpression
* @param string $aliasIdentificationVariable
* @param IndexBy|null $indexBy
*/
public function __construct($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy)
{
$this->joinAssociationPathExpression = $joinAssociationPathExpression;
$this->aliasIdentificationVariable = $aliasIdentificationVariable;
$this->indexBy = $indexBy;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkJoinAssociationDeclaration($this);
}
}
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* JoinAssociationPathExpression ::= IdentificationVariable "." (SingleValuedAssociationField | CollectionValuedAssociationField)
*
* @link www.doctrine-project.org
*/
class JoinAssociationPathExpression extends Node
{
/** @var string */
public $identificationVariable;
/** @var string */
public $associationField;
/**
* @param string $identificationVariable
* @param string $associationField
*/
public function __construct($identificationVariable, $associationField)
{
$this->identificationVariable = $identificationVariable;
$this->associationField = $associationField;
}
}
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* JoinClassPathExpression ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
*
* @link www.doctrine-project.org
*/
class JoinClassPathExpression extends Node
{
/** @var mixed */
public $abstractSchemaName;
/** @var mixed */
public $aliasIdentificationVariable;
/**
* @param mixed $abstractSchemaName
* @param mixed $aliasIdentificationVar
*/
public function __construct($abstractSchemaName, $aliasIdentificationVar)
{
$this->abstractSchemaName = $abstractSchemaName;
$this->aliasIdentificationVariable = $aliasIdentificationVar;
}
/**
* {@inheritDoc}
*/
public function dispatch($walker)
{
return $walker->walkJoinPathExpression($this);
}
}
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* JoinVariableDeclaration ::= Join [IndexBy]
*
* @link www.doctrine-project.org
*/
class JoinVariableDeclaration extends Node
{
/** @var Join */
public $join;
/** @var IndexBy|null */
public $indexBy;
/**
* @param Join $join
* @param IndexBy|null $indexBy
*/
public function __construct($join, $indexBy)
{
$this->join = $join;
$this->indexBy = $indexBy;
}
/**
* {@inheritDoc}
*/
public function dispatch($walker)
{
return $walker->walkJoinVariableDeclaration($this);
}
}
+48
View File
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
/**
* LikeExpression ::= StringExpression ["NOT"] "LIKE" string ["ESCAPE" char]
*
* @link www.doctrine-project.org
*/
class LikeExpression extends Node
{
/** @var bool */
public $not = false;
/** @var Node|string */
public $stringExpression;
/** @var InputParameter|FunctionNode|PathExpression|Literal */
public $stringPattern;
/** @var Literal|null */
public $escapeChar;
/**
* @param Node|string $stringExpression
* @param InputParameter|FunctionNode|PathExpression|Literal $stringPattern
* @param Literal|null $escapeChar
*/
public function __construct($stringExpression, $stringPattern, $escapeChar = null, bool $not = false)
{
$this->stringExpression = $stringExpression;
$this->stringPattern = $stringPattern;
$this->escapeChar = $escapeChar;
$this->not = $not;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkLikeExpression($this);
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
class Literal extends Node
{
public const STRING = 1;
public const BOOLEAN = 2;
public const NUMERIC = 3;
/**
* @var int
* @psalm-var self::*
*/
public $type;
/** @var mixed */
public $value;
/**
* @param int $type
* @param mixed $value
* @psalm-param self::* $type
*/
public function __construct($type, $value)
{
$this->type = $type;
$this->value = $value;
}
/**
* {@inheritDoc}
*/
public function dispatch($walker)
{
return $walker->walkLiteral($this);
}
}
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* NewObjectExpression ::= "NEW" IdentificationVariable "(" NewObjectArg {"," NewObjectArg}* ")"
*
* @link www.doctrine-project.org
*/
class NewObjectExpression extends Node
{
/** @var string */
public $className;
/** @var mixed[] */
public $args;
/**
* @param string $className
* @param mixed[] $args
*/
public function __construct($className, array $args)
{
$this->className = $className;
$this->args = $args;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkNewObject($this);
}
}
+95
View File
@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
use Doctrine\ORM\Query\SqlWalker;
use function get_debug_type;
use function get_object_vars;
use function is_array;
use function is_object;
use function str_repeat;
use function var_export;
use const PHP_EOL;
/**
* Abstract class of an AST node.
*
* @link www.doctrine-project.org
*/
abstract class Node
{
/**
* Double-dispatch method, supposed to dispatch back to the walker.
*
* Implementation is not mandatory for all nodes.
*
* @param SqlWalker $walker
*
* @return string
*
* @throws ASTException
*/
public function dispatch($walker)
{
throw ASTException::noDispatchForNode($this);
}
/**
* Dumps the AST Node into a string representation for information purpose only.
*
* @return string
*/
public function __toString()
{
return $this->dump($this);
}
/**
* @param mixed $value
*
* @return string
*/
public function dump($value)
{
static $ident = 0;
$str = '';
if ($value instanceof Node) {
$str .= get_debug_type($value) . '(' . PHP_EOL;
$props = get_object_vars($value);
foreach ($props as $name => $prop) {
$ident += 4;
$str .= str_repeat(' ', $ident) . '"' . $name . '": '
. $this->dump($prop) . ',' . PHP_EOL;
$ident -= 4;
}
$str .= str_repeat(' ', $ident) . ')';
} elseif (is_array($value)) {
$ident += 4;
$str .= 'array(';
$some = false;
foreach ($value as $k => $v) {
$str .= PHP_EOL . str_repeat(' ', $ident) . '"'
. $k . '" => ' . $this->dump($v) . ',';
$some = true;
}
$ident -= 4;
$str .= ($some ? PHP_EOL . str_repeat(' ', $ident) : '') . ')';
} elseif (is_object($value)) {
$str .= 'instanceof(' . get_debug_type($value) . ')';
} else {
$str .= var_export($value, true);
}
return $str;
}
}
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* NullComparisonExpression ::= (SingleValuedPathExpression | InputParameter) "IS" ["NOT"] "NULL"
*
* @link www.doctrine-project.org
*/
class NullComparisonExpression extends Node
{
/** @var bool */
public $not;
/** @var Node */
public $expression;
/** @param Node $expression */
public function __construct($expression, bool $not = false)
{
$this->expression = $expression;
$this->not = $not;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkNullComparisonExpression($this);
}
}
+37
View File
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* NullIfExpression ::= "NULLIF" "(" ScalarExpression "," ScalarExpression ")"
*
* @link www.doctrine-project.org
*/
class NullIfExpression extends Node
{
/** @var mixed */
public $firstExpression;
/** @var mixed */
public $secondExpression;
/**
* @param mixed $firstExpression
* @param mixed $secondExpression
*/
public function __construct($firstExpression, $secondExpression)
{
$this->firstExpression = $firstExpression;
$this->secondExpression = $secondExpression;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkNullIfExpression($this);
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}*
*
* @link www.doctrine-project.org
*/
class OrderByClause extends Node
{
/** @var OrderByItem[] */
public $orderByItems = [];
/** @param OrderByItem[] $orderByItems */
public function __construct(array $orderByItems)
{
$this->orderByItems = $orderByItems;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkOrderByClause($this);
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
use function strtoupper;
/**
* OrderByItem ::= (ResultVariable | StateFieldPathExpression) ["ASC" | "DESC"]
*
* @link www.doctrine-project.org
*/
class OrderByItem extends Node
{
/** @var mixed */
public $expression;
/** @var string */
public $type;
/** @param mixed $expression */
public function __construct($expression)
{
$this->expression = $expression;
}
/** @return bool */
public function isAsc()
{
return strtoupper($this->type) === 'ASC';
}
/** @return bool */
public function isDesc()
{
return strtoupper($this->type) === 'DESC';
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkOrderByItem($this);
}
}
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* ParenthesisExpression ::= "(" ArithmeticPrimary ")"
*/
class ParenthesisExpression extends Node
{
/** @var Node */
public $expression;
public function __construct(Node $expression)
{
$this->expression = $expression;
}
/**
* {@inheritDoc}
*/
public function dispatch($walker)
{
return $walker->walkParenthesisExpression($this);
}
}
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
class PartialObjectExpression extends Node
{
/** @var string */
public $identificationVariable;
/** @var mixed[] */
public $partialFieldSet;
/**
* @param string $identificationVariable
* @param mixed[] $partialFieldSet
*/
public function __construct($identificationVariable, array $partialFieldSet)
{
$this->identificationVariable = $identificationVariable;
$this->partialFieldSet = $partialFieldSet;
}
}
+60
View File
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
* SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
* StateFieldPathExpression ::= SimpleStateFieldPathExpression | SimpleStateFieldAssociationPathExpression
* SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
* CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField
* StateField ::= {EmbeddedClassStateField "."}* SimpleStateField
* SimpleStateFieldPathExpression ::= IdentificationVariable "." StateField
*/
class PathExpression extends Node
{
public const TYPE_COLLECTION_VALUED_ASSOCIATION = 2;
public const TYPE_SINGLE_VALUED_ASSOCIATION = 4;
public const TYPE_STATE_FIELD = 8;
/**
* @var int|null
* @psalm-var self::TYPE_*|null
*/
public $type;
/**
* @var int
* @psalm-var int-mask-of<self::TYPE_*>
*/
public $expectedType;
/** @var string */
public $identificationVariable;
/** @var string|null */
public $field;
/**
* @param int $expectedType
* @param string $identificationVariable
* @param string|null $field
* @psalm-param int-mask-of<self::TYPE_*> $expectedType
*/
public function __construct($expectedType, $identificationVariable, $field = null)
{
$this->expectedType = $expectedType;
$this->identificationVariable = $identificationVariable;
$this->field = $field;
}
/**
* {@inheritDoc}
*/
public function dispatch($walker)
{
return $walker->walkPathExpression($this);
}
}
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* Marks types that can be used in place of a ConditionalExpression as a phase
* 2 optimization.
*
* @internal
*
* @psalm-inheritors ConditionalPrimary|ConditionalFactor|ConditionalTerm
*/
interface Phase2OptimizableConditional
{
}
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
use function strtoupper;
/**
* QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")"
*
* @link www.doctrine-project.org
*/
class QuantifiedExpression extends Node
{
/** @var string */
public $type;
/** @var Subselect */
public $subselect;
/** @param Subselect $subselect */
public function __construct($subselect)
{
$this->subselect = $subselect;
}
/** @return bool */
public function isAll()
{
return strtoupper($this->type) === 'ALL';
}
/** @return bool */
public function isAny()
{
return strtoupper($this->type) === 'ANY';
}
/** @return bool */
public function isSome()
{
return strtoupper($this->type) === 'SOME';
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkQuantifiedExpression($this);
}
}
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
*
* @link www.doctrine-project.org
*/
class RangeVariableDeclaration extends Node
{
/** @var string */
public $abstractSchemaName;
/** @var string */
public $aliasIdentificationVariable;
/** @var bool */
public $isRoot;
/**
* @param string $abstractSchemaName
* @param string $aliasIdentificationVar
* @param bool $isRoot
*/
public function __construct($abstractSchemaName, $aliasIdentificationVar, $isRoot = true)
{
$this->abstractSchemaName = $abstractSchemaName;
$this->aliasIdentificationVariable = $aliasIdentificationVar;
$this->isRoot = $isRoot;
}
/**
* {@inheritDoc}
*/
public function dispatch($walker)
{
return $walker->walkRangeVariableDeclaration($this);
}
}
+37
View File
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* SelectClause = "SELECT" ["DISTINCT"] SelectExpression {"," SelectExpression}
*
* @link www.doctrine-project.org
*/
class SelectClause extends Node
{
/** @var bool */
public $isDistinct;
/** @var mixed[] */
public $selectExpressions = [];
/**
* @param mixed[] $selectExpressions
* @param bool $isDistinct
*/
public function __construct(array $selectExpressions, $isDistinct)
{
$this->isDistinct = $isDistinct;
$this->selectExpressions = $selectExpressions;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSelectClause($this);
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* SelectExpression ::= IdentificationVariable ["." "*"] | StateFieldPathExpression |
* (AggregateExpression | "(" Subselect ")") [["AS"] ["HIDDEN"] FieldAliasIdentificationVariable]
*
* @link www.doctrine-project.org
*/
class SelectExpression extends Node
{
/** @var mixed */
public $expression;
/** @var string|null */
public $fieldIdentificationVariable;
/** @var bool */
public $hiddenAliasResultVariable;
/**
* @param mixed $expression
* @param string|null $fieldIdentificationVariable
* @param bool $hiddenAliasResultVariable
*/
public function __construct($expression, $fieldIdentificationVariable, $hiddenAliasResultVariable = false)
{
$this->expression = $expression;
$this->fieldIdentificationVariable = $fieldIdentificationVariable;
$this->hiddenAliasResultVariable = $hiddenAliasResultVariable;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSelectExpression($this);
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* SelectStatement = SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
*
* @link www.doctrine-project.org
*/
class SelectStatement extends Node
{
/** @var SelectClause */
public $selectClause;
/** @var FromClause */
public $fromClause;
/** @var WhereClause|null */
public $whereClause;
/** @var GroupByClause|null */
public $groupByClause;
/** @var HavingClause|null */
public $havingClause;
/** @var OrderByClause|null */
public $orderByClause;
/**
* @param SelectClause $selectClause
* @param FromClause $fromClause
*/
public function __construct($selectClause, $fromClause)
{
$this->selectClause = $selectClause;
$this->fromClause = $fromClause;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSelectStatement($this);
}
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* SimpleArithmeticExpression ::= ArithmeticTerm {("+" | "-") ArithmeticTerm}*
*
* @link www.doctrine-project.org
*/
class SimpleArithmeticExpression extends Node
{
/** @var mixed[] */
public $arithmeticTerms = [];
/** @param mixed[] $arithmeticTerms */
public function __construct(array $arithmeticTerms)
{
$this->arithmeticTerms = $arithmeticTerms;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSimpleArithmeticExpression($this);
}
}
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* SimpleCaseExpression ::= "CASE" CaseOperand SimpleWhenClause {SimpleWhenClause}* "ELSE" ScalarExpression "END"
*
* @link www.doctrine-project.org
*/
class SimpleCaseExpression extends Node
{
/** @var PathExpression */
public $caseOperand = null;
/** @var mixed[] */
public $simpleWhenClauses = [];
/** @var mixed */
public $elseScalarExpression = null;
/**
* @param PathExpression $caseOperand
* @param mixed[] $simpleWhenClauses
* @param mixed $elseScalarExpression
*/
public function __construct($caseOperand, array $simpleWhenClauses, $elseScalarExpression)
{
$this->caseOperand = $caseOperand;
$this->simpleWhenClauses = $simpleWhenClauses;
$this->elseScalarExpression = $elseScalarExpression;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSimpleCaseExpression($this);
}
}
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* SimpleSelectClause ::= "SELECT" ["DISTINCT"] SimpleSelectExpression
*
* @link www.doctrine-project.org
*/
class SimpleSelectClause extends Node
{
/** @var bool */
public $isDistinct = false;
/** @var SimpleSelectExpression */
public $simpleSelectExpression;
/**
* @param SimpleSelectExpression $simpleSelectExpression
* @param bool $isDistinct
*/
public function __construct($simpleSelectExpression, $isDistinct)
{
$this->simpleSelectExpression = $simpleSelectExpression;
$this->isDistinct = $isDistinct;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSimpleSelectClause($this);
}
}
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* SimpleSelectExpression ::= StateFieldPathExpression | IdentificationVariable
* | (AggregateExpression [["AS"] FieldAliasIdentificationVariable])
*
* @link www.doctrine-project.org
*/
class SimpleSelectExpression extends Node
{
/** @var Node|string */
public $expression;
/** @var string */
public $fieldIdentificationVariable;
/** @param Node|string $expression */
public function __construct($expression)
{
$this->expression = $expression;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSimpleSelectExpression($this);
}
}
+37
View File
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* SimpleWhenClause ::= "WHEN" ScalarExpression "THEN" ScalarExpression
*
* @link www.doctrine-project.org
*/
class SimpleWhenClause extends Node
{
/** @var mixed */
public $caseScalarExpression = null;
/** @var mixed */
public $thenScalarExpression = null;
/**
* @param mixed $caseScalarExpression
* @param mixed $thenScalarExpression
*/
public function __construct($caseScalarExpression, $thenScalarExpression)
{
$this->caseScalarExpression = $caseScalarExpression;
$this->thenScalarExpression = $thenScalarExpression;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkWhenClauseExpression($this);
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
*
* @link www.doctrine-project.org
*/
class Subselect extends Node
{
/** @var SimpleSelectClause */
public $simpleSelectClause;
/** @var SubselectFromClause */
public $subselectFromClause;
/** @var WhereClause|null */
public $whereClause;
/** @var GroupByClause|null */
public $groupByClause;
/** @var HavingClause|null */
public $havingClause;
/** @var OrderByClause|null */
public $orderByClause;
/**
* @param SimpleSelectClause $simpleSelectClause
* @param SubselectFromClause $subselectFromClause
*/
public function __construct($simpleSelectClause, $subselectFromClause)
{
$this->simpleSelectClause = $simpleSelectClause;
$this->subselectFromClause = $subselectFromClause;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSubselect($this);
}
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* SubselectFromClause ::= "FROM" SubselectIdentificationVariableDeclaration {"," SubselectIdentificationVariableDeclaration}*
*
* @link www.doctrine-project.org
*/
class SubselectFromClause extends Node
{
/** @var mixed[] */
public $identificationVariableDeclarations = [];
/** @param mixed[] $identificationVariableDeclarations */
public function __construct(array $identificationVariableDeclarations)
{
$this->identificationVariableDeclarations = $identificationVariableDeclarations;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkSubselectFromClause($this);
}
}
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* SubselectIdentificationVariableDeclaration ::= AssociationPathExpression ["AS"] AliasIdentificationVariable
*
* @link www.doctrine-project.org
*/
class SubselectIdentificationVariableDeclaration
{
/** @var PathExpression */
public $associationPathExpression;
/** @var string */
public $aliasIdentificationVariable;
/**
* @param PathExpression $associationPathExpression
* @param string $aliasIdentificationVariable
*/
public function __construct($associationPathExpression, $aliasIdentificationVariable)
{
$this->associationPathExpression = $associationPathExpression;
$this->aliasIdentificationVariable = $aliasIdentificationVariable;
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
use Doctrine\DBAL\Types\Type;
/**
* Provides an API for resolving the type of a Node
*/
interface TypedExpression
{
public function getReturnType(): Type;
}
+40
View File
@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* UpdateClause ::= "UPDATE" AbstractSchemaName [["AS"] AliasIdentificationVariable] "SET" UpdateItem {"," UpdateItem}*
*
* @link www.doctrine-project.org
*/
class UpdateClause extends Node
{
/** @var string */
public $abstractSchemaName;
/** @var string */
public $aliasIdentificationVariable;
/** @var mixed[] */
public $updateItems = [];
/**
* @param string $abstractSchemaName
* @param mixed[] $updateItems
*/
public function __construct($abstractSchemaName, array $updateItems)
{
$this->abstractSchemaName = $abstractSchemaName;
$this->updateItems = $updateItems;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkUpdateClause($this);
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* UpdateItem ::= [IdentificationVariable "."] {StateField | SingleValuedAssociationField} "=" NewValue
* NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary |
* EnumPrimary | SimpleEntityExpression | "NULL"
*
* @link www.doctrine-project.org
*/
class UpdateItem extends Node
{
/** @var PathExpression */
public $pathExpression;
/** @var InputParameter|ArithmeticExpression|null */
public $newValue;
/**
* @param PathExpression $pathExpression
* @param InputParameter|ArithmeticExpression|null $newValue
*/
public function __construct($pathExpression, $newValue)
{
$this->pathExpression = $pathExpression;
$this->newValue = $newValue;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkUpdateItem($this);
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* UpdateStatement = UpdateClause [WhereClause]
*
* @link www.doctrine-project.org
*/
class UpdateStatement extends Node
{
/** @var UpdateClause */
public $updateClause;
/** @var WhereClause|null */
public $whereClause;
/** @param UpdateClause $updateClause */
public function __construct($updateClause)
{
$this->updateClause = $updateClause;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkUpdateStatement($this);
}
}
+37
View File
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* WhenClause ::= "WHEN" ConditionalExpression "THEN" ScalarExpression
*
* @link www.doctrine-project.org
*/
class WhenClause extends Node
{
/** @var ConditionalExpression|Phase2OptimizableConditional */
public $caseConditionExpression;
/** @var mixed */
public $thenScalarExpression = null;
/**
* @param ConditionalExpression|Phase2OptimizableConditional $caseConditionExpression
* @param mixed $thenScalarExpression
*/
public function __construct($caseConditionExpression, $thenScalarExpression)
{
$this->caseConditionExpression = $caseConditionExpression;
$this->thenScalarExpression = $thenScalarExpression;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkWhenClauseExpression($this);
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\AST;
/**
* WhereClause ::= "WHERE" ConditionalExpression
*
* @link www.doctrine-project.org
*/
class WhereClause extends Node
{
/** @var ConditionalExpression|Phase2OptimizableConditional */
public $conditionalExpression;
/** @param ConditionalExpression|Phase2OptimizableConditional $conditionalExpression */
public function __construct($conditionalExpression)
{
$this->conditionalExpression = $conditionalExpression;
}
/**
* {@inheritDoc}
*/
public function dispatch($sqlWalker)
{
return $sqlWalker->walkWhereClause($this);
}
}
@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Result;
use Doctrine\DBAL\Types\Type;
use function array_diff;
use function array_keys;
use function array_map;
use function array_values;
use function str_replace;
/**
* Base class for SQL statement executors.
*
* @link http://www.doctrine-project.org
*
* @todo Rename: AbstractSQLExecutor
*/
abstract class AbstractSqlExecutor
{
/**
* @deprecated use $sqlStatements instead
*
* @var list<string>|string
*/
protected $_sqlStatements;
/** @var list<string>|string */
protected $sqlStatements;
/** @var QueryCacheProfile */
protected $queryCacheProfile;
public function __construct()
{
$this->_sqlStatements = &$this->sqlStatements;
}
/**
* Gets the SQL statements that are executed by the executor.
*
* @return list<string>|string All the SQL update statements.
*/
public function getSqlStatements()
{
return $this->sqlStatements;
}
public function setQueryCacheProfile(QueryCacheProfile $qcp): void
{
$this->queryCacheProfile = $qcp;
}
/**
* Do not use query cache
*/
public function removeQueryCacheProfile(): void
{
$this->queryCacheProfile = null;
}
/**
* Executes all sql statements.
*
* @param Connection $conn The database connection that is used to execute the queries.
* @param list<mixed>|array<string, mixed> $params The parameters.
* @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types The parameter types.
*
* @return Result|int
*/
abstract public function execute(Connection $conn, array $params, array $types);
/** @return list<string> */
public function __sleep(): array
{
/* Two reasons for this:
- we do not need to serialize the deprecated property, we can
rebuild the reference to the new property in __wakeup()
- not having the legacy property in the serialized data means the
serialized representation becomes compatible with 3.0.x, meaning
there will not be a deprecation warning about a missing property
when unserializing data */
return array_values(array_diff(array_map(static function (string $prop): string {
return str_replace("\0*\0", '', $prop);
}, array_keys((array) $this)), ['_sqlStatements']));
}
public function __wakeup(): void
{
if ($this->_sqlStatements !== null && $this->sqlStatements === null) {
$this->sqlStatements = $this->_sqlStatements;
}
$this->_sqlStatements = &$this->sqlStatements;
}
}
@@ -0,0 +1,138 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Query\AST;
use Doctrine\ORM\Query\AST\DeleteStatement;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Utility\PersisterHelper;
use Throwable;
use function array_merge;
use function array_reverse;
use function implode;
/**
* Executes the SQL statements for bulk DQL DELETE statements on classes in
* Class Table Inheritance (JOINED).
*
* @link http://www.doctrine-project.org
*/
class MultiTableDeleteExecutor extends AbstractSqlExecutor
{
/** @var string */
private $createTempTableSql;
/** @var string */
private $dropTempTableSql;
/** @var string */
private $insertSql;
/**
* Initializes a new <tt>MultiTableDeleteExecutor</tt>.
*
* Internal note: Any SQL construction and preparation takes place in the constructor for
* best performance. With a query cache the executor will be cached.
*
* @param DeleteStatement $AST The root AST node of the DQL query.
* @param SqlWalker $sqlWalker The walker used for SQL generation from the AST.
*/
public function __construct(AST\Node $AST, $sqlWalker)
{
parent::__construct();
$em = $sqlWalker->getEntityManager();
$conn = $em->getConnection();
$platform = $conn->getDatabasePlatform();
$quoteStrategy = $em->getConfiguration()->getQuoteStrategy();
if ($conn instanceof PrimaryReadReplicaConnection) {
$conn->ensureConnectedToPrimary();
}
$primaryClass = $em->getClassMetadata($AST->deleteClause->abstractSchemaName);
$primaryDqlAlias = $AST->deleteClause->aliasIdentificationVariable;
$rootClass = $em->getClassMetadata($primaryClass->rootEntityName);
$tempTable = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName());
$idColumnNames = $rootClass->getIdentifierColumnNames();
$idColumnList = implode(', ', $idColumnNames);
// 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause()
$sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $primaryDqlAlias);
$this->insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')'
. ' SELECT t0.' . implode(', t0.', $idColumnNames);
$rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $primaryDqlAlias);
$fromClause = new AST\FromClause([new AST\IdentificationVariableDeclaration($rangeDecl, null, [])]);
$this->insertSql .= $sqlWalker->walkFromClause($fromClause);
// Append WHERE clause, if there is one.
if ($AST->whereClause) {
$this->insertSql .= $sqlWalker->walkWhereClause($AST->whereClause);
}
// 2. Create ID subselect statement used in DELETE ... WHERE ... IN (subselect)
$idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable;
// 3. Create and store DELETE statements
$classNames = array_merge($primaryClass->parentClasses, [$primaryClass->name], $primaryClass->subClasses);
foreach (array_reverse($classNames) as $className) {
$tableName = $quoteStrategy->getTableName($em->getClassMetadata($className), $platform);
$this->sqlStatements[] = 'DELETE FROM ' . $tableName
. ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')';
}
// 4. Store DDL for temporary identifier table.
$columnDefinitions = [];
foreach ($idColumnNames as $idColumnName) {
$columnDefinitions[$idColumnName] = [
'notnull' => true,
'type' => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em)),
];
}
$this->createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
. $platform->getColumnDeclarationListSQL($columnDefinitions) . ', PRIMARY KEY(' . implode(',', $idColumnNames) . '))';
$this->dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
}
/**
* {@inheritDoc}
*
* @return int
*/
public function execute(Connection $conn, array $params, array $types)
{
// Create temporary id table
$conn->executeStatement($this->createTempTableSql);
try {
// Insert identifiers
$numDeleted = $conn->executeStatement($this->insertSql, $params, $types);
// Execute DELETE statements
foreach ($this->sqlStatements as $sql) {
$conn->executeStatement($sql);
}
} catch (Throwable $exception) {
// FAILURE! Drop temporary table to avoid possible collisions
$conn->executeStatement($this->dropTempTableSql);
// Re-throw exception
throw $exception;
}
// Drop temporary table
$conn->executeStatement($this->dropTempTableSql);
return $numDeleted;
}
}
@@ -0,0 +1,188 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\Query\AST;
use Doctrine\ORM\Query\AST\UpdateStatement;
use Doctrine\ORM\Query\ParameterTypeInferer;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Utility\PersisterHelper;
use function array_merge;
use function array_reverse;
use function array_slice;
use function implode;
/**
* Executes the SQL statements for bulk DQL UPDATE statements on classes in
* Class Table Inheritance (JOINED).
*/
class MultiTableUpdateExecutor extends AbstractSqlExecutor
{
/** @var string */
private $createTempTableSql;
/** @var string */
private $dropTempTableSql;
/** @var string */
private $insertSql;
/** @var mixed[] */
private $sqlParameters = [];
/** @var int */
private $numParametersInUpdateClause = 0;
/**
* Initializes a new <tt>MultiTableUpdateExecutor</tt>.
*
* Internal note: Any SQL construction and preparation takes place in the constructor for
* best performance. With a query cache the executor will be cached.
*
* @param UpdateStatement $AST The root AST node of the DQL query.
* @param SqlWalker $sqlWalker The walker used for SQL generation from the AST.
*/
public function __construct(AST\Node $AST, $sqlWalker)
{
parent::__construct();
$em = $sqlWalker->getEntityManager();
$conn = $em->getConnection();
$platform = $conn->getDatabasePlatform();
$quoteStrategy = $em->getConfiguration()->getQuoteStrategy();
if ($conn instanceof PrimaryReadReplicaConnection) {
$conn->ensureConnectedToPrimary();
}
$updateClause = $AST->updateClause;
$primaryClass = $sqlWalker->getEntityManager()->getClassMetadata($updateClause->abstractSchemaName);
$rootClass = $em->getClassMetadata($primaryClass->rootEntityName);
$updateItems = $updateClause->updateItems;
$tempTable = $platform->getTemporaryTableName($rootClass->getTemporaryIdTableName());
$idColumnNames = $rootClass->getIdentifierColumnNames();
$idColumnList = implode(', ', $idColumnNames);
// 1. Create an INSERT INTO temptable ... SELECT identifiers WHERE $AST->getWhereClause()
$sqlWalker->setSQLTableAlias($primaryClass->getTableName(), 't0', $updateClause->aliasIdentificationVariable);
$this->insertSql = 'INSERT INTO ' . $tempTable . ' (' . $idColumnList . ')'
. ' SELECT t0.' . implode(', t0.', $idColumnNames);
$rangeDecl = new AST\RangeVariableDeclaration($primaryClass->name, $updateClause->aliasIdentificationVariable);
$fromClause = new AST\FromClause([new AST\IdentificationVariableDeclaration($rangeDecl, null, [])]);
$this->insertSql .= $sqlWalker->walkFromClause($fromClause);
// 2. Create ID subselect statement used in UPDATE ... WHERE ... IN (subselect)
$idSubselect = 'SELECT ' . $idColumnList . ' FROM ' . $tempTable;
// 3. Create and store UPDATE statements
$classNames = array_merge($primaryClass->parentClasses, [$primaryClass->name], $primaryClass->subClasses);
foreach (array_reverse($classNames) as $className) {
$affected = false;
$class = $em->getClassMetadata($className);
$updateSql = 'UPDATE ' . $quoteStrategy->getTableName($class, $platform) . ' SET ';
$sqlParameters = [];
foreach ($updateItems as $updateItem) {
$field = $updateItem->pathExpression->field;
if (
(isset($class->fieldMappings[$field]) && ! isset($class->fieldMappings[$field]['inherited'])) ||
(isset($class->associationMappings[$field]) && ! isset($class->associationMappings[$field]['inherited']))
) {
$newValue = $updateItem->newValue;
if (! $affected) {
$affected = true;
} else {
$updateSql .= ', ';
}
$updateSql .= $sqlWalker->walkUpdateItem($updateItem);
if ($newValue instanceof AST\InputParameter) {
$sqlParameters[] = $newValue->name;
++$this->numParametersInUpdateClause;
}
}
}
if ($affected) {
$this->sqlParameters[] = $sqlParameters;
$this->sqlStatements[] = $updateSql . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')';
}
}
// Append WHERE clause to insertSql, if there is one.
if ($AST->whereClause) {
$this->insertSql .= $sqlWalker->walkWhereClause($AST->whereClause);
}
// 4. Store DDL for temporary identifier table.
$columnDefinitions = [];
foreach ($idColumnNames as $idColumnName) {
$columnDefinitions[$idColumnName] = [
'notnull' => true,
'type' => Type::getType(PersisterHelper::getTypeOfColumn($idColumnName, $rootClass, $em)),
];
}
$this->createTempTableSql = $platform->getCreateTemporaryTableSnippetSQL() . ' ' . $tempTable . ' ('
. $platform->getColumnDeclarationListSQL($columnDefinitions) . ', PRIMARY KEY(' . implode(',', $idColumnNames) . '))';
$this->dropTempTableSql = $platform->getDropTemporaryTableSQL($tempTable);
}
/**
* {@inheritDoc}
*
* @return int
*/
public function execute(Connection $conn, array $params, array $types)
{
// Create temporary id table
$conn->executeStatement($this->createTempTableSql);
try {
// Insert identifiers. Parameters from the update clause are cut off.
$numUpdated = $conn->executeStatement(
$this->insertSql,
array_slice($params, $this->numParametersInUpdateClause),
array_slice($types, $this->numParametersInUpdateClause)
);
// Execute UPDATE statements
foreach ($this->sqlStatements as $key => $statement) {
$paramValues = [];
$paramTypes = [];
if (isset($this->sqlParameters[$key])) {
foreach ($this->sqlParameters[$key] as $parameterKey => $parameterName) {
$paramValues[] = $params[$parameterKey];
$paramTypes[] = $types[$parameterKey] ?? ParameterTypeInferer::inferType($params[$parameterKey]);
}
}
$conn->executeStatement($statement, $paramValues, $paramTypes);
}
} finally {
// Drop temporary table
$conn->executeStatement($this->dropTempTableSql);
}
return $numUpdated;
}
}
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Result;
use Doctrine\ORM\Query\AST\SelectStatement;
use Doctrine\ORM\Query\SqlWalker;
/**
* Executor that executes the SQL statement for simple DQL SELECT statements.
*
* @link www.doctrine-project.org
*/
class SingleSelectExecutor extends AbstractSqlExecutor
{
public function __construct(SelectStatement $AST, SqlWalker $sqlWalker)
{
parent::__construct();
$this->sqlStatements = $sqlWalker->walkSelectStatement($AST);
}
/**
* {@inheritDoc}
*
* @return Result
*/
public function execute(Connection $conn, array $params, array $types)
{
return $conn->executeQuery($this->sqlStatements, $params, $types, $this->queryCacheProfile);
}
}
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
use Doctrine\ORM\Query\AST;
use Doctrine\ORM\Query\SqlWalker;
/**
* Executor that executes the SQL statements for DQL DELETE/UPDATE statements on classes
* that are mapped to a single table.
*
* @link www.doctrine-project.org
*
* @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor.
*/
class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor
{
/** @param SqlWalker $sqlWalker */
public function __construct(AST\Node $AST, $sqlWalker)
{
parent::__construct();
if ($AST instanceof AST\UpdateStatement) {
$this->sqlStatements = $sqlWalker->walkUpdateStatement($AST);
} elseif ($AST instanceof AST\DeleteStatement) {
$this->sqlStatements = $sqlWalker->walkDeleteStatement($AST);
}
}
/**
* {@inheritDoc}
*
* @return int
*/
public function execute(Connection $conn, array $params, array $types)
{
if ($conn instanceof PrimaryReadReplicaConnection) {
$conn->ensureConnectedToPrimary();
}
return $conn->executeStatement($this->sqlStatements, $params, $types);
}
}
+689
View File
@@ -0,0 +1,689 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query;
use Traversable;
use function func_get_args;
use function implode;
use function is_bool;
use function is_float;
use function is_int;
use function is_iterable;
use function iterator_to_array;
use function str_replace;
/**
* This class is used to generate DQL expressions via a set of PHP static functions.
*
* @link www.doctrine-project.org
*
* @todo Rename: ExpressionBuilder
*/
class Expr
{
/**
* Creates a conjunction of the given boolean expressions.
*
* Example:
*
* [php]
* // (u.type = ?1) AND (u.role = ?2)
* $expr->andX($expr->eq('u.type', ':1'), $expr->eq('u.role', ':2'));
*
* @param Expr\Comparison|Expr\Func|Expr\Andx|Expr\Orx|string $x Optional clause. Defaults to null,
* but requires at least one defined
* when converting to string.
* @psalm-param Expr\Comparison|Expr\Func|Expr\Andx|Expr\Orx|string ...$x
*
* @return Expr\Andx
*/
public function andX($x = null)
{
return new Expr\Andx(func_get_args());
}
/**
* Creates a disjunction of the given boolean expressions.
*
* Example:
*
* [php]
* // (u.type = ?1) OR (u.role = ?2)
* $q->where($q->expr()->orX('u.type = ?1', 'u.role = ?2'));
*
* @param Expr\Comparison|Expr\Func|Expr\Andx|Expr\Orx|string $x Optional clause. Defaults to null,
* but requires at least one defined
* when converting to string.
* @psalm-param Expr\Comparison|Expr\Func|Expr\Andx|Expr\Orx|string ...$x
*
* @return Expr\Orx
*/
public function orX($x = null)
{
return new Expr\Orx(func_get_args());
}
/**
* Creates an ASCending order expression.
*
* @param mixed $expr
*
* @return Expr\OrderBy
*/
public function asc($expr)
{
return new Expr\OrderBy($expr, 'ASC');
}
/**
* Creates a DESCending order expression.
*
* @param mixed $expr
*
* @return Expr\OrderBy
*/
public function desc($expr)
{
return new Expr\OrderBy($expr, 'DESC');
}
/**
* Creates an equality comparison expression with the given arguments.
*
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> = <right expr>. Example:
*
* [php]
* // u.id = ?1
* $expr->eq('u.id', '?1');
*
* @param mixed $x Left expression.
* @param mixed $y Right expression.
*
* @return Expr\Comparison
*/
public function eq($x, $y)
{
return new Expr\Comparison($x, Expr\Comparison::EQ, $y);
}
/**
* Creates an instance of Expr\Comparison, with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> <> <right expr>. Example:
*
* [php]
* // u.id <> ?1
* $q->where($q->expr()->neq('u.id', '?1'));
*
* @param mixed $x Left expression.
* @param mixed $y Right expression.
*
* @return Expr\Comparison
*/
public function neq($x, $y)
{
return new Expr\Comparison($x, Expr\Comparison::NEQ, $y);
}
/**
* Creates an instance of Expr\Comparison, with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> < <right expr>. Example:
*
* [php]
* // u.id < ?1
* $q->where($q->expr()->lt('u.id', '?1'));
*
* @param mixed $x Left expression.
* @param mixed $y Right expression.
*
* @return Expr\Comparison
*/
public function lt($x, $y)
{
return new Expr\Comparison($x, Expr\Comparison::LT, $y);
}
/**
* Creates an instance of Expr\Comparison, with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> <= <right expr>. Example:
*
* [php]
* // u.id <= ?1
* $q->where($q->expr()->lte('u.id', '?1'));
*
* @param mixed $x Left expression.
* @param mixed $y Right expression.
*
* @return Expr\Comparison
*/
public function lte($x, $y)
{
return new Expr\Comparison($x, Expr\Comparison::LTE, $y);
}
/**
* Creates an instance of Expr\Comparison, with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> > <right expr>. Example:
*
* [php]
* // u.id > ?1
* $q->where($q->expr()->gt('u.id', '?1'));
*
* @param mixed $x Left expression.
* @param mixed $y Right expression.
*
* @return Expr\Comparison
*/
public function gt($x, $y)
{
return new Expr\Comparison($x, Expr\Comparison::GT, $y);
}
/**
* Creates an instance of Expr\Comparison, with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> >= <right expr>. Example:
*
* [php]
* // u.id >= ?1
* $q->where($q->expr()->gte('u.id', '?1'));
*
* @param mixed $x Left expression.
* @param mixed $y Right expression.
*
* @return Expr\Comparison
*/
public function gte($x, $y)
{
return new Expr\Comparison($x, Expr\Comparison::GTE, $y);
}
/**
* Creates an instance of AVG() function, with the given argument.
*
* @param mixed $x Argument to be used in AVG() function.
*
* @return Expr\Func
*/
public function avg($x)
{
return new Expr\Func('AVG', [$x]);
}
/**
* Creates an instance of MAX() function, with the given argument.
*
* @param mixed $x Argument to be used in MAX() function.
*
* @return Expr\Func
*/
public function max($x)
{
return new Expr\Func('MAX', [$x]);
}
/**
* Creates an instance of MIN() function, with the given argument.
*
* @param mixed $x Argument to be used in MIN() function.
*
* @return Expr\Func
*/
public function min($x)
{
return new Expr\Func('MIN', [$x]);
}
/**
* Creates an instance of COUNT() function, with the given argument.
*
* @param mixed $x Argument to be used in COUNT() function.
*
* @return Expr\Func
*/
public function count($x)
{
return new Expr\Func('COUNT', [$x]);
}
/**
* Creates an instance of COUNT(DISTINCT) function, with the given argument.
*
* @param mixed $x Argument to be used in COUNT(DISTINCT) function.
*
* @return string
*/
public function countDistinct($x)
{
return 'COUNT(DISTINCT ' . implode(', ', func_get_args()) . ')';
}
/**
* Creates an instance of EXISTS() function, with the given DQL Subquery.
*
* @param mixed $subquery DQL Subquery to be used in EXISTS() function.
*
* @return Expr\Func
*/
public function exists($subquery)
{
return new Expr\Func('EXISTS', [$subquery]);
}
/**
* Creates an instance of ALL() function, with the given DQL Subquery.
*
* @param mixed $subquery DQL Subquery to be used in ALL() function.
*
* @return Expr\Func
*/
public function all($subquery)
{
return new Expr\Func('ALL', [$subquery]);
}
/**
* Creates a SOME() function expression with the given DQL subquery.
*
* @param mixed $subquery DQL Subquery to be used in SOME() function.
*
* @return Expr\Func
*/
public function some($subquery)
{
return new Expr\Func('SOME', [$subquery]);
}
/**
* Creates an ANY() function expression with the given DQL subquery.
*
* @param mixed $subquery DQL Subquery to be used in ANY() function.
*
* @return Expr\Func
*/
public function any($subquery)
{
return new Expr\Func('ANY', [$subquery]);
}
/**
* Creates a negation expression of the given restriction.
*
* @param mixed $restriction Restriction to be used in NOT() function.
*
* @return Expr\Func
*/
public function not($restriction)
{
return new Expr\Func('NOT', [$restriction]);
}
/**
* Creates an ABS() function expression with the given argument.
*
* @param mixed $x Argument to be used in ABS() function.
*
* @return Expr\Func
*/
public function abs($x)
{
return new Expr\Func('ABS', [$x]);
}
/**
* Creates a MOD($x, $y) function expression to return the remainder of $x divided by $y.
*
* @param mixed $x
* @param mixed $y
*/
public function mod($x, $y): Expr\Func
{
return new Expr\Func('MOD', [$x, $y]);
}
/**
* Creates a product mathematical expression with the given arguments.
*
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> * <right expr>. Example:
*
* [php]
* // u.salary * u.percentAnnualSalaryIncrease
* $q->expr()->prod('u.salary', 'u.percentAnnualSalaryIncrease')
*
* @param mixed $x Left expression.
* @param mixed $y Right expression.
*
* @return Expr\Math
*/
public function prod($x, $y)
{
return new Expr\Math($x, '*', $y);
}
/**
* Creates a difference mathematical expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> - <right expr>. Example:
*
* [php]
* // u.monthlySubscriptionCount - 1
* $q->expr()->diff('u.monthlySubscriptionCount', '1')
*
* @param mixed $x Left expression.
* @param mixed $y Right expression.
*
* @return Expr\Math
*/
public function diff($x, $y)
{
return new Expr\Math($x, '-', $y);
}
/**
* Creates a sum mathematical expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> + <right expr>. Example:
*
* [php]
* // u.numChildren + 1
* $q->expr()->sum('u.numChildren', '1')
*
* @param mixed $x Left expression.
* @param mixed $y Right expression.
*
* @return Expr\Math
*/
public function sum($x, $y)
{
return new Expr\Math($x, '+', $y);
}
/**
* Creates a quotient mathematical expression with the given arguments.
* First argument is considered the left expression and the second is the right expression.
* When converted to string, it will generated a <left expr> / <right expr>. Example:
*
* [php]
* // u.total / u.period
* $expr->quot('u.total', 'u.period')
*
* @param mixed $x Left expression.
* @param mixed $y Right expression.
*
* @return Expr\Math
*/
public function quot($x, $y)
{
return new Expr\Math($x, '/', $y);
}
/**
* Creates a SQRT() function expression with the given argument.
*
* @param mixed $x Argument to be used in SQRT() function.
*
* @return Expr\Func
*/
public function sqrt($x)
{
return new Expr\Func('SQRT', [$x]);
}
/**
* Creates an IN() expression with the given arguments.
*
* @param string $x Field in string format to be restricted by IN() function.
* @param mixed $y Argument to be used in IN() function.
*
* @return Expr\Func
*/
public function in($x, $y)
{
if (is_iterable($y)) {
if ($y instanceof Traversable) {
$y = iterator_to_array($y);
}
foreach ($y as &$literal) {
if (! ($literal instanceof Expr\Literal)) {
$literal = $this->quoteLiteral($literal);
}
}
}
return new Expr\Func($x . ' IN', (array) $y);
}
/**
* Creates a NOT IN() expression with the given arguments.
*
* @param string $x Field in string format to be restricted by NOT IN() function.
* @param mixed $y Argument to be used in NOT IN() function.
*
* @return Expr\Func
*/
public function notIn($x, $y)
{
if (is_iterable($y)) {
if ($y instanceof Traversable) {
$y = iterator_to_array($y);
}
foreach ($y as &$literal) {
if (! ($literal instanceof Expr\Literal)) {
$literal = $this->quoteLiteral($literal);
}
}
}
return new Expr\Func($x . ' NOT IN', (array) $y);
}
/**
* Creates an IS NULL expression with the given arguments.
*
* @param string $x Field in string format to be restricted by IS NULL.
*
* @return string
*/
public function isNull($x)
{
return $x . ' IS NULL';
}
/**
* Creates an IS NOT NULL expression with the given arguments.
*
* @param string $x Field in string format to be restricted by IS NOT NULL.
*
* @return string
*/
public function isNotNull($x)
{
return $x . ' IS NOT NULL';
}
/**
* Creates a LIKE() comparison expression with the given arguments.
*
* @param string $x Field in string format to be inspected by LIKE() comparison.
* @param mixed $y Argument to be used in LIKE() comparison.
*
* @return Expr\Comparison
*/
public function like($x, $y)
{
return new Expr\Comparison($x, 'LIKE', $y);
}
/**
* Creates a NOT LIKE() comparison expression with the given arguments.
*
* @param string $x Field in string format to be inspected by LIKE() comparison.
* @param mixed $y Argument to be used in LIKE() comparison.
*
* @return Expr\Comparison
*/
public function notLike($x, $y)
{
return new Expr\Comparison($x, 'NOT LIKE', $y);
}
/**
* Creates a CONCAT() function expression with the given arguments.
*
* @param mixed $x First argument to be used in CONCAT() function.
* @param mixed $y,... Other arguments to be used in CONCAT() function.
*
* @return Expr\Func
*/
public function concat($x, $y)
{
return new Expr\Func('CONCAT', func_get_args());
}
/**
* Creates a SUBSTRING() function expression with the given arguments.
*
* @param mixed $x Argument to be used as string to be cropped by SUBSTRING() function.
* @param int $from Initial offset to start cropping string. May accept negative values.
* @param int|null $len Length of crop. May accept negative values.
*
* @return Expr\Func
*/
public function substring($x, $from, $len = null)
{
$args = [$x, $from];
if ($len !== null) {
$args[] = $len;
}
return new Expr\Func('SUBSTRING', $args);
}
/**
* Creates a LOWER() function expression with the given argument.
*
* @param mixed $x Argument to be used in LOWER() function.
*
* @return Expr\Func A LOWER function expression.
*/
public function lower($x)
{
return new Expr\Func('LOWER', [$x]);
}
/**
* Creates an UPPER() function expression with the given argument.
*
* @param mixed $x Argument to be used in UPPER() function.
*
* @return Expr\Func An UPPER function expression.
*/
public function upper($x)
{
return new Expr\Func('UPPER', [$x]);
}
/**
* Creates a LENGTH() function expression with the given argument.
*
* @param mixed $x Argument to be used as argument of LENGTH() function.
*
* @return Expr\Func A LENGTH function expression.
*/
public function length($x)
{
return new Expr\Func('LENGTH', [$x]);
}
/**
* Creates a literal expression of the given argument.
*
* @param scalar $literal Argument to be converted to literal.
*
* @return Expr\Literal
*/
public function literal($literal)
{
return new Expr\Literal($this->quoteLiteral($literal));
}
/**
* Quotes a literal value, if necessary, according to the DQL syntax.
*
* @param scalar $literal The literal value.
*/
private function quoteLiteral($literal): string
{
if (is_int($literal) || is_float($literal)) {
return (string) $literal;
}
if (is_bool($literal)) {
return $literal ? 'true' : 'false';
}
return "'" . str_replace("'", "''", $literal) . "'";
}
/**
* Creates an instance of BETWEEN() function, with the given argument.
*
* @param mixed $val Valued to be inspected by range values.
* @param int|string $x Starting range value to be used in BETWEEN() function.
* @param int|string $y End point value to be used in BETWEEN() function.
*
* @return string A BETWEEN expression.
*/
public function between($val, $x, $y)
{
return $val . ' BETWEEN ' . $x . ' AND ' . $y;
}
/**
* Creates an instance of TRIM() function, with the given argument.
*
* @param mixed $x Argument to be used as argument of TRIM() function.
*
* @return Expr\Func a TRIM expression.
*/
public function trim($x)
{
return new Expr\Func('TRIM', $x);
}
/**
* Creates an instance of MEMBER OF function, with the given arguments.
*
* @param string $x Value to be checked
* @param string $y Value to be checked against
*
* @return Expr\Comparison
*/
public function isMemberOf($x, $y)
{
return new Expr\Comparison($x, 'MEMBER OF', $y);
}
/**
* Creates an instance of INSTANCE OF function, with the given arguments.
*
* @param string $x Value to be checked
* @param string $y Value to be checked against
*
* @return Expr\Comparison
*/
public function isInstanceOf($x, $y)
{
return new Expr\Comparison($x, 'INSTANCE OF', $y);
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\Expr;
/**
* Expression class for building DQL and parts.
*
* @link www.doctrine-project.org
*/
class Andx extends Composite
{
/** @var string */
protected $separator = ' AND ';
/** @var string[] */
protected $allowedClasses = [
Comparison::class,
Func::class,
Orx::class,
self::class,
];
/** @psalm-var list<string|Comparison|Func|Orx|self> */
protected $parts = [];
/** @psalm-return list<string|Comparison|Func|Orx|self> */
public function getParts()
{
return $this->parts;
}
}
+103
View File
@@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\Expr;
use InvalidArgumentException;
use Stringable;
use function count;
use function get_class;
use function get_debug_type;
use function implode;
use function in_array;
use function is_string;
use function sprintf;
/**
* Abstract base Expr class for building DQL parts.
*
* @link www.doctrine-project.org
*/
abstract class Base
{
/** @var string */
protected $preSeparator = '(';
/** @var string */
protected $separator = ', ';
/** @var string */
protected $postSeparator = ')';
/** @var list<class-string> */
protected $allowedClasses = [];
/** @var list<string|Stringable> */
protected $parts = [];
/** @param mixed $args */
public function __construct($args = [])
{
$this->addMultiple($args);
}
/**
* @param string[]|object[]|string|object $args
* @psalm-param list<string|object>|string|object $args
*
* @return $this
*/
public function addMultiple($args = [])
{
foreach ((array) $args as $arg) {
$this->add($arg);
}
return $this;
}
/**
* @param mixed $arg
*
* @return $this
*
* @throws InvalidArgumentException
*/
public function add($arg)
{
if ($arg !== null && (! $arg instanceof self || $arg->count() > 0)) {
// If we decide to keep Expr\Base instances, we can use this check
if (! is_string($arg) && ! in_array(get_class($arg), $this->allowedClasses, true)) {
throw new InvalidArgumentException(sprintf(
"Expression of type '%s' not allowed in this context.",
get_debug_type($arg)
));
}
$this->parts[] = $arg;
}
return $this;
}
/**
* @return int
* @psalm-return 0|positive-int
*/
public function count()
{
return count($this->parts);
}
/** @return string */
public function __toString()
{
if ($this->count() === 1) {
return (string) $this->parts[0];
}
return $this->preSeparator . implode($this->separator, $this->parts) . $this->postSeparator;
}
}
+67
View File
@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\Expr;
/**
* Expression class for DQL comparison expressions.
*
* @link www.doctrine-project.org
*/
class Comparison
{
public const EQ = '=';
public const NEQ = '<>';
public const LT = '<';
public const LTE = '<=';
public const GT = '>';
public const GTE = '>=';
/** @var mixed */
protected $leftExpr;
/** @var string */
protected $operator;
/** @var mixed */
protected $rightExpr;
/**
* Creates a comparison expression with the given arguments.
*
* @param mixed $leftExpr
* @param string $operator
* @param mixed $rightExpr
*/
public function __construct($leftExpr, $operator, $rightExpr)
{
$this->leftExpr = $leftExpr;
$this->operator = $operator;
$this->rightExpr = $rightExpr;
}
/** @return mixed */
public function getLeftExpr()
{
return $this->leftExpr;
}
/** @return string */
public function getOperator()
{
return $this->operator;
}
/** @return mixed */
public function getRightExpr()
{
return $this->rightExpr;
}
/** @return string */
public function __toString()
{
return $this->leftExpr . ' ' . $this->operator . ' ' . $this->rightExpr;
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query\Expr;
use function implode;
use function is_object;
use function preg_match;
/**
* Expression class for building DQL and parts.
*
* @link www.doctrine-project.org
*/
class Composite extends Base
{
/** @return string */
public function __toString()
{
if ($this->count() === 1) {
return (string) $this->parts[0];
}
$components = [];
foreach ($this->parts as $part) {
$components[] = $this->processQueryPart($part);
}
return implode($this->separator, $components);
}
/** @param string|object $part */
private function processQueryPart($part): string
{
$queryPart = (string) $part;
if (is_object($part) && $part instanceof self && $part->count() > 1) {
return $this->preSeparator . $queryPart . $this->postSeparator;
}
// Fixes DDC-1237: User may have added a where item containing nested expression (with "OR" or "AND")
if (preg_match('/\s(OR|AND)\s/i', $queryPart)) {
return $this->preSeparator . $queryPart . $this->postSeparator;
}
return $queryPart;
}
}

Some files were not shown because too many files have changed in this diff Show More