处理异常

在 Snowflake Scripting 块中,如果发生错误,可以引发异常。您还可以处理 Snowflake Scripting 代码中发生的异常。

简介

如果在执行语句时发生错误(例如,如果语句尝试对不存在的表进行 DROP 操作),则 Snowflake Scripting 会引发异常。异常会阻止下一行代码的执行。

在 Snowflake Scripting 块中,您可以编写异常处理程序,捕获该块和嵌套在该块中的块中已声明的特定类型的异常。

此外,对于代码中可能发生的错误,您可以定义自己的异常,以便在发生错误时引发这些异常。

当 Snowflake Scripting 块中引发异常时(由代码或无法执行的语句引发),Snowflake Scripting 会尝试查找该异常的处理程序:

  • 如果发生异常的块具有该异常的处理程序,则在该异常处理程序的开头恢复执行。

  • 如果该块没有自己的异常处理程序,则封闭块可以捕获该异常。

    如果异常发生的深度超过一层,则系统每次将异常向上发送一层,直到出现以下任一情况:

    • 具有适当异常处理程序的层可以处理该异常。

    • 到达最外层,在这种情况下会发生错误。

  • 如果当前块或任何封闭块中没有异常处理程序,则块的执行将停止,提交块以供执行的客户端(例如 Web 界面、SnowSQL 等)会将其报告为 Snowflake 错误。

异常处理程序可以包含自己的异常处理程序,以防在处理其他异常时发生异常。

声明异常

您可以在块的 DECLARE 部分中声明自己的异常。请使用 异常声明语法 中描述的语法。例如:

DECLARE
  my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
Copy

引发已声明的异常

要引发异常,请执行 RAISE 命令。例如:

DECLARE
  my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
BEGIN
  LET counter := 0;
  LET should_raise_exception := true;
  IF (should_raise_exception) THEN
    RAISE my_exception;
  END IF;
  counter := counter + 1;
  RETURN counter;
END;
Copy

注意:如果您在 Python Connector 代码中使用 SnowSQLClassic Console 或者 execute_streamexecute_string 方法,请改用本示例(请参阅 在 SnowSQL、Classic Console 和 Python Connector 中使用 Snowflake Scripting):

EXECUTE IMMEDIATE $$
DECLARE
  my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
BEGIN
  LET counter := 0;
  LET should_raise_exception := true;
  IF (should_raise_exception) THEN
    RAISE my_exception;
  END IF;
  counter := counter + 1;
  RETURN counter;
END;
$$
;
Copy

引发异常后,执行随即停止。(在本例中,counter 从不递增和返回。)

提交此块以供执行的客户端(例如 Snowsight)将报告错误,并指示未捕获异常:

-20002 (P0001): Uncaught exception of type 'MY_EXCEPTION' on line 8 at position 4 : Raised MY_EXCEPTION.
Copy

如果要添加代码来处理引发的任何异常(以及语句无法执行时引发的异常),可以编写异常处理程序。请参阅 处理异常

备注

在异常处理程序中,如果需要再次引发相同的异常,请参阅 在异常处理程序中再次引发相同的异常

处理异常

您可以使用 EXCEPTION 子句捕获异常来显式处理异常,也可以允许块将异常传递给封闭块。

EXCEPTION 子句中,请使用 WHEN 子句按名称处理异常。您可以处理您声明的异常以及内置异常。目前,Snowflake 提供以下内置异常:

  • STATEMENT_ERROR:此异常表示执行语句时出错。例如,如果尝试弃用不存在的表,则会引发此异常。

  • EXPRESSION_ERROR:此异常表示与表达式相关的错误。例如,如果创建了一个计算结果为 VARCHAR 的表达式,并尝试将该表达式的值赋给 FLOAT,则会引发此错误。

发生异常时,可以通过读取以下三个内置变量,获取有关异常的信息:

  • SQLCODE:这是一个 5 位的带符号整数。对于用户定义的异常,这是 用于声明异常的语法 中显示的 exception_number

  • SQLERRM:这是一条错误消息。对于用户定义的异常,这是 用于声明异常的语法 中显示的 exception_message

  • SQLSTATE:这是一个基于 ANSI SQL 标准 SQLSTATE (link removed) 的 5 字符代码。除 ANSI SQL 标准中的值外,Snowflake 还使用了其他值。

