Skip to content

Latest commit

 

History

History
executable file
·
1029 lines (643 loc) · 56.1 KB

pep-0249.rst

File metadata and controls

executable file
·
1029 lines (643 loc) · 56.1 KB

PEP-0249: Python データベース API 仕様 v2.0

PEP:249
Title:Python Database API Specification v2.0
Version: 1.1
Author: db-sig@python.org (Python Database SIG)
Editor:mal@lemburg.com (Marc-Andre Lemburg)
Status: Final
Type:Informational
Replaces:248
Release-Date:07 Apr 1999

Note

この和訳は、Python ドキュメント日本語化プロジェクトの成果物をもとに、文字コードや書式を修正したものです。

はじめに

この API は、様々なデータベースにアクセスするための Python モジュールが、互いに似通ったインタフェースとなるよう促すために定義されています。 そうすることで一貫性が生まれ、モジュールをより理解しやすくなるとともに、データベースにアクセスするコードを他のデータベースにも対応しやすくして、Python からより多くのデータベースを扱えるようになります。

この仕様に関するコメントや質問は、 データベースと Python の連携 SIG にしてください。

データベースと Python を連携させる方法や、データベースの操作に使えるパッケージの詳細は、 データベース関連の話題のガイド を参照してください。

このドキュメントでは、Python データベース API 仕様 2.0 (Python Database API Specification 2.0) と、共通のオプション拡張について述べています。以前の API バージョン 1.0 は、 PEP 248 で参照できます。これからパッケージを作成する開発者は、この仕様に基づいて新たなインタフェースを開発するよう勧めます。

モジュールインタフェース

コンストラクタ

データベースへのアクセスは 接続オブジェクト を介して行います。 モジュールは、接続オブジェクトを生成するために、以下のコンストラクタを提供せねばなりません:

connect(parameters...)
データベースへの接続を作成するコンストラクタです。接続オブジェクトを返します。データベースに応じて、いくつかのパラメタをとります。 [1]

グローバル変数

モジュールレベルで、以下のグローバル変数を定義せねばなりません:

apilevel

サポートされている DB API のレベルを示す定数文字列です。

現在のところ、'1.0' および '2.0' のみが使えます。 この値がない場合は、DB-API 1.0 準拠とみなします。

threadsafety

整数の定数で、インタフェースがサポートしているスレッド安全性のレベルを示します。取りえる値は以下の通りです:

意味
0 スレッド間でモジュールを共有することはできません。
1 モジュールは共有できますが、接続は共有できません。
2 モジュールと接続を共有できます。
3 モジュール、接続に加えて、カーソルも共有できます。

ここでの「共有」とは、二つのスレッド間が一つのリソースを利用でき、その際にリソースのロック制御を行うために排他制御セマフォを使ったラップ処理などが必要ないことを意味します。 ただし、排他制御を用いてアクセスを管理したからといっても、外部リソースをスレッド安全にできるとは限らないので注意してください。 というのも、件のリソースがグローバル変数や、制御不能な外部のソースに依存している可能性があるからです。

paramstyle

文字列の定数で、このインタフェースで使われるパラメタマーカの書式を示します。とりえる値は以下のとおりです [2] :

paramstyle 説明
qmark クエスチョンマーク。例: ...WHERE name=?
numeric 数字、実引数の順番。例: ...WHERE name=:1
named 名前付き形式。例: ...WHERE name=:name
format ANSI C の printf 形式。例: ...WHERE name=%s
pyformat Python の拡張形式。例: ...WHERE name=%(name)s

例外

モジュールは、全てのエラー情報や例外を、以下の例外のサブクラスを介して扱えねばなりません。

Warning
データの挿入中に欠落が生じた場合など、重要な警告時に発行される例外です。 この例外は (exceptions モジュールで定義されている) Python の StandardError 例外のサブクラスにせねばなりません。
Error
上記以外の全ての例外に対する基底クラスです。 このクラスを使えば、全てのエラーを一つの except 文でキャッチできるものとします。 警告はエラーとみなさないので、このクラスをベースにしてはなりません。 Python の StandardError のサブクラスにせねばなりません。
InterfaceError
データベース自体ではなく、データベースインタフェースに関係するエラーの際に送出する例外です。 Error のサブクラスにせねばなりません。
DatabaseError
データベースに関係するエラーの際で送出する例外です。 Error のサブクラスにせねばなりません。
DataError
ゼロによる除算、数値の定義域超えなど、処理済のデータに問題が生じたことに起因するエラーの際に送出する例外です。 DatabaseError のサブクラスにせねばなりません。
OperationalError
データベースの操作に関連したエラーで、プログラマがコントロールしきれない状況に対して送出される例外です。 例えば、予期しない接続遮断の発生、データソース名が見つからなかった、トランザクションが処理できなかった、処理中に発生したメモリアロケーションエラーなどです。 DatabaseError のサブクラスにせねばなりません。
IntegrityError
外部キーチェックの失敗のような、データベースの完全性が影響する場合に送出するエラーです。 DatabaseError のサブクラスにせねばなりません。
InternalError
カーソルが有効でなくなっている、トランザクションの同期がとれない、といった、データベースが内部エラーを起こした場合に送出する例外です。 DatabaseError のサブクラスにせねばなりません。
ProgrammingError
テーブルが見つからない、テーブルがすでに存在する、SQL 文の構文エラー、引数の数が間違って指定されているといった、SQL 文のエラーに対して送出する例外です。 DatabaseError のサブクラスにせねばなりません。
NotSupportedError
トランザクションをサポートしないか無効になっている接続に対して .rollback() を要求したときのように、データベースがサポートしていないメソッドやデータベース API を使った時に送出する例外です。 DatabaseError のサブクラスにせねばなりません。

