diff --git a/src/backend/access/fdwxact/fdwxact.c b/src/backend/access/fdwxact/fdwxact.c index fdc6b1f415..a0ac22d9eb 100644 --- a/src/backend/access/fdwxact/fdwxact.c +++ b/src/backend/access/fdwxact/fdwxact.c @@ -232,7 +232,7 @@ static bool fdwXactExitRegistered = false; static void register_fdwxact(Oid serverid, Oid userid, bool modified); static void FdwXactParticipantEndTransaction(FdwXactParticipant *fdw_part, bool onephase, bool for_commit); -static bool checkForeignTwophaseCommitRequired(void); +static bool checkForeignTwophaseCommitRequired(bool for_prepare); static FdwXact FdwXactInsertFdwXactEntry(TransactionId xid, FdwXactParticipant *fdw_part); static FdwXact insert_fdwxact(Oid dbid, TransactionId xid, Oid serverid, Oid userid, Oid umid, char *fdwxact_id); @@ -464,7 +464,7 @@ PreCommit_FdwXacts(void) * Check if we need to use foreign twophase commit. It's always false if * foreign twophase commit is disabled. */ - need_twophase_commit = checkForeignTwophaseCommitRequired(); + need_twophase_commit = checkForeignTwophaseCommitRequired(false); /* * Prepare foreign transactions on foreign servers that support two-phase @@ -511,10 +511,10 @@ PreCommit_FdwXacts(void) * in FdwXactParticipants and local server itself. */ static bool -checkForeignTwophaseCommitRequired(void) +checkForeignTwophaseCommitRequired(bool for_prepare) { ListCell *lc; - bool need_twophase_commit; + bool need_twophase_commit = false; bool have_notwophase; int nserverswritten = 0; int nserverstwophase = 0; @@ -538,32 +538,43 @@ checkForeignTwophaseCommitRequired(void) /* check if there is a server that doesn't support two-phase commit */ have_notwophase = (nserverswritten != nserverstwophase); - /* Did we modify the local non-temporary data? */ - if ((MyXactFlags & XACT_FLAGS_WROTENONTEMPREL) != 0) - nserverswritten++; - - if (nserverswritten <= 1) - return false; - - if (foreign_twophase_commit == FOREIGN_TWOPHASE_COMMIT_REQUIRED) + if (for_prepare) { /* - * In 'required' case, we require for all modified server to support - * two-phase commit. + * In case of PREPARE TRANSACTION we must use 2PC even when + * only one foreign server is modified */ - need_twophase_commit = (nserverswritten >= 2); + need_twophase_commit = (nserverswritten >= 1); } - else + + /* Did we modify the local non-temporary data? */ + if ((MyXactFlags & XACT_FLAGS_WROTENONTEMPREL) != 0) + nserverswritten++; + + if (!for_prepare) { - Assert(foreign_twophase_commit == FOREIGN_TWOPHASE_COMMIT_PREFER); + if (nserverswritten <= 1) + return false; - /* - * In 'prefer' case, we prepare transactions on only servers that - * capable of two-phase commit. - */ - need_twophase_commit = (nserverstwophase >= 2); - } + if (foreign_twophase_commit == FOREIGN_TWOPHASE_COMMIT_REQUIRED) + { + /* + * In 'required' case, we require for all modified server to support + * two-phase commit. + */ + need_twophase_commit = (nserverswritten >= 2); + } + else + { + Assert(foreign_twophase_commit == FOREIGN_TWOPHASE_COMMIT_PREFER); + /* + * In 'prefer' case, we prepare transactions on only servers that + * capable of two-phase commit. + */ + need_twophase_commit = (nserverstwophase >= 1); + } + } /* * If foreign two phase commit is required then all foreign serves must be * capable of doing two-phase commit @@ -589,6 +600,12 @@ checkForeignTwophaseCommitRequired(void) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot process a distributed transaction that has operated on a foreign server"), errdetail("foreign_twophase_commit is \'required\' but the transaction has some foreign servers which are not capable of two-phase commit"))); + + if (have_notwophase && for_prepare) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot process a distributed transaction that has operated on a foreign server"), + errdetail("PREPARE TRANSACTION requires all foreign servers to support two-phase commit"))); } return need_twophase_commit; @@ -1001,7 +1018,7 @@ AtPrepare_FdwXacts(void) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot PREPARE a distributed transaction when foreign_twophase_commit is \'disabled\'"))); - if (!checkForeignTwophaseCommitRequired()) + if (!checkForeignTwophaseCommitRequired(true)) return; /* Prepare transactions on participating foreign servers. */ diff --git a/src/test/modules/test_fdwxact/expected/test_fdwxact.out b/src/test/modules/test_fdwxact/expected/test_fdwxact.out index a5c8b89655..97f5052928 100644 --- a/src/test/modules/test_fdwxact/expected/test_fdwxact.out +++ b/src/test/modules/test_fdwxact/expected/test_fdwxact.out @@ -186,3 +186,41 @@ BEGIN; INSERT INTO t VALUES (1); INSERT INTO ft_no2pc_1 VALUES (1); COMMIT; +SELECT count(*) FROM pg_foreign_xacts(); + count +------- + 0 +(1 row) + +BEGIN; +INSERT INTO ft_2pc_1 VALUES(1); +INSERT INTO t VALUES(3); +PREPARE TRANSACTION 'global_x1'; +SELECT count(*) FROM pg_foreign_xacts(); + count +------- + 1 +(1 row) + +COMMIT PREPARED 'global_x1'; +BEGIN; +INSERT INTO ft_2pc_1 VALUES(1); +PREPARE TRANSACTION 'global_x1'; +SELECT count(*) FROM pg_foreign_xacts(); + count +------- + 1 +(1 row) + +COMMIT PREPARED 'global_x1'; +--ERROR case +BEGIN; +INSERT INTO ft_no2pc_1 VALUES (1); +PREPARE TRANSACTION 'global_x1'; +ERROR: cannot process a distributed transaction that has operated on a foreign server +DETAIL: PREPARE TRANSACTION requires all foreign servers to support two-phase commit +BEGIN; +INSERT INTO ft_no2pc_1 VALUES (1); +INSERT INTO ft_2pc_1 VALUES (1); +INSERT INTO t VALUES (1); +COMMIT /*should do 2PC*/; diff --git a/src/test/modules/test_fdwxact/sql/test_fdwxact.sql b/src/test/modules/test_fdwxact/sql/test_fdwxact.sql index 554312542f..271f651baf 100644 --- a/src/test/modules/test_fdwxact/sql/test_fdwxact.sql +++ b/src/test/modules/test_fdwxact/sql/test_fdwxact.sql @@ -160,10 +160,12 @@ BEGIN; INSERT INTO ft_1 VALUES (1); INSERT INTO ft_2pc_1 VALUES (1); COMMIT; + BEGIN; INSERT INTO ft_no2pc_1 VALUES (1); INSERT INTO ft_2pc_1 VALUES (1); COMMIT; + BEGIN; INSERT INTO ft_1 VALUES (1); INSERT INTO ft_2 VALUES (1); @@ -176,3 +178,29 @@ BEGIN; INSERT INTO t VALUES (1); INSERT INTO ft_no2pc_1 VALUES (1); COMMIT; + +SELECT count(*) FROM pg_foreign_xacts(); + +BEGIN; +INSERT INTO ft_2pc_1 VALUES(1); +INSERT INTO t VALUES(3); +PREPARE TRANSACTION 'global_x1'; +SELECT count(*) FROM pg_foreign_xacts(); +COMMIT PREPARED 'global_x1'; + +BEGIN; +INSERT INTO ft_2pc_1 VALUES(1); +PREPARE TRANSACTION 'global_x1'; +SELECT count(*) FROM pg_foreign_xacts(); +COMMIT PREPARED 'global_x1'; + +--ERROR case +BEGIN; +INSERT INTO ft_no2pc_1 VALUES (1); +PREPARE TRANSACTION 'global_x1'; + +BEGIN; +INSERT INTO ft_no2pc_1 VALUES (1); +INSERT INTO ft_2pc_1 VALUES (1); +INSERT INTO t VALUES (1); +COMMIT /*should do 2PC*/;