mirror of
https://github.com/postgres/postgres.git
synced 2025-06-11 20:28:21 +03:00
Fix postgres_fdw's issues with inconsistent interpretation of data values.
For datatypes whose output formatting depends on one or more GUC settings,
we have to worry about whether the other server will interpret the value
the same way it was meant. pg_dump has been aware of this hazard for a
long time, but postgres_fdw needs to deal with it too. To fix data
retrieval from the remote server, set the necessary remote GUC settings at
connection startup. (We were already assuming that settings made then
would persist throughout the remote session.) To fix data transmission to
the remote server, temporarily force the relevant GUCs to the right values
when we're about to convert any data values to text for transmission.
This is all pretty grotty, and not very cheap either. It's tempting to
think of defining one uber-GUC that would override any settings that might
render printed data values unportable. But of course, older remote servers
wouldn't know any such thing and would still need this logic.
While at it, revert commit f7951eef89
, since
this provides a real fix. (The timestamptz given in the error message
returned from the "remote" server will now reliably be shown in UTC.)
This commit is contained in:
@ -67,6 +67,7 @@ static bool xact_got_connection = false;
|
||||
static PGconn *connect_pg_server(ForeignServer *server, UserMapping *user);
|
||||
static void check_conn_params(const char **keywords, const char **values);
|
||||
static void configure_remote_session(PGconn *conn);
|
||||
static void do_sql_command(PGconn *conn, const char *sql);
|
||||
static void begin_remote_xact(ConnCacheEntry *entry);
|
||||
static void pgfdw_xact_callback(XactEvent event, void *arg);
|
||||
static void pgfdw_subxact_callback(SubXactEvent event,
|
||||
@ -314,11 +315,43 @@ check_conn_params(const char **keywords, const char **values)
|
||||
static void
|
||||
configure_remote_session(PGconn *conn)
|
||||
{
|
||||
const char *sql;
|
||||
PGresult *res;
|
||||
int remoteversion = PQserverVersion(conn);
|
||||
|
||||
/* Force the search path to contain only pg_catalog (see deparse.c) */
|
||||
sql = "SET search_path = pg_catalog";
|
||||
do_sql_command(conn, "SET search_path = pg_catalog");
|
||||
|
||||
/*
|
||||
* Set remote timezone; this is basically just cosmetic, since all
|
||||
* transmitted and returned timestamptzs should specify a zone explicitly
|
||||
* anyway. However it makes the regression test outputs more predictable.
|
||||
*
|
||||
* We don't risk setting remote zone equal to ours, since the remote
|
||||
* server might use a different timezone database.
|
||||
*/
|
||||
do_sql_command(conn, "SET timezone = UTC");
|
||||
|
||||
/*
|
||||
* Set values needed to ensure unambiguous data output from remote. (This
|
||||
* logic should match what pg_dump does. See also set_transmission_modes
|
||||
* in postgres_fdw.c.)
|
||||
*/
|
||||
do_sql_command(conn, "SET datestyle = ISO");
|
||||
if (remoteversion >= 80400)
|
||||
do_sql_command(conn, "SET intervalstyle = postgres");
|
||||
if (remoteversion >= 90000)
|
||||
do_sql_command(conn, "SET extra_float_digits = 3");
|
||||
else
|
||||
do_sql_command(conn, "SET extra_float_digits = 2");
|
||||
}
|
||||
|
||||
/*
|
||||
* Convenience subroutine to issue a non-data-returning SQL command to remote
|
||||
*/
|
||||
static void
|
||||
do_sql_command(PGconn *conn, const char *sql)
|
||||
{
|
||||
PGresult *res;
|
||||
|
||||
res = PQexec(conn, sql);
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||
pgfdw_report_error(ERROR, res, true, sql);
|
||||
@ -339,7 +372,6 @@ static void
|
||||
begin_remote_xact(ConnCacheEntry *entry)
|
||||
{
|
||||
int curlevel = GetCurrentTransactionNestLevel();
|
||||
PGresult *res;
|
||||
|
||||
/* Start main transaction if we haven't yet */
|
||||
if (entry->xact_depth <= 0)
|
||||
@ -353,10 +385,7 @@ begin_remote_xact(ConnCacheEntry *entry)
|
||||
sql = "START TRANSACTION ISOLATION LEVEL SERIALIZABLE";
|
||||
else
|
||||
sql = "START TRANSACTION ISOLATION LEVEL REPEATABLE READ";
|
||||
res = PQexec(entry->conn, sql);
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||
pgfdw_report_error(ERROR, res, true, sql);
|
||||
PQclear(res);
|
||||
do_sql_command(entry->conn, sql);
|
||||
entry->xact_depth = 1;
|
||||
}
|
||||
|
||||
@ -370,10 +399,7 @@ begin_remote_xact(ConnCacheEntry *entry)
|
||||
char sql[64];
|
||||
|
||||
snprintf(sql, sizeof(sql), "SAVEPOINT s%d", entry->xact_depth + 1);
|
||||
res = PQexec(entry->conn, sql);
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||
pgfdw_report_error(ERROR, res, true, sql);
|
||||
PQclear(res);
|
||||
do_sql_command(entry->conn, sql);
|
||||
entry->xact_depth++;
|
||||
}
|
||||
}
|
||||
@ -509,10 +535,7 @@ pgfdw_xact_callback(XactEvent event, void *arg)
|
||||
{
|
||||
case XACT_EVENT_PRE_COMMIT:
|
||||
/* Commit all remote transactions during pre-commit */
|
||||
res = PQexec(entry->conn, "COMMIT TRANSACTION");
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||
pgfdw_report_error(ERROR, res, true, "COMMIT TRANSACTION");
|
||||
PQclear(res);
|
||||
do_sql_command(entry->conn, "COMMIT TRANSACTION");
|
||||
|
||||
/*
|
||||
* If there were any errors in subtransactions, and we made
|
||||
@ -647,10 +670,7 @@ pgfdw_subxact_callback(SubXactEvent event, SubTransactionId mySubid,
|
||||
{
|
||||
/* Commit all remote subtransactions during pre-commit */
|
||||
snprintf(sql, sizeof(sql), "RELEASE SAVEPOINT s%d", curlevel);
|
||||
res = PQexec(entry->conn, sql);
|
||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||
pgfdw_report_error(ERROR, res, true, sql);
|
||||
PQclear(res);
|
||||
do_sql_command(entry->conn, sql);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
Reference in New Issue
Block a user