Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/ArrayAgg.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@

namespace MartinGeorgiev\Doctrine\ORM\Query\AST\Functions;

use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Traits\DistinctableTrait;
use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Traits\OrderableTrait;
use MartinGeorgiev\Utils\DoctrineOrm;

/**
* Implementation of PostgreSQL ARRAY_AGG().
Expand All @@ -15,21 +20,38 @@
*
* @author Martin Georgiev <martin.georgiev@gmail.com>
*/
class ArrayAgg extends BaseOrderableFunction
class ArrayAgg extends BaseFunction
{
use OrderableTrait;
use DistinctableTrait;

protected function customizeFunction(): void
{
$this->setFunctionPrototype('array_agg(%s%s)');
$this->setFunctionPrototype('array_agg(%s%s%s)');
$this->addNodeMapping('StringPrimary');
}

protected function parseFunction(Parser $parser): void
public function parse(Parser $parser): void
{
$shouldUseLexer = DoctrineOrm::isPre219();

$this->customizeFunction();

$parser->match($shouldUseLexer ? Lexer::T_IDENTIFIER : TokenType::T_IDENTIFIER);
$parser->match($shouldUseLexer ? Lexer::T_OPEN_PARENTHESIS : TokenType::T_OPEN_PARENTHESIS);

$this->parseDistinctClause($parser);
$this->expression = $parser->StringPrimary();

$this->parseOrderByClause($parser);

$parser->match($shouldUseLexer ? Lexer::T_CLOSE_PARENTHESIS : TokenType::T_CLOSE_PARENTHESIS);
}

public function getSql(SqlWalker $sqlWalker): string
{
$dispatched = [
$this->getOptionalDistinctClause(),
$this->expression->dispatch($sqlWalker),
$this->getOptionalOrderByClause($sqlWalker),
];
Expand Down
26 changes: 17 additions & 9 deletions src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/StringAgg.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Traits\DistinctableTrait;
use MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Traits\OrderableTrait;
use MartinGeorgiev\Utils\DoctrineOrm;

/**
Expand All @@ -19,38 +21,44 @@
*
* @author Martin Georgiev <martin.georgiev@gmail.com>
*/
class StringAgg extends BaseOrderableFunction
class StringAgg extends BaseFunction
{
private bool $isDistinct = false;
use OrderableTrait;
use DistinctableTrait;

private Node $delimiter;

protected function customizeFunction(): void
{
$this->setFunctionPrototype('string_agg(%s%s, %s%s)');
$this->addNodeMapping('StringPrimary');
$this->addNodeMapping('StringPrimary');
}

protected function parseFunction(Parser $parser): void
public function parse(Parser $parser): void
{
$shouldUseLexer = DoctrineOrm::isPre219();
$lexer = $parser->getLexer();

if ($lexer->isNextToken($shouldUseLexer ? Lexer::T_DISTINCT : TokenType::T_DISTINCT)) {
$parser->match($shouldUseLexer ? Lexer::T_DISTINCT : TokenType::T_DISTINCT);
$this->isDistinct = true;
}
$this->customizeFunction();

$parser->match($shouldUseLexer ? Lexer::T_IDENTIFIER : TokenType::T_IDENTIFIER);
$parser->match($shouldUseLexer ? Lexer::T_OPEN_PARENTHESIS : TokenType::T_OPEN_PARENTHESIS);

$this->parseDistinctClause($parser);
$this->expression = $parser->StringPrimary();

$parser->match($shouldUseLexer ? Lexer::T_COMMA : TokenType::T_COMMA);

$this->delimiter = $parser->StringPrimary();
$this->parseOrderByClause($parser);

$parser->match($shouldUseLexer ? Lexer::T_CLOSE_PARENTHESIS : TokenType::T_CLOSE_PARENTHESIS);
}

public function getSql(SqlWalker $sqlWalker): string
{
$dispatched = [
$this->isDistinct ? 'DISTINCT ' : '',
$this->getOptionalDistinctClause(),
$this->expression->dispatch($sqlWalker),
$this->delimiter->dispatch($sqlWalker),
$this->getOptionalOrderByClause($sqlWalker),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Traits;

use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\TokenType;
use MartinGeorgiev\Utils\DoctrineOrm;

trait DistinctableTrait
{
protected bool $isDistinct = false;

protected function parseDistinctClause(Parser $parser): void
{
$shouldUseLexer = DoctrineOrm::isPre219();
$lexer = $parser->getLexer();

if ($lexer->isNextToken($shouldUseLexer ? Lexer::T_DISTINCT : TokenType::T_DISTINCT)) {
$parser->match($shouldUseLexer ? Lexer::T_DISTINCT : TokenType::T_DISTINCT);
$this->isDistinct = true;
}
}

protected function getOptionalDistinctClause(): string
{
return $this->isDistinct ? 'DISTINCT ' : '';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

declare(strict_types=1);

namespace MartinGeorgiev\Doctrine\ORM\Query\AST\Functions;
namespace MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Traits;

use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\AST\OrderByClause;
Expand All @@ -12,33 +12,22 @@
use Doctrine\ORM\Query\TokenType;
use MartinGeorgiev\Utils\DoctrineOrm;

abstract class BaseOrderableFunction extends BaseFunction
trait OrderableTrait
{
protected Node $expression;

protected ?OrderByClause $orderByClause = null;

public function parse(Parser $parser): void
protected function parseOrderByClause(Parser $parser): void
{
$shouldUseLexer = DoctrineOrm::isPre219();

$this->customizeFunction();

$parser->match($shouldUseLexer ? Lexer::T_IDENTIFIER : TokenType::T_IDENTIFIER);
$parser->match($shouldUseLexer ? Lexer::T_OPEN_PARENTHESIS : TokenType::T_OPEN_PARENTHESIS);

$this->parseFunction($parser);

$lexer = $parser->getLexer();

if ($lexer->isNextToken($shouldUseLexer ? Lexer::T_ORDER : TokenType::T_ORDER)) {
$this->orderByClause = $parser->OrderByClause();
}

$parser->match($shouldUseLexer ? Lexer::T_CLOSE_PARENTHESIS : TokenType::T_CLOSE_PARENTHESIS);
}

abstract protected function parseFunction(Parser $parser): void;

protected function getOptionalOrderByClause(SqlWalker $sqlWalker): string
{
return $this->orderByClause instanceof OrderByClause ? $this->orderByClause->dispatch($sqlWalker) : '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,32 @@ protected function getStringFunctions(): array
protected function getExpectedSqlStatements(): array
{
return [
// Basic usage
'SELECT array_agg(c0_.text1) AS sclr_0 FROM ContainsTexts c0_',
// With concatenation
'SELECT array_agg(c0_.text1 || c0_.text2) AS sclr_0 FROM ContainsTexts c0_',
// With ORDER BY
'SELECT array_agg(c0_.text1 ORDER BY c0_.text1 ASC) AS sclr_0 FROM ContainsTexts c0_',
'SELECT array_agg(c0_.text1 ORDER BY c0_.text1 DESC) AS sclr_0 FROM ContainsTexts c0_',
// With concatenation and ORDER BY
'SELECT array_agg(c0_.text1 || c0_.text2 ORDER BY c0_.text1 ASC) AS sclr_0 FROM ContainsTexts c0_',
// With multiple ORDER BY columns
'SELECT array_agg(c0_.text1 ORDER BY c0_.text1 ASC, c0_.text2 DESC) AS sclr_0 FROM ContainsTexts c0_',
'basic usage' => 'SELECT array_agg(c0_.text1) AS sclr_0 FROM ContainsTexts c0_',
'with concatenation' => 'SELECT array_agg(c0_.text1 || c0_.text2) AS sclr_0 FROM ContainsTexts c0_',
'with DISTINCT' => 'SELECT array_agg(DISTINCT c0_.text1) AS sclr_0 FROM ContainsTexts c0_',
'with DISTINCT and concatenation' => 'SELECT array_agg(DISTINCT c0_.text1 || c0_.text2) AS sclr_0 FROM ContainsTexts c0_',
'with ORDER BY' => 'SELECT array_agg(c0_.text1 ORDER BY c0_.text1 ASC) AS sclr_0 FROM ContainsTexts c0_',
'with ORDER BY DESC' => 'SELECT array_agg(c0_.text1 ORDER BY c0_.text1 DESC) AS sclr_0 FROM ContainsTexts c0_',
'with DISTINCT and ORDER BY' => 'SELECT array_agg(DISTINCT c0_.text1 ORDER BY c0_.text1 ASC) AS sclr_0 FROM ContainsTexts c0_',
'with concatenation and ORDER BY' => 'SELECT array_agg(c0_.text1 || c0_.text2 ORDER BY c0_.text1 ASC) AS sclr_0 FROM ContainsTexts c0_',
'with DISTINCT, concatenation and ORDER BY' => 'SELECT array_agg(DISTINCT c0_.text1 || c0_.text2 ORDER BY c0_.text1 ASC) AS sclr_0 FROM ContainsTexts c0_',
'with multiple ORDER BY columns' => 'SELECT array_agg(c0_.text1 ORDER BY c0_.text1 ASC, c0_.text2 DESC) AS sclr_0 FROM ContainsTexts c0_',
];
}

protected function getDqlStatements(): array
{
return [
\sprintf('SELECT ARRAY_AGG(e.text1) FROM %s e', ContainsTexts::class),
\sprintf('SELECT ARRAY_AGG(CONCAT(e.text1, e.text2)) FROM %s e', ContainsTexts::class),
\sprintf('SELECT ARRAY_AGG(e.text1 ORDER BY e.text1) FROM %s e', ContainsTexts::class),
\sprintf('SELECT ARRAY_AGG(e.text1 ORDER BY e.text1 DESC) FROM %s e', ContainsTexts::class),
\sprintf('SELECT ARRAY_AGG(CONCAT(e.text1, e.text2) ORDER BY e.text1) FROM %s e', ContainsTexts::class),
\sprintf('SELECT ARRAY_AGG(e.text1 ORDER BY e.text1 ASC, e.text2 DESC) FROM %s e', ContainsTexts::class),
'basic usage' => \sprintf('SELECT ARRAY_AGG(e.text1) FROM %s e', ContainsTexts::class),
'with concatenation' => \sprintf('SELECT ARRAY_AGG(CONCAT(e.text1, e.text2)) FROM %s e', ContainsTexts::class),
'with DISTINCT' => \sprintf('SELECT ARRAY_AGG(DISTINCT e.text1) FROM %s e', ContainsTexts::class),
'with DISTINCT and concatenation' => \sprintf('SELECT ARRAY_AGG(DISTINCT CONCAT(e.text1, e.text2)) FROM %s e', ContainsTexts::class),
'with ORDER BY' => \sprintf('SELECT ARRAY_AGG(e.text1 ORDER BY e.text1) FROM %s e', ContainsTexts::class),
'with ORDER BY DESC' => \sprintf('SELECT ARRAY_AGG(e.text1 ORDER BY e.text1 DESC) FROM %s e', ContainsTexts::class),
'with DISTINCT and ORDER BY' => \sprintf('SELECT ARRAY_AGG(DISTINCT e.text1 ORDER BY e.text1) FROM %s e', ContainsTexts::class),
'with concatenation and ORDER BY' => \sprintf('SELECT ARRAY_AGG(CONCAT(e.text1, e.text2) ORDER BY e.text1) FROM %s e', ContainsTexts::class),
'with DISTINCT, concatenation and ORDER BY' => \sprintf('SELECT ARRAY_AGG(DISTINCT CONCAT(e.text1, e.text2) ORDER BY e.text1) FROM %s e', ContainsTexts::class),
'with multiple ORDER BY columns' => \sprintf('SELECT ARRAY_AGG(e.text1 ORDER BY e.text1 ASC, e.text2 DESC) FROM %s e', ContainsTexts::class),
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,41 +19,32 @@ protected function getStringFunctions(): array
protected function getExpectedSqlStatements(): array
{
return [
// Basic usage
"SELECT string_agg(c0_.text1, ',') AS sclr_0 FROM ContainsTexts c0_",
// With concatenation
"SELECT string_agg(c0_.text1 || c0_.text2, ',') AS sclr_0 FROM ContainsTexts c0_",
// With DISTINCT
"SELECT string_agg(DISTINCT c0_.text1, ',') AS sclr_0 FROM ContainsTexts c0_",
// With DISTINCT and concatenation
"SELECT string_agg(DISTINCT c0_.text1 || c0_.text2, ',') AS sclr_0 FROM ContainsTexts c0_",
// With ORDER BY
"SELECT string_agg(c0_.text1, ',' ORDER BY c0_.text1 ASC) AS sclr_0 FROM ContainsTexts c0_",
"SELECT string_agg(c0_.text1, ',' ORDER BY c0_.text1 DESC) AS sclr_0 FROM ContainsTexts c0_",
// With DISTINCT and ORDER BY
"SELECT string_agg(DISTINCT c0_.text1, ',' ORDER BY c0_.text1 ASC) AS sclr_0 FROM ContainsTexts c0_",
// With concatenation, DISTINCT and ORDER BY
"SELECT string_agg(DISTINCT c0_.text1 || c0_.text2, ',' ORDER BY c0_.text1 ASC) AS sclr_0 FROM ContainsTexts c0_",
// With multiple ORDER BY columns
"SELECT string_agg(c0_.text1, ',' ORDER BY c0_.text1 ASC, c0_.text2 DESC) AS sclr_0 FROM ContainsTexts c0_",
// With different delimiter
"SELECT string_agg(c0_.text1, ' | ') AS sclr_0 FROM ContainsTexts c0_",
'basic usage' => "SELECT string_agg(c0_.text1, ',') AS sclr_0 FROM ContainsTexts c0_",
'with concatenation' => "SELECT string_agg(c0_.text1 || c0_.text2, ',') AS sclr_0 FROM ContainsTexts c0_",
'with DISTINCT' => "SELECT string_agg(DISTINCT c0_.text1, ',') AS sclr_0 FROM ContainsTexts c0_",
'with DISTINCT and concatenation' => "SELECT string_agg(DISTINCT c0_.text1 || c0_.text2, ',') AS sclr_0 FROM ContainsTexts c0_",
'with ORDER BY' => "SELECT string_agg(c0_.text1, ',' ORDER BY c0_.text1 ASC) AS sclr_0 FROM ContainsTexts c0_",
'with ORDER BY DESC' => "SELECT string_agg(c0_.text1, ',' ORDER BY c0_.text1 DESC) AS sclr_0 FROM ContainsTexts c0_",
'with DISTINCT and ORDER BY' => "SELECT string_agg(DISTINCT c0_.text1, ',' ORDER BY c0_.text1 ASC) AS sclr_0 FROM ContainsTexts c0_",
'with concatenation, DISTINCT and ORDER BY' => "SELECT string_agg(DISTINCT c0_.text1 || c0_.text2, ',' ORDER BY c0_.text1 ASC) AS sclr_0 FROM ContainsTexts c0_",
'with multiple ORDER BY columns' => "SELECT string_agg(c0_.text1, ',' ORDER BY c0_.text1 ASC, c0_.text2 DESC) AS sclr_0 FROM ContainsTexts c0_",
'with different delimiter' => "SELECT string_agg(c0_.text1, ' | ') AS sclr_0 FROM ContainsTexts c0_",
];
}

protected function getDqlStatements(): array
{
return [
\sprintf("SELECT STRING_AGG(e.text1, ',') FROM %s e", ContainsTexts::class),
\sprintf("SELECT STRING_AGG(CONCAT(e.text1, e.text2), ',') FROM %s e", ContainsTexts::class),
\sprintf("SELECT STRING_AGG(DISTINCT e.text1, ',') FROM %s e", ContainsTexts::class),
\sprintf("SELECT STRING_AGG(DISTINCT CONCAT(e.text1, e.text2), ',') FROM %s e", ContainsTexts::class),
\sprintf("SELECT STRING_AGG(e.text1, ',' ORDER BY e.text1) FROM %s e", ContainsTexts::class),
\sprintf("SELECT STRING_AGG(e.text1, ',' ORDER BY e.text1 DESC) FROM %s e", ContainsTexts::class),
\sprintf("SELECT STRING_AGG(DISTINCT e.text1, ',' ORDER BY e.text1) FROM %s e", ContainsTexts::class),
\sprintf("SELECT STRING_AGG(DISTINCT CONCAT(e.text1, e.text2), ',' ORDER BY e.text1) FROM %s e", ContainsTexts::class),
\sprintf("SELECT STRING_AGG(e.text1, ',' ORDER BY e.text1 ASC, e.text2 DESC) FROM %s e", ContainsTexts::class),
\sprintf("SELECT STRING_AGG(e.text1, ' | ') FROM %s e", ContainsTexts::class),
'basic usage' => \sprintf("SELECT STRING_AGG(e.text1, ',') FROM %s e", ContainsTexts::class),
'with concatenation' => \sprintf("SELECT STRING_AGG(CONCAT(e.text1, e.text2), ',') FROM %s e", ContainsTexts::class),
'with DISTINCT' => \sprintf("SELECT STRING_AGG(DISTINCT e.text1, ',') FROM %s e", ContainsTexts::class),
'with DISTINCT and concatenation' => \sprintf("SELECT STRING_AGG(DISTINCT CONCAT(e.text1, e.text2), ',') FROM %s e", ContainsTexts::class),
'with ORDER BY' => \sprintf("SELECT STRING_AGG(e.text1, ',' ORDER BY e.text1) FROM %s e", ContainsTexts::class),
'with ORDER BY DESC' => \sprintf("SELECT STRING_AGG(e.text1, ',' ORDER BY e.text1 DESC) FROM %s e", ContainsTexts::class),
'with DISTINCT and ORDER BY' => \sprintf("SELECT STRING_AGG(DISTINCT e.text1, ',' ORDER BY e.text1) FROM %s e", ContainsTexts::class),
'with concatenation, DISTINCT and ORDER BY' => \sprintf("SELECT STRING_AGG(DISTINCT CONCAT(e.text1, e.text2), ',' ORDER BY e.text1) FROM %s e", ContainsTexts::class),
'with multiple ORDER BY columns' => \sprintf("SELECT STRING_AGG(e.text1, ',' ORDER BY e.text1 ASC, e.text2 DESC) FROM %s e", ContainsTexts::class),
'with different delimiter' => \sprintf("SELECT STRING_AGG(e.text1, ' | ') FROM %s e", ContainsTexts::class),
];
}
}