43.5. 从 PL/Tcl 访问数据库

可以使用以下命令从 PL/Tcl 函数的主体访问数据库:

可选的-count值告诉spi_exec在命令中要处理的最大行数。其效果相当于将查询设置为游标,然后说FETCH n

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

如果该命令是SELECT语句,并且没有给出* loop-body *脚本,则仅将结果的第一行存储到 Tcl 变量或数组元素中;否则,结果将保留为 0.剩余的行(如果有)将被忽略。如果查询不返回任何行,则不进行存储。 (可以通过检查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在循环体内以通常的方式工作。

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

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

spi_prepare的返回值是在随后对spi_execp的调用中使用的查询 ID。有关示例,请参见spi_execp

-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 变量替换替代。

"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 命令字符串的一部分。

上一章 首页 下一章