PostgreSQL 22011 오류 원인과 해결 방법 완벽 가이드

22011
2026년 10월 21일 | DBMS Error 가이드

이 글에서 다루는 내용

22011 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

22011 substring error 는?

PostgreSQL 에러 코드 22011은 substring error로, 문자열에서 특정 부분을 추출하는 SUBSTRING 함수 또는 관련 함수를 사용할 때 잘못된 인자값이 전달될 경우 발생합니다. 대표적으로 시작 위치(start position)나 길이(length) 값이 유효하지 않은 범위를 벗어났을 때 이 에러가 트리거됩니다. 실무에서는 사용자 입력값을 직접 SUBSTRING 함수에 넘기거나, 동적 쿼리를 생성하는 과정에서 검증 없이 값을 사용할 때 자주 마주치는 에러입니다.


주요 발생 원인

1. SUBSTRING 함수의 길이(length) 인자에 음수값 전달

SUBSTRING(string FROM start FOR length) 구문에서 length 파라미터에 음수값이 들어오면 PostgreSQL은 즉시 22011 에러를 발생시킵니다. 이는 SQL 표준에서 문자열의 길이는 반드시 0 이상이어야 한다고 명시하고 있기 때문이며, 사용자 입력값을 필터링하지 않고 그대로 넘기는 코드에서 빈번하게 발생합니다.

2. 정규표현식(REGEX) 기반 SUBSTRING 함수 사용 시 패턴 오류

PostgreSQL은 SUBSTRING(string FROM pattern) 형태로 정규표현식을 활용한 문자열 추출을 지원합니다. 이때 패턴이 올바르지 않거나, POSIX 정규표현식 문법과 맞지 않는 패턴이 전달되면 내부적으로 substring 처리 중 에러가 발생할 수 있습니다. 특히 외부 시스템에서 전달된 동적 패턴을 검증 없이 사용하는 경우가 주요 원인입니다.

3. 연산 결과로 생성된 음수 또는 비정상적인 오프셋 값 사용

테이블의 컬럼 값을 계산하여 SUBSTRING의 시작 위치나 길이로 활용할 때, 예상치 못한 데이터로 인해 음수나 NULL이 아닌 비정상적인 값이 산출되는 경우가 있습니다. 예를 들어 LENGTH(column) - some_value 형태의 계산에서 some_value가 LENGTH(column)보다 클 경우 음수가 되어 에러를 유발합니다.


해결 방법

원인 1: 음수 length 값 처리

GREATEST 함수를 활용하여 length 값이 항상 0 이상이 되도록 보정합니다.

-- 문제 발생 쿼리 예시
SELECT SUBSTRING('Hello, PostgreSQL!' FROM 1 FOR -5);
-- ERROR:  negative substring length not allowed

-- 해결 방법 1: GREATEST로 최솟값 보정
SELECT SUBSTRING('Hello, PostgreSQL!' FROM 1 FOR GREATEST(0, -5));
-- 결과: '' (빈 문자열 반환, 에러 없음)

-- 해결 방법 2: CASE WHEN으로 방어 로직 추가
SELECT
  CASE
    WHEN length_val < 0 THEN ''
    ELSE SUBSTRING(target_column FROM 1 FOR length_val)
  END AS safe_substring
FROM (
  SELECT 'Hello, PostgreSQL!' AS target_column, -5 AS length_val
) AS t;

-- 실무 예시: 사용자 입력 기반 동적 substring
SELECT
  SUBSTRING(
    product_name
    FROM 1
    FOR GREATEST(0, COALESCE(user_input_length, 0))
  ) AS trimmed_name
FROM products;

원인 2: 정규표현식 패턴 오류 처리

정규표현식 패턴을 사용하기 전에 유효성을 검사하거나, 예외 처리를 통해 안전하게 사용합니다.

-- 문제 발생 예시: 잘못된 정규표현식 패턴
SELECT SUBSTRING('order-20240101-XYZ' FROM '([0-9]{8}');
-- 패턴이 닫히지 않아 에러 발생 가능

-- 올바른 정규표현식 패턴 사용
SELECT SUBSTRING('order-20240101-XYZ' FROM '([0-9]{8})');
-- 결과: 20240101

-- 실무 예시: 이메일에서 도메인 추출
SELECT
  email,
  SUBSTRING(email FROM '@(.+)$') AS domain
FROM users
WHERE email IS NOT NULL;

-- 안전한 동적 패턴 처리 (PL/pgSQL 활용)
DO $$
DECLARE
  v_result TEXT;
