Skip to content

Commit 7607a30

Browse files
committed
port fdw patches
1 parent 2aa6c03 commit 7607a30

File tree

4 files changed

+284
-41
lines changed

4 files changed

+284
-41
lines changed

contrib/postgres_fdw/connection.c

Lines changed: 230 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include "access/htup_details.h"
1818
#include "catalog/pg_user_mapping.h"
1919
#include "access/xact.h"
20+
#include "access/xtm.h"
21+
#include "access/transam.h"
2022
#include "mb/pg_wchar.h"
2123
#include "miscadmin.h"
2224
#include "pgstat.h"
@@ -72,12 +74,18 @@ static unsigned int prep_stmt_number = 0;
7274
/* tracks whether any work is needed in callback functions */
7375
static bool xact_got_connection = false;
7476

77+
typedef long long csn_t;
78+
static csn_t currentGlobalTransactionId = 0;
79+
static int currentLocalTransactionId = 0;
80+
static PGconn* currentConnection = NULL;
81+
7582
/* prototypes of private functions */
7683
static PGconn *connect_pg_server(ForeignServer *server, UserMapping *user);
7784
static void disconnect_pg_server(ConnCacheEntry *entry);
7885
static void check_conn_params(const char **keywords, const char **values);
7986
static void configure_remote_session(PGconn *conn);
8087
static void do_sql_command(PGconn *conn, const char *sql);
88+
static void do_sql_send_command(PGconn *conn, const char *sql);
8189
static void begin_remote_xact(ConnCacheEntry *entry);
8290
static void pgfdw_xact_callback(XactEvent event, void *arg);
8391
static void pgfdw_subxact_callback(SubXactEvent event,
@@ -403,6 +411,19 @@ do_sql_command(PGconn *conn, const char *sql)
403411
PQclear(res);
404412
}
405413

414+
static void
415+
do_sql_send_command(PGconn *conn, const char *sql)
416+
{
417+
if (PQsendQuery(conn, sql) != PGRES_COMMAND_OK)
418+
{
419+
PGresult *res = PQgetResult(conn);
420+
421+
elog(WARNING, "Failed to send command %s", sql);
422+
pgfdw_report_error(ERROR, res, conn, true, sql);
423+
PQclear(res);
424+
}
425+
}
426+
406427
/*
407428
* Start remote transaction or subtransaction, if needed.
408429
*
@@ -417,15 +438,26 @@ static void
417438
begin_remote_xact(ConnCacheEntry *entry)
418439
{
419440
int curlevel = GetCurrentTransactionNestLevel();
441+
PGresult *res;
442+
420443

421444
/* Start main transaction if we haven't yet */
422445
if (entry->xact_depth <= 0)
423446
{
447+
TransactionId gxid = GetTransactionManager()->GetGlobalTransactionId();
424448
const char *sql;
425449

426450
elog(DEBUG3, "starting remote transaction on connection %p",
427451
entry->conn);
428452

453+
if (TransactionIdIsValid(gxid))
454+
{
455+
char stmt[64];
456+
snprintf(stmt, sizeof(stmt), "select public.dtm_join_transaction(%d)", gxid);
457+
res = PQexec(entry->conn, stmt);
458+
PQclear(res);
459+
}
460+
429461
if (IsolationIsSerializable())
430462
sql = "START TRANSACTION ISOLATION LEVEL SERIALIZABLE";
431463
else
@@ -434,6 +466,41 @@ begin_remote_xact(ConnCacheEntry *entry)
434466
do_sql_command(entry->conn, sql);
435467
entry->xact_depth = 1;
436468
entry->changing_xact_state = false;
469+
470+
if (UseTsDtmTransactions)
471+
{
472+
if (currentConnection == NULL)
473+
{
474+
currentConnection = entry->conn;
475+
}
476+
else if (entry->conn != currentConnection)
477+
{
478+
if (!currentGlobalTransactionId)
479+
{
480+
char *resp;
481+
res = PQexec(currentConnection, psprintf("SELECT public.dtm_extend('%d.%d')",
482+
MyProcPid, ++currentLocalTransactionId));
483+
484+
if (PQresultStatus(res) != PGRES_TUPLES_OK)
485+
{
486+
pgfdw_report_error(ERROR, res, currentConnection, true, sql);
487+
}
488+
resp = PQgetvalue(res, 0, 0);
489+
if (resp == NULL || (*resp) == '\0' || sscanf(resp, "%lld", &currentGlobalTransactionId) != 1)
490+
{
491+
pgfdw_report_error(ERROR, res, currentConnection, true, sql);
492+
}
493+
PQclear(res);
494+
}
495+
res = PQexec(entry->conn, psprintf("SELECT public.dtm_access(%llu, '%d.%d')", currentGlobalTransactionId, MyProcPid, currentLocalTransactionId));
496+
497+
if (PQresultStatus(res) != PGRES_TUPLES_OK)
498+
{
499+
pgfdw_report_error(ERROR, res, entry->conn, true, sql);
500+
}
501+
PQclear(res);
502+
}
503+
}
437504
}
438505

