2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本: devel
不支持的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4

42.5. 从 PL/Tcl 访问数据库 #

在本节中,我们遵循 Tcl 的常规约定,使用问号而不是方括号来表示语法摘要中的可选元素。在 PL/Tcl 函数体中访问数据库的命令如下:

spi_exec ?-count n? ?-array name? command ?loop-body?

执行一个给定的 SQL 命令字符串。命令中的错误会导致引发一个错误。否则,spi_exec 的返回值是该命令处理的行数(选择、插入、更新或删除),如果命令是实用程序语句,则为零。此外,如果命令是 SELECT 语句,则所选列的值会存储在 Tcl 变量中,如下所述。

可选的 -count 值告诉 spi_exec 一旦检索到 n 行就停止,就像查询包含 LIMIT 子句一样。如果 n 为零,则查询运行完成,与省略 -count 时相同。

如果命令是 SELECT 语句,则结果列的值将被放入以列名命名的 Tcl 变量中。如果给出了 -array 选项,则列值将存储在命名的关联数组的元素中,并使用列名作为数组索引。此外,结果中的当前行号(从零开始计数)将存储在名为 .tupno 的数组元素中,除非该名称已被用作结果中的列名。

如果命令是 SELECT 语句且未给出 loop-body 脚本,则只有结果的第一行会被存储到 Tcl 变量或数组元素中;其余行(如果有)将被忽略。如果查询没有返回行,则不发生存储。(可以通过检查 spi_exec 的返回值来检测这种情况。)例如

spi_exec "SELECT count(*) AS cnt FROM pg_proc"

将把 Tcl 变量 $cnt 设置为 pg_proc 系统目录中的行数。

如果给出了可选的 loop-body 参数,它是一段 Tcl 脚本,该脚本将为查询结果中的每一行执行一次。(如果给定的命令不是 SELECT,则 loop-body 被忽略。)当前行的列值将在每次迭代之前存储到 Tcl 变量或数组元素中。例如

spi_exec -array C "SELECT * FROM pg_class" {
    elog DEBUG "have table $C(relname)"
}

将为 pg_class 的每一行打印一条日志消息。此功能与其他 Tcl 循环构造类似;特别是 continuebreak 在循环体内的行为方式与通常情况相同。

如果查询结果的列为 NULL,则其目标变量将被“取消设置”而不是被设置。

spi_prepare query typelist

准备并保存一个查询计划以供稍后执行。保存的计划将在当前会话的生命周期内保留。

查询可以使用参数,即占位符,用于在每次实际执行计划时提供值。在查询字符串中,通过符号 $1 ... $n 来引用参数。如果查询使用参数,则参数类型的名称必须以 Tcl 列表的形式给出。(如果未使用参数,请为 typelist 写入一个空列表。)

spi_prepare 的返回值是一个用于后续调用 spi_execp 的查询 ID。请参阅 spi_execp 以获取示例。

spi_execp ?-count n? ?-array name? ?-nulls string? queryid ?value-list? ?loop-body?

执行一个之前使用 spi_prepare 准备的查询。queryidspi_prepare 返回的 ID。如果查询引用了参数,则必须提供 value-list。这是一个 Tcl 列表,包含参数的实际值。该列表的长度必须与之前传递给 spi_prepare 的参数类型列表相同。如果查询没有参数,请省略 value-list

-nulls 的可选值是一个由空格和 'n' 字符组成的字符串,用于告诉 spi_execp 哪些参数是 NULL 值。如果给定,其长度必须与 value-list 完全相同。如果未给定,则所有参数值都不是 NULL。

除了指定查询及其参数的方式外,spi_execp 的工作方式与 spi_exec 相同。-count-arrayloop-body 选项以及结果值都相同。

这是一个使用准备好的计划的 PL/Tcl 函数的示例

CREATE FUNCTION t1_count(integer, integer) RETURNS integer AS $$
    if {![ info exists GD(plan) ]} {
        # prepare the saved plan on the first call
        set GD(plan) [ spi_prepare \
                "SELECT count(*) AS cnt FROM t1 WHERE num >= \$1 AND num <= \$2" \
                [ list int4 int4 ] ]
    }
    spi_execp -count 1 $GD(plan) [ list $1 $2 ]
    return $cnt
$$ LANGUAGE pltcl;

我们需要在传递给 spi_prepare 的查询字符串中添加反斜杠,以确保 $n 标记会原样传递给 spi_prepare,而不是被 Tcl 变量替换。

subtransaction command

command 中的 Tcl 脚本将在 SQL 子事务中执行。如果脚本返回错误,则在将错误返回给外部 Tcl 代码之前,整个子事务都会被回滚。有关更多详细信息和示例,请参阅 第 42.9 节

quote string

将给定字符串中的所有单引号和反斜杠字符加倍。这可以用来安全地引用要插入到传递给 spi_execspi_prepare 的 SQL 命令中的字符串。例如,考虑一个 SQL 命令字符串,如

"SELECT '$val' AS ret"

其中 Tcl 变量 val 实际包含 doesn't。这将导致最终的命令字符串

SELECT 'doesn't' AS ret

这将在 spi_execspi_prepare 执行期间导致解析错误。为了正常工作,提交的命令应该包含

SELECT 'doesn''t' AS ret

这可以使用以下方式在 PL/Tcl 中形成:

"SELECT '[ quote $val ]' AS ret"

spi_execp 的一个优点是您不必像这样引用参数值,因为参数永远不会作为 SQL 命令字符串的一部分进行解析。

elog level msg

发出日志或错误消息。可能的级别包括 DEBUGLOGINFONOTICEWARNINGERRORFATALERROR 会引发一个错误条件;如果 Tcl 代码没有捕获该错误,则错误将传播到调用查询,导致当前事务或子事务中止。这实际上与 Tcl 的 error 命令相同。FATAL 会中止事务并导致当前会话关闭。(在 PL/Tcl 函数中使用此错误级别可能没有充分的理由,但为了完整性而提供。)其他级别仅生成不同优先级的消息。是否将特定优先级的消息报告给客户端、写入服务器日志或两者兼有,由 log_min_messagesclient_min_messages 配置变量控制。有关更多信息,请参阅 第 19 章第 42.8 节

提交更正

如果您在文档中看到任何不正确、与您对特定功能的体验不符或需要进一步澄清的内容,请使用 此表单 来报告文档问题。