'Invalid schema name' 错误由 Doctrine 抛出,但原始的 SQL 似乎有效

'Invalid schema name' error thrown by Doctrine, but the raw SQL seems to work

我正在使用一些自定义 DQL 函数通过 PostgreSQL 中的一些 JSONB 字段过滤行。这是我的查询函数:

private function findTopLevelResources(): array {
    return $this->createQueryBuilder('r')
        ->where("JSON_EXISTS(r.contents, '-1') = FALSE")
        ->getQuery()
        ->getResult();
}

运行 此代码从 AbstractPostgreSQLDriver:

得到 DriverException
An exception occurred while executing 'SELECT r0_.id AS id_0, r0_.marking AS marking_1, r0_.contents AS contents_2, r0_.kind_id AS kind_id_3 FROM resource r0_ WHERE r0_.contents?'-1' = false':

SQLSTATE[3F000]: Invalid schema name: 7 ERROR: schema "r0_" does not exist
LINE 1: ... r0_.kind_id AS kind_id_3 FROM resource r0_ WHERE r0_.conten...
                                                             ^

我尝试从 PHPStorm 手动执行原始 SQL 查询并且成功了,没有错误。

如何在 Doctrine 中使用它?

为什么这个查询不适用于 Doctrine,但当我手动测试它时却可以?


这里是 JSON_EXISTS:(基于 syslogic/doctrine-json-functions

class JsonExists extends FunctionNode
{
    const FUNCTION_NAME = 'JSON_EXISTS';
    const OPERATOR = '?';

    public $jsonData;
    public $jsonPath;

    public function getSql(SqlWalker $sqlWalker)
    {
        $jsonData = $sqlWalker->walkStringPrimary($this->jsonData);
        $jsonPath = $this->jsonPath->value;
        return $jsonData . self::OPERATOR . "'$jsonPath'";
    }

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

        $this->jsonData = $parser->StringPrimary();

        $parser->match(Lexer::T_COMMA);

        $this->jsonPath = $parser->StringPrimary();

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

通过 Symfony 的 YAML 配置注册,如下所示:

doctrine:
  orm:
    dql:
      numeric_functions:
        json_exists: Syslogic\DoctrineJsonFunctions\Query\AST\Functions\Postgresql\JsonExists

内容版本:

错误信息是一个错误的线索。

实际问题是由 PDO 引起的(这就是它在 PHPStorm 中起作用的原因)。当它看到这样的查询时:

SELECT * FROM foo WHERE contents?'bar'

它将其视为参数化查询,并将问号 ? 视为参数。由于某种原因,它有时会导致无意义的错误消息。这种特殊情况可以通过在问号后添加 space 来解决,但它不适用于运算符 ?|?&,它们不能在 space 中中间.

解决方法是使用对应运算符的函数(this question saved the day)。可以使用这样的查询找出它们是如何被调用的:

SELECT oprname, oprcode FROM pg_operator WHERE oprname IN ('?', '?|', '?&')

这是与 JSON 相关的结果部分:

  • ?jsonb_exists
  • ?|jsonb_exists_any
  • ?&jsonb_exists_all

因此,可以使用以下等效查询代替之前通过 PDO 导致问题的查询:

SELECT * FROM foo WHERE jsonb_exists(contents, 'bar')