439506
/*
@@ -643,6 +710,78 @@ pgfdw_report_error(int elevel, PGresult *res, PGconn *conn,
643710
PQclear(res);
644711
}
645712

713+
typedef bool (*DtmCommandResultHandler) (PGresult *result, void *arg);
714+
715+
static bool
716+
RunDtmStatement(char const * sql, unsigned expectedStatus, DtmCommandResultHandler handler, void *arg)
717+
{
718+
HASH_SEQ_STATUS scan;
719+
ConnCacheEntry *entry;
720+
bool allOk = true;
721+
722+
hash_seq_init(&scan, ConnectionHash);
723+
while ((entry = (ConnCacheEntry *) hash_seq_search(&scan)))
724+
{
725+
if (entry->xact_depth > 0)
726+
{
727+
do_sql_send_command(entry->conn, sql);
728+
}
729+
}
730+
731+
hash_seq_init(&scan, ConnectionHash);
732+
while ((entry = (ConnCacheEntry *) hash_seq_search(&scan)))
733+
{
734+
if (entry->xact_depth > 0)
735+
{
736+
PGresult *result = PQgetResult(entry->conn);
737+
738+
if (PQresultStatus(result) != expectedStatus || (handler && !handler(result, arg)))
739+
{
740+
elog(WARNING, "Failed command %s: status=%d, expected status=%d", sql, PQresultStatus(result), expectedStatus);
741+
pgfdw_report_error(ERROR, result, entry->conn, true, sql);
742+
allOk = false;
743+
}
744+
PQclear(result);
745+
PQgetResult(entry->conn); /* consume NULL result */
746+
}
747+
}
748+
return allOk;
749+
}
750+
751+
static bool
752+
RunDtmCommand(char const * sql)
753+
{
754+
return RunDtmStatement(sql, PGRES_COMMAND_OK, NULL, NULL);
755+
}
756+
757+
static bool
758+
RunDtmFunction(char const * sql)
759+
{
760+
return RunDtmStatement(sql, PGRES_TUPLES_OK, NULL, NULL);
761+
}
762+
763+
764+
static bool
765+
DtmMaxCSN(PGresult *result, void *arg)
766+
{
767+
char *resp = PQgetvalue(result, 0, 0);
768+
csn_t *maxCSN = (csn_t *) arg;
769+
csn_t csn = 0;
770+
771+
if (resp == NULL || (*resp) == '\0' || sscanf(resp, "%lld", &csn) != 1)
772+
{
773+
return false;
774+
}
775+
else
776+
{
777+
if (*maxCSN < csn)
778+
{
779+
*maxCSN = csn;
780+
}
781+
return true;
782+
}
783+
}
784+
646785
/*
647786
* pgfdw_xact_callback --- cleanup at main-transaction end.
648787
*/
@@ -652,10 +791,55 @@ pgfdw_xact_callback(XactEvent event, void *arg)
652791
HASH_SEQ_STATUS scan;
653792
ConnCacheEntry *entry;
654793

