阅读(1233) (19)

PostgreSQL 数据值

2021-08-21 11:32:42 更新
45.3.1. 数据类型映射
45.3.2. Null, None
45.3.3. 数组、列表
45.3.4. 组合类型
45.3.5. 集合返回函数

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

45.3.1. 数据类型映射

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

  • PostgreSQL 的boolean被转换成 Python 的bool

  • PostgreSQL 的smallintint被转换成 Python 的int。 PostgreSQL 的bigintoid被转换成 Python 2 的long或者 Python 3 的int

  • PostgreSQL 的realdouble被转换成 Python 的float

  • PostgreSQL 的numeric被转换成 Python 的Decimal。如果存在cdecimal包,则会从其中导入该类型。否则将使用来自标准库的decimal.Decimalcdecimaldecimal要更快。不过,在 Python 3.3 以及更高的版本中,cdecimal已经被整合到了标准库中(也是用decimal这个名字),因此也就不再有什么区别。

  • PostgreSQL 的bytea被转换成 Python 的str(Python 2)和bytes(Python 3)。在 Python 2 中,串应该被当做没有任何字符编码的字节序列对待。

  • 包括 PostgreSQL 字符串类型在内的所有其他数据类型会被转换成一个 Python 的str。在 Python 2 中,这个串将用 PostgreSQL 服务器编码;在 Python 3 中,它将和所有串一样使用 Unicode。

  • 对于非标量数据类型,请见下文。

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

  • 当 PostgreSQL 返回类型是boolean时,返回值会被根据Python规则计算真假。也就是说,0 和空串是假,但是要特别注意'f'是真。

  • 当 PostgreSQL 返回类型是bytea时,返回值会被使用相应的 Python 内建机制转换成串(Python 2)或者字节(Python 3),结果将被转换成bytea

  • 对于所有其他 PostgreSQL 返回类型,返回值被使用 Python 内建的str转换成一个串,并且结果会被传递给 PostgreSQL 数据类型的输入函数(如果该 Python 值是一个float,它会被用内建的repr而不是str转换,这是为了避免精度损失)。

    当 Python 2 的串被传递给 PostgreSQL 时,它们被要求是 PostgreSQL 服务器编码。在当前服务器编码中不可用的串将会产生错误,但是并非所有的编码失配都能被检测到,因此当没有正确地将串编码时,垃圾数据仍然会产生。Unicode 串会被自动地转换为正确的编码,因此使用 Unicode 串更加安全并且更加方便。在 Python 3 中,所有串都是 Unicode 串。

  • 对于非标量数据类型,请见下文。

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

45.3.2. Null, None

如果一个 SQL 空值 被传递给一个函数,该参数值将作为 Python 中的None出现。例如,第 45.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。不管该函数严格与否都可以这样做。

45.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。例如,一个2维数组是一个列表的列表。在把一个多维SQL数组从PL/Python函数返回出去时,每一层的内层列表都必须是相同的尺寸。例如:

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)

为了与PostgreSQL的9.6以及更低版本的向后兼容性,当不支持多维数组时,也接受元组之类的其他Python序列。不过,它们总是被当做一维数组,因为它们会和组合类型混淆。出于同样的原因,当一个组合类型被用在多维数组中时,它必须被表示为一个元组而不是一个列表。

注意在 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)

45.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

当一个组合类型的数组被返回时,它不能被返回为列表,因为会弄不清该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);

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