44.3. 数据值

一般来讲,PL/Python 的目标是提供在 PostgreSQL 和 Python 世界之间的一种"自然的"映射。这包括下面介绍的数据映射规则。

44.3.1. 数据类型映射

在调用一个 PL/Python 函数时,它的参数会被从 PostgreSQL 的数据类型转换成相应的 Python 类型:

当一个 PL/Python 函数返回时,会按照下列规则把它的返回值转换成该函数声明的 PostgreSQL 返回数据类型:

注意所声明的 PostgreSQL 返回类型和实际返回对象的 Python 数据类型之间的逻辑失配不会被标志,无论怎样该值都会被转换。

44.3.2. Null, None

如果一个 SQL 空值被传递给一个函数,该参数值将作为 Python 中的None出现。例如,第 44.2 节中展示的pymax的函数定义对于空值输入将会返回错误的回答。我们可以为函数定义增加STRICTPostgreSQL做得更加合理:如果一个空值被传入,该函数将根本不会被调用,而只是自动地返回一个空结果。此外,我们可以在函数体中检查空输入:

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 空值,可返回值None。不管该函数严格与否都可以这样做。

44.3.3. 数组、列表

SQL 数组会被作为一个 Python 列表传递到 PL/Python 中。要从一个 PL/Python 函数中返回出一个 SQL 数组值,可返回一个序列,例如一个列表或者元组:

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)

44.3.4. 组合类型

组合类型参数被作为 Python 映射传递给函数。映射的元素名称就是组合类型的属性名。如果被传递的行中有一个属性是空值,在映射中它的值是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
);

一个组合结果可以被返回为:

序列类型(一个元组或者列表,但不是集合,因为 集合不能被索引)

被返回的序列对象必须具有和组合结果类型的域个数相同的项。索引号为 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 空,应在对应的位置插入None

映射(字典)

用列名作为键从映射中检索每一个结果类型列的值。例如:

CREATE FUNCTION make_pair (name text, value integer)
  RETURNS named_value
AS $$
  return { "name": name, "value": value }
$$ LANGUAGE plpythonu;

任何额外的字典键/值对都会被忽略。丢失的键会被当做错误。要为任意列返回一个 SQL 空,应用相应的列名作为键插入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)

  # 或简单地
  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();

44.3.5. 集合返回函数

PL/Python函数也能返回标量类型或者组合类型的集合。有多种方法可以做到这一点,因为被返回的对象在内部会被转变成一个迭代器。下面的例子假设我们有组合类型:

CREATE TYPE greeting AS (
  how text,
  who text
);

可从以下类型返回集合结果:

序列类型(元组、列表、集合)

CREATE FUNCTION greet (how text)
  RETURNS SETOF greeting
AS $$
  # 把包含列表的元组返回为组合类型
  # 所有其他组合也能行
  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;

警告

由于 Pythonbug #1483133,已知 Python 2.4 的某些调试版本(用选项--with-pydebug配置并且编译)在使用迭代器返回集合结果时会让PostgreSQL服务器崩溃。没有打过补丁的 Fedora 4 包含这个缺陷。在生产版本的 Python 中或者打过补丁的 Fedora 4 上不会发生该问题。

也支持有OUT参数的集合返回函数(使用RETURNS SETOF record)。例如:

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