PostgreSQL 教程: PL/pgSQL 异常

八月 21, 2023

摘要:在本教程中,您将学习如何在 PL/pgSQL 中捕获 PostgreSQL 异常。

PL/pgSQL 异常子句简介

代码块中发生错误时,PostgreSQL 将中止该块以及周围事务的执行。

要从错误中恢复,您可以使用begin...end块中的exception子句。

下面说明了exception子句的语法:

<<label>>
declare
begin
    statements;
exception
    when condition [or condition...] then
       handle_exception;
   [when condition [or condition...] then
       handle_exception;]
   [when others then
       handle_other_exceptions;
   ]
end;

怎么运行的。

  • 首先,当beginexception之间发生错误时,PL/pgSQL 停止执行并将控制权传递给异常列表。
  • 其次,PL/pgSQL 搜索第一个与发生错误相匹配的condition
  • 第三,如果匹配,则执行相应的handle_exception语句。PL/pgSQL 将控制权传递给end关键字后面的语句。
  • 最后,如果没有找到匹配项,错误就会传播出去,并且可以被包围块的exception子句捕获。如果该exception子句外部没有包围的块,PL/pgSQL 将中止处理。

select语句不返回行时,异常名称可以是no_data_found,或者select语句返回多行时,异常名称可以是too_many_rows。有关异常名称的完整列表,请查看 PostgreSQL 文档

也可以通过SQLSTATE代码指定错误条件。例如,对于no_data_found可指定P0002,对于too_many_rows可指定P0003

通常,您将捕获特定的异常并进行相应的处理。要处理没有在异常列表中指定的其他异常,可以使用when others then子句。

处理异常示例

我们将使用示例数据库中的film表进行演示。

Film table

1) 处理 no_data_found 异常的示例

以下示例会发出错误,因为 id 为 2000 的影片不存在。

do
$$
declare
	rec record;
	v_film_id int = 2000;
begin
	-- select a film 
	select film_id, title 
	into strict rec
	from film
	where film_id = v_film_id;
end;
$$
language plpgsql;

输出:

ERROR:  query returned no rows
CONTEXT:  PL/pgSQL function inline_code_block line 6 at SQL statement
SQL state: P0002

以下示例使用exception子句捕获no_data_found异常并报告更有意义的消息:

do
$$
declare
	rec record;
	v_film_id int = 2000;
begin
	-- select a film 
	select film_id, title 
	into strict rec
	from film
	where film_id = v_film_id;
        -- catch exception
	exception 
	   when no_data_found then 
	      raise exception 'film % not found', v_film_id;
end;
$$
language plpgsql;

输出:

ERROR:  film 2000 not found
CONTEXT:  PL/pgSQL function inline_code_block line 14 at RAISE
SQL state: P0001

2) 处理 too_many_rows 异常的示例

下面的例子说明了如何处理too_many_rows异常:

do
$$
declare
	rec record;
begin
	-- select film
	select film_id, title
	into strict rec
	from film
	where title LIKE 'A%';

	exception
	    when too_many_rows then
	        raise exception 'Search query returns too many rows';
end;
$$
language plpgsql;

输出:

ERROR:  Search query returns too many rows
CONTEXT:  PL/pgSQL function inline_code_block line 15 at RAISE
SQL state: P0001

在此示例中,发生too_many_rows异常是因为该select into语句返回多行,而它应该只返回一行。

3) 处理多个异常

以下示例说明了如何捕获多个异常:

do
$$
declare
	rec record;
	v_length int = 90;
begin
	-- select a film 
	select film_id, title 
	into strict rec
	from film
	where length = v_length;
	
        -- catch exception
	exception 
	   when sqlstate 'P0002' then 
	      raise exception 'film with length % not found', v_length;
	   when sqlstate 'P0003' then 
	      raise exception 'The with length % is not unique', v_length;
end;
$$
language plpgsql;

输出:

ERROR:  The with length 90 is not unique
CONTEXT:  PL/pgSQL function inline_code_block line 17 at RAISE
SQL state: P0001

4) 以 SQLSTATE 代码处理异常

以下示例与上面的示例相同,只是它使用的是SQLSTATE代码而不是异常名称:

do
$$
declare
	rec record;
	v_length int = 30;
begin
	-- select a film 
	select film_id, title 
	into strict rec
	from film
	where length = v_length;

        -- catch exception
	exception 
	   when sqlstate 'P0002' then 
	      raise exception 'film with length % not found', v_length;
	   when sqlstate 'P0003' then 
	      raise exception 'The with length % is not unique', v_length;
end;
$$
language plpgsql;

输出:

ERROR:  film with length 30 not found
CONTEXT:  PL/pgSQL function inline_code_block line 15 at RAISE
SQL state: P0001

概括

  • 使用begin...end块中的exception子句来捕获和处理异常。