PostgreSQL,PL/pgSQL - SQL过程语言,控制结构:从一个函数返回,条件,简单循环,通过查询结果循环,通过数组循环,俘获错误,获得执行位置信息

本文主要是介绍PostgreSQL,PL/pgSQL - SQL过程语言,控制结构:从一个函数返回,条件,简单循环,通过查询结果循环,通过数组循环,俘获错误,获得执行位置信息,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

PostgreSQL,PL/pgSQL - SQL过程语言,控制结构:从一个函数返回,条件,简单循环,通过查询结果循环,通过数组循环,俘获错误,获得执行位置信息

  • 42.6.1. 从一个函数返回
    • 42.6.1.1. RETURN
    • 42.6.1.2. RETURN NEXT以及RETURN QUERY
  • 42.6.2. 条件
    • 42.6.2.1. IF-THEN
    • 42.6.2.2. IF-THEN-ELSE
    • 42.6.2.3. IF-THEN-ELSIF
    • 42.6.2.4. 简单CASE
  • 42.6.3. 简单循环
    • 42.6.3.1. LOOP
    • 42.6.3.2. EXIT
    • 42.6.3.3. CONTINUE
  • 42.6.4. 通过查询结果循环
  • 42.6.5. 通过数组循环
  • 42.6.6. 俘获错误
    • 42.6.6.1. 得到有关一个错误的信息
  • 42.6.7. 获得执行位置信息
  • 参考来源

控制结构可能是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语句。

一些例子:

-- 返回一个标量类型的函数RETURN 1 + 2;
RETURN scalar_var;
-- 返回一个组合类型的函数RETURN composite_type_var;
RETURN (1, 2, 'three'::text);  -- 必须把列造型成正确的类型

42.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可以被用于标量和复合数据类型;对于复合类型,将返回一个完整的结果“表”。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$
DECLAREr foo%rowtype;
BEGINFOR r INSELECT * FROM foo WHERE fooid > 0LOOP-- 这里可以做一些处理RETURN NEXT r; -- 返回 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$
BEGINRETURN QUERY SELECT flightidFROM flightWHERE flightdate >= $1AND flightdate < ($1 + 1);-- 因为执行还未结束,我们可以检查是否有行被返回-- 如果没有就抛出异常。IF NOT FOUND THENRAISE EXCEPTION 'No flight at %.', $1;END IF;RETURN;END
$BODY$
LANGUAGE plpgsql;
-- 返回可用的航班或者在没有可用航班时抛出异常。
SELECT * FROM get_available_flightid(CURRENT_DATE);

注意
如上所述,目前RETURN NEXT和RETURN QUERY的实现在从函数返回之前会把整个结果集都保存起来。这意味着如果一个PL/pgSQL函数生成一个非常大的结果集,性能可能会很差:数据将被写到磁盘上以避免内存耗尽,但是函数本身在整个结果集都生成之前不会退出。将来的PL/pgSQL版本可能会允许用户定义没有这种限制的集合返回函数。目前,数据开始被写入到磁盘的时机由配置变量work_mem控制。拥有足够内存来存储大型结果集的管理员可以考虑增大这个参数。

42.6.2. 条件

IF和CASE语句让你可以根据某种条件执行二选其一的命令。PL/pgSQL有三种形式的IF:

IF ... THEN ... END IFIF ... THEN ... ELSE ... END IFIF ... THEN ... ELSIF ... THEN ... ELSE ... END IF

以及两种形式的CASE:

CASE ... WHEN ... THEN ... ELSE ... END CASECASE WHEN ... THEN ... ELSE ... END CASE

42.6.2.1. IF-THEN

IF boolean-expression THENstatements
END IF;

IF-THEN语句是IF的最简单形式。 如果条件为真,在THEN和END IF之间的语句将被执行。否则,将忽略它们。

例子:

IF v_user_id <> 0 THENUPDATE users SET email = v_email WHERE user_id = v_user_id;
END IF;

42.6.2.2. IF-THEN-ELSE

IF boolean-expression THENstatements
ELSEstatements
END IF;

IF-THEN-ELSE语句对IF-THEN进行了增加,它让你能够指定一组在条件不为真时应该被执行的语句(注意这也包括条件为 NULL 的情况)。

例子:

IF parentid IS NULL OR parentid = ''
THENRETURN fullname;
ELSERETURN hp_true_filename(parentid) || '/' || fullname;
END IF;
IF v_count > 0 THENINSERT INTO users_count (count) VALUES (v_count);RETURN 't';
ELSERETURN 'f';
END IF;

