BTP

예외 처리 금지 패턴 — ROLLBACK 2줄 필수 #shorts #SAP #HANA

▶ YouTube에서 보기

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

아직 댓글이 없습니다.