39.5. 从 PL/Tcl 访问数据库 #
在 PL/Tcl 函数体中可以使用下列命令来访问数据库:
spi_exec?-countn? ?-arrayname?command?loop-body?执行以字符串形式给出的 SQL 命令。命令出错时会引发错误。否则,
spi_exec的返回值是该命令处理的行数(选出、插入、更新或删除的行),如果命令是工具语句则返回零。此外,如果命令是SELECT语句,则所选列的值会按下文所述放入 Tcl 变量中。可选的
-count值告诉spi_exec此命令最多处理多少行,其效果类似于将查询设为游标后执行FETCH。n如果命令是
SELECT语句,则结果列的值会放入以列名命名的 Tcl 变量中。如果给定了-array选项,则列值会存储在指定的关联数组元素中,列名用作数组索引。如果命令是
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 循环构造;特别是continue和break在循环体中按通常方式工作。如果查询结果的某一列为空值,则其目标变量会被 “unset”(取消设置),而不是被设置。
spi_preparequerytypelist准备并保存一个查询计划以供后续执行。保存的计划会在当前会话的整个生命周期内保留。
查询可以使用参数,也就是在实际执行该计划时要提供的值的占位符。在查询字符串中,用符号
$1...$引用参数。如果查询使用了参数,则必须以 Tcl 列表的形式给出各参数类型的名称。(如果未使用参数,则为ntypelist写一个空列表。)spi_prepare的返回值是一个查询 ID,供后续调用spi_execp时使用。示例参见spi_execp。spi_execp?-countn? ?-arrayname? ?-nullsstring?queryid?value-list? ?loop-body?执行先前用
spi_prepare准备好的查询。queryid是spi_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的查询字符串中加入反斜杠,以确保$标记会原样传递给nspi_prepare,而不会被 Tcl 执行变量替换。spi_lastoid如果最后一次
spi_exec或spi_execp执行的是单行INSERT,且被修改的表包含 OID,则返回所插入行的 OID。(否则返回零。)quotestring将给定字符串中的所有单引号和反斜杠字符都加倍。这可用于安全地为那些要插入到传给
spi_exec或spi_prepare的 SQL 命令中的字符串加引号。例如,考虑如下 SQL 命令字符串:"SELECT '$val' AS ret"
其中 Tcl 变量
val的实际内容是doesn't。这会得到最终命令字符串:SELECT 'doesn't' AS ret
这会在
spi_exec或spi_prepare期间导致解析错误。要使其正常工作,提交的命令应当包含:SELECT 'doesn''t' AS ret
它在 PL/Tcl 中可以这样构成:
"SELECT '[ quote $val ]' AS ret"
spi_execp的一个优点是,你不必像这样为参数值加引号,因为参数永远不会被当作 SQL 命令字符串的一部分来解析。eloglevelmsg发出日志或错误消息。可用级别包括
DEBUG、LOG、INFO、NOTICE、WARNING、ERROR和FATAL。ERROR会引发错误条件;如果外围 Tcl 代码没有捕获它,该错误就会传播到调用查询,导致当前事务或子事务中止。这实际上与 Tcl 的error命令相同。FATAL会中止事务并导致当前会话关闭。(在 PL/Tcl 函数中使用这个错误级别可能并没有什么充分理由,但为了完整性仍提供它。)其他级别只会生成不同优先级的消息。某个特定优先级的消息是报告给客户端、写入服务器日志,还是两者都做,由log_min_messages和client_min_messages配置变量控制。参见第 18 章了解更多信息。