Skip to content

지원 매트릭스 (한국어)

🌐 SUPPORT_MATRIX.md의 번역입니다. 영어 원문이 표준이며, CI가 영어 원문과의 구조 일치를 검사합니다.

pycubrid 릴리스의 호환성과 기능 지원.

참조: 이 페이지는 최근 배포된 릴리스보다 앞서 있을 수 있는 main 브랜치를 설명합니다. main의 pycubrid/__init__.py에 있는 pycubrid.__version__은 소스의 버전 표시일 뿐 그 버전이 배포되었다는 증거가 아닙니다. main은 릴리스 PR이 바꿀 때까지 마지막으로 배포된 번호를 유지하며, 열려 있는 릴리스 PR은 후보일 뿐입니다. 버전은 그 태그가 릴리스 목록에 나타나고 파일이 PyPI에 올라갔을 때 배포된 것이며, 그 버전의 지원 범위는 해당 태그 시점의 이 페이지(docs/SUPPORT_MATRIX.md)입니다. 릴리스 노트는 CHANGELOG.md에 있습니다.


버전 호환성

Python

Python 버전 상태
3.11 ✅ 지원
3.12 ✅ 지원
3.13 ✅ 지원
3.14 ✅ 지원
3.15 ✅ 지원 (표준 GIL 빌드)
3.10 ❌ 1.10.0부터 미지원 (1.9.x까지 지원)
< 3.10 ❌ 미지원

Python 3.10 지원 종료: Python 3.10은 2026-10-01에 공식 지원이 종료됐습니다(PEP 619). 1.8.x와 1.9.x는 Python 3.10을 지원하며, 1.9.0에 사전 안내가 포함됐습니다. 1.10.0부터 패키지는 Python 3.11 이상을 요구하므로(requires-python = ">=3.11"), Python 3.10의 pip은 계속 1.9.x를 설치합니다. 수정은 최신 릴리스 라인에만 제공되므로 1.9.x의 추가 릴리스는 없습니다. 업그레이드하기 전에 Python을 업그레이드하고 가상 환경을 새로 만든 뒤 애플리케이션을 검증하세요.

Python 3.15: 표준 GIL을 쓰는 CPython 3.15.0 정식 릴리스를 지원합니다. free-threaded 빌드는 지원하지 않습니다. 전체 호환성 매트릭스는 수동 실행과 모든 릴리스에서 Python 3.15를 지원되는 모든 CUBRID 버전과 함께 실행하며, 3.15는 최신 오프라인 엔드포인트입니다. 수동 python-canary.yml 레인은 계속 3.15에서 wheel/sdist의 새 환경 설치를 확인합니다. 환경 참고 사항:

  • Decimal 하위 클래스 파라미터에는 C _decimal 모듈이 필요합니다. libmpdec 없이 빌드한 Python에는 이 모듈이 없으며, 이런 파라미터는 ProgrammingError를 발생시킵니다. 일반 Decimal 값은 영향이 없습니다. CI가 쓰는 actions/setup-python 3.15.0 빌드, python:3.15-rc Docker 이미지, libmpdec-dev 없는 소스 빌드 모두에서 이 모듈이 없음을 확인했습니다. 따라서 CI는 C 모듈 없이 3.15 셀을 실행하며, 이 모듈이 필요한 테스트는 그곳에서 건너뜁니다.
  • Python 3.15는 ksc5601 코덱 별칭과 다른 KS C 5601 별칭을 CP949로 매핑합니다. pycubrid는 charset="ksc5601"을 EUC-KR로 유지하며, 다른 별칭은 Python을 따르므로 거부됩니다(연결 참고).
  • 3.15를 지원 목록에 올린 시점에 Docker Hub에는 정식 python:3.15 이미지가 없었습니다. CI는 actions/setup-python을 사용합니다.

CUBRID 서버