42.6.2.3. IF-THEN-ELSIF

IF boolean-expression THENstatements
[ ELSIF boolean-expression THENstatements
[ ELSIF boolean-expression THENstatements...]]
[ ELSEstatements ]
END IF;

有时会有多于两种选择。IF-THEN-ELSIF则提供了一个简便的方法来检查多个条件。IF条件会被一个接一个测试,直到找到第一个为真的。然后执行相关语句,然后控制会被交给END IF之后的下一个语句(后续的任何IF条件不会被测试)。如果没有一个IF条件为真,那么ELSE块(如果有)将被执行。

这里有一个例子:

IF number = 0 THENresult := 'zero';
ELSIF number > 0 THENresult := 'positive';
ELSIF number < 0 THENresult := 'negative';
ELSE-- 嗯,唯一的其他可能性是数字为空result := 'NULL';
END IF;

关键词ELSIF也可以被拼写成ELSEIF。

另一个可以完成相同任务的方法是嵌套IF-THEN-ELSE语句,如下例:

IF demo_row.sex = 'm' THENpretty_sex := 'man';
ELSEIF demo_row.sex = 'f' THENpretty_sex := 'woman';END IF;
END IF;

不过,这种方法需要为每个IF都写一个匹配的END IF,因此当有很多选择时,这种方法比使用ELSIF要麻烦得多。

42.6.2.4. 简单CASE

CASE search-expressionWHEN expression [, expression [ ... ]] THENstatements[ WHEN expression [, expression [ ... ]] THENstatements... ][ ELSEstatements ]
END CASE;

CASE的简单形式提供了基于操作数等值判断的有条件执行。search-expression会被计算(一次)并且一个接一个地与WHEN子句中的每个expression比较。如果找到一个匹配,那么相应的statements会被执行,并且接着控制会被交给END CASE之后的下一个语句(后续的WHEN表达式不会被计算)。如果没有找到匹配,ELSE 语句会被执行。但是如果ELSE不存在,将会抛出一个CASE_NOT_FOUND异常。

这里是一个简单的例子:

CASE xWHEN 1, 2 THENmsg := 'one or two';ELSEmsg := 'other value than one or two';
END CASE;
42.6.2.5. 搜索CASE
CASEWHEN boolean-expression THENstatements[ WHEN boolean-expression THENstatements... ][ ELSEstatements ]
END CASE;

CASE的搜索形式基于布尔表达式真假的有条件执行。每一个WHEN子句的boolean-expression会被依次计算,直到找到一个得到真的。然后相应的statements会被执行,并且接下来控制会被传递给END CASE之后的下一个语句(后续的WHEN表达式不会被计算)。如果没有找到为真的结果,ELSE statements会被执行。但是如果ELSE不存在,那么将会抛出一个CASE_NOT_FOUND异常。

这里是一个例子:

CASEWHEN x BETWEEN 0 AND 10 THENmsg := 'value is between zero and ten';WHEN x BETWEEN 11 AND 20 THENmsg := 'value is between eleven and twenty';
END CASE;

这种形式的CASE整体上等价于IF-THEN-ELSIF,不同之处在于CASE到达一个被忽略的ELSE子句时会导致一个错误而不是什么也不做。

42.6.3. 简单循环

使用LOOP、EXIT、CONTINUE、WHILE、FOR和FOREACH语句,你可以安排PL/pgSQL重复一系列命令。

42.6.3.1. LOOP

[ <<label>> ]
LOOPstatements
END LOOP [ label ];

LOOP定义一个无条件的循环,它会无限重复直到被EXIT或RETURN语句终止。可选的label可以被EXIT和CONTINUE语句用在嵌套循环中指定这些语句引用的是哪一层循环。

42.6.3.2. EXIT

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 THENEXIT;  -- 退出循环END IF;
END LOOP;LOOP-- 一些计算EXIT WHEN count > 0;  -- 和前一个例子相同的结果
END LOOP;<<ablock>>
BEGIN-- 一些计算IF stocks > 100000 THENEXIT ablock;  -- 导致从 BEGIN 块中退出END IF;-- 当stocks > 100000时,这里的计算将被跳过
END;

42.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;-- 一些用于 count IN [50 .. 100] 的计算
END LOOP;
42.6.3.4. WHILE
[ <<label>> ]
WHILE boolean-expression LOOPstatements
END LOOP [ label ];

只要boolean-expression被计算为真,WHILE语句就会重复一个语句序列。在每次进入到循环体之前都会检查该表达式。

例如:

WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP-- 这里是一些计算
END LOOP;WHILE NOT done LOOP-- 这里是一些计算
END LOOP;
42.6.3.5. FOR(整型变体)
[ <<label>> ]
FOR name IN [ REVERSE ] expression .. expression [ BY expression ] LOOPstatements
END LOOP [ label ];

这种形式的FOR会创建一个在一个整数范围上迭代的循环。变量name会自动定义为类型integer并且只在循环内存在(任何该变量名的现有定义在此循环内都将被忽略)。给出范围上下界的两个表达式在进入循环的时候计算一次。如果没有指定BY子句,迭代步长为 1,否则步长是BY中指定的值,该值也只在循环进入时计算一次。如果指定了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的限定名引用。

42.6.4. 通过查询结果循环

使用一种不同类型的FOR循环,你可以通过一个查询的结果进行迭代并且操纵相应的数据。语法是:

[ <<label>> ]
FOR target IN query LOOPstatements
END LOOP [ label ];

target是一个记录变量、行变量或者逗号分隔的标量变量列表。target被连续不断被赋予来自query的每一行,并且循环体将为每一行执行一次。下面是一个例子:

CREATE FUNCTION cs_refresh_mviews() RETURNS integer AS $$
DECLAREmviews RECORD;
BEGINRAISE 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 format('TRUNCATE TABLE %I', mviews.mv_name);EXECUTE format('INSERT INTO %I %s', 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变量会被替换到查询文本中,并且如第 42.10.1 节和第 42.10.2 节中详细讨论的,查询计划会被缓存以用于可能的重用。

FOR-IN-EXECUTE语句是在行上迭代的另一种方式:

[ <<label>> ]
FOR target IN EXECUTE text_expression [ USING expression [, ... ] ] LOOPstatements
END LOOP [ label ];

这个例子类似前面的形式,只不过源查询被指定为一个字符串表达式,在每次进入FOR循环时都会计算它并且重新规划。这允许程序员在一个预先规划好了的命令的速度和一个动态命令的灵活性之间进行选择,就像一个纯EXECUTE语句那样。在使用EXECUTE时,可以通过USING将参数值插入到动态命令中。

另一种指定要对其结果迭代的查询的方式是将它声明为一个游标。这会在第 42.7.4 节中描述。

42.6.5. 通过数组循环

FOREACH循环很像一个FOR循环,但不是通过一个 SQL 查询返回的行进行迭代,它通过一个数组值的元素来迭代(通常,FOREACH意味着通过一个组合值表达式的部件迭代;用于通过除数组之外组合类型进行循环的变体可能会在未来被加入)。在一个数组上循环的FOREACH语句是:

[ <<label>> ]
FOREACH target [ SLICE number ] IN ARRAY expression LOOPstatements
END LOOP [ label ];

如果没有SLICE,或者如果没有指定SLICE 0,循环会通过计算expression得到的数组的个体元素进行迭代。target变量被逐一赋予每一个元素值,并且循环体会为每一个元素执行。这里是一个通过整数数组的元素循环的例子:

CREATE FUNCTION sum(int[]) RETURNS int8 AS $$
DECLAREs int8 := 0;x int;
BEGINFOREACH x IN ARRAY $1LOOPs := s + x;END LOOP;RETURN s;
END;
$$ LANGUAGE plpgsql;

元素会被按照存储顺序访问,而不管数组的维度数。尽管target通常只是一个单一变量,当通过一个组合值(记录)的数组循环时,它可以是一个变量列表。在那种情况下,对每一个数组元素,变量会被从组合值的连续列赋值。

通过一个正SLICE值,FOREACH通过数组的切片而不是单一元素迭代。SLICE值必须是一个不大于数组维度数的整数常量。target变量必须是一个数组,并且它接收数组值的连续切片,其中每一个切片都有SLICE指定的维度数。这里是一个通过一维切片迭代的例子:

CREATE FUNCTION scan_rows(int[]) RETURNS void AS $$
DECLAREx int[];
BEGINFOREACH x SLICE 1 IN ARRAY $1LOOPRAISE 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>> ]
[ DECLAREdeclarations ]
BEGINstatements
EXCEPTIONWHEN condition [ OR condition ... ] THENhandler_statements[ WHEN condition [ OR condition ... ] THENhandler_statements... ]
END;

