40.1. PL/Python関数

PL/Pythonで作成された関数は標準のCREATE FUNCTION構文で宣言されます。

CREATE FUNCTION funcname (argument-list)
  RETURNS return-type
AS $$
  # PL/Python function body
$$ LANGUAGE plpythonu;

関数本体は単なるPythonスクリプトです。 関数が呼び出されると、引数はargs[]配列の要素として、また、名前付きの引数は通常の変数としてPythonスクリプトに渡されます。 結果は、Pythonコードから通常の方法、returnまたはyield(結果セット文の場合)で返されるものです。

たとえば、2つの整数の内大きな数を返す関数は以下のように定義することができます。

CREATE FUNCTION pymax (a integer, b integer)
  RETURNS integer
AS $$
  if a > b:
    return a
  return b
$$ LANGUAGE plpythonu;

関数定義の本体として提供されたPythonのコードはPythonの関数に変換されます。 例えば上の例は以下のようになります。

def __plpython_procedure_pymax_23456():
  if a > b:
    return a
  return b

ここで、23456はPostgreSQLにより割り当てられたこの関数のOIDです。

PostgreSQL関数パラメータはargsグローバルリストから利用可能です。 pymaxを例にすると、args[0]には最初の引数として渡されたものが含まれ、args[1]には2番目の引数の値が含まれます。 別の方法として、上の例のように名前付きパラメータを使用することができます。 名前付きパラメータを使用することで通常可読性が増します。

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

複合型の引数は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;

戻り値を用意していない場合、PythonはデフォルトのNoneを返します。 PL/Pythonは、PythonのNoneをSQLのNULL値に変換します。

また、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ではこの問題は起こりません。

グローバルなSD辞書は、関数呼び出し間のデータ保存のために使用することができます。 この変数はプライベートな静的データです。 グローバルなGD辞書は、共有データであり、セッション内の全てのPython関数で使用することができます。 注意して使用してください。

各関数は、Pythonインタプリタ内で自身の実行環境を入手します。 そのため、myfuncによるグローバルデータと関数の引数はmyfunc2から使用することはできません。 上記で説明した通り、GD辞書内のデータは例外です。