HANA SQLScript에서 예외 처리를 생략하면 생기는 일
SAP HANA 프로시저에서 예외 처리 블록을 작성하지 않으면 DML(INSERT/UPDATE/DELETE) 실행 도중 오류가 발생했을 때 트랜잭션이 자동으로 롤백되지 않습니다. 일부 데이터만 변경된 상태로 커밋될 수 있어 데이터 정합성이 깨집니다. 이 글은 HANA SQLScript에서 절대로 빠뜨리면 안 되는 예외 처리 2줄 패턴과, 자주 발생하는 실수 시나리오를 다룹니다.
예외 처리 없는 프로시저 — 위험한 패턴
-- 위험: 예외 처리 없음
CREATE OR REPLACE PROCEDURE proc_transfer_stock(
IN iv_from_loc NVARCHAR(10),
IN iv_to_loc NVARCHAR(10),
IN iv_product NVARCHAR(18),
IN iv_qty INTEGER
)
LANGUAGE SQLSCRIPT AS
BEGIN
-- Step 1: 출발지 재고 차감
UPDATE t_stock_location
SET qty = qty - :iv_qty
WHERE location = :iv_from_loc AND product_id = :iv_product;
-- Step 2: 도착지 재고 증가 (이 시점에 오류 발생 시?)
UPDATE t_stock_location
SET qty = qty + :iv_qty
WHERE location = :iv_to_loc AND product_id = :iv_product;
-- Step 2에서 오류 발생 → Step 1은 이미 실행됨
-- 출발지 재고만 줄고 도착지는 안 늘어남 → 데이터 불일치
END;
필수 2줄 패턴: ROLLBACK + RESIGNAL
-- 올바른 패턴: EXIT HANDLER + ROLLBACK + RESIGNAL
CREATE OR REPLACE PROCEDURE proc_transfer_stock(
IN iv_from_loc NVARCHAR(10),
IN iv_to_loc NVARCHAR(10),
IN iv_product NVARCHAR(18),
IN iv_qty INTEGER
)
LANGUAGE SQLSCRIPT AS
BEGIN
-- 필수 2줄: 모든 프로시저에 이 패턴을 포함할 것
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK; -- 1줄: 부분 변경 취소
RESIGNAL; -- 2줄: 오류를 호출자에게 전달
END;
-- Step 1: 출발지 재고 차감
UPDATE t_stock_location
SET qty = qty - :iv_qty
WHERE location = :iv_from_loc AND product_id = :iv_product;
-- Step 2: 도착지 재고 증가
UPDATE t_stock_location
SET qty = qty + :iv_qty
WHERE location = :iv_to_loc AND product_id = :iv_product;
-- 여기까지 오류 없으면 커밋
COMMIT;
END;
ROLLBACK과 RESIGNAL, 이 두 줄이 트랜잭션 무결성의 핵심입니다. ROLLBACK만 쓰고 RESIGNAL을 빠뜨리면 오류가 조용히 삼켜져서 호출자가 실패를 모릅니다.
왜 RESIGNAL이 필요한가
-- RESIGNAL 없는 경우: 호출자가 성공으로 착각
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
-- RESIGNAL 없음 → 프로시저가 정상 종료된 것처럼 보임
END;
-- 호출 측
CALL proc_transfer_stock('WH01', 'WH02', 'PROD-A', 100);
-- sy-subrc = 0 (성공) 으로 잘못 판단 → 이후 로직 오류
-- RESIGNAL 있는 경우: 호출자가 실패를 인지
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL; -- 예외를 호출자에게 다시 던짐
END;
-- 호출 측에서 예외 처리
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
-- 재고 이동 실패 처리
INSERT INTO t_transfer_error_log(...)
VALUES(...);
END;
CALL proc_transfer_stock('WH01', 'WH02', 'PROD-A', 100);
END;
에러 정보를 로그에 남기는 완성형 패턴
CREATE OR REPLACE PROCEDURE proc_safe_invoice_post(
IN iv_invoice_id NVARCHAR(20),
IN iv_amount DECIMAL(15,2)
)
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;
-- 에러 로그 (ROLLBACK 전에 기록 — 같은 트랜잭션)
-- 주의: ROLLBACK 후에는 이 INSERT도 롤백됨
-- 별도 자율 트랜잭션 필요 시 다른 테이블 또는 별도 처리
ROLLBACK;
-- ROLLBACK 후 별도 에러 로그 INSERT (새 트랜잭션)
INSERT INTO t_proc_error_log(proc_name, err_code, err_msg, logged_at)
VALUES('proc_safe_invoice_post', :lv_err_code, :lv_err_msg, CURRENT_TIMESTAMP);
RESIGNAL;
END;
INSERT INTO t_invoice(invoice_id, amount, status, posted_at)
VALUES(:iv_invoice_id, :iv_amount, 'POSTED', CURRENT_TIMESTAMP);
UPDATE t_invoice_summary SET total = total + :iv_amount
WHERE summary_key = 'TOTAL';
COMMIT;
END;
중첩 호출에서의 트랜잭션 경계
-- 최상위 프로시저에만 ROLLBACK
-- 내부 프로시저는 RESIGNAL만
CREATE OR REPLACE PROCEDURE proc_inner_validate(IN iv_id NVARCHAR(10))
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
RESIGNAL; -- ROLLBACK 없음 — 상위에서 처리
END;
IF :iv_id IS NULL OR :iv_id = '' THEN
SIGNAL SQL_ERROR_CODE 99001
SET MESSAGE_TEXT = 'ID는 필수입니다';
END IF;
END;
공식 문서
HANA SQLScript 예외 처리 전체 문법은 SAP HANA SQLScript Reference에서 확인하세요.
댓글 0
아직 댓글이 없습니다.