天天看點

PostgreSQL 事務控制及invalid transaction termination研究

PostgreSQL 事務控制

PostgreSQL 事務控制及invalid transaction termination研究

在我們做PG遷移的時候,用AWS工具經常會遇到這樣的提示“Transactioncontrol is only possible from the top level or nested callinvocations without any other intervening command”

在點一下具體的問題,如上圖所示,發現是commit的問題。

這裡我寫了一個存儲過程,用python程式調用來測試了一下這個問題。

1.首先建立一個表

create tabletest(a1,a2) as select 1 n,current_timestamp t;

2.接下來建立一個存儲過程

create or replaceprocedure test_insert(a1 int, a2 int) as

$$

begin

for i in a1..a2loop

insert into testvalues (i,current_timestamp);

commit;

end loop;

end;

$$ language plpgsql;

3.在指令行執行調用

PostgreSQL 事務控制及invalid transaction termination研究

存儲過程執行成功,插入4條資料。

4.接下來我們使用python程式來調用存儲過程

Python程式代碼如下:

PostgreSQL 事務控制及invalid transaction termination研究

執行python

PostgreSQL 事務控制及invalid transaction termination研究

發現報錯,這裡報“invalidtransaction termination”,然後指向存儲過程第5行COMMIT。

5.取消存儲過程中的commit

PostgreSQL 事務控制及invalid transaction termination研究

這一次把commit取消,再次執行python

PostgreSQL 事務控制及invalid transaction termination研究

查詢資料庫中的資料

PostgreSQL 事務控制及invalid transaction termination研究

通過測試可以發現,在指令行中不管你在存儲過程中加commit或者不加commit,都不會報錯。而通過外部程式調用的情況,如果你在存儲過程中加了commit,就會報“invalidtransaction termination”

通過搜尋,我們發現官方文檔如下描述。

If CALL isexecuted in a transaction block, then the called procedure cannotexecute transaction control statements. Transaction controlstatements are only allowed if CALL isexecuted in its own transaction.

如果CALL在事務塊中執行,則被調用的存儲過程無法執行事務控制語句(也就是commit/rollback)等TCL語句。隻有CALL在自己事務中執行時,才允許事務控制語句。而我們使用python程式子產品psycopg連接配接的時候,通常是以begin開始運作的,這就代表了CALL在事務塊中運作,是沒辦法在存儲過程中執行commit的。

這一點我們可以再證明一下。再次在存儲過程中增加commit語句,然後運作starttransaction。

PostgreSQL 事務控制及invalid transaction termination研究

可以看到指令行也報了ERROR: invalid transaction termination的錯誤。

那麼這個問題如何解決呢?

需要我們在應用程式進行設定,增加conn.autocommit= True,這樣就使用了資料庫call中本身的事務,而不是程式在開啟的一層事務。

PostgreSQL 事務控制及invalid transaction termination研究
PostgreSQL 事務控制及invalid transaction termination研究

還需說明一下,在官方文檔是如下介紹的:

Transactioncontrol is only possible in CALL or DO invocationsfrom the top level or nested CALL or DO invocationswithout any other intervening command. For example, if the call stackis CALLproc1() → CALLproc2() → CALLproc3(),then the second and third procedures can perform transaction controlactions. But if the call stack is CALLproc1() → SELECTfunc2() → CALLproc3(),then the last procedure cannot do transaction control, because ofthe SELECT inbetween.

意思是事務控制隻能在call或者do從頂層進行調用。在沒有任何其他中間指令的嵌套CALL或DO調用中也能進行事務控制。例如,如果調用棧是CALLproc1() → CALL proc2() → CALLproc3(),那麼第二個和第三個過程可以執行事務控制動作。但是如果調用棧是CALLproc1() → SELECT func2() → CALLproc3(),則最後一個過程不能做事務控制,因為中間有個SELECT。

以上是我們關于此類事務控制問題的一些小研究。