以下に例外クラスの継承関係を示します:

StandardError
|__Warning
|__Error
   |__InterfaceError
   |__DatabaseError
      |__DataError
      |__OperationalError
      |__IntegrityError
      |__InternalError
      |__ProgrammingError
      |__NotSupportedError

Note

例外の値について、特に規定はありません。 とはいえ、おのおのの例外は、何がまずいのかについてユーザに十分なヒントを与えるようにしてください。

コネクションオブジェクト

コネクションオブジェクトは以下のメソッドに応答しなければなりません。

コネクションオブジェクトのメソッド

.close()
(__del__ を呼び出したときと違って) 接続を即座に切断します。 その後、接続は使用できなくなります。 切断された接続に対して何らかの操作を試みると、 Error (またはそのサブクラスの) 例外を送出します。 この接続を利用しようとする全てのカーソルに対しても同様です。 前もって更新の commit を行わずに接続の切断をすると、暗黙的にロールバックを実行します。
.commit()
処理中のトランザクションをデータベースに commit します。 データベースが自動コミット (auto-commit) 機能をサポートしている場合、自動 commit 機能は初期状態で必ずオフになっています。 自動コミットをオンに戻すインタフェースメソッドが提供されている場合もあります。 トランザクションをサポートしないデータベースモジュールでは、このメソッドを何もしない (void functionality) メソッドとして実装すべきです。
.rollback()

全てのデータベースがトランザクションをサポートしているわけではないので、このメソッドはオプションです。 [3]

データベースがトランザクション機能を提供している場合、このメソッドは、処理中のトランザクションすべてについて、トランザクション開始時の状態にデータベースをロールバック (roll back) させます。 前もって更新の commit を行わずに接続の切断をすると、暗黙的にロールバックを実行します。

.cursor()
現在の接続を使って、新たなカーソルオブジェクトを返します。 データベースが直接的なカーソルの概念を提供しない場合、モジュールは何らかの方法を使って、本仕様で必要な範囲でカーソルをエミュレートせねばなりません。 [4]

カーソルオブジェクト

カーソルオブジェクトは、個々のフェッチ (fetch) 操作のコンテキストの管理に用いる、いわゆるデータベースカーソル (database cursor) を表現します。 同じ接続から生成されたカーソルには、区別がありません。 すなわち、あるカーソルを介してデータベースを変更すると、その結果は即座に他のカーソルから見えます。 異なる接続から生成したカーソルは、トランザクションのサポートをどう実装しているかによって、区別できてもできなくてもかまいません。 (コネクションオブジェクトの rollback() および commit() メソッドも参照してください)。

カーソルオブジェクトは以下のメソッドおよび属性参照に対応せねばなりません:

カーソルの属性

.description

読み出し専用の属性で、配列です。配列の各要素は 7 要素の配列です。 各要素の配列には、クエリ結果の個々のカラム情報に関する記述: (name, type_code, display_size, internal_size, precision, scale, null_ok) が入っています。 先頭の二つの要素 (nametype_code) は必須の要素です。 その他はオプションで、有意な値を提供できない場合には None に設定せねばなりません。

応答行を返さない操作や、カーソルに executeXXX() メソッドを介した操作を行っていない場合には、この属性は None になります。

type_code は、下節で定義されている型オブジェクト (Type Object) との比較で解釈できます。

.rowcount

読み出し専用の属性で、最後に実行した executeXXX() によって ('select' のような DQL 文で) 生成された行や、 ('update' や 'insert' のような DML 文が) 影響を及ぼした行数を示します。

現在のカーソルがまだ executeXXX() を実行していない場合や、データベースインタフェースから最後に行った操作の結果行数を決定できない場合には、この属性は -1 となります。 [7]