794+
/* Do nothing for this events */
795+
switch (event)
796+
{
797+
case XACT_EVENT_START:
798+
case XACT_EVENT_COMMIT_PREPARED:
799+
case XACT_EVENT_ABORT_PREPARED:
800+
return;
801+
default:
802+
break;
803+
}
804+
655805
/* Quick exit if no connections were touched in this transaction. */
656806
if (!xact_got_connection)
657807
return;
658808

809+
if (currentGlobalTransactionId != 0)
810+
{
811+
switch (event)
812+
{
813+
case XACT_EVENT_PARALLEL_PRE_COMMIT:
814+
case XACT_EVENT_PRE_COMMIT:
815+
{
816+
csn_t maxCSN = 0;
817+
818+
if (!RunDtmCommand(psprintf("PREPARE TRANSACTION '%d.%d'",
819+
MyProcPid, currentLocalTransactionId)) ||
820+
!RunDtmFunction(psprintf("SELECT public.dtm_begin_prepare('%d.%d')",
821+
MyProcPid, currentLocalTransactionId)) ||
822+
!RunDtmStatement(psprintf("SELECT public.dtm_prepare('%d.%d',0)",
823+
MyProcPid, currentLocalTransactionId), PGRES_TUPLES_OK, DtmMaxCSN, &maxCSN) ||
824+
!RunDtmFunction(psprintf("SELECT public.dtm_end_prepare('%d.%d',%lld)",
825+
MyProcPid, currentLocalTransactionId, maxCSN)) ||
826+
!RunDtmCommand(psprintf("COMMIT PREPARED '%d.%d'",
827+
MyProcPid, currentLocalTransactionId)))
828+
{
829+
RunDtmCommand(psprintf("ROLLBACK PREPARED '%d.%d'",
830+
MyProcPid, currentLocalTransactionId));
831+
ereport(ERROR,
832+
(errcode(ERRCODE_TRANSACTION_ROLLBACK),
833+
errmsg("transaction was aborted at one of the shards")));
834+
break;
835+
}
836+
return;
837+
}
838+
default:
839+
break;
840+
}
841+
}
842+
659843
/*
660844
* Scan all connection cache entries to find open remote transactions, and
661845
* close them.
@@ -689,10 +873,34 @@ pgfdw_xact_callback(XactEvent event, void *arg)
689873
pgfdw_reject_incomplete_xact_state_change(entry);
690874

691875
/* Commit all remote transactions during pre-commit */
692-
entry->changing_xact_state = true;
693-
do_sql_command(entry->conn, "COMMIT TRANSACTION");
694-
entry->changing_xact_state = false;
876+
do_sql_send_command(entry->conn, "COMMIT TRANSACTION");
877+
continue;
878+
879+
case XACT_EVENT_PRE_PREPARE:
880+
881+
/*
882+
* We disallow remote transactions that modified anything,
883+
* since it's not very reasonable to hold them open until
884+
* the prepared transaction is committed. For the moment,
885+
* throw error unconditionally; later we might allow
886+
* read-only cases. Note that the error will cause us to
887+
* come right back here with event == XACT_EVENT_ABORT, so
888+
* we'll clean up the connection state at that point.
889+
*/
890+
ereport(ERROR,
891+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
892+
errmsg("cannot prepare a transaction that modified remote tables")));
893+
break;
695894

