예외 처리하기

Snowflake Scripting 블록에서 사용자는 오류가 일어나면 예외를 발생시킬 수 있습니다. Snowflake Scripting 코드에서 일어나는 예외를 처리할 수도 있습니다.

소개

Snowflake Scripting은 문을 실행하는 동안 오류가 일어나면 예외를 발생시킵니다(예: 존재하지 않는 테이블을 문이 DROP 하려고 시도하는 경우). 예외가 발생하면 다음 코드 행이 실행되지 않습니다.

Snowflake Scripting 블록에서 사용자는 해당 블록과 해당 블록 내부에 중첩된 블록에서 선언된 특정 타입의 예외를 포착하는 예외 처리기를 작성할 수 있습니다.

또한, 코드에서 일어날 수 있는 오류에 대해 사용자는 오류가 일어날 때 발생할 수 있는 고유한 예외를 정의할 수 있습니다.

Snowflake Scripting 블록에서 예외가 발생하면(귀하의 코드나 실행에 실패한 문에 의해 발생) Snowflake Scripting은 해당 예외에 대한 처리기를 찾으려고 시도합니다.

  • 예외가 발생한 블록에 해당 예외에 대한 처리기가 있는 경우 해당 예외 처리기의 시작 부분에서 실행이 다시 시작됩니다.

  • 블록에 자체 예외 처리기가 없는 경우, 둘러싸는 블록에서 예외를 포착할 수 있습니다.

    예외가 둘 이상의 레이어 깊이에서 발생하면 예외는 다음 중 하나가 될 때까지 한 번에 한 레이어 위로 전송됩니다.

    • 적절한 예외 처리기가 있는 레이어가 예외를 처리합니다.

    • 가장 바깥쪽 레이어에 도달하면 오류가 발생합니다.

  • 현재 블록이나 주변 블록에 예외 처리기가 없으면 블록 실행이 중지되고, 실행을 위해 블록을 제출하는 클라이언트(예: 웹 인터페이스, 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 코드에서 SnowSQL, Classic Console 또는 execute_stream 또는 execute_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 을 모델로 한 5자 코드입니다. Snowflake는 ANSI SQL 표준보다 더 많은 값을 사용합니다.

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 코드에서 SnowSQL, Classic Console 또는 execute_stream 또는 execute_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 명령을 참조하십시오.

예외 핸들러를 설정하지 않는 경우, 실행을 위해 블록을 제출하는 클라이언트(예: 웹 인터페이스)가 오류를 보고합니다(선언된 예외 발생시키기 에서 설명됨).

-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 코드에서 SnowSQL, Classic Console 또는 execute_stream 또는 execute_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

예외 처리기에 변수 전달하기

예외 처리기에 변수를 전달할 수 있습니다. 예외 처리기는 변수 값에 따라 코드를 실행할 수 있으며, 변수 값은 오류 메시지로 반환될 수 있습니다.

변수를 EXCEPTION 섹션의 처리기로 전달하려면 변수를 DECLARE 섹션에서 선언해야 합니다. 변수가 블록의 BEGIN … END 섹션에서 선언된 경우 EXCEPTION 섹션에서는 변수에 액세스할 수 없습니다.

또한 인자를 허용하는 Snowflake Scripting 저장 프로시저를 작성하는 경우 예외 처리기에서 해당 인자를 사용할 수도 있습니다.

예를 들어, 다음 익명 블록은 counter_val 변수 값을 예외 처리기로 전달합니다.

DECLARE
  counter_val INTEGER DEFAULT 0;
  my_exception EXCEPTION (-20002, 'My exception text');
BEGIN
  WHILE (counter_val < 12) DO
    counter_val := counter_val + 1;
    IF (counter_val > 10) THEN
      RAISE my_exception;
    END IF;
  END WHILE;
  RETURN counter_val;
EXCEPTION
  WHEN my_exception THEN
    RETURN 'Error ' || sqlcode || ': Counter value ' || counter_val || ' exceeds the limit of 10.';
END;
Copy

참고: Python Connector 코드에서 SnowSQL, Classic Console 또는 execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하십시오(SnowSQL, Classic Console 및 Python Connector에서 Snowflake Scripting 사용하기 참조).

EXECUTE IMMEDIATE $$
DECLARE
  counter_val INTEGER DEFAULT 0;
  my_exception EXCEPTION (-20002, 'My exception text');
BEGIN
  WHILE (counter_val < 12) DO
    counter_val := counter_val + 1;
    IF (counter_val > 10) THEN
      RAISE my_exception;
    END IF;
  END WHILE;
  RETURN counter_val;
EXCEPTION
  WHEN my_exception THEN
    RETURN 'Error ' || sqlcode || ': Counter value ' || counter_val || ' exceeds the limit of 10.';
END;
$$
;
Copy

블록은 다음 오류 메시지를 반환합니다.

+---------------------------------------------------------+
| anonymous block                                         |
|---------------------------------------------------------|
| Error -20002: Counter value 11 exceeds the limit of 10. |
+---------------------------------------------------------+

다음은 인자를 전달하는 Snowflake Scripting 저장 프로시저의 예입니다. 이 예제에서는 예외 처리기에서 인자를 사용하는 방법을 보여줍니다.

CREATE OR REPLACE PROCEDURE exception_test_vars(amount INT)
  RETURNS TEXT
  LANGUAGE SQL
AS
DECLARE
  my_exception_1 EXCEPTION (-20002, 'Value too low');
  my_exception_2 EXCEPTION (-20003, 'Value too high');
BEGIN
  CREATE OR REPLACE TABLE test_order_insert(units INT);
  IF (amount < 1) THEN
    RAISE my_exception_1;
  ELSEIF (amount > 10) THEN
    RAISE my_exception_2;
  ELSE
    INSERT INTO test_order_insert VALUES (:amount);
  END IF;
  RETURN 'Order inserted successfully.';
EXCEPTION
  WHEN my_exception_1 THEN
    RETURN 'Error ' || sqlcode || ': Submitted amount ' || amount || ' is too low (1 or greater required).';
  WHEN my_exception_2 THEN
    RETURN 'Error ' || sqlcode || ': Submitted amount ' || amount || ' is too high (exceeds limit of 10).';
END;
Copy

참고: Python Connector 코드에서 SnowSQL, Classic Console 또는 execute_stream 또는 execute_string 메서드를 사용하는 경우 이 예제를 대신 사용하십시오(SnowSQL, Classic Console 및 Python Connector에서 Snowflake Scripting 사용하기 참조).

CREATE OR REPLACE PROCEDURE exception_test_vars(amount INT)
  RETURNS TEXT
  LANGUAGE SQL
AS
$$
DECLARE
  my_exception_1 EXCEPTION (-20002, 'Value too low');
  my_exception_2 EXCEPTION (-20003, 'Value too high');
BEGIN
  CREATE OR REPLACE TABLE test_order_insert(units INT);
  IF (amount < 1) THEN
    RAISE my_exception_1;
  ELSEIF (amount > 10) THEN
    RAISE my_exception_2;
  ELSE
    INSERT INTO test_order_insert VALUES (:amount);
  END IF;
  RETURN 'Order inserted successfully.';
EXCEPTION
  WHEN my_exception_1 THEN
    RETURN 'Error ' || sqlcode || ': Submitted amount ' || amount || ' is too low (1 or greater required).';
  WHEN my_exception_2 THEN
    RETURN 'Error ' || sqlcode || ': Submitted amount ' || amount || ' is too high (exceeds limit of 10).';
END;
$$
;
Copy

저장 프로시저에 대한 다음 호출은 예상되는 출력을 보여줍니다.

CALL exception_test_vars(7);
Copy
+------------------------------+
| EXCEPTION_TEST_VARS          |
|------------------------------|
| Order inserted successfully. |
+------------------------------+
CALL exception_test_vars(-3);
Copy
+-----------------------------------------------------------------------+
| EXCEPTION_TEST_VARS                                                   |
|-----------------------------------------------------------------------|
| Error -20002: Submitted amount -3 is too low (1 or greater required). |
+-----------------------------------------------------------------------+
CALL exception_test_vars(20);
Copy
+----------------------------------------------------------------------+
| EXCEPTION_TEST_VARS                                                  |
|----------------------------------------------------------------------|
| Error -20003: Submitted amount 20 is too high (exceeds limit of 10). |
+----------------------------------------------------------------------+