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 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3

44.6. 資料庫訪問 #

PL/Python 語言模組會自動匯入一個名為 plpy 的 Python 模組。此模組中的函式和常量可以在 Python 程式碼中以 plpy.foo 的形式使用。

44.6.1. 資料庫訪問函式 #

plpy 模組提供了一些函式來執行資料庫命令。

plpy.execute(query [, limit])

呼叫帶有查詢字串和可選行限制引數的 plpy.execute 會執行該查詢,並將結果返回給結果物件。

如果指定了 limit 並且大於零,則 plpy.execute 最多檢索 limit 行,這很像查詢中包含 LIMIT 子句。省略 limit 或將其指定為零將不會限制行數。

結果物件模擬列表或字典物件。結果物件可以透過行號和列名訪問。例如

rv = plpy.execute("SELECT * FROM my_table", 5)

my_table 中返回最多 5 行。如果 my_table 有一個名為 my_column 的列,則可以透過以下方式訪問它:

foo = rv[i]["my_column"]

可以使用內建的 len 函式獲取返回的行數。

結果物件還有以下附加方法:

nrows()

返回命令處理的行數。請注意,這不一定與返回的行數相同。例如,UPDATE 命令將設定此值,但不會返回任何行(除非使用了 RETURNING)。

status()

SPI_execute() 的返回值。

colnames()
coltypes()
coltypmods()

分別返回列名列表、列型別 OID 列表和列的型別特定的型別修飾符列表。

當在不產生結果集的命令(例如,不帶 RETURNINGUPDATEDROP TABLE)的結果物件上呼叫這些方法時,它們會引發異常。但是,在包含零行的結果集上使用這些方法是可以的。

__str__()

定義了標準的 __str__ 方法,以便可以例如使用 plpy.debug(rv) 來除錯查詢執行結果。

結果物件可以被修改。

請注意,呼叫 plpy.execute 將導致整個結果集被讀入記憶體。僅當您確定結果集相對較小時才使用此函式。如果您不想在獲取大型結果時冒著過度使用記憶體的風險,請使用 plpy.cursor 而不是 plpy.execute

plpy.prepare(query [, argtypes])
plpy.execute(plan [, arguments [, limit]])

plpy.prepare 準備查詢的執行計劃。如果查詢中有引數引用,則使用查詢字串和引數型別列表進行呼叫。例如:

plan = plpy.prepare("SELECT last_name FROM my_users WHERE first_name = $1", ["text"])

text 是您將為 $1 傳遞的變數的型別。如果您不想將任何引數傳遞給查詢,則第二個引數是可選的。

準備好語句後,您可以使用 plpy.execute 函式的變體來執行它:

rv = plpy.execute(plan, ["name"], 5)

將計劃作為第一個引數(而不是查詢字串),並將要替換到查詢中的值列表作為第二個引數傳遞。如果查詢不接受任何引數,則第二個引數是可選的。第三個引數是可選的行限制,與之前相同。

或者,您可以呼叫計劃物件上的 execute 方法:

rv = plan.execute(["name"], 5)

查詢引數和結果行欄位在 PostgreSQL 和 Python 資料型別之間進行轉換,如 第 44.2 節中所述。

當您使用 PL/Python 模組準備計劃時,它會自動儲存。閱讀 SPI 文件(第 45 節)以瞭解其含義。為了在函式呼叫之間有效利用這一點,需要使用持久化儲存字典 SDGD(請參閱 第 44.3 節)。例如:

CREATE FUNCTION usesavedplan() RETURNS trigger AS $$
    if "plan" in SD:
        plan = SD["plan"]
    else:
        plan = plpy.prepare("SELECT 1")
        SD["plan"] = plan
    # rest of function
$$ LANGUAGE plpython3u;
plpy.cursor(query)
plpy.cursor(plan [, arguments])

plpy.cursor 函式接受與 plpy.execute 相同的引數(行限制除外),並返回一個遊標物件,該物件允許您分塊處理大型結果集。與 plpy.execute 一樣,可以使用查詢字串或計劃物件以及引數列表,也可以將 cursor 函式作為計劃物件的方法進行呼叫。

