PEP 249 – Python 데이터베이스 API 명세 v2.0
- Author:
- Marc-André Lemburg <mal at lemburg.com>
- Discussions-To:
- Db-SIG list
- Status:
- Final
- Type:
- Informational
- Created:
- 12-Apr-1999
- Post-History:
- Replaces:
- 248
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
소개
이 API는 데이터베이스에 접근하는 데 사용되는 Python 모듈 간의 유사성을 높이기 위해 정의되었습니다. 이를 통해 더 쉽게 이해할 수 있는 모듈, 일반적으로 여러 데이터베이스 간에 이식성이 더 높은 코드, 그리고 Python에서 더 폭넓은 데이터베이스 연결성을 제공하는 일관성을 달성하고자 합니다.
이 명세에 대한 의견과 질문은 SIG for Database Interfacing with Python으로 보내실 수 있습니다.
파이썬에서 데이터베이스를 다루는 방법과 사용할 수 있는 패키지에 대한 자세한 정보는 Database Topic Guide를 참고하십시오.
이 문서는 Python 데이터베이스 API 명세 2.0과 공통 선택적 확장 기능을 설명합니다. 이전 버전인 1.0은 PEP 248에서 여전히 참고용으로 볼 수 있습니다. 패키지 작성자에게는 새 인터페이스를 만들 때 이 버전의 명세를 기반으로 삼기를 권장합니다.
모듈 인터페이스
생성자
데이터베이스에는 연결 객체를 통해 접근합니다. 모듈은 연결 객체를 만들 수 있도록 다음 생성자를 제공해야 합니다:
- connect( 매개변수… )
- 데이터베이스에 대한 연결을 만드는 생성자입니다.
Connection 객체를 반환합니다. 데이터베이스에 따라 달라지는 여러 매개변수를 받습니다. [1]
전역 변수
다음 모듈 전역 변수를 반드시 정의해야 합니다:
- apilevel
- 지원하는 DB API 수준을 나타내는 문자열 상수입니다.
현재는 “
1.0”과 “2.0” 문자열만 허용됩니다. 지정되지 않은 경우 DB-API 1.0 수준의 인터페이스로 간주해야 합니다.
- threadsafety
- 인터페이스가 지원하는 스레드 안전성 수준을 나타내는 정수 상수입니다. 가능한 값은 다음과 같습니다:
threadsafety 의미 0 스레드는 모듈을 공유할 수 없습니다. 1 스레드는 모듈을 공유할 수 있지만 연결은 공유할 수 없습니다. 2 스레드는 모듈과 연결을 공유할 수 있습니다. 3 스레드는 모듈, 연결, 커서를 공유할 수 있습니다. 위 맥락에서 공유란 두 스레드가 자원 잠금을 구현하려고 뮤텍스 세마포어로 자원을 감싸지 않고도 그 자원을 사용할 수 있다는 뜻입니다. 뮤텍스로 접근을 관리한다고 해서 외부 자원을 항상 스레드 안전하게 만들 수 있는 것은 아니라는 점에 유의하십시오. 해당 자원이 전역 변수나 제어할 수 없는 다른 외부 요소에 의존할 수 있기 때문입니다.
- paramstyle
- 인터페이스가 기대하는 매개변수 마커 형식의 종류를 나타내는 문자열 상수입니다. 가능한 값은 다음과 같습니다 [2]:
paramstyle 의미 qmark물음표 스타일, 예: ...WHERE name=?numeric숫자 위치 스타일, 예: ...WHERE name=:1named이름 지정 스타일, 예: ...WHERE name=:nameformatANSI C printf 형식 코드, 예: ...WHERE name=%spyformatPython 확장 형식 코드, 예: ...WHERE name=%(name)s
예외
모듈은 모든 오류 정보를 다음 예외들이나 그 서브클래스를 통해 제공해야 합니다:
- Warning
- 삽입 중 데이터 잘림 등과 같은 중요한 경고에 대해 발생하는 예외입니다. Python
Exception클래스의 서브클래스여야 합니다 [10] [11].
- Error
- 다른 모든 오류 예외의 베이스 클래스가 되는 예외입니다. 이 예외를 사용하면
except문 하나로 모든 오류를 잡을 수 있습니다. 경고는 오류로 간주되지 않으므로 이 클래스를 베이스로 사용해서는 안 됩니다. PythonException클래스의 서브클래스여야 합니다 [10].
- InterfaceError
- 데이터베이스 자체가 아니라 데이터베이스 인터페이스와 관련된 오류에 대해 발생하는 예외입니다. Error의 서브클래스여야 합니다.
- DatabaseError
- 데이터베이스와 관련된 오류에 대해 발생하는 예외입니다. Error의 서브클래스여야 합니다.
- DataError
- 0으로 나누기, 범위를 벗어난 숫자 값 등 처리된 데이터의 문제로 인한 오류에 대해 발생하는 예외입니다. DatabaseError의 서브클래스여야 합니다.
- OperationalError
- 데이터베이스의 동작과 관련되어 있으며 반드시 프로그래머가 제어할 수 있는 것은 아닌 오류에 대해 발생하는 예외입니다. 예를 들어 예기치 않은 연결 끊김이 발생한 경우, 데이터 소스 이름을 찾을 수 없는 경우, 트랜잭션을 처리할 수 없는 경우, 처리 중 메모리 할당 오류가 발생한 경우 등이 있습니다. DatabaseError의 서브클래스여야 합니다.
- IntegrityError
- 외래 키 검사가 실패하는 경우처럼 데이터베이스의 관계 무결성이 영향을 받을 때 발생하는 예외입니다. DatabaseError의 서브클래스여야 합니다.
- InternalError
- 커서가 더 이상 유효하지 않거나 트랜잭션이 동기화되지 않은 경우처럼 데이터베이스에 내부 오류가 발생했을 때 발생하는 예외입니다. DatabaseError의 서브클래스여야 합니다.
- ProgrammingError
- 프로그래밍 오류에 대해 발생하는 예외로, 예를 들어 테이블을 찾을 수 없거나 이미 존재하는 경우, SQL 문의 문법 오류, 지정된 매개변수 개수가 잘못된 경우 등이 있습니다. DatabaseError의 서브클래스여야 합니다.
- NotSupportedError
- 데이터베이스가 지원하지 않는 메서드나 데이터베이스 API가 사용된 경우에 발생하는 예외입니다. 예를 들어 트랜잭션을 지원하지 않거나 트랜잭션이 꺼져 있는 연결에서 .rollback()을 요청하는 경우가 이에 해당합니다. DatabaseError의 서브클래스여야 합니다.
Exception
|__Warning
|__Error
|__InterfaceError
|__DatabaseError
|__DataError
|__OperationalError
|__IntegrityError
|__InternalError
|__ProgrammingError
|__NotSupportedError
Note
이러한 예외의 값은 정의되어 있지 않습니다. 다만 무엇이 잘못되었는지 사용자가 충분히 파악할 수 있도록 해야 합니다.
연결 객체
연결 객체는 다음 메서드에 응답해야 합니다.
연결 메서드
- .close()
- (
.__del__()이 호출될 때가 아니라) 지금 바로 연결을 닫습니다.이 시점부터 연결은 사용할 수 없게 되며, 이 연결로 어떤 작업이든 시도하면 Error (또는 그 서브클래스) 예외가 발생합니다. 이 연결을 사용하려는 모든 커서 객체에도 똑같이 적용됩니다. 변경 사항을 먼저 커밋하지 않고 연결을 닫으면 암묵적으로 롤백이 수행된다는 점에 유의하십시오.
- .commit()
- 보류 중인 모든 트랜잭션을 데이터베이스에 커밋합니다.
데이터베이스가 자동 커밋 기능을 지원한다면, 이 기능은 처음에 꺼져 있어야 한다는 점에 유의하십시오. 이 기능을 다시 켜는 인터페이스 메서드를 제공할 수 있습니다.
트랜잭션을 지원하지 않는 데이터베이스 모듈은 이 메서드를 아무 동작도 하지 않도록 구현해야 합니다.
- .rollback()
- 모든 데이터베이스가 트랜잭션을 지원하지는 않으므로 이 메서드는 선택 사항입니다. [3]
데이터베이스가 트랜잭션을 지원하는 경우, 이 메서드는 데이터베이스를 대기 중인 트랜잭션의 시작 지점으로 롤백합니다. 변경 사항을 먼저 커밋하지 않고 연결을 닫으면 암묵적으로 롤백이 수행됩니다.
- .cursor()
- 이 연결을 사용하는 새 Cursor 객체를 반환합니다.
데이터베이스가 커서 개념을 직접 제공하지 않는 경우, 모듈은 이 명세가 요구하는 범위까지 다른 수단으로 커서를 에뮬레이트해야 합니다. [4]
커서 객체
이 객체는 데이터베이스 커서를 나타내며, 데이터베이스 커서는 가져오기(fetch) 작업의 컨텍스트를 관리하는 데 사용됩니다. 같은 연결에서 만든 커서들은 서로 격리되지 않습니다. 즉, 한 커서가 데이터베이스에 가한 모든 변경 사항은 다른 커서에서 즉시 볼 수 있습니다. 서로 다른 연결에서 만든 커서들은 트랜잭션 지원이 구현된 방식에 따라 격리될 수도 있고 격리되지 않을 수도 있습니다(연결의 .rollback() 및 .commit() 메서드도 참고하십시오).
커서 객체는 다음 메서드와 속성을 지원해야 합니다.
커서 속성
- .description
- 이 읽기 전용 속성은 항목 7개짜리 시퀀스들의 시퀀스입니다.
각 시퀀스에는 결과 열 하나를 설명하는 정보가 담겨 있습니다:
nametype_codedisplay_sizeinternal_sizeprecisionscalenull_ok
처음 두 항목(
name과type_code)은 필수입니다. 나머지 다섯 항목은 선택 사항이며, 의미 있는 값을 제공할 수 없으면None으로 설정됩니다.행을 반환하지 않는 작업이거나 커서에서 아직 .execute*() 메서드로 작업을 실행하지 않은 경우, 이 속성은
None입니다.type_code는 아래 절에 명시된 Type Objects와 비교하여 해석할 수 있습니다.
- .rowcount
- 이 읽기 전용 속성은 마지막 .execute*()가 생성한 행 수(
SELECT같은 DQL 문의 경우) 또는 영향을 준 행 수(UPDATE나INSERT같은 DML 문의 경우)를 나타냅니다. [9]커서에서 .execute*()가 수행된 적이 없거나 인터페이스가 마지막 작업의 행 수를 알아낼 수 없는 경우, 이 속성은 -1입니다. [7]
Note
DB API 명세의 향후 버전에서는 후자의 경우 객체가 -1 대신
None을 반환하도록 재정의할 수 있습니다.
커서 메서드
- .callproc( procname [, 매개변수 ] )
- (모든 데이터베이스가 저장 프로시저를 제공하는 것은 아니므로 이 메서드는 선택 사항입니다. [3])
주어진 이름의 데이터베이스 저장 프로시저를 호출합니다. 매개변수 시퀀스에는 프로시저가 기대하는 인자마다 하나의 항목이 있어야 합니다. 호출 결과는 입력 시퀀스를 수정한 사본으로 반환됩니다. 입력 매개변수는 그대로 유지되고, 출력 매개변수와 입출력 매개변수는 새로운 값으로 바뀔 수 있습니다.
프로시저는 결과 집합을 출력으로 제공할 수도 있습니다. 이 경우 그 결과 집합은 표준 .fetch*() 메서드를 통해 가져올 수 있어야 합니다.
- .close()
- (
__del__이 호출될 때가 아니라) 지금 바로 커서를 닫습니다.이 시점부터 커서는 사용할 수 없으며, 커서로 어떤 연산이든 시도하면 Error (또는 그 서브클래스) 예외가 발생합니다.
- .execute(operation [, 매개변수])
- 데이터베이스 연산(쿼리 또는 명령)을 준비하고 실행합니다.
매개변수는 시퀀스나 매핑으로 제공할 수 있으며, 연산 안의 변수에 바인딩됩니다. 변수는 데이터베이스별 표기법으로 지정합니다(자세한 내용은 모듈의 paramstyle 속성을 참고하십시오). [5]
커서는 연산에 대한 참조를 유지합니다. 같은 연산 객체가 다시 전달되면 커서는 동작을 최적화할 수 있습니다. 이는 같은 연산을 사용하되 다른 매개변수를 (여러 번) 바인딩하는 알고리즘에서 가장 효과적입니다.
연산을 재사용할 때 효율을 최대한 높이려면 .setinputsizes() 메서드로 매개변수의 타입과 크기를 미리 지정하는 것이 가장 좋습니다. 매개변수가 미리 정의된 정보와 일치하지 않아도 허용됩니다. 이 경우 구현이 이를 보완해야 하며, 그 과정에서 효율이 떨어질 수 있습니다.
예를 들어 한 번의 연산으로 여러 행을 삽입하기 위해 매개변수를 튜플의 리스트로 지정할 수도 있지만, 이런 사용 방식은 폐지 예정입니다. 대신 .executemany()를 사용해야 합니다.
반환값은 정의되어 있지 않습니다.
- .executemany( operation, seq_of_parameters )
- 데이터베이스 연산(쿼리 또는 명령)을 준비한 다음, 시퀀스 seq_of_parameters에 있는 모든 매개변수 시퀀스 또는 매핑에 대해 실행합니다.
모듈은 .execute() 메서드를 여러 번 호출하거나, 배열 연산을 사용해 데이터베이스가 시퀀스 전체를 한 번의 호출로 처리하게 하는 방식으로 이 메서드를 자유롭게 구현할 수 있습니다.
하나 이상의 결과 집합을 생성하는 연산에 이 메서드를 사용하는 것은 정의되지 않은 동작입니다. 구현은 연산 호출로 결과 집합이 생성된 것을 감지하면 예외를 발생시킬 수 있습니다(필수는 아닙니다).
.execute()에 대한 설명은 이 메서드에도 마찬가지로 적용됩니다.
반환값은 정의되어 있지 않습니다.
- .fetchone()
- 쿼리 결과 집합의 다음 행을 가져와 단일 시퀀스로 반환하며, 더 이상 가져올 데이터가 없으면
None을 반환합니다. [6]이전의 .execute*() 호출이 결과 집합을 생성하지 않았거나 아직 호출이 이루어지지 않은 경우 Error (또는 서브클래스) 예외가 발생합니다.
- .fetchmany([size=cursor.arraysize])
- 쿼리 결과의 다음 행 집합을 가져와 시퀀스의 시퀀스(예: 튜플의 리스트)로 반환합니다. 더 이상 가져올 행이 없으면 빈 시퀀스를 반환합니다.
호출마다 가져올 행의 수는 매개변수로 지정합니다. 매개변수를 지정하지 않으면 커서의 arraysize가 가져올 행의 수를 결정합니다. 이 메서드는 size 매개변수가 지정한 수만큼 행을 가져오려고 시도해야 합니다. 지정한 수만큼 행이 남아 있지 않아 그렇게 할 수 없으면 더 적은 수의 행을 반환할 수 있습니다.
이전의 .execute*() 호출이 결과 집합을 생성하지 않았거나 아직 호출이 이루어지지 않은 경우 Error (또는 서브클래스) 예외가 발생합니다.
size 매개변수와 관련하여 성능상 고려할 사항이 있다는 점에 유의하십시오. 최적의 성능을 얻으려면 보통 .arraysize 속성을 사용하는 것이 가장 좋습니다. size 매개변수를 사용한다면 .fetchmany() 호출마다 같은 값을 유지하는 것이 가장 좋습니다.
- .fetchall()
- 쿼리 결과의 (남은) 모든 행을 가져와 시퀀스의 시퀀스(예: 튜플의 리스트)로 반환합니다. 커서의 arraysize 속성이 이 연산의 성능에 영향을 줄 수 있다는 점에 유의하십시오.
이전의 .execute*() 호출이 결과 집합을 생성하지 않았거나 아직 호출이 이루어지지 않은 경우 Error (또는 서브클래스) 예외가 발생합니다.
- .nextset()
- (모든 데이터베이스가 다중 결과 집합을 지원하지는 않으므로 이 메서드는 선택 사항입니다. [3])
이 메서드는 현재 집합에 남은 행을 모두 버리고 커서를 사용 가능한 다음 집합으로 건너뛰게 합니다.
더 이상 집합이 없으면 이 메서드는
None을 반환합니다. 그렇지 않으면 참 값을 반환하며, 이후의 .fetch*() 메서드 호출은 다음 결과 집합의 행을 반환합니다.이전의 .execute*() 호출이 결과 집합을 생성하지 않았거나 아직 호출이 이루어지지 않은 경우 Error (또는 서브클래스) 예외가 발생합니다.
- .arraysize
- 이 읽기/쓰기 속성은 .fetchmany()로 한 번에 가져올 행의 수를 지정합니다. 기본값은 1이며, 한 번에 한 행씩 가져온다는 뜻입니다.
구현체는 .fetchmany() 메서드에 대해서는 이 값을 반드시 따라야 하지만, 데이터베이스와는 한 번에 한 행씩 상호작용해도 됩니다. 이 값은 .executemany()를 구현할 때도 사용할 수 있습니다.
- .setinputsizes(sizes)
- .execute*() 호출 전에 이 메서드를 사용하여 연산의 매개변수에 쓸 메모리 영역을 미리 정의할 수 있습니다.
sizes는 시퀀스로 지정하며, 입력 매개변수마다 항목이 하나씩 있습니다. 각 항목은 사용될 입력에 해당하는 타입 객체이거나, 문자열 매개변수의 최대 길이를 지정하는 정수여야 합니다. 항목이
None이면 해당 열에는 미리 정의된 메모리 영역을 예약하지 않습니다(큰 입력에 대해 미리 정의된 영역을 두지 않으려 할 때 유용합니다).이 메서드는 .execute*() 메서드가 호출되기 전에 사용됩니다.
구현은 이 메서드가 아무 일도 하지 않도록 해도 되며, 사용자는 이 메서드를 사용하지 않아도 됩니다.
- .setoutputsize(size [, column])
- 큰 열(예:
LONG,BLOB등)을 페치할 때 사용할 열 버퍼 크기를 설정합니다. 열은 결과 시퀀스의 인덱스로 지정합니다. 열을 지정하지 않으면 커서의 모든 큰 열에 대한 기본 크기가 설정됩니다.이 메서드는 .execute*() 메서드가 호출되기 전에 사용됩니다.
구현은 이 메서드가 아무 일도 하지 않도록 해도 되며, 사용자는 이 메서드를 사용하지 않아도 됩니다.
타입 객체와 생성자
많은 데이터베이스에서는 연산의 입력 매개변수에 바인딩하려면 입력이 특정 형식이어야 합니다. 예를 들어 입력이 DATE 열에 들어갈 값이라면, 특정 문자열 형식으로 데이터베이스에 바인딩되어야 합니다. “Row ID” 열이나 큰 바이너리 항목(예: blob 또는 RAW 열)에도 비슷한 문제가 있습니다. .execute*() 메서드의 매개변수에는 타입이 없으므로 이는 Python에 문제가 됩니다. 데이터베이스 모듈이 Python 문자열 객체를 받았을 때, 이를 단순한 CHAR 열로 바인딩해야 하는지, 원시 BINARY 항목으로 바인딩해야 하는지, 아니면 DATE로 바인딩해야 하는지 알 수 없습니다.
이 문제를 해결하기 위해 모듈은 특수한 값을 담을 수 있는 객체를 만드는, 아래에 정의된 생성자들을 제공해야 합니다. 이렇게 만든 객체가 커서 메서드에 전달되면 모듈은 입력 매개변수의 올바른 타입을 감지하여 그에 맞게 바인딩할 수 있습니다.
Cursor 객체의 description 속성은 쿼리 결과의 각 열에 대한 정보를 반환합니다. type_code는 아래에 정의된 타입 객체 중 하나와 비교했을 때 같아야 합니다. 타입 객체는 둘 이상의 타입 코드와 같을 수 있습니다(예: DATETIME은 date, time, timestamp 열의 타입 코드와 같을 수 있습니다. 자세한 내용은 아래의 Implementation Hints를 참조하십시오).
모듈은 다음 생성자와 싱글턴을 내보냅니다:
- Date(year, month, day)
- 이 함수는 날짜 값을 담은 객체를 생성합니다.
- Time(hour, minute, second)
- 이 함수는 시간 값을 담은 객체를 생성합니다.
- Timestamp(year, month, day, hour, minute, second)
- 이 함수는 타임스탬프 값을 담은 객체를 생성합니다.
- DateFromTicks(ticks)
- 이 함수는 주어진 틱 값(에포크 이후 경과한 초 수. 자세한 내용은 파이썬 표준 time 모듈 문서를 참조하십시오)으로부터 날짜 값을 담은 객체를 생성합니다.
- TimeFromTicks(ticks)
- 이 함수는 주어진 틱 값(에포크 이후 경과한 초 수, 자세한 내용은 표준 Python time 모듈 문서를 참고하십시오)으로부터 시간 값을 담는 객체를 생성합니다.
- TimestampFromTicks(ticks)
- 이 함수는 주어진 틱 값(에포크 이후 경과한 초 수, 자세한 내용은 표준 Python time 모듈 문서를 참고하십시오)으로부터 타임스탬프 값을 담는 객체를 생성합니다.
- Binary(문자열)
- 이 함수는 바이너리 (긴) 문자열 값을 담을 수 있는 객체를 생성합니다.
- STRING 타입
- 이 타입 객체는 데이터베이스에서 문자열 기반인 열(예:
CHAR)을 기술하는 데 사용됩니다.
- BINARY 타입
- 이 타입 객체는 데이터베이스에서 (긴) 바이너리 열(예:
LONG,RAW,BLOBs)을 기술하는 데 사용됩니다.
- NUMBER 타입
- 이 타입 객체는 데이터베이스에서 숫자 열을 기술하는 데 사용됩니다.
- DATETIME 타입
- 이 타입 객체는 데이터베이스에서 날짜/시간 열을 기술하는 데 사용됩니다.
- ROWID 타입
- 이 타입 객체는 데이터베이스에서 “Row ID” 열을 기술하는 데 사용됩니다.
SQL NULL 값은 입력과 출력 모두에서 Python None 싱글턴으로 표현됩니다.
Note
데이터베이스 인터페이스에 Unix 틱을 사용하면 표현할 수 있는 날짜 범위가 제한되어 있기 때문에 문제가 발생할 수 있습니다.
모듈 작성자를 위한 구현 힌트
- 날짜/시간 객체는 Python datetime module 객체(파이썬 2.3부터 사용 가능하며, C API는 2.4부터 제공)로 구현하거나 mxDateTime 패키지(1.5.2 이후의 모든 파이썬 버전에서 사용 가능)를 사용해 구현할 수 있습니다. 둘 다 Python 수준과 C 수준에서 필요한 모든 생성자와 메서드를 제공합니다.
- 다음은 작업을 일반 생성자에 위임하는, Unix 틱 기반 날짜/시간 생성자의 샘플 구현입니다:
import time def DateFromTicks(ticks): return Date(*time.localtime(ticks)[:3]) def TimeFromTicks(ticks): return Time(*time.localtime(ticks)[3:6]) def TimestampFromTicks(ticks): return Timestamp(*time.localtime(ticks)[:6])
- Binary 객체에 권장되는 객체 타입은 버전 1.5.2부터 표준 Python에서 사용할 수 있는 버퍼 타입입니다. 자세한 내용은 Python 문서를 참고하십시오. C 인터페이스에 대한 정보는 Python 소스 배포판의
Include/bufferobject.h및Objects/bufferobject.c파일을 살펴보십시오. - 이 Python 클래스를 사용하면 description 타입 코드 필드가 하나의 타입 객체에 대해 여러 값을 내더라도 위의 타입 객체를 구현할 수 있습니다:
class DBAPITypeObject: def __init__(self,*values): self.values = values def __cmp__(self,other): if other in self.values: return 0 if other < self.values: return 1 else: return -1
이렇게 만든 타입 객체는 생성자에 전달한 모든 값과 같다고 비교됩니다.
- 다음은 위에서 정의한 예외 계층 구조를 구현하는 Python 코드 조각입니다 [10]:
class Error(Exception): pass class Warning(Exception): pass class InterfaceError(Error): pass class DatabaseError(Error): pass class InternalError(DatabaseError): pass class OperationalError(DatabaseError): pass class ProgrammingError(DatabaseError): pass class IntegrityError(DatabaseError): pass class DataError(DatabaseError): pass class NotSupportedError(DatabaseError): pass
C에서는
PyErr_NewException(fullname, base, NULL)API를 사용해 예외 객체를 생성할 수 있습니다.
선택적 DB API 확장
DB API 2.0이 쓰이는 동안 모듈 작성자들은 이 DB API 명세가 요구하는 범위를 넘어 구현을 확장하는 경우가 많았습니다. 호환성을 높이고 향후 나올 수 있는 명세 버전으로 깔끔하게 업그레이드할 수 있는 경로를 제공하기 위해, 이 절에서는 핵심 DB API 2.0 명세에 대한 공통 확장 집합을 정의합니다.
다른 모든 DB API 선택 기능과 마찬가지로, 데이터베이스 모듈 작성자는 이러한 추가 속성과 메서드를 구현하지 않아도 되며(이 경우 이를 사용하면 AttributeError가 발생합니다), 사용 가능 여부를 실행 시간에만 확인할 수 있는 경우에는 NotSupportedError 예외를 발생시켜도 됩니다.
Python 경고 프레임워크를 통해 Python 경고를 발생시킴으로써 이러한 확장 기능의 사용을 프로그래머가 선택적으로 확인할 수 있도록 하자는 제안이 있었습니다. 이 기능이 유용하려면 경고 메시지를 걸러 낼 수 있도록 경고 메시지를 표준화해야 합니다. 이러한 표준 메시지를 아래에서는 Warning Message라고 부릅니다.
- Cursor.rownumber
- 이 읽기 전용 속성은 결과 집합에서 커서의 현재 인덱스(0부터 시작)를 제공해야 하며, 인덱스를 결정할 수 없는 경우에는
None을 제공해야 합니다.이 인덱스는 시퀀스(결과 집합)에서 커서의 인덱스로 볼 수 있습니다. 다음 페치 연산은 해당 시퀀스에서 .rownumber로 인덱싱된 행을 가져옵니다.
경고 메시지: “DB-API extension cursor.rownumber used”
- Connection.Error, Connection.ProgrammingError 등
- DB API 표준에 정의된 모든 예외 클래스는 (모듈 범위에서 사용할 수 있는 것에 더하여) Connection 객체에 속성으로 노출되어야 합니다.
이 속성들은 다중 연결 환경에서 오류 처리를 단순화합니다.
경고 메시지: “DB-API extension connection.<exception> used” (DB-API 확장 연결.<exception> 사용됨)
- Cursor.connection
- 이 읽기 전용 속성은 커서가 생성된 Connection 객체에 대한 참조를 반환합니다.
이 속성은 다중 연결 환경에서 다형적인 코드 작성을 단순화합니다.
경고 메시지: “DB-API extension cursor.connection used”
- Cursor.scroll(value [, mode=’relative’ ])
- mode에 따라 결과 집합에서 커서를 새 위치로 스크롤합니다.
mode가
relative(기본값)이면 value는 결과 집합에서 현재 위치에 대한 오프셋으로 간주되고,absolute로 설정되면 value는 절대 목표 위치를 나타냅니다.스크롤 연산이 결과 집합을 벗어나게 되는 경우
IndexError가 발생해야 합니다. 이 경우 커서 위치는 정의되지 않은 상태로 남습니다(커서를 전혀 이동하지 않는 것이 이상적입니다).Note
이 메서드는 가능한 경우 네이티브 스크롤 가능 커서를 사용해야 하며, 그렇지 않으면 전방 전용 스크롤 가능 커서에 대한 에뮬레이션으로 대체해야 합니다. 이 메서드는 특정 연산(예: 역방향 스크롤)이 데이터베이스에서 지원되지 않음을 알리기 위해 NotSupportedError를 발생시킬 수 있습니다.
경고 메시지: “DB-API extension cursor.scroll() used”
- Cursor.messages
- 이것은 Python 리스트 객체로, 인터페이스가 이 커서에 대해 하위 데이터베이스로부터 받는 모든 메시지에 대해 (예외 클래스, 예외 값) 튜플을 이 리스트에 추가합니다.
이 리스트는 과도한 메모리 사용을 피하기 위해 .fetch*() 호출을 제외한 모든 표준 커서 메서드 호출에 의해 (호출을 실행하기 전에) 자동으로 비워지며,
del cursor.messages[:]를 실행하여 비울 수도 있습니다.데이터베이스가 생성한 모든 오류 및 경고 메시지는 이 리스트에 들어가므로, 사용자는 리스트를 확인하여 메서드 호출이 올바르게 동작했는지 검증할 수 있습니다.
이 속성의 목적은 종종 문제를 일으키는 Warning 예외의 필요성을 없애는 것입니다(일부 경고는 실제로 정보 제공 성격만 가집니다).
경고 메시지: “DB-API extension cursor.messages used”
- Connection.messages
- 리스트의 메시지가 연결 지향적이라는 점을 제외하면 Cursor.messages와 같습니다.
이 리스트는 과도한 메모리 사용을 피하기 위해 모든 표준 연결 메서드 호출에 의해 (호출을 실행하기 전에) 자동으로 비워지며,
del connection.messages[:]를 실행하여 비울 수도 있습니다.경고 메시지: “DB-API extension connection.messages used”
- Cursor.next()
- .fetchone()과 같은 의미 체계로 현재 실행 중인 SQL 문에서 다음 행을 반환합니다. Python 2.2 이상 버전에서는 결과 집합이 소진되면
StopIteration예외가 발생합니다. 이전 버전에는StopIteration예외가 없으므로, 이 메서드는 대신IndexError를 발생시켜야 합니다.경고 메시지: “DB-API extension cursor.next() used”
- Cursor.__iter__()
- 커서가 이터레이션 프로토콜과 호환되도록 self를 반환합니다 [8].
경고 메시지: “DB-API extension cursor.__iter__() used”
- Cursor.lastrowid
- 이 읽기 전용 속성은 마지막으로 수정된 행의 rowid를 제공합니다(대부분의 데이터베이스는 단일
INSERT연산이 수행될 때만 rowid를 반환합니다). 연산이 rowid를 설정하지 않거나 데이터베이스가 rowid를 지원하지 않으면, 이 속성은None으로 설정되어야 합니다.마지막으로 실행된 문이 두 개 이상의 행을 수정한 경우, 예를 들어
INSERT를.executemany()와 함께 사용한 경우에는.lastrowid의 의미가 정의되지 않습니다.경고 메시지: “DB-API extension cursor.lastrowid used”
- 연결(Connection).autocommit
- 연결의 자동 커밋 모드를 조회하고 설정하는 속성입니다.
연결이 자동 커밋(비트랜잭션) 모드로 동작하고 있으면
True를 반환합니다. 연결이 수동 커밋(트랜잭션) 모드로 동작하고 있으면False를 반환합니다.이 속성을
True또는False로 설정하면 그에 따라 연결의 모드가 조정됩니다.설정을
True에서False로 변경하면(자동 커밋 비활성화) 데이터베이스는 자동 커밋 모드를 벗어나 새 트랜잭션을 시작합니다.False에서True로 변경하는 경우(자동 커밋 활성화), 보류 중인 트랜잭션을 처리하는 방식에 관한 의미는 데이터베이스에 따라 다릅니다. [12]폐지 안내: 여러 데이터베이스 모듈이 이 속성의 읽기와 쓰기를 모두 구현하고 있지만, 속성에 값을 써서 자동 커밋 모드를 설정하는 방식은 폐지되었습니다. 이 방식은 I/O 및 관련 예외를 일으킬 수 있어 비동기 컨텍스트에서 구현하기 어렵기 때문입니다. [13]
경고 메시지: “DB-API extension connection.autocommit used”
선택적 오류 처리 확장
핵심 DB API 명세는 사용자에게 오류를 보고하기 위해 발생시킬 수 있는 예외 집합만을 도입합니다. 경우에 따라 예외는 프로그램의 흐름을 지나치게 방해하거나 실행 자체를 불가능하게 만들 수도 있습니다.
이러한 경우를 위해, 그리고 데이터베이스를 다룰 때의 오류 처리를 단순화하기 위해, 데이터베이스 모듈 작성자는 사용자가 정의할 수 있는 오류 핸들러를 구현할 수 있습니다. 이 절에서는 이러한 오류 핸들러를 정의하는 표준 방법을 설명합니다.
- Connection.errorhandler, Cursor.errorhandler
- 오류 조건이 발생했을 때 호출할 오류 핸들러를 참조하는 읽기/쓰기 속성입니다.
핸들러는 다음 인자를 받는 Python 호출 가능 객체여야 합니다:
errorhandler(connection, cursor, errorclass, errorvalue)
여기서 connection은 커서가 동작하는 연결에 대한 참조이고, cursor는 커서에 대한 참조(오류가 커서에 해당하지 않는 경우에는
None)이며, errorclass는 errorvalue를 생성 인자로 사용하여 인스턴스화할 오류 클래스입니다.표준 오류 핸들러는 오류 정보를 적절한
.messages속성(Connection.messages 또는 Cursor.messages)에 추가하고, 주어진 errorclass와 errorvalue 매개변수로 정의되는 예외를 발생시켜야 합니다..errorhandler가 설정되어 있지 않으면(속성이None이면), 위에서 설명한 표준 오류 처리 방식이 적용되어야 합니다.경고 메시지: “DB-API extension .errorhandler used”
커서는 생성될 때 자신의 연결 객체로부터 .errorhandler 설정을 상속해야 합니다.
선택적 2단계 커밋 확장
많은 데이터베이스가 2단계 커밋(TPC)을 지원하며, 이를 통해 여러 데이터베이스 연결과 기타 리소스에 걸친 트랜잭션을 관리할 수 있습니다.
데이터베이스 백엔드가 2단계 커밋을 지원하고 데이터베이스 모듈 작성자가 이 기능을 노출하려는 경우, 다음 API를 구현해야 합니다. 데이터베이스 백엔드의 2단계 커밋 지원 여부를 실행 시점에만 확인할 수 있는 경우에는 NotSupportedError를 발생시켜야 합니다.
TPC 트랜잭션 ID
많은 데이터베이스가 XA 명세를 따르므로, 트랜잭션 ID는 다음 세 가지 구성 요소로 이루어집니다:
- 형식 ID
- 전역 트랜잭션 ID
- 브랜치 한정자
특정 전역 트랜잭션에서 처음 두 구성 요소는 모든 리소스에 대해 동일해야 합니다. 전역 트랜잭션에 속한 각 리소스에는 서로 다른 브랜치 한정자가 할당되어야 합니다.
각 구성 요소는 다음 기준을 충족해야 합니다:
- 형식 ID: 음이 아닌 32비트 정수입니다.
- 전역 트랜잭션 ID와 브랜치 한정자: 길이가 64자를 넘지 않는 바이트 문자열입니다.
트랜잭션 ID는 .xid() 연결(Connection) 메서드로 생성합니다:
- .xid(format_id, global_transaction_id, branch_qualifier)
- 이 연결의 .tpc_*() 메서드에 전달하기에 적합한 트랜잭션 ID 객체를 반환합니다.
데이터베이스 연결이 TPC를 지원하지 않으면 NotSupportedError가 발생합니다.
.xid()가 반환하는 객체의 타입은 정의되어 있지 않지만, 세 구성 요소에 접근할 수 있도록 시퀀스 동작을 제공해야 합니다. 규격을 준수하는 데이터베이스 모듈은 사용자 정의 객체 대신 튜플로 트랜잭션 ID를 표현하는 방식을 택할 수 있습니다.
TPC 연결 메서드
- .tpc_begin(xid)
- 주어진 트랜잭션 ID xid로 TPC 트랜잭션을 시작합니다.
이 메서드는 트랜잭션 밖에서 호출해야 합니다(즉, 마지막 .commit() 또는 .rollback() 이후로 아무것도 실행되지 않았어야 합니다).
또한 TPC 트랜잭션 안에서 .commit() 또는 .rollback()을 호출하는 것은 오류입니다. 활성 TPC 트랜잭션 중에 애플리케이션이 .commit() 또는 .rollback()을 호출하면 ProgrammingError가 발생합니다.
데이터베이스 연결이 TPC를 지원하지 않으면 NotSupportedError가 발생합니다.
- .tpc_prepare()
- .tpc_begin()으로 시작한 트랜잭션의 첫 번째 단계를 수행합니다. 이 메서드를 TPC 트랜잭션 밖에서 호출하면 ProgrammingError를 발생시켜야 합니다.
.tpc_prepare()를 호출한 후에는 .tpc_commit() 또는 .tpc_rollback()이 호출될 때까지 어떤 문도 실행할 수 없습니다.
- .tpc_commit([ xid ])
- 인자 없이 호출하면 .tpc_commit()은 이전에 .tpc_prepare()로 준비된 TPC 트랜잭션을 커밋합니다.
.tpc_commit()이 .tpc_prepare()보다 먼저 호출되면 단일 단계 커밋이 수행됩니다. 전역 트랜잭션에 단 하나의 자원만 참여하는 경우 트랜잭션 관리자는 이 방식을 선택할 수 있습니다.
트랜잭션 ID xid와 함께 호출하면 데이터베이스는 해당 트랜잭션을 커밋합니다. 유효하지 않은 트랜잭션 ID가 주어지면 ProgrammingError가 발생합니다. 이 형태는 트랜잭션 외부에서 호출해야 하며, 복구 용도로 사용하기 위한 것입니다.
반환 시점에 TPC 트랜잭션은 종료됩니다.
- .tpc_rollback([ xid ])
- 인자 없이 호출하면 .tpc_rollback()은 TPC 트랜잭션을 롤백합니다. 이 메서드는 .tpc_prepare() 호출 전이나 후에 호출할 수 있습니다.
트랜잭션 ID xid와 함께 호출하면 해당 트랜잭션을 롤백합니다. 유효하지 않은 트랜잭션 ID가 주어지면 ProgrammingError가 발생합니다. 이 형태는 트랜잭션 외부에서 호출해야 하며, 복구 용도로 사용하기 위한 것입니다.
반환 시점에 TPC 트랜잭션은 종료됩니다.
- .tpc_recover()
.tpc_commit(xid)또는.tpc_rollback(xid)에 사용하기에 적합한, 보류 중인 트랜잭션 ID의 리스트를 반환합니다.데이터베이스가 트랜잭션 복구를 지원하지 않는 경우 빈 리스트를 반환하거나 NotSupportedError를 발생시킬 수 있습니다.
자주 묻는 질문
데이터베이스 SIG에는 DB API 명세에 관한 같은 질문이 반복해서 자주 올라옵니다. 이 절에서는 사람들이 명세와 관련하여 종종 겪는 몇 가지 문제를 다룹니다.
질문:
.fetch*()가 반환하는 튜플로 딕셔너리를 어떻게 만들 수 있습니까?
답변:
이 작업을 돕는 헬퍼를 제공하는 기존 도구가 여러 가지 있습니다. 대부분은 커서 속성 .description에 정의된 열 이름을 행 딕셔너리의 키로 사용하는 방식을 취합니다.
DB API 명세를 확장하여 .fetch*() 메서드가 딕셔너리 반환값도 지원하도록 하지 않은 이유는 이 접근 방식에 다음과 같은 몇 가지 단점이 있기 때문입니다:
- 일부 데이터베이스는 대소문자를 구분하는 열 이름을 지원하지 않거나, 열 이름을 모두 소문자 또는 모두 대문자로 자동 변환합니다.
- 쿼리가 생성하는 결과 집합의 열(예: SQL 함수를 사용한 경우)은 테이블 열 이름에 대응하지 않으며, 데이터베이스는 보통 이러한 열의 이름을 데이터베이스마다 크게 다른 방식으로 생성합니다.
그 결과 딕셔너리 키를 통해 열에 접근하는 방식이 데이터베이스마다 달라지므로 이식 가능한 코드를 작성할 수 없게 됩니다.
버전 1.0에서 버전 2.0으로의 주요 변경 사항
Python 데이터베이스 API 2.0은 1.0 버전과 비교하여 몇 가지 주요 변경 사항을 도입합니다. 이러한 변경 사항 중 일부는 기존 DB API 1.0 기반 스크립트를 동작하지 않게 만들기 때문에, 이 변화를 반영하여 메이저 버전 번호를 조정했습니다.
1.0에서 2.0으로의 가장 중요한 변경 사항은 다음과 같습니다:
- 별도의 dbi 모듈이 더 이상 필요하지 않게 되었으며, 그 기능은 모듈 인터페이스 자체에 통합되었습니다.
- 날짜/시간 값을 위한 새 생성자와 Type Objects가 추가되었고,
RAW타입 객체는BINARY로 이름이 바뀌었습니다. 그 결과로 만들어진 집합은 현대 SQL 데이터베이스에서 흔히 볼 수 있는 모든 기본 데이터 타입을 포괄할 것입니다. - 더 나은 데이터베이스 바인딩을 제공하기 위해 새 상수(apilevel, threadsafety, paramstyle)와 메서드(.executemany(), .nextset())가 추가되었습니다.
- 저장 프로시저를 호출하는 데 필요한 .callproc()의 의미가 이제 명확하게 정의되었습니다.
- .execute() 반환값의 정의가 변경되었습니다. 이전에는 반환값이 SQL 문의 유형에 따라 결정되었으나(이는 올바르게 구현하기 어려웠습니다) 이제는 정의되지 않습니다. 대신 더 유연한 .rowcount 속성을 사용하십시오. 모듈은 예전 방식의 반환값을 자유롭게 반환할 수 있지만, 이는 더 이상 명세에서 요구하는 사항이 아니며 데이터베이스 인터페이스에 따라 달라지는 것으로 간주해야 합니다.
- 클래스 기반 Exceptions가 명세에 포함되었습니다. 모듈 구현자는 정의된 예외 클래스를 서브클래싱하여 이 명세에서 정의한 예외 구조를 자유롭게 확장할 수 있습니다.
DB API 2.0 명세 발표 이후 추가된 사항:
- 핵심 기능 집합에 대한 추가적인 선택적 DB API 확장이 명세되었습니다.
미해결 문제
2.0 버전 명세는 1.0 버전에서 미해결로 남아 있던 많은 질문을 명확히 하지만, 향후 버전에서 다루어야 할 문제가 여전히 남아 있습니다:
- 새 결과 집합을 사용할 수 있는 경우에 대해 .nextset()의 유용한 반환값을 정의하십시오.
- 손실 없는 화폐 및 십진수 교환 형식으로 사용할 수 있도록 decimal 모듈의
Decimal객체를 통합하십시오.
각주
감사의 글
2001년에 Python 데이터베이스 API 명세 2.0을 원래의 HTML 형식에서 PEP 형식으로 변환해 준 Andrew Kuchling에게 깊이 감사드립니다.
2008년에 2단계 커밋 API 확장의 표준화로 이어진 논의를 이끌어 준 James Henstridge에게 깊이 감사드립니다.
2012년에 이 명세를 텍스트 PEP 형식에서 ReST PEP 형식으로 변환하여 여러 부분에 링크할 수 있게 해 준 Daniele Varrazzo에게 깊이 감사드립니다.
Copyright
This document has been placed in the Public Domain.