Note: 将来のバージョンの DB API 使用では、後者のケースについては -1 でなく None を返すように再定義するかもしれません。

.callproc(procname [,parameters])

(全てのデータベースがストアドプロシジャ (stored procedure) をサポートしているわけではないので、このメソッドはオプションです。 [3])

ストアドプロシジャを名前で指定して呼び出します。 パラメタの配列には、プロシジャが要求する各引数をそれぞれ一つづつ入れねばなりません。 呼び出し結果は、 parameters に指定した配列のコピーを、ストアドプロシジャによって変更したものです。 このコピーでは、入力専用のパラメタはそのまま残され、出力および入出力用のパラメタが必要に応じて新たな値で置き換わっています。

プロシジャは出力として処理結果を出すことがあります。この場合、処理結果は標準的な .fetch*() メソッドを介して取得できねばなりません。

.close()
(__del__ を呼び出したときと違って) カーソルを即座に閉じます。その後、当該カーソルは利用できなくなります。 閉じたカーソルに対してどのような操作を行っても、Error (またはそのサブクラスの) 例外を送出します。
.execute(operation [,parameters])

データベース操作 (クエリやコマンド) を準備して実行します。 パラメタの指定方法は、配列でも辞書でもかまいません。 指定した値は、 operation 内の変数に割り当てられます。 変数はデータベース固有の表記法で指定されます (詳細はモジュールの paramstyle 属性を参照してください)。 [5]

カーソルは、 operation への参照を維持してよく、同じ operation が呼び出されたときに、最適化を行ってかまいません。 この仕様は、同じデータベース操作を、パラメタを変えて何度も繰り返し使うようなアルゴリズムに対してきわめて効果的です。

operation を再利用する際、最大限の効果を得るためには、 setinputsizes() メソッドを使って、前もってパラメタの型とサイズを指定するのがベストです。 実際に与えるパラメタが、前もって指定した情報と一致しなくても、不正としてはなりません。実装側で、効率を犠牲にして補わねばなりません。

単一の operation で複数の行を INSERT するような場合のために、パラメタをタプルからなるリストで指定できるようにしてもかまいません。 ただし、この仕様は撤廃されており、今後は executemany() を使うべきです。

戻り値の指定はありません。

.executemany(operation, seq_of_parameters)

データベース操作 (クエリまたはコマンド) を準備し、 seq_of_parameters 内にある全てのパラメタ配列やパラメタ辞書について実行します。

モジュールの実装は、 execute() メソッドの複数回呼び出しにしても、アレイ操作を使ってデータベースに一括処理させる方法でもかまいません。

このメソッドを、一つまたはそれ以上の結果セットを生成するような operation に対して使った場合の動作は未定義です。 操作を実行したときに結果セットが返った場合、(必須ではありませんが) 例外を送出してもかまいません。

その他、 execute() と同じコメントが当てはまります。

戻り値の指定はありません。

.fetchone()

クエリ結果セットの次の行を取得し、配列を返します。 次のデータがなかった場合には None を返します。 [6]

直前に呼び出した executeXXX() が何も結果を生成しなかった場合や、まだ何も実行していないのにこのメソッドを呼んだ場合には、 Error (またはそのサブクラスの) 例外を送出します。

fetchmany([size=cursor.arraysize])

クエリ結果の次の一連の行集合を取得し、各行のデータ配列からなる配列を (タプルのリストなどで) 返します。取得できるデータがないときには空の配列を返します。

一度の呼び出しあたりの行数はパラメタで指定します。 パラメタ指定のない場合、カーソルの arraysize から、取得する行数を決定します。 このメソッドは、size パラメタに指定しただけの行数を取得しようと試みなければなりません。 指定した行数を取得できない場合には、より少ない行数を返してもかまいません。

直前に呼び出した executeXXX() が何も結果セットを生成しなかった場合や、まだ何も呼び出しを行っていない場合には、 Error (またはそのサブクラスの) 例外を送出します。

size パラメタを指定するときは、パフォーマンスを考慮する必要があるので注意してください。 最適なパフォーマンスを実現するには、通常は arraysize 属性を使うのがベストです。 size パラメタを指定する場合は、 fetchmany() の呼び出しごとに同じ size を使うのがベストです。

.fetchall()

全ての (カーソルに残っている) クエリ結果行を取得し、配列の配列 (タプルからなるリスト) を返します。 カーソルの arraysize 属性は、この操作のパフォーマンスに影響を及ぼすので注意してください。

直前に呼び出した executeXXX() が何も結果セットを生成しなかった場合や、まだ何も呼び出しを行っていない場合には、 Error (またはそのサブクラスの) 例外を送出します。

.nextset()

