40.6. 控制结构
控制结构可能是PL/pgSQL中最有用的(以及最重要)的部分了。 利用PL/pgSQL的控制结构, 你可以以非常灵活而且强大的方法操纵PostgreSQL的数据。
40.6.1. 从函数返回
有两个命令可以用来从函数中返回数据:RETURN
和 RETURN NEXT
。
40.6.1.1. RETURN
RETURN _expression_;
带表达式的RETURN
用于终止函数 并把_expression_
的值返回给调用者。 这种形式用于不返回集合的PL/pgSQL函数。
如果函数中返回标量类型,那么表达式结果将被自动转换成函数的返回类型, 就像在赋值中描述的那样。但是要返回一个复合(行)数值, 你必须写一个准确提供需求列集合的表达式,这可能需要显式转换。
如果你声明带有输出参数的函数,那么就只需要写无表达式的RETURN
。 那么输出参数变量的当前值将被返回。
如果你声明函数返回void
,那么一个RETURN
语句可以用于提前退出函数; 但是不要在RETURN
后面写一个表达式。
一个函数的返回值不能是未定义。 如果控制到达了函数最顶层的块而没有碰到一个RETURN
语句, 那么它就会发生一个错误。不过,这个限制不适用于带输出参数的函数以及那些返回void
的函数。 在这些例子里,如果顶层的块结束,则自动执行一个RETURN
语句。
例子:
-- 返回一个标量类型函数
RETURN 1 + 2;
RETURN scalar_var;
-- 返回复合类型函数
RETURN composite_type_var;
RETURN (1, 2, 'three'::text); -- must cast columns to correct types
40.6.1.2. RETURN NEXT
和RETURN QUERY
RETURN NEXT _expression_;
RETURN QUERY _query_;
RETURN QUERY EXECUTE _command-string_ [ USING `_expression_` [, ... ] ];
如果一个PL/pgSQL函数声明为返回SETOF
_sometype_
, 那么遵循的过程则略有不同。 在这种情况下,要返回的独立项是在RETURN NEXT
或者 RETURN QUERY
命令里声明的, 然后最后有一个不带参数的RETURN
命令用于告诉这个函数已经完成执行了。 RETURN NEXT
可以用于标量和复合数据类型;对于复合类型, 将返回一个完整的结果"table"。 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
或者是在只有一个类 型为_sometype_
的输出参数时声明 为SETOF
_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 -- 可以在这里做一些处理
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);
-- 由于没有完成执行,我们可以检查行是否返回并且如果没有则抛出异常。
IF NOT FOUND THEN
RAISE EXCEPTION 'No flight at %.', $1;
END IF;
RETURN;
END
$BODY$
LANGUAGE plpgsql;
-- 如果没有可用航班,则返回可用航班或者抛出异常。
SELECT * FROM get_available_flightid(CURRENT_DATE);
Note: 目前
RETURN NEXT
和RETURN QUERY
实现在从函数返回之前把整个结果集都保存起来, 就像上面描述的那样。这意味着如果一个PL/pgSQL函数生成一个非常大的结果集, 性能可能会很差:数据将被写到磁盘上以避免内存耗尽, 但是函数在完成整个结果集的生成之前不会退出。 将来的PL/pgSQL版本可能会允许用户定义没有这样限制的返回集合的函数。 目前,数据开始向磁盘里写的时刻是由配置变量work_mem控制的。 拥有足够内存的管理员如果想在内存里存储更大的结果集, 则可以考虑把这个参数增大一些。
40.6.2. 条件
IF
和CASE
语句让你可以根据某种条件执行命令。 PL/pgSQL有三种形式的IF
:
IF ... THEN
IF ... THEN ... ELSE
IF ... THEN ... ELSIF ... THEN ... ELSE
以及两种形式的CASE
:
CASE ... WHEN ... THEN ... ELSE ... END CASE
CASE WHEN ... THEN ... ELSE ... END CASE
40.6.2.1. IF-THEN
IF _boolean-expression_ THEN
_statements_
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;
40.6.2.2. IF-THEN-ELSE
IF _boolean-expression_ THEN
_statements_
ELSE
_statements_
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;
40.6.2.3. IF-THEN-ELSIF
IF _boolean-expression_ THEN
_statements_
[ ELSIF `_boolean-expression_` THEN
`_statements_`
[ ELSIF `_boolean-expression_` THEN
`_statements_`
...]]
[ ELSE
`_statements_` ]
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 -- 唯一可能性是号码为空
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
繁琐。
40.6.2.4. 简单CASE
CASE _search-expression_
WHEN _expression_ [, `_expression_` [ ... ]] THEN
_statements_
[ WHEN `_expression_` [, `_expression_` [ ... ]] THEN
`_statements_`
... ]
[ ELSE
`_statements_` ]
END CASE;
CASE
简单的形式提供基于操作数平等的条件执行。_search-expression_
被评价并且 先后比较WHEN
子句中的每个_表达式_
。 如果找到匹配,那么相应的_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;
40.6.2.5. 搜索CASE
CASE
WHEN _boolean-expression_ THEN
_statements_
[ WHEN `_boolean-expression_` THEN
`_statements_`
... ]
[ ELSE
`_statements_` ]
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
子句结果而不是什么都不做的规则。
40.6.3. 简单循环
使用LOOP
, EXIT
,CONTINUE
, WHILE
, FOR
和FOREACH
语句, 可以控制PL/pgSQL函数重复一系列命令。
40.6.3.1. 循环
[ <<`_label_`>> ]
LOOP
_statements_
END LOOP [ `_label_` ];
LOOP
定义一个无条件的循环,无限循环, 直到由EXIT
或者 RETURN
语句终止。 可选的_label_
可以由EXIT
和CONTINUE
语句使用, 用于在嵌套循环中声明应该应用于哪一层循环。
40.6.3.2. 退出
EXIT [ `_label_` ] [ WHEN `_boolean-expression_` ];
如果没有给出_label_
,那么退出最内层的循环, 然后执行跟在END LOOP
后面的语句。 如果给出_label_
, 那么它必须是当前或者更高层的嵌套循环块或者语句块的标签。 然后该命名块或者循环就会终止,而控制落到对应循环/块的END
语句后面的语句上。
如果声明了WHEN
, 循环退出只有在_boolean-expression_
为真的时候才发生, 否则控制会落到EXIT
后面的语句上。
EXIT
可以用于在所有的循环类型中,它并不仅仅限制于在无条件循环中使用。
在和BEGIN
块一起使用的时候,EXIT
把控制交给块结束后的下一个语句。 需要注意的是,一个标签必须用于这个目的; 一个没有标记的EXIT
永远无法与BEGIN
进行匹配。 (这是PostgreSQL 8.4之前版本的一个变化, 这将允许未标记EXIT
匹配BEGIN
块)。
例如:
LOOP
-- 一些计算
IF count > 0 THEN
EXIT; -- exit loop
END IF;
END LOOP;
LOOP
-- 一些计算
EXIT WHEN count > 0; -- 和前面的例子相同结果
END LOOP;
<<ablock>>
BEGIN
-- 一些计算
IF stocks > 100000 THEN
EXIT ablock; -- 导致从BEGIN块退出
END IF;
-- 忽略这儿的计算,当stocks > 100000时
END;
40.6.3.3. CONTINUE
CONTINUE [ `_label_` ] [ WHEN `_boolean-expression_` ];
如果没有给出_label_
,那么就开始最内层循环的下一次执行。 也就是说,控制传递回给循环控制表达式(如果有),然后重新计算循环体。 如果出现了_label_
,它声明即将继续执行的循环的标签。
如果声明了WHEN
,那么循环的下一次执行只有 在_boolean-expression_
为真的情况下才进行。 否则,控制传递给CONTINUE
后面的语句。
CONTINUE
可以用于所有类型的循环;它并不仅仅限于无条件循环。
例如:
LOOP
-- 一些计算
EXIT WHEN count > 100;
CONTINUE WHEN count < 50;
-- 在[50 .. 100]内的计算
END LOOP;
40.6.3.4. WHILE
[ <<`_label_`>> ]
WHILE _boolean-expression_ LOOP
_statements_
END LOOP [ `_label_` ];
只要条件表达式(_boolean-expression_
)为真, WHILE
语句就会不停的在一系列语句上进行循环, 条件是在每次进入循环体的时候被检查。
例如:
WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP
-- 这里的一些计算
END LOOP;
WHILE NOT done LOOP
-- 这里的一些计算
END LOOP;
40.6.3.5. FOR
(Integer 变量)
[ <<`_label_`>> ]
FOR _name_ IN [ REVERSE ] _expression_ .. _expression_ [ BY `_expression_` ] LOOP
_statements_
END LOOP [ `_label_` ];
这种形式的FOR
对一定范围的整数进行迭代的循环。 变量_name_
会自动定义为BY
类型并且只在循环里存在 (任何该变量名的现存定义在此循环内都将被忽略)。 给出范围上下界的两个表达式在进入循环的时候计算一次。 BY
子句指定迭代步长(缺省为 1), 但如果声明了REVERSE
步长将变为相应的负值。
一些整数FOR
循环的例子:
FOR i IN 1..10 LOOP
-- 我将在值1,2,3,4,5,6,7,8,9,10中循环
END LOOP;
FOR i IN REVERSE 10..1 LOOP
-- 将在值10,9,8,7,6,5,4,3,2,1中循环
END LOOP;
FOR i IN REVERSE 10..1 BY 2 LOOP
-- 将在值10,8,6,4,2中循环
END LOOP;
如果下界大于上界(或者是在REVERSE
情况下是小于), 那么循环体将完全不被执行。 而且不会抛出任何错误。
如果_label_
被附加到FOR
循环,那么整数循环变量 可以使用_label_
引用适当名称。
40.6.4. 遍历命令结果
使用不同类型的FOR
循环, 你可以遍历一个命令的结果并且对其进行相应的操作。语法是:
[ <<`_label_`>> ]
FOR _target_ IN _query_ LOOP
_statements_
END LOOP [ `_label_` ];
_target_
是一个记录变量、 行变量、逗号分隔的标量变量列表_target_
被连续不断赋予所有来自_query_
的行, 并且循环体将为每行执行一次。 下面是一个例子:
CREATE FUNCTION cs_refresh_mviews() RETURNS integer AS $$
DECLARE
mviews RECORD;
BEGIN
RAISE NOTICE 'Refreshing materialized views...';
FOR mviews IN SELECT * FROM cs_materialized_views ORDER BY sort_key LOOP
-- 现在"mviews"里有了一条来自 cs_materialized_views 的记录
RAISE NOTICE 'Refreshing materialized view %s ...', quote_ident(mviews.mv_name);
EXECUTE 'TRUNCATE TABLE ' || quote_ident(mviews.mv_name);
EXECUTE 'INSERT INTO '
|| quote_ident(mviews.mv_name) || ' '
|| mviews.mv_query;
END LOOP;
RAISE NOTICE 'Done refreshing materialized views.';
RETURN 1;
END;
$$ LANGUAGE plpgsql;
如果循环是用一个EXIT
语句终止的, 那么在循环之后你仍然可以访问最后赋值的行。
FOR
语句中使用的这种_query_
可以是任何返回行的SQL命令, 通常是SELECT
,不过带有RETURNING
子句的INSERT
, UPDATE
或DELETE
也是可以的, 一些诸如EXPLAIN
之类的命令也可以。
PL/pgSQL变量代替查询文本,并且查询计划为了重新使用被缓存,正如 Section 40.10.1和Section 40.10.2。
FOR-IN-EXECUTE
语句是遍历所有行的另外一种方法:
[ <<`_label_`>> ]
FOR _target_ IN EXECUTE _text_expression_ [ USING `_expression_` [, ... ] ] LOOP
_statements_
END LOOP [ `_label_` ];
这个例子类似前面的形式,只不过源查询语句声明为了一个字符串表达式, 这样它在每次进入FOR
循环的时候都会重新计算和生成执行计划。 这样就允许程序员在一个预先规划好了的命令所获得的速度和一个动态命令所获得的灵活性 (就像一个简单的EXECUTE
语句那样)之间进行选择。 当使用EXECUTE
时, 可以通过USING
将参数值插入到动态命令中。
对于一个需要将结果迭代的查询, 另外一个声明的方法是将它定义为游标(cursor), 可参阅Section 40.7.4。
40.6.5. 遍历数组
FOREACH
循环类似于FOR
循环, 但不是遍历SQL查询返回的行,它遍历数组值元素。 (一般而言,FOREACH
是遍历复合值表达式组成部分; 循环遍历除数组外的复合值变量将来可以被添加。) FOREACH
语句循环数组是:
[ <<`_label_`>> ]
FOREACH _target_ [ SLICE `_number_` ] IN ARRAY _expression_ LOOP
_statements_
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}
40.6.6. 捕获错误
缺省时,一个在PL/pgSQL函数里发生的错误退出函数的执行, 并且实际上其周围的事务也会退出。 你可以使用一个带有EXCEPTION
子句的BEGIN
块捕获错误并且从中恢复。 其语法是正常的BEGIN
块语法的一个扩展:
[ <<`_label_`>> ]
[ DECLARE
`_declarations_` ]
BEGIN
_statements_
EXCEPTION
WHEN _condition_ [ OR `_condition_` ... ] THEN
_handler_statements_
[ WHEN `_condition_` [ OR `_condition_` ... ] THEN
`_handler_statements_`
... ]
END;
如果没有发生错误,这种形式的块只是简单地执行所有_statements_
, 然后转到下一个END
之后的语句。 但是如果在_statements_
内部发生了一个错误, 则对_statements_
的进一步处理将废弃,然后转到EXCEPTION
列表。 系统搜索这个列表,寻找匹配错误的第一个_condition_
。 如果找到匹配,则执行对应的_handler_statements_
, 然后转到END
之后的下一个语句。如果没有找到匹配,该错误就会广播出去, 就好像根本没有EXCEPTION
子句一样: 该错误可以被一个包围块用EXCEPTION
捕获, 如果没有包围块,则退出函数的处理。
_condition_
的名字可以是Appendix A里显示的任何名字。 一个范畴名匹配任意该范畴里的错误。 特殊的条件名OTHERS
匹配除了QUERY_CANCELED
之外的所有错误类型。 可以用名字捕获QUERY_CANCELED
,不过通常是不明智的。 条件名是大小写无关的。同时也可以通过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
。
Tip: 进入和退出一个包含
EXCEPTION
子句的块要比不包含的块开销大的多。 因此,不必要的时候不要使用EXCEPTION
。
Example 40-2. UPDATE
/INSERT
异常
这个例子根据使用异常处理器执行恰当的UPDATE
或者INSERT
。
CREATE TABLE db (a INT PRIMARY KEY, b TEXT);
CREATE FUNCTION merge_db(key INT, data TEXT) RETURNS VOID AS
$$
BEGIN
LOOP
-- 第一次尝试更新key
UPDATE db SET b = data WHERE a = key;
IF found THEN
RETURN;
END IF;
-- 不存在,所以尝试插入key,如果其他人同时插入相同的key,我们可能得到唯一key失败。
BEGIN
INSERT INTO db(a,b) VALUES (key, data);
RETURN;
EXCEPTION WHEN unique_violation THEN
-- 什么也不做,并且循环尝试再次更新。
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;
SELECT merge_db(1, 'david');
SELECT merge_db(1, 'dennis');
这个代码假设通过INSERT
不是说表上触发器函数中的INSERT
产生unique_violation
错误,如果表上有超过一个以上的唯一索引,它可能行为不端, 因为将重试操作不论哪个索引产生错误。 可以通过特性讨论下一步检查捕获的错误是预期的来获取更高的安全性。
40.6.6.1. 获得有关错误的信息
异常处理程序经常需要确定发生的具体错误。有两种方法来获得 当前PL/pgSQL异常: 特殊变量和GET STACKED DIAGNOSTICS
命令 的有关信息。
在一个异常处理程序中,特殊变量 SQLSTATE
包含相当于发生异常的错误代码 (参考Table A-1获得可能错误代码列)。 特殊变量SQLERRM
包含与异常有关的错误消息。 这些变量是在异常处理外未被定义的。
在一个异常处理程序中,也可以检索关于使用GET STACKED DIAGNOSTICS
命令的当前异常信息,形成了:
GET STACKED DIAGNOSTICS _variable_ = _item_ [ , ... ];
每个_item_
是识别被分配到指定变量的状态值(应该是接收它的正确数据类型)的一个关键字。 目前可用的状态显示在Table 40-1中。
Table 40-1. 错误诊断值
名字 | 类型 | 描述 |
---|---|---|
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 | 描述调用堆栈的文本线程 |
如果异常没有设置项值,则返回空字符串。
例子:
DECLARE
text_var1 text;
text_var2 text;
text_var3 text;
BEGIN
-- 一些处理可能引起异常
...
EXCEPTION WHEN OTHERS THEN
GET STACKED DIAGNOSTICS text_var1 = MESSAGE_TEXT,
text_var2 = PG_EXCEPTION_DETAIL,
text_var3 = PG_EXCEPTION_HINT;
END;