如果没有发生错误,这种形式的块只是简单地执行所有statements, 并且接着控制转到END之后的下一个语句。但是如果在statements内发生了一个错误,则会放弃对statements的进一步处理,然后控制会转到EXCEPTION列表。系统会在列表中寻找匹配所发生错误的第一个condition。如果找到一个匹配,则执行对应的handler_statements,并且接着把控制转到END之后的下一个语句。如果没有找到匹配,该错误就会传播出去,就好像根本没有EXCEPTION一样:错误可以被一个带有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');
BEGINUPDATE mytab SET firstname = 'Joe' WHERE lastname = 'Jones';x := x + 1;y := x / 0;
EXCEPTIONWHEN division_by_zero THENRAISE NOTICE 'caught division_by_zero';RETURN x;
END;

当控制到达对y赋值的地方时,它会带着一个division_by_zero错误失败。这个错误将被EXCEPTION子句捕获。而在RETURN语句中返回的值将是x增加过后的值。但是UPDATE命令的效果将已经被回滚。不过,在该块之前的INSERT将不会被回滚,因此最终的结果是数据库包含Tom Jones但不包含Joe Jones。

提示
进入和退出一个包含EXCEPTION子句的块要比不包含EXCEPTION的块开销大的多。因此,只在必要的时候使用EXCEPTION。

例 42.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
$$
BEGINLOOP-- 首先尝试更新见UPDATE db SET b = data WHERE a = key;IF found THENRETURN;END IF;-- 不在这里,那么尝试插入该键-- 如果其他某人并发地插入同一个键,-- 我们可能得到一个唯一键失败BEGININSERT INTO db(a,b) VALUES (key, data);RETURN;EXCEPTION WHEN unique_violation THEN-- 什么也不做,并且循环再次尝试 UPDATEEND;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包含了对应于被抛出异常的错误代码(可能的错误代码列表见表 A.1)。特殊变量SQLERRM包含与该异常相关的错误消息。这些变量在异常处理器外是未定义的。

在一个异常处理器内,我们也可以用GET STACKED DIAGNOSTICS命令检索有关当前异常的信息,该命令的形式为:

GET STACKED DIAGNOSTICS variable { = | := } item [ , ... ];

每个item是一个关键词,它标识一个被赋予给指定变量(应该具有接收该值的正确数据类型)的状态值。表 42.2中显示了当前可用的状态项。

表 42.2. 错误诊断项

名称类型描述
RETURNED_SQLSTATEtext该异常的 SQLSTATE 错误代码
COLUMN_NAMEtext与异常相关的列名
CONSTRAINT_NAMEtext与异常相关的约束名
PG_DATATYPE_NAMEtext与异常相关的数据类型名
MESSAGE_TEXTtext该异常的主要消息的文本
TABLE_NAMEtext与异常相关的表名
SCHEMA_NAMEtext与异常相关的模式名
PG_EXCEPTION_DETAILtext该异常的详细消息文本(如果有)
PG_EXCEPTION_HINTtext该异常的提示消息文本(如果有)
PG_EXCEPTION_CONTEXTtext描述产生异常时调用栈的文本行(见第 42.6.7 节)

如果异常没有为一个项设置值,将返回一个空字符串。

这里是一个例子:

DECLAREtext_var1 text;text_var2 text;text_var3 text;
BEGIN-- 某些可能导致异常的处理...
EXCEPTION WHEN OTHERS THENGET STACKED DIAGNOSTICS text_var1 = MESSAGE_TEXT,text_var2 = PG_EXCEPTION_DETAIL,text_var3 = PG_EXCEPTION_HINT;
END;

42.6.7. 获得执行位置信息

GET DIAGNOSTICS(之前在第 42.5.5 节中描述)命令检索有关当前执行状态的信息(反之上文讨论的GET STACKED DIAGNOSTICS命令会把有关执行状态的信息报告成一个以前的错误)。它的PG_CONTEXT状态项可用于标识当前执行位置。状态项PG_CONTEXT将返回一个文本字符串,其中有描述该调用栈的多行文本。第一行会指向当前函数以及当前正在执行GET DIAGNOSTICS的命令。第二行及其后的行表示调用栈中更上层的调用函数。例如:

CREATE OR REPLACE FUNCTION outer_func() RETURNS integer AS $$
BEGINRETURN inner_func();
END;
$$ LANGUAGE plpgsql;CREATE OR REPLACE FUNCTION inner_func() RETURNS integer AS $$
DECLAREstack text;
BEGINGET 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 RETURNouter_func------------1
(1 row)

GET STACKED DIAGNOSTICS ... PG_EXCEPTION_CONTEXT返回同类的栈跟踪,但是它描述检测到错误的位置而不是当前位置。

参考来源

http://postgres.cn/docs/10/plpgsql-control-structures.html