(全てのデータベースが結果セットの多重化をサポートしているわけではないので、このメソッドはオプションです。 [3])

このメソッドは、カーソルを次の取得可能な結果セットに移動させます。 その際現在の結果セット上の残りのデータは無視されます。

次の結果セットがない場合、このメソッドは None を返します。 そうでない場合には真値を返し、以降の .fetch*() メソッドは次の結果セットから行データを返すことになります。

直前に呼び出した executeXXX() が何も結果セットを生成しなかったり、まだ何も呼び出しを行っていない場合には、 Error (またはそのサブクラスの) 例外を送出します。

.arraysize

読み書き可能な属性値で、 fetchmany() で一度に取得可能な行数を指定します。デフォルトの値は 1 で、これは一度に一つの行しか取得できないことを意味します。

fetchmany() メソッドに関しては、実装上はこの値を参照せねばなりませんが、データベースとは一度に一行づつやりとりをしてもかまいません。 この値を executemany() の実装で使ってもかまいません。

.setinputsizes(sizes)

executeXXX() の呼び出し前に、データベース操作パラメタとして使うメモリ領域を予約するために使われます。

sizes は配列で指定します -- 各入力パラメタあたり一要素です。 各要素は入力パラメタの型に対応する型オブジェクト (Type Object) か、文字列パラメタの場合にはその最大長を指定する整数値です。 要素を None にすると、そのカラムに対してはメモリ予約を行いません (これは巨大な入力に対するメモリ予約を避けるのに有効です)。

このメソッドは executeXXX() メソッドを呼び出す前に使います。

実装では、このメソッドを何も処理しないようにしてよく、ユーザはこのメソッドを使わなくてもかまいません。

.setoutputsize(size[,column])

巨大なカラムデータ (LONG, BLOB 等) を fetch するためのカラムバッファサイズを設定します。 カラムは結果行の配列におけるインデクスで指定します。 カラムの指定を省略すると、カーソル中の全ての巨大データカラムに対してデフォルトサイズを設定します。

このメソッドは executeXXX() メソッドを呼び出す前に使わなければなりません。

実装では、このメソッドを何も処理しないようにしてよく、ユーザはこのメソッドを使わなくてもかまいません。

型オブジェクト (Type Object) とコンストラクタ

多くのデータベースでは、データベースを操作する時の入力パラメタに、決まった形式でデータを指定せねばなりません。 例えば、入力が DATE 型のカラム用なら、データベースごとに指定の日付フォーマットにせねばなりません。 "Row ID" カラムや、巨大なバイナリ要素 (blob や RAW カラム) にも、同様の問題があります。 これは Python では問題で、というのも executeXXX() メソッドのパラメタには型の制約がないからです。 データベースモジュールとしては、引数に Python の文字列オブジェクトが渡されてきても、データを単なる CHAR カラムにすればよいのか、 BINARY なのか、はたまた DATE が適切なのか判らないのです。

この問題を解決するために、 DB-API モジュールは、特殊な値を保持できるオブジェクト(型オブジェクト)を生成するためのコンストラクタを提供せねばなりません。 型オブジェクトをカーソルメソッドに渡すと、モジュールは型オブジェクトを使って、入力パラメタの適切な型を検出し、それに従った値をデータベース操作の入力にします。

クエリの結果を取得する際は、 カーソル オブジェクトの .description 属性が、各カラムに関する情報を返します。 description の type_code は、以下に示す型オブジェクトのいずれか一つに対応していなければなりません。 一方、一つの型オブジェクトが、複数の type_code に結びついていてもかまいません。(例えば、 DATETIME が date, time および timestamp のカラムに対応している場合があります。詳細は、 実装のヒント を参照してください)。

モジュールは、以下のコンストラクタとシングルトンを公開します:

Date(year,month,day)

日付値を保持するオブジェクトを生成します。

Time(hour,minute,second)

時刻値を保持するオブジェクトを生成します。

Timestamp(year, month, day, hour, minute, second)

タイムスタンプを保持するオブジェクトを生成します。

DateFromTicks(ticks)

ticks の指定値を epoch からの経過秒数としたときの日付値を保持するオブジェクトを生成します (詳細は、 標準の Python time モジュール のドキュメントを参照してください)。

TimeFromTicks(ticks)

ticks の指定値を epoch からの経過秒数としたときの時刻値を保持するオブジェクトを生成します(詳細は、 標準の Python time モジュール のドキュメントを参照してください)。

TimestampFromTicks(ticks)

ticks の指定値を epoch からの経過秒数としたときのタイムスタンプ値を保持するオブジェクトを生成します(詳細は、 標準の Python time モジュール のドキュメントを参照してください)。

Binary(string)

バイナリ形式の (長い) 文字列を保持するオブジェクトを生成します。

STRING

