Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

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 以通常方式工作。

如果查询结果的某一列为空,则其目标变量将为 unset,而不会进行设置。

spi_prepare 查询 typelist

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

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

spi_prepare 的返回值是一个查询 ID,将在对 spi_execp 的后续调用中使用。有关示例,请参见 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 哪些参数为空值。如果已给出,则必须与 value-list 长度完全相同。如果未给出,则所有参数值均为非空。

除了指定查询及其参数的方式之外,spi_execp 的工作方式与 spi_exec 完全相同。-count -array loop-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

包含在 命令 中的 Tcl 脚本在 SQL 子事务里执行。如果脚本返回错误,在将错误返回到周围的 Tcl 代码前,该整个子事务将被回滚。有关详细信息和示例,请参阅 第 42.9 节

quote 字符串

在给定字符串中将所有单引号和反斜杠字符加倍。可通过这种方法安全引用要插入到 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 级别 msg

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