Skip to content

지원 매트릭스 (한국어)

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

sqlalchemy-cubrid 릴리스의 호환성과 기능 지원.


버전 호환성

SQLAlchemy

SQLAlchemy 버전 상태 비고
2.0.x ✅ 지원 최소 요구 버전
2.1.x ✅ 지원 GA 정식 릴리스이며 CI에서 2.1.1 테스트됨
≥ 2.2 ❌ 미지원 2.2가 출시되어 전체 매트릭스를 통과할 때까지 <2.2 상한으로 제외됨; 코드가 SA 비공개 내부를 사용 (아래 참고)
< 2.0 ❌ 미지원 SA 1.x API 제거됨

왜 <2.2인가? 방언이 예고 없이 변경될 수 있는 비공개 SQLAlchemy API에 접근합니다:

비공개 API 위치 용도
select._limit_clause compiler.py:104 LIMIT 절 컴파일
select._offset_clause compiler.py:105 OFFSET 절 컴파일
select._for_update_arg compiler.py:93 FOR UPDATE 절

리터럴 감지와 typed-bind 재생성은 이제 로컬 _compat.py 헬퍼를 거치므로, 직접적인 비공개 API 서피스는 이 세 속성으로 줄었습니다.

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의 새 환경 설치를 확인합니다. 지원 범위:

  • pycubrid (cubrid+pycubrid://, cubrid+aiopycubrid://): pycubrid는 1.10.0 다음 릴리스부터 Python 3.15를 선언합니다 (cubrid-lab/pycubrid#831). [pycubrid] 하한은 pycubrid>=1.8.0,<2.0 그대로입니다. pycubrid 1.10.0은 3.15에서 동기·비동기 통합 스위트를 통과하지만 3.15를 선언하지 않습니다. Python 3.15는 ksc5601 코덱 별칭을 CP949로 매핑하므로, 3.15에서 pycubrid 1.10.0 이하는 ?charset=ksc5601을 거부합니다. ?charset=euckr을 쓰거나, ksc5601을 직접 EUC-KR로 매핑하는 3.15 선언 pycubrid 릴리스를 쓰세요.
  • CUBRIDdb (cubrid://): 공식 cubrid-python v11.3.0.51 소스는 ubuntu-latest의 CI 빌드 절차로 3.15용으로 빌드되며, 전체 매트릭스는 3.15에서 해당 레인을 실행합니다. GCC 14에서는 Python 3.14와 3.15 모두에서 기본 빌드가 실패합니다(비교한 두 버전이며 3.11–3.13은 시험하지 않음). GCC 14가 -Wincompatible-pointer-types와 -Wint-conversion을 오류로 만들기 때문입니다. 이 프로젝트는 CI에 경고 완화 플래그를 추가하지 않습니다. 드라이버 호환성을 참고하세요.
  • 3.15에서 실행하지 않는 검사: SQLAlchemy 컴플라이언스 레인은 Python 3.14/CUBRID 11.4와 Python 3.11/CUBRID 10.2에 그대로 둡니다.
  • 가비지 컬렉션된 연결: SQLAlchemy 2.1에서는 참조 순환 안에서 잃어버린 풀 연결(트랜잭션이 아직 열린 연결 등)이 3.15에서 종료되지도 풀로 반환되지도 않습니다. 이는 모든 방언에 영향을 주는 SQLAlchemy 2.1/CPython 3.15 상호작용이며, 해당 통합 테스트는 그 환경에서 예상된 실패(xfail)로 처리됩니다. 연결 가이드를 참고하세요.
  • 환경: pycubrid를 통한 Decimal 하위 클래스 파라미터에는 C _decimal 모듈이 필요합니다. libmpdec 없이 빌드한 Python에는 이 모듈이 없으며, 이때 pycubrid는 이런 파라미터에 ProgrammingError를 발생시킵니다. 일반 Decimal 값은 영향이 없습니다. CI가 쓰는 actions/setup-python 3.15.0 빌드, python:3.15-rc Docker 이미지, libmpdec-dev 없는 소스 빌드에서 이 모듈이 없음을 확인했으므로, CI의 3.15 셀에서는 Decimal 하위 클래스 왕복 테스트를 건너뜁니다. 3.15를 지원 목록에 올린 시점에 Docker Hub에는 정식 python:3.15 이미지가 없었으며, CI는 actions/setup-python을 사용합니다.

CUBRID 서버

CUBRID 버전 상태 비고
11.4 ✅ 지원 최신 안정 버전
11.2 ✅ 지원
11.0 ✅ 지원
10.2 ✅ 지원 최소 테스트 버전
< 10.2 ❌ 미지원

드라이버

드라이버 설치 URL 스킴 상태
CUBRIDdb (CCI) cubrid-python v11.3.0.51+에서 빌드 (방법). [cubrid] / [cubriddb] extra는 폐기 예정이며 테스트되지 않은 PyPI 9.3.x를 설치 cubrid:// / cubrid+cubriddb:// ✅ 지원되는 C 확장 드라이버 (v11.3.0.51+만)
pycubrid (순수 Python) pip install "sqlalchemy-cubrid[pycubrid]" cubrid+pycubrid:// ✅ 지원
pycubrid 비동기 pip install "sqlalchemy-cubrid[pycubrid]" cubrid+aiopycubrid:// ✅ 지원

기능 지원

SQLAlchemy Core

기능 상태 비고
create_engine() ✅ cubrid://, cubrid+cubrid://, cubrid+cubriddb://, cubrid+pycubrid:// 스킴
비동기 엔진 ✅ create_async_engine("cubrid+aiopycubrid://...")
SQL 컴파일 ✅ SELECT, INSERT, UPDATE, DELETE, JOIN, 서브쿼리
DDL 컴파일 ✅ CREATE TABLE, ALTER, DROP, AUTO_INCREMENT, COMMENT
타입 시스템 ✅ 출하된 모든 CUBRID 타입 컴파일. 리플렉션 커버리지는 아래에 별도 표기
스키마 리플렉션 ✅ 테이블, 컬럼, PK, FK, 인덱스, 유니크 제약, 코멘트
트랜잭션 관리 ✅ 커밋, 롤백, 세이브포인트 (RELEASE SAVEPOINT 없음)
커넥션 풀링 ✅ pool_pre_ping, 연결 해제 감지를 갖춘 SA 풀
문장 캐싱 ✅ supports_statement_cache = True
executemany (text(), UPDATE, DELETE) ✅ 모든 드라이버에서 None은 NULL로 바인딩되고 rowcount는 모든 파라미터 세트의 합계입니다(supports_sane_multi_rowcount = True). cubrid://에서는 CUBRIDdb 버그를 피하기 위해 방언이 각 파라미터 세트를 별도의 execute()로 실행합니다. 이 때문에 행마다 문장 준비가 한 번 더 필요하며, 1000행 UPDATE 기준 드라이버 자체 executemany보다 약 2.9배 느립니다. pycubrid는 한 번만 준비하는 executemany를 그대로 사용합니다. insertmanyvalues를 사용하는 여러 행의 Core insert()는 영향이 없습니다. 타입이 bind_expression()을 정의하는 컬럼이 있는 테이블에 대한 INSERT는 executemany로 대체되므로(#421) cubrid://에서 행 단위 가드를 사용합니다. 드라이버 호환성, 알려진 문제 7 참고

SQLAlchemy ORM

기능 상태 비고
선언형 모델 ✅
릴레이션십 ✅
Session / Unit of Work ✅
일괄 UPDATE / DELETE 행 수 검사 ✅ 모든 드라이버에서 일괄 flush가 일치한 행 수를 검사합니다. cubrid://에서는 행 단위 executemany 가드에 의존합니다. 드라이버 호환성, 알려진 문제 7 참고
Query API ✅
하이브리드 속성 ✅

Alembic

기능 상태 비고
자동 등록 ✅ 방언 로드 시 등록 (env.py 임포트 불필요)
스키마 마이그레이션 ✅ CREATE, ALTER, DROP
Autogenerate ✅ 컬렉션 타입(SET, MULTISET, SEQUENCE) 포함. CUBRID 전용 멤버 타입은 cubrid_types.MONETARY(), cubrid_types.BIT(...) 등으로 렌더링됩니다
트랜잭션 DDL ✅ DDL은 트랜잭션과 함께 롤백됨. 기본적으로 업그레이드 전체가 원자적 (리비전별 커밋은 transaction_per_migration=True)

DML 확장

기능 상태 비고
ON DUPLICATE KEY UPDATE ✅ sqlalchemy_cubrid.insert()를 통해
MERGE 문 ✅ sqlalchemy_cubrid.merge()를 통해
REPLACE INTO ✅ sqlalchemy_cubrid.replace()를 통해
GROUP_CONCAT ✅
TRUNCATE TABLE ✅ 둘러싼 트랜잭션과 함께 커밋
FOR UPDATE ✅ OF 절 포함
재귀 CTE ✅ WITH RECURSIVE (CUBRID 10.2, 11.0, 11.2, 11.4에서 확인)
윈도우 함수 ✅ ROW_NUMBER, RANK, LAG, LEAD 등
인덱스 힌트 ✅ SA with_hint() / suffix_with()를 통해

알려진 한계

기능 상태 비고
JSON 타입 ✅ v1.2.0부터, CUBRID ≥ 10.2 필요
네이티브 Enum ✅ 네이티브 ENUM('a', 'b', ...) — 10.2–11.4에서 검증 (#343)
Interval 타입 ❌ CUBRID 미지원
RETURNING 절 ❌ INSERT/UPDATE/DELETE ... RETURNING 미지원
BOOLEAN ⚠️ SMALLINT(0/1)로 매핑 — 네이티브 불리언 없음. CUBRID의 IS는 NULL/TRUE/FALSE만 받으므로 col.is_(True) / is_not(False) 등은 null-safe <=>로 에뮬레이트합니다(col <=> 1, (col <=> 1) = 0) (#465). IS [NOT] NULL, col == True, not_(col)은 그대로 컴파일됩니다. SELECT 목록 항목, CAST/함수 인자, 윈도우 ORDER BY/PARTITION BY로 쓰인 논리식은 방언이 그룹화하므로 select(and_(...)), select(exists()...), over(order_by=exists())가 동작합니다(#966, #968, #970). 논리식은 여전히 GROUP BY 항목으로는 거부됩니다. 지원되는 위치와 이 제한은 불리언 SELECT 목록 항목, IS/IS NOT은 불리언 조건식 참고
시퀀스 ❌ CUBRID는 AUTO_INCREMENT만 사용
CHECK 제약 리플렉션 ❌ get_check_constraints()가 빈 리스트 반환
멀티 스키마 ❌ CUBRID는 단일 스키마 모델
RELEASE SAVEPOINT ❌ no-op (CUBRID 미지원)
Lateral 조인 ❌ CUBRID에 LATERAL 서브쿼리 지원 없음
전문 검색 ❌ MATCH … AGAINST 구문 없음
비동기 DBAPI ✅ pycubrid.aio 비동기 드라이버 경유(cubrid+aiopycubrid://), pycubrid >= 1.8.0,<2.0 필요

타입 매핑

선언/컴파일 타입과 리플렉트 타입은 동일하지 않습니다. REAL, MONETARY, OBJECT는 올바르게 컴파일되고 모델에 선언할 수 있지만 dialect.ischema_names에 없어서 리플렉션이 자동으로 매핑하지 않습니다. 리플렉션 열은 스키마 메타데이터 매핑을, Python 타입 열은 의도한 값 형태를 설명합니다. 어느 열도 모든 드라이버가 그 값을 변형 없이 바인딩하고 반환한다는 보장은 아닙니다. BLOB/CLOB 로케이터, 빈 BIT 값, CUBRIDdb DECIMAL 절삭, 컬렉션 바인딩 차이는 타입 매핑과 드라이버 호환성을 참고하세요.

CUBRID 타입 SQLAlchemy 타입 Python 타입 리플렉션
INTEGER sa.Integer int ✅
BIGINT sa.BigInteger int ✅
SMALLINT sa.SmallInteger int ✅
FLOAT sa.Float float ✅
REAL REAL float ❌ 선언/컴파일 전용
DOUBLE sa.Float float ✅
NUMERIC / DECIMAL sa.Numeric decimal.Decimal ✅
MONETARY MONETARY float ❌ 선언/컴파일 전용
CHAR sa.CHAR str ✅
VARCHAR sa.String str ✅
NCHAR NCHAR str ✅
NVARCHAR NVARCHAR str ✅
STRING STRING str ✅
DATE sa.Date datetime.date ✅
TIME sa.Time datetime.time ✅
DATETIME sa.DateTime datetime.datetime ✅
TIMESTAMP sa.TIMESTAMP datetime.datetime ✅
TIMESTAMPTZ TIMESTAMPTZ (timezone=True) datetime.datetime ✅ TIMESTAMP로 합쳐지지 않는 별도 리플렉트 타입
TIMESTAMPLTZ TIMESTAMPLTZ (timezone=True) datetime.datetime ✅ TIMESTAMP로 합쳐지지 않는 별도 리플렉트 타입
DATETIMETZ DATETIMETZ (timezone=True) datetime.datetime ✅ DATETIME으로 합쳐지지 않는 별도 리플렉트 타입
DATETIMELTZ DATETIMELTZ (timezone=True) datetime.datetime ✅ DATETIME으로 합쳐지지 않는 별도 리플렉트 타입
BIT(n) / BIT VARYING(n) BIT(n) / BIT(n, varying=True) bytes ✅ 길이와 VARYING 유지
BIT(n*8) sa.BINARY(n) (BINARY() → BIT(8)) bytes ✅ BIT(n*8)로 리플렉트
BIT VARYING(n*8) sa.VARBINARY(n) (VARBINARY() → BIT VARYING) bytes ✅ BIT VARYING(n*8)로 리플렉트
CHAR(32) sa.Uuid / sa.UUID uuid.UUID, as_uuid=False이면 str ✅ CHAR(32)로 리플렉트
BLOB sa.LargeBinary bytes (문서상). NULL이 아닌 값 조회는 현재 드라이버 LOB 로케이터를 반환합니다. 드라이버 호환성, 알려진 문제 6 참고 ✅
CLOB CLOB str (문서상). NULL이 아닌 값 조회는 현재 드라이버 LOB 로케이터를 반환합니다. 드라이버 호환성, 알려진 문제 6 참고 ✅
SET SET 컬렉션 ✅
MULTISET MULTISET 컬렉션 ✅
SEQUENCE SEQUENCE 컬렉션 ✅
OBJECT OBJECT OID 참조 ❌ 선언/컴파일 전용

CUBRID에는 BINARY, VARBINARY, UUID 타입이 없습니다. sa.BINARY(n) / sa.VARBINARY(n)은 비트 단위 길이의 비트 문자열(BIT(n*8) / BIT VARYING(n*8))로 컴파일되며, BINARY는 더 짧은 값을 \x00으로 채웁니다. 빈 b""는 보존되지 않고 None(pycubrid의 BINARY(n)은 0 바이트)으로 조회됩니다. sa.Uuid와 sa.UUID는 CHAR(32)에 32자 16진 문자열로 저장됩니다. 리플렉트된 BIT 컬럼은 길이를 유지하므로 Alembic autogenerate가 이 컬럼들에 대해 잘못된 타입 변경을 보고하지 않습니다. 표준 SQL 타입 참고.


CI 매트릭스

검증 일상 실행 전체 호환성
오프라인 95% 커버리지의 전체 스위트: PR은 Ubuntu/Python 3.12, main·변경이 있는 주간 실행·수동 실행은 Python 3.11과 3.15 로컬 전체 테스트는 그대로 사용 가능
라이브 통합 고위험 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 검사는 지원되는 모든 조합에 대한 증거가 아닙니다.

SQLAlchemy 컴플라이언스 레인

공식 SQLAlchemy 컴플라이언스 스위트는 두 드라이버 모두에서 병합을 차단합니다. 각 레인은 test/known_failures.txt에 검토된 자체 알려진 실패 기준선을 가집니다. 개발 가이드를 참고하세요.

레인 드라이버 SQLAlchemy CUBRID (PR CI) 알려진 실패 (11.4 / 10.2)
cubrid@sa2.0 CUBRIDdb (cubrid-python v11.3.0.51) 2.0.53 11.4 68 / 61
pycubrid@sa2.0 pycubrid 1.8.0 (선언된 하한) 2.0.53 10.2 54 (10.2에서만 게이트)
pycubrid@sa2.1 pycubrid 1.8.0 (선언된 하한) 2.1.1 11.4 58 / 51

권장 드라이버는 pycubrid입니다. 컴플라이언스 레인은 사용자가 설치할 수 있는 가장 오래된 릴리스를 검증하도록 pycubrid>=1.8.0,<2.0의 선언된 하한을 일부러 고정합니다. 통합 레인은 최신 pycubrid 릴리스를 설치하며, 1.8.0 이후 바뀐 동작은 별도의 회귀 테스트가 있고 1.8.0에서는 strict xfail입니다(#479).

알려진 실패 대부분은 두 드라이버에 공통입니다. CUBRID 백엔드 규칙(식별자 소문자 변환, [ ] 식별자 구분자, 윈도 프레임 절 미지원, 행 단위 외래 키 검사, 단정밀도 FLOAT)과 test/known_failures.txt에 기록된 미해결 방언 리플렉션/DDL 버그입니다. NUMERIC 절단과 정수 나눗셈 항목은 CUBRIDdb 레인에만 있습니다.

테스트 커버리지

오프라인 및 통합 테스트 개수는 PR마다 늘어나므로, 여기에 고정된 스냅샷 대신 offline-tests / integration-tests CI 작업 출력에서 현재 수치를 확인하세요.

지표 값
커버리지 하한 95% (CI 강제, offline-tests 작업의 --cov-fail-under=95)

참고: 연결 가이드 · 타입 시스템 · 기능 지원 · 드라이버 호환성 · 변경 이력