(CHAR 型などの) 文字列系のカラムの表現に使われます。

BINARY

(長い) バイナリデータのカラム (LONG, RAW, BLOB 等) の表現に使われます。

NUMBER

数値カラムの表現に使われます。

DATETIME

date/time カラムの表現に使われます。

ROWID

"Row ID" カラムの表現に使われます。

SQL の NULL 値は、入力側、出力側のどちらでも、 Python のシングルトン None で表現します。

Note

古い Unix の ticks は表現できる日付の範囲が限られているので、データベースインタフェースに用いるとトラブルを引き起こすことがあります。

モジュール作者のための実装ヒント

  • date/time オブジェクトは Python の datetime モジュール のオブジェクト (Python 2.3 から、 C API は 2.4 から) か、 mxDateTime パッケージ (Python 1.5.2 から利用可能) で実装できます。いずれも、必要なコンストラクタとメソッドを Python および C の両方のレベルで提供しています。

  • Python 2.3 からは、モジュール作者は標準の datetime モジュールで 定義されているオブジェクト型を使うことができます。しかし、この方 法では mxDateTime が提供しているような C API を公開していません。 従って、C ベースのデータベースモジュールの組み込みがより難しいこ とになります。

  • 以下は、 Unix の ticks を使う date/time コンストラクタの例です。 汎用のコンストラクタに処理を移譲しています:

    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 オブジェクトに対するオブジェクト型としては、Python 1.5.2 以降で利用できる buffer 型を推奨します。 詳細は Python ドキュメンテーションを参照してください。 C インタフェースに関する情報は、 Python ソース配布物中の Include/bufferobject.h および Objects/bufferobject.c を参照してください。

  • 以下の Python クラスのように実装すると、 .description 内の type_code フィールドが複数の型オブジェクトに対応している場合でも動作します:

    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 コードの一部を以下に示します:

    import exceptions
    
    class Error(exceptions.StandardError):
        pass
    
    class Warning(exceptions.StandardError):
        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 の warning フレームワークを介して警告を出し、これらの拡張がプログラマからオプションで見えるようにする提案があります。 この機能を有用にするためには、警告メッセージを標準化して、メッセージのマスクを可能にせねばなりません。 こうした標準メッセージは以下の 警告メッセージ で触れています。

Cursor.rownumber

読み出し専用の属性で、結果セット中のカーソルの位置をゼロから始まるインデクスで返すか、インデクスが決定できない場合には None を返さねばなりません。

インデクスは配列 (結果セット) 中のカーソルの位置を表すインデクスです。 次に fetch 操作を行なうと、配列中の .rownumber 番目の行データを返します。

警告メッセージ: "DB-API extension cursor.rownumber used"

Connection.Error, Connection.ProgrammingError など

DB API 標準で定義している全例外は、 (モジュールのスコープだけでなく) コネクションオブジェクトの属性としてアクセスできねばなりません。

Connection の属性になっていると、複数の接続を扱う環境でエラー処理が簡単になります。

警告メッセージ: "DB-API extension connection.<exception> used"

Cursor.connection

読み出し専用の属性で、カーソルの生成に使ったコネクションオブジェクトへの参照を返します。

この属性値は、複数の接続を扱う環境で、コードの多態化 (polymorph) を簡単に実装できるようにします。

警告メッセージ: "DB-API extension cursor.connection used"

Cursor.scroll(value[,mode='relative'])

結果セット中のカーソルをスクロールさせ、mode からの相対で表した新たな位置に設定します。

mode が relative (デフォルト値) の場合、 value は結果セット中のカーソルの現在位置からの相対です。 absolute の場合には、 value は絶対位置です。

スクロール操作によって結果セットの外側に出てしまうような場合、 IndexError を送出せねばなりません。その場合のカーソル位置は未定義です (全くカーソルを動かさないのが理想的でしょう)。

Note

このメソッドは、ネイティブのカーソルがスクロール可能ならそれを使い、そうでないときは、先送りスクロールのみ (forward-only) のカーソルエミュレーションに切り替えてください。 データベースがスクロールの一部の機能 (巻き戻しスクロールなど) をサポートしていない場合は、 NotSupportedError を送出してかまいません。

警告メッセージ: "DB-API extension cursor.scroll() used"

Cursor.messages

Python のリストオブジェクトです。データベースインタフェースは、このカーソルのアクセス先のデータベースから受信した全てのメッセージを、このリストに (exception class, exception value) の形式で追加します。

このリストは、メモリの使いすぎを防ぐため、 .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()

現在実行中の SQL 文について、結果セットの次の行を取得して返します。 .fetchone() と同じ意味です。 Python バージョン 2.2 以降では、結果セットを読み尽くすと StopIteration 例外を送出します。 それ以前のバージョンには StopIteration 例外がないので、代わりに IndexError を送出せねばなりません。

