40.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在循环体中按通常方式工作。如果查询结果的某一列为空值,对应的目标变量将被“取消设置”而不是被设置。
spi_preparequerytypelist为以后的执行准备并保存一个查询计划。保存的计划将在当前会话的生存期内保留。
查询可以使用参数,也就是在计划实际执行时才提供值的占位符。在查询字符串中,用符号
$1...$引用参数。If the query uses parameters, the names of the parameter types must be given as a Tcl list. (Write an empty list forntypelistif no parameters are used.)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完全相同。If it is not given, all the parameter values are nonnull.除了指定查询及其参数的方式之外,
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;We need backslashes inside the query string given to
spi_prepareto ensure that the$markers will be passed through tonspi_prepareas-is, and not replaced by Tcl variable substitution.spi_lastoid返回最近一次
spi_exec或spi_execp插入的行的 OID,前提是该命令是单行INSERT且被修改的表包含 OID。(否则返回零。)quotestring把给定字符串中所有单引号和反斜线字符加倍。这可以用来安全地引用将被插入到交给
spi_exec或spi_prepare的 SQL 命令中的字符串。例如,考虑下面这样的 SQL 命令字符串:"SELECT '$val' AS ret"
where the Tcl variable
valactually containsdoesn't. This would result in the final command string:SELECT 'doesn't' AS ret
which would cause a parse error during
spi_execorspi_prepare. To work properly, the submitted command should contain:SELECT 'doesn''t' AS ret
which can be formed in PL/Tcl using:
"SELECT '[ quote $val ]' AS ret"
One advantage of
spi_execpis that you don't have to quote parameter values like this, since the parameters are never parsed as part of an SQL command string.eloglevelmsg发出一条日志或错误消息。可用的级别有
DEBUG、LOG、INFO、NOTICE、WARNING、ERROR和FATAL。ERROR会引发一个错误条件;如果周围的 Tcl 代码没有捕获它,该错误会传播到调用查询,导致当前事务或子事务被中止。This is effectively the same as the Tclerrorcommand.FATALaborts the transaction and causes the current session to shut down. (There is probably no good reason to use this error level in PL/Tcl functions, but it's provided for completeness.) The other levels only generate messages of different priority levels. Whether messages of a particular priority are reported to the client, written to the server log, or both is controlled by the log_min_messages and client_min_messages configuration variables. See 第 18 章 for more information.