CUBRID 버전 상태 비고
11.4 ✅ 지원 최신 안정 버전
11.2 ✅ 지원
11.0 ✅ 지원
10.2 ✅ 지원 최소 테스트 버전
< 10.2 ❌ 미지원 현재 드라이버는 CAS 프로토콜 v8 대상

CI 매트릭스

검증 일상 실행 전체 호환성
오프라인 Ubuntu/Python 3.12 PR 스모크. 고위험 PR은 커버리지를 제외한 전체 회귀. main/변경이 있는 주간 실행은 95% 커버리지의 전체 스위트 로컬 전체 테스트는 그대로 사용 가능
라이브 통합 고위험 PR은 Python 3.14/CUBRID 11.4. main/변경이 있는 주간 실행은 Python 3.11/CUBRID 10.2도 실행 수동 실행과 모든 릴리스에서 Python 3.11–3.15 × CUBRID 10.2/11.0/11.2/11.4

CI 실행 정책을 참고하세요. 이 정책은 각 실행이 무엇을 테스트할지를 고를 뿐, 위에 나열한 지원 버전을 정의하지 않습니다. 대표 PR 검사는 지원되는 모든 조합에 대한 증거가 아닙니다.

CUBRID 버전 간 서버 동작 차이

버전 차분 테스트(tests/test_version_differential.py)는 지원되는 모든 서버에서 같은 문장에 대해 pycubrid가 반환하는 것을 비교합니다. 오류 클래스, errno와 sqlstate, rowcount, lastrowid, description, 각 값의 Python 타입과 값이 대상입니다. pycubrid는 모든 버전을 같은 방식으로 디코딩합니다. 아래 차이는 서버에서 비롯된 것이며(csql에서도 같은 결과가 나옵니다), 스위트가 허용하는 차이는 이것뿐입니다. 각 항목은 업스트림 링크와 함께 tests/helpers/version_matrix.py에 있습니다.

동작 10.2 11.0 11.2 11.4
CHAR(n)/VARCHAR(n)/BIT VARYING(n) 컬럼보다 긴 문자열/비트 값 조용히 잘림 ProgrammingError -494 ProgrammingError -494 ProgrammingError -494
REGEXP_LIKE / REGEXP_* 함수 정의되지 않음 (-494) 사용 가능 사용 가능 사용 가능
조건으로 쓴 단순 값 (IF(1, ...), WHERE 1) 허용 허용 ProgrammingError -493 ProgrammingError -493
'a' \|\| 'b' / CONCAT의 타입 CHAR VARCHAR VARCHAR VARCHAR
CAST('a' AS VARCHAR) = 'a ' 참 거짓 거짓 거짓
FK로 참조되는 부모 테이블의 TRUNCATE IntegrityError -924 IntegrityError -924 IntegrityError -1284 IntegrityError -1284
COUNT(*)의 타입 INTEGER INTEGER BIGINT BIGINT
정수와 NUMERIC의 혼합 ((5) * (0.100)) NUMERIC(14,3) NUMERIC(14,3) NUMERIC(19,3). 17자리 이상 BIGINT는 오버플로 (-427) NUMERIC(14,3)
3바이트 UTF-8 문자가 있는 텍스트에 대한 REGEXP 연산자 일치 절대 일치하지 않음 절대 일치하지 않음 절대 일치하지 않음
3바이트 UTF-8 문자가 있는 텍스트에 대한 REGEXP_* 정의되지 않음 NULL 0 0

기능 지원

PEP 249 (DB-API 2.0)