警告メッセージ: "DB-API extension cursor.next() used"

Cursor.__iter__()

カーソルをイテレーションプロトコル互換にするために、自分自身を返します。 [8]

警告メッセージ: "DB-API extension cursor.__iter__() used"

Cursor.lastrowid

読み出し専用の属性です。 最後に変更が行われた行の rowid を返します (大半のデータベースは、 INSERT 操作が一度行われたときのみ、 rowid を返します)。 操作によって rowid が設定されなかった場合や、データベースが rowid をサポートしていない場合、この属性値を None にせねばなりません。

INSERT.executemany() で用いたときのように、最後に実行した文が二つ以上の行を変更した場合の .lastrowid は未定義です。

警告メッセージ: "DB-API extension cursor.lastrowid used"

オプションのエラー処理拡張

コアの DB API 仕様は、一連の例外を定義して、ユーザにエラーを報告したいときに送出できるようにしています。 しかし、状況によっては、例外を使うとプログラムの流れをとめてしまったり、実行不能にしてしまったりします。

そうした場合に対処し、かつ、データベースに関わるエラー処理を簡単にするため、データベースモジュールの作者は、ユーザにエラーハンドラを実装できる仕組みを提供してもかまいません。 この節では、エラーハンドラを定義する標準的な方法について述べます。

Connection.errorhandler, Cursor.errorhandler

エラー条件に遭遇した際に呼び出されるエラーハンドラを参照する、 読み書き可能な属性です。

このハンドラは Python の呼び出し可能オブジェクトで、以下のように引数をとらねばなりません:

errorhandler(connection, cursor, errorclass, errorvalue)

ここで、 connection はカーソルが操作しているコネクションオブジェクトへの参照、 cursor は カーソルへの参照 (エラーがカーソルに関係しない場合には None)、 errorclass はエラーオブジェクトを生成するのに使ったクラス、 errorvalue はエラーオブジェクトを生成するときに使った引数です。

標準のエラーハンドラは、エラー情報を適切なオブジェクトの .message 属性 (Connection.messages または Cursor.messages) に追加せねばなりません。 また、 errorclasserrorvalue パラメタに従って例外を送出せねばなりません。

.errorhandler が設定されていない場合 (属性が None の場合) は、上で説明したような標準のエラー処理手順を適用せねばなりません。

警告メッセージ: "DB-API extension .errorhandler used"

コネクションオブジェクトからカーソルを生成する際、カーソルに .errorhandler の設定を受け継がせてください。

オプションの2フェーズコミット (TPC) 拡張

多くのデータベースが、2フェーズコミット (TPC: Two-Phase Commit) をサポートしています。 2フェーズコミットは、複数のデータベース接続や、他のリソースにまたがるようなトランザクションを扱えるようにします。

データベースバックエンドが2フェーズコミットをサポートし、かつデータベースモジュールの作者がこの機能を公開したいなら、以下のAPIを実装してください。 データベースバックエンドが2フェーズコミットをサポートしているかどうかを実行時に判定する必要があり、サポートしない場合には、 NotSupportedError を送出せねばなりません。

TPCトランザクションID

X/Open XA 仕様に準拠している多くのデータベースにならって、トランザクション ID は3つのコンポーネントで成り立っています:

  • フォーマット ID (format ID)
  • グローバルトランザクション ID (global transaction ID)
  • ブランチ修飾子 (branch qualifier)

一つのグローバルトランザクションの中で、上の最初の二つのコンポーネントは、全リソース間で同じでなければなりません。 グローバルトランザクション中の各リソースには、それぞれ別々のブランチ修飾子を結び付けねばなりません。

それぞれのコンポーネントは、以下の要件を満たさねばなりません:

  • フォーマット ID: 非負の 32 ビットの整数
  • グローバルトランザクション ID とブランチ修飾子: 64文字を超えないバイト文字列

トランザクション ID はコネクションのメソッド .xid() で生成します:

.xid(format_id, global_transaction_id, branch_qualifier)

トランザクション ID オブジェクトを返します。 このトランザクション ID は、コネクションの .tpc_*() メソッドに渡せます。

データベース接続が TPC をサポートしない場合は、 NotSupportedError を送出します。

.xid() の返すオブジェクトの型に特に指定はありませんが、3つのコンポーネントにアクセスできるよう、配列のような振る舞いをせねばなりません。 現在 TPC に対応しているデータベースは、トランザクション ID の表現に、カスタムオブジェクトではなく、タプルを使っています。

TPC コネクションメソッド

.tpc_begin(xid)
xid に指定したトランザクション IDで TPC トランザクションを開始します。

