控制结构可能是 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
,当只有一个类型为 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 -- 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
调用过程。输出参数的处理方式与 CALL
在普通 SQL 中的工作方式不同。过程的每个 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
语句中最简单的形式。如果条件为 true,则 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
中添加内容,让你可以指定当条件不为 true 时应该执行的一系列备用语句。(请注意这包括条件评估为 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
条件会被先后测试,直到找到第一个为 true。然后,关联语句将会被执行,之后控制权将转到 END IF
后的下一个语句。(后面的任何 IF
条件都不会被测试。)如果所有 IF
条件都不为 true,则会执行 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
循环,则整数循环变量可以使用限定名称引用,用该 label
。
使用不同类型的 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
语句终止,则在上一个赋值的行值仍可在循环后访问。
query
用在这种类型的 FOR
语句中可以是返回行给调用的任何 SQL 命令: SELECT
是最常见的情况,但也可使用带 RETURNING
子句的 INSERT
、UPDATE
、DELETE
或 MERGE
。某些实用命令(如 EXPLAIN
)也适用。
PL/pgSQL 变量由查询参数替换,并且查询计划被缓存以备可能重新使用,正如在 第 41.11.1 节 和 第 41.11.2 节 中详细讨论的那样。
FOR-IN-EXECUTE
语句是遍历行的另一种方式
[ <<label
>> ] FORtarget
IN EXECUTEtext_expression
[ USINGexpression
[, ... ] ] LOOPstatements
END LOOP [label
];
这类似于之前的形式,只是源查询指定为一个字符串表达式,该表达式在 FOR
循环的每个入口处求值并重新计划。这允许程序员根据需要选择预计划查询的速度或灵活的动态查询,就像使用简单 EXECUTE
语句一样。跟 EXECUTE
一样,可以使用 USING
在动态命令中插入参数值。
指定应该遍历其结果的查询的另一种方法是将其声明为游标。这将在 第 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
名称可为 附录 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
子句的块进入和退出的开销要大得多。因此,不要无谓地使用 EXCEPTION
。
示例 41.2。异常与 UPDATE
/INSERT
此示例使用异常处理来执行 UPDATE
或 INSERT
,视具体情况而定。建议应用程序使用 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
包含与引发异常对应的错误代码(请参阅表 A.1以获取可能的错误代码列表)。特殊变量SQLERRM
包含与异常关联的错误消息。这些变量在异常处理程序外部未定义。
在异常处理程序内,还可以使用GET STACKED DIAGNOSTICS
命令检索有关当前异常的信息,其形式为
GET STACKED DIAGNOSTICSvariable
{ = | := }item
[ , ... ];
每个项目
是标识要分配给指定变量
(应该是接收它的正确数据类型)的状态值的关键字。当前可用的状态项目显示在表 41.2中。
表 41.2. 错误诊断项
名称 | 类型 | 描述 |
---|---|---|
RETURNED_SQLSTATE |
文本 |
异常的 SQLSTATE 错误代码 |
COLUMN_NAME |
文本 |
与异常相关的列的名称 |
CONSTRAINT_NAME |
文本 |
与异常相关的约束的名称 |
PG_DATATYPE_NAME |
文本 |
与异常相关的的数据类型的名称 |
MESSAGE_TEXT |
文本 |
异常的主要消息的文本 |
TABLE_NAME |
文本 |
与异常相关的表的名称 |
SCHEMA_NAME |
文本 |
与异常相关的模式的名称 |
PG_EXCEPTION_DETAIL |
文本 |
异常的详细信息消息的文本(如果有) |
PG_EXCEPTION_HINT |
文本 |
异常的提示消息的文本(如果有) |
PG_EXCEPTION_CONT |
文本 |
在异常发生时描述调用堆栈的行文本(参见 第 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;
先前已在 第 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
返回相同类型的堆栈跟踪,但描述的是检测到错误的位置,而不是当前位置。