Le 20/11/2020 à 16:18, Gilles Darold a écrit : > I will work later on a POC to demonstrate the use case I want to > implement.
Hi Andres, I have created a new version of the pg_statement_rollback extension [1] to demonstrate the use of the hooks on start_xact_command(), finish_xact_command() and AbortCurrentTransaction() to implement the statement-level rollback feature entirely driven at serverside. It require that the patch [2] I've provided be applied on PostgreSQL source first. Here is what can be achieved with this patch: LOAD 'pg_statement_rollback.so'; LOAD SET pg_statement_rollback.enabled TO on; SET CREATE SCHEMA testrsl; CREATE SCHEMA SET search_path TO testrsl,public; SET BEGIN; BEGIN CREATE TABLE tbl_rsl(id integer, val varchar(256)); CREATE TABLE INSERT INTO tbl_rsl VALUES (1, 'one'); INSERT 0 1 WITH write AS (INSERT INTO tbl_rsl VALUES (2, 'two') RETURNING id, val) SELECT * FROM write; id | val ----+----- 2 | two (1 row) UPDATE tbl_rsl SET id = 'two', val = 2 WHERE id = 1; -- >>>>> will fail psql:simple.sql:14: ERROR: invalid input syntax for type integer: "two" LINE 1: UPDATE tbl_rsl SET id = 'two', val = 2 WHERE id = 1; ^ SELECT * FROM tbl_rsl; -- Should show records id 1 + 2 id | val ----+----- 1 | one 2 | two (2 rows) COMMIT; COMMIT Actually unlike I've though this is the hook on finish_xact_command() that is useless. In the extension I'm executing the RELEASE/SAVEPOINT in the start_xact_command() hook before executing the next statement. The hook on AbortCurrentTransaction() is used to signal that a ROLLOBACK TO/SAVEPOINT need to be executed into the start_xact_command() hook instead of a RELEASE/SAVEPOINT. This works perfectly and do not crash PG anymore when compiled with assert. Advanced tests (with triggers, client savepoint, CTE, etc.) are available in the test/sql/ directory. Use of "make installcheck" allow to run the regression tests. Based on this result I really think that these hooks should be included to be able to extend PostgreSQL for such feature although I have not though about an other use that this one. Regards, I've attached all code for archiving but the current version can be found here too: [1] https://github.com/darold/pg_statement_rollbackv2 [2] https://raw.githubusercontent.com/darold/pg_statement_rollbackv2/main/command-start-finish-hook-v1.patch -- Gilles Darold http://www.darold.net/
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 03c553e7ea..81e1df27ef 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -314,6 +314,9 @@ typedef struct SubXactCallbackItem static SubXactCallbackItem *SubXact_callbacks = NULL; +/* Hook for plugins to get control of at end of AbortCurrentTransaction */ +AbortCurrentTransaction_hook_type abort_current_transaction_hook = NULL; + /* local function prototypes */ static void AssignTransactionId(TransactionState s); static void AbortTransaction(void); @@ -3358,6 +3361,9 @@ AbortCurrentTransaction(void) AbortCurrentTransaction(); break; } + + if (abort_current_transaction_hook) + (*abort_current_transaction_hook) (); } /* diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 7c5f7c775b..3fff54ff51 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -192,6 +192,14 @@ static void log_disconnections(int code, Datum arg); static void enable_statement_timeout(void); static void disable_statement_timeout(void); +/* + * Hook for plugins to get control at end/start of + * start_xact_command/finish_xact_command. The hook + * on start_xact_command might be useless as it happens + * before UtilityProccess and the Executor* hooks. + */ +XactCommandStart_hook_type start_xact_command_hook = NULL; +XactCommandFinish_hook_type finish_xact_command_hook = NULL; /* ---------------------------------------------------------------- * routines to obtain user input @@ -2634,6 +2642,7 @@ exec_describe_portal_message(const char *portal_name) static void start_xact_command(void) { + if (!xact_started) { StartTransactionCommand(); @@ -2649,11 +2658,19 @@ start_xact_command(void) * not desired, the timeout has to be disabled explicitly. */ enable_statement_timeout(); + + if (start_xact_command_hook) + (*start_xact_command_hook) (); + } + static void finish_xact_command(void) { + if (finish_xact_command_hook) + (*finish_xact_command_hook) (); + /* cancel active statement timeout after each command */ disable_statement_timeout(); diff --git a/src/include/access/xact.h b/src/include/access/xact.h index 7320de345c..2e866b2a91 100644 --- a/src/include/access/xact.h +++ b/src/include/access/xact.h @@ -467,4 +467,8 @@ extern void EnterParallelMode(void); extern void ExitParallelMode(void); extern bool IsInParallelMode(void); +/* Hook for plugins to get control in start_xact_command() and finish_xact_command() */ +typedef void (*AbortCurrentTransaction_hook_type) (void); +extern PGDLLIMPORT AbortCurrentTransaction_hook_type abort_current_transaction_hook; + #endif /* XACT_H */ diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h index 437642cc72..a8bef2f639 100644 --- a/src/include/tcop/pquery.h +++ b/src/include/tcop/pquery.h @@ -42,4 +42,10 @@ extern uint64 PortalRunFetch(Portal portal, long count, DestReceiver *dest); +/* Hook for plugins to get control in start_xact_command() and finish_xact_command() */ +typedef void (*XactCommandStart_hook_type) (void); +extern PGDLLIMPORT XactCommandStart_hook_type start_xact_command_hook; +typedef void (*XactCommandFinish_hook_type) (void); +extern PGDLLIMPORT XactCommandFinish_hook_type finish_xact_command_hook; + #endif /* PQUERY_H */ diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index fde701bfd4..23ddca9891 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -13,6 +13,7 @@ A_Expr_Kind A_Indices A_Indirection A_Star +AbortCurrentTransaction_hook_type AbsoluteTime AccessMethodInfo AccessPriv @@ -2794,6 +2795,8 @@ XPVIV XPVMG XactCallback XactCallbackItem +XactCommandStart_hook_type +XactCommandFinish_hook_type XactEvent XactLockTableWaitInfo XidBoundsViolation
pg_statement_rollbackv2.tar.gz
Description: application/gzip