このメソッドはトランザクションの外 (たとえば、最後の .commit().rollback() の呼び出し以降、一切 .execute() 実行していない状態です) で呼び出さねばなりません。

さらに、TPC トランザクション中で .commit().rollback() を呼んだ場合もエラーになります。 TPC トランザクション中にアプリケーションが .commit().rollback() を呼んだ場合は、 ProgrammingError を送出せねばなりません。

データベース接続が TPC をサポートしない場合は、 NotSupportedError を送出せねばなりません。

.tpc_prepare()

.tpc_begin() で開始したトランザクションの第一フェーズを実行します。 TPC トランザクションの外でこのメソッドを呼んだ場合は、 ProgrammingError を送出せねばなりません。

.tpc_prepare() を呼び出した後は、 .tpc_commit() または .tpc_rollback() を呼び出すまで .execute() を実行できません。

.tpc_commit([ xid ])

引数なしで呼び出すと、 .tpc_commit() は直前の .tpc_prepare() で準備した TPC トランザクションをコミットします。

.tpc_prepare() よりも前に .tpc_commit() を呼び出すと、シングルフェーズのコミットを実行します。 トランザクションマネジャは、グローバルトランザクションに参加しているリソースが一つの場合にのみ、この動作を許可してください。

トランザクションID xid を指定して呼び出すと、指定のトランザクションをコミットします。 無効なトランザクション ID を指定すると、 ProgrammingError を送出します。 xid 指定の形式は復旧用で、トランザクションの外で実行せねばなりません。

このメソッドが処理を戻したら、TPC トランザクションは終了します。

.tpc_rollback([ xid ])

引数なしで呼び出すと、 .tpc_rollback() は直前の .tpc_prepare() で準備した TPC トランザクションをコミットします。

トランザクションID xid を指定して呼び出すと、指定のトランザクションをロールバックします。 無効なトランザクション ID を指定すると、 ProgrammingError を送出します。 xid 指定の形式は復旧用で、トランザクションの外で実行せねばなりません。

このメソッドが処理を戻したら、TPC トランザクションは終了します。

.tpc_recover()

.tpc_commit(xid).tpc_rollback(xid) で復旧するために、解決待ちのトランザクション ID を返します。

データベースがトランザクション復旧をサポートしていない場合は、空のリストを返すか、 NotSupportedError を送出してください。

よくある質問

データベース SIG には、 DB API 仕様についての質問が何度も繰り返し寄せられます。 この節では、ユーザがこの仕様についてたびたび抱く疑問を扱って言います。

質問:

.fetch*() の返すタプルから辞書を構築するには、どうすればよいですか。

回答:

この手の操作に対するヘルパー機能を提供するツールがいくつかあります。 そうしたツールのほとんどが、カーソルの .description 属性の中で定義されているカラム名を使って、レコード行単位で辞書を構築しています。