895+
case XACT_EVENT_PARALLEL_COMMIT:
896+
case XACT_EVENT_COMMIT:
897+
case XACT_EVENT_PREPARE:
898+
if (!currentGlobalTransactionId)
899+
{
900+
entry->changing_xact_state = true;
901+
do_sql_command(entry->conn, "COMMIT TRANSACTION");
902+
entry->changing_xact_state = false;
903+
}
696904
/*
697905
* If there were any errors in subtransactions, and we
698906
* made prepared statements, do a DEALLOCATE ALL to make
@@ -716,27 +924,7 @@ pgfdw_xact_callback(XactEvent event, void *arg)
716924
entry->have_prep_stmt = false;
717925
entry->have_error = false;
718926
break;
719-
case XACT_EVENT_PRE_PREPARE:
720927

721-
/*
722-
* We disallow remote transactions that modified anything,
723-
* since it's not very reasonable to hold them open until
724-
* the prepared transaction is committed. For the moment,
725-
* throw error unconditionally; later we might allow
726-
* read-only cases. Note that the error will cause us to
727-
* come right back here with event == XACT_EVENT_ABORT, so
728-
* we'll clean up the connection state at that point.
729-
*/
730-
ereport(ERROR,
731-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
732-
errmsg("cannot prepare a transaction that modified remote tables")));
733-
break;
734-
case XACT_EVENT_PARALLEL_COMMIT:
735-
case XACT_EVENT_COMMIT:
736-
case XACT_EVENT_PREPARE:
737-
/* Pre-commit should have closed the open transaction */
738-
elog(ERROR, "missed cleaning up connection during pre-commit");
739-
break;
740928
case XACT_EVENT_PARALLEL_ABORT:
741929
case XACT_EVENT_ABORT:
742930

@@ -800,6 +988,11 @@ pgfdw_xact_callback(XactEvent event, void *arg)
800988
/* Disarm changing_xact_state if it all worked. */
801989
entry->changing_xact_state = abort_cleanup_failure;
802990
break;
991+
992+
case XACT_EVENT_START:
993+
case XACT_EVENT_COMMIT_PREPARED:
994+
case XACT_EVENT_ABORT_PREPARED:
995+
break;
803996
}
804997
}
805998

@@ -818,16 +1011,22 @@ pgfdw_xact_callback(XactEvent event, void *arg)
8181011
disconnect_pg_server(entry);
8191012
}
8201013
}
1014+
if (event != XACT_EVENT_PARALLEL_PRE_COMMIT && event != XACT_EVENT_PRE_COMMIT)
1015+
{
1016+
/*
1017+
* Regardless of the event type, we can now mark ourselves as out of
1018+
* the transaction. (Note: if we are here during PRE_COMMIT or
1019+
* PRE_PREPARE, this saves a useless scan of the hashtable during
1020+
* COMMIT or PREPARE.)
1021+
*/
1022+
xact_got_connection = false;
8211023

822-
/*
823-
* Regardless of the event type, we can now mark ourselves as out of the
824-
* transaction. (Note: if we are here during PRE_COMMIT or PRE_PREPARE,
825-
* this saves a useless scan of the hashtable during COMMIT or PREPARE.)
826-
*/
827-
xact_got_connection = false;
1024+
/* Also reset cursor numbering for next transaction */
1025+
cursor_number = 0;
8281026

829-
/* Also reset cursor numbering for next transaction */
830-
cursor_number = 0;
1027+
currentGlobalTransactionId = 0;
1028+
currentConnection = NULL;
1029+
}
8311030
}
8321031

8331032
/*

contrib/postgres_fdw/postgres_fdw--1.0.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,8 @@ LANGUAGE C STRICT;
1616
CREATE FOREIGN DATA WRAPPER postgres_fdw
1717
HANDLER postgres_fdw_handler
1818
VALIDATOR postgres_fdw_validator;
19+
20+
CREATE FUNCTION postgres_fdw_exec(relid oid, sql cstring)
21+
RETURNS void
22+
AS 'MODULE_PATHNAME'
23+
LANGUAGE C STRICT;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy