总的来说,PL/Python 的目标是在 PostgreSQL 世界和 Python 世界之间 提供“自然的”映射。下面描述的数据映射规则都源于这一 目标。
当 PL/Python 函数被调用时,其参数会从 PostgreSQL 数据类型转换为 对应的 Python 类型:
PostgreSQL boolean 转换为 Python bool。
PostgreSQL smallint、int、
bigint 和 oid 转换为 Python
int。
PostgreSQL real 和 double 转换为
Python float。
PostgreSQL numeric 转换为
Python Decimal。如果 cdecimal
包可用,则从该包导入此类型。
否则,将使用标准库的 decimal.Decimal。
cdecimal 比 decimal 快得
多。不过在 Python 3.3 及以上版本中,
cdecimal 已被整合进标准库并更名为
decimal,因此不再有任何区别。
PostgreSQL bytea 转换为 Python bytes。
所有其他数据类型(包括 PostgreSQL 字符串类型)都被转换为
Python str(与所有 Python 字符串一样是 Unicode 的)。
非标量数据类型见下文。
当 PL/Python 函数返回时,其返回值按如下方式被转换为该函数声明的 PostgreSQL 返回数据类型:
当 PostgreSQL 返回类型是 boolean 时,返回值会按
Python 规则求真值。也就是说,0 和空字符串
为假,但值得注意的是 'f' 为真。
当 PostgreSQL 返回类型是 bytea 时,返回值会先用
相应的 Python 内建函数转换为 Python bytes,再把
结果转换为 bytea。
对于所有其他 PostgreSQL 返回类型,返回值会用 Python 内建的
str 转换为字符串,然后把结果传给
PostgreSQL 数据类型的输入函数。
(如果 Python 值是 float,会用
repr 内建函数而非 str 来
转换,以避免精度损失。)
字符串在被传给 PostgreSQL 时会自动转换为 PostgreSQL 服务器 编码。
非标量数据类型见下文。
注意,声明的 PostgreSQL 返回类型与实际返回对象的 Python 数据类型 之间的逻辑不匹配不会被标记;无论如何都会进行转换。
如果把一个 SQL 空值传给函数,参数值
在 Python 中会表现为 None。例如,
Section 44.1 所示的 pymax
函数定义对空输入会返回错误答案。我们可以在函数定义中加入
STRICT,让 PostgreSQL
做更合理的事情:如果传入空值,函数根本不会被调用,而是自动返回
空结果。或者,我们也可以在函数体内检查空输入:
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 plpython3u;
如上所示,要从 PL/Python 函数返回 SQL 空值,返回值
None 即可。无论函数是否严格都可以这样做。
SQL 数组值会以 Python 列表的形式传入 PL/Python。要从 PL/Python 函数返回 SQL 数组值,返回一个 Python 列表即可:
CREATE FUNCTION return_arr()
RETURNS int[]
AS $$
return [1, 2, 3, 4, 5]
$$ LANGUAGE plpython3u;
SELECT return_arr();
return_arr
-------------
{1,2,3,4,5}
(1 row)
多维数组会以嵌套 Python 列表的形式传入 PL/Python。 例如,二维数组就是列表的列表。从 PL/Python 函数返回多维 SQL 数组时, 每一层级的内层列表的大小必须都相同。例如:
CREATE FUNCTION test_type_conversion_array_int4(x int4[]) RETURNS int4[] AS $$
plpy.info(x, type(x))
return x
$$ LANGUAGE plpython3u;
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 及更早版本(那时不支持多维数组)。不过,它们总是被 当作一维数组处理,因为它们与复合类型有歧义。出于同样的原因, 当复合类型被用在多维数组中时,必须用元组而非列表来表示它。
注意,在 Python 中字符串也是序列,这可能带来 Python 程序员熟悉 的不良效果:
CREATE FUNCTION return_str_arr()
RETURNS varchar[]
AS $$
return "hello"
$$ LANGUAGE plpython3u;
SELECT return_str_arr();
return_str_arr
----------------
{h,e,l,l,o}
(1 row)
复合类型参数会以 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 plpython3u;
从 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 list: return [ name, value ] $$ LANGUAGE plpython3u;
要为任何列返回 SQL 空值,请在相应位置插入
None。
当返回复合类型的数组时,不能以列表形式返回, 因为无法区分 Python 列表表示的是复合类型还是另一个数组 维度。
每个结果类型列的值都以列名为键从映射中取出。例如:
CREATE FUNCTION make_pair (name text, value integer)
RETURNS named_value
AS $$
return { "name": name, "value": value }
$$ LANGUAGE plpython3u;
任何多余的字典键/值对会被忽略。缺失的键被视为错误。
要为任何列返回 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)
# or simply
class nv: pass
nv.name = name
nv.value = value
return nv
$$ LANGUAGE plpython3u;
带 OUT 参数的函数也是受支持的。例如:
CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$ return (1, 2) $$ LANGUAGE plpython3u; SELECT * FROM multiout_simple();
过程的输出参数也以同样的方式传回。例如:
CREATE PROCEDURE python_triple(INOUT a integer, INOUT b integer) AS $$ return (a * 3, b * 3) $$ LANGUAGE plpython3u; CALL python_triple(5, 10);
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 plpython3u;
__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 plpython3u;
yield)
CREATE FUNCTION greet (how text)
RETURNS SETOF greeting
AS $$
for who in [ "World", "PostgreSQL", "PL/Python" ]:
yield ( how, who )
$$ LANGUAGE plpython3u;
带 OUT 参数的集合返回函数
(使用 RETURNS SETOF record)也是
受支持的。例如:
CREATE FUNCTION multiout_simple_setof(n integer, OUT integer, OUT integer) RETURNS SETOF record AS $$ return [(1, 2)] * n $$ LANGUAGE plpython3u; SELECT * FROM multiout_simple_setof(3);