一般的にいって、PL/Pythonの目標はPostgreSQLとPythonの世界の間で「自然な」対応付けを提供することです。 これは以下のようなデータの対応付けを形成します。
PL/Python関数が呼ばれると、その引数は、以下のようにPostgreSQLの型から対応するPython型に変換されます。
PostgreSQLのboolean
はPythonのbool
に変換されます。
PostgreSQLのsmallint
およびint
はPythonのint
に変換されます。
PostgreSQLのbigint
およびoid
は、Python 2ではlong
に、Python 3ではint
に変換されます。
PostgreSQLのreal
およびdouble
はPythonのfloat
に変換されます。
PostgreSQLのnumeric
はPythonのDecimal
に変換されます。
この型は可能ならばcdecimal
パッケージからインポートできます。
可能でなければ、標準ライブラリのdecimal.Decimal
が使用できます。
cdecimal
はdecimal
より高速です。
しかしPython 3.3から、cdecimal
はdecimal
という名前で標準ライブラリに統合されたので、もはや差異はありません。
PostgreSQLのbytea
は、Python 2ではstr
に、Python 3ではbytes
に変換されます。
Python 2では文字列は文字符号化方式を持たないバイト列として扱われるはずです。
PostgreSQLの文字列型を含む、上記以外のデータ型はすべてPythonのstr
に変換されます。
Python 2ではこの文字列はPostgreSQLのサーバ符号化方式で表されます。
Python 3ではすべての文字列と同様にUnicode文字列となります。
スカラ型以外については後述します。
PL/Python関数が戻る時には、その戻り値は、以下のようにPostgreSQLの宣言された戻り値データ型に変換されます。
PostgreSQLの戻り値の型がboolean
の場合、戻り値はPythonの規約に従った真に対して評価されます。
つまり、0や空文字列は偽です。
'f'
が真となることには注意してください。
PostgreSQLの戻り値の型がbytea
の場合、戻り値は文字列(Python 2)またはbytes(Python 3)に、それぞれ対応するPythonのビルトインを使用して変換され、その結果がbytea
に変換されます。
この他のPostgreSQLの戻り値型では、返される値はPythonのビルトインstr
を使用して文字列に変換され、その結果がPostgreSQLデータ型の入力関数に渡されます。
(Pythonの値がfloat
であれば、精度が失われるのを避けるため、str
の代わりにrepr
ビルトインを使って変換されます。)
Python 2における文字列はPostgreSQLに渡される時にPostgreSQLサーバの符号化方式でなければなりません。 現在のサーバ符号化方式で無効な文字列はエラーとなりますが、符号化方式の不一致がすべて検知されることはありません。 このため正確に行われなかった場合にはゴミデータという結果になります。 Unicode文字列は自動的に正しい符号化方式に変換されます。 このためこれを使用することがより安全でより便利です。 Python 3における文字列はすべてUnicode文字列です。
スカラデータ型以外については後述します。
宣言されたPostgreSQLの戻り値型と実際に返されるオブジェクトのPythonデータ型との間の論理的な不整合が伝わらないことに注意してください。 値はいかなる場合でも変換されます。
SQLのNULL値が関数に渡されると、その引数値はPythonではNone
となります。
例えば、45.2に示されたpymax
関数の定義では、NULL入力に対して間違った結果が返されます。
関数定義にSTRICT
を付与してPostgreSQLを、NULL値が渡された場合にその関数を呼び出さず、自動的に単にNULL結果を返すという、より理想的に動作させることができます。
他に、関数本体でNULL入力を検査することもできます。
CREATE FUNCTION pymax (a integer, b integer) RETURNS integer AS $$ if (a is None) or (b is None): return None if a > b: return a return b $$ LANGUAGE plpythonu;
上で示したように、PL/Python関数からSQL NULL値を返すには、None
という値を返してください。
関数を厳密とした場合でも厳密としない場合でも、これを行うことができます。
SQL配列値はPythonのリストとしてPL/Pythonに渡されます。 PL/Python関数の外部にSQL配列値を返すためには、Pythonのリストを返します。
CREATE FUNCTION return_arr() RETURNS int[] AS $$ return [1, 2, 3, 4, 5] $$ LANGUAGE plpythonu; SELECT return_arr(); return_arr ------------- {1,2,3,4,5} (1 row)
多次元配列はPL/Pythonに入れ子のPythonのリストとして渡されます。 例えば、2次元配列はリストのリストです。 PL/Pythonから多次元のSQLの配列を返す場合には、各レベルの内側のリストはすべて同じ大きさでなければなりません。 例えば、
CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$ plpy.info(x, type(x)) return x $$ LANGUAGE plpythonu; SELECT * FROM test_type_conversion_array_int4(ARRAY[[1,2,3],[4,5,6]]); INFO: ([[1, 2, 3], [4, 5, 6]], <type 'list'>) test_type_conversion_array_int4 --------------------------------- {{1,2,3},{4,5,6}} (1 row)
タプル等のその他のPythonのシーケンスも、PostgreSQLバージョン9.6以下との後方互換性のために受け入れられます。当時は、多次元配列はサポートされていませんでした。 しかしながら、複合型と区別できないため、常に1次元配列として扱われます。 同じ理由で、複合型を多次元配列内で使う場合、リストではなくタプルとして表現しなければなりません。
Pythonでは、文字列はシーケンスであることに注意してください。 これは予想できない影響を与えることがありますが、Pythonプログラマには慣れたものでしょう。
CREATE FUNCTION return_str_arr() RETURNS varchar[] AS $$ return "hello" $$ LANGUAGE plpythonu; SELECT return_str_arr(); return_str_arr ---------------- {h,e,l,l,o} (1 row)
複合型の引数はPythonのマップとして渡されます。
マップの要素名は複合型の属性名です。
渡された行の属性値がNULLの場合、マップ上ではNone
という値となります。
以下に例を示します。
CREATE TABLE employee ( name text, salary integer, age integer ); CREATE FUNCTION overpaid (e employee) RETURNS boolean AS $$ if e["salary"] > 200000: return True if (e["age"] < 30) and (e["salary"] > 100000): return True return False $$ LANGUAGE plpythonu;
Python関数から行または複合型を返す方法は複数存在します。 以下の例では
CREATE TYPE named_value AS ( name text, value integer );
を前提とします。 複合型の結果は以下のように返されます。
set
は不可)返されるシーケンスオブジェクトは、結果の複合型が持つフィールドと同じ項目数をもたなければなりません。 0というインデックスの項目が複合型の最初のフィールド、1が次のフィールド、などとなります。 以下に例を示します。
CREATE FUNCTION make_pair (name text, value integer) RETURNS named_value AS $$ return ( name, value ) # もしくは、タプルとして返すなら: return [ name, value ] $$ LANGUAGE plpythonu;
任意の列でSQL NULL値を返すには、対応する位置にNone
を挿入します。
複合型の配列を返す場合、Pythonのリストが複合型を表しているのか、また別の配列の次元を表しているのかあいまいですので、リストとして返すことはできません。
結果型の列の値は、列名をキーとして持つマップから取り出されます。 以下に例を示します。
CREATE FUNCTION make_pair (name text, value integer) RETURNS named_value AS $$ return { "name": name, "value": value } $$ LANGUAGE plpythonu;
余計な辞書のキーと値の組み合わせは無視されます。
存在しないキーはエラーとして扱われます。
任意の列でSQL NULLを返すためには、対応する列名をキーとしてNone
を挿入してください。
__getattr__
メソッドを提供する任意のオブジェクト)これはマップと同じように動作します。 以下に例を示します。
CREATE FUNCTION make_pair (name text, value integer) RETURNS named_value AS $$ class named_value: def __init__ (self, n, v): self.name = n self.value = v return named_value(name, value) # or simply class nv: pass nv.name = name nv.value = value return nv $$ LANGUAGE plpythonu;
OUT
パラメータを用いる関数もサポートされています。
以下に例を示します。
CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$ return (1, 2) $$ LANGUAGE plpythonu; SELECT * FROM multiout_simple();
また、PL/Python関数はスカラまたは複合型の集合を返すこともできます。 返されるオブジェクトは内部的にイテレータに変換されるため、複数の実現方法があります。 以下の例では、以下の複合型が存在することを仮定します。
CREATE TYPE greeting AS ( how text, who text );
集合という結果は以下から返されます。
CREATE FUNCTION greet (how text) RETURNS SETOF greeting AS $$ # return tuple containing lists as composite types # all other combinations work also return ( [ how, "World" ], [ how, "PostgreSQL" ], [ how, "PL/Python" ] ) $$ LANGUAGE plpythonu;
__iter__
メソッドとnext
メソッドを提供する任意のオブジェクト)
CREATE FUNCTION greet (how text) RETURNS SETOF greeting AS $$ class producer: def __init__ (self, how, who): self.how = how self.who = who self.ndx = -1 def __iter__ (self): return self def next (self): self.ndx += 1 if self.ndx == len(self.who): raise StopIteration return ( self.how, self.who[self.ndx] ) return producer(how, [ "World", "PostgreSQL", "PL/Python" ]) $$ LANGUAGE plpythonu;
yield
)CREATE FUNCTION greet (how text) RETURNS SETOF greeting AS $$ for who in [ "World", "PostgreSQL", "PL/Python" ]: yield ( how, who ) $$ LANGUAGE plpythonu;
(RETURNS SETOF record
を使用して)OUT
パラメータを持つ集合を返す関数もサポートされます。
以下に例を示します。
CREATE FUNCTION multiout_simple_setof(n integer, OUT integer, OUT integer) RETURNS SETOF record AS $$ return [(1, 2)] * n $$ LANGUAGE plpythonu; SELECT * FROM multiout_simple_setof(3);