無料スクリプト配布のPHP.TO   PHPの実用的なtips PHPマニュアル MySQLマニュアル Apacheマニュアル PostgreSQLマニュアル マニュアル検索    

43.7. データベースアクセス

PL/Python言語モジュールは自動的に plpy というPythonモジュールをインポートします。 このモジュールの関数と定数は、 plpy. foo のように作成したPythonコードから使用することができます。

43.7.1. データベースアクセス関数

plpy モジュールはデータベースコマンドを実行するために数個の関数を用意しています。

plpy. execute ( query [, max-rows ])

plpy.execute を、問い合わせ文字列および省略可能な行数制限引数を付けて呼び出すと、問い合わせが実行され、結果オブジェクトとして問い合わせ結果が返ります。

結果オブジェクトはリストもしくは辞書オブジェクトをエミュレートします。 結果オブジェクトは、行番号や列名によってアクセスすることができます。 例を示します。

rv = plpy.execute("SELECT * FROM my_table", 5)

これは、 my_table から5行までを返します。 my_table my_column 列が存在する場合、その列には以下のようにアクセスできます。

foo = rv[i]["my_column"]

戻った行数はビルトイン len 関数を使用して取得できます。

結果オブジェクトには以下のメソッドが追加されています。

nrows ()

コマンドによる処理の行数を返します。 戻った行数と同じとは限らないことに注意してください。 例えば、 UPDATE コマンドではゼロでない値を返しますが、行を戻すことはありません( RETURNING を使用したときは別です)。

status ()

SPI_execute() 関数の戻り値を返します。

colnames ()
coltypes ()
coltypmods ()

各々、列名のリスト、列の型OIDのリスト、列に関する型独自の型修飾子のリストを返します。

RETURNING を持たない UPDATE DROP TABLE など、結果セットを生成しないコマンドによる結果オブジェクトに対して呼び出された場合、これらのメソッドは例外を発生します。 しかし、ゼロ行の結果セットに対してこれらのメソッドを使用することには問題ありません。

__str__ ()

標準の __str__ メソッドは、例えば plpy.debug(rv) を使用して問い合わせの実行結果のデバッグが可能なように定義されます。

結果オブジェクトは変更できます。

plpy.execute を呼び出すことにより、結果セット全体がメモリ内に読み込まれることに注意してください。 結果セットが比較的小さいことが確実な場合だけ、この関数を使用してください。 大規模な結果を取り込む場合の過渡なメモリ使用に関する危険を回避したい場合は、 plpy.execute ではなく plpy.cursor を使用してください。

plpy. prepare ( query [, argtypes ])
plpy. execute ( plan [, arguments [, max-rows ]])

plpy.prepare は問い合わせの実行計画を準備します。 問い合わせ内にパラメータ参照がある場合、問い合わせ文字列および引数型のリストとともに呼び出されます。 例を示します。

plan = plpy.prepare("SELECT last_name FROM my_users WHERE first_name = $1", ["text"])

text $1 として渡される変数の型です。 問い合わせにパラメータを渡さない場合、2番目の引数は省略可能です。

文を準備した後、それを実行するために関数 plpy.execute の亜種を使用します。

rv = plpy.execute(plan, ["name"], 5)

実行計画を(問い合わせ文字列ではなく)最初の引数として渡してください。 問い合わせに代入する値のリストを、2番目の引数として渡してください。 問い合わせにパラメータがない場合、2番目の引数は省略可能です。 3番目の引数は、前に述べた省略可能な行数制限引数です。

問い合わせパラメータおよび結果行のフィールドは 項43.3 で示した通り、PostgreSQLとPythonのデータ型の間で変換されます。 現在複合型がサポートされていないという例外があります。 複合型は問い合わせパラメータとしては拒絶され、問い合わせの結果に存在する場合は文字列に変換されます。 後者の問題の回避方法として、結果行のフィールドとしてではなく結果行として複合型が存在するように問い合わせを書き換えることができる場合があります。 他に、結果として生じる文字列を手作業で解析して分離することもできますが、この方法は時代に左右されてしまいますので推奨されません。

PL/Pythonモジュールを使用して準備した計画は自動的に保存されます。 これが何を意味するのかについてはSPIの文書( 第44章 )を参照してください。 これを複数呼び出しにおいて効果的に使用するためには、永続的な格納用辞書である SD または GD 項43.4 を参照)のいずれかを使用する必要があります。 例を示します。

CREATE FUNCTION usesavedplan() RETURNS trigger AS $$
    plan = SD.setdefault("plan", plpy.prepare("SELECT 1"))
    # rest of function
$$ LANGUAGE plpythonu;

plpy. cursor ( query )
plpy. cursor ( plan [, arguments ])

