PostgreSQL 顯式子事務(wù)

2021-09-06 10:28 更新
45.8.1. 子事務(wù)上下文管理器
45.8.2. 更舊的 Python 版本

第 45.7.2 節(jié)中所述的從數(shù)據(jù)庫訪問導(dǎo)致的錯誤中恢復(fù)可能導(dǎo)致不好的情況:某些操作在其中一個操作失敗之前已經(jīng)成功,并且在從錯誤中恢復(fù)后這些操作的數(shù)據(jù)形成了一種不一致的狀態(tài)。PL/Python 通過顯式子事務(wù)的形式為這種問題提供了一套解決方案。

45.8.1. 子事務(wù)上下文管理器

考慮一個實現(xiàn)在兩個賬戶間進(jìn)行轉(zhuǎn)賬的函數(shù):

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 plpythonu;

如果第二個UPDATE語句導(dǎo)致產(chǎn)生一個異常,這個函數(shù)將會報告該錯誤,但是第一個UPDATE的結(jié)果卻不會被提交。換句話說,資金將從 Joe 的賬戶中收回,而不會轉(zhuǎn)移到 Mary 的賬戶中。

為了避免這類問題,可以把plpy.execute包裹在顯式子事務(wù)中。plpy模塊提供了一種助手對象來管理用plpy.subtransaction()函數(shù)創(chuàng)建的顯式子事務(wù)。這個函數(shù)創(chuàng)建的對象實現(xiàn)了上下文管理器接口。通過使用顯式子事務(wù),我們可以把函數(shù)寫成:

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 plpythonu;

注意仍需使用try/catch。否則異常會傳播到 Python 棧的頂層并且將導(dǎo)致整個函數(shù)以一個PostgreSQL錯誤中止,這樣不會有任何行被插入到operations表。子事務(wù)上下文管理器不會捕捉錯誤,它只確保在其范圍內(nèi)執(zhí)行的所有數(shù)據(jù)庫操作將被原子性地提交或者回滾。在任何類型的異常(并非只是數(shù)據(jù)庫訪問產(chǎn)生的錯誤)退出時,會發(fā)生子事務(wù)塊回滾。在顯式子事務(wù)塊內(nèi)部產(chǎn)生的常規(guī) Python 異常也會導(dǎo)致子事務(wù)被回滾。

45.8.2. 更舊的 Python 版本

Python 2.6 中默認(rèn)可用的是使用with關(guān)鍵詞的上下文管理器語法。為了與舊的 Python 版本兼容, 你可以使用別名enterexit調(diào)用子事務(wù)管理器的__enter____exit__函數(shù)。轉(zhuǎn)移資金的例子函數(shù)可以寫成:

CREATE FUNCTION transfer_funds_old() RETURNS void AS $$ try: subxact = plpy.subtransaction() subxact.enter() 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: import sys subxact.exit(*sys.exc_info()) raise else: subxact.exit(None, None, None) 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 plpythonu;


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號