2010-03-11 48 views
10

來自MS SQL世界,我傾向於大量使用存儲過程。我目前正在編寫一個應用程序,它使用了很多PostgreSQL plpgsql函數。我想要做的就是回滾特定函數中包含的所有INSERT/UPDATES,如果我在其中的任何位置發生異常。PostgreSQL:回滾plpgsql函數中的事務?

我最初的印象是,每個函數都包含在自己的事務中,並且異常會自動回滾所有內容。但是,情況似乎並非如此。我想知道是否應該將保存點與異常處理結合使用呢?但我並不真正理解事務和保存點之間的區別,以瞭解這是否是最佳方法。有什麼建議嗎?

CREATE OR REPLACE FUNCTION do_something(
     _an_input_var int 
       ) RETURNS bool AS $$ 
     DECLARE 
       _a_variable int; 
     BEGIN 
       INSERT INTO tableA (col1, col2, col3) 
         VALUES (0, 1, 2); 

       INSERT INTO tableB (col1, col2, col3) 
         VALUES (0, 1, 'whoops! not an integer'); 

       -- The exception will cause the function to bomb, but the values 
       -- inserted into "tableA" are not rolled back.  

       RETURN True; 
END; $$ LANGUAGE plpgsql; 
+0

你可以發佈一個函數的例子,不會像你期望的那樣回滾一切嗎?在調用語句的事務上下文中執行PL/pgSQL函數* do *,但BEGIN..EXCEPTION塊可以修改該行爲。沒有看到一個例子,很難給出適當的建議。 – 2010-03-11 18:19:40

+0

編輯添加示例。謝謝。 – jamieb 2010-03-11 18:33:52

+0

我正在運行8.4.2,創建tableA和B,每個都有三個int列,運行您的示例(在INSERT INTO tableB行結束時刪除「;」)並將其轟炸。我檢查了兩張桌子,他們都是空的。我甚至在兩者之間添加了一些調試代碼來驗證記錄在失敗之前是否存在,然後在失敗之後失效。 – 2010-03-11 19:00:03

回答

13

函數確實表示一個事務。您不必在BEGIN/COMMIT中包裝功能。

+7

這對於PostgreSQL來說並非如此。單個SQL語句在未作爲顯式事務的一部分執行時(不在BEGIN和COMMIT/END之間執行)作爲單個事務執行。但是如果這樣的語句調用多個函數,那麼所有的函數都在一個事務中執行。另外,當你有多個語句事務(顯式BEGIN,COMMIT)並且這些語句調用某些過程時,所有這些都將在單個事務中執行。正如其他人所說:保存點是要走的路。 – 2010-03-11 18:32:27

+2

@Jacek:Joshua是對的,一個函數確實代表了它自己的事務。您不需要保存點來回滾兩個插入,如果一個失敗,它們都會失敗。 – 2010-03-11 19:08:47

+10

@Frank:但那是因爲外部交易。在同一個語句或事務中的前面的語句中調用的其他函數中的插入也會失敗。除非使用保存點,否則無法回滾該函數的插入而無需回滾外部事務的其他結果。 函數體總是在一個事務中執行,但它本身不是一個事務。沒有辦法調用不啓動事務的函數。 – 2010-03-12 07:30:52

1

docs這樣說:

保存點是事務,允許它的成立是爲了被回滾之後執行的所有命令中一個特殊的標記,該交易狀態恢復到什麼當時是保存點。

他們也舉例說明。

編輯:

你需要在BEGIN和COMMIT命令來包裝一個transaction

事務由事務的SQL命令以BEGIN和COMMIT命令周圍設置

+0

我不同意文檔清晰。 「保存點」的描述聽起來正是我所知道的「交易」。保存點是原子嗎? – jamieb 2010-03-11 17:56:31

+0

據我所知,它們只是您可以回退的交易中的標記。整個交易是原子的;直到提交爲止,任何其他交易都不會看到任何更改。 – tom 2010-03-11 23:58:27

1

保存點可以被用於模擬嵌套事務。因爲postgresql事務是一系列將被應用或丟棄的語句,所以保存點可以在該序列中標記允許回滾的點。

由於不支持真正的嵌套交易,這是你最好的選擇(也是最好的選擇)。

+0

您是否嘗試過在plpgsql函數中使用保存點? – jamieb 2010-03-11 18:10:22

4

不能使用commit或rollback命令進入功能,但你可以使用你的函數成提交的事務,

BEGIN TRANSACTION; SELECT do_something();承諾;

該SQL腳本僅在do_something中沒有異常的情況下提交,然後它將回滾該函數的事務。