★PostgreSQLカンファレンス2024 12月6日開催/チケット販売中★
他のバージョンの文書 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

43.3. データ値

一般的にいって、PL/Pythonの目標はPostgreSQLとPythonの世界の間で自然な対応付けを提供することです。 これは以下のようなデータの対応付けを形成します。

43.3.1. データ型の対応付け

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が使用できます。 cdecimaldecimalより高速です。 しかしPython 3.3から、cdecimaldecimalという名前で標準ライブラリに統合されたので、もはや差異はありません。

  • 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データ型との間の論理的な不整合が伝わらないことに注意してください。 値はいかなる場合でも変換されます。

43.3.2. NullとNone

SQLのNULL値が関数に渡されると、その引数値はPythonではNoneとなります。 例えば、43.2. PL/Python関数に示された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という値を返してください。 関数を厳密とした場合でも厳密としない場合でも、これを行うことができます。

43.3.3. 配列、リスト

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)

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)

43.3.4. 複合型

複合型の引数は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 ]
  # or alternatively, as tuple: return ( name, value )
$$ LANGUAGE plpythonu;

任意の列でSQL NULL値を返すには、対応する位置にNoneを挿入します。

マップ(辞書)

結果型の列の値は、列名をキーとして持つマップから取り出されます。 以下に例を示します。

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();

43.3.5. 集合を返す関数

また、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;

警告

Pythonのbug #1483133のため、一部のPython 2.4デバッグ版(--with-pydebugオプション付きで設定/コンパイルされたPython)が、集合結果を返すためにイテレータを使用する場合にPostgreSQLサーバをクラッシュさせることがわかっています。 未パッチのFedora 4にはこの不具合があります。 Python運用版やパッチ適用済みのFedora 4ではこの問題は起こりません。

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);