Skip to content

공식 드라이버 API 목록과 호환성 설계 (한국어)

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

Deprecated. pycubrid.compat은 FutureWarning을 내며 이후의 1.x MINOR 릴리스에서 제거됩니다. 마이그레이션 가이드를 참고하세요. 아래 내용은 과거 기록이며 고쳐 쓰지 않습니다.

기계 판독 가능한 카탈로그는 공식 CUBRID/cubrid-python 소스 스냅샷에 드라이버가 선언한 공개 연산을 기록합니다. 소스 참조, 시그니처, 기본값, 반환값·오류 관찰 결과, 고정된 pycubrid 기준선 7e0aad8의 대응 항목과 명시적 공백을 담습니다. 이는 #396 범위에서 #436이 제공하는 목록 산출물입니다.

이 문서는 소스 항목을 목록화한 것입니다. 기능 패리티, 공식 드라이버의 성공적인 실행, 완전한 API 상위 집합을 인증하지 않습니다. 비슷한 이름이나 기존 패킷 클래스만으로는 충분한 증거가 되지 않습니다. #438의 선택된 additive 설계를 아래에 기록합니다. 명시적 네임스페이스는 아래의 제한된 구현 범위 (#465/#439/#440/#441/#442/#467)를 제공합니다. 이 카탈로그는 남은 API나 완전한 네이티브 동등성을 인증하지 않으며 기존 1.x 기본값도 바꾸지 않습니다. 카탈로그의 대상 매핑은 과거 기준선으로 유지하며 #465의 생성 기능은 이 가이드에 따로 기록합니다.

카탈로그 읽기

각 연산에는 안정적인 id가 있습니다. CUBRIDdb는 래퍼 연산을, documented_cubrid는 네이티브 _cubrid 모듈의 문서화되거나 내보낸 공개 표면을 식별합니다. 두 네임스페이스는 구별됩니다. 래퍼의 execute(query, args)는 네이티브 prepared 커서의 execute(option, max_col_size)와 같지 않습니다.

sources는 고정된 upstream 리비전의 경로와 줄을 참조합니다. 모든 행에는 시그니처·기본값 맵, 관찰 가능한 반환값·오류 설명, 현재 pycubrid 대상 또는 명시적 부재가 있습니다. 대상은 소스 수준의 대응 항목이지 비교 테스트의 통과가 아닙니다. internal_analogue는 호환되는 공개 export 없이 대응 프로토콜 열거형만 내부에 존재한다는 뜻입니다. tracking은 기존 작업을 참조합니다. 호환성 결정이 논의 대상 기능을 그 자체로 구현하는 것은 아닙니다. 네이티브 오류 프로필은 코드·facility 변환과 두 요소로 이루어진 예외 인자 튜플을 별도로 기록합니다. 예외 이름이 같아도 오류 코드, 인자 또는 Python의 인자 검증 동작까지 같다고 인증할 수는 없습니다.

래퍼는 네이티브 공개 이름을 가져옵니다. 네이티브 행의 aliases와 최상위 재내보내기 규칙이 이를 기록하며, 별칭을 새로운 기능으로 세지 않습니다. 래퍼는 네이티브 모듈의 connection 이름을 자신의 팩터리로 대체합니다. 네이티브 연결 연산은 래퍼의 connection 속성을 통해 접근하며, 존재하지 않는 CUBRIDdb.connection.<method> 별칭을 통하는 것이 아닙니다. 공유 래퍼 커서 메서드는 cursors.BaseCursor에 기록되어 있으며, cursors.Cursor와 cursors.DictCursor가 모두 상속합니다. 래퍼가 재내보낸 표준 라이브러리 클래스는 Python 생성자 동작을 유지합니다. 카탈로그는 상속된 모든 내장 메서드를 새로운 드라이버 연산으로 나열하지 않습니다.

결정 또는 구현이 필요한 차이

