On this page
42.6. 控制结构
控制结构可能是 PL/pgSQL 中最有用(也是最重要)的部分。使用 PL/pgSQL 的控制结构,您可以以非常灵活和强大的方式操作 PostgreSQL 数据。
42 .6.1. 从函数返回
有两个命令可用于使您从函数返回数据:RETURN
和RETURN NEXT
。
42.6.1.1. RETURN
RETURN expression;
带有表达式的RETURN
终止函数,并将* expression
*的值返回给调用方。该格式用于不返回集合的 PL/pgSQL 函数。
在返回标量类型的函数中,表达式的结果将自动转换为函数的返回类型,如赋值所述。但是要返回一个复合(行)值,您必须编写一个表达式来传递所要求的列集。这可能需要使用显式转换。
如果使用输出参数声明了该函数,则只写不带表达式的RETURN
。将返回输出参数变量的当前值。
如果您声明该函数返回void
,则可以使用RETURN
语句提前退出该函数;但不要在RETURN
之后写一个表达式。
函数的返回值不能保持不确定。如果控制未到达RETURN
语句就到达了函数的顶级块的末尾,则会发生运行时错误。但是,此限制不适用于具有输出参数的函数和返回void
的函数。在这些情况下,如果顶级块完成,则会自动执行RETURN
语句。
Some examples:
-- 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
42 .6.1.2. 返回下一个并返回查询
RETURN NEXT expression;
RETURN QUERY query;
RETURN QUERY EXECUTE command-string [ USING expression [, ... ] ];
当声明 PL/pgSQL 函数返回SETOF sometype
时,遵循的过程略有不同。在这种情况下,要返回的单个项由RETURN NEXT
或RETURN QUERY
命令序列指定,然后使用不带参数的最终RETURN
命令来指示函数已完成执行。 RETURN NEXT
可用于标量和复合数据类型;如果使用复合结果类型,则将返回整个结果“表”。 RETURN QUERY
将执行查询的结果附加到函数的结果集中。 RETURN NEXT
和RETURN QUERY
可以在单个集合返回函数中自由混合,在这种情况下,它们的结果将串联在一起。
RETURN NEXT
和RETURN QUERY
实际上并不从函数返回-它们只是将零或更多行附加到函数的结果集中。然后执行 continue 执行 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);
Note
如上所述,RETURN NEXT
和RETURN QUERY
的当前实现在从函数返回之前存储了整个结果集。这意味着如果 PL/pgSQL 函数产生非常大的结果集,则性能可能会很差:将数据写入磁盘以避免内存耗尽,但是在生成整个结果集之前,函数本身不会返回。 PL/pgSQL 的 Future 版本可能允许用户定义不具有此限制的集合返回函数。当前,数据开始写入磁盘的位置由work_mem配置变量控制。有足够内存在内存中存储更大结果集的 Management 员应考虑增加此参数。
42.6.2. Conditionals
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
42.6.2.1. IF-THEN
IF boolean-expression THEN
statements
END IF;
IF-THEN
语句是IF
的最简单形式。如果条件为真,则将执行THEN
和END IF
之间的语句。否则,它们将被跳过。
Example:
IF v_user_id <> 0 THEN
UPDATE users SET email = v_email WHERE user_id = v_user_id;
END IF;
42.6.2.2. IF-THEN-ELSE
IF boolean-expression THEN
statements
ELSE
statements
END IF;
通过让您指定条件不成立时应执行的另一组语句,可以将IF-THEN-ELSE
条语句添加到IF-THEN
中。 (请注意,这包括条件评估为 NULL 的情况.)
Examples:
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;
42.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
条件为 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
麻烦很多。
42 .6.2.4. 简单案例
CASE search-expression
WHEN expression [, expression [ ... ]] THEN
statements
[ WHEN expression [, expression [ ... ]] THEN
statements
... ]
[ ELSE
statements ]
END CASE;
CASE
的简单形式提供基于操作数相等的条件执行。 * search-expression
被评估(一次),并与WHEN
子句中的每个 expression
相比较。如果找到匹配项,则执行相应的 statements
*,然后控制传递到END CASE
之后的下一条语句。 (不评估随后的WHEN
表达式.)如果未找到匹配项,则执行ELSE
* statements
*;否则,将执行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;
42 .6.2.5. 搜索案例
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
* 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
子句会导致错误而不是不执行任何操作。
42 .6.3. 简单循环
使用LOOP
,EXIT
,CONTINUE
,WHILE
,FOR
和FOREACH
语句,您可以安排 PL/pgSQL 函数重复一系列命令。
42.6.3.1. LOOP
[ <<label>> ]
LOOP
statements
END LOOP [ label ];
LOOP
定义了一个无条件循环,该循环将无限重复,直到被EXIT
或RETURN
语句终止为止。嵌套循环中的EXIT
和CONTINUE
语句可以使用可选的* label
*,以指定这些语句所引用的循环。
42.6.3.2. EXIT
EXIT [ label ] [ WHEN boolean-expression ];
如果未给出* label
,则终止最里面的循环,然后执行END LOOP
之后的语句。如果给出 label
*,则它必须是当前或嵌套循环或块的某个外部级别的标签。然后,已命名的循环或块终止,并且控制在循环/块的相应END
之后 continue 执行该语句。
如果指定了WHEN
,则仅当* boolean-expression
*为 true 时才发生循环退出。否则,控制权传递给EXIT
之后的语句。
EXIT
可用于所有类型的循环;它不限于与无条件循环一起使用。
与BEGIN
块一起使用时,EXIT
将控制权传递给该块结束后的下一条语句。请注意,为此目的必须使用标签。未标记的EXIT
永远不会与BEGIN
块匹配。 (这是对 PostgreSQL 8.4 之前的版本的更改,该版本允许未标记的EXIT
匹配BEGIN
块.)
Examples:
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;
42.6.3.3. CONTINUE
CONTINUE [ label ] [ WHEN boolean-expression ];
如果没有给出* label
,则开始最内层循环的下一次迭代。也就是说,将跳过循环主体中剩余的所有语句,并且控制返回到循环控制表达式(如果有)以确定是否需要另一个循环迭代。如果存在 label
*,则它指定将 continue 执行的循环的标签。
如果指定了WHEN
,则仅当* boolean-expression
*为 true 时才开始循环的下一次迭代。否则,控制权传递给CONTINUE
之后的语句。
CONTINUE
可用于所有类型的循环;它不限于与无条件循环一起使用。
Examples:
LOOP
-- some computations
EXIT WHEN count > 100;
CONTINUE WHEN count < 50;
-- some computations for count IN [50 .. 100]
END LOOP;
42.6.3.4. WHILE
[ <<label>> ]
WHILE boolean-expression LOOP
statements
END LOOP [ label ];
只要* boolean-expression
*的计算结果为 true,WHILE
语句将重复一系列语句。在每次进入循环主体之前检查表达式。
For example:
WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP
-- some computations here
END LOOP;
WHILE NOT done LOOP
-- some computations here
END LOOP;
42 .6.3.5. FOR(整数变体)
[ <<label>> ]
FOR name IN [ REVERSE ] expression .. expression [ BY expression ] LOOP
statements
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
情况下小于),则根本不执行循环体。没有错误。
如果在FOR
循环上附加了* label
,则可以使用 label
*来用限定名称引用整数循环变量。
42 .6.4. 循环查询结果
使用其他类型的FOR
循环,您可以遍历查询结果并相应地操作该数据。语法为:
[ <<label>> ]
FOR target IN query LOOP
statements
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
是最常见的情况,但是您也可以将INSERT
,UPDATE
或DELETE
与RETURNING
子句一起使用。某些 Util 命令(例如EXPLAIN
)也可以使用。
如Section 42.10.1和Section 42.10.2中详细讨论的,将 PL/pgSQL 变量替换为查询文本,并缓存查询计划以供可能的重用。
FOR-IN-EXECUTE
语句是另一种遍历行的方法:
[ <<label>> ]
FOR target IN EXECUTE text_expression [ USING expression [, ... ] ] LOOP
statements
END LOOP [ label ];
类似于以前的形式,只是源查询指定为字符串表达式,该表达式在FOR
循环的每个条目上进行评估和重新计划。就像普通的EXECUTE
语句一样,这使程序员可以选择预先计划的查询的速度或动态查询的灵 Active。与EXECUTE
一样,可以通过USING
将参数值插入到动态命令中。
指定要迭代其结果的查询的另一种方法是将其声明为游标。 Section 42.7.4中对此进行了描述。
42 .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
*变量按 Sequences 分配给每个元素值,并为每个元素执行循环体。这是循环遍历整数数组的元素的示例:
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;
元素以存储 Sequences 访问,而与数组维数无关。尽管* 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}
42 .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
和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
。
Tip
包含EXCEPTION
子句的块比不包含EXCEPTION
子句的块要昂贵得多。因此,请不要使用EXCEPTION
。
例 42.2. UPDATE
/INSERT
的 exception
本示例根据需要使用异常处理来执行UPDATE
或INSERT
。建议应用程序将INSERT
与ON CONFLICT DO UPDATE
一起使用,而不是实际使用此模式。该示例主要用于说明 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
引起的。如果表上有多个唯一索引,也可能会发生异常,因为无论哪个索引导致错误,它都会重试该操作。通过使用下面讨论的功能来检查陷阱错误是否是预期的错误,可以提高安全性。
42 .6.6.1. 获取有关错误的信息
异常处理程序经常需要识别发生的特定错误。有两种方法可以获取有关 PL/pgSQL 中当前异常的信息:特殊变量和GET STACKED DIAGNOSTICS
命令。
在异常处理程序中,特殊变量SQLSTATE
包含与引发的异常相对应的错误代码(有关可能的错误代码的列表,请参考Table A.1)。特殊变量SQLERRM
包含与异常关联的错误消息。这些变量在异常处理程序之外是未定义的。
在异常处理程序中,还可以使用GET STACKED DIAGNOSTICS
命令检索有关当前异常的信息,其格式为:
GET STACKED DIAGNOSTICS variable { = | := } item [ , ... ];
每个* item
是一个关键字,标识一个状态值,该状态值将分配给指定的 variable
*(应该具有正确的数据类型以接收它)。当前可用的状态项显示在Table 42.2中。
表 42.2. 错误诊断项目
Name | Type | Description |
---|---|---|
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 42.6.7) |
如果异常未为项目设置值,则将返回一个空字符串。
这是一个例子:
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;
42 .6.7. 获取执行位置信息
先前在Section 42.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
返回相同类型的堆栈跟踪,但是描述检测到错误的位置,而不是当前位置。