正如第 44.6.2 節中所述,從資料庫訪問引起的錯誤中恢復,可能會導致一種不良情況,即某些操作在其中一個操作失敗之前成功了,而在從該錯誤恢復後,資料將處於不一致狀態。PL/Python 提供了一種解決方案來解決這個問題,即顯式子事務。
考慮一個實現兩個賬戶之間轉賬的函式
CREATE FUNCTION transfer_funds() RETURNS void AS $$ try: plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'") plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'") except plpy.SPIError as e: result = "error transferring funds: %s" % e.args else: result = "funds transferred correctly" plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"]) plpy.execute(plan, [result]) $$ LANGUAGE plpython3u;
如果第二個 UPDATE
語句引發了異常,此函式將報告錯誤,但第一個 UPDATE
的結果仍將提交。換句話說,資金將從 Joe 的賬戶中取出,但不會轉到 Mary 的賬戶。
為避免此類問題,您可以將 plpy.execute
呼叫包裝在顯式子事務中。plpy
模組提供了一個幫助物件來管理顯式子事務,該物件是透過 plpy.subtransaction()
函式建立的。由該函式建立的物件實現了上下文管理器介面。使用顯式子事務,我們可以重寫函式如下:
CREATE FUNCTION transfer_funds2() RETURNS void AS $$ try: with plpy.subtransaction(): plpy.execute("UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'") plpy.execute("UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'") except plpy.SPIError as e: result = "error transferring funds: %s" % e.args else: result = "funds transferred correctly" plan = plpy.prepare("INSERT INTO operations (result) VALUES ($1)", ["text"]) plpy.execute(plan, [result]) $$ LANGUAGE plpython3u;
請注意,仍然需要使用 try
/except
。否則,異常將傳播到 Python 堆疊的頂部,並導致整個函式因 PostgreSQL 錯誤而中止,因此 operations
表不會插入任何行。子事務上下文管理器不會捕獲錯誤,它只確保在其作用域內執行的所有資料庫操作都將原子地提交或回滾。子事務塊的回滾發生在任何型別的異常退出時,而不僅僅是資料庫訪問引起的錯誤。在顯式子事務塊內引發的常規 Python 異常也會導致子事務回滾。
如果您在文件中看到任何不正確、與您對特定功能的體驗不符或需要進一步澄清的內容,請使用此表單報告文件問題。