.fetch*(() メソッドの戻り値として辞書をサポートするように DB-API を拡張しないのは、以下のような問題点があるからです:
  • データベースによっては、カラム名の大小文字を区別しなかったり、カラム名を全て大文字や全て小文字の文字列に変換するので、辞書のキーが一意になりません。。
  • クエリの中で (SQL の関数などで) で生成した結果セットのカラムは、テーブルのカラム名と直接対応しておらず、データベースがこうしたカラムに名前を付ける方法がデータベースごとに独特で、一意になりません。

結論として、辞書キーを介してカラムにアクセスする方法が、データベース間で異なったものとなってしまうので、可搬性のあるコードを書けなくなってしまいます。

1.0 から 2.0 への主要な変更

DB-API 2.0 には、1.0 から大きな変更がいくつか追加されています。 変更のうちいくつかは、既存の DB API 1.0 用に書いたスクリプトを動かなくしてしまうので、変更を反映させるためにバージョン番号が変えられました。

1.0 から 2.0 への最も重要な変更は、以下のとおりです:

  • 別途 dbi モジュールを用意する必要がなくなり、 dbi の機能はモジュールインタフェース自体に統合されました。
  • date/time 値用の新たなコンストラクタと 型オブジェクト が追加されました。また、 RAW 型オブジェクトは BINARY に名前を変更しました。 これにより、今時の SQL データベースで広く使われている基本データ型すべてをカバーするようなセットになったはずです。
  • 新たな定数 (apilevel, threadsafety, paramstyle) とメソッド (executemany(), nextset()) が追加され、データベースとの結びつきが改善されました。
  • ストアドプロシジャを呼び出すために必要な .callproc() の動作が明確に定義されました。
  • .execute() の戻り値の定義が変更されました。 以前は、戻り値は SQL 文の型に基づいて決定されていました (そのため、正しく実装するのが困難でした)。現仕様では戻り値は未定義になり、結果の行数の取得にはよりフレキシブルな .rowcount 属性をいます。 モジュールの実装で、古い形式の戻り値を返させるのは自由ですが、もはや必須の仕様ではなく、データベースインタフェース固有の仕様として扱います。
  • クラスベースの例外が仕様に組み込まれました。 モジュールの開発者は、この仕様の例外クラスのレイアウトをサブクラスで自由に拡張できます。

以下は DB API 2.0 公開後に追加された仕様です:

  • コア機能セットに対するオプションの DB API 拡張の追加項目が仕 様化されました。

未解決の問題

バージョン 2.0 仕様は、1.0 で未解決となっていた多くの課題を解決しましたが、今後のバージョンで解決するべき問題がいくつか残っています。

  • .nextset() の戻り値として、次の結果セットを得られる時に返すべき有用な値を決めねばなりません。
  • 通貨や 10 進小数を相互にロスレス変換できるフォーマットとして、 decimal モジュールDecimal オブジェクトを組み込まねばなりません。

付記

[1]

ガイドラインとして、connection のコンストラクタに指定するパラメタは、直感的に利用できるよう、キーワード引数として実装せねばなりません。また、パラメタの順番は以下のようにしてください:

パラメタ 意味
dsn データソース名の文字列
user ユーザ名の文字列 (オプション)
password パスワード文字列 (オプション)
host ホスト名 (オプション)
database データベース名 (オプション)

例えば、 connect メソッドは以下のようになります:

connect(dsn='myhost:MYDB',user='guido',password='234$')
[2]明瞭性やフレキシビリティの高い numeric, named, pyformat を使うようにしてください。
[3](1, 2, 3)

メソッドの実行に必要な機能をデータベースがサポートしていない場合は、メソッドが使われたときに何らかの例外を送出させてください。

推奨するアプローチは、このメソッドを全く実装しないという方法です。 そうすれば、メソッドが呼ばれたときに、 Pythonが AttributeError を送出します。 このアプローチには、開発者が hasattr() を使ってロールバック機能の有無を調べられるという利点もあります。

動的にインタフェースを構成するようなモジュールの場合、ロールバック動的に利用可能にする実装が適切でないこともあります。 そういうときは、メソッドが呼ばれたときに NotSupportedError を送出して、ロールバックの機能がないことを示してください。

[4]このメソッドに文字列引数を持たせて、名前つきカーソルをサポートしてもかまいません。 ただし、この機能は .fetch*() メソッドの動作の定義を難解にするため、本仕様の一部ではありません。
[5]

モジュールを実装するときは、 parameters オブジェクトの __getitem__ メソッドを使って、インデクス (整数) またはキー (文字列) とパラメタの値を対応付けることになるでしょう。それによって、配列とマップ型の両方を入力として使えます。

割り当てる (bind) とは、入力値をデータベースの実行バッファに結びつけるプロセスを表します。 この定義は、事実上、入力値がデータベース操作時の値として直接使われることを意味します。 データベースクライアントは、値を必ずしもエスケープして使うとは限らないので、値は実際のデータ—ベースの値と同じにせねばなりません。

[6]インタフェースは、この操作を、アレイを使ったり、何らかの最適化を行って実装するかもしれないので注意してください。このメソッドを呼んだ際、カーソルが一行しかレコードを読まないという保証はありません。
[7]rowcount 属性の値は、値を動的に更新するような実装にしてもかまいません。このふるまいは、最初に .fetch*() メソッドを呼び出すまで、意味のある rowcount 値をに返さないデータベースの場合に便利です。
[8]実装のヒント: Python の C 拡張では、カーソルオブジェクトは .__iter__() メソッドの代わり、に tp_iter スロットを実装することになるでしょう。
[9]「影響を受けた行数 (number of affected rows) 」は、通常、データベースカーソルで直前に実行した操作により、削除・更新・挿入された行数を指します。 ほとんどのデータベースは、 WHERE 節で絞り込んだ後の行数を返します。 データベースによっては、 UPDATE のときの行数の解釈が異なり、実際に UPDATE で変更した行数だけを返すために、 WHERE 節による行数より少ないことがあります。 データベースモジュールの作者は、基本的には WHERE 節による行数を返すような実装を試みてください。それができないときは、 .rowcount 属性の意味が通常と異なる旨、明確にドキュメントに記載してください。

謝辞

Python Database API Specification 2.0 を原版の HTML 形式から PEP 形式に変換した Andrew Kuchling に深く感謝します。

2 フェーズコミット API 拡張の標準化にあたって議論をまとめた James Henstridge に感謝します。

テキスト形式の PEP から ReST に変換し、各所にリンクできるようにした Daniele Varrazzo に感謝します。

著作権

パブリックドメインの文書です。