这篇关于PostgreSQL,PL/pgSQL - SQL过程语言,控制结构:从一个函数返回,条件,简单循环,通过查询结果循环,通过数组循环,俘获错误,获得执行位置信息的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/723847

相关文章

C语言中联合体union的使用

本文编辑整理自: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=179471 一、前言 “联合体”(union)与“结构体”(struct)有一些相似之处。但两者有本质上的不同。在结构体中,各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间, 一个联合变量

房产证 不动产查询

陕西政务服务网(便民服务)陕西政务服务网(手机版?更直观)不动产权证书|不动产登记证明(电子证照)商品房合同备案查询权利人查询

C++工程编译链接错误汇总VisualStudio

目录 一些小的知识点 make工具 可以使用windows下的事件查看器崩溃的地方 dumpbin工具查看dll是32位还是64位的 _MSC_VER .cc 和.cpp 【VC++目录中的包含目录】 vs 【C/C++常规中的附加包含目录】——头文件所在目录如何怎么添加,添加了以后搜索头文件就会到这些个路径下搜索了 include<> 和 include"" WinMain 和

一份LLM资源清单围观技术大佬的日常;手把手教你在美国搭建「百万卡」AI数据中心;为啥大模型做不好简单的数学计算? | ShowMeAI日报

👀日报&周刊合集 | 🎡ShowMeAI官网 | 🧡 点赞关注评论拜托啦! 1. 为啥大模型做不好简单的数学计算?从大模型高考数学成绩不及格说起 司南评测体系 OpenCompass 选取 7 个大模型 (6 个开源模型+ GPT-4o),组织参与了 2024 年高考「新课标I卷」的语文、数学、英语考试,然后由经验丰富的判卷老师评判得分。 结果如上图所

C/C++的编译和链接过程

目录 从源文件生成可执行文件(书中第2章) 1.Preprocessing预处理——预处理器cpp 2.Compilation编译——编译器cll ps:vs中优化选项设置 3.Assembly汇编——汇编器as ps:vs中汇编输出文件设置 4.Linking链接——链接器ld 符号 模块,库 链接过程——链接器 链接过程 1.简单链接的例子 2.链接过程 3.地址和

mysql索引四(组合索引)

单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引;组合索引,即一个索引包含多个列。 因为有事,下面内容全部转自:https://www.cnblogs.com/farmer-cabbage/p/5793589.html 为了形象地对比单列索引和组合索引,为表添加多个字段:    CREATE TABLE mytable( ID INT NOT NULL, use

mysql索引三(全文索引)

前面分别介绍了mysql索引一(普通索引)、mysql索引二(唯一索引)。 本文学习mysql全文索引。 全文索引(也称全文检索)是目前搜索引擎使用的一种关键技术。它能够利用【分词技术】等多种算法智能分析出文本文字中关键词的频率和重要性,然后按照一定的算法规则智能地筛选出我们想要的搜索结果。 在MySql中,创建全文索引相对比较简单。例如:我们有一个文章表(article),其中有主键ID(

mysql索引二(唯一索引)

前文中介绍了MySQL中普通索引用法,和没有索引的区别。mysql索引一(普通索引) 下面学习一下唯一索引。 创建唯一索引的目的不是为了提高访问速度,而只是为了避免数据出现重复。唯一索引可以有多个但索引列的值必须唯一,索引列的值允许有空值。如果能确定某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该使用关键字UNIQUE,把它定义为一个唯一索引。 添加数据库唯一索引的几种

mysql索引一(普通索引)

mysql的索引分为两大类,聚簇索引、非聚簇索引。聚簇索引是按照数据存放的物理位置为顺序的,而非聚簇索引则不同。聚簇索引能够提高多行检索的速度、非聚簇索引则对单行检索的速度很快。         在这两大类的索引类型下,还可以降索引分为4个小类型:         1,普通索引:最基本的索引,没有任何限制,是我们经常使用到的索引。         2,唯一索引:与普通索引

大语言模型(LLMs)能够进行推理和规划吗?

大语言模型(LLMs),基本上是经过强化训练的 n-gram 模型,它们在网络规模的语言语料库(实际上,可以说是我们文明的知识库)上进行了训练,展现出了一种超乎预期的语言行为,引发了我们的广泛关注。从训练和操作的角度来看,LLMs 可以被认为是一种巨大的、非真实的记忆库,相当于为我们所有人提供了一个外部的系统 1(见图 1)。然而,它们表面上的多功能性让许多研究者好奇,这些模型是否也能在通常需要系