46.3. 数据值 #
一般来说,PL/Python 的目标是在 PostgreSQL 世界和 Python 世界之间提供一种“自然”的映射。下面描述的数据映射规则就是基于这一目标。
46.3.1. 数据类型映射 #
调用 PL/Python 函数时,其参数会从 PostgreSQL 数据类型转换为相应的 Python 类型:
PostgreSQL
boolean会转换为 Pythonbool。PostgreSQL 的
smallint和int转换为 Python 的int。PostgreSQL 的bigint和oid在 Python 2 中转换为long,在 Python 3 中转换为int。PostgreSQL
real和double会转换为 Pythonfloat。PostgreSQL
numeric会转换为 PythonDecimal。如果可用,将从cdecimal包导入这种类型。否则,将使用标准库中的decimal.Decimal。cdecimal明显快于decimal。不过在 Python 3.3 及更高版本中,cdecimal已经以decimal这一名称并入标准库,因此不再有区别。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 内置函数,将返回值转换为字符串(Python 2)或 bytes(Python 3),再将结果转换为bytea。对于所有其他 PostgreSQL 返回类型,返回值会使用 Python 内置函数
str转换为字符串,然后将结果传给 PostgreSQL 数据类型的输入函数。(如果 Python 值是float,则会使用repr内置函数而不是str来转换,以避免精度损失。)Python 2 中的字符串传给 PostgreSQL 时,必须采用 PostgreSQL 服务器编码。在当前服务器编码中无效的字符串会引发错误,但并非所有编码不匹配都能被检测到,因此处理不当仍可能产生乱码数据。Unicode 字符串会自动转换为正确的编码,因而使用它们可能更安全、更方便。在 Python 3 中,所有字符串都是 Unicode 字符串。
对于非标量数据类型,请参见下文。
请注意,即使声明的 PostgreSQL 返回类型与实际返回对象的 Python 数据类型在逻辑上并不匹配,也不会报错;无论如何该值都会被转换。
46.3.2. 空值、None #
如果把 SQL 空值传给函数,参数值在 Python 中会显示为 None。例如,第 46.2 节中展示的 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 plpythonu;
如上所示,要从 PL/Python 函数返回 SQL 空值,只需返回 None。无论函数是否被声明为 STRICT,都可以这样做。
46.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 列表传入 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 plpythonu;
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 plpythonu;
SELECT return_str_arr();
return_str_arr
----------------
{h,e,l,l,o}
(1 row)
46.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 ) # 也可以使用列表: return [ name, value ] $$ LANGUAGE plpythonu;
要为某一列返回 SQL 空值,请将
None放在对应位置。当返回复合类型数组时,不能将其表示为列表,因为这样无法区分 Python 列表表示的是复合类型还是另一个数组维度。
- 映射(字典)
结果类型中每一列的值,都使用列名作为键从映射中取得。例如:
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();
过程的输出参数也以同样的方式传回。例如:
CREATE PROCEDURE python_triple(INOUT a integer, INOUT b integer) AS $$ return (a * 3, b * 3) $$ LANGUAGE plpythonu; CALL python_triple(5, 10);
46.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;
也支持带 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);
报告文档问题
阅读 上游文档. 通过 PostgreSQL 文档反馈表单.