레이블이 ErrorNo인 게시물을 표시합니다. 모든 게시물 표시
레이블이 ErrorNo인 게시물을 표시합니다. 모든 게시물 표시

2010년 12월 27일 월요일

Stored routine (Procedure, Function, Trigger)에서의 예외 처리 방법

Stored routine (Procedure, Function, Trigger)는 다른 절차적인 프로그램 언어와 같이
여러가지 에러 상황에 대한 Exception handling이 필수적이다. 
여기에서는 MySQL Stored routine에서의 예외 처리를 한번 알아보고자 한다.


우선 MySQL의 예외처리는 DECLARE ... HANDLER 구문을 이용하여
각 예외 케이스의 이벤트가 발생하면 그 Handler가 작동하는 형태로 구현이 가능하다.


HANDLER 정의 구문

DECLARE handler_type HANDLER
    FOR condition_value [, condition_value] ...
    handler_statements

handler_type:
    CONTINUE
  | EXIT

condition_value:
    SQLSTATE sqlstate_value
  | SQLWARNING
  | NOT FOUND
  | SQLEXCEPTION
  | mysql_error_code
  | condition_name

Handler type
  • CONTINUE 정의된 handler_statements를 실행하고, Stored routine의 마지막 실행 지점으로 다시 돌아간다.
  • EXIT 정의된 handler_statements를 실행하고, 현재 Handler가 정의된 BEGIN ... END 블록을 벗어난다. (만약, 현재 Handler가 최상위 블록에 정의되었다면, 현재 Stored routine을 종료하게 된다.)



Condition value
  • SQLSTATE
    ${sqlstate_value}
    Stored routine에서 코드를 실행하던 중 어떤 이벤트가 발생했을 때, 그 이벤트의 SQLSTATE 값을 체크해서 실행되는 Handler를 정의할 때 사용.
  • SQLWARNINGStored routine에서 코드를 실행하던 중 SQLWarning이 발생했을 때, 실행되는 Handler를 정의할 때 사용. SQLWARNING"01"로 시작되는SQLSTATE 를 표현하는 동의어와 같은 것이다.
  • NOT FOUNDSELECT 쿼리 문의 결과 건수가 1건도 없거나, CURSOR의 레코드를 마지막까지 읽은 경우 실행되는 Handler를 정의할 때 사용. NOT FOUND "02"로 시작되는 SQLSTATE를 표현하는 동의어와 같은 것이다.
  • SQLEXCEPTIONSQLWARNING NOT FOUND 그리고 "00"(정상 처리)으로 시작되는 SQLSTATE 이외의 모든 케이스를 의미하는 키워드이다.
  • ${mysql_error_code}코드 실행 중 어떤 이벤트가 발생했을 때, SQLSTATE값이 아닌 MySQL ErrorNo 값을 비교해서 실행되는 Handler를 정의할 때 사용.
  • ${condition_name}DECLARE ${condition_name} CONDITION FOR ... 구문을 이용하여 사용자 정의 CONDITION을 생성할 수 있는데, 그 이름을 여기에 명시해서 Handler를 정의할 때 사용. (자세한 설명은 매뉴얼 참조)


Condition value 정의시 주의사항
  • Condition value는 콤마(",")를 이용하여 여러 개 동시에 나열 하는 것이 가능하다.
  • Condition value SQLSTATE '00000' 또는 MySQL ErrorNo 0 은 정의해서는 안 된다. 이 두 개의 값은 모두 정상 처리를 의미하는 SQLSTATE MySQL ErrorNo 값이다.
  • SQLSTATE와 ErrorNo에 대한 자세한 사항은 "MySQL ErrorNo와 SqlState 는 무엇을 의미할까?"를 참조



Handler statements
  • 특정 이벤트가 발생했을 때, 그 이벤트에 대한 처리(Handling) 코드를 여기에 명시하며, Handler statements에는 단순히 명령문 하나만 명시될 수도 있으며, BEGIN ... END 블록으로 정의된 코드 블록이 명시될 수도 있다.



예제로 간단한 Handler 정의 문장을 살펴보자.

예제 1)
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION   SET error_flag=1;

SQLException (Sql State "00", "01", "02" 이외의 값으로 시작되는 에러)이 발생했을 때, error_flag 변수의 값을 1로 설정하고, 마지막 실행했던 Stored routine의 코드로 돌아가서 계속 실행(CONTINUE)

예제 2)
DECLARE EXIT HANDLER FOR SQLEXCEPTION
  BEGIN
      ROLLBACK;
      SELECT 'Error occurred - terminating';
  END;

SQLException (Sql State "00", "01", "02" 이외의 값으로 시작되는 에러)이 발생했을 때, ROLLBACK을 실행하고, SELECT ... 문장을 실행 후, Handler가 실행된 코드 블록을 벗어나거나, Handler Stored routine의 최상위 블록에 정의된 경우에는 실행을 종료한다.
Stored routine에서 실행되는 SELECT 문장의 결과를 Console이나 Client로 즉시 출력된다.
(이 예제에서는 " Error occurred - terminating "라는 값이 출력된다.)

예제 3)
DECLARE CONTINUE HANDER FOR 1062   SELECT 'Duplicate key in index';

MySQL ErrorNo 1062인 이벤트가 발생했을 때, Console이나 Client"Duplicate key in index"라는 결과 셋을 출력하고, Stored routine의 원래 실행 지점으로 돌아가서 계속 나머지 코드를 실행한다.

예제 4)
DECLARE CONTINUE HANDER FOR SQLSTATE '23000'   SELECT 'Duplicate key in index';

MySQL SqlState"23000"인 이벤트가 발생했을 때, Console이나 Client"Duplicate key in index"라는 결과 셋을 출력하고, Stored routine의 원래 실행 지점으로 돌아가서 계속 나머지 코드를 실행한다.

