43.3. 数据值

一般来说,PL/Python的目的是提供PostgreSQL和Python世界之间的"自然"映射。 如下面描述的数据映射规则。

43.3.1. 数据类型映射

函数参数是从他们的PostgreSQL类型转换为相应的Python类型:

函数的返回值被转换为声明的PostgreSQL返回数据类型,如下:

请注意,PostgreSQL声明的返回类型和实际返回对象的Python数据类型之间的逻辑不匹配是不标记的; 该值将在任何情况下被转换。

43.3.2. Null, None

如果向函数传递了一个SQL null值,参数值在Python中将会显示为 None。比如,在第 43.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即可。 无论函数是否严格,这样做都有效。

43.3.3. Arrays, Lists

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

一个复合类型结果可以作为下列之一返回:

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

返回的序列对象必须与复合结果类型字段数相同。索引为0的项将被分配给复合类型的第一个字段, 索引为1的项分配给第2个字段,以此类推。比如:

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选项配置和编译) 已知会让PostgreSQL服务器在使用迭代器返回集合时崩溃。 Fedora 4的应用补丁版本包含这个臭虫。但是在生产版本的Python或已经修补的Fedora 4上没有这个问题。

也支持带有OUT参数的设置返回函数(使用RETURNS SETOF记录)。 例如:

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