38.5. 在 PL/Tcl 裡面訪問數據庫

在 PL/Tcl 過程體裡有下面的命令可以用于從訪問數據庫:

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

執行一個以字串形式給出的 SQL 查詢。查詢中的錯誤會導致拋出一個錯誤。 否則,該命令的返回值是命令處理的行數(選出,插入,更新,或者刪除), 如果該命令是一個功能性語句則返回零。另外,如果查詢是一條SELECT 語句,那麼選出的字段按照下面描述的方法放在 Tcl 變量中。

可選的 -count 值告訴 spi_exec 在該查詢中處理的最大的行數。其效果和把查詢設置為一個遊標, 然後說 FETCH n 是一樣的。

如果查詢是一個SELECT語句,那麼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 結果中的每一行執行一次 (注意:如果給出的查詢不是 SELECT,那麼忽略 loop-body)。 在每次迭代之前,當前行的字段的數值都存儲到 Tcl 變量中去了。比如,

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

將為 pg_class 的每一行打印一行日志信息。 這個特性和其它 Tcl 循環構造的運做方式類似;特別是 continuebreak 在循環體中的作用和平常是一樣的。

如果一個查詢結果的某個字段是 NULL,那麼其目標變量就是 "unset" 而不會設置上什麼東西.

spi_prepare query typelist

為後面的執行準備並保存一個查詢規劃。保存的規劃的生命期就是當前會話的生命期。

查詢可以使用參數,這些參數是規劃實際執行的時候提供的數值的佔位符。 在查詢字串裡用符號 $1 ... $n 引用各個參數。如果查詢使用了參數,那麼參數類型名必需以一個 Tcl 列表的形式給出。 (如果沒有使用參數,那麼給 typelist 寫一個空列表。) 目前,參數類型必需和 pg_type 裡顯示的內部類型名一樣; 比如是int4 而不是 integer

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-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;

請注意在函數裡每個需要 Tcl 看到的反斜扛都必需寫雙份, 因為主分析器在CREATE FUNCTION裡也處理反斜扛。 我們需要在給 spi_prepare 的查詢字串裡放反斜扛, 以確保 $n 標記會原樣傳遞給 spi_prepare, 而不是被 Tcl 的變量代換替換掉。

spi_lastoid

如果該查詢是單行INSERT, 返回最後的 spi_exec 或者 spi_execp 查詢插入的行的 OID。(如果不是,你收到零。)

quote string

在給出的字串裡將所由單引號和反斜扛字符復制成雙份。 它可以用于安全地處理那些要輸入到 spi_exec 或者 spi_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

發出一個日志或者錯誤消息。可能的級別是 DEBUGLOGINFONOTICEWARNINGERROR,和 FATAL。 大多數只是簡單地發出指定消息,就象 C 函數 elogERROR 拋出一個錯誤條件:該行數進一步的執行將中止, 同時退出當前事務。FATAL 退出當前事務並且導致當前會話關閉(可能在 PL/Tcl 函數裡沒有什麼理由使用這個錯誤級別, 提供它主要是為了完整)。