2026년 09월 02일 | DBMS Error 가이드
이 글에서 다루는 내용
ORA-06561 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-06561 given statement is not supported by package DBMS_SQL 는?
ORA-06561 에러는 Oracle의 동적 SQL 처리 패키지인 DBMS_SQL을 사용할 때, 해당 패키지가 지원하지 않는 SQL 구문을 실행하려고 할 때 발생합니다. DBMS_SQL 패키지는 모든 SQL 문장을 처리할 수 있는 것이 아니며, 특정 DDL, DML, 또는 PL/SQL 블록에 대해서만 제한적으로 동작합니다. 특히 DBMS_SQL.PARSE 단계에서 파싱은 성공하더라도, 이후 EXECUTE 또는 결과 페칭 단계에서 이 에러가 발생하는 경우가 많습니다.
주요 발생 원인
- DBMS_SQL이 지원하지 않는 SQL 구문 사용
DBMS_SQL 패키지는 SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER 등 표준 SQL 문장을 지원하지만, EXPLAIN PLAN, LOCK TABLE 같은 일부 특수 SQL 구문이나 PL/SQL 익명 블록(BEGIN ... END)을 직접 실행하려 할 때 이 에러가 발생할 수 있습니다. 특히 Oracle 버전에 따라 지원 범위가 다를 수 있으므로 사용 중인 Oracle 버전의 공식 문서를 반드시 확인해야 합니다.
- DBMS_SQL.TO_REFCURSOR 또는 DBMS_SQL.TO_CURSOR_NUMBER 변환 시 잘못된 커서 상태
DBMS_SQL에서 REF CURSOR로 변환하거나, 반대로 변환할 때 커서가 적절한 상태에 있지 않으면 이 에러가 발생합니다. 예를 들어 SELECT 문이 아닌 DML 문장으로 열린 커서를 TO_REFCURSOR로 변환하려 시도하거나, EXECUTE 이전에 변환을 시도하는 경우가 대표적입니다.
- 지원되지 않는 DDL 또는 복합 SQL 구문을 DBMS_SQL로 실행하려는 경우
DBMS_SQL을 통해 여러 SQL 문장을 세미콜론으로 이어 붙인 복합 SQL 문이나, MERGE 문의 특정 복잡한 형태, 또는 일부 Oracle 내부 명령어를 실행하려 할 때 이 에러가 발생할 수 있습니다. 단일 SQL 단위로 처리해야 하는 DBMS_SQL의 설계 철학에 맞지 않는 구문들이 대표적인 원인입니다.
해결 방법
원인 1 해결: 지원되지 않는 구문은 EXECUTE IMMEDIATE로 대체
DBMS_SQL이 지원하지 않는 SQL 구문은 EXECUTE IMMEDIATE를 사용하거나, Native Dynamic SQL로 전환하는 것이 가장 빠른 해결책입니다.
-- 잘못된 방법: DBMS_SQL로 지원 안 되는 구문 실행 시도
DECLARE
v_cursor INTEGER;
v_ret INTEGER;
BEGIN
v_cursor := DBMS_SQL.OPEN_CURSOR;
-- EXPLAIN PLAN 같은 구문은 DBMS_SQL에서 지원하지 않음
DBMS_SQL.PARSE(v_cursor, 'EXPLAIN PLAN FOR SELECT * FROM EMP', DBMS_SQL.NATIVE);
v_ret := DBMS_SQL.EXECUTE(v_cursor); -- ORA-06561 발생 가능
DBMS_SQL.CLOSE_CURSOR(v_cursor);
END;
/
-- 올바른 방법: EXECUTE IMMEDIATE 사용
BEGIN
EXECUTE IMMEDIATE 'EXPLAIN PLAN FOR SELECT * FROM EMP';
-- PLAN_TABLE에서 결과 조회
FOR rec IN (SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY())) LOOP
DBMS_OUTPUT.PUT_LINE(rec.PLAN_TABLE_OUTPUT);
END LOOP;
END;
/
원인 2 해결: 커서 상태 확인 후 TO_REFCURSOR 변환
DBMS_SQL.TO_REFCURSOR는 반드시 SELECT 문으로 열리고 EXECUTE가 완료된 커서에만 사용해야 합니다.
-- 잘못된 방법: DML 커서를 TO_REFCURSOR로 변환 시도
DECLARE
v_cursor INTEGER;
v_ret INTEGER;
v_refcur SYS_REFCURSOR;
BEGIN
v_cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(v_cursor,
'UPDATE EMP SET SAL = SAL * 1.1 WHERE DEPTNO = 10',
DBMS_SQL.NATIVE);
v_ret := DBMS_SQL.EXECUTE(v_cursor);
-- DML 커서는 TO_REFCURSOR 변환 불가 -> ORA-06561 발생
v_refcur := DBMS_SQL.TO_REFCURSOR(v_cursor);
END;
/
-- 올바른 방법: SELECT 문 커서만 TO_REFCURSOR로 변환
DECLARE
v_cursor INTEGER;
v_ret INTEGER;
v_refcur SYS_REFCURSOR;
v_empno NUMBER;
v_ename VARCHAR2(50);
BEGIN
v_cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(v_cursor,
'SELECT EMPNO, ENAME FROM EMP WHERE DEPTNO = 10',
DBMS_SQL.NATIVE);
-- SELECT 문에 대해 컬럼 정의 후 EXECUTE
DBMS_SQL.DEFINE_COLUMN(v_cursor, 1, v_empno);
DBMS_SQL.DEFINE_COLUMN(v_cursor, 2, v_ename, 50);
v_ret := DBMS_SQL.EXECUTE(v_cursor);
-- EXECUTE 이후에만 TO_REFCURSOR 변환 가능
v_refcur := DBMS_SQL.TO_REFCURSOR(v_cursor);
-- 이후 REF CURSOR로 데이터 페칭
LOOP
FETCH v_refcur INTO v_empno, v_ename;
EXIT WHEN v_refcur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('EMPNO: ' || v_empno || ', ENAME: ' || v_ename);
END LOOP;
CLOSE v_refcur;
END;
/
원인 3 해결: 복합 SQL을 단일 문장으로 분리 실행
여러 SQL 문장을 하나의 문자열로 묶어 DBMS_SQL에 전달하면 안 됩니다. 반드시 개별 SQL 단위로 분리하여 처리하세요.
-- 잘못된 방법: 세미콜론으로 이어진 복합 SQL
DECLARE
v_cursor INTEGER;
v_ret INTEGER;
BEGIN
v_cursor := DBMS_SQL.OPEN_CURSOR;
-- 여러 문장을 한 번에 전달 -> 오류 발생
DBMS_SQL.PARSE(v_cursor,
'INSERT INTO EMP_LOG VALUES(1); INSERT INTO EMP_LOG VALUES(2);',
DBMS_SQL.NATIVE);
v_ret := DBMS_SQL.EXECUTE(v_cursor);
DBMS_SQL.CLOSE_CURSOR(v_cursor);
END;
/
-- 올바른 방법: SQL 문장을 개별 실행
DECLARE
v_cursor INTEGER;
v_ret INTEGER;
PROCEDURE exec_sql(p_sql IN VARCHAR2) IS
l_cursor INTEGER;
l_ret INTEGER;
BEGIN
l_cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(l_cursor, p_sql, DBMS_SQL.NATIVE);
l_ret := DBMS_SQL.EXECUTE(l_cursor);
DBMS_SQL.CLOSE_CURSOR(l_cursor);
DBMS_OUTPUT.PUT_LINE('실행 완료: ' || p_sql);
EXCEPTION
WHEN OTHERS THEN
IF DBMS_SQL.IS_OPEN(l_cursor) THEN
DBMS_SQL.CLOSE_CURSOR(l_cursor);
END IF;
RAISE;
END;
BEGIN
exec_sql('INSERT INTO EMP_LOG VALUES(1)');
exec_sql('INSERT INTO EMP_LOG VALUES(2)');
COMMIT;
END;
/
예방 방법
- DBMS_SQL 사용 전 지원 구문 범위를 명확히 파악하고 문서화하라
프로젝트 초기 설계 단계에서 DBMS_SQL과 EXECUTE IMMEDIATE(Native Dynamic SQL)의 사용 범위를 명확히 구분하여 코딩 가이드라인으로 정립하세요. 단순한 동적 SQL 실행은 EXECUTE IMMEDIATE를, 바인드 변수 개수가 런타임에 결정되거나 대량 바인딩이 필요한 경우에만 DBMS_SQL을 사용하도록 규칙을 정하면 이 에러를 원천 차단할 수 있습니다. 또한 팀 내 코드 리뷰 시 DBMS_SQL.PARSE에 전달되는 SQL 구문의 적합성을 반드시 검토하는 프로세스를 만들어야 합니다.
- 커서 예외 처리와 상태 검증 로직을 반드시 포함하라
DBMS_SQL을 사용하는 모든 PL/SQL 블록에는 예외 처리 핸들러와 DBMS_SQL.IS_OPEN 검증 로직을 반드시 포함해야 합니다. 커서 누수(Cursor Leak)는 ORA-01000(최대 커서 수 초과) 에러로 이어질 수 있으며, 비정상 종료 시 커서가 닫히지 않아 시스템 전반에 영향을 줄 수 있습니다. 아래와 같은 패턴을 표준화하면 ORA-06561을 포함한 다양한 DBMS_SQL 관련 에러에 선제적으로 대응할 수 있습니다.
-- 표준화된 DBMS_SQL 사용 패턴 (예방적 코드 구조)
DECLARE
v_cursor INTEGER := NULL;
v_ret INTEGER;
BEGIN
v_cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(v_cursor, :p_sql_text, DBMS_SQL.NATIVE);
v_ret := DBMS_SQL.EXECUTE(v_cursor);
DBMS_SQL.CLOSE_CURSOR(v_cursor);
EXCEPTION
WHEN OTHERS THEN
-- 커서가 열려 있으면 반드시 닫음
IF v_cursor IS NOT NULL AND DBMS_SQL.IS_OPEN(v_cursor) THEN
DBMS_SQL.CLOSE_CURSOR(v_cursor);
END IF;
-- 에러 로깅 후 재발생
DBMS_OUTPUT.PUT_LINE('에러 발생 [' || SQLCODE || ']: ' || SQLERRM);
RAISE;
END;
/
관련 에러
- ORA-06550: PL/SQL 컴파일 오류로, 동적 SQL 블록 내 문법 오류 시 함께 발생하는 경우가 있습니다.
- ORA-01000: 최대 열린 커서 수 초과 에러로,
DBMS_SQL커서를 제대로 닫지 않을 때 ORA-06561 이후 이 에러가 연쇄 발생할 수 있습니다. - ORA-06512: PL/SQL 스택 추적 에러로, ORA-06561 발생 시 함께 출력되어 정확한 오류 위치를 알려줍니다.
- ORA-00900:
invalid SQL statement에러로,DBMS_SQL에 완전히 잘못된 구문이 전달될 때 ORA-06561과 유사한 맥락에서 발생합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.