将 PDO 与 PostgreSQL 一起使用时如何忽略问号作为占位符

Posted

技术标签:

【中文标题】将 PDO 与 PostgreSQL 一起使用时如何忽略问号作为占位符【英文标题】:How to ignore question mark as placeholder when using PDO with PostgreSQL 【发布时间】:2016-07-10 11:09:12 【问题描述】:

注意:

这个问题可以被认为是这个Question的重复。 它确实指出了 PDO 的相同问题。但由于目标不同,它的变通解决方案略有不同。我将在那里发布 JSONB 的解决方法和 php 票证的链接。

当我准备以下查询时:

SELECT * FROM post WHERE locations ? :location;

出现以下警告:

警告:PDO::prepare(): SQLSTATE[HY093]: 无效参数号:/path/file.php 第 xx 行中的混合命名参数和位置参数

问号是有效的PostgreSQL operator,但 PDO 将其视为占位符。

是否有适当的方法来配置 PDO 以忽略作为占位符的问号?

我将在下面发布一个解决方法。希望有更好的方法

编辑 我在 PHP 错误跟踪系统中添加了 ticket

【问题讨论】:

已经被问过几次了,例如***.com/questions/16311939/… 没有好的答案。 @AlexBlex,是的,不是的。我读了那个问题(和其他几个),但它不一样。检查我的解决方法。我会在 PHP PDO 开一张票来解决这个问题 实际上,问题和答案在我看来都是重复的——mikl 使用了 ? 运算符(在这种情况下,在 hstore 上而不是 json 上)并且 Craig Ringer 建议使用函数作为解决方法(看起来实现操作符的内置函数,而不是创建一个新函数)。 BTW 不确定 PHP/PDO 但尝试删除列名和运算符之间的空格:WHERE locations? :location; @Abelisto 不幸的是,根据我的测试,PDO 的解析器太聪明了,仍然可以发现 ? 【参考方案1】:

这是我的问题的解决方法。它通过使用PostgreSQL函数替换?运算符解决了这个问题。

我不太喜欢它,因为它不会使 PDO 更符合 PostgreSQL。但我没有找到真正的解决方案。

CREATE FUNCTION json_key_exists(JSONB,TEXT) RETURNS BOOLEAN LANGUAGE SQL STABLE AS $f$
    SELECT $1 ? $2
$f$;

现在我可以使用查询了:

SELECT * FROM post WHERE json_key_exists(locations, :location);

解决方法是由来自 freenode #postgresql 的神话般的 RhodiumToad 提出的

编辑

正如@Abelisto 建议的那样,不需要创建上面的函数,因为jsonb_exists(jsonb, text) 是avialabe

【讨论】:

您可以探索?运算符定义(psql中的\do+ "?"),发现它使用函数jsonb_exists(jsonb, text)作为其背景。所以你不需要定义你的函数,而是重用已经存在的函数。 我想建议使用自定义不可变函数,其中包含?,因为 jsonb_exists() 不可索引,而 ?。见dba.stackexchange.com/questions/90002/… @НЛО 顺便说一句,你错了。 create index i on t(jsonb_exists(x,'a'::text)); 和索引也使用得很好。【参考方案2】:

自 PHP 7.4 起,supports for escaping question mark have landed.

(...) 问号可以通过加倍 (...) 来转义。这意味着“??”字符串将被翻译为“?”将查询发送到数据库时,而“?”仍将被解释为位置参数占位符。

在您的示例中,您可以使用:

$sql = "SELECT * FROM post WHERE locations ?? :location;";

【讨论】:

【参考方案3】:

好的,最简单的方法是create the new operator 使用相同的选项,例如:

-- Operator: ~!@#%^&(jsonb, text)

-- DROP OPERATOR ~!@#%^&(jsonb, text);

CREATE OPERATOR 
  ~!@#%^& -- Change it to any other non-conflicted symbols combination
(
  PROCEDURE = jsonb_exists,
  LEFTARG = jsonb,
  RIGHTARG = text,
  RESTRICT = contsel,
  JOIN = contjoinsel);
COMMENT ON OPERATOR ~!@#%^&(jsonb, text) IS 'key exists';

(原始脚本由pgAdmin生成)

并以通常的方式使用它

SELECT * FROM post WHERE locations ~!@#%^& :location;

【讨论】:

很好的解决方法。我想使用哪一个的决定将与查询的类型和数量有关。【参考方案4】:

你可以使用

jsonb_exists 而不是 ? jsonb_exists_any 而不是 ?| jsonb_exists_all 而不是 ?&

但是postgresql site上没有文档。

【讨论】:

未定义函数:7 错误:函数 jsonb_exists_any(json, text[]) 不存在,postgres 10【参考方案5】:

对于搜索键并根据 Yoann 的回答,我已经测试过表达式 ( jsonbData ? 'keySearched' ) 等效于 jsonb_exists(jsonbData , 'keySearched')

【讨论】:

【参考方案6】:

使用CREATE OPERATOR ~@& (LEFTARG = jsonb, RIGHTARG = text[], PROCEDURE = jsonb_exists_any) 并使用~@& 代替?|一切都会好起来的

【讨论】:

【参考方案7】:

这对我有用:

jsonb_exists(some_jsonb_array,'search_value');

【讨论】:

以上是关于将 PDO 与 PostgreSQL 一起使用时如何忽略问号作为占位符的主要内容,如果未能解决你的问题,请参考以下文章

将数据表与 PDO 一起使用

是否可以将 Doctrine 与持久 PDO 连接一起使用?

markdown 将PDO与IN子句一起使用

将 mysql_real_escape_string 与 PDO 一起使用(不连接到 localhost 服务器)

使用 PDO 准备好插入后获取最后一个插入 ID

为啥 PHP PDO DSN 是 MySQL 与 PostgreSQL 不同的格式?