即使函式中止,UDF 中的持久插入也是如此
我有一個相當複雜的 UDF(在一堆表周圍移動並創建一堆新表),其中可能會發生多次中止。在每次操作之前,我想記錄操作發生的時間和查詢本身。UDF 如下所示:
log function_start log sql1 execute sql1 log sql2 execute sql2 ... log sqlN execute sqlN log function_end
每條日誌語句都意味著在下表中插入一條新記錄:
CREATE TABLE backup_logs ( id serial NOT NULL, t timestamp with time zone default now(), sql text, CONSTRAINT backup_logs_pkey PRIMARY KEY (id) )
在中止的情況下,我想
sql1, sql2, ... sqlN
回滾,但inserto into backup_logs
要堅持。問題:我怎樣才能做到這一點?
當發生異常時,一切都會回滾。您希望在目前事務上下文“外部”執行選定的程式碼,這通常稱為自主事務。目前尚未實施(截至 pg 9.4)。Postgres TODO wiki 中有一個項目,但這是一件棘手的事情,不要屏住呼吸。
現在您可以使用附加模組
dblink
。它提供了在單獨的連接(因此也是單獨的事務)中連接到另一個 DB 以在那裡執行 SQL 的功能。已完成的事情已經完成,無法回滾。通過再次連接到同一個數據庫,您可以有效地實現自主事務(有一些額外的成本)。它的速度相當快。概念證明
準備工作
您需要為每個數據庫(執行函式的位置)安裝一次 dblink:
CREATE EXTENSION dblink;
為方便起見,我將連接資訊封裝在一個
FOREIGN SERVER
加號中USER MAPPING
:CREATE SERVER myserver FOREIGN DATA WRAPPER dblink_fdw OPTIONS (hostaddr '127.0.0.1', dbname 'test'); CREATE USER MAPPING FOR role_source SERVER myserver OPTIONS (user 'role_target', password 'secret');
連接字元串有多種選項。該範例適用於名為localhost的數據庫中的使用者
role_source
進行連接。源和目標的角色名稱可能相同。我在這裡使用密碼(簡單範例),但我更喜歡無密碼訪問,因此我們不必保存 PW(並且它不會以備份等形式結束):role_target``test``127.0.0.1
但是,根據文件:
只有超級使用者可以
dblink_connect
用來創建非密碼認證的連接。如果非超級使用者需要此功能,請dblink_connect_u
改用。或者創建一個
SECURITY DEFINER
函式來封裝相關部分。見下文。主功能
CREATE OR REPLACE FUNCTION f_work() RETURNS void AS $func$ DECLARE sql1 text := 'SELECT 1'; -- dummy code sql2 text := 'SELECT 2'; BEGIN PERFORM dblink_connect('myserver'); -- name of foreign server PERFORM dblink_exec($f$INSERT INTO backup_logs(sql) VALUES ('f_work() start')$f$); -- log start PERFORM dblink_exec('INSERT INTO backup_logs(sql) SELECT ' || quote_literal(sql1)); -- log sql1 EXECUTE sql1; -- RAISE EXCEPTION 'foo'; -- unquote to test error case PERFORM dblink_exec('INSERT INTO backup_logs(sql) SELECT ' || quote_literal(sql2)); -- log sql2 EXECUTE sql2; PERFORM dblink_exec($f$INSERT INTO backup_logs(sql) VALUES ('f_work() end')$f$); -- log end PERFORM dblink_disconnect(); END $func$ LANGUAGE plpgsql VOLATILE;
確保為
USER MAPPING
您執行此功能的角色提供一個,或使其成為SECURITY DEFINER
您要使用的角色擁有的功能: