如何在 PostgreSQL 中记录“准备”语句?

Posted

技术标签:

【中文标题】如何在 PostgreSQL 中记录“准备”语句?【英文标题】:How can I log `PREPARE` statements in PostgreSQL? 【发布时间】:2016-12-08 14:29:23 【问题描述】:

我正在使用一个数据库工具(Elixir's Ecto),它使用prepared statements 进行大多数 PostgreSQL 查询。我想看看它是如何以及何时这样做的。

我通过在psql 中运行SHOW config_file; 找到了正确的Postgres 配置文件postgresql.conf。我对其进行了编辑以包含

log_statement = 'all'

正如 Dogbert 建议的 here。

根据the PostgreSQL 9.6 docs,此设置应导致PREPARE 语句被记录。

重启 PostgreSQL 后,我可以 tail -f 其日志文件(运行 postgres 可执行文件时由 -r 标志指定的文件),我确实看到了这样的条目:

LOG:  execute ecto_728: SELECT i0."id", i0."store_id", i0."title", i0."description"
  FROM "items" AS i0 WHERE (i0."description" LIKE $1)
DETAIL:  parameters: $1 = '%foo%'

这对应于一个查询(尽管我认为是通过二进制协议完成的)

EXECUTE ecto_728('%foo%');

但是,我没有看到创建 ecto_728 的原始 PREPARE

我尝试删除并重新创建数据库。之后,同样的查询被执行为ecto_578,所以看起来原来的prepared statement与数据库一起被删除并创建了一个新的。

但是当我在 PostgreSQL 日志中搜索 ecto_578 时,我只看到它正在执行,而不是创建。

如何在 PostgreSQL 日志中查看 PREPARE 语句?

【问题讨论】:

为什么,出于兴趣?无论如何,您在执行时都会看到查询文本。 @CraigRinger 没有实际原因。 :) 我只是想了解我正在使用的工具。例如,我很好奇 Ecto 是否在编译我的代码或启动应用程序时执行 PREPARE。我想我可以在 PostgreSQL 未运行时尝试编译,看看会发生什么。 原来它在给定会话中第一次执行查询之前执行PREPARE,因为"a named prepared-statement object lasts till the end of the current session"所以真的没有其他方法可以做到这一点。 【参考方案1】:

正如您所提到的,您的查询是通过扩展查询协议准备的,这与 PREPARE 语句不同。根据log_statement 的文档:

对于使用扩展查询协议的客户端,日志记录发生在 收到执行消息

(也就是说,当接收到 Parse 或 Bind 消息时,记录不会发生。)

但是,如果你设置log_min_duration_statement = 0,那么:

对于使用扩展查询协议的客户端,解析的持续时间, 独立记录绑定和执行步骤

同时启用这两个设置将在每次执行时为您提供两个日志条目(收到消息时来自log_statement,在执行完成后另一个来自log_min_duration_statement)。

【讨论】:

不错!如果您好奇,请参阅我的答案以了解我的发现。干杯!【参考方案2】:

Nick's answer 是正确的;我只是在回答添加我通过尝试学到的知识。

首先,我能够在日志中看到三个单独的操作:parse 创建准备好的语句,bind 为其提供参数,execute 让数据库实际携带它出并返回结果。这被描述为in the PostgreSQL docs for the "extended query protocol"。

LOG:  duration: 0.170 ms  parse ecto_918: SELECT i0."id", i0."store_id",
   i0."title", i0."description"
  FROM "items" AS i0 WHERE (i0."description" LIKE $1)
LOG:  duration: 0.094 ms  bind ecto_918: SELECT i0."id", i0."store_id",
  i0."title", i0."description"
  FROM "items" AS i0 WHERE (i0."description" LIKE $1)
DETAIL:  parameters: $1 = '%priceless%'
LOG:  execute ecto_918: SELECT i0."id", i0."store_id", 
  i0."title", i0."description"
  FROM "items" AS i0 WHERE (i0."description" LIKE $1)
DETAIL:  parameters: $1 = '%priceless%'

此输出是在运行一些自动化测试期间生成的。在随后的运行中,我看到相同的查询具有不同的名称 - 例如,ecto_1573。这没有删除数据库,甚至没有重新启动 PostgreSQL 进程。 The docs 这么说

如果成功创建,一个命名的prepared-statement对象将持续到 当前会话的结束,除非明确销毁。

所以这些语句必须在每个会话中重新创建,并且我的测试套件可能在每次运行时都有一个新会话。

【讨论】:

以上是关于如何在 PostgreSQL 中记录“准备”语句?的主要内容,如果未能解决你的问题,请参考以下文章

将 Oracle upsert 转换为 PostgreSQL 准备好的语句

对于 postgresql,java sql 准备好的语句不能正确转义

将 SQL 语句作为记录值插入 PostgreSQL 列

如何在准备好的语句php中的不同行中插入多条记录

在 postgresql 中使用替换更新语句

如何在 postgresql 中使用多个 RETURN QUERY 限制结果集