遊標物件提供一個 fetch 方法,該方法接受一個整數引數並返回一個結果物件。每次呼叫 fetch 時,返回的物件將包含下一批行,其大小不超過引數值。一旦所有行都已提取完畢,fetch 將開始返回一個空的結果物件。遊標物件還提供一個 迭代器介面,每次生成一行,直到所有行都已提取完畢。以這種方式獲取的資料不會作為結果物件返回,而是作為字典返回,每個字典對應一行結果。

處理大型表資料的兩種方法的示例是:

CREATE FUNCTION count_odd_iterator() RETURNS integer AS $$
odd = 0
for row in plpy.cursor("select num from largetable"):
    if row['num'] % 2:
         odd += 1
return odd
$$ LANGUAGE plpython3u;

CREATE FUNCTION count_odd_fetch(batch_size integer) RETURNS integer AS $$
odd = 0
cursor = plpy.cursor("select num from largetable")
while True:
    rows = cursor.fetch(batch_size)
    if not rows:
        break
    for row in rows:
        if row['num'] % 2:
            odd += 1
return odd
$$ LANGUAGE plpython3u;

CREATE FUNCTION count_odd_prepared() RETURNS integer AS $$
odd = 0
plan = plpy.prepare("select num from largetable where num % $1 <> 0", ["integer"])
rows = list(plpy.cursor(plan, [2]))  # or: = list(plan.cursor([2]))

return len(rows)
$$ LANGUAGE plpython3u;

遊標會自動處理。但如果您想顯式釋放遊標持有的所有資源,請使用 close 方法。一旦關閉,就不能再從遊標中獲取資料了。

提示

請不要將 plpy.cursor 建立的物件與 Python 資料庫 API 規範定義的 DB-API 遊標混淆。它們除了名稱之外沒有任何共同之處。

44.6.2. 捕獲錯誤 #

訪問資料庫的函式可能會遇到錯誤,這些錯誤會導致它們中止並引發異常。 plpy.executeplpy.prepare 都可以引發 plpy.SPIError 的子類例項,該例項預設會終止函式。此錯誤可以像任何其他 Python 異常一樣處理,方法是使用 try/except 構造。例如:

CREATE FUNCTION try_adding_joe() RETURNS text AS $$
    try:
        plpy.execute("INSERT INTO users(username) VALUES ('joe')")
    except plpy.SPIError:
        return "something went wrong"
    else:
        return "Joe added"
$$ LANGUAGE plpython3u;

引發異常的實際類對應於導致錯誤的特定條件。有關可能條件的列表,請參閱 表 A.1plpy.spiexceptions 模組為每個 PostgreSQL 條件定義一個異常類,其名稱從條件名稱派生。例如,division_by_zero 變為 DivisionByZerounique_violation 變為 UniqueViolationfdw_error 變為 FdwError,依此類推。這些異常類都繼承自 SPIError。這種分離使得處理特定錯誤更加容易,例如:

CREATE FUNCTION insert_fraction(numerator int, denominator int) RETURNS text AS $$
from plpy import spiexceptions
try:
    plan = plpy.prepare("INSERT INTO fractions (frac) VALUES ($1 / $2)", ["int", "int"])
    plpy.execute(plan, [numerator, denominator])
except spiexceptions.DivisionByZero:
    return "denominator cannot equal zero"
except spiexceptions.UniqueViolation:
    return "already have that fraction"
except plpy.SPIError as e:
    return "other error, SQLSTATE %s" % e.sqlstate
else:
    return "fraction inserted"
$$ LANGUAGE plpython3u;

請注意,由於 plpy.spiexceptions 模組中的所有異常都繼承自 SPIError,因此處理它的 except 子句將捕獲任何資料庫訪問錯誤。

作為處理不同錯誤條件的替代方法,您可以捕獲 SPIError 異常,並在 except 塊中透過檢視異常物件的 sqlstate 屬性來確定特定的錯誤條件。此屬性是一個字串值,包含“SQLSTATE”錯誤程式碼。此方法提供了大致相同的功能:

提交更正

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