控制结构可能是 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;
除了到达省略的 ELSE
子句会导致错误而不是不执行任何操作的规则外,这种形式的 CASE
与 IF-THEN-ELSIF
完全等效。
使用 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
];
只要 boolean-expression
的计算结果为 true,WHILE
语句就会重复执行一系列语句。在每次进入循环体之前都会检查该表达式。
例如
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
语句终止,则在循环后仍然可以访问最后分配的行值。
在此类型的 FOR
语句中使用的 query
可以是向调用者返回行的任何 SQL 命令:SELECT
是最常见的情况,但您也可以使用带有 RETURNING
子句的 INSERT
、UPDATE
、DELETE
或 MERGE
。一些实用命令(例如 EXPLAIN
)也可以使用。
按照 第 41.11.1 节和 第 41.11.2 节中详细讨论的那样,PL/pgSQL 变量将替换为查询参数,并且查询计划将缓存以供可能重复使用。
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
。
示例 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
包含与引发的异常对应的错误代码(有关可能的错误代码列表,请参阅 表 A.1)。特殊变量 SQLERRM
包含与异常关联的错误消息。这些变量在异常处理程序之外是未定义的。
在异常处理程序中,还可以使用 GET STACKED DIAGNOSTICS
命令检索有关当前异常的信息,该命令的格式如下:
GET STACKED DIAGNOSTICSvariable
{ = | := }item
[ , ... ];
每个 item
都是一个关键字,用于标识要分配给指定 variable
的状态值(该变量应具有接收该值的正确数据类型)。当前可用的状态项在 表 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_CONTEXT |
文本 |
描述异常发生时的调用堆栈的文本行(请参阅 第 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
返回相同类型的堆栈跟踪,但描述了检测到错误的位置,而不是当前位置。
如果您在文档中发现任何不正确的内容、与您使用特定功能的体验不符或需要进一步澄清的内容,请使用 此表单 报告文档问题。