공개 표면 기존 pycubrid 경로 또는 미완료 작업
생성자, autocommit, 스레딩 네이티브 초기화는 autocommit을 켭니다. 기존 pycubrid는 수동 commit과 threadsafety=1을 유지합니다. #465는 명시적 CUBRID/UTF-8 생성자와 별칭만 제공하며 공유·설정·CCI URL/HA 옵션은 별도 작업입니다.
문자셋, dict 커서, 변환기 #86부터 일반·비동기·호환 생성자가 선택한 코덱을 받아 EUC-KR 데이터베이스에서도 검증했습니다. 연결에 charset 속성은 유지하지 않습니다. #466은 기존 네이티브 스칼라 범위 위에 한정된 튜플/dict 커서와 연결별 변환기를 추가합니다. UTF-8 기본값은 바뀌지 않습니다.
Prepare/타입 지정 바인딩 일반 커서에는 공개 prepare/bind/execute가 없습니다. 명시적 compat.native 커서는 동기 스칼라 바인딩(#439)과 컬렉션 바인딩(#440), 조회한 LOB 핸들의 조회·바인딩(#441)을 제공합니다.
LOB 커서·파일 동작 일반 pycubrid는 명시적 오프셋의 바이트 읽기·쓰기를 유지합니다. 선택적인 네이티브 네임스페이스는 제한된 #442 위치·스트림 및 #443 원시 파일 계약을 제공하며, 보편적인 CCI 패리티 대신 안전상 차이를 명시합니다.
결과 탐색과 메타데이터 #444는 살아 있는 현재 SELECT의 네이티브 절대·상대 seek와 shadow tell을 추가합니다. 측정한 교차 페이지 비교는 prepare 전에 수동 autocommitFalse를 명시하며, 기본True는 마지막 배치 뒤 브로커 결과를 해제할 수 있습니다. #445는 캐시된 15필드 메타데이터를 제공합니다. 네이티브 next_result와 래퍼 nextset은 여전히 별도 기능입니다.
스키마 행 #412가 스키마 결과 소비와 핸들 정리를 추적합니다. 반환된 프로토콜 패킷은 네이티브 스키마 행 반환 계약과 같지 않습니다.
배치 파사드와 옵션 플래그 Cursor.executemany_batch(sql_list, auto_commit=None)는 이미 임의 SQL을 배치 처리합니다. 연결 수준 파사드와 네이티브의 문장별 오류 레코드는 이 메서드의 튜플 결과·첫 오류 발생 방식과 다릅니다. 실행 플래그·쿼리 계획 옵션과 연결 멤버 설정자는 #438 아래의 집중된 후속 작업이 필요합니다.

선택된 additive 계약 (#438)

2026-09-28 메인테이너가 선택한 설계는 기존 pycubrid를 유지하고 별도의 pycubrid.compat.cubriddb(래퍼)와 pycubrid.compat.native(네이티브)를 추가합니다. #465의 생성·종료, 네이티브 prepared 스칼라(#439), 컬렉션 바인딩(#440), LOB 핸들·스트림(#441/#442), 캐시와 실제 설정자(#467)를 제공하지만 아래의 다른 행은 아직 목표 계약입니다. 되돌릴 수 있는 이 additive 설계는 1.x 기본값 대체, 2.0 대체 도입, 릴리스 발행을 허가하지 않습니다. 전역 스위치, CUBRIDdb/_cubrid 이름 가리기, 기존 Cursor.execute의 오버로딩은 선택하지 않습니다. 래퍼와 네이티브의 실행 형태는 의미를 바꾸지 않고 통합할 수 없기 때문입니다. 두 표면은 별도 드라이버가 아니라 기존 순수 Python 전송 계층을 재사용해야 합니다. 후속 호환성 기능은 동일한 네이티브 연결 소유자를 확장해야 하며, 내부의 기존 드라이버 핸들을 새 공개 전송 API로 노출해서는 안 됩니다.

기존 connect/aio, user=dba, autocommit=False, threadsafety=1, execute의 self 반환, 캐시된 문자열/None identity, None 크기 필드, Boolean null_ok, 정규화된 컬렉션 코드 16/17/18, raw/옵트인 타입 값, 명시적 오프셋의 bytes LOB API는 유지합니다. 기존 SQLAlchemy import와 현재 pycubrid>=1.3.2,<2.0 요구 범위도 유지합니다. 새 의존성이나 import 시점의 네이티브 드라이버는 필요하지 않습니다.

아래는 선택된 목표 계약입니다. /는 위치 전용 인자를 나타내며, 선택적 인자 생략과 명시적인 None은 서로 바꿔 쓸 수 없습니다.

표면 선택된 계약 / 구현 경계
연결 유틸리티 (#666) 인자가 없는 네이티브 server_version은 매번 FC15로 전체 문자열을 조회하고 client_version은 종료 후에도 I/O 없이 자체의 고정된 패키지 ID를 반환하며 ping은 일반 CHECK_CAS bool이 아닌 쿼리 기반 int1/0입니다. 래퍼는 server_version/ping에 한 번 위임합니다. 정확한 소유자·실제 모드 검사는 재시도·재접속을 금지하며 autocommit 중 활성 private 스키마 결과를 거부합니다. Ping은 페이지를 검증하고 소유 중이며 폐기되지 않은 핸들만 정리합니다. 정리만 실패하면 오류를 전달하고 앞선 오류는 유지합니다. 정상 기본·수동 모드 비교와 서로 다른 원시 client ID 관측은 별개이며 전체 C 버퍼·정규식·오류·복구·스레드 공유 패리티가 아닙니다.
래퍼 트랜잭션 (#662) 인자가 없는 Connection.commit()/rollback()은 각각 정확한 네이티브 소유자에 한 번 위임하고 None을 반환하며 새 예외 래핑·재시도·복구 로직을 추가하지 않습니다. 수동 모드는 명시적으로 설정합니다. 기존 성공 콜백을 따르며, rollback이 fetch를 무효화한 뒤에도 래퍼 rowcount/description 캐시는 남을 수 있습니다. 필수 스칼라 사례 하나는 새 DML 커서, 독립 관측자의 가시성과 rollback 전후의 새 실행 조회를 사용합니다. 원격 결과 수명, 기본 모드, 결함 또는 스레드 공유 패리티는 아닙니다.
팩터리 (#465) 래퍼 Connect/connect/connection(*args, **kwargs)는 Connection(dsn='', user='public', password='', charset='utf8')에 위임합니다. 위치 인자 최대 세 개가 dsn/user/password 키워드를 덮어씁니다. 네이티브 connect(url, user='public', passwd='')와 소문자 connection 생성은 autocommit=True로 시작합니다. 래퍼 .connection은 정확히 그 호환성 네이티브 객체이며 일반 객체가 아닙니다. 생성·종료, #467 autocommit 접근, #86 문자셋과 #466 한정된 행 커서를 제공하며 HA·미지원 DSN 옵션은 거절합니다.
공유 / 전역 값 (향후) 래퍼 apilevel='2.0', paramstyle='qmark', threadsafety=2에는 래퍼 커서 지원과 명시적 객체의 연결별 요청·수명주기 직렬화 및 두 스레드 테스트가 먼저 필요합니다. 현재의 제한된 호환 모듈은 이 전역 값을 내보내지 않습니다. 락 없는 기존 객체와 전역 threadsafety=1은 유지합니다. 네이티브 부분집합의 private RLock은 일반적인 스레드 공유 보장이 아닙니다.
설정 (#467) 네이티브의 autocommit, isolation_level, lock_timeout, max_string_len은 직접 대입이 객체를 그대로 받고 브로커 요청을 보내지 않는 쓰기 가능한 캐시이며, 실제 설정은 별도 set_autocommit(mode, /) / set_isolation_level(level, /)로 바꿉니다. bool autocommit 설정자는 로컬 CCI 대응 모드를 바꾸며, 실제 모드가 바뀌고 활성 트랜잭션이 있을 때만 커밋합니다. 격리 수준 4/5/6의 SET은 커밋하지 않으며 같은 물리 세션의 실제 수준이 같으면 생략합니다. 래퍼 .autocommit의 세터는 bool을 검증·위임하고 getter는 원시 캐시를 읽습니다. 생성자는 lock/max/isolation의 실제 값을 읽고 max-string 서버의 완전한 오류에만 0을 적용하며, 숫자 수준 4의 초기 문자열은 공식 확장의 CUBRID_TRAN_UNKNOWN_ISOLATION 표기를 유지합니다. lock/max 실제 세터·일반 기본값 변경·위험한 네이티브 파서 패리티는 없습니다.
래퍼 커서 (#466) pycubrid.compat.cursors.Cursor/DictCursor와 Connection.cursor(dictCursor=None)를 제공합니다. execute(query, args=None, set_type=None) -> int는 기존 네이티브 int/문자열/NULL 스칼라(#840부터 부호 있는 32비트를 넘는 int는 BIGINT 또는 NUMERIC)와 #610부터 컬렉션 인자(아래 행)를 위임하며 매핑은 I/O 전에 거부합니다. SELECT의 7필드 설명, 정확한 이름의 튜플/dict 행(중복 키는 마지막 값), 현재 연결별 변환기를 제공합니다. 거짓 변환 결과는 행을 소비한 뒤 bulk fetch를 멈추고 반복자는 None에서만 멈춥니다. LOB 인자, mapping/default_cursor docstring은 미제공입니다. 비SELECT 설명은 공식 확장의 누락 가능성 대신 안정적인 None을 사용합니다.
네이티브 prepared 커서 (#439) 동기 전용으로 제공됩니다. prepare(sql) -> None; 1부터 시작하는 인덱스의 bind_param(index, value, bind_type=0, /) -> None; execute(option=0, max_col_size=0, /) -> int; fetch_row(how=0, /)는 튜플 또는 None을 반환합니다. #439에서는 INT32, UTF-8 문자열, SQL NULL과 기본 플래그만 동작했고, #840이 타입 지정 bool, BIGINT, DOUBLE, NUMERIC, DATE, TIME, DATETIME, DATETIMETZ, VARBIT 스칼라를 추가했습니다(매핑). 그 바이트는 공식 확장과 비교하지 않았습니다. dict 행·변환기는 #466입니다. 물리 세션 소유권, pooling 활성 게이트, commit/rollback 결과 동작과 보호된 FC2/FC3/FC6/FETCH가 이 부분집합에 포함됩니다. 일반 FC41이나 공개 async 변경은 없습니다.
네이티브 컬렉션 바인딩 (#440) 동기 전용으로 제공됩니다. connection.set() -> set, set(connection, /), set.imports(data, type, /, *, kind=SET) -> None, cursor.bind_set(index, set, /) -> None. data는 tuple이어야 하고(아니면 InterfaceError) type은 BIT/VARBIT(NotSupportedError)을 제외한 모든 int 타입 코드이며, 공식 드라이버처럼 import의 표시일 뿐입니다. 공식 드라이버처럼 모든 원소는 type과 관계없이 STRING(2)으로 보내며, 기본 종류 SET은 공식 바이트를 보냅니다. kind=MULTISET은 중복을 유지하고(브로커가 MULTISET을 -454로 거부하므로 SEQUENCE로 보내며, 컬럼은 순서를 유지하지 않음) kind=SEQUENCE는 중복과 순서를 유지합니다. None이 NULL 원소이며, 'NULL', '', Python int 원소(INT 전용), NUL 거부, #439 오류 클래스는 의도적으로 분류된 차이입니다. import하지 않은 set은 SQL NULL을 바인딩합니다. 래퍼 컬렉션 호출 형태는 이 위에서 동작합니다(#610, 다음 행).
래퍼 컬렉션 인자 (#610) 한정된 래퍼 커서에서 동기 전용으로 제공됩니다. execute(query, args, set_type)는 list/tuple/set/frozenset 인자를 네이티브 set()/imports()/bind_set()으로 바인딩하며, 모든 컬렉션에 쓰는 하나의 set_type 코드, 위치별 list/tuple(없거나 None인 항목은 추론), 또는 업스트림 추론(int/bool INT, float FLOAT, Decimal NUMERIC, date/datetime DATE, time TIME, str STRING, 비면 STRING, 섞이면 TypeError)을 사용합니다. 원소는 str() 텍스트로 SET 종류에 담겨 전송되며, 지원되는 None이 아닌 원소라면 공식과 같은 바이트입니다. executemany(query, args_list) -> None은 한 번 준비하기 전에 모든 그룹을 검사하고, 준비 후 어떤 실행보다 먼저 모든 그룹의 값 개수를 검사하며, 마지막 rowcount를 유지합니다. 빈 목록이면 -1/None입니다(비교하지 않은 pycubrid 자체 동작). 준비 전 오류는 이전 결과와 스냅샷을 버립니다. None 원소는 NULL 원소(공식은 텍스트 'None' 또는 UnboundLocalError), BIT/VARBIT 원소는 NotSupportedError, 다른 이터러블은 ProgrammingError, 서버 -494는 ProgrammingError이며 모두 분류된 차이입니다. 오프라인 테스트로만 확인: 뒤쪽 그룹 오류 시 어떤 그룹도 실행하지 않음(공식은 앞선 그룹 실행), 명시적 set_type이 있어도 미지원 원소는 오류(공식은 str() 전송). 스칼라 범위는 그대로입니다.
Description (name, native_type, 0, 0, precision, scale, null_ok)에서 null_ok는 정수 0/1이고 precision은 쿼리별 값이며 네이티브 플래그 타입을 유지합니다. 값과 Python 타입을 모두 보존하고 컬렉션 16→32를 무조건 변환하지 않습니다.
확장 메타데이터 (#445) 네이티브 전용 cursor.result_info([n])는 성공한 실행의 캐시된 메타데이터를 읽어 15필드 튜플들의 튜플(1부터 시작하는 번호는 바깥 항목 하나)을 반환하며, 실행한 컬럼 없는 문장은 None을 반환합니다. 실제 CCI 타입·정수 플래그·전달된 문자열을 공식 구현 순서로 사용합니다. 지원 CUBRID 생산자는 없는 텍스트를 None이 아닌 빈 문자열로 보내며 속성·기본값을 추측하지 않습니다. 일반·래퍼 description과 행 위치는 바뀌지 않습니다.
컬렉션 저장된 SET의 목표는 변경 가능한 set, MULTISET/SEQUENCE는 list입니다. 비NULL 요소는 검증된 타입별 텍스트 변환을 사용하고 중복·순서·빈 값을 보존합니다. 전체 SQL NULL과 NULL 요소는 아래 안전성 차이에 따라 None입니다. 중괄호 리터럴은 저장된 SET의 증거가 아니며 네이티브 타입 지정 import/bind는 위의 #440 행입니다. 일반 커서용 pycubrid.types.Set/Multiset/Sequence 파라미터(#567)에는 공식 대응물이 없습니다. 공식 wrapper execute(query, args, set_type)는 일반 list를 네이티브 prepared bind_set으로 바인딩하며 pycubrid는 이를 호환 래퍼에서만 제공하므로(#610), 이 일반 커서 파라미터에 대해서는 차등 비교 주장을 하지 않습니다.
Identity / 스키마 네이티브 insert_id() -> int \| None은 기존 INSERT 캐시의 형변환이 아니라 현재 브로커 identity를 조회합니다. 네이티브 schema_info(schema_type, class_name, attr_name omitted, /)는 CLASS/VCLASS(플래그 1)와 ATTRIBUTE/CLASS_ATTRIBUTE(플래그 2)에 대해서만 제공됩니다(#1011, 아래). 키워드나 명시적 None 없이 첫 행의 list 또는 None을 반환하고, DOMAIN은 CCI처럼 마스킹하며, 소유한 핸들은 항상 닫습니다. 범위 안의 나머지 형식은 NotSupportedError를 발생시키며 기존 스키마 소비 API는 계속 모든 행을 반환합니다.
네이티브 LOB 핸들 조회·바인딩 (#441) 동기 전용으로 제공됩니다. connection.lob() -> lob, lob(connection, /), lob.close() -> None(로컬 전용, 서버 해제 요청 없음, 그 전에 만든 바인딩은 유효), cursor.fetch_lob(col, lob, /) -> None, cursor.bind_lob(index, lob, /) -> None. fetch_lob은 fetch_row처럼 다음 행을 소비하고 BLOB/CLOB 타입을 col에서 정합니다(공식은 1번 컬럼을 읽지만 10.2/11.4에서 저장되는 복사본은 같음). int가 아닌 col은 공식 인자 파서처럼 먼저 TypeError를 내고, 결과 끝에서는 공식과 같이 컬럼 범위·타입이나 lob 상태를 검사하기 전에 None을 반환하며, 그 밖에는 LOB가 아니거나 범위를 벗어난 컬럼이 행을 소비하지 않고 ProgrammingError를 내며, NULL 셀은 공식과 같이 행을 소비하고 lob을 비웁니다. bind_lob은 공식과 같이 lob이 아니면 TypeError를 내고, 공식과 같이 커밋된 행에서 조회한 핸들을 다시, 다른 연결에서, 원래 연결이 닫히거나 재접속한 뒤에도 바인딩합니다(서버가 복사본 저장). 닫히거나 빈 lob은 요청 전에 InterfaceError를 내며(공식은 NULL 바인딩), 닫힌 lob이나 다른 연결의 lob으로 fetch_lob하면 InterfaceError입니다(공식은 채움). int가 아닌 인덱스는 공식과 같이 lob보다 먼저 TypeError를 내고, 범위를 벗어난 인덱스는 ProgrammingError입니다. 완전한 응답의 셀이 컬럼 LOB 타입의 핸들이 아니면 행을 소비하지 않고 DataError를 내며, 핸들 구조가 손상되었으면 OperationalError를 내고 세션을 폐기합니다. 각 lob은 출처(fetched/created)를 기록하므로 #442가 생성한 임시 핸들을 자기 세션에 묶을 수 있고, 실제 autocommit 모드에서 조회했는지도 기록합니다. 수동 모드에서 조회한 핸들은 나중에 커밋해도 다른 연결로 넘기지 않으며, autocommit 모드에서 다시 조회해야 합니다. 스트림 작업은 아래 #442에서 제공하며, 원시 파일 전송은 #443에서 별도로 설명합니다.
네이티브 LOB 스트림 (#442) 동기 전용으로 lob.write(data, type="B", /) -> None, read(length=0, /) -> str, seek(offset, whence=SEEK_CUR, /) -> int와 SEEK_* 상수를 제공합니다. 위치와 크기는 바이트 단위이고 BLOB/CLOB 모두 UTF-8 텍스트를 반환하며 SEEK_END는 크기-offset입니다. 첫 쓰기는 BLOB/CLOB을 만들고 이후 쓰기는 끝에만 추가합니다. 안전상 중간 쓰기·유효하지 않은 위치는 요청 전에 거부하고 빈 값·EOF·초과 읽기는 공식 CCI의 위험한 동작 대신 가능한 문자열을 반환합니다. 네이티브 객체의 close는 계속 종료 상태이고, 생성 핸들은 원래 세션에 묶이며 첫 autocommit 바인딩이 임시 파일을 소비합니다. 일반 절대 오프셋 Lob은 그대로이며 추가된 네이티브 파일 작업은 아래 #443에서 설명합니다.
예외 네임스페이스별 PEP 249 어댑터는 (numeric_code, formatted_message) args와 code/errno/SQLSTATE 증거를 유지하며 기존 예외 identity/args는 바꾸지 않습니다. 제공된 result_info의 로컬 오류는 아래에 분류한 pycubrid 메시지 하나의 args를 유지하며 더 넓은 어댑터는 별도 작업입니다. 불안정한 메시지의 완전 일치나 네이티브 인자 파서 충돌은 목표가 아닙니다.

#662 비교는 두 래퍼 모두 INSERT 전에 수동 모드를 명시합니다. 독립적인 autocommit 관측자로 commit 가시성을 검사하고, rollback은 새 실행의 자기 세션 조회로도 확인하므로 빈 메서드와 close의 암묵적 rollback만으로 통과할 수 없습니다. 고정된 래퍼는 두 메서드를 위임하고 암묵적으로 None을 반환합니다. 불변 코드 3da3ecb2fba1a62334d35385a059580b686b1241의 필수 비교는 Python 3.10.12와 소유한 10.2.18.9024/11.4.6.1963에서 수동 실행자와 autocommit 관측자를 사용했습니다. 메인테이너 로컬 official662-3da3ecb-combined.jsonl에 관측을 기록하며 증거 검증기가 일치를 재계산합니다. 이 부분집합은 캐시 초기화, 결함 복구나 모든 upstream 트랜잭션 단언을 인증하지 않습니다. 시나리오 차이를 명시하고 소스 원장·별칭은 그대로 유지합니다.

네이티브 유틸리티 검증 (#666)

불변 코드 ffab52c61abcb4118a1cbeed07f7ebce876404ca의 필수 비교는 Python 3.10.12와 소유한 10.2.18.9024/11.4.6.1963, 고정된 e75ec36/CCI7d1eb8f 오라클을 사용했습니다. 두 빌드에서 정상 네이티브·래퍼 사례의 전체 서버 문자열과 정확한 정수 ping1이 기본 True 및 명시적 수동 False 모드에서 일치했습니다. 메인테이너 로컬 official666-ffab52c-combined.jsonl은 전체 비교 목록과 서로 다른 원시 client ID를 기록하고 증거 검증기가 결과를 재계산합니다. 각 드라이버의 자체 ID는 종료 후에도 안정적인 문자열이며 자체 패키지·manifest와 일치합니다. 다른 드라이버의 ID나 네 부분 정규식과의 일치를 뜻하지 않습니다. 정상 사례가 소스 단언, 결함 복구, 트랜잭션 중립성이나 전체 기능 패리티를 인증하지 않으며 기존 주장과 원장 판정은 유지합니다.

네이티브 schema_info 계약 (#1011)

네이티브 소스 (PyArg_ParseTuple(args, "is|s", ...), METH_VARARGS)와 CCI confirm_schema_type_info로 고정한 뒤, scripts/build_official_oracle.py로 만든 e75ec36/CCI 7d1eb8f 오라클(로컬 Python 3.12.13)로 소유한 CUBRID 10.2.18.9024와 11.4.6.1963 컨테이너에서 실제 동작을 관측했습니다.

항목 공식(고정 소스와 관측) pycubrid compat.native
인자 위치 인자만 허용. class_name은 필수 str, attr_name은 선택 str. 키워드, None, bytes, NUL, 정수가 아니거나 32비트를 넘는 형식은 파서에서 실패 같은 예외 클래스(TypeError, ValueError, OverflowError), 메시지는 pycubrid 고유
스키마 형식 범위 1–20(CCI_SCH_FIRST..CCI_SCH_LAST) 밖은 InterfaceError -30015 InterfaceError, 메시지만 담은 args
지원 형식 1–20 전체를 브로커로 전송 CLASS 1, VCLASS 2(플래그 1)와 ATTRIBUTE 4, CLASS_ATTRIBUTE 5(플래그 2)만. 3과 6–20은 I/O 전에 NotSupportedError
attr_name 주었을 때만 모든 형식에서 FC9 arg2로 전송 같음
결과 첫 행을 list로. INT/SHORT 열은 int, 나머지 열은 str, NULL은 None. 행이 없으면 None 같은 값과 형식. STRING/SHORT/INT가 아닌 열은 NotSupportedError
ATTRIBUTE DOMAIN CCI가 첫 형식 바이트의 컬렉션(0x60)·프로토콜(0x80) 비트를 지워 SET(INT)는 8 같은 마스크
인코딩 불가 문자열 UnicodeEncodeError(UTF-8 변환) 연결 문자 집합 기준으로 I/O 전에 DataError
핸들 수명 행을 가져온 뒤에만 닫고 행 없음·오류 경로에서는 열어 둠 현재 세션에서 반환 전에 항상 닫음. 닫기 실패 시 세션 폐기
세션 OUT_TRAN이면 CCI가 재접속 후 FC9를 다시 보낼 수 있음 고정 물리 세션, 재시도·재접속·재실행 없음. 종료 후에는 InterfaceError

native-schema-info 주장은 모드(기본 autocommit True와 명시적 수동 False)마다 63개 호출과 키워드 호출 하나를 두 서버에서 오라클과 비교합니다. 정확한 테이블 이름과 패턴, 뷰, 없는 이름, 명시적 속성 이름과 패턴, 클래스 속성, 스칼라·NUMERIC· SET/SEQUENCE/MULTISET·BLOB·JSON·ENUM·CHAR 열의 DOMAIN, 인자 오류와 종료된 연결을 포함합니다. 두 서버 모두 일치했고 로컬 증거 검증기는 Python 버전(레인의 3.11이 아닌 3.12.13)만 거부했으므로 CI 레인이 다시 실행합니다. 실서버 테스트는 각 호출이 같은 CAS 핸들 ID를 재사용하고(누수 없음) 열린 prepared 결과를 계속 쓸 수 있음을 보입니다. 형식 3과 6–20(오라클은 응답함), 오류 메시지와 인자 튜플, 장애·복구, 스레드 공유, upstream test_schema_info 단언은 비교하거나 주장하지 않습니다.

네이티브 LOB 파일 범위와 안전상 차이 (#443)

명시적인 동기 네이티브 객체는 imports(file, type="B", /)와 export(file, /)을 제공합니다. 둘 다 None을 반환하며 BLOB/CLOB 원시 바이트를 제한된 청크로 전송하고 바이트 위치를 유지합니다. 세 긍정적 주장 그룹은 생성한 모든 바이트 값의 BLOB과 175000바이트 BLOB·UTF-8 CLOB으로 실제 import→prepared 바인딩→저장 컬럼→새 조회→export를 확인합니다. 값이 있는 객체의 성공적인 교체와 기존 출력 덮어쓰기도 다룹니다. 픽스처 생성·비교 자체가 드라이버의 스트리밍을 증명하는 것은 아닙니다. 현대 소스의 637번 줄 파일 바이트 동등성 단언만 평가하며 PNG 픽스처, 전체 함수와 이전 별칭은 인증하지 않습니다.

필수 고정 오라클 차등 검사는 불변 코드 33031d41d6ca49294f4fab3d93b1591efda144b3의 전체 주장 카탈로그를 Python 3.10.12로 소유한 CUBRID 10.2.18.9024와 11.4.6.1963에서 실행했습니다. 메인테이너 로컬 official443-33031d4-combined.jsonl은 양쪽 드라이버 관측을 기록하며 기존 증거 검증기가 일치를 재계산했습니다. 시나리오 원장은 현대 소스 637번 단언만 고정하며 전체 선언이나 파일 API를 인증하지 않습니다.

별도의 후보 전용 저장 컬럼 작업은 소유한 CUBRID 10.2.18.9024와 11.4.6.1963에서 빈 BLOB/CLOB 출력과 import·export 위치 보존을 확인했습니다. C 오라클을 불러오지 않았으며 일치 주장을 추가하지 않습니다. 공식 빈 export 동작은 소스 설명일 뿐 측정한 패리티가 아닙니다.

고정된 C import는 입력을 열기 전에 객체를 변경하지만, 이 구현은 먼저 파일을 열고 입력 닫기까지 완료한 뒤 고정 세션에서 만든 핸들을 채택합니다. 고정된 C export는 목적지에 직접 truncate/write하고 실패하면 지울 수 있지만, 이 구현은 배타적인 동일 디렉터리 임시 파일을 성공 시 교체합니다. 정확한 문자열 정책, 값이 있는 빈 출력, 로컬 오류 원인과 메시지만 있는 args는 의도적인 차이입니다. 유효하지 않은 C 생성, 로컬 I/O 결함이나 C의 빈 export는 실행한 차등 주장이 아닙니다. 파일시스템 샌드박스, 기존 inode·권한 보존, fsync나 DB·파일 트랜잭션을 보장하지 않으며 단계적으로 만든 서버 파일은 세션 수명 규칙을 따릅니다. 네이티브 파일 API를 참고하세요.

네이티브 위치 이동의 결과 수명과 증거 (#444)

data_seek, row_seek, row_tell은 안전한 공식 물리 위치·shadow의 차이, 재실행 shadow 유지와 실패한 상대 이동의 clamp를 유지합니다. 선택된 비교는 두 드라이버·래퍼 모두 prepare· execute 전에 set_autocommit(False)를 명시하며 기본 모드를 추측하지 않습니다. 원래 공식 전용 257행 기준도 수동 모드였고 초기 문서에 빠졌던 이 설정은 투명하게 정정했습니다. 별도 공식 1537행 defaultTrue 진단은 1205행 소비 후 3으로 돌아가며 CAS no-more-data -10012로 실패했습니다. 후보 기본 모드는 legacy -1012를 유지하며, 이는 기본 모드 전체 scroll 일치나 프로토콜 플래그 변경의 근거가 아닌 수명 진단입니다.

seek는 로컬이며 나중의 페이지 밖 fetch는 동일 결과가 브로커에 남아 있어야 합니다. EOF·트랜잭션 경계·연결 손실·오래된/다른 소유자로부터 무조건 결과를 재생성하지 않습니다. 새 검증은 상태를 바꾸는 선택자 __index__ 콜백과 정의되지 않은 signed overflow를 안전하게 거부하고 메시지 하나의 InterfaceError args를 유지합니다. .code는 실제 클라이언트 코드이며 errno를 만들어 붙이지 않습니다.

완료된 후보 FC3·FC8 위치·행과 페이지·스트리밍 지표는 후보 전용 보조 증거입니다. 요청 fetch100은 관측 페이지 상한이 아니며 서버가 더 큰 응답을 보낼 수 있습니다. C 확장의 페이지 저장소는 노출되지 않고, 래퍼 fetchall의 반환 목록도 내부 캐시 상한이 아닙니다. 원본 위치 assertion393/406/409만 매핑하며 num_fields/num_rows나 함수 전체를 검증 통과로 취급하지 않습니다. 네이티브 API 범위를 참고하세요.

#445 메타데이터 기능은 다른 docstring이 아닌 고정된 튜플 생성 구현을 따릅니다. 새 커서·준비만 한 커서·잘못된 번호 오류는 .code == -30006을 가지고, 닫힌 커서 오류는 -30019를 가집니다. 기존 pycubrid의 메시지 하나인 args는 공식의 정수·메시지 쌍과 다르며 별도 차등 비교 주장에 기록합니다. 메시지에서 코드를 파싱하거나 errno를 만들지 않고 정수 변환·닫힘·키워드 우선순위를 측정합니다. 캐시된 메타데이터는 EOF와 동일 소유자 commit·rollback 이후에도 유지되며 행 무효화와 별개입니다. 실행 시도 실패 뒤 이전 메타데이터를 숨기고, 끊긴·다른· 오래된 소유자는 CCI 포인터의 위험 대신 안전하게 실패합니다. 텍스트는 연결 코덱을 유지하며 고정 UTF-8 비교로 비UTF-8 동등성을 주장하지 않습니다. API 설명을 참고하세요.

검토를 마친 #418 타입 지정 CAS 설계는 첫 동기 전용 prepared 스칼라 범위(#439), 그 FC2/FC3/FC6 바이트, 핸들·결과·트랜잭션 소유권, statement pooling이 켜진 환경에서 측정한 경계와 먼저 실패하도록 작성하는 테스트 ID를 정의합니다. 이는 설계이며 사용 가능한 실행 API나 공식 드라이버 전체 패리티의 증거가 아닙니다. 기존 1.x 리터럴 바인딩은 그대로 유지됩니다.

#611의 준비 핸들 보정은 CCI의 같은 호출 내 invalid-plan 재시도보다 의도적으로 좁습니다. 완전한 브로커 실행 오류가 발생한 뒤 다음 명시적인 사용자 execute에서만 LOB이 없는 핸들을 원래 세션에서 다시 준비하고 FC3을 한 번 보냅니다. 원래 오류는 그대로 전달합니다. 이미 효과가 생겼을 수도 있는 문장을 실패한 호출 안에서 재실행하지 않으며 전송 오류·LOB 바인딩은 자동 재시도하지 않습니다. 구형 wire 오류 -1024는 CCI에서 정규화한 CAS_ER_STMT_POOLING=-10024이고 이 프로젝트의 오래된 ER_STMT_POOLING=-15 상수와 다릅니다. 정수 코드만으로 재실행하지 않습니다. 근거는 고정된 CCI 재시도 경로와 실행 진입 뒤에도 pooling 오류를 낼 수 있는 10.2·11.4 브로커 경로입니다.

증거와 의도적인 안전성 차이

소스 계약은 모순되는 docstring보다 고정된 실제 구현을 따릅니다. 래퍼 생성, 커서 어댑터, 네이티브 구현을 참조하며 CCI gitlink는 7d1eb8f40f04089b8218d08e36e2c24a2de11b24입니다.

메인테이너 로컬 증거 native-e75-oracle.y5UnxU에는 README, observations.jsonl, verify_observations.py가 있습니다. 정확한 e75+7d 빌드와 Python 3.10.12로 CUBRID 10.2.18.9024/11.4.6.1963의 정적 SELECT에서 타입을 기록한 33개 레코드입니다. 스칼라 튜플 행, 정수 null_ok, 메타데이터 컨테이너, 초기 autocommit=True를 측정했습니다. 컬렉션 리터럴은 문자열 list와 코드 104/96 또는 72/64였으며 저장된 SET이나 쿼리 독립적인 precision의 증거는 아닙니다. 저장된 컬럼·LOB·schema_info·문자셋/HA·실패 동작은 이 oracle이 검증하지 않았고, #446이 더 넓고 이식 가능한 차분 증거를 추적합니다. 이 이전 관측은 네이티브 패리티를 인증하지 않습니다.

#439 P11에서는 공개 소유자를 만든 뒤 구현된 동기 스칼라 커서를 공식 cubrid-python e75ec36b2a92b8829a49a967a29a1fbb9d7c322b와 그 CCI gitlink 7d1eb8f40f04089b8218d08e36e2c24a2de11b24를 사용해 비교했습니다. 격리된 Python 3.10.12 네이티브 확장의 SHA-256은 53861f3a058addb6131dab0796281ae9783209916cb6f6bfc3d26ae097114a84였고, pycubrid 구현 head는 f917a97b5095da826db4a0b0f6883ab5c1d35eb8였습니다. upstream 빌드 래퍼는 오래된 정적 라이브러리 경로와 Bash 호출 때문에 임시 로컬 전용 수정이 필요했으며, CCI와 확장 C 소스는 변경하지 않았습니다. 재현 가능한 probe는 CUBRID 10.2.18.9024와 11.4.6.1963에서 실행했고, 각 서버에서 비NULL 사례 그룹 네 개가 정확히 일치했습니다(INT 최솟값/0/최댓값 반복 SELECT, 따옴표/백슬래시/한국어 문자열 및 빈 문자열 SELECT, autocommit을 켠 상태와 끈 상태에서 바인딩 두 개의 반복 INSERT와 독립적인 가시성 검사). 검증기는 일치 사례 8개와 분류된 차이 4개를 보고했습니다. 공식 네이티브 bind_param(None)은 두 서버 모두 INT와 VARCHAR에서 SystemError를 발생시켰고, pycubrid는 SQL NULL을 반환했습니다. 네이티브 NULL 실패는 패리티 통과가 아닙니다. 이 선택된 비교는 컬렉션, LOB, 임의의 서버 설정이나 공식 드라이버 전체 패리티를 인증하지 않으며, 더 넓은 차분 게이트는 #446이 담당합니다.

  • 네이티브의 직접 bind_param(None)은 SystemError를 발생시킵니다. 뒤에 바인딩되지 않은 슬롯을 실행한 것은 명시적 NULL 바인딩 성공이 아닙니다. #439 호환성 커서는 명시적 SQL NULL을 안전하게 바인딩하고 미완성 슬롯은 FC3 전에 거부합니다. 이는 의도적인 차이이며 네이티브와 동일한 동작이 아닙니다.
  • 네이티브 컬렉션 NULL은 ''가 되었습니다. None과 진짜 빈 텍스트를 구별하고, 기존 디코딩 값의 str()로 네이티브 텍스트를 재구성하지 않습니다. 타입 지정 import는 명시적인 요소 타입과 SQL NULL의 None을 사용하며 손실되는 문자열 sentinel은 안 됩니다.
  • LOB 위치는 실제 전송량만큼 이동하며 짧은 write는 실패합니다. 요청 길이만큼 이동하거나 위험한 짧은 read 버퍼 동작을 복제하지 않고 기존 #394 안전성을 유지합니다.
  • 초과 팩터리 인자나 미지원 mapping 호출을 조용히 버리거나 키를 바인딩 값으로 취급하지 말고 명확히 거절합니다.

이는 명시적 차분 분류이지 포괄적 면제나 "미지원이므로 완료" 항목이 아닙니다. 영향 없는 동작은 정확한 비교가, NULL·데이터 보존은 테스트가 필요합니다. 저장된 타입 증거가 없으면 그 영역을 인증할 수 없지만 독립적인 prepared 코어 작업은 가능합니다.

공식 드라이버 차분 게이트 (#446)

pycubrid가 주장하는 공식 동작은 tests/fixtures/official_differential_claims.json에 기록합니다. 각 주장은 match(공식 드라이버와 Python 타입·값이 같음) 또는 deviation(이유, 담당 이슈, 두 드라이버의 정확한 관측값)이며, 검증하는 인벤토리 연산과 upstream 시나리오 id를 가집니다. 각 주장은 tests/test_official_differential.py의 라이브 케이스 하나와 연결되고, 이 케이스는 같은 서버에서 같은 SQL·값·autocommit 설정을 pycubrid와 공식 드라이버로 실행합니다. 불일치하면 실패하고, 분류된 차이도 어느 한쪽 관측값이 바뀌면 실패합니다.

오라클 출처. scripts/build_official_oracle.py는 cubrid-python e75ec36b2a92b8829a49a967a29a1fbb9d7c322b를 가져와 cci-src gitlink가 7d1eb8f40f04089b8218d08e36e2c24a2de11b24인지 확인한 뒤 그 CCI 커밋을 가져옵니다. CCI 정적 라이브러리는 CCI가 추적하는 번들 OpenSSL 1.1.1f 라이브러리로 CMake를 직접 사용해 빌드하고, 변경하지 않은 cubrid_ext/python_cubrid.c를 컴파일합니다. 이전 메인테이너 로컬 #439 빌드와 달리 upstream setup.py/build_cci.sh 래퍼를 실행하지 않고 upstream 파일을 패치하지 않습니다. upstream version.h 템플릿만 소스 트리 밖에서 생성합니다. 두 커밋, 도구 체인, 확장 모듈 SHA-256은 oracle.json에 기록됩니다. Linux x86_64만 지원하며 TLS는 이 오라클로 검증하지 않습니다.

필수 레인. 일반 CI의 official-differential 작업은 CI Gate에 포함되며 문서 전용 변경일 때만 건너뜁니다. Python 3.11로 CUBRID 10.2와 11.4를 대상으로 실행하고, 두 고정값을 담은 빌드 스크립트 해시를 키로 오라클 빌드를 캐시합니다. 같은 작업이 nightly와 릴리스 전체 매트릭스도 막습니다. PYCUBRID_OFFICIAL_ORACLE_REQUIRED=1에서는 다음 경우 건너뛰지 않고 실패합니다.

  • 드라이버나 매니페스트가 없거나, 확장 모듈 해시가 매니페스트와 다른 경우
  • 케이스가 0개이거나, 어느 한 서버에서 결과가 없는 주장이 있는 경우
  • 불일치하거나, 분류되지 않은 차이가 있는 경우

레인 감사(check_integration_lanes.py --lane official)는 모든 skip을 거부합니다. 케이스별 JSON Lines 증거와 요약은 official-differential-evidence 아티팩트로 업로드됩니다. 증거에는 Python·서버·드라이버 버전, pycubrid 커밋, 모든 관측값이 기록됩니다. PYCUBRID_OFFICIAL_ORACLE_REQUIRED=1이 없으면(오프라인, 다른 통합 레인, 로컬 실행) 다른 CUBRIDdb 빌드를 가져올 수 있더라도 모듈을 건너뛰며, 이런 실행은 주장을 인증하지 않습니다. 증거 검사는 기록된 결과 표시를 믿지 않고, 기록된 관측값과 원장으로 각 결과를 다시 계산합니다.

아래 수치는 원장에서 생성되므로 직접 수정하지 마세요. 주장을 바꾼 뒤 python scripts/check_official_differential.py --write-docs를 실행합니다. 오프라인 검사는 오래된 수치, 알 수 없는 인벤토리·시나리오 id, 케이스 없는 주장, 빌드 스크립트와 다른 오라클 고정값을 실패로 처리합니다.

표면 일치 분류된 차이 합계
래퍼 (CUBRIDdb) 18 4 22
네이티브 (_cubrid) 34 13 47
합계 52 17 69
  • 오라클: cubrid-python e75ec36b2a92, CCI 7d1eb8f40f04, Python 3.11
  • 필수 서버: CUBRID 10.2, CUBRID 11.4
  • 분류된 차이: fetch-monetary (#344), description-size-and-null-ok (#438), prepared-bind-null (#439), bind-multiset-duplicates (#440), bind-sequence-order (#440), bind-set-null-text (#440), bind-set-empty-string (#440), bind-set-python-int (#440), bind-set-nul-truncation (#440), bind-set-error-classes (#440), wrapper-collection-null-element (#610), wrapper-collection-errors (#610), lob-bind-without-value (#441), lob-error-classes (#441), lob-fetch-into-closed-or-foreign (#441), native-result-info-error-args (#445), native-position-error-args (#444)

이 주장들은 측정한 제한된 범위입니다. 저장된 스칼라 조회, 정적 스칼라 행과 description, #466의 한정된 래퍼 행 변환 부분집합, #662 수동 commit/rollback 가시성, #439 prepared INT/문자열 부분집합, #440 네이티브 컬렉션 바인딩, #441/#442 네이티브 LOB 핸들·스트림, 제한된 #443 원시 파일 성공 작업, #467 캐시와 안전한 실제 설정자 값을 다룹니다. 래퍼 컬렉션 형태, 더 넓은 LOB 파일·실패 동작, HA와 나머지 인벤토리 연산은 여기에 주장이 생길 때까지 인증되지 않습니다. 주장 수는 패리티 비율이 아닙니다.

이행 목표와 작은 구현 단위의 수용 기준

일반 쿼리에는 기존 import를 유지하세요. 명시적 네이티브 모듈은 제한된 동기 스칼라·컬렉션·LOB prepared 실행, #467 설정과 #666 유틸리티를 지원하며, 래퍼는 연결 생성·종료와 autocommit, 명시적 commit/rollback과 서버 버전/query ping 위임, #466의 한정된 네이티브 스칼라 기반 행 커서를 제공합니다. 이 범위의 래퍼 이행은 import CUBRIDdb → from pycubrid.compat import cubriddb as CUBRIDdb, 네이티브는 import _cubrid → from pycubrid.compat import native as _cubrid입니다. 수동 트랜잭션에서 래퍼는 conn.autocommit = False, 네이티브는 conn.set_autocommit(False)를 사용해야 합니다. 네이티브 멤버 대입은 스냅샷만 바꿉니다. NULL 사용자는 ''-as-NULL 비교를 is None으로 바꾸되 진짜 빈 문자열은 보존하세요. 바이너리 LOB에는 호환성 Unicode read가 아니라 기존 bytes API를 유지합니다.

잠정 구현 단위 수용 기준 / 디펜던시
M 팩터리 / M 공유 #465는 명시적 생성·종료, 별칭, DSN/user/autocommit 기본값과 래퍼-네이티브 관계를 제공하면서 기존 동작을 유지합니다. 공유·수명주기 직렬화는 별도이며 threadsafety=2를 게이팅합니다. prepared 엔진이나 전역 스위치는 제공하지 않습니다.
M prepared / 타입 바인딩 #439는 #418과 이 계약 뒤에 count/반환/위치 인자/NULL/오류를 검증하고, #440 네이티브 컬렉션 바인딩과 #441 네이티브 LOB 핸들 조회·바인딩은 라이브 차등 비교 주장과 함께 제공됩니다.
M 변환 / 문자셋 / HA #466의 한정된 튜플/dict 행 및 연결별 변환기, #86의 문자셋 인코딩(래퍼의 charset 속성 유지는 아직 없음)을 제공합니다. HA/URL 옵션은 실제 failover 증거가 필요한 별도 단위입니다. 옵션 파싱이나 upstream default_cursor 스텁만으로는 미완료입니다.
S 배치 / 예외 / identity 기존 임의 SQL 전송을 이용하는 네이티브 배치 레코드, 네임스페이스 예외/export 어댑터, fresh/트랜잭션/CALL/non-auto 제어를 갖춘 브로커 identity를 각각 분리합니다. 기존 첫 오류·캐시 문자열 동작은 유지합니다.
M 설정 / 탐색 실제 서버 연산과 네 캐시 멤버를 구별합니다. #444 seek/position과 별도 next_result/실행 옵션/쿼리 계획 단위이며 스텁·플래그만으로 완성하지 않습니다.
S/M 메타데이터 / 스키마 / LOB #445 값·타입·15필드, #412/#455–457 소유한 스키마 재사용, #442 실제 바이트 위치·짧은 전송, #443 파일입니다. 하나의 거대한 파사드 PR이 아닌 집중된 작업입니다.
M 차분 증거 #446/#351은 고정 네이티브 리비전에서 제공된 각 단위를 비교하며 저장된 컬렉션 NULL/빈 값/순서/중복, LOB UTF-8 실패와 기존 SQLAlchemy smoke를 포함합니다. 소스 목록은 패리티 통과가 아닙니다.

#465 기반 구현은 서브모듈의 명시적 __all__, 기존 공개 API 검사기의 추적 모듈/클래스와 RELEASE_POLICY §1 확장, baseline 재생성을 함께 제공합니다. 후속 API 단위도 baseline을 갱신해야 합니다. 새 root 별칭이나 async 변경은 없습니다. 새 명시적 API는 MINOR 추가이고 기존 약속의 수정은 PATCH입니다. #438은 설계를 선택했고 #465는 생성만 제공하며 나머지 하위 기능은 제공하지 않습니다. #396은 범위 내 기능과 검증이 완료될 때까지 열어 둡니다.

소스의 불일치는 패리티 목표가 아님

카탈로그는 다음 불일치를 조용히 정규화하지 않고 보존합니다.

  • 래퍼의 callproc()와 nextset()은 설명적인 docstring과 달리 스텁입니다.
  • result_info()는 첫 필드들의 순서를 docstring과 다르게 구현합니다. 기록된 순서는 고정 소스에서 튜플을 구성하는 방식을 따릅니다.
  • 네이티브 execute는 옵션 기본값을 0으로 파싱하지만 docstring은 QUERY_ALL을 설명합니다. bind_param은 문서의 None이 아니라 정수 기본값 0을 파싱합니다.
  • 네이티브 schema_info는 class_name을, Set.imports는 type 인자를 요구합니다. 문서의 선택적 인자·기본값 설명과 다릅니다.
  • schema-info는 문서의 튜플 설명과 달리 행 리스트를 구성합니다. 네이티브 insert_id는 pycubrid의 문자열 편의 API와 달리 정수를 반환합니다.
  • 패키지 __all__은 패키지 할당이 확인되지 않은 이름도 광고합니다. 카탈로그는 광고된 export와 실제 작동하는 연산을 구분합니다.
  • 래퍼 문자셋 변환, 컬렉션 요소 추론, LOB의 짧은 전송량 처리·텍스트 디코딩, 선택된 LOB 컬럼 처리는 독립적인 계약 증거가 필요합니다. 소스 관찰은 새로 실행해 확인한 결함 주장과 같지 않습니다.

실제로 내부적인 세부 사항에는 명시적 제외 이유가 있습니다. 예를 들어 _set_charset_name은 내부용이며 사용자가 호출하면 안 된다고 명시합니다. 하지만 래퍼의 공개 문자셋 기능은 공백으로 계속 포함됩니다. 네이티브 ABI 배치, CCI 포인터, 할당자·소멸자, 메모리 주소 형식은 복사하지 않습니다. 어떤 연산도 C로 구현되었다는 이유만으로 제외되지 않습니다.

검증과 출처

오프라인 일관성 검사는 다음과 같이 실행합니다.

python -m pytest tests/test_official_api_inventory.py -q

이 검사는 네이티브 드라이버를 가져오거나 데이터베이스에 연결하지 않고, 매핑을 패리티 증거로 바꾸지 않은 채 목록과 현재 명명된 대상을 검증합니다. 소스 시나리오 목록화(#437)는 별도의 산출물입니다. 공식 드라이버 비교 증거는 위에서 설명한 라이브 차분 게이트(#446)에서만 나옵니다.

python scripts/check_official_differential.py   # 오프라인 주장/문서 구조 검사

공식 드라이버의 메인테이너와 기여자에게 감사드립니다. 여기의 설명은 고정 소스에서 독립적으로 풀어 쓴 것으로, upstream 소스나 docstring을 통째로 복사하지 않았습니다. 기존 NOTICE와 제3자 참조 기록이 출처와 라이선스 한계의 기준으로 유지됩니다. 이 카탈로그는 새로운 라이선스 추론, 런타임 의존성 또는 upstream 소스 재사용 허가를 추가하지 않습니다.