PostgreSQL PL/Tcl中的顯式子事務(wù)

2021-09-06 10:10 更新

第 43.8 節(jié)中介紹的數(shù)據(jù)庫訪問導(dǎo)致的錯誤中恢復(fù)可能導(dǎo)致一種不可取的情況,其中一些操作在它們中的一個失敗前成功完成,并且在從錯誤中恢復(fù)過來后數(shù)據(jù)還處于一種不一致的狀態(tài)。PL/Tcl以顯式子事務(wù)的形式為這類問題提供了一個解決方案。

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

CREATE FUNCTION transfer_funds() RETURNS void AS $$
    if [catch {
        spi_exec "UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'"
        spi_exec "UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'"
    } errormsg] {
        set result [format "error transferring funds: %s" $errormsg]
    } else {
        set result "funds transferred successfully"
    }
    spi_exec "INSERT INTO operations (result) VALUES ('[quote $result]')"
$$ LANGUAGE pltcl;

如果第二個UPDATE語句導(dǎo)致一個異常,這個函數(shù)將記下該失敗,但是第一個UPDATE的結(jié)果將被提交。換句話說,資金將從Joe的賬戶中被取走,但不會被轉(zhuǎn)到Mary的賬戶。這是因為每個spi_exec都是一個單獨的子事務(wù),并且那些子事務(wù)中只有一個被回滾。

為了處理這類情況,你可以把多個數(shù)據(jù)庫操作包裹在一個顯式子事務(wù)中,它將作為一個整體成功完成或者回滾。PL/Tcl提供了一個subtransaction命令來做這件事情。我們可以把我們的函數(shù)寫成:

CREATE FUNCTION transfer_funds2() RETURNS void AS $$
    if [catch {
        subtransaction {
            spi_exec "UPDATE accounts SET balance = balance - 100 WHERE account_name = 'joe'"
            spi_exec "UPDATE accounts SET balance = balance + 100 WHERE account_name = 'mary'"
        }
    } errormsg] {
        set result [format "error transferring funds: %s" $errormsg]
    } else {
        set result "funds transferred successfully"
    }
    spi_exec "INSERT INTO operations (result) VALUES ('[quote $result]')"
$$ LANGUAGE pltcl;

注意,為了實現(xiàn)這個目的仍要求使用catch。否則錯誤將傳播到該函數(shù)的頂層,導(dǎo)致想要對operations表的插入被阻止。subtransaction命令不會捕捉錯誤,它僅確保報告錯誤時在其范圍內(nèi)執(zhí)行的所有數(shù)據(jù)庫操作將被一起回滾。

一個顯式子事務(wù)的回滾發(fā)生在包含它的Tcl代碼報告任何錯誤時,而不僅僅是數(shù)據(jù)庫訪問導(dǎo)致的錯誤。因此一個subtransaction命令中發(fā)生的常規(guī)Tcl異常也將導(dǎo)致該子事務(wù)被回滾。不過,無錯誤退出到包含子事務(wù)的Tcl代碼外面(例如,由于return)不會導(dǎo)致回滾。


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號