若要处理不含 WHEN 子句的所有其他异常,请使用 WHEN OTHER THEN 子句。

例如:

DECLARE
  my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
BEGIN
  LET counter := 0;
  LET should_raise_exception := true;
  IF (should_raise_exception) THEN
    RAISE my_exception;
  END IF;
  counter := counter + 1;
  RETURN counter;
EXCEPTION
  WHEN statement_error THEN
    RETURN OBJECT_CONSTRUCT('Error type', 'STATEMENT_ERROR',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
  WHEN my_exception THEN
    RETURN OBJECT_CONSTRUCT('Error type', 'MY_EXCEPTION',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
  WHEN OTHER THEN
    RETURN OBJECT_CONSTRUCT('Error type', 'Other error',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
  END;
Copy

注意:如果您在 Python Connector 代码中使用 SnowSQLClassic Console 或者 execute_streamexecute_string 方法,请改用本示例(请参阅 在 SnowSQL、Classic Console 和 Python Connector 中使用 Snowflake Scripting):

EXECUTE IMMEDIATE $$
DECLARE
  my_exception EXCEPTION (-20002, 'Raised MY_EXCEPTION.');
BEGIN
  LET counter := 0;
  LET should_raise_exception := true;
  IF (should_raise_exception) THEN
    RAISE my_exception;
  END IF;
  counter := counter + 1;
  RETURN counter;
EXCEPTION
  WHEN statement_error THEN
    RETURN OBJECT_CONSTRUCT('Error type', 'STATEMENT_ERROR',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
  WHEN my_exception THEN
    RETURN OBJECT_CONSTRUCT('Error type', 'MY_EXCEPTION',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
  WHEN OTHER THEN
    RETURN OBJECT_CONSTRUCT('Error type', 'Other error',
                            'SQLCODE', sqlcode,
                            'SQLERRM', sqlerrm,
                            'SQLSTATE', sqlstate);
END;
$$
;
Copy

此示例通过调用 OBJECT_CONSTRUCT 构造并返回一个包含异常详细信息的对象,从而处理每种类型的异常。此示例生成以下输出:

+--------------------------------------+
| anonymous block                      |
|--------------------------------------|
| {                                    |
|   "Error type": "MY_EXCEPTION",      |
|   "SQLCODE": -20002,                 |
|   "SQLERRM": "Raised MY_EXCEPTION.", |
|   "SQLSTATE": "P0001"                |
| }                                    |
+--------------------------------------+

在极少数情况下,您可能希望不执行任何操作来显式处理异常。这使您可以在发生异常时继续,而不是中止。有关更多信息,请参阅 NULL 命令。

如果没有为异常设置处理程序,则提交块以供执行的客户端(例如 Web 界面)将报告错误(如 引发已声明的异常 中所述)。

-20002 (P0001): Uncaught exception of type 'MY_EXCEPTION' on line 8 at position 4 : Raised MY_EXCEPTION.
Copy

备注

如果需要再次引发相同的异常,请参阅 在异常处理程序中再次引发相同的异常

在异常处理程序中再次引发相同的异常

在某些情况下,您可能需要引发在异常处理程序中捕获的相同异常。在这些情况下,请执行 RAISE 命令,且不指定任何实参。

例如,假设在异常处理期间,您需要先获取有关异常的一些详细信息,然后再引发相同的异常。获取详细信息后,请执行 RAISE 命令:

BEGIN
  SELECT * FROM non_existent_table;
EXCEPTION
  WHEN OTHER THEN
    LET LINE := SQLCODE || ': ' || SQLERRM;
    INSERT INTO myexceptions VALUES (:line);
    RAISE; -- Raise the same exception that you are handling.
END;
Copy

注意:如果您在 Python Connector 代码中使用 SnowSQLClassic Console 或者 execute_streamexecute_string 方法,请改用本示例(请参阅 在 SnowSQL、Classic Console 和 Python Connector 中使用 Snowflake Scripting):

EXECUTE IMMEDIATE $$
BEGIN
  SELECT * FROM non_existent_table;
EXCEPTION
  WHEN OTHER THEN
    LET LINE := SQLCODE || ': ' || SQLERRM;
    INSERT INTO myexceptions VALUES (:line);
    RAISE; -- Raise the same exception that you are handling.
END;
$$;
Copy
语言: 中文