PostgreSQL 數(shù)據(jù)值

2021-09-06 10:23 更新
45.3.1. 數(shù)據(jù)類型映射
45.3.2. Null, None
45.3.3. 數(shù)組、列表
45.3.4. 組合類型
45.3.5. 集合返回函數(shù)

一般來講,PL/Python 的目標(biāo)是提供在 PostgreSQL 和 Python 世界之間的一種自然的映射。這包括下面介紹的數(shù)據(jù)映射規(guī)則。

45.3.1. 數(shù)據(jù)類型映射

在調(diào)用一個(gè) PL/Python 函數(shù)時(shí),它的參數(shù)會被從 PostgreSQL 的數(shù)據(jù)類型轉(zhuǎn)換成相應(yīng)的 Python 類型:

  • PostgreSQL 的boolean被轉(zhuǎn)換成 Python 的bool。

  • PostgreSQL 的smallintint被轉(zhuǎn)換成 Python 的int。 PostgreSQL 的bigintoid被轉(zhuǎn)換成 Python 2 的long或者 Python 3 的int。

  • PostgreSQL 的realdouble被轉(zhuǎn)換成 Python 的float

  • PostgreSQL 的numeric被轉(zhuǎn)換成 Python 的Decimal。如果存在cdecimal包,則會從其中導(dǎo)入該類型。否則將使用來自標(biāo)準(zhǔn)庫的decimal.Decimalcdecimaldecimal要更快。不過,在 Python 3.3 以及更高的版本中,cdecimal已經(jīng)被整合到了標(biāo)準(zhǔn)庫中(也是用decimal這個(gè)名字),因此也就不再有什么區(qū)別。

  • PostgreSQL 的bytea被轉(zhuǎn)換成 Python 的str(Python 2)和bytes(Python 3)。在 Python 2 中,串應(yīng)該被當(dāng)做沒有任何字符編碼的字節(jié)序列對待。

  • 包括 PostgreSQL 字符串類型在內(nèi)的所有其他數(shù)據(jù)類型會被轉(zhuǎn)換成一個(gè) Python 的str。在 Python 2 中,這個(gè)串將用 PostgreSQL 服務(wù)器編碼;在 Python 3 中,它將和所有串一樣使用 Unicode。

  • 對于非標(biāo)量數(shù)據(jù)類型,請見下文。

當(dāng)一個(gè) PL/Python 函數(shù)返回時(shí),會按照下列規(guī)則把它的返回值轉(zhuǎn)換成該函數(shù)聲明的 PostgreSQL 返回?cái)?shù)據(jù)類型:

  • 當(dāng) PostgreSQL 返回類型是boolean時(shí),返回值會被根據(jù)Python規(guī)則計(jì)算真假。也就是說,0 和空串是假,但是要特別注意'f'是真。

  • 當(dāng) PostgreSQL 返回類型是bytea時(shí),返回值會被使用相應(yīng)的 Python 內(nèi)建機(jī)制轉(zhuǎn)換成串(Python 2)或者字節(jié)(Python 3),結(jié)果將被轉(zhuǎn)換成bytea。

  • 對于所有其他 PostgreSQL 返回類型,返回值被使用 Python 內(nèi)建的str轉(zhuǎn)換成一個(gè)串,并且結(jié)果會被傳遞給 PostgreSQL 數(shù)據(jù)類型的輸入函數(shù)(如果該 Python 值是一個(gè)float,它會被用內(nèi)建的repr而不是str轉(zhuǎn)換,這是為了避免精度損失)。

    當(dāng) Python 2 的串被傳遞給 PostgreSQL 時(shí),它們被要求是 PostgreSQL 服務(wù)器編碼。在當(dāng)前服務(wù)器編碼中不可用的串將會產(chǎn)生錯(cuò)誤,但是并非所有的編碼失配都能被檢測到,因此當(dāng)沒有正確地將串編碼時(shí),垃圾數(shù)據(jù)仍然會產(chǎn)生。Unicode 串會被自動(dòng)地轉(zhuǎn)換為正確的編碼,因此使用 Unicode 串更加安全并且更加方便。在 Python 3 中,所有串都是 Unicode 串。

  • 對于非標(biāo)量數(shù)據(jù)類型,請見下文。

注意所聲明的 PostgreSQL 返回類型和實(shí)際返回對象的 Python 數(shù)據(jù)類型之間的邏輯失配不會被標(biāo)志,無論怎樣該值都會被轉(zhuǎn)換。

45.3.2. Null, None