예제 5)
DECLARE CONTINUE HANDLER FOR NOT FOUND   SET process_done=1;

SELECT 문의 실행 결과 레코드 건이 없거나, CURSOR의 결과 레코드를 모두 Fetch하고 나면 process_done 변수 값을 1로 설정하고 Stored routine의 마지막 실행 지점으로 돌아가서 계속 나머지 코드를 실행한다.

예제 6)
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'   SET process_done=1;

SELECT 문의 실행 결과 레코드 건이 없거나, CURSOR의 결과 레코드를 모두 Fetch하고 나면 process_done 변수 값을 1로 설정하고 Stored routine의 마지막 실행 지점으로 돌아가서 계속 나머지 코드를 실행한다. ("02000" NOT FOUND를 의미하는 SQLSTATE값이다.)

예제 7)
DECLARE CONTINUE HANDLER FOR 1329   SET process_done=1;

SELECT 문의 실행 결과 레코드 건이 없거나, CURSOR의 결과 레코드를 모두 Fetch하고 나면 process_done 변수 값을 1로 설정하고 Stored routine의 마지막 실행 지점으로 돌아가서 계속 나머지 코드를 실행한다. (1329 NOT FOUND를 의미하는 MySQL ErrorNo값이다.)

예제 8)
DECLARE EXIT HANDLER FOR SQLWARNING, SQLEXCEPTION
  BEGIN
    ROLLBACK;
    SELECT 'Process terminated, Because error';
    SHOW ERRORS;
    SHOW WARNINGS;
  END;

SQLWARNING 이나 SQLEXCEPTION이 발생하면, 지금까지의 데이터 변경을 모두 ROLLBACK하고, 에러와 경고 메시지를 출력하고 Stored routine을 종료한다.

MySQL ErrorNo와 SqlState 는 무엇을 의미할까?


PHP JDBCMySQL 데이터베이스를 이용하는 프로그램을 작성하다 보면 ErrorNo SqlState라는 말이나 값들을 자주 접하게 된다.
그러면, " MySQL은 에러나 상태 코드를 이렇게 ErrorNo SqlState라는 것으로 따로 분리해서 사용할까"라는 의문이 들 것이다.
이 글에서는 ErrorNo SqlState의 차이점과 대표적인 ErrorNo SqlState값을 확인 해보자.

mysql> SELECT * FROM not_found_table;
ERROR 1146 (42S02): Table 'test.not_found_table' doesn't exist

위의 예제에서와 같이 mysql client를 이용해서 쿼리를 실행하면, 에러나 경고 발생시에 콘솔에 
"ERROR ${ERROR-NO} (${SQL-STATE}): ${ERROR-MESSAGE}"
와 같은 형태의 결과물을 확인할 수 있다. 각 부분의 출력 값들의 의미는 아래와 같다. 이런 ErrorNo SqlState값은 기본적인 프로그램 작성뿐만 아니라 MySQL Stored program (Procedure, Function, Trigger)을 작성할 때 예외 처리를 위해서도 반드시 알아야 할 내용이기도 하다.

${ERROR-NO}
4
자리 (현재까지는) 숫자 값으로 구성된 에러 코드이며, 이 에러 코드 값은 MySQL에서만 유효한 에러 식별 번호이다. ,  1146이라는 에러 코드 값은 MySQL에서는 "테이블이 존재하지 않는다"라는 것을 의미하지만, 다른 DBMS 벤더에서는 다른 의미로 사용될 수 있다는 것을 의미한다. (또한 다른 DBMS 벤더에서는 다른 형태로 에러 코드가 구성되어 있을 수도 있다.)
* ${SQL-STATE}
5
글자의 Alpha-Numeric 으로 구성되며, 에러뿐만 아니라 여러 가지 상태를 의미하는 코드로써, 이 값은 DBMS 종류가 다르다 하더라도 ANSI SQL 표준을 준수하는 DBMS (ODBC, JDBC 포함)에서는 모두 동일한 값을 가지고 된다. 즉 이 값은 표준 값으로 정의되기 때문에 DBMS  벤더에 의존적이지 않다. 대 부분의 ErrorNo는 특정 SqlState값과 매핑이 되어 있으며, 그 이외의 매핑되지 않는 ErrorNo의 경우에는 SqlState 값이 "HY000" (General error)으로 설정된다.
SqlState
값의 앞 2글자는 아래와 같은 의미를 가지고 있다.
- "00"
정상 처리됨
- "01"
경고 메시지 (Warning)
- "02" Not found (SELECT
Cursor 에서 결과가 없는 경우에만 사용됨)
-
그 이외의 값들은 각자의 에러 케이스들을 의미한다.
* ${ERROR-MESSAGE}포맷팅된 텍스트 문장으로, Human readable한 에러 메시지이다. 이 정보 또한 DBMS 벤더 별로 내용이나 구조가 다르다.

MySQL의 ErrorNo와 SqlState 및 Error message의 목록은 아래 링크에서 확인
http://dev.mysql.com/doc/refman/5.1/en/error-messages-server.html 

 
SqlState ErrorNo의 예제를 몇 개 살펴보자.

경고
    Error: 1262 SQLSTATE: 01000 (ER_WARN_TOO_MANY_RECORDS)
    Message: Row %ld was truncated; it contained more data than there were input columns

NotFound
    Error: 1329 SQLSTATE: 02000 (ER_SP_FETCH_NO_DATA)
    Message: No data - zero rows fetched, selected, or processed

그 외 에러
    Error: 1061 SQLSTATE: 42000 (ER_DUP_KEYNAME)
    Message: Duplicate key name '%s'