2026년 07월 13일 | DBMS Error 가이드
이 글에서 다루는 내용
42P11 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
42P11 invalid cursor definition 는?
PostgreSQL 에러 코드 42P11은 잘못된 커서 정의(invalid cursor definition)로 발생하는 오류입니다. 주로 커서(CURSOR)를 선언하거나 사용하는 과정에서 문법적으로 올바르지 않거나, 커서 정의에 허용되지 않는 구문이 포함된 경우에 발생합니다. PL/pgSQL 함수나 프로시저 내에서 커서를 잘못 선언할 때, 또는 DECLARE 블록에서 커서에 부적합한 쿼리를 바인딩할 때 이 에러를 자주 마주치게 됩니다.
주요 발생 원인
1. 커서에 FOR UPDATE와 함께 허용되지 않는 쿼리 사용
커서에 FOR UPDATE 옵션을 사용할 때, GROUP BY, DISTINCT, UNION, 집계 함수(aggregate function) 등 업데이트를 허용하지 않는 구문을 쿼리에 포함하면 이 에러가 발생합니다. PostgreSQL은 갱신 가능한 커서(updatable cursor)에 대해 엄격한 제약을 두고 있으며, 결과 집합이 단일 테이블의 행에 직접 매핑되어야 합니다. 따라서 복잡한 집계나 집합 연산이 포함된 쿼리를 FOR UPDATE 커서에 사용하면 에러가 발생합니다.
-- 잘못된 예: GROUP BY와 FOR UPDATE를 함께 사용
DO $$
DECLARE
cur CURSOR FOR
SELECT department_id, COUNT(*) AS emp_count
FROM employees
GROUP BY department_id
FOR UPDATE; -- 42P11 에러 발생!
BEGIN
OPEN cur;
END;
$$;
2. PL/pgSQL 함수 내에서 커서 선언 문법 오류
PL/pgSQL에서 커서를 선언할 때 올바른 문법을 따르지 않으면 42P11 에러가 발생합니다. 예를 들어, CURSOR FOR 대신 잘못된 키워드를 사용하거나, 바인드 변수(bind variable)를 잘못된 위치에 배치하는 경우입니다. 커서 변수 선언 시 refcursor 타입과 명시적 커서(bound cursor) 선언 방식을 혼동하는 경우에도 이 에러가 나타날 수 있습니다.
-- 잘못된 예: 잘못된 커서 선언 문법
DO $$
DECLARE
-- 잘못된 방식: CURSOR 키워드 위치 오류
my_cur FOR SELECT id, name FROM users; -- 42P11 에러 발생!
BEGIN
OPEN my_cur;
END;
$$;
3. 스크롤 가능(SCROLL) 커서와 FOR UPDATE 동시 사용
SCROLL 옵션과 FOR UPDATE를 동시에 지정하는 것은 PostgreSQL에서 허용되지 않습니다. 스크롤 가능한 커서는 결과를 앞뒤로 탐색할 수 있도록 설계되어 있는데, 이를 갱신 가능한 커서와 함께 사용하면 내부적으로 충돌이 발생합니다. 이 조합은 커서 정의 자체가 논리적으로 모순되기 때문에 PostgreSQL이 즉시 에러를 반환합니다.
-- 잘못된 예: SCROLL과 FOR UPDATE 동시 사용
DO $$
DECLARE
cur SCROLL CURSOR FOR
SELECT id, name FROM users
FOR UPDATE; -- 42P11 에러 발생!
BEGIN
OPEN cur;
END;
$$;
해결 방법
원인 1 해결: 집계 쿼리에서 FOR UPDATE 제거
집계 함수나 GROUP BY, DISTINCT 등이 포함된 쿼리에서는 FOR UPDATE를 제거하고, 별도의 업데이트 로직을 구현해야 합니다.
-- 올바른 예: 집계 쿼리에서 FOR UPDATE 제거
DO $$
DECLARE
cur CURSOR FOR
SELECT department_id, COUNT(*) AS emp_count
FROM employees
GROUP BY department_id; -- FOR UPDATE 없이 선언
rec RECORD;
BEGIN
FOR rec IN cur LOOP
RAISE NOTICE 'Department: %, Count: %', rec.department_id, rec.emp_count;
END LOOP;
END;
$$;
-- 갱신이 필요하다면 별도 UPDATE 문 사용
UPDATE employees
SET salary = salary * 1.1
WHERE department_id = 10;
원인 2 해결: 올바른 커서 선언 문법 사용
PL/pgSQL에서 커서는 반드시 변수명 CURSOR FOR 쿼리 형식으로 선언해야 합니다.
-- 올바른 예: 정확한 커서 선언 문법
DO $$
DECLARE
-- 바운드 커서 (쿼리 고정)
my_cur CURSOR FOR SELECT id, name FROM users WHERE active = true;
-- 언바운드 커서 (쿼리 나중에 지정)
ref_cur refcursor;
rec RECORD;
BEGIN
-- 바운드 커서 사용
FOR rec IN my_cur LOOP
RAISE NOTICE 'User ID: %, Name: %', rec.id, rec.name;
END LOOP;
-- 언바운드 커서 사용
OPEN ref_cur FOR SELECT id, name FROM users WHERE active = false;
FETCH ref_cur INTO rec;
RAISE NOTICE 'Inactive User: %', rec.name;
CLOSE ref_cur;
END;
$$;
원인 3 해결: SCROLL과 FOR UPDATE 분리 사용
스크롤 기능이 필요한 경우에는 FOR UPDATE를 제거하고, 업데이트가 필요한 경우에는 SCROLL을 제거합니다.
-- 올바른 예 1: 스크롤 가능한 커서 (읽기 전용)
DO $$
DECLARE
cur SCROLL CURSOR FOR
SELECT id, name FROM users ORDER BY id;
rec RECORD;
BEGIN
OPEN cur;
FETCH LAST FROM cur INTO rec;
RAISE NOTICE 'Last User: %', rec.name;
FETCH FIRST FROM cur INTO rec;
RAISE NOTICE 'First User: %', rec.name;
CLOSE cur;
END;
$$;
-- 올바른 예 2: 갱신 가능한 커서 (스크롤 없음)
DO $$
DECLARE
cur CURSOR FOR
SELECT id, name, salary FROM employees
WHERE department_id = 10
FOR UPDATE;
rec RECORD;
BEGIN
OPEN cur;
LOOP
FETCH cur INTO rec;
EXIT WHEN NOT FOUND;
UPDATE employees SET salary = rec.salary * 1.05
WHERE CURRENT OF cur;
END LOOP;
CLOSE cur;
END;
$$;
예방 방법
1. 커서 정의 전 쿼리 유효성 사전 검증
커서에 사용할 쿼리를 먼저 독립적으로 실행하여 문법 및 논리적 오류가 없는지 확인한 후, 커서 선언에 포함시키는 습관을 들이세요. 특히 FOR UPDATE를 사용하는 경우, 해당 쿼리가 단일 테이블을 대상으로 하고 집계나 집합 연산이 없는지 반드시 확인해야 합니다. PostgreSQL 공식 문서의 [커서 섹션](https://www.postgresql.org/docs/current/plpgsql-cursors.html)을 참고하여 갱신 가능한 커서의 요건을 숙지하는 것이 좋습니다.
-- 커서 선언 전 쿼리 단독 테스트
EXPLAIN
SELECT id, name, salary
FROM employees
WHERE department_id = 10
FOR UPDATE;
-- 위 쿼리가 정상 실행되면 커서에 포함 가능
2. 커서 사용 시 명확한 타입과 옵션 명시
커서를 선언할 때 SCROLL / NO SCROLL, FOR UPDATE / 읽기 전용 등의 옵션을 명확하게 명시하고, 팀 내 코딩 컨벤션으로 정착시키세요. 암묵적인 기본값에 의존하기보다 의도를 명확히 코드에 표현함으로써 추후 유지보수 시 발생할 수 있는 오류를 사전에 방지할 수 있습니다.
-- 권장: 옵션을 명확히 명시한 커서 선언
DECLARE
-- 읽기 전용, 스크롤 가능 커서
read_cur NO SCROLL CURSOR FOR
SELECT id, name FROM users ORDER BY id;
-- 갱신 가능, 스크롤 불가 커서
update_cur NO SCROLL CURSOR FOR
SELECT id, salary FROM employees
WHERE department_id = 5
FOR UPDATE OF salary;
관련 에러
42601(syntax_error): 커서 선언 자체의 SQL 문법 오류로,42P11과 함께 자주 발생합니다. 커서 쿼리 내 기본적인 문법 오류가 있을 때 나타납니다.55000(object_not_in_prerequisite_state): 이미 열려 있는 커서를 다시 열려고 하거나, 닫힌 커서에서 FETCH를 시도할 때 발생합니다.34000(invalid_cursor_name): 존재하지 않는 커서 이름을 참조할 때 발생하며, 동적 커서 관리 시 주의가 필요합니다.24000(invalid_transaction_state): 트랜잭션 외부에서WITH HOLD없이 선언된 커서를 사용하려 할 때 발생할 수 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.