如果一個(gè) SQL 空值 被傳遞給一個(gè)函數(shù),該參數(shù)值將作為 Python 中的None出現(xiàn)。例如,第 45.2 節(jié)中展示的pymax的函數(shù)定義對于空值輸入將會返回錯(cuò)誤的回答。我們可以為函數(shù)定義增加STRICTPostgreSQL做得更加合理:如果一個(gè)空值被傳入,該函數(shù)將根本不會被調(diào)用,而只是自動(dòng)地返回一個(gè)空結(jié)果。此外,我們可以在函數(shù)體中檢查空輸入:

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;

如前所示,要從一個(gè) PL/Python 函數(shù)返回一個(gè) SQL 空值,可返回值None。不管該函數(shù)嚴(yán)格與否都可以這樣做。

45.3.3. 數(shù)組、列表

SQL 數(shù)組會被作為一個(gè) Python 列表傳遞到 PL/Python 中。要從一個(gè) PL/Python 函數(shù)中返回出一個(gè) SQL 數(shù)組值,可返回一個(gè)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)

多維數(shù)組被當(dāng)做嵌套的Python列表傳入PL/Python。例如,一個(gè)2維數(shù)組是一個(gè)列表的列表。在把一個(gè)多維SQL數(shù)組從PL/Python函數(shù)返回出去時(shí),每一層的內(nèi)層列表都必須是相同的尺寸。例如:

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以及更低版本的向后兼容性,當(dāng)不支持多維數(shù)組時(shí),也接受元組之類的其他Python序列。不過,它們總是被當(dāng)做一維數(shù)組,因?yàn)樗鼈儠徒M合類型混淆。出于同樣的原因,當(dāng)一個(gè)組合類型被用在多維數(shù)組中時(shí),它必須被表示為一個(gè)元組而不是一個(gè)列表。

注意在 Python 中,串是序列,這可能產(chǎn)生與 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. 組合類型

組合類型參數(shù)被作為 Python 映射傳遞給函數(shù)。映射的元素名稱就是組合類型的屬性名。如果被傳遞的行中有一個(gè)屬性是空值,在映射中它的值是None。這里是一個(gè)例子:

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;

有多種方式從一個(gè) Python 函數(shù)返回行或者組合類型。下面的例子假設(shè)我們有:

CREATE TYPE named_value AS (
  name   text,
  value  integer
);

一個(gè)組合結(jié)果可以被返回為:

序列類型(一個(gè)元組或者列表,但不是集合,因?yàn)? 集合不能被索引)

被返回的序列對象必須具有和組合結(jié)果類型的域個(gè)數(shù)相同的項(xiàng)。索引號為 0 的項(xiàng)被分配給組合類型的第一個(gè)域,為 1 的項(xiàng)給第二個(gè)域,以此類推。例如:

CREATE FUNCTION make_pair (name text, value integer)
  RETURNS named_value
AS $$
  return ( name, value )
  # or alternatively, as tuple: return [ name, value ]
$$ LANGUAGE plpythonu;

要為任意列返回一個(gè) SQL 空,應(yīng)在對應(yīng)的位置插入None。

當(dāng)一個(gè)組合類型的數(shù)組被返回時(shí),它不能被返回為列表,因?yàn)闀磺逶揚(yáng)ython列表究竟是表示一個(gè)組合類型還是另一個(gè)數(shù)組維度。

映射(字典)

用列名作為鍵從映射中檢索每一個(gè)結(jié)果類型列的值。例如:

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

任何額外的字典鍵/值對都會被忽略。丟失的鍵會被當(dāng)做錯(cuò)誤。要為任意列返回一個(gè) SQL 空,應(yīng)用相應(yīng)的列名作為鍵插入None。

對象(任何提供方法__getattr__的對象)

這和映射的運(yùn)作方式相同。例如:

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參數(shù)的函數(shù)。例如:

CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$
return (1, 2)
$$ LANGUAGE plpythonu;

SELECT * FROM multiout_simple();

過程的輸出參數(shù)會以同樣的方式傳回。例如:

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. 集合返回函數(shù)

PL/Python函數(shù)也能返回標(biāo)量類型或者組合類型的集合。有多種方法可以做到這一點(diǎn),因?yàn)楸环祷氐膶ο笤趦?nèi)部會被轉(zhuǎn)變成一個(gè)迭代器。下面的例子假設(shè)我們有組合類型:

CREATE TYPE greeting AS (
  how text,
  who text
);

可從以下類型返回集合結(jié)果:

序列類型(元組、列表、集合)

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;

發(fā)生器(yield

CREATE FUNCTION greet (how text)
  RETURNS SETOF greeting
AS $$
  for who in [ "World", "PostgreSQL", "PL/Python" ]:
    yield ( how, who )
$$ LANGUAGE plpythonu;

也支持有OUT參數(shù)的集合返回函數(shù)(使用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);


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號