기능 상태 비고
apilevel ✅ "2.0"
threadsafety ✅ 1 스레드는 모듈 공유 가능, 연결은 불가
paramstyle ✅ "qmark" ? 파라미터 마커
connect() ✅ 모듈 수준 생성자
Connection ✅ 전체 수명 주기: 커밋, 롤백, 종료, 오토커밋
Cursor ✅ execute, executemany, fetch*, callproc, description, rowcount
Cursor.nextset() ✅ 1.2.0부터 (#79) — NotSupportedError 발생, CUBRID는 다중 결과 집합이 없음
예외 계층 ✅ PEP 249 예외 클래스 전체 10종
DatabaseError의 errno / sqlstate ✅ 1.2.0부터 (#71) — SQLSTATE 매핑 19종
타입 객체 ✅ STRING, BINARY, NUMBER, DATETIME, ROWID
타입 생성자 ✅ Date, Time, Timestamp, *FromTicks, Binary
컨텍스트 매니저 ✅ Connection과 Cursor 모두

연결 기능

기능 상태 도입 비고
connect_timeout ✅ 1.0.0 TCP 연결 타임아웃 (초), 리다이렉트 재연결을 포함한 시도마다 (#855)
read_timeout (동기) ✅ 1.2.0 (#81) 소켓 송신·수신 호출별 타임아웃. 조각마다 제때 도착하는 응답은 끊지 않음 (#855)
read_timeout (비동기) ✅ 1.2.0 (#82) 요청 왕복 전체에 대한 asyncio.wait_for() 기한. 두 드라이버 모두 SQL 벽시계 기한이 아니며 서버의 문을 취소하지 않음 (#855)
fetch_size ✅ 1.2.0 (#81) 구성 가능한 결과 배치 크기 (기본 100)
autocommit 속성 ✅ 1.0.0 Connection에서 조회/설정
Connection.ping() ✅ 1.2.0 (#70) 네이티브 CHECK_CAS 헬스 체크, SQL 불필요
get_server_version() ✅ 1.0.0 버전 문자열 반환 (예: "11.2.0.0378")
get_last_insert_id() ✅ 1.0.0 AUTO_INCREMENT INSERT 이후
스키마 getter ✅ 1.0.0 원시 GetSchemaPacket; 기존 위치 인자 유지
소유권이 있는 스키마 행 ✅ 1.8.0 (#456) 동기/비동기 fetch_schema_info() / close_schema_info(), 키워드 전용 arg2=None; #457 실서버 행렬은 10.2/11.4에서 CLASS/VCLASS/ATTRIBUTE/CONSTRAINT/PRIMARY_KEY/IMPORTED_KEYS/EXPORTED_KEYS를 검증하며 네이티브 동등성이나 다른 스키마 코드는 인증하지 않음
듀얼스택 주소 폴백 (동기) ✅ 1.0.0 getaddrinfo IPv4/IPv6 순회
듀얼스택 주소 폴백 (비동기) ✅ 1.2.0 (#83) 비동기 대응
명시적 연결 복구 ✅ 1.2.0 (#70); 1.8.0 (#471, #485) ping(reconnect=True)는 연결 끊김, 음수 CHECK_CAS(CAS–DB 링크 장애) 또는 검사 중 전송/프로토콜 오류 후 재접속할 수 있음; CAS_INFO[0]=0은 OUT_TRAN이며 세션을 유지함. 다음 요청 전에 OUT_TRAN 세션을 CHECK_CAS로 확인하고, 검사가 실패할 때만(CAS 재시작, broker reset, CHANGE CLIENT) SQL 재실행 없이 한 번 재접속함(#485). 자동 이스케이프 모드는 새 물리 세션마다 감지하고 명시적 모드는 유지하며, 감지 실패 시 False 반환. 이전 세대에서 바인딩한 비동기 파라미터 SQL은 전송 전 거부. 동적 SET 또는 이기종 페일오버 보장은 아님.
알 수 없는 옵션 보고 ✅ 1.8.0 (#377) 인식하지 못한 연결 키워드는 무시되지만 UnknownConnectionOptionWarning을 냅니다(철자 제안 포함). warnings.simplefilter("error", ...)로 오류로 올릴 수 있습니다

TLS / SSL

기능 상태 도입 비고
동기 TLS — ssl=True (검증 컨텍스트) ✅ 1.3.0 (#85) 기본 보안 컨텍스트. TLS 1.2 최소 강제 (#145)
동기 TLS — ssl=ssl.SSLContext(...) ✅ 1.3.0 (#85) 커스텀 컨텍스트 (호출자가 최소 TLS 버전 제어)
동기 TLS — ssl=False / None ✅ 1.3.0 평문 (기본)
비동기 TLS ✅ 1.4.0 STARTTLS 방식 업그레이드: 평문 CUBRS 핸드셰이크 후 OPEN_DATABASE 전에 loop.start_tls() (ssl_handshake_timeout 제한) (#136, #154). 기본 컨텍스트는 TLS 1.2 최소 강제 (#145).

비동기 (pycubrid.aio)

기능 상태 도입 비고
pycubrid.aio.connect() ✅ 1.1.0 유사한 비동기 서피스. AsyncConnection.ping()은 1.3.2에 추가 (네이티브 CHECK_CAS FC=32). create_lob()은 동기 전용 유지.
AsyncCursor execute / fetch / executemany / callproc ✅ 1.1.0 await를 사용하는 동기 유사 커서 API. 연결 오토커밋 변경은 속성 세터 대신 set_autocommit() 사용
AsyncConnection.commit() / rollback() / close() ✅ 1.1.0
비동기 컨텍스트 매니저 ✅ 1.1.0 연결과 커서 모두 async with
비동기 read_timeout ✅ 1.2.0 (#82)
비동기 듀얼스택 폴백 ✅ 1.2.0 (#83)
비동기 파라미터 바인딩 동등성 ✅ 1.2.0 (#76, #77) 동기와 _escape_string 공유
비동기 TLS ✅ 1.4.0 STARTTLS 방식 업그레이드: 평문 CUBRS 핸드셰이크 후 OPEN_DATABASE 전에 loop.start_tls() (ssl_handshake_timeout 제한) (#136, #154). 기본 컨텍스트는 TLS 1.2 최소 강제 (#145).

드라이버 수준 진단

기능 상태 도입 비고
선택적 타이밍 훅 (enable_timing=True) ✅ 1.0.0 (#54) 기본 꺼짐. 비활성 시 오버헤드 0 — PERFORMANCE.md 참고
PYCUBRID_ENABLE_TIMING 환경 변수 ✅ 1.0.0 참값: 1, true, yes (대소문자 무시)
Connection.timing_stats ✅ 1.0.0 TimingStats 또는 None 반환
TimingStats (connect / execute / fetch / close) ✅ 1.0.0 나노초 정밀도, 스레드 안전
DEBUG 로깅 (pycubrid.connection, pycubrid.cursor, pycubrid.lob, pycubrid.aio.*) ✅ 1.x 드라이버가 연결·커서·LOB·비동기 연산의 옵트인 디버그 로그 출력

데이터 타입

CUBRID 타입 Python 타입 상태 비고
INTEGER, BIGINT, SMALLINT, SHORT int ✅
FLOAT, DOUBLE, MONETARY float ✅
NUMERIC, DECIMAL decimal.Decimal ✅
CHAR, VARCHAR, NCHAR, NVARCHAR, STRING str ✅
DATE datetime.date ✅
TIME datetime.time ✅
DATETIME, TIMESTAMP datetime.datetime ✅ naive (tzinfo 없음)
DATETIMETZ, TIMESTAMPTZ datetime.datetime (tz 포함) ✅ 1.2.0부터 (#78) — IANA 타임존 키
BIT, VARBIT bytes ✅
BLOB dict ✅ lob_type, lob_length, file_locator, packed_lob_handle을 가진 LOB 핸들 dict[^lob]
CLOB dict ✅ lob_type, lob_length, file_locator, packed_lob_handle을 가진 LOB 핸들 dict[^lob]
JSON Any (디시리얼라이저 경유) ✅ 1.2.0부터 (#72) — connect()의 옵트인 json_deserializer=; CAS 프로토콜 v8
SET frozenset ✅ 1.2.0부터 (#73) — connect()의 옵트인 decode_collections=True
MULTISET list ✅ 1.2.0부터 (#73) — 옵트인 decode_collections=True
SEQUENCE list ✅ 1.2.0부터 (#73) — 옵트인 decode_collections=True
컬렉션 (기본, decode_collections=False) bytes ⚠️ 하위 호환을 위한 raw CAS 와이어 형식
OBJECT (OID) str ⚠️ OID:@page\|slot\|volume로 디코딩. 고수준 OID API 없음
NULL None ✅

문장 / 커서

기능 상태 도입 비고
cursor.execute(sql, params) ✅ 1.0.0 드라이버 측 리터럴 바인딩. 렌더링된 SQL을 PREPARE_AND_EXECUTE로 전송(서버 측 타입 바인딩 없음) — PARAMETER_BINDING.md 참고
cursor.executemany(sql, seq) ✅ 1.0.0 비-SELECT DML을 BatchExecutePacket으로 배치. SELECT만 행별 루프로 폴백
cursor.executemany_batch(sql_list, auto_commit=None) ✅ 1.0.0 단일 왕복 BatchExecutePacket
cursor.callproc(name, params) ✅ 1.0.0 저장 프로시저 호출
cursor.fetchone() / fetchmany() / fetchall() ✅ 1.0.0
이터레이터 프로토콜 (for row in cursor) ✅ 1.0.0
cursor.description ✅ 1.0.0 PEP 249 7-튜플
cursor.rowcount ✅ 1.0.0
cursor.lastrowid ✅ 1.0.0

LOB

기능 상태 도입 비고
Connection.create_lob(BLOB) ✅ 1.0.0
Connection.create_lob(CLOB) ✅ 1.0.0
LOB 읽기/쓰기 ✅ 1.0.0
BLOB/CLOB 컬럼에 bytes/str 직접 삽입 ✅ 1.0.0 Lob 파라미터 바인딩(미지원)보다 권장

성능 최적화

기능 상태 도입 비고
socket.recv_into 제로카피 수신 ✅ 1.0.0
TCP_NODELAY 활성 ✅ 1.0.0
커서 클래스 캐시 ✅ 1.0.0
사전 컴파일된 struct 패커 ✅ 1.0.0
fetch용 타입 디스패치 테이블 ✅ 1.0.0
슬라이스 기반 fetch ✅ 1.0.0
배치 executemany (BatchExecutePacket) ✅ 1.0.0

운영

관심사 상태 비고
순수 Python (C 확장 없음) ✅ pip install pycubrid만으로 끝
PEP 561 타입 패키지 ✅ py.typed 포함
커넥션 풀링 ❌ 내장 없음. SQLAlchemy QueuePool이나 외부 풀 사용 — 연결 가이드 참고
외부 프로파일링 라이브러리 의존성 ❌ 표준 라이브러리의 time.perf_counter_ns만 사용

테스트 커버리지

지표 값
오프라인 회귀 검사 make test; 현재 사례는 테스트 트리 참조
대표 통합 검사 고위험 PR: Python 3.14/CUBRID 11.4; main/변경이 있는 주간 실행: Python 3.11/CUBRID 10.2 추가
전체 통합 (릴리스 workflow_call + 수동 dispatch) 20 (Python 5버전 × CUBRID 4버전)
스트레스 테스트 스레드 (워커 16 × insert 25, 리더 32) 및 asyncio.gather (워커 16, 리더 32)
재연결 / 네트워크 엣지 케이스 현재 테스트 트리의 리셋·타임아웃·broken pipe·부분 읽기 회귀 검사
전체 실행 커버리지 강제되는 95% 하한; 측정 결과이며 일반 PR 스모크의 주장은 아님

[^lob]: LOB 컬럼 fetch는 내용이 아니라 핸들 딕셔너리를 반환합니다. 바이트를 읽으려면 packed_lob_handle과 함께 pycubrid.lob.Lob을 사용하거나, CLOB/BLOB 값을 쓸 때 str/bytes를 직접 삽입하세요.

참고: 연결 가이드 · 타입 시스템 · API 참조 · 성능 가이드 · 변경 이력