왜 HANA 프로시저에서 예외 처리가 필수인가
SAP HANA SQLScript 프로시저는 기본적으로 예외가 발생해도 자동으로 트랜잭션을 롤백하지 않습니다. 예외 처리 블록을 명시적으로 작성하지 않으면, 부분적으로 커밋된 데이터가 DB에 남아 데이터 정합성이 깨질 수 있습니다. 이 글은 HANA SQLScript의 예외 처리 메커니즘을 처음부터 끝까지 실전 수준으로 다룹니다.
HANA SQLScript 예외 처리 기본 구조
SQLScript에서는 BEGIN ... EXCEPTION WHEN ... THEN ... END 블록으로 예외를 잡습니다. 가장 기본적인 패턴은 다음과 같습니다.
CREATE OR REPLACE PROCEDURE proc_order_insert(
IN iv_order_id NVARCHAR(10),
IN iv_amount DECIMAL(15,2)
)
LANGUAGE SQLSCRIPT
SQL SECURITY INVOKER AS
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
INSERT INTO t_order(order_id, amount, status)
VALUES (:iv_order_id, :iv_amount, 'NEW');
UPDATE t_order_summary SET total = total + :iv_amount
WHERE summary_id = 'GLOBAL';
END;
DECLARE EXIT HANDLER FOR SQLEXCEPTION는 프로시저 스코프 내 모든 SQL 예외를 잡습니다. EXIT 키워드는 핸들러 실행 후 BEGIN 블록을 즉시 종료한다는 의미입니다. ROLLBACK 후 RESIGNAL로 예외를 호출자에게 다시 던집니다.
HANDLER 종류: EXIT vs CONTINUE
HANA는 두 가지 핸들러 타입을 지원합니다.
-- EXIT: 예외 발생 시 핸들러 실행 후 블록 종료
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
END;
-- CONTINUE: 예외 발생 시 핸들러 실행 후 다음 문장으로 계속
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
-- 오류 로그만 기록하고 계속 진행
INSERT INTO t_error_log(msg, ts)
VALUES(::SQL_ERROR_MESSAGE, CURRENT_TIMESTAMP);
END;
트랜잭션 보호 목적에는 반드시 EXIT HANDLER를 써야 합니다. CONTINUE HANDLER는 오류를 무시하고 계속 진행하므로, 잘못 쓰면 데이터 불일치를 조용히 넘어갑니다.
특정 예외 코드만 잡기
모든 SQL 예외를 한꺼번에 잡는 대신, 특정 에러 코드만 처리할 수 있습니다.
CREATE OR REPLACE PROCEDURE proc_safe_upsert(
IN iv_product_id NVARCHAR(10),
IN iv_qty INTEGER
)
LANGUAGE SQLSCRIPT AS
BEGIN
-- 301: unique constraint violation
DECLARE CONTINUE HANDLER FOR SQL_ERROR_CODE 301
BEGIN
-- 이미 존재하는 경우 UPDATE로 대체
UPDATE t_product_stock
SET qty = qty + :iv_qty
WHERE product_id = :iv_product_id;
RETURN;
END;
INSERT INTO t_product_stock(product_id, qty)
VALUES(:iv_product_id, :iv_qty);
END;
이 패턴은 UPSERT 로직을 구현할 때 유용합니다. INSERT가 unique violation으로 실패하면 CONTINUE HANDLER가 UPDATE를 수행하고 RETURN으로 빠져나옵니다.
중첩 프로시저와 예외 전파
프로시저 A가 프로시저 B를 호출할 때, B에서 예외가 발생하면 A의 핸들러가 잡을 수 있습니다.
CREATE OR REPLACE PROCEDURE proc_inner(
IN iv_id NVARCHAR(10)
)
LANGUAGE SQLSCRIPT AS
BEGIN
-- 예외 처리 없음 — 예외가 호출자로 전파됨
INSERT INTO t_detail(id, created_at)
VALUES(:iv_id, CURRENT_TIMESTAMP);
END;
CREATE OR REPLACE PROCEDURE proc_outer(
IN iv_id NVARCHAR(10)
)
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
CALL proc_inner(:iv_id);
UPDATE t_header SET status = 'PROCESSED'
WHERE id = :iv_id;
END;
이 구조에서 proc_inner의 INSERT가 실패하면 예외가 proc_outer로 전파되고, proc_outer의 EXIT HANDLER가 ROLLBACK을 처리합니다. 두 INSERT가 원자적으로 동작합니다.
::SQL_ERROR_MESSAGE와 ::SQL_ERROR_CODE 변수 활용
핸들러 내부에서 HANA 시스템 변수를 이용해 오류 정보를 로그에 기록할 수 있습니다.
CREATE OR REPLACE PROCEDURE proc_with_logging(
IN iv_doc_id NVARCHAR(20)
)
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE lv_err_code INTEGER;
DECLARE lv_err_msg NVARCHAR(512);
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
lv_err_code := ::SQL_ERROR_CODE;
lv_err_msg := ::SQL_ERROR_MESSAGE;
INSERT INTO t_proc_error_log(
doc_id, err_code, err_msg, logged_at
) VALUES(
:iv_doc_id, :lv_err_code, :lv_err_msg, CURRENT_TIMESTAMP
);
ROLLBACK;
RESIGNAL;
END;
INSERT INTO t_document(doc_id, created_at)
VALUES(:iv_doc_id, CURRENT_TIMESTAMP);
CALL proc_update_index(:iv_doc_id);
END;
::SQL_ERROR_CODE는 HANA 에러 번호, ::SQL_ERROR_MESSAGE는 에러 메시지 텍스트입니다. 로그 INSERT는 ROLLBACK 전에 완료되어야 하므로, ROLLBACK 직전에 배치합니다. 단, 이 로그 INSERT 자체가 실패하면 무한 루프가 될 수 있으므로 별도 테이블에 분리하거나 Autonomous Transaction 패턴을 사용하는 것이 좋습니다.
SAVEPOINT를 활용한 부분 롤백
전체 롤백 대신 특정 지점까지만 롤백하고 싶을 때는 SAVEPOINT를 사용합니다.
CREATE OR REPLACE PROCEDURE proc_bulk_insert(
IN iv_batch_id NVARCHAR(10)
)
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE lv_count INTEGER := 0;
SAVEPOINT sp_start;
FOR lv_count IN 1..100 DO
BEGIN
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
-- 개별 행 실패는 무시하고 계속
INSERT INTO t_skip_log(batch_id, row_num, ts)
VALUES(:iv_batch_id, :lv_count, CURRENT_TIMESTAMP);
END;
INSERT INTO t_bulk_data(batch_id, seq, value)
VALUES(:iv_batch_id, :lv_count, :lv_count * 100);
END;
END FOR;
-- 모든 처리 완료 후 커밋
COMMIT;
END;
이 패턴에서는 개별 행 삽입 실패를 CONTINUE HANDLER로 잡아 스킵 로그에 기록하고, 나머지 행은 계속 처리합니다. 배치 처리 시 실패 허용 정책이 있을 때 유용합니다.
실전 체크리스트
프로시저를 배포하기 전 반드시 확인해야 할 사항입니다.
- 모든 DML(INSERT/UPDATE/DELETE)이 포함된 프로시저에 EXIT HANDLER가 있는가
- ROLLBACK 후 RESIGNAL을 빠뜨리지 않았는가 (호출자가 실패를 인지해야 함)
- CONTINUE HANDLER에서 데이터 불일치가 발생할 가능성은 없는가
- 중첩 프로시저 호출 시 최상위 프로시저에 ROLLBACK이 있는가
- 에러 로그 테이블이 ROLLBACK 대상에서 분리되어 있는가
HANA 공식 문서 참고
예외 처리 전체 문법은 SAP HANA SQLScript Reference의 "Exception Handling" 섹션(help.sap.com)에서 확인할 수 있습니다. HANDLER 선언 위치, 스코프 규칙, RESIGNAL 동작 방식이 자세히 설명되어 있습니다.
핵심 요약
HANA 프로시저에서 예외 처리를 생략하면 부분 커밋으로 인한 데이터 정합성 문제가 발생합니다. DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; RESIGNAL; END; 두 줄이 트랜잭션을 보호하는 최소 안전망입니다. 에러 코드 분기, 로깅, SAVEPOINT를 조합하면 운영 환경에서 발생하는 다양한 예외 시나리오를 안전하게 처리할 수 있습니다.
댓글 0
아직 댓글이 없습니다.