BEGIN
  BEGIN
    v_result := SUBSTRING('test-string-123' FROM '[0-9]+');
    RAISE NOTICE 'Result: %', v_result;
  EXCEPTION
    WHEN substring_error THEN
      RAISE NOTICE 'Invalid pattern or substring error occurred';
    WHEN others THEN
      RAISE NOTICE 'Unexpected error: %', SQLERRM;
  END;
END;
$$;

원인 3: 연산 기반 오프셋 값 보정

컬럼 값을 연산하여 SUBSTRING 인자로 사용할 때는 반드시 결과값을 검증합니다.

-- 문제 상황: 계산된 length가 음수가 될 수 있는 쿼리
SELECT
  description,
  SUBSTRING(description FROM 1 FOR LENGTH(description) - 10)
FROM articles
WHERE id = 42;
-- description 길이가 10 이하이면 음수 length 발생 → 에러

-- 해결 방법: NULLIF와 GREATEST 조합으로 안전하게 처리
SELECT
  description,
  SUBSTRING(
    description
    FROM 1
    FOR GREATEST(0, LENGTH(description) - 10)
  ) AS trimmed_description
FROM articles;

-- 실무 예시: 마지막 N글자를 제거한 문자열 추출
SELECT
  file_name,
  SUBSTRING(
    file_name
    FROM 1
    FOR GREATEST(0, LENGTH(file_name) - LENGTH(extension))
  ) AS base_name
FROM (
  SELECT
    'report_2024.pdf' AS file_name,
    '.pdf'            AS extension
) AS f;

예방 방법

1. 입력값 검증 레이어를 데이터베이스 레벨에서 구현하기

애플리케이션 레벨의 검증만 믿지 말고, 데이터베이스 함수나 트리거 레벨에서 SUBSTRING 함수에 전달되는 인자를 항상 검증하는 습관을 들여야 합니다. 아래와 같이 래퍼(Wrapper) 함수를 만들어 팀 전체에서 표준으로 사용하면 동일한 실수를 반복하지 않을 수 있습니다.

-- 안전한 SUBSTRING 래퍼 함수 생성
CREATE OR REPLACE FUNCTION safe_substring(
  p_input  TEXT,
  p_start  INTEGER,
  p_length INTEGER
)
RETURNS TEXT
LANGUAGE plpgsql
IMMUTABLE
AS $$
BEGIN
  -- 입력값 검증
  IF p_input IS NULL THEN
    RETURN NULL;
  END IF;

  IF p_start < 1 THEN
    p_start := 1;
  END IF;

  IF p_length < 0 THEN
    p_length := 0;
  END IF;

  RETURN SUBSTRING(p_input FROM p_start FOR p_length);
END;
$$;

-- 사용 예시
SELECT safe_substring('PostgreSQL Expert', 1, -3);  -- '' 반환 (에러 없음)
SELECT safe_substring('PostgreSQL Expert', 1, 10);   -- 'PostgreSQL' 반환

2. CHECK 제약조건 및 도메인(Domain) 활용으로 데이터 품질 보장

SUBSTRING 에러의 근본 원인은 결국 잘못된 데이터가 쿼리에 유입되는 것입니다. 테이블 설계 단계에서 CHECK 제약조건과 PostgreSQL DOMAIN을 활용하여 애초에 비정상적인 값이 저장되지 않도록 방어합니다.

-- 양수만 허용하는 도메인 생성
CREATE DOMAIN positive_integer AS INTEGER
  CHECK (VALUE > 0);

-- 테이블에 도메인 적용
CREATE TABLE document_config (
  id           SERIAL PRIMARY KEY,
  doc_name     TEXT        NOT NULL,
  start_pos    positive_integer NOT NULL DEFAULT 1,
  extract_len  positive_integer NOT NULL DEFAULT 100
);

-- CHECK 제약조건으로 문자열 길이 범위 제한
ALTER TABLE documents
  ADD CONSTRAINT chk_content_length
  CHECK (LENGTH(content) BETWEEN 1 AND 100000);

관련 에러

  • 22007 (invalid_datetime_format): 날짜/시간 형식 변환 시 발생하며, 문자열 파싱 오류라는 점에서 22011과 유사한 맥락을 가집니다.
  • 22003 (numeric_value_out_of_range): 숫자 연산 결과가 허용 범위를 초과할 때 발생하며, SUBSTRING의 오프셋 계산 오류와 함께 복합적으로 발생할 수 있습니다.
  • 22001 (string_data_right_truncation): 문자열 데이터가 컬럼의 허용 길이를 초과할 때 발생하며, 문자열 처리 관련 에러 클래스 22에 속합니다.
  • 2201S (invalid_escape_sequence): 정규표현식 또는 LIKE 패턴에서 이스케이프 처리가 잘못되었을 때 발생하며, SUBSTRING(string FROM pattern) 형태 사용 시 함께 검토가 필요합니다.
DBMS 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기