

 从补丁 198 开始，Amazon Redshift 将不再支持创建新的 Python UDF。现有的 Python UDF 将继续正常运行至 2026 年 6 月 30 日。有关更多信息，请参阅[博客文章](https://www.amazonaws.cn/blogs/big-data/amazon-redshift-python-user-defined-functions-will-reach-end-of-support-after-june-30-2026/)。

# 捕获错误
<a name="stored-procedure-trapping-errors"></a>

本主题介绍 Amazon Redshift 如何处理错误。

当存储过程中的查询或命令导致错误时，后续查询将停止运行，事务进行回滚。您可以使用 EXCEPTION 块处理错误。

**注意**  
默认行为是，即使存储过程中没有其他产生错误的情形，错误也会导致后续查询停止运行。

```
[ <<label>> ]
[ DECLARE
  declarations ]
BEGIN
  statements
EXCEPTION
  WHEN OTHERS THEN
    statements
END;
```

当发生异常并添加异常处理块时，您可以编写 REAE 语句和大多数其它 PL/pgSQL 语句。例如，您可以使用自定义消息引发异常，也可以将记录插入日志表中。

进入异常处理块时，将回滚当前事务并创建一个新事务来运行块中的语句。如果块中的语句运行没有错误，则会提交事务并重新抛出异常。最后，退出存储过程。

异常块中唯一支持的条件是 OTHERS，它匹配除了查询取消之外的各个错误类型。此外，如果异常处理块中出现错误，错误可由外部异常处理块捕获。

当 NONATOMIC 过程内部出现错误时，如果错误由异常块处理，则不会重新引发错误。请查看 PL/pgSQL 语句 `RAISE`，以引发由异常处理块捕获的异常。此语句仅在异常处理块中有效。有关更多信息，请参阅 [RAISE](c_PLpgSQL-statements.md#r_PLpgSQL-messages-errors)。

**使用 CONTINUE 处理程序控制存储过程出错后的处理情况**

 `CONTINUE` 处理程序是一种异常处理程序，用于控制 NONATOMIC 存储过程中的执行流程。通过使用此处理程序，您可以在不结束现有语句块的情况下捕获和处理异常。通常，当存储过程中发生错误时，流程将中断，错误将返回给调用者。不过，在某些使用案例中，错误情况并没有严重到需要中断流程的地步。您可能希望在一个单独的事务中使用自己选择的错误处理逻辑优雅地处理错误，然后继续运行错误后的语句。语法如下。

```
[ DECLARE
  declarations ]
BEGIN
  statements
EXCEPTION
  [ CONTINUE_HANDLER | EXIT_HANDLER ] WHEN OTHERS THEN
    handler_statements
END;
```

您可以利用多个系统表来帮助收集有关各种类型错误的信息。有关更多信息，请参阅 [STL\$1LOAD\$1ERRORS](r_STL_LOAD_ERRORS.md)、[STL\$1ERROR](r_STL_ERROR.md) 和 [SYS\$1STREAM\$1SCAN\$1ERRORS](r_SYS_STREAM_SCAN_ERRORS.md)。您还可以使用其他系统表来进行故障排除。在[系统表和视图参考](cm_chap_system-tables.md)中可以找到有关这些内容的更多信息。

## 示例
<a name="stored-procedure-trapping-errors-examples"></a>

以下示例演示如何在异常处理块中写入语句。存储过程使用默认事务管理行为。

```
CREATE TABLE employee (firstname varchar, lastname varchar);
INSERT INTO employee VALUES ('Tomas','Smith');
CREATE TABLE employee_error_log (message varchar);

CREATE OR REPLACE PROCEDURE update_employee_sp() AS
$$
BEGIN
    UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith';
    EXECUTE 'select invalid';
EXCEPTION WHEN OTHERS THEN
    RAISE INFO 'An exception occurred.';
    INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
END;
$$ 
LANGUAGE plpgsql;

CALL update_employee_sp();

INFO:  An exception occurred.
ERROR:  column "invalid" does not exist
CONTEXT:  SQL statement "select invalid"
PL/pgSQL function "update_employee_sp" line 3 at execute statement
```

在此示例中，如果我们调用 `update_employee_sp`，则会引发信息性消息 *An exception occurred.*（发生异常。）并将错误消息插入到日志记录表的 `employee_error_log` 日志中。在存储过程退出之前再次抛出原始异常。以下查询显示了运行该示例所产生的记录。

```
SELECT * from employee;

firstname | lastname 
-----------+----------
 Tomas     | Smith

SELECT * from employee_error_log;

          message                     
------------------------------------------------
 Error message: column "invalid" does not exist
```

有关 RAISE 的更多信息，包括格式帮助和附加级别列表，请参阅[支持的 PL/pgSQL 语句](c_PLpgSQL-statements.md)。

以下示例演示如何在异常处理块中写入语句。存储过程使用 NONATOMIC 事务管理行为。在此示例中，过程调用完成后，不会引发任何返回给调用方的错误。由于下一条语句中的错误，UPDATE 语句无法回滚。此时将引发信息性消息，并将错误消息插入到日志记录表中。

```
CREATE TABLE employee (firstname varchar, lastname varchar); 
INSERT INTO employee VALUES ('Tomas','Smith'); 
CREATE TABLE employee_error_log (message varchar);

-- Create the SP in NONATOMIC mode
CREATE OR REPLACE PROCEDURE update_employee_sp_2() NONATOMIC AS
$$
BEGIN
    UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith';
    EXECUTE 'select invalid';
EXCEPTION WHEN OTHERS THEN
    RAISE INFO 'An exception occurred.';
    INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
END;
$$ 
LANGUAGE plpgsql;

CALL update_employee_sp_2();
INFO:  An exception occurred.
CALL

SELECT * from employee;

 firstname | lastname 
-----------+----------
 Adam      | Smith
(1 row)

SELECT * from employee_error_log;

                    message                     
------------------------------------------------
 Error message: column "invalid" does not exist
(1 row)
```

此示例演示如何创建具有两个子块的过程。调用存储过程时，第一个子块中错误由其异常处理块处理。第一个子块完成后，过程继续执行第二个子块。您可在结果中看到，过程调用完成时没有引发错误。对表 employee 执行的 UPDATE 和 INSERT 操作已提交。来自两个异常块的错误消息都插入到日志记录表中。

```
CREATE TABLE employee (firstname varchar, lastname varchar); 
INSERT INTO employee VALUES ('Tomas','Smith'); 
CREATE TABLE employee_error_log (message varchar);

CREATE OR REPLACE PROCEDURE update_employee_sp_3() NONATOMIC AS
$$
BEGIN
    BEGIN
        UPDATE employee SET firstname = 'Adam' WHERE lastname = 'Smith';
        EXECUTE 'select invalid1';
    EXCEPTION WHEN OTHERS THEN
        RAISE INFO 'An exception occurred in the first block.';
        INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
    END;
    BEGIN
        INSERT INTO employee VALUES ('Edie','Robertson');
        EXECUTE 'select invalid2';
    EXCEPTION WHEN OTHERS THEN
        RAISE INFO 'An exception occurred in the second block.';
        INSERT INTO employee_error_log VALUES ('Error message: ' || SQLERRM);
    END;
END;
$$ 
LANGUAGE plpgsql;

CALL update_employee_sp_3();
INFO:  An exception occurred in the first block.
INFO:  An exception occurred in the second block.
CALL

SELECT * from employee;

 firstname | lastname  
-----------+-----------
 Adam      | Smith
 Edie      | Robertson
(2 rows)

SELECT * from employee_error_log;

                     message                     
-------------------------------------------------
 Error message: column "invalid1" does not exist
 Error message: column "invalid2" does not exist
(2 rows)
```

以下示例演示如何使用 CONTINUINE 异常处理程序。此示例创建了两个表，并在存储过程中使用它们。CONTINUE 处理程序控制具有 NONATOMIC 事务管理行为的存储过程中的执行流程。

```
CREATE TABLE tbl_1 (a int);
CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar);

CREATE OR REPLACE PROCEDURE sp_exc_handling_1() NONATOMIC AS
$$
BEGIN
    INSERT INTO tbl_1 VALUES (1);
    -- Expect an error for the insert statement following, because of the invalid value
    INSERT INTO tbl_1 VALUES ("val");
    INSERT INTO tbl_1 VALUES (2);
EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN
    INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
END;
$$ LANGUAGE plpgsql;
```

调用存储过程：

```
CALL sp_exc_handling_1();
```

流程如下：

1. 由于试图在列中插入不兼容的数据类型，因此出现错误。控制权传递到 EXCEPTION 块。进入异常处理块后，当前事务将回滚，并创建一个新的隐式事务来运行其中的语句。

1. 如果 CONTINUE\$1HANDLER 中的语句运行无误，控制权将传递到紧接着导致异常的语句之后的语句。（如果 CONTINUE\$1HANDLER 中的语句引发了新的异常，可以在 EXCEPTION 块中使用异常处理程序来处理。）

调用示例存储过程后，表中会包含以下记录：
+ 如果您运行 `SELECT * FROM tbl_1;`，它会返回两条记录。其中包含值 `1` 和 `2`。
+ 如果您运行 `SELECT * FROM tbl_error_logging;`，它会返回一条包含以下值的记录：*Encountered error*、*42703* 和 *column "val" does not exist in tbl\$11*。

下面的附加错误处理示例同时使用 EXIT 处理程序和 CONTINUE 处理程序。它创建两个表：一个数据表和一个日志表。它还会创建一个演示错误处理的存储过程：

```
CREATE TABLE tbl_1 (a int);
CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar);

CREATE OR REPLACE PROCEDURE sp_exc_handling_2() NONATOMIC AS
$$
BEGIN
    INSERT INTO tbl_1 VALUES (1);
    BEGIN
        INSERT INTO tbl_1 VALUES (100);
        -- Expect an error for the insert statement following, because of the invalid value
        INSERT INTO tbl_1 VALUES ("val");
        INSERT INTO tbl_1 VALUES (101);
    EXCEPTION EXIT_HANDLER WHEN OTHERS THEN
        INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
    END;
    INSERT INTO tbl_1 VALUES (2);
    -- Expect an error for the insert statement following, because of the invalid value
    INSERT INTO tbl_1 VALUES ("val");
    INSERT INTO tbl_1 VALUES (3);
EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN
    INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
END;
$$ LANGUAGE plpgsql;
```

创建存储过程后，使用以下内容对其进行调用：

```
CALL sp_exc_handling_2();
```

当内部异常块（由内部的 BEGIN 和 END 所括弧）中出现错误时，将由 EXIT 处理程序进行处理。外部块中发生的任何错误都由 CONTINUE 处理程序处理。

调用示例存储过程后，表中会包含以下记录：
+ 如果您运行 `SELECT * FROM tbl_1;`，它会返回四条记录，值分别为 1、2、3 和 100。
+ 如果您运行 `SELECT * FROM tbl_error_logging;`，它会返回两条记录。它们有以下值：*Encountered error*、*42703* 和 *column "val" does not exist in tbl\$11*。

如果 **tbl\$1error\$1logging** 表不存在，则会引发异常。

以下示例演示如何将 CONTINUE 异常处理程序与 FOR 循环一起使用。此示例创建了三个表，并在存储过程的 FOR 循环中使用它们。FOR 循环是结果集变体，这意味着它会遍历查询结果：

```
CREATE TABLE tbl_1 (a int);
INSERT INTO tbl_1 VALUES (1), (2), (3);
CREATE TABLE tbl_2 (a int);
CREATE TABLE tbl_error_logging(info varchar, err_state varchar, err_msg varchar);

CREATE OR REPLACE PROCEDURE sp_exc_handling_loop() NONATOMIC AS
$$
DECLARE
 rec RECORD;
BEGIN
    FOR rec IN SELECT a FROM tbl_1
    LOOP
        IF rec.a = 2 THEN
            -- Expect an error for the insert statement following, because of the invalid value
            INSERT INTO tbl_2 VALUES("val");
        ELSE
            INSERT INTO tbl_2 VALUES (rec.a);
        END IF;
    END LOOP;
EXCEPTION CONTINUE_HANDLER WHEN OTHERS THEN
    INSERT INTO tbl_error_logging VALUES ('Encountered error', SQLSTATE, SQLERRM);
END;
$$ LANGUAGE plpgsql;
```

调用存储过程：

```
CALL sp_exc_handling_loop();
```

调用示例存储过程后，表中会包含以下记录：
+  如果您运行 `SELECT * FROM tbl_2;`，它会返回两条记录。其中包含值 1 和 3。
+ 如果您运行 `SELECT * FROM tbl_error_logging;`，它会返回一条包含以下值的记录：*Encountered error*、*42703* 和 *column "val" does not exist in tbl\$12*。

关于 CONTINUE 处理程序的使用说明：
+ CONTINUE\$1HANDLER 和 EXIT\$1HANDLER 关键字只能在 NONATOMIC 存储过程中使用。
+ CONTINUE\$1HANDLER 和 EXIT\$1HANDLER 关键字是可选的。EXIT\$1HANDLER 是默认值。