控制結構可能是 PL/pgSQL 中最有用(也是最重要的)部分。使用 PL/pgSQL 的控制結構,您可以非常靈活且強大地操作 PostgreSQL 資料。
有兩個命令可用於從函式返回資料: RETURN
和 RETURN NEXT
。
RETURN
#RETURN expression
;
RETURN
後面跟表示式,會終止函式並將 expression
的值返回給呼叫者。此形式用於不返回集合的 PL/pgSQL 函式。
在返回標量型別的函式中,表示式的結果將自動轉換為函式的返回型別,如賦值部分所述。但要返回複合(行)值,您必須編寫一個提供完全請求列集的表示式。這可能需要使用顯式轉換。
如果您宣告的函式帶有輸出引數,則只需編寫 RETURN
,後面不跟表示式。輸出引數變數的當前值將被返回。
如果您宣告的函式返回 void
,則可以使用 RETURN
語句提前退出函式;但不要在 RETURN
後面寫表示式。
函式的返回值不能未定義。如果控制在未遇到 RETURN
語句的情況下到達函式頂層塊的末尾,將發生執行時錯誤。但是,此限制不適用於帶有輸出引數的函式和返回 void
的函式。在這些情況下,如果頂層塊結束,將自動執行 RETURN
語句。
一些例子
-- functions returning a scalar type RETURN 1 + 2; RETURN scalar_var; -- functions returning a composite type RETURN composite_type_var; RETURN (1, 2, 'three'::text); -- must cast columns to correct types
RETURN NEXT
和 RETURN QUERY
#RETURN NEXTexpression
; RETURN QUERYquery
; RETURN QUERY EXECUTEcommand-string
[ USINGexpression
[, ... ] ];
當 PL/pgSQL 函式宣告為返回 SETOF
時,處理方式略有不同。在這種情況下,要返回的單個項由一系列 sometype
RETURN NEXT
或 RETURN QUERY
命令指定,然後使用一個不帶引數的最終 RETURN
命令來指示函式已執行完畢。RETURN NEXT
可與標量和複合資料型別一起使用;對於複合結果型別,將返回整個結果“表”。RETURN QUERY
將執行查詢的結果追加到函式的返回集中。RETURN NEXT
和 RETURN QUERY
可以在單個集合返回函式中自由混合使用,在這種情況下,它們的輸出將連線起來。
RETURN NEXT
和 RETURN QUERY
實際上並不會從函式返回——它們只是將零行或多行追加到函式的返回集中。然後,執行將繼續 PL/pgSQL 函式中的下一個語句。隨著連續的 RETURN NEXT
或 RETURN QUERY
命令的執行,結果集將被構建起來。最終的 RETURN
(應不帶引數)會導致控制退出函式(或者您可以讓控制到達函式末尾)。
RETURN QUERY
有一個變體 RETURN QUERY EXECUTE
,它指定要動態執行的查詢。引數表示式可以透過 USING
插入到計算出的查詢字串中,這與普通 EXECUTE
命令中的方式相同。
如果您宣告的函式帶有輸出引數,則只需編寫 RETURN NEXT
,後面不跟表示式。每次執行時,輸出引數變數的當前值將被儲存,以便最終作為結果集中的一行返回。請注意,您必須將函式宣告為返回 SETOF record
(當有多個輸出引數時),或 SETOF
(當只有一個型別為 sometype
sometype
的輸出引數時),才能建立帶有輸出引數的集合返回函式。
以下是一個使用 RETURN NEXT
的函式示例
CREATE TABLE foo (fooid INT, foosubid INT, fooname TEXT); INSERT INTO foo VALUES (1, 2, 'three'); INSERT INTO foo VALUES (4, 5, 'six'); CREATE OR REPLACE FUNCTION get_all_foo() RETURNS SETOF foo AS $BODY$ DECLARE r foo%rowtype; BEGIN FOR r IN SELECT * FROM foo WHERE fooid > 0 LOOP -- can do some processing here RETURN NEXT r; -- return current row of SELECT END LOOP; RETURN; END; $BODY$ LANGUAGE plpgsql; SELECT * FROM get_all_foo();
以下是一個使用 RETURN QUERY
的函式示例
CREATE FUNCTION get_available_flightid(date) RETURNS SETOF integer AS $BODY$ BEGIN RETURN QUERY SELECT flightid FROM flight WHERE flightdate >= $1 AND flightdate < ($1 + 1); -- Since execution is not finished, we can check whether rows were returned -- and raise exception if not. IF NOT FOUND THEN RAISE EXCEPTION 'No flight at %.', $1; END IF; RETURN; END; $BODY$ LANGUAGE plpgsql; -- Returns available flights or raises exception if there are no -- available flights. SELECT * FROM get_available_flightid(CURRENT_DATE);
如上所述,RETURN NEXT
和 RETURN QUERY
的當前實現是在函式返回之前儲存整個結果集。這意味著,如果 PL/pgSQL 函式產生非常大的結果集,效能可能會很差:資料將被寫入磁碟以避免記憶體不足,但函式本身在生成整個結果集之前不會返回。未來版本的 PL/pgSQL 可能會允許使用者定義沒有此限制的集合返回函式。當前,資料開始寫入磁碟的點由 work_mem 配置變數控制。具有足夠記憶體將更大結果集儲存在記憶體中的管理員應考慮增加此引數。
過程沒有返回值。因此,過程可以在沒有 RETURN
語句的情況下結束。如果您希望使用 RETURN
語句提前退出程式碼,則只需編寫 RETURN
,後面不跟表示式。
如果過程有輸出引數,則輸出引數變數的最終值將返回給呼叫者。
PL/pgSQL 函式、過程或 DO
塊可以使用 CALL
呼叫過程。輸出引數的處理方式與普通 SQL 中的 CALL
工作方式不同。過程的每個 OUT
或 INOUT
引數必須對應於 CALL
語句中的一個變數,並且無論過程返回什麼,都會在過程返回後將其分配回該變數。例如
CREATE PROCEDURE triple(INOUT x int) LANGUAGE plpgsql AS $$ BEGIN x := x * 3; END; $$; DO $$ DECLARE myvar int := 5; BEGIN CALL triple(myvar); RAISE NOTICE 'myvar = %', myvar; -- prints 15 END; $$;
對應於輸出引數的變數可以是簡單變數,也可以是複合型別變數的欄位。當前,它不能是陣列的元素。
IF
和 CASE
語句允許您根據特定條件執行替代命令。PL/pgSQL 有三種形式的 IF
IF ... THEN ... END IF
IF ... THEN ... ELSE ... END IF
IF ... THEN ... ELSIF ... THEN ... ELSE ... END IF
以及兩種形式的 CASE
CASE ... WHEN ... THEN ... ELSE ... END CASE
CASE WHEN ... THEN ... ELSE ... END CASE
IF-THEN
#IFboolean-expression
THENstatements
END IF;
IF-THEN
語句是最簡單的 IF
形式。如果條件為真,則執行 THEN
和 END IF
之間的語句。否則,將跳過它們。
示例:
IF v_user_id <> 0 THEN UPDATE users SET email = v_email WHERE user_id = v_user_id; END IF;
IF-THEN-ELSE
#IFboolean-expression
THENstatements
ELSEstatements
END IF;
IF-THEN-ELSE
語句在 IF-THEN
的基礎上增加了功能,允許您指定當條件不為真時應執行的替代語句集。(請注意,這包括條件評估為 NULL 的情況。)
示例
IF parentid IS NULL OR parentid = '' THEN RETURN fullname; ELSE RETURN hp_true_filename(parentid) || '/' || fullname; END IF;
IF v_count > 0 THEN INSERT INTO users_count (count) VALUES (v_count); RETURN 't'; ELSE RETURN 'f'; END IF;
IF-THEN-ELSIF
#IFboolean-expression
THENstatements
[ ELSIFboolean-expression
THENstatements
[ ELSIFboolean-expression
THENstatements
... ] ] [ ELSEstatements
] END IF;
有時會存在兩個以上的選擇。IF-THEN-ELSIF
提供了一種方便的方法來依次檢查多個選項。IF
條件按順序測試,直到找到第一個為真的條件。然後執行關聯的語句,之後控制傳遞到 END IF
之後的下一個語句。(任何後續的 IF
條件將 不會 進行測試。)如果所有 IF
條件都不為真,則執行 ELSE
塊(如果存在)。
以下是一個例子
IF number = 0 THEN result := 'zero'; ELSIF number > 0 THEN result := 'positive'; ELSIF number < 0 THEN result := 'negative'; ELSE -- hmm, the only other possibility is that number is null result := 'NULL'; END IF;
關鍵字 ELSIF
也可以拼寫為 ELSEIF
。
另一種完成相同任務的方法是巢狀 IF-THEN-ELSE
語句,如下面的示例所示
IF demo_row.sex = 'm' THEN pretty_sex := 'man'; ELSE IF demo_row.sex = 'f' THEN pretty_sex := 'woman'; END IF; END IF;
然而,這種方法需要為每個 IF
編寫匹配的 END IF
,因此當存在許多選項時,它比使用 ELSIF
更加麻煩。
CASE
#CASEsearch-expression
WHENexpression
[,expression
[ ... ]] THENstatements
[ WHENexpression
[,expression
[ ... ]] THENstatements
... ] [ ELSEstatements
] END CASE;
簡單形式的 CASE
基於運算元的相等性提供條件執行。search-expression
被(僅一次)計算,並依次與 WHEN
子句中的每個 expression
進行比較。如果找到匹配項,則執行相應的 statements
,然後控制傳遞到 END CASE
之後的下一個語句。(後續的 WHEN
表示式不會被評估。)如果未找到匹配項,則執行 ELSE
statements
;但如果 ELSE
不存在,則會引發 CASE_NOT_FOUND
異常。
以下是一個簡單的例子
CASE x WHEN 1, 2 THEN msg := 'one or two'; ELSE msg := 'other value than one or two'; END CASE;
CASE
#CASE WHENboolean-expression
THENstatements
[ WHENboolean-expression
THENstatements
... ] [ ELSEstatements
] END CASE;
搜尋式形式的 CASE
基於布林表示式的真偽提供條件執行。WHEN
子句的每個 boolean-expression
按順序評估,直到找到一個產生 true
的表示式。然後執行相應的 statements
,然後控制傳遞到 END CASE
之後的下一個語句。(後續的 WHEN
表示式不會被評估。)如果沒有找到真結果,則執行 ELSE
statements
;但如果 ELSE
不存在,則會引發 CASE_NOT_FOUND
異常。
以下是一個例子
CASE WHEN x BETWEEN 0 AND 10 THEN msg := 'value is between zero and ten'; WHEN x BETWEEN 11 AND 20 THEN msg := 'value is between eleven and twenty'; END CASE;
這種形式的 CASE
與 IF-THEN-ELSIF
完全等價,除了規則是:到達一個省略的 ELSE
子句會引發錯誤,而不是什麼都不做。
使用 LOOP
、EXIT
、CONTINUE
、WHILE
、FOR
和 FOREACH
語句,您可以安排您的 PL/pgSQL 函式重複執行一系列命令。
LOOP
#[ <<label
>> ] LOOPstatements
END LOOP [label
];
LOOP
定義一個無條件迴圈,該迴圈將無限重複,直到被 EXIT
或 RETURN
語句終止。可選的 label
可以被巢狀迴圈內的 EXIT
和 CONTINUE
語句使用,以指定這些語句引用哪個迴圈。
EXIT
#EXIT [label
] [ WHENboolean-expression
];
如果沒有給出 label
,則終止最內層迴圈,並接著執行 END LOOP
之後的語句。如果給出了 label
,則它必須是當前或某個外層巢狀迴圈或塊的標籤。然後,指定的迴圈或塊將被終止,並且控制將繼續執行迴圈/塊的相應 END
之後的語句。
如果指定了 WHEN
,則僅當 boolean-expression
為真時才會發生迴圈退出。否則,控制將傳遞到 EXIT
之後的語句。
EXIT
可用於所有型別的迴圈;它不限於用於無條件迴圈。
當與 BEGIN
塊一起使用時,EXIT
將控制傳遞到塊末尾之後的下一個語句。請注意,為此目的必須使用標籤;未標記的 EXIT
永遠不會被視為匹配 BEGIN
塊。(這是對 PostgreSQL 8.4 之前的版本的更改,當時允許未標記的 EXIT
匹配 BEGIN
塊。)
示例
LOOP -- some computations IF count > 0 THEN EXIT; -- exit loop END IF; END LOOP; LOOP -- some computations EXIT WHEN count > 0; -- same result as previous example END LOOP; <<ablock>> BEGIN -- some computations IF stocks > 100000 THEN EXIT ablock; -- causes exit from the BEGIN block END IF; -- computations here will be skipped when stocks > 100000 END;
CONTINUE
#CONTINUE [label
] [ WHENboolean-expression
];
如果沒有給出 label
,則開始最內層迴圈的下一次迭代。也就是說,迴圈體中剩餘的所有語句都將被跳過,並且控制將返回到迴圈控制表示式(如果存在)以確定是否需要另一次迴圈迭代。如果存在 label
,則它指定了將繼續執行的迴圈的標籤。
如果指定了 WHEN
,則僅當 boolean-expression
為真時才會開始迴圈的下一次迭代。否則,控制將傳遞到 CONTINUE
之後的語句。
CONTINUE
可用於所有型別的迴圈;它不限於用於無條件迴圈。
示例
LOOP -- some computations EXIT WHEN count > 100; CONTINUE WHEN count < 50; -- some computations for count IN [50 .. 100] END LOOP;
WHILE
#[ <<label
>> ] WHILEboolean-expression
LOOPstatements
END LOOP [label
];
WHILE
語句在 boolean-expression
評估為真時重複執行一系列語句。表示式在每次進入迴圈體之前進行檢查。
例如
WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP -- some computations here END LOOP; WHILE NOT done LOOP -- some computations here END LOOP;
FOR
(整數變體) #[ <<label
>> ] FORname
IN [ REVERSE ]expression
..expression
[ BYexpression
] LOOPstatements
END LOOP [label
];
此形式的 FOR
建立一個迴圈,該迴圈在整數值範圍內迭代。變數 name
被自動定義為 integer
型別,並且僅在迴圈內部存在(在迴圈內部,忽略該變數名的任何現有定義)。給出範圍的下限和上限的兩個表示式在進入迴圈時被評估一次。如果未指定 BY
子句,則迭代步長為 1,否則為 BY
子句中指定的值,該值在迴圈進入時被評估一次。如果指定了 REVERSE
,則在每次迭代後減去步長值,而不是加。
整數 FOR
迴圈的一些示例
FOR i IN 1..10 LOOP -- i will take on the values 1,2,3,4,5,6,7,8,9,10 within the loop END LOOP; FOR i IN REVERSE 10..1 LOOP -- i will take on the values 10,9,8,7,6,5,4,3,2,1 within the loop END LOOP; FOR i IN REVERSE 10..1 BY 2 LOOP -- i will take on the values 10,8,6,4,2 within the loop END LOOP;
如果下限大於上限(在 REVERSE
的情況下小於),則根本不執行迴圈體。不會引發錯誤。
如果 label
附加到 FOR
迴圈,則可以使用帶標籤的限定名稱引用整數迴圈變數。
使用不同型別的 FOR
迴圈,您可以遍歷查詢結果並相應地操作這些資料。語法是
[ <<label
>> ] FORtarget
INquery
LOOPstatements
END LOOP [label
];
target
是一個記錄變數、行變數或逗號分隔的標量變數列表。target
被依次賦值 query
返回的每一行,並且迴圈體為每一行執行。例如
CREATE FUNCTION refresh_mviews() RETURNS integer AS $$ DECLARE mviews RECORD; BEGIN RAISE NOTICE 'Refreshing all materialized views...'; FOR mviews IN SELECT n.nspname AS mv_schema, c.relname AS mv_name, pg_catalog.pg_get_userbyid(c.relowner) AS owner FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace) WHERE c.relkind = 'm' ORDER BY 1 LOOP -- Now "mviews" has one record with information about the materialized view RAISE NOTICE 'Refreshing materialized view %.% (owner: %)...', quote_ident(mviews.mv_schema), quote_ident(mviews.mv_name), quote_ident(mviews.owner); EXECUTE format('REFRESH MATERIALIZED VIEW %I.%I', mviews.mv_schema, mviews.mv_name); END LOOP; RAISE NOTICE 'Done refreshing materialized views.'; RETURN 1; END; $$ LANGUAGE plpgsql;
如果迴圈被 EXIT
語句終止,則最後一個賦值的行值在迴圈之後仍然可訪問。
此型別 FOR
語句中使用的 query
可以是任何返回行給呼叫者的 SQL 命令:SELECT
是最常見的情況,但您也可以使用帶有 RETURNING
子句的 INSERT
、UPDATE
、DELETE
或 MERGE
。某些實用程式命令,如 EXPLAIN
,也將起作用。
PL/pgSQL 變數被查詢引數替換,並且查詢計劃被快取以供重用,具體細節請參見 Section 41.11.1 和 Section 41.11.2。
FOR-IN-EXECUTE
語句是遍歷行的另一種方式
[ <<label
>> ] FORtarget
IN EXECUTEtext_expression
[ USINGexpression
[, ... ] ] LOOPstatements
END LOOP [label
];
這與前一種形式類似,只是源查詢被指定為一個字串表示式,該表示式在每次進入 FOR
迴圈時進行評估和重新計劃。這允許程式設計師像使用普通 EXECUTE
語句一樣,選擇預計劃查詢的速度或動態查詢的靈活性。與 EXECUTE
一樣,可以透過 USING
將引數值插入到動態命令中。
指定應遍歷其結果的查詢的另一種方法是將其宣告為遊標。這在 Section 41.7.4 中進行了描述。
FOREACH
迴圈非常類似於 FOR
迴圈,但它不是遍歷 SQL 查詢返回的行,而是遍歷陣列值的元素。(通常,FOREACH
用於迴圈遍歷複合值表示式的元件;未來可能會新增用於迴圈遍歷除陣列以外的複合型別的變體。)用於遍歷陣列的 FOREACH
語句是
[ <<label
>> ] FOREACHtarget
[ SLICEnumber
] IN ARRAYexpression
LOOPstatements
END LOOP [label
];
不帶 SLICE
,或者如果指定了 SLICE 0
,則迴圈遍歷透過評估 expression
生成的陣列的單個元素。target
變數按順序被賦值每個元素的值,並且迴圈體為每個元素執行。以下是一個遍歷整數陣列元素的示例
CREATE FUNCTION sum(int[]) RETURNS int8 AS $$ DECLARE s int8 := 0; x int; BEGIN FOREACH x IN ARRAY $1 LOOP s := s + x; END LOOP; RETURN s; END; $$ LANGUAGE plpgsql;
無論陣列維度如何,元素都按儲存順序訪問。雖然 target
通常只是一個變數,但在遍歷複合值(記錄)陣列時,它可以是一個變數列表。在這種情況下,對於每個陣列元素,變數將從複合值的連續列中賦值。
當 SLICE
為正值時,FOREACH
遍歷陣列的切片而不是單個元素。SLICE
值必須是一個不大於陣列維數的整數常量。target
變數必須是一個數組,它接收陣列值的連續切片,其中每個切片都具有 SLICE
指定的維數。以下是一個遍歷一維切片的示例
CREATE FUNCTION scan_rows(int[]) RETURNS void AS $$ DECLARE x int[]; BEGIN FOREACH x SLICE 1 IN ARRAY $1 LOOP RAISE NOTICE 'row = %', x; END LOOP; END; $$ LANGUAGE plpgsql; SELECT scan_rows(ARRAY[[1,2,3],[4,5,6],[7,8,9],[10,11,12]]); NOTICE: row = {1,2,3} NOTICE: row = {4,5,6} NOTICE: row = {7,8,9} NOTICE: row = {10,11,12}
預設情況下,PL/pgSQL 函式中發生的任何錯誤都會中止函式和周圍事務的執行。您可以使用帶有 EXCEPTION
子句的 BEGIN
塊來捕獲錯誤並從中恢復。語法是普通 BEGIN
塊語法的擴充套件
[ <<label
>> ] [ DECLAREdeclarations
] BEGINstatements
EXCEPTION WHENcondition
[ ORcondition
... ] THENhandler_statements
[ WHENcondition
[ ORcondition
... ] THENhandler_statements
... ] END;
如果沒有發生錯誤,此形式的塊將簡單地執行所有 statements
,然後控制傳遞到 END
之後的下一個語句。但是,如果在 statements
中發生錯誤,則會放棄對 statements
的進一步處理,並將控制傳遞給 EXCEPTION
列表。列表將被搜尋以查詢與發生的錯誤匹配的第一個 condition
。如果找到匹配項,則執行相應的 handler_statements
,然後控制傳遞到 END
之後的下一個語句。如果沒有找到匹配項,則錯誤將像沒有 EXCEPTION
子句一樣傳播出去:錯誤可以被帶有 EXCEPTION
的巢狀塊捕獲,如果沒有,則會中止函式的處理。
condition
名稱可以是 Appendix A 中顯示的任何名稱。類別名稱匹配其類別內的任何錯誤。特殊條件名稱 OTHERS
匹配除 QUERY_CANCELED
和 ASSERT_FAILURE
之外的所有錯誤型別。(透過名稱捕獲這兩個錯誤型別是可能的,但通常不明智。)條件名稱不區分大小寫。此外,可以透過 SQLSTATE
程式碼指定錯誤條件;例如,以下是等效的
WHEN division_by_zero THEN ... WHEN SQLSTATE '22012' THEN ...
如果在選定的 handler_statements
中發生新錯誤,它將無法被此 EXCEPTION
子句捕獲,但會傳播出去。一個圍繞的 EXCEPTION
子句可以捕獲它。
當錯誤被 EXCEPTION
子句捕獲時,PL/pgSQL 函式的區域性變數將保持在錯誤發生時的狀態,但塊內對持久資料庫狀態的所有更改都將被回滾。例如,考慮此片段
INSERT INTO mytab(firstname, lastname) VALUES('Tom', 'Jones'); BEGIN UPDATE mytab SET firstname = 'Joe' WHERE lastname = 'Jones'; x := x + 1; y := x / 0; EXCEPTION WHEN division_by_zero THEN RAISE NOTICE 'caught division_by_zero'; RETURN x; END;
當控制到達對 y
的賦值時,它將因 division_by_zero
錯誤而失敗。這將由 EXCEPTION
子句捕獲。在 RETURN
語句中返回的值將是 x
的遞增值,但 UPDATE
命令的效果將被回滾。但是,在塊之前的 INSERT
命令不會被回滾,因此最終結果是資料庫包含 Tom Jones
而不是 Joe Jones
。
包含 EXCEPTION
子句的塊在進入和退出時比沒有它的塊要昂貴得多。因此,請不要在不需要時使用 EXCEPTION
。
示例 41.2. 帶有 UPDATE
/INSERT
的異常
此示例使用異常處理來執行 UPDATE
或 INSERT
(視情況而定)。建議應用程式使用帶有 ON CONFLICT DO UPDATE
的 INSERT
,而不是實際使用此模式。此示例主要用於說明 PL/pgSQL 控制流結構的使用。
CREATE TABLE db (a INT PRIMARY KEY, b TEXT); CREATE FUNCTION merge_db(key INT, data TEXT) RETURNS VOID AS $$ BEGIN LOOP -- first try to update the key UPDATE db SET b = data WHERE a = key; IF found THEN RETURN; END IF; -- not there, so try to insert the key -- if someone else inserts the same key concurrently, -- we could get a unique-key failure BEGIN INSERT INTO db(a,b) VALUES (key, data); RETURN; EXCEPTION WHEN unique_violation THEN -- Do nothing, and loop to try the UPDATE again. END; END LOOP; END; $$ LANGUAGE plpgsql; SELECT merge_db(1, 'david'); SELECT merge_db(1, 'dennis');
此程式碼假定 unique_violation
錯誤是由 INSERT
引起的,而不是由表上的觸發器函式中的 INSERT
等引起的。它也可能表現不佳,如果表上有多個唯一索引,因為它將重試操作,而不管哪個索引導致了錯誤。透過使用接下來討論的功能來檢查捕獲到的錯誤是否是預期的錯誤,可以獲得更高的安全性。
異常處理程式經常需要識別發生的特定錯誤。PL/pgSQL 中獲取當前異常資訊有兩種方法:特殊變數和 GET STACKED DIAGNOSTICS
命令。
在異常處理程式中,特殊變數 SQLSTATE
包含與引發的異常相對應的錯誤程式碼(有關可能錯誤程式碼的列表,請參閱 Table A.1)。特殊變數 SQLERRM
包含與異常相關的錯誤訊息。這些變數在異常處理程式外部是未定義的。
在異常處理程式中,還可以透過使用 GET STACKED DIAGNOSTICS
命令來檢索當前異常的資訊,該命令的形式是
GET STACKED DIAGNOSTICSvariable
{ = | := }item
[ , ... ];
每個 item
都是一個關鍵字,用於標識要分配給指定 variable
的狀態值(該變數應具有接收它的正確資料型別)。當前可用的狀態項顯示在 Table 41.2 中。
Table 41.2. 錯誤診斷項
名稱 | 型別 | 描述 |
---|---|---|
RETURNED_SQLSTATE |
text |
異常的 SQLSTATE 錯誤程式碼 |
COLUMN_NAME |
text |
與異常相關的列名 |
CONSTRAINT_NAME |
text |
與異常相關的約束名稱 |
PG_DATATYPE_NAME |
text |
與異常相關的資料型別名稱 |
MESSAGE_TEXT |
text |
異常主訊息的文字 |
TABLE_NAME |
text |
與異常相關的表名 |
SCHEMA_NAME |
text |
與異常相關的模式名 |
PG_EXCEPTION_DETAIL |
text |
異常詳細訊息的文字(如果有) |
PG_EXCEPTION_HINT |
text |
異常提示訊息的文字(如果有) |
PG_EXCEPTION_CONTEXT |
text |
描述異常發生時呼叫堆疊的文字行(參見 Section 41.6.9) |
如果異常未為某個項設定值,則返回空字串。
以下是一個例子
DECLARE text_var1 text; text_var2 text; text_var3 text; BEGIN -- some processing which might cause an exception ... EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS text_var1 = MESSAGE_TEXT, text_var2 = PG_EXCEPTION_DETAIL, text_var3 = PG_EXCEPTION_HINT; END;
前面在 Section 41.5.5 中描述的 GET DIAGNOSTICS
命令用於檢索有關當前執行狀態的資訊(而上面討論的 GET STACKED DIAGNOSTICS
命令報告的是關於先前錯誤發生時的執行狀態的資訊)。其 PG_CONTEXT
狀態項對於識別當前執行位置非常有用。PG_CONTEXT
返回一個文字字串,其中包含描述呼叫堆疊的文字行。第一行指的是當前函式和當前正在執行的 GET DIAGNOSTICS
命令。第二行及後續行指代呼叫堆疊更上層的呼叫函式。例如
CREATE OR REPLACE FUNCTION outer_func() RETURNS integer AS $$ BEGIN RETURN inner_func(); END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION inner_func() RETURNS integer AS $$ DECLARE stack text; BEGIN GET DIAGNOSTICS stack = PG_CONTEXT; RAISE NOTICE E'--- Call Stack ---\n%', stack; RETURN 1; END; $$ LANGUAGE plpgsql; SELECT outer_func(); NOTICE: --- Call Stack --- PL/pgSQL function inner_func() line 5 at GET DIAGNOSTICS PL/pgSQL function outer_func() line 3 at RETURN CONTEXT: PL/pgSQL function outer_func() line 3 at RETURN outer_func ------------ 1 (1 row)
GET STACKED DIAGNOSTICS ... PG_EXCEPTION_CONTEXT
返回相同的堆疊跟蹤,但描述的是檢測到錯誤的位置,而不是當前位置。
如果您在文件中看到任何不正確、與您對特定功能的體驗不符或需要進一步澄清的內容,請使用 此表單 報告文件問題。