martin-georgiev / postgresql-for-doctrine

PostgreSQL enhancements for Doctrine. Provides support for advanced data types (json, jssnb, arrays), text search, array operators and jsonb specific functions.
https://packagist.org/packages/martin-georgiev/postgresql-for-doctrine
MIT License
364 stars 44 forks source link

ARRAY with multiple arguments #95

Open flaushi opened 3 years ago

flaushi commented 3 years ago

I'd like to use this kind of postgres code Array_to_string(ARRAY['a', 'b', 'c'], ';') which gives a;b;c

but this does not parse in dql :

ARRAY_TO_STRING( ARRAY( 'a', 'b', 'c' ), ';')
-> [Syntax Error] line 0, col 70: Error: Expected Doctrine\ORM\Query\Lexer::T_CLOSE_PARENTHESIS, got ','
LeoAdamek commented 3 years ago

Looking at this package I've noticed too that the ARRAY function doesn't work correctly, it seems to accept only a single argument, rather than the variable number of arguments of the pg function.

My understanding is that the DQL ARRAY( 'foo', 'bar', 'baz' ) should produce the SQL ARRAY['foo','bar','baz'] , but instead throws a parser error as it's expecting only a single argument?

Mashishe commented 2 years ago

Here a rewrite that permit to have array with multiple arg

<?php

namespace App\Dql;

use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;

/**
 * array with multiple element
 * ex: OVERLAPS(ARRAY('anru', 'tva'), l.fiscalites) = true
 */
class Arr extends FunctionNode
{
    private ?Node $field = null;

    private array $values = [];

    public function __construct()
    {
        parent::__construct("ARRAY");
    }

    public function parse(Parser $parser): void
    {
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);

        $this->field = $parser->ArithmeticPrimary();

        $lexer = $parser->getLexer();

        while (count($this->values) < 1 || $lexer->lookahead['type'] !== Lexer::T_CLOSE_PARENTHESIS) {
            $parser->match(Lexer::T_COMMA);
            $this->values[] = $parser->ArithmeticPrimary(); // renvoi le node
        }

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

    public function getSql(SqlWalker $sqlWalker): string
    {
        $query = 'ARRAY[';

        $query .= $this->field->dispatch($sqlWalker);

        $query .= ', ';

        foreach ($this->values as $i => $iValue) {
            if ($i > 0) {
                $query .= ', ';
            }

            $query .= $iValue->dispatch($sqlWalker);
        }

        $query .= ']';

        return $query;
    }
}