plpy.cursor 関数は plpy.execute と同じ引数を受け取り(行数制限引数を除いた)カーソルオブジェクトとして返します。 これにより大規模な結果セットをより小さな塊の中で処理することができます。 plpy.execute の場合と同様、問い合わせ文字列または引数リスト付きの計画オブジェクトを使用することができます。

カーソルオブジェクトは、整数パラメータを受付け、結果オブジェクトを返す fetch メソッドを提供します。 fetch を呼び出す度に、返されるオブジェクトには次の一群の行が含まれます。 この行数はパラメータ値より多くなることはありません。 全ての行が出し尽くされると、 fetch は空の結果オブジェクトを返すようになります。 カーソルオブジェクトはまた、すべての行を出し尽くすまで一度に1行を生成する イテレータインタフェース を提供します。 この方法で取り出されたデータは結果オブジェクトとしては返されず、1つの辞書が単一の結果行に対応する辞書群として返されます。

大きなテーブルのデータを処理する、2つの方法の例を示します。

CREATE FUNCTION count_odd_iterator() RETURNS integer AS $$
odd = 0
for row in plpy.cursor("select num from largetable"):
    if row['num'] % 2:
         odd += 1
return odd
$$ LANGUAGE plpythonu;

CREATE FUNCTION count_odd_fetch(batch_size integer) RETURNS integer AS $$
odd = 0
cursor = plpy.cursor("select num from largetable")
while True:
    rows = cursor.fetch(batch_size)
    if not rows:
        break
    for row in rows:
        if row['num'] % 2:
            odd += 1
return odd
$$ LANGUAGE plpythonu;

CREATE FUNCTION count_odd_prepared() RETURNS integer AS $$
odd = 0
plan = plpy.prepare("select num from largetable where num % $1 <> 0", ["integer"])
rows = list(plpy.cursor(plan, [2]))

return len(rows)
$$ LANGUAGE plpythonu;

カーソルは自動的に処分されます。 しかし、カーソルが保有していた資源を明示的に開放したい場合は、 close メソッドを使用してください。 閉じた後、カーソルからこれ以上取り込むことはできません。

ティップ: plpy.cursor によって作成されたオブジェクトと、 PythonデータベースAPI仕様 において定義されたDB-APIカーソルとを混同しないでください。 名称以外の共通点はありません。

43.7.2. エラーの捕捉

データベースにアクセスする関数はエラーに遭遇し、エラーが関数をアボートして例外を発生させる原因となります。 plpy.execute および plpy.prepare は、デフォルトでは関数を終了させる plpy.SPIError のサブクラスのインスタンスを発生させることができます。 このエラーは、 try/except 構文を使用して、Pythonの他の例外と同様に処理できます。 例を示します。

CREATE FUNCTION try_adding_joe() RETURNS text AS $$
    try:
        plpy.execute("INSERT INTO users(username) VALUES ('joe')")
    except plpy.SPIError:
        "うまくいかなかった" を返す
    else:
        "Joeが追加された" を返す
$$ LANGUAGE plpythonu;

発生される例外の実クラスはエラーを引き起こした特定の条件と対応します。 表A-1 にあり得る条件のリストがありますので参照してください。 plpy.spiexceptions モジュールは PostgreSQL の条件それぞれに対して、その条件名に因んだ名前の例外クラスを定義しています。 例えば division_by_zero DivisionByZero unique_violation UniqueViolation に、 fdw_error FdwError などのようになります。 これらの例外クラスはそれぞれ SPIError を継承したものです。 このように分離することで特定のエラーをより簡単に扱うことができるようになります。 以下に例を示します。

CREATE FUNCTION insert_fraction(numerator int, denominator int) RETURNS text AS $$
from plpy import spiexceptions
try:
    plan = plpy.prepare("INSERT INTO fractions (frac) VALUES ($1 / $2)", ["int", "int"])
    plpy.execute(plan, [numerator, denominator])
except spiexceptions.DivisionByZero:
    return "denominator cannot equal zero"
except spiexceptions.UniqueViolation:
    return "already have that fraction"
except plpy.SPIError, e:
    return "other error, SQLSTATE %s" % e.sqlstate
else:
    return "fraction inserted"
$$ LANGUAGE plpythonu;

plpy.spiexceptions モジュールからの全ての例外は SPIError を継承するため、例外を処理する except 句は全てのデータベースアクセスエラーを捕捉することに注意してください。

異なったエラー条件を処理する代りの方法として、 SPIError 例外を捕捉して、例外オブジェクトの sqlstate 属性を調べることにより、 except ブロック内部の明細なエラー条件を決定できます。 この属性は "SQLSTATE" エラーコードを含む文字列値です。 この方法は、ほぼ同じ機能を提供します。


powered by SEO.CUG.NET