2025年9月25日: PostgreSQL 18 釋出!
支援的版本: 當前 (18) / 17 / 16 / 15 / 14 / 13
開發版本: devel
不受支援的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0

44.2. 資料值 #

總的來說,PL/Python 的目標是在 PostgreSQL 和 Python 世界之間提供一個“自然”的對映。這一點體現在以下描述的資料對映規則中。

44.2.1. 資料型別對映 #

當呼叫 PL/Python 函式時,其引數會從 PostgreSQL 資料型別轉換為相應的 Python 型別。

  • PostgreSQL 的 boolean 型別會被轉換為 Python 的 bool 型別。

  • PostgreSQL 的 smallintintbigintoid 型別會被轉換為 Python 的 int 型別。

  • PostgreSQL 的 realdouble 型別會被轉換為 Python 的 float 型別。

  • PostgreSQL 的 numeric 型別會被轉換為 Python 的 Decimal 型別。如果 cdecimal 包可用,則會從中匯入該型別;否則,將使用標準庫中的 decimal.Decimalcdecimal 的速度比 decimal 快得多。然而,在 Python 3.3 及更高版本中,cdecimal 已被整合到標準庫中,並命名為 decimal,因此不再有區別。

  • PostgreSQL 的 bytea 型別會被轉換為 Python 的 bytes 型別。

  • 所有其他資料型別,包括 PostgreSQL 的字串型別,都會被轉換為 Python 的 str 型別(在 Unicode 中,就像所有 Python 字串一樣)。

  • 對於非標量資料型別,請參見下文。

當 PL/Python 函式返回時,其返回值會轉換為函式宣告的 PostgreSQL 返回資料型別,轉換方式如下:

  • 當 PostgreSQL 返回型別為 boolean 時,返回值將根據 **Python** 的規則進行真值評估。也就是說,0 和空字串為 False,但值得注意的是,'f' 為 True。

  • 當 PostgreSQL 返回型別為 bytea 時,返回值將使用相應的 Python 內建函式轉換為 Python 的 bytes 型別,然後將結果轉換為 bytea

  • 對於所有其他 PostgreSQL 返回型別,返回值將使用 Python 內建函式 str 轉換為字串,然後將結果傳遞給 PostgreSQL 資料型別的輸入函式。(如果 Python 值是 float,則會使用 repr 內建函式而不是 str 進行轉換,以避免精度損失。)

    字串在傳遞給 PostgreSQL 時會自動轉換為 PostgreSQL 伺服器編碼。

  • 對於非標量資料型別,請參見下文。

請注意,宣告的 PostgreSQL 返回型別與實際返回物件的 Python 資料型別之間的邏輯不匹配不會被標記;無論如何都會進行值轉換。

44.2.2. NULL,None #

如果將 SQL NULL 值傳遞給函式,則該引數值在 Python 中將顯示為 None。例如,上面 Section 44.1 中顯示的 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 plpython3u;

如上所示,要從 PL/Python 函式返回 SQL NULL 值,請返回 None。無論函式是否為 strict,都可以這樣做。

44.2.3. 陣列,列表 #

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)

出於向後相容 PostgreSQL 9.6 及以下版本(當時不支援多維陣列)的目的,其他 Python 序列(如元組)也被接受。但是,它們始終被視為一維陣列,因為它們與複合型別存在歧義。出於相同的原因,當複合型別用於多維陣列時,必須用元組而不是列表來表示。

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

44.2.4. 複合型別 #

複合型別引數被作為 Python 對映(mapping)傳遞給函式。對映的元素名稱是複合型別的屬性名稱。如果傳入行中的屬性值為 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 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 NULL,請在相應位置插入 None

當返回複合型別的陣列時,不能將其作為列表返回,因為無法區分 Python 列表代表的是複合型別還是另一個數組維度。

對映(字典)

每個結果型別列的值都從對映中檢索,鍵為列名。示例:

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

任何額外的字典鍵/值對都將被忽略。缺失的鍵將被視為錯誤。要為任何列返回 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 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);

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

提交更正

如果您在文件中看到任何不正確、與您對特定功能的體驗不符或需要進一步澄清的內容,請使用 此表格 報告文件問題。