Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * connection.c
4 : : * Connection management functions for postgres_fdw
5 : : *
6 : : * Portions Copyright (c) 2012-2026, PostgreSQL Global Development Group
7 : : *
8 : : * IDENTIFICATION
9 : : * contrib/postgres_fdw/connection.c
10 : : *
11 : : *-------------------------------------------------------------------------
12 : : */
13 : : #include "postgres.h"
14 : :
15 : : #if HAVE_POLL_H
16 : : #include <poll.h>
17 : : #endif
18 : :
19 : : #include "access/htup_details.h"
20 : : #include "access/xact.h"
21 : : #include "catalog/pg_user_mapping.h"
22 : : #include "commands/defrem.h"
23 : : #include "common/base64.h"
24 : : #include "funcapi.h"
25 : : #include "libpq/libpq-be.h"
26 : : #include "libpq/libpq-be-fe-helpers.h"
27 : : #include "mb/pg_wchar.h"
28 : : #include "miscadmin.h"
29 : : #include "pgstat.h"
30 : : #include "postgres_fdw.h"
31 : : #include "storage/latch.h"
32 : : #include "utils/builtins.h"
33 : : #include "utils/hsearch.h"
34 : : #include "utils/inval.h"
35 : : #include "utils/syscache.h"
36 : : #include "utils/tuplestore.h"
37 : :
38 : : /*
39 : : * Connection cache hash table entry
40 : : *
41 : : * The lookup key in this hash table is the user mapping OID. We use just one
42 : : * connection per user mapping ID, which ensures that all the scans use the
43 : : * same snapshot during a query. Using the user mapping OID rather than
44 : : * the foreign server OID + user OID avoids creating multiple connections when
45 : : * the public user mapping applies to all user OIDs.
46 : : *
47 : : * The "conn" pointer can be NULL if we don't currently have a live connection.
48 : : * When we do have a connection, xact_depth tracks the current depth of
49 : : * transactions and subtransactions open on the remote side. We need to issue
50 : : * commands at the same nesting depth on the remote as we're executing at
51 : : * ourselves, so that rolling back a subtransaction will kill the right
52 : : * queries and not the wrong ones.
53 : : */
54 : : typedef Oid ConnCacheKey;
55 : :
56 : : typedef struct ConnCacheEntry
57 : : {
58 : : ConnCacheKey key; /* hash key (must be first) */
59 : : PGconn *conn; /* connection to foreign server, or NULL */
60 : : /* Remaining fields are invalid when conn is NULL: */
61 : : int xact_depth; /* 0 = no xact open, 1 = main xact open, 2 =
62 : : * one level of subxact open, etc */
63 : : bool xact_read_only; /* xact r/o state */
64 : : bool have_prep_stmt; /* have we prepared any stmts in this xact? */
65 : : bool have_error; /* have any subxacts aborted in this xact? */
66 : : bool changing_xact_state; /* xact state change in process */
67 : : bool parallel_commit; /* do we commit (sub)xacts in parallel? */
68 : : bool parallel_abort; /* do we abort (sub)xacts in parallel? */
69 : : bool invalidated; /* true if reconnect is pending */
70 : : bool keep_connections; /* setting value of keep_connections
71 : : * server option */
72 : : Oid serverid; /* foreign server OID used to get server name */
73 : : uint32 server_hashvalue; /* hash value of foreign server OID */
74 : : uint32 mapping_hashvalue; /* hash value of user mapping OID */
75 : : PgFdwConnState state; /* extra per-connection state */
76 : : } ConnCacheEntry;
77 : :
78 : : /*
79 : : * Connection cache (initialized on first use)
80 : : */
81 : : static HTAB *ConnectionHash = NULL;
82 : :
83 : : /* for assigning cursor numbers and prepared statement numbers */
84 : : static unsigned int cursor_number = 0;
85 : : static unsigned int prep_stmt_number = 0;
86 : :
87 : : /* tracks whether any work is needed in callback functions */
88 : : static bool xact_got_connection = false;
89 : :
90 : : /*
91 : : * tracks the topmost read-only local transaction's nesting level determined
92 : : * by GetTopReadOnlyTransactionNestLevel()
93 : : */
94 : : static int read_only_level = 0;
95 : :
96 : : /* custom wait event values, retrieved from shared memory */
97 : : static uint32 pgfdw_we_cleanup_result = 0;
98 : : static uint32 pgfdw_we_connect = 0;
99 : : static uint32 pgfdw_we_get_result = 0;
100 : :
101 : : /*
102 : : * Milliseconds to wait to cancel an in-progress query or execute a cleanup
103 : : * query; if it takes longer than 30 seconds to do these, we assume the
104 : : * connection is dead.
105 : : */
106 : : #define CONNECTION_CLEANUP_TIMEOUT 30000
107 : :
108 : : /*
109 : : * Milliseconds to wait before issuing another cancel request. This covers
110 : : * the race condition where the remote session ignored our cancel request
111 : : * because it arrived while idle.
112 : : */
113 : : #define RETRY_CANCEL_TIMEOUT 1000
114 : :
115 : : /* Macro for constructing abort command to be sent */
116 : : #define CONSTRUCT_ABORT_COMMAND(sql, entry, toplevel) \
117 : : do { \
118 : : if (toplevel) \
119 : : snprintf((sql), sizeof(sql), \
120 : : "ABORT TRANSACTION"); \
121 : : else \
122 : : snprintf((sql), sizeof(sql), \
123 : : "ROLLBACK TO SAVEPOINT s%d; RELEASE SAVEPOINT s%d", \
124 : : (entry)->xact_depth, (entry)->xact_depth); \
125 : : } while(0)
126 : :
127 : : /*
128 : : * Extension version number, for supporting older extension versions' objects
129 : : */
130 : : enum pgfdwVersion
131 : : {
132 : : PGFDW_V1_1 = 0,
133 : : PGFDW_V1_2,
134 : : };
135 : :
136 : : /*
137 : : * SQL functions
138 : : */
2047 fujii@postgresql.org 139 :CBC 6 : PG_FUNCTION_INFO_V1(postgres_fdw_get_connections);
762 140 : 7 : PG_FUNCTION_INFO_V1(postgres_fdw_get_connections_1_2);
2039 141 : 7 : PG_FUNCTION_INFO_V1(postgres_fdw_disconnect);
142 : 7 : PG_FUNCTION_INFO_V1(postgres_fdw_disconnect_all);
174 jdavis@postgresql.or 143 : 14 : PG_FUNCTION_INFO_V1(postgres_fdw_connection);
144 : :
145 : : /* prototypes of private functions */
146 : : static void make_new_connection(ConnCacheEntry *entry, UserMapping *user);
147 : : static PGconn *connect_pg_server(ForeignServer *server, UserMapping *user);
148 : : static void disconnect_pg_server(ConnCacheEntry *entry);
149 : : static void check_conn_params(const char **keywords, const char **values, UserMapping *user);
150 : : static void configure_remote_session(PGconn *conn);
151 : : static void do_sql_command_begin(PGconn *conn, const char *sql);
152 : : static void do_sql_command_end(PGconn *conn, const char *sql,
153 : : bool consume_input);
154 : : static void begin_remote_xact(ConnCacheEntry *entry);
155 : : static void pgfdw_report_internal(int elevel, PGresult *res, PGconn *conn,
156 : : const char *sql);
157 : : static void pgfdw_xact_callback(XactEvent event, void *arg);
158 : : static void pgfdw_subxact_callback(SubXactEvent event,
159 : : SubTransactionId mySubid,
160 : : SubTransactionId parentSubid,
161 : : void *arg);
162 : : static void pgfdw_inval_callback(Datum arg, SysCacheIdentifier cacheid,
163 : : uint32 hashvalue);
164 : : static void pgfdw_reject_incomplete_xact_state_change(ConnCacheEntry *entry);
165 : : static void pgfdw_reset_xact_state(ConnCacheEntry *entry, bool toplevel);
166 : : static bool pgfdw_cancel_query(PGconn *conn);
167 : : static bool pgfdw_cancel_query_begin(PGconn *conn, TimestampTz endtime);
168 : : static bool pgfdw_cancel_query_end(PGconn *conn, TimestampTz endtime,
169 : : TimestampTz retrycanceltime,
170 : : bool consume_input);
171 : : static bool pgfdw_exec_cleanup_query(PGconn *conn, const char *query,
172 : : bool ignore_errors);
173 : : static bool pgfdw_exec_cleanup_query_begin(PGconn *conn, const char *query);
174 : : static bool pgfdw_exec_cleanup_query_end(PGconn *conn, const char *query,
175 : : TimestampTz endtime,
176 : : bool consume_input,
177 : : bool ignore_errors);
178 : : static bool pgfdw_get_cleanup_result(PGconn *conn, TimestampTz endtime,
179 : : TimestampTz retrycanceltime,
180 : : PGresult **result, bool *timed_out);
181 : : static void pgfdw_abort_cleanup(ConnCacheEntry *entry, bool toplevel);
182 : : static bool pgfdw_abort_cleanup_begin(ConnCacheEntry *entry, bool toplevel,
183 : : List **pending_entries,
184 : : List **cancel_requested);
185 : : static void pgfdw_finish_pre_commit_cleanup(List *pending_entries);
186 : : static void pgfdw_finish_pre_subcommit_cleanup(List *pending_entries,
187 : : int curlevel);
188 : : static void pgfdw_finish_abort_cleanup(List *pending_entries,
189 : : List *cancel_requested,
190 : : bool toplevel);
191 : : static void pgfdw_security_check(const char **keywords, const char **values,
192 : : UserMapping *user, PGconn *conn);
193 : : static bool UserMappingPasswordRequired(UserMapping *user);
194 : : static bool UseScramPassthrough(ForeignServer *server, UserMapping *user);
195 : : static bool disconnect_cached_connections(Oid serverid);
196 : : static void postgres_fdw_get_connections_internal(FunctionCallInfo fcinfo,
197 : : enum pgfdwVersion api_version);
198 : : static int pgfdw_conn_check(PGconn *conn);
199 : : static bool pgfdw_conn_checkable(void);
200 : : static bool pgfdw_has_required_scram_options(const char **keywords, const char **values);
201 : :
202 : : /*
203 : : * Get a PGconn which can be used to execute queries on the remote PostgreSQL
204 : : * server with the user's authorization. A new connection is established
205 : : * if we don't already have a suitable one, and a transaction is opened at
206 : : * the right subtransaction nesting depth if we didn't do that already.
207 : : *
208 : : * will_prep_stmt must be true if caller intends to create any prepared
209 : : * statements. Since those don't go away automatically at transaction end
210 : : * (not even on error), we need this flag to cue manual cleanup.
211 : : *
212 : : * If state is not NULL, *state receives the per-connection state associated
213 : : * with the PGconn.
214 : : */
215 : : PGconn *
1975 efujita@postgresql.o 216 : 2328 : GetConnection(UserMapping *user, bool will_prep_stmt, PgFdwConnState **state)
217 : : {
218 : : bool found;
2141 fujii@postgresql.org 219 : 2328 : bool retry = false;
220 : : ConnCacheEntry *entry;
221 : : ConnCacheKey key;
222 : 2328 : MemoryContext ccxt = CurrentMemoryContext;
223 : :
224 : : /* First time through, initialize connection cache hashtable */
4935 tgl@sss.pgh.pa.us 225 [ + + ]: 2328 : if (ConnectionHash == NULL)
226 : : {
227 : : HASHCTL ctl;
228 : :
962 noah@leadboat.com 229 [ + - ]: 12 : if (pgfdw_we_get_result == 0)
230 : 12 : pgfdw_we_get_result =
231 : 12 : WaitEventExtensionNew("PostgresFdwGetResult");
232 : :
4935 tgl@sss.pgh.pa.us 233 : 12 : ctl.keysize = sizeof(ConnCacheKey);
234 : 12 : ctl.entrysize = sizeof(ConnCacheEntry);
235 : 12 : ConnectionHash = hash_create("postgres_fdw connections", 8,
236 : : &ctl,
237 : : HASH_ELEM | HASH_BLOBS);
238 : :
239 : : /*
240 : : * Register some callback functions that manage connection cleanup.
241 : : * This should be done just once in each backend.
242 : : */
243 : 12 : RegisterXactCallback(pgfdw_xact_callback, NULL);
244 : 12 : RegisterSubXactCallback(pgfdw_subxact_callback, NULL);
3324 245 : 12 : CacheRegisterSyscacheCallback(FOREIGNSERVEROID,
246 : : pgfdw_inval_callback, (Datum) 0);
247 : 12 : CacheRegisterSyscacheCallback(USERMAPPINGOID,
248 : : pgfdw_inval_callback, (Datum) 0);
249 : : }
250 : :
251 : : /* Set flag that we did GetConnection during the current transaction */
4935 252 : 2328 : xact_got_connection = true;
253 : :
254 : : /* Create hash key for the entry. Assume no pad bytes in key struct */
3864 rhaas@postgresql.org 255 : 2328 : key = user->umid;
256 : :
257 : : /*
258 : : * Find or create cached entry for requested connection.
259 : : */
4935 tgl@sss.pgh.pa.us 260 : 2328 : entry = hash_search(ConnectionHash, &key, HASH_ENTER, &found);
261 [ + + ]: 2328 : if (!found)
262 : : {
263 : : /*
264 : : * We need only clear "conn" here; remaining fields will be filled
265 : : * later when "conn" is set.
266 : : */
267 : 23 : entry->conn = NULL;
268 : : }
269 : :
270 : : /* Reject further use of connections which failed abort cleanup. */
3368 rhaas@postgresql.org 271 : 2328 : pgfdw_reject_incomplete_xact_state_change(entry);
272 : :
273 : : /*
274 : : * If the connection needs to be remade due to invalidation, disconnect as
275 : : * soon as we're out of all transactions.
276 : : */
2141 fujii@postgresql.org 277 [ + + - + : 2326 : if (entry->conn != NULL && entry->invalidated && entry->xact_depth == 0)
- - ]
278 : : {
2141 fujii@postgresql.org 279 [ # # ]:UBC 0 : elog(DEBUG3, "closing connection %p for option changes to take effect",
280 : : entry->conn);
3324 tgl@sss.pgh.pa.us 281 : 0 : disconnect_pg_server(entry);
282 : : }
283 : :
284 : : /*
285 : : * If cache entry doesn't have a connection, we have to establish a new
286 : : * connection. (If connect_pg_server throws an error, the cache entry
287 : : * will remain in a valid empty state, ie conn == NULL.)
288 : : */
4935 tgl@sss.pgh.pa.us 289 [ + + ]:CBC 2326 : if (entry->conn == NULL)
2141 fujii@postgresql.org 290 : 87 : make_new_connection(entry, user);
291 : :
292 : : /*
293 : : * We check the health of the cached connection here when using it. In
294 : : * cases where we're out of all transactions, if a broken connection is
295 : : * detected, we try to reestablish a new connection later.
296 : : */
2151 297 [ + + ]: 2318 : PG_TRY();
298 : : {
299 : : /* Process a pending asynchronous request if any. */
1975 efujita@postgresql.o 300 [ - + ]: 2318 : if (entry->state.pendingAreq)
1975 efujita@postgresql.o 301 :UBC 0 : process_pending_request(entry->state.pendingAreq);
302 : : /* Start a new transaction or subtransaction if needed. */
2151 fujii@postgresql.org 303 :CBC 2318 : begin_remote_xact(entry);
304 : : }
305 : 2 : PG_CATCH();
306 : : {
2141 307 : 2 : MemoryContext ecxt = MemoryContextSwitchTo(ccxt);
308 : 2 : ErrorData *errdata = CopyErrorData();
309 : :
310 : : /*
311 : : * Determine whether to try to reestablish the connection.
312 : : *
313 : : * After a broken connection is detected in libpq, any error other
314 : : * than connection failure (e.g., out-of-memory) can be thrown
315 : : * somewhere between return from libpq and the expected ereport() call
316 : : * in pgfdw_report_error(). In this case, since PQstatus() indicates
317 : : * CONNECTION_BAD, checking only PQstatus() causes the false detection
318 : : * of connection failure. To avoid this, we also verify that the
319 : : * error's sqlstate is ERRCODE_CONNECTION_FAILURE. Note that also
320 : : * checking only the sqlstate can cause another false detection
321 : : * because pgfdw_report_error() may report ERRCODE_CONNECTION_FAILURE
322 : : * for any libpq-originated error condition.
323 : : */
324 [ + - ]: 2 : if (errdata->sqlerrcode != ERRCODE_CONNECTION_FAILURE ||
325 [ + - ]: 2 : PQstatus(entry->conn) != CONNECTION_BAD ||
326 [ + + ]: 2 : entry->xact_depth > 0)
327 : : {
328 : 1 : MemoryContextSwitchTo(ecxt);
2151 329 : 1 : PG_RE_THROW();
330 : : }
331 : :
332 : : /* Clean up the error state */
2141 333 : 1 : FlushErrorState();
334 : 1 : FreeErrorData(errdata);
335 : 1 : errdata = NULL;
336 : :
337 : 1 : retry = true;
338 : : }
2151 339 [ - + ]: 2317 : PG_END_TRY();
340 : :
341 : : /*
342 : : * If a broken connection is detected, disconnect it, reestablish a new
343 : : * connection and retry a new remote transaction. If connection failure is
344 : : * reported again, we give up getting a connection.
345 : : */
2141 346 [ + + ]: 2317 : if (retry)
347 : : {
348 [ - + ]: 1 : Assert(entry->xact_depth == 0);
349 : :
2151 350 [ - + ]: 1 : ereport(DEBUG3,
351 : : (errmsg_internal("could not start remote transaction on connection %p",
352 : : entry->conn)),
353 : : errdetail_internal("%s", pchomp(PQerrorMessage(entry->conn))));
354 : :
2141 355 [ - + ]: 1 : elog(DEBUG3, "closing connection %p to reestablish a new one",
356 : : entry->conn);
357 : 1 : disconnect_pg_server(entry);
358 : :
1259 efujita@postgresql.o 359 : 1 : make_new_connection(entry, user);
360 : :
2141 fujii@postgresql.org 361 : 1 : begin_remote_xact(entry);
362 : : }
363 : :
364 : : /* Remember if caller will prepare statements */
4918 tgl@sss.pgh.pa.us 365 : 2317 : entry->have_prep_stmt |= will_prep_stmt;
366 : :
367 : : /* If caller needs access to the per-connection state, return it. */
1975 efujita@postgresql.o 368 [ + + ]: 2317 : if (state)
369 : 809 : *state = &entry->state;
370 : :
4935 tgl@sss.pgh.pa.us 371 : 2317 : return entry->conn;
372 : : }
373 : :
374 : : /*
375 : : * Reset all transient state fields in the cached connection entry and
376 : : * establish new connection to the remote server.
377 : : */
378 : : static void
2141 fujii@postgresql.org 379 : 88 : make_new_connection(ConnCacheEntry *entry, UserMapping *user)
380 : : {
381 : 88 : ForeignServer *server = GetForeignServer(user->serverid);
382 : : ListCell *lc;
383 : :
384 [ - + ]: 88 : Assert(entry->conn == NULL);
385 : :
386 : : /* Reset all transient state fields, to be sure all are clean */
387 : 88 : entry->xact_depth = 0;
144 efujita@postgresql.o 388 : 88 : entry->xact_read_only = false;
2141 fujii@postgresql.org 389 : 88 : entry->have_prep_stmt = false;
390 : 88 : entry->have_error = false;
391 : 88 : entry->changing_xact_state = false;
392 : 88 : entry->invalidated = false;
2050 393 : 88 : entry->serverid = server->serverid;
2141 394 : 88 : entry->server_hashvalue =
395 : 88 : GetSysCacheHashValue1(FOREIGNSERVEROID,
396 : : ObjectIdGetDatum(server->serverid));
397 : 88 : entry->mapping_hashvalue =
398 : 88 : GetSysCacheHashValue1(USERMAPPINGOID,
399 : : ObjectIdGetDatum(user->umid));
1975 efujita@postgresql.o 400 : 88 : memset(&entry->state, 0, sizeof(entry->state));
401 : :
402 : : /*
403 : : * Determine whether to keep the connection that we're about to make here
404 : : * open even after the transaction using it ends, so that the subsequent
405 : : * transactions can re-use it.
406 : : *
407 : : * By default, all the connections to any foreign servers are kept open.
408 : : *
409 : : * Also determine whether to commit/abort (sub)transactions opened on the
410 : : * remote server in parallel at (sub)transaction end, which is disabled by
411 : : * default.
412 : : *
413 : : * Note: it's enough to determine these only when making a new connection
414 : : * because if these settings for it are changed, it will be closed and
415 : : * re-made later.
416 : : */
1973 fujii@postgresql.org 417 : 88 : entry->keep_connections = true;
1645 efujita@postgresql.o 418 : 88 : entry->parallel_commit = false;
1239 419 : 88 : entry->parallel_abort = false;
1973 fujii@postgresql.org 420 [ + - + + : 412 : foreach(lc, server->options)
+ + ]
421 : : {
422 : 324 : DefElem *def = (DefElem *) lfirst(lc);
423 : :
424 [ + + ]: 324 : if (strcmp(def->defname, "keep_connections") == 0)
425 : 17 : entry->keep_connections = defGetBoolean(def);
1645 efujita@postgresql.o 426 [ + + ]: 307 : else if (strcmp(def->defname, "parallel_commit") == 0)
427 : 2 : entry->parallel_commit = defGetBoolean(def);
1239 428 [ + + ]: 305 : else if (strcmp(def->defname, "parallel_abort") == 0)
429 : 2 : entry->parallel_abort = defGetBoolean(def);
430 : : }
431 : :
432 : : /* Now try to make the connection */
2141 fujii@postgresql.org 433 : 88 : entry->conn = connect_pg_server(server, user);
434 : :
435 [ - + ]: 80 : elog(DEBUG3, "new postgres_fdw connection %p for server \"%s\" (user mapping oid %u, userid %u)",
436 : : entry->conn, server->servername, user->umid, user->userid);
437 : 80 : }
438 : :
439 : : /*
440 : : * Check that non-superuser has used password or delegated credentials
441 : : * to establish connection; otherwise, he's piggybacking on the
442 : : * postgres server's user identity. See also dblink_security_check()
443 : : * in contrib/dblink and check_conn_params.
444 : : */
445 : : static void
1232 sfrost@snowman.net 446 : 82 : pgfdw_security_check(const char **keywords, const char **values, UserMapping *user, PGconn *conn)
447 : : {
448 : : /* Superusers bypass the check */
449 [ + + ]: 82 : if (superuser_arg(user->userid))
450 : 74 : return;
451 : :
452 : : #ifdef ENABLE_GSS
453 : : /* Connected via GSSAPI with delegated credentials- all good. */
1195 bruce@momjian.us 454 [ - + - - ]: 8 : if (PQconnectionUsedGSSAPI(conn) && be_gssapi_get_delegation(MyProcPort))
1232 sfrost@snowman.net 455 :UBC 0 : return;
456 : : #endif
457 : :
458 : : /* Ok if superuser set PW required false. */
1232 sfrost@snowman.net 459 [ + + ]:CBC 8 : if (!UserMappingPasswordRequired(user))
460 : 2 : return;
461 : :
462 : : /* Connected via PW, with PW required true, and provided non-empty PW. */
463 [ + + ]: 6 : if (PQconnectionUsedPassword(conn))
464 : : {
465 : : /* ok if params contain a non-empty password */
466 [ + + ]: 40 : for (int i = 0; keywords[i] != NULL; i++)
467 : : {
468 [ - + - - ]: 36 : if (strcmp(keywords[i], "password") == 0 && values[i][0] != '\0')
1232 sfrost@snowman.net 469 :UBC 0 : return;
470 : : }
471 : : }
472 : :
473 : : /*
474 : : * Ok if SCRAM pass-through is being used and all required SCRAM options
475 : : * are set correctly. If pgfdw_has_required_scram_options returns true we
476 : : * assume that UseScramPassthrough is also true since SCRAM options are
477 : : * only set when UseScramPassthrough is enabled.
478 : : */
384 peter@eisentraut.org 479 [ + - + + :CBC 6 : if (MyProcPort != NULL && MyProcPort->has_scram_keys && pgfdw_has_required_scram_options(keywords, values))
+ - ]
521 480 : 4 : return;
481 : :
1232 sfrost@snowman.net 482 [ + - ]: 2 : ereport(ERROR,
483 : : (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
484 : : errmsg("password or GSSAPI delegated credentials required"),
485 : : errdetail("Non-superuser cannot connect if the server does not request a password or use GSSAPI with delegated credentials."),
486 : : errhint("Target server's authentication method must be changed or password_required=false set in the user mapping attributes.")));
487 : : }
488 : :
489 : : /*
490 : : * Construct connection params from generic options of ForeignServer and
491 : : * UserMapping. (Some of them might not be libpq options, in which case we'll
492 : : * just waste a few array slots.)
493 : : */
494 : : static void
174 jdavis@postgresql.or 495 : 95 : construct_connection_params(ForeignServer *server, UserMapping *user,
496 : : const char ***p_keywords, const char ***p_values,
497 : : char **p_appname)
498 : : {
499 : : const char **keywords;
500 : : const char **values;
501 : 95 : char *appname = NULL;
502 : : int n;
503 : :
504 : : /*
505 : : * Add 4 extra slots for application_name, fallback_application_name,
506 : : * client_encoding, end marker, and 3 extra slots for scram keys and
507 : : * required scram pass-through options.
508 : : */
509 : 95 : n = list_length(server->options) + list_length(user->options) + 4 + 3;
10 michael@paquier.xyz 510 :GNC 95 : keywords = palloc_array(const char *, n);
511 : 95 : values = palloc_array(const char *, n);
512 : :
174 jdavis@postgresql.or 513 :CBC 95 : n = 0;
514 : 190 : n += ExtractConnectionOptions(server->options,
515 : 95 : keywords + n, values + n);
516 : 190 : n += ExtractConnectionOptions(user->options,
517 : 95 : keywords + n, values + n);
518 : :
519 : : /*
520 : : * Use pgfdw_application_name as application_name if set.
521 : : *
522 : : * PQconnectdbParams() processes the parameter arrays from start to end.
523 : : * If any key word is repeated, the last value is used. Therefore note
524 : : * that pgfdw_application_name must be added to the arrays after options
525 : : * of ForeignServer are, so that it can override application_name set in
526 : : * ForeignServer.
527 : : */
528 [ + + + - ]: 95 : if (pgfdw_application_name && *pgfdw_application_name != '\0')
529 : : {
530 : 1 : keywords[n] = "application_name";
531 : 1 : values[n] = pgfdw_application_name;
532 : 1 : n++;
533 : : }
534 : :
535 : : /*
536 : : * Search the parameter arrays to find application_name setting, and
537 : : * replace escape sequences in it with status information if found. The
538 : : * arrays are searched backwards because the last value is used if
539 : : * application_name is repeatedly set.
540 : : */
541 [ + + ]: 254 : for (int i = n - 1; i >= 0; i--)
542 : : {
543 [ + + ]: 186 : if (strcmp(keywords[i], "application_name") == 0 &&
544 [ + - ]: 27 : *(values[i]) != '\0')
545 : : {
546 : : /*
547 : : * Use this application_name setting if it's not empty string even
548 : : * after any escape sequences in it are replaced.
549 : : */
550 : 27 : appname = process_pgfdw_appname(values[i]);
551 [ + - ]: 27 : if (appname[0] != '\0')
552 : : {
553 : 27 : values[i] = appname;
554 : 27 : break;
555 : : }
556 : :
557 : : /*
558 : : * This empty application_name is not used, so we set values[i] to
559 : : * NULL and keep searching the array to find the next one.
560 : : */
174 jdavis@postgresql.or 561 :UBC 0 : values[i] = NULL;
562 : 0 : pfree(appname);
563 : 0 : appname = NULL;
564 : : }
565 : : }
566 : :
174 jdavis@postgresql.or 567 :CBC 95 : *p_appname = appname;
568 : :
569 : : /* Use "postgres_fdw" as fallback_application_name */
570 : 95 : keywords[n] = "fallback_application_name";
571 : 95 : values[n] = "postgres_fdw";
572 : 95 : n++;
573 : :
574 : : /* Set client_encoding so that libpq can convert encoding properly. */
575 : 95 : keywords[n] = "client_encoding";
576 : 95 : values[n] = GetDatabaseEncodingName();
577 : 95 : n++;
578 : :
579 : : /* Add required SCRAM pass-through connection options if it's enabled. */
580 [ + + + + : 95 : if (MyProcPort != NULL && MyProcPort->has_scram_keys && UseScramPassthrough(server, user))
+ + ]
581 : : {
582 : : int len;
583 : : char *encoded;
584 : : int encoded_len;
585 : :
586 : 5 : keywords[n] = "scram_client_key";
587 : 5 : len = pg_b64_enc_len(sizeof(MyProcPort->scram_ClientKey));
588 : : /* don't forget the zero-terminator */
9 peter@eisentraut.org 589 :GNC 5 : encoded = palloc0(len + 1);
174 jdavis@postgresql.or 590 :CBC 5 : encoded_len = pg_b64_encode(MyProcPort->scram_ClientKey,
591 : : sizeof(MyProcPort->scram_ClientKey),
592 : : encoded, len);
593 [ - + ]: 5 : if (encoded_len < 0)
174 jdavis@postgresql.or 594 [ # # ]:UBC 0 : elog(ERROR, "could not encode SCRAM client key");
9 peter@eisentraut.org 595 :GNC 5 : values[n] = encoded;
4935 tgl@sss.pgh.pa.us 596 :CBC 5 : n++;
597 : :
174 jdavis@postgresql.or 598 : 5 : keywords[n] = "scram_server_key";
599 : 5 : len = pg_b64_enc_len(sizeof(MyProcPort->scram_ServerKey));
600 : : /* don't forget the zero-terminator */
9 peter@eisentraut.org 601 :GNC 5 : encoded = palloc0(len + 1);
174 jdavis@postgresql.or 602 :CBC 5 : encoded_len = pg_b64_encode(MyProcPort->scram_ServerKey,
603 : : sizeof(MyProcPort->scram_ServerKey),
604 : : encoded, len);
605 [ - + ]: 5 : if (encoded_len < 0)
174 jdavis@postgresql.or 606 [ # # ]:UBC 0 : elog(ERROR, "could not encode SCRAM server key");
9 peter@eisentraut.org 607 :GNC 5 : values[n] = encoded;
4935 tgl@sss.pgh.pa.us 608 :CBC 5 : n++;
609 : :
610 : : /*
611 : : * Require scram-sha-256 to ensure that no other auth method is used
612 : : * when connecting with foreign server.
613 : : */
174 jdavis@postgresql.or 614 : 5 : keywords[n] = "require_auth";
615 : 5 : values[n] = "scram-sha-256";
616 : 5 : n++;
617 : : }
618 : :
619 : 95 : keywords[n] = values[n] = NULL;
620 : :
621 : : /* Verify the set of connection parameters. */
622 : 95 : check_conn_params(keywords, values, user);
623 : :
624 : 92 : *p_keywords = keywords;
625 : 92 : *p_values = values;
626 : 92 : }
627 : :
628 : : /*
629 : : * Connect to remote server using specified server and user mapping properties.
630 : : */
631 : : static PGconn *
632 : 88 : connect_pg_server(ForeignServer *server, UserMapping *user)
633 : : {
634 : 88 : PGconn *volatile conn = NULL;
635 : :
636 : : /*
637 : : * Use PG_TRY block to ensure closing connection on error.
638 : : */
639 [ + + ]: 88 : PG_TRY();
640 : : {
641 : : const char **keywords;
642 : : const char **values;
643 : : char *appname;
644 : : PGconn *start_conn;
645 : :
646 : 88 : construct_connection_params(server, user, &keywords, &values, &appname);
647 : :
648 : : /* first time, allocate or get the custom wait event */
1057 michael@paquier.xyz 649 [ + + ]: 85 : if (pgfdw_we_connect == 0)
650 : 11 : pgfdw_we_connect = WaitEventExtensionNew("PostgresFdwConnect");
651 : :
652 : : /* OK to make connection */
653 : : start_conn =
91 fujii@postgresql.org 654 : 85 : libpqsrv_connect_params_start(keywords, values,
655 : : /* expand_dbname = */ false);
96 656 : 85 : PQsetNoticeReceiver(start_conn, libpqsrv_notice_receiver,
657 : : "received message via remote connection");
658 : 85 : libpqsrv_connect_complete(start_conn, pgfdw_we_connect);
659 : 85 : conn = start_conn;
660 : :
4935 tgl@sss.pgh.pa.us 661 [ + - + + ]: 85 : if (!conn || PQstatus(conn) != CONNECTION_OK)
662 [ + - ]: 3 : ereport(ERROR,
663 : : (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION),
664 : : errmsg("could not connect to server \"%s\"",
665 : : server->servername),
666 : : errdetail_internal("%s", pchomp(PQerrorMessage(conn)))));
667 : :
668 : : /* Perform post-connection security checks. */
521 peter@eisentraut.org 669 : 82 : pgfdw_security_check(keywords, values, user, conn);
670 : :
671 : : /* Prepare new session for use */
4934 tgl@sss.pgh.pa.us 672 : 80 : configure_remote_session(conn);
673 : :
1707 fujii@postgresql.org 674 [ + + ]: 80 : if (appname != NULL)
675 : 27 : pfree(appname);
4935 tgl@sss.pgh.pa.us 676 : 80 : pfree(keywords);
677 : 80 : pfree(values);
678 : : }
679 : 8 : PG_CATCH();
680 : : {
1312 andres@anarazel.de 681 : 8 : libpqsrv_disconnect(conn);
4935 tgl@sss.pgh.pa.us 682 : 8 : PG_RE_THROW();
683 : : }
684 [ - + ]: 80 : PG_END_TRY();
685 : :
686 : 80 : return conn;
687 : : }
688 : :
689 : : /*
690 : : * Disconnect any open connection for a connection cache entry.
691 : : */
692 : : static void
3324 693 : 71 : disconnect_pg_server(ConnCacheEntry *entry)
694 : : {
695 [ + - ]: 71 : if (entry->conn != NULL)
696 : : {
1312 andres@anarazel.de 697 : 71 : libpqsrv_disconnect(entry->conn);
3324 tgl@sss.pgh.pa.us 698 : 71 : entry->conn = NULL;
699 : : }
700 : 71 : }
701 : :
702 : : /*
703 : : * Check and return the value of password_required, if defined; otherwise,
704 : : * return true, which is the default value of it. The mapping has been
705 : : * pre-validated.
706 : : */
707 : : static bool
2442 andrew@dunslane.net 708 : 17 : UserMappingPasswordRequired(UserMapping *user)
709 : : {
710 : : ListCell *cell;
711 : :
712 [ + + + + : 31 : foreach(cell, user->options)
+ + ]
713 : : {
714 : 17 : DefElem *def = (DefElem *) lfirst(cell);
715 : :
716 [ + + ]: 17 : if (strcmp(def->defname, "password_required") == 0)
717 : 3 : return defGetBoolean(def);
718 : : }
719 : :
720 : 14 : return true;
721 : : }
722 : :
723 : : /*
724 : : * Return whether SCRAM pass-through is enabled.
725 : : *
726 : : * If use_scram_passthrough is specified in both the foreign server
727 : : * and the user mapping, the user mapping setting takes precedence.
728 : : */
729 : : static bool
589 peter@eisentraut.org 730 : 14 : UseScramPassthrough(ForeignServer *server, UserMapping *user)
731 : : {
732 : : ListCell *cell;
733 : :
93 fujii@postgresql.org 734 [ + + + + : 20 : foreach(cell, user->options)
+ + ]
735 : : {
589 peter@eisentraut.org 736 : 8 : DefElem *def = (DefElem *) lfirst(cell);
737 : :
738 [ + + ]: 8 : if (strcmp(def->defname, "use_scram_passthrough") == 0)
739 : 2 : return defGetBoolean(def);
740 : : }
741 : :
93 fujii@postgresql.org 742 [ + - + + : 48 : foreach(cell, server->options)
+ + ]
743 : : {
589 peter@eisentraut.org 744 : 41 : DefElem *def = (DefElem *) lfirst(cell);
745 : :
746 [ + + ]: 41 : if (strcmp(def->defname, "use_scram_passthrough") == 0)
747 : 5 : return defGetBoolean(def);
748 : : }
749 : :
750 : 7 : return false;
751 : : }
752 : :
753 : : /*
754 : : * For non-superusers, insist that the connstr specify a password or that the
755 : : * user provided their own GSSAPI delegated credentials. This
756 : : * prevents a password from being picked up from .pgpass, a service file, the
757 : : * environment, etc. We don't want the postgres user's passwords,
758 : : * certificates, etc to be accessible to non-superusers. (See also
759 : : * dblink_connstr_check in contrib/dblink.)
760 : : */
761 : : static void
3187 rhaas@postgresql.org 762 : 95 : check_conn_params(const char **keywords, const char **values, UserMapping *user)
763 : : {
764 : : int i;
765 : :
766 : : /* no check required if superuser */
767 [ + + ]: 95 : if (superuser_arg(user->userid))
4935 tgl@sss.pgh.pa.us 768 : 83 : return;
769 : :
770 : : #ifdef ENABLE_GSS
771 : : /* ok if the user provided their own delegated credentials */
1195 bruce@momjian.us 772 [ - + ]: 12 : if (be_gssapi_get_delegation(MyProcPort))
1232 sfrost@snowman.net 773 :UBC 0 : return;
774 : : #endif
775 : :
776 : : /* ok if params contain a non-empty password */
4935 tgl@sss.pgh.pa.us 777 [ + + ]:CBC 81 : for (i = 0; keywords[i] != NULL; i++)
778 : : {
779 [ + + + - ]: 72 : if (strcmp(keywords[i], "password") == 0 && values[i][0] != '\0')
780 : 3 : return;
781 : : }
782 : :
783 : : /* ok if the superuser explicitly said so at user mapping creation time */
2442 andrew@dunslane.net 784 [ + + ]: 9 : if (!UserMappingPasswordRequired(user))
785 : 1 : return;
786 : :
787 : : /*
788 : : * Ok if SCRAM pass-through is being used and all required scram options
789 : : * are set correctly. If pgfdw_has_required_scram_options returns true we
790 : : * assume that UseScramPassthrough is also true since SCRAM options are
791 : : * only set when UseScramPassthrough is enabled.
792 : : */
384 peter@eisentraut.org 793 [ + - + + : 8 : if (MyProcPort != NULL && MyProcPort->has_scram_keys && pgfdw_has_required_scram_options(keywords, values))
+ + ]
521 794 : 5 : return;
795 : :
4935 tgl@sss.pgh.pa.us 796 [ + - ]: 3 : ereport(ERROR,
797 : : (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
798 : : errmsg("password or GSSAPI delegated credentials required"),
799 : : errdetail("Non-superusers must delegate GSSAPI credentials, provide a password, or enable SCRAM pass-through in user mapping.")));
800 : : }
801 : :
802 : : /*
803 : : * Issue SET commands to make sure remote session is configured properly.
804 : : *
805 : : * We do this just once at connection, assuming nothing will change the
806 : : * values later. Since we'll never send volatile function calls to the
807 : : * remote, there shouldn't be any way to break this assumption from our end.
808 : : * It's possible to think of ways to break it at the remote end, eg making
809 : : * a foreign table point to a view that includes a set_config call ---
810 : : * but once you admit the possibility of a malicious view definition,
811 : : * there are any number of ways to break things.
812 : : */
813 : : static void
4934 814 : 80 : configure_remote_session(PGconn *conn)
815 : : {
4917 816 : 80 : int remoteversion = PQserverVersion(conn);
817 : :
818 : : /* Force the search path to contain only pg_catalog (see deparse.c) */
819 : 80 : do_sql_command(conn, "SET search_path = pg_catalog");
820 : :
821 : : /*
822 : : * Set remote timezone; this is basically just cosmetic, since all
823 : : * transmitted and returned timestamptzs should specify a zone explicitly
824 : : * anyway. However it makes the regression test outputs more predictable.
825 : : *
826 : : * We don't risk setting remote zone equal to ours, since the remote
827 : : * server might use a different timezone database. Instead, use GMT
828 : : * (quoted, because very old servers are picky about case). That's
829 : : * guaranteed to work regardless of the remote's timezone database,
830 : : * because pg_tzset() hard-wires it (at least in PG 9.2 and later).
831 : : */
858 832 : 80 : do_sql_command(conn, "SET timezone = 'GMT'");
833 : :
834 : : /*
835 : : * Set values needed to ensure unambiguous data output from remote. (This
836 : : * logic should match what pg_dump does. See also set_transmission_modes
837 : : * in postgres_fdw.c.)
838 : : */
4917 839 : 80 : do_sql_command(conn, "SET datestyle = ISO");
840 [ + - ]: 80 : if (remoteversion >= 80400)
841 : 80 : do_sql_command(conn, "SET intervalstyle = postgres");
842 [ + - ]: 80 : if (remoteversion >= 90000)
843 : 80 : do_sql_command(conn, "SET extra_float_digits = 3");
844 : : else
4917 tgl@sss.pgh.pa.us 845 :UBC 0 : do_sql_command(conn, "SET extra_float_digits = 2");
4917 tgl@sss.pgh.pa.us 846 :CBC 80 : }
847 : :
848 : : /*
849 : : * Convenience subroutine to issue a non-data-returning SQL command to remote
850 : : */
851 : : void
852 : 1976 : do_sql_command(PGconn *conn, const char *sql)
853 : : {
1645 efujita@postgresql.o 854 : 1976 : do_sql_command_begin(conn, sql);
855 : 1976 : do_sql_command_end(conn, sql, false);
856 : 1973 : }
857 : :
858 : : static void
859 : 1994 : do_sql_command_begin(PGconn *conn, const char *sql)
860 : : {
3368 rhaas@postgresql.org 861 [ - + ]: 1994 : if (!PQsendQuery(conn, sql))
394 tgl@sss.pgh.pa.us 862 :UBC 0 : pgfdw_report_error(NULL, conn, sql);
1645 efujita@postgresql.o 863 :CBC 1994 : }
864 : :
865 : : static void
866 : 1994 : do_sql_command_end(PGconn *conn, const char *sql, bool consume_input)
867 : : {
868 : : PGresult *res;
869 : :
870 : : /*
871 : : * If requested, consume whatever data is available from the socket. (Note
872 : : * that if all data is available, this allows pgfdw_get_result to call
873 : : * PQgetResult without forcing the overhead of WaitLatchOrSocket, which
874 : : * would be large compared to the overhead of PQconsumeInput.)
875 : : */
876 [ + + - + ]: 1994 : if (consume_input && !PQconsumeInput(conn))
394 tgl@sss.pgh.pa.us 877 :UBC 0 : pgfdw_report_error(NULL, conn, sql);
962 noah@leadboat.com 878 :CBC 1994 : res = pgfdw_get_result(conn);
4934 tgl@sss.pgh.pa.us 879 [ + + ]: 1994 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
394 880 : 3 : pgfdw_report_error(res, conn, sql);
4934 881 : 1991 : PQclear(res);
882 : 1991 : }
883 : :
884 : : /*
885 : : * Start remote transaction or subtransaction, if needed.
886 : : *
887 : : * Note that we always use at least REPEATABLE READ in the remote session.
888 : : * This is so that, if a query initiates multiple scans of the same or
889 : : * different foreign tables, we will get snapshot-consistent results from
890 : : * those scans. A disadvantage is that we can't provide sane emulation of
891 : : * READ COMMITTED behavior --- it would be nice if we had some other way to
892 : : * control which remote queries share a snapshot.
893 : : *
894 : : * Note also that we always start the remote transaction with the same
895 : : * read/write and deferrable properties as the local transaction, and start
896 : : * the remote subtransaction with the same read/write property as the local
897 : : * subtransaction.
898 : : */
899 : : static void
4935 900 : 2319 : begin_remote_xact(ConnCacheEntry *entry)
901 : : {
902 : 2319 : int curlevel = GetCurrentTransactionNestLevel();
903 : :
904 : : /*
905 : : * If the current local (sub)transaction is read-only, set the topmost
906 : : * read-only local transaction's nesting level if we haven't yet.
907 : : *
908 : : * Note: once it's set, it's retained until the topmost read-only local
909 : : * transaction is committed/aborted (see pgfdw_xact_callback and
910 : : * pgfdw_subxact_callback).
911 : : */
144 efujita@postgresql.o 912 [ + + ]: 2319 : if (XactReadOnly)
913 : : {
914 [ + + ]: 10 : if (read_only_level == 0)
915 : 9 : read_only_level = GetTopReadOnlyTransactionNestLevel();
916 [ - + ]: 10 : Assert(read_only_level > 0);
917 : : }
918 : : else
919 [ - + ]: 2309 : Assert(read_only_level == 0);
920 : :
921 : : /*
922 : : * Start main transaction if we haven't yet; otherwise, change the current
923 : : * remote (sub)transaction's read/write mode if needed.
924 : : */
4935 tgl@sss.pgh.pa.us 925 [ + + ]: 2319 : if (entry->xact_depth <= 0)
926 : : {
927 : : /*
928 : : * This is the case when we haven't yet started a main transaction.
929 : : */
930 : : StringInfoData sql;
144 efujita@postgresql.o 931 : 805 : bool ro = (read_only_level == 1);
932 : :
4935 tgl@sss.pgh.pa.us 933 [ - + ]: 805 : elog(DEBUG3, "starting remote transaction on connection %p",
934 : : entry->conn);
935 : :
144 efujita@postgresql.o 936 : 805 : initStringInfo(&sql);
937 : 805 : appendStringInfoString(&sql, "START TRANSACTION ISOLATION LEVEL ");
4935 tgl@sss.pgh.pa.us 938 [ + + ]: 805 : if (IsolationIsSerializable())
144 efujita@postgresql.o 939 : 3 : appendStringInfoString(&sql, "SERIALIZABLE");
940 : : else
941 : 802 : appendStringInfoString(&sql, "REPEATABLE READ");
942 [ + + ]: 805 : if (ro)
943 : 3 : appendStringInfoString(&sql, " READ ONLY");
944 [ + + ]: 805 : if (XactDeferrable)
945 : 2 : appendStringInfoString(&sql, " DEFERRABLE");
3368 rhaas@postgresql.org 946 : 805 : entry->changing_xact_state = true;
144 efujita@postgresql.o 947 : 805 : do_sql_command(entry->conn, sql.data);
4935 tgl@sss.pgh.pa.us 948 : 804 : entry->xact_depth = 1;
144 efujita@postgresql.o 949 [ + + ]: 804 : if (ro)
950 : : {
951 [ - + ]: 3 : Assert(!entry->xact_read_only);
952 : 3 : entry->xact_read_only = true;
953 : : }
3368 rhaas@postgresql.org 954 : 804 : entry->changing_xact_state = false;
955 : : }
144 efujita@postgresql.o 956 [ + + ]: 1514 : else if (!entry->xact_read_only)
957 : : {
958 : : /*
959 : : * The remote (sub)transaction has been opened in read-write mode.
960 : : */
961 [ + + - + ]: 1513 : Assert(read_only_level == 0 ||
962 : : entry->xact_depth <= read_only_level);
963 : :
964 : : /*
965 : : * If its nesting depth matches read_only_level, it means that the
966 : : * local read-write (sub)transaction that started it has changed to
967 : : * read-only after that; in which case change it to read-only as well.
968 : : * Otherwise, the local (sub)transaction is still read-write, so there
969 : : * is no need to do anything.
970 : : */
971 [ + + ]: 1513 : if (entry->xact_depth == read_only_level)
972 : : {
973 : 4 : entry->changing_xact_state = true;
974 : 4 : do_sql_command(entry->conn, "SET transaction_read_only = on");
975 : 4 : entry->xact_read_only = true;
976 : 4 : entry->changing_xact_state = false;
977 : : }
978 : : }
979 : : else
980 : : {
981 : : /*
982 : : * The remote (sub)transaction has been opened in read-only mode.
983 : : */
984 [ + - - + ]: 1 : Assert(read_only_level > 0 &&
985 : : entry->xact_depth >= read_only_level);
986 : :
987 : : /*
988 : : * The local read-only (sub)transaction that started it is guaranteed
989 : : * to be still read-only (see check_transaction_read_only), so there
990 : : * is no need to do anything.
991 : : */
992 : : }
993 : :
994 : : /*
995 : : * If we're in a subtransaction, stack up savepoints to match our level.
996 : : * This ensures we can rollback just the desired effects when a
997 : : * subtransaction aborts.
998 : : */
4935 tgl@sss.pgh.pa.us 999 [ + + ]: 2339 : while (entry->xact_depth < curlevel)
1000 : : {
1001 : : StringInfoData sql;
144 efujita@postgresql.o 1002 : 22 : bool ro = (entry->xact_depth + 1 == read_only_level);
1003 : :
1004 : 22 : initStringInfo(&sql);
1005 : 22 : appendStringInfo(&sql, "SAVEPOINT s%d", entry->xact_depth + 1);
1006 [ + + ]: 22 : if (ro)
1007 : 2 : appendStringInfoString(&sql, "; SET transaction_read_only = on");
3368 rhaas@postgresql.org 1008 : 22 : entry->changing_xact_state = true;
144 efujita@postgresql.o 1009 : 22 : do_sql_command(entry->conn, sql.data);
4935 tgl@sss.pgh.pa.us 1010 : 21 : entry->xact_depth++;
144 efujita@postgresql.o 1011 [ + + ]: 21 : if (ro)
1012 : : {
1013 [ - + ]: 2 : Assert(!entry->xact_read_only);
1014 : 2 : entry->xact_read_only = true;
1015 : : }
3368 rhaas@postgresql.org 1016 : 21 : entry->changing_xact_state = false;
1017 : : }
4935 tgl@sss.pgh.pa.us 1018 : 2317 : }
1019 : :
1020 : : /*
1021 : : * Release connection reference count created by calling GetConnection.
1022 : : */
1023 : : void
1024 : 2242 : ReleaseConnection(PGconn *conn)
1025 : : {
1026 : : /*
1027 : : * Currently, we don't actually track connection references because all
1028 : : * cleanup is managed on a transaction or subtransaction basis instead. So
1029 : : * there's nothing to do here.
1030 : : */
1031 : 2242 : }
1032 : :
1033 : : /*
1034 : : * Assign a "unique" number for a cursor.
1035 : : *
1036 : : * These really only need to be unique per connection within a transaction.
1037 : : * For the moment we ignore the per-connection point and assign them across
1038 : : * all connections in the transaction, but we ask for the connection to be
1039 : : * supplied in case we want to refine that.
1040 : : *
1041 : : * Note that even if wraparound happens in a very long transaction, actual
1042 : : * collisions are highly improbable; just be sure to use %u not %d to print.
1043 : : */
1044 : : unsigned int
1045 : 604 : GetCursorNumber(PGconn *conn)
1046 : : {
1047 : 604 : return ++cursor_number;
1048 : : }
1049 : :
1050 : : /*
1051 : : * Assign a "unique" number for a prepared statement.
1052 : : *
1053 : : * This works much like GetCursorNumber, except that we never reset the counter
1054 : : * within a session. That's because we can't be 100% sure we've gotten rid
1055 : : * of all prepared statements on all connections, and it's not really worth
1056 : : * increasing the risk of prepared-statement name collisions by resetting.
1057 : : */
1058 : : unsigned int
4918 1059 : 189 : GetPrepStmtNumber(PGconn *conn)
1060 : : {
1061 : 189 : return ++prep_stmt_number;
1062 : : }
1063 : :
1064 : : /*
1065 : : * Submit a query and wait for the result.
1066 : : *
1067 : : * Since we don't use non-blocking mode, this can't process interrupts while
1068 : : * pushing the query text to the server. That risk is relatively small, so we
1069 : : * ignore that for now.
1070 : : *
1071 : : * Caller is responsible for the error handling on the result.
1072 : : */
1073 : : PGresult *
1975 efujita@postgresql.o 1074 : 4309 : pgfdw_exec_query(PGconn *conn, const char *query, PgFdwConnState *state)
1075 : : {
1076 : : /* First, process a pending asynchronous request, if any. */
1077 [ + + + + ]: 4309 : if (state && state->pendingAreq)
1078 : 6 : process_pending_request(state->pendingAreq);
1079 : :
3780 rhaas@postgresql.org 1080 [ + + ]: 4309 : if (!PQsendQuery(conn, query))
962 noah@leadboat.com 1081 : 1 : return NULL;
1082 : 4308 : return pgfdw_get_result(conn);
1083 : : }
1084 : :
1085 : : /*
1086 : : * Wrap libpqsrv_get_result_last(), adding wait event.
1087 : : *
1088 : : * Caller is responsible for the error handling on the result.
1089 : : */
1090 : : PGresult *
1091 : 8693 : pgfdw_get_result(PGconn *conn)
1092 : : {
1093 : 8693 : return libpqsrv_get_result_last(conn, pgfdw_we_get_result);
1094 : : }
1095 : :
1096 : : /*
1097 : : * Report an error we got from the remote server.
1098 : : *
1099 : : * Callers should use pgfdw_report_error() to throw an error, or use
1100 : : * pgfdw_report() for lesser message levels. (We make this distinction
1101 : : * so that pgfdw_report_error() can be marked noreturn.)
1102 : : *
1103 : : * res: PGresult containing the error (might be NULL)
1104 : : * conn: connection we did the query on
1105 : : * sql: NULL, or text of remote command we tried to execute
1106 : : *
1107 : : * If "res" is not NULL, it'll be PQclear'ed here (unless we throw error,
1108 : : * in which case memory context cleanup will clear it eventually).
1109 : : *
1110 : : * Note: callers that choose not to throw ERROR for a remote error are
1111 : : * responsible for making sure that the associated ConnCacheEntry gets
1112 : : * marked with have_error = true.
1113 : : */
1114 : : void
394 tgl@sss.pgh.pa.us 1115 : 26 : pgfdw_report_error(PGresult *res, PGconn *conn, const char *sql)
1116 : : {
1117 : 26 : pgfdw_report_internal(ERROR, res, conn, sql);
394 tgl@sss.pgh.pa.us 1118 :UBC 0 : pg_unreachable();
1119 : : }
1120 : :
1121 : : void
394 tgl@sss.pgh.pa.us 1122 :CBC 3 : pgfdw_report(int elevel, PGresult *res, PGconn *conn, const char *sql)
1123 : : {
1124 [ - + ]: 3 : Assert(elevel < ERROR); /* use pgfdw_report_error for that */
1125 : 3 : pgfdw_report_internal(elevel, res, conn, sql);
1126 : 3 : }
1127 : :
1128 : : static void
1129 : 29 : pgfdw_report_internal(int elevel, PGresult *res, PGconn *conn,
1130 : : const char *sql)
1131 : : {
398 1132 : 29 : char *diag_sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
1133 : 29 : char *message_primary = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
1134 : 29 : char *message_detail = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL);
1135 : 29 : char *message_hint = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT);
1136 : 29 : char *message_context = PQresultErrorField(res, PG_DIAG_CONTEXT);
1137 : : int sqlstate;
1138 : :
1139 [ + + ]: 29 : if (diag_sqlstate)
1140 : 22 : sqlstate = MAKE_SQLSTATE(diag_sqlstate[0],
1141 : : diag_sqlstate[1],
1142 : : diag_sqlstate[2],
1143 : : diag_sqlstate[3],
1144 : : diag_sqlstate[4]);
1145 : : else
1146 : 7 : sqlstate = ERRCODE_CONNECTION_FAILURE;
1147 : :
1148 : : /*
1149 : : * If we don't get a message from the PGresult, try the PGconn. This is
1150 : : * needed because for connection-level failures, PQgetResult may just
1151 : : * return NULL, not a PGresult at all.
1152 : : */
1153 [ + + ]: 29 : if (message_primary == NULL)
1154 : 7 : message_primary = pchomp(PQerrorMessage(conn));
1155 : :
1156 [ + - + - : 29 : ereport(elevel,
+ - + + +
+ + + +
- ]
1157 : : (errcode(sqlstate),
1158 : : (message_primary != NULL && message_primary[0] != '\0') ?
1159 : : errmsg_internal("%s", message_primary) :
1160 : : errmsg("could not obtain message string for remote error"),
1161 : : message_detail ? errdetail_internal("%s", message_detail) : 0,
1162 : : message_hint ? errhint("%s", message_hint) : 0,
1163 : : message_context ? errcontext("%s", message_context) : 0,
1164 : : sql ? errcontext("remote SQL command: %s", sql) : 0));
1165 : 3 : PQclear(res);
4935 1166 : 3 : }
1167 : :
1168 : : /*
1169 : : * pgfdw_xact_callback --- cleanup at main-transaction end.
1170 : : *
1171 : : * This runs just late enough that it must not enter user-defined code
1172 : : * locally. (Entering such code on the remote side is fine. Its remote
1173 : : * COMMIT TRANSACTION may run deferred triggers.)
1174 : : */
1175 : : static void
1176 : 4491 : pgfdw_xact_callback(XactEvent event, void *arg)
1177 : : {
1178 : : HASH_SEQ_STATUS scan;
1179 : : ConnCacheEntry *entry;
1645 efujita@postgresql.o 1180 : 4491 : List *pending_entries = NIL;
1239 1181 : 4491 : List *cancel_requested = NIL;
1182 : :
1183 : : /* Quick exit if no connections were touched in this transaction. */
4935 tgl@sss.pgh.pa.us 1184 [ + + ]: 4491 : if (!xact_got_connection)
1185 : 3719 : return;
1186 : :
1187 : : /*
1188 : : * Scan all connection cache entries to find open remote transactions, and
1189 : : * close them.
1190 : : */
1191 : 772 : hash_seq_init(&scan, ConnectionHash);
1192 [ + + ]: 4060 : while ((entry = (ConnCacheEntry *) hash_seq_search(&scan)))
1193 : : {
1194 : : PGresult *res;
1195 : :
1196 : : /* Ignore cache entry if no open connection right now */
4588 1197 [ + + ]: 3289 : if (entry->conn == NULL)
4935 1198 : 1902 : continue;
1199 : :
1200 : : /* If it has an open remote transaction, try to close it */
4588 1201 [ + + ]: 1387 : if (entry->xact_depth > 0)
1202 : : {
1203 [ - + ]: 805 : elog(DEBUG3, "closing remote transaction on connection %p",
1204 : : entry->conn);
1205 : :
1206 [ + + - + : 805 : switch (event)
- ]
1207 : : {
4137 rhaas@postgresql.org 1208 : 744 : case XACT_EVENT_PARALLEL_PRE_COMMIT:
1209 : : case XACT_EVENT_PRE_COMMIT:
1210 : :
1211 : : /*
1212 : : * If abort cleanup previously failed for this connection,
1213 : : * we can't issue any more commands against it.
1214 : : */
3368 1215 : 744 : pgfdw_reject_incomplete_xact_state_change(entry);
1216 : :
1217 : : /* Commit all remote transactions during pre-commit */
1218 : 744 : entry->changing_xact_state = true;
1645 efujita@postgresql.o 1219 [ + + ]: 744 : if (entry->parallel_commit)
1220 : : {
1221 : 16 : do_sql_command_begin(entry->conn, "COMMIT TRANSACTION");
1222 : 16 : pending_entries = lappend(pending_entries, entry);
1223 : 16 : continue;
1224 : : }
4588 tgl@sss.pgh.pa.us 1225 : 728 : do_sql_command(entry->conn, "COMMIT TRANSACTION");
3368 rhaas@postgresql.org 1226 : 728 : entry->changing_xact_state = false;
1227 : :
1228 : : /*
1229 : : * If there were any errors in subtransactions, and we
1230 : : * made prepared statements, do a DEALLOCATE ALL to make
1231 : : * sure we get rid of all prepared statements. This is
1232 : : * annoying and not terribly bulletproof, but it's
1233 : : * probably not worth trying harder.
1234 : : *
1235 : : * DEALLOCATE ALL only exists in 8.3 and later, so this
1236 : : * constrains how old a server postgres_fdw can
1237 : : * communicate with. We intentionally ignore errors in
1238 : : * the DEALLOCATE, so that we can hobble along to some
1239 : : * extent with older servers (leaking prepared statements
1240 : : * as we go; but we don't really support update operations
1241 : : * pre-8.3 anyway).
1242 : : */
4918 tgl@sss.pgh.pa.us 1243 [ + + - + ]: 728 : if (entry->have_prep_stmt && entry->have_error)
1244 : : {
962 noah@leadboat.com 1245 :UBC 0 : res = pgfdw_exec_query(entry->conn, "DEALLOCATE ALL",
1246 : : NULL);
4918 tgl@sss.pgh.pa.us 1247 : 0 : PQclear(res);
1248 : : }
4918 tgl@sss.pgh.pa.us 1249 :CBC 728 : entry->have_prep_stmt = false;
1250 : 728 : entry->have_error = false;
4588 1251 : 728 : break;
1252 : 1 : case XACT_EVENT_PRE_PREPARE:
1253 : :
1254 : : /*
1255 : : * We disallow any remote transactions, since it's not
1256 : : * very reasonable to hold them open until the prepared
1257 : : * transaction is committed. For the moment, throw error
1258 : : * unconditionally; later we might allow read-only cases.
1259 : : * Note that the error will cause us to come right back
1260 : : * here with event == XACT_EVENT_ABORT, so we'll clean up
1261 : : * the connection state at that point.
1262 : : */
1263 [ + - ]: 1 : ereport(ERROR,
1264 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1265 : : errmsg("cannot PREPARE a transaction that has operated on postgres_fdw foreign tables")));
1266 : : break;
4137 rhaas@postgresql.org 1267 :UBC 0 : case XACT_EVENT_PARALLEL_COMMIT:
1268 : : case XACT_EVENT_COMMIT:
1269 : : case XACT_EVENT_PREPARE:
1270 : : /* Pre-commit should have closed the open transaction */
4588 tgl@sss.pgh.pa.us 1271 [ # # ]: 0 : elog(ERROR, "missed cleaning up connection during pre-commit");
1272 : : break;
4137 rhaas@postgresql.org 1273 :CBC 60 : case XACT_EVENT_PARALLEL_ABORT:
1274 : : case XACT_EVENT_ABORT:
1275 : : /* Rollback all remote transactions during abort */
1239 efujita@postgresql.o 1276 [ + + ]: 60 : if (entry->parallel_abort)
1277 : : {
1278 [ + - ]: 4 : if (pgfdw_abort_cleanup_begin(entry, true,
1279 : : &pending_entries,
1280 : : &cancel_requested))
1281 : 4 : continue;
1282 : : }
1283 : : else
1284 : 56 : pgfdw_abort_cleanup(entry, true);
4588 tgl@sss.pgh.pa.us 1285 : 56 : break;
1286 : : }
1287 : : }
1288 : :
1289 : : /* Reset state to show we're out of a transaction */
1645 efujita@postgresql.o 1290 : 1366 : pgfdw_reset_xact_state(entry, true);
1291 : : }
1292 : :
1293 : : /* If there are any pending connections, finish cleaning them up */
1239 1294 [ + + - + ]: 771 : if (pending_entries || cancel_requested)
1295 : : {
1296 [ + - + + ]: 15 : if (event == XACT_EVENT_PARALLEL_PRE_COMMIT ||
1297 : : event == XACT_EVENT_PRE_COMMIT)
1298 : : {
1299 [ - + ]: 13 : Assert(cancel_requested == NIL);
1300 : 13 : pgfdw_finish_pre_commit_cleanup(pending_entries);
1301 : : }
1302 : : else
1303 : : {
1304 [ + - - + ]: 2 : Assert(event == XACT_EVENT_PARALLEL_ABORT ||
1305 : : event == XACT_EVENT_ABORT);
1306 : 2 : pgfdw_finish_abort_cleanup(pending_entries, cancel_requested,
1307 : : true);
1308 : : }
1309 : : }
1310 : :
1311 : : /*
1312 : : * Regardless of the event type, we can now mark ourselves as out of the
1313 : : * transaction. (Note: if we are here during PRE_COMMIT or PRE_PREPARE,
1314 : : * this saves a useless scan of the hashtable during COMMIT or PREPARE.)
1315 : : */
4935 tgl@sss.pgh.pa.us 1316 : 771 : xact_got_connection = false;
1317 : :
1318 : : /* Also reset cursor numbering for next transaction */
1319 : 771 : cursor_number = 0;
1320 : :
1321 : : /* Likewise for read_only_level */
144 efujita@postgresql.o 1322 : 771 : read_only_level = 0;
1323 : : }
1324 : :
1325 : : /*
1326 : : * pgfdw_subxact_callback --- cleanup at subtransaction end.
1327 : : */
1328 : : static void
4935 tgl@sss.pgh.pa.us 1329 : 72 : pgfdw_subxact_callback(SubXactEvent event, SubTransactionId mySubid,
1330 : : SubTransactionId parentSubid, void *arg)
1331 : : {
1332 : : HASH_SEQ_STATUS scan;
1333 : : ConnCacheEntry *entry;
1334 : : int curlevel;
1645 efujita@postgresql.o 1335 : 72 : List *pending_entries = NIL;
1239 1336 : 72 : List *cancel_requested = NIL;
1337 : :
1338 : : /* Nothing to do at subxact start, nor after commit. */
4935 tgl@sss.pgh.pa.us 1339 [ + + + + ]: 72 : if (!(event == SUBXACT_EVENT_PRE_COMMIT_SUB ||
1340 : : event == SUBXACT_EVENT_ABORT_SUB))
1341 : 43 : return;
1342 : :
1343 : : /* Quick exit if no connections were touched in this transaction. */
1344 [ - + ]: 29 : if (!xact_got_connection)
4935 tgl@sss.pgh.pa.us 1345 :UBC 0 : return;
1346 : :
1347 : : /*
1348 : : * Scan all connection cache entries to find open remote subtransactions
1349 : : * of the current level, and close them.
1350 : : */
4935 tgl@sss.pgh.pa.us 1351 :CBC 29 : curlevel = GetCurrentTransactionNestLevel();
1352 : 29 : hash_seq_init(&scan, ConnectionHash);
1353 [ + + ]: 242 : while ((entry = (ConnCacheEntry *) hash_seq_search(&scan)))
1354 : : {
1355 : : char sql[100];
1356 : :
1357 : : /*
1358 : : * We only care about connections with open remote subtransactions of
1359 : : * the current level.
1360 : : */
1361 [ + + + + ]: 213 : if (entry->conn == NULL || entry->xact_depth < curlevel)
1362 : 198 : continue;
1363 : :
1364 [ - + ]: 21 : if (entry->xact_depth > curlevel)
4935 tgl@sss.pgh.pa.us 1365 [ # # ]:UBC 0 : elog(ERROR, "missed cleaning up remote subtransaction at level %d",
1366 : : entry->xact_depth);
1367 : :
4935 tgl@sss.pgh.pa.us 1368 [ + + ]:CBC 21 : if (event == SUBXACT_EVENT_PRE_COMMIT_SUB)
1369 : : {
1370 : : /*
1371 : : * If abort cleanup previously failed for this connection, we
1372 : : * can't issue any more commands against it.
1373 : : */
3368 rhaas@postgresql.org 1374 : 7 : pgfdw_reject_incomplete_xact_state_change(entry);
1375 : :
1376 : : /* Commit all remote subtransactions during pre-commit */
4935 tgl@sss.pgh.pa.us 1377 : 7 : snprintf(sql, sizeof(sql), "RELEASE SAVEPOINT s%d", curlevel);
3368 rhaas@postgresql.org 1378 : 7 : entry->changing_xact_state = true;
1645 efujita@postgresql.o 1379 [ + + ]: 7 : if (entry->parallel_commit)
1380 : : {
1381 : 2 : do_sql_command_begin(entry->conn, sql);
1382 : 2 : pending_entries = lappend(pending_entries, entry);
1383 : 2 : continue;
1384 : : }
4917 tgl@sss.pgh.pa.us 1385 : 5 : do_sql_command(entry->conn, sql);
3368 rhaas@postgresql.org 1386 : 5 : entry->changing_xact_state = false;
1387 : : }
1388 : : else
1389 : : {
1390 : : /* Rollback all remote subtransactions during abort */
1239 efujita@postgresql.o 1391 [ + + ]: 14 : if (entry->parallel_abort)
1392 : : {
1393 [ + - ]: 4 : if (pgfdw_abort_cleanup_begin(entry, false,
1394 : : &pending_entries,
1395 : : &cancel_requested))
1396 : 4 : continue;
1397 : : }
1398 : : else
1399 : 10 : pgfdw_abort_cleanup(entry, false);
1400 : : }
1401 : :
1402 : : /* OK, we're outta that level of subtransaction */
1645 1403 : 15 : pgfdw_reset_xact_state(entry, false);
1404 : : }
1405 : :
1406 : : /* If there are any pending connections, finish cleaning them up */
1239 1407 [ + + - + ]: 29 : if (pending_entries || cancel_requested)
1408 : : {
1409 [ + + ]: 3 : if (event == SUBXACT_EVENT_PRE_COMMIT_SUB)
1410 : : {
1411 [ - + ]: 1 : Assert(cancel_requested == NIL);
1412 : 1 : pgfdw_finish_pre_subcommit_cleanup(pending_entries, curlevel);
1413 : : }
1414 : : else
1415 : : {
1416 [ - + ]: 2 : Assert(event == SUBXACT_EVENT_ABORT_SUB);
1417 : 2 : pgfdw_finish_abort_cleanup(pending_entries, cancel_requested,
1418 : : false);
1419 : : }
1420 : : }
1421 : :
1422 : : /* If in read_only_level, reset it */
144 1423 [ + + ]: 29 : if (curlevel == read_only_level)
1424 : 3 : read_only_level = 0;
1425 : : }
1426 : :
1427 : : /*
1428 : : * Connection invalidation callback function
1429 : : *
1430 : : * After a change to a pg_foreign_server or pg_user_mapping catalog entry,
1431 : : * close connections depending on that entry immediately if current transaction
1432 : : * has not used those connections yet. Otherwise, mark those connections as
1433 : : * invalid and then make pgfdw_xact_callback() close them at the end of current
1434 : : * transaction, since they cannot be closed in the midst of the transaction
1435 : : * using them. Closed connections will be remade at the next opportunity if
1436 : : * necessary.
1437 : : *
1438 : : * Although most cache invalidation callbacks blow away all the related stuff
1439 : : * regardless of the given hashvalue, connections are expensive enough that
1440 : : * it's worth trying to avoid that.
1441 : : *
1442 : : * NB: We could avoid unnecessary disconnection more strictly by examining
1443 : : * individual option values, but it seems too much effort for the gain.
1444 : : */
1445 : : static void
190 michael@paquier.xyz 1446 : 188 : pgfdw_inval_callback(Datum arg, SysCacheIdentifier cacheid, uint32 hashvalue)
1447 : : {
1448 : : HASH_SEQ_STATUS scan;
1449 : : ConnCacheEntry *entry;
1450 : :
3324 tgl@sss.pgh.pa.us 1451 [ + + - + ]: 188 : Assert(cacheid == FOREIGNSERVEROID || cacheid == USERMAPPINGOID);
1452 : :
1453 : : /* ConnectionHash must exist already, if we're registered */
1454 : 188 : hash_seq_init(&scan, ConnectionHash);
1455 [ + + ]: 1222 : while ((entry = (ConnCacheEntry *) hash_seq_search(&scan)))
1456 : : {
1457 : : /* Ignore invalid entries */
1458 [ + + ]: 1034 : if (entry->conn == NULL)
1459 : 838 : continue;
1460 : :
1461 : : /* hashvalue == 0 means a cache reset, must clear all state */
1462 [ + - + + ]: 196 : if (hashvalue == 0 ||
1463 : 140 : (cacheid == FOREIGNSERVEROID &&
1464 [ + + + + ]: 196 : entry->server_hashvalue == hashvalue) ||
1465 : 56 : (cacheid == USERMAPPINGOID &&
1466 [ + + ]: 56 : entry->mapping_hashvalue == hashvalue))
1467 : : {
1468 : : /*
1469 : : * Close the connection immediately if it's not used yet in this
1470 : : * transaction. Otherwise mark it as invalid so that
1471 : : * pgfdw_xact_callback() can close it at the end of this
1472 : : * transaction.
1473 : : */
2068 fujii@postgresql.org 1474 [ + + ]: 59 : if (entry->xact_depth == 0)
1475 : : {
1476 [ - + ]: 56 : elog(DEBUG3, "discarding connection %p", entry->conn);
1477 : 56 : disconnect_pg_server(entry);
1478 : : }
1479 : : else
1480 : 3 : entry->invalidated = true;
1481 : : }
1482 : : }
3324 tgl@sss.pgh.pa.us 1483 : 188 : }
1484 : :
1485 : : /*
1486 : : * Raise an error if the given connection cache entry is marked as being
1487 : : * in the middle of an xact state change. This should be called at which no
1488 : : * such change is expected to be in progress; if one is found to be in
1489 : : * progress, it means that we aborted in the middle of a previous state change
1490 : : * and now don't know what the remote transaction state actually is.
1491 : : * Such connections can't safely be further used. Re-establishing the
1492 : : * connection would change the snapshot and roll back any writes already
1493 : : * performed, so that's not an option, either. Thus, we must abort.
1494 : : *
1495 : : * Note: there might be open cursors that use the connection, so even if the
1496 : : * connection cache entry is marked as such, we will retain it until abort
1497 : : * cleanup of the main transaction, to ensure such open cursors can safely
1498 : : * refer to the PGconn for the connection.
1499 : : */
1500 : : static void
3368 rhaas@postgresql.org 1501 : 3079 : pgfdw_reject_incomplete_xact_state_change(ConnCacheEntry *entry)
1502 : : {
1503 : : ForeignServer *server;
1504 : :
1505 : : /* nothing to do for inactive entries and entries of sane state */
3324 tgl@sss.pgh.pa.us 1506 [ + + + + ]: 3079 : if (entry->conn == NULL || !entry->changing_xact_state)
3368 rhaas@postgresql.org 1507 : 3077 : return;
1508 : :
1509 : : /* find server name to be shown in the message below */
2050 fujii@postgresql.org 1510 : 2 : server = GetForeignServer(entry->serverid);
1511 : :
3368 rhaas@postgresql.org 1512 [ + - ]: 2 : ereport(ERROR,
1513 : : (errcode(ERRCODE_CONNECTION_EXCEPTION),
1514 : : errmsg("connection to server \"%s\" cannot be used due to abort cleanup failure",
1515 : : server->servername)));
1516 : : }
1517 : :
1518 : : /*
1519 : : * Reset state to show we're out of a (sub)transaction.
1520 : : */
1521 : : static void
1645 efujita@postgresql.o 1522 : 1407 : pgfdw_reset_xact_state(ConnCacheEntry *entry, bool toplevel)
1523 : : {
1524 [ + + ]: 1407 : if (toplevel)
1525 : : {
1526 : : /* Reset state to show we're out of a transaction */
1527 : 1386 : entry->xact_depth = 0;
1528 : :
1529 : : /* Reset xact r/o state */
144 1530 : 1386 : entry->xact_read_only = false;
1531 : :
1532 : : /*
1533 : : * If the connection isn't in a good idle state, it is marked as
1534 : : * invalid or keep_connections option of its server is disabled, then
1535 : : * discard it to recover. Next GetConnection will open a new
1536 : : * connection.
1537 : : */
1645 1538 [ + + + - ]: 2768 : if (PQstatus(entry->conn) != CONNECTION_OK ||
1539 : 1382 : PQtransactionStatus(entry->conn) != PQTRANS_IDLE ||
1540 [ + - ]: 1382 : entry->changing_xact_state ||
1541 [ + + ]: 1382 : entry->invalidated ||
1542 [ + + ]: 1380 : !entry->keep_connections)
1543 : : {
1544 [ - + ]: 7 : elog(DEBUG3, "discarding connection %p", entry->conn);
1545 : 7 : disconnect_pg_server(entry);
1546 : : }
1547 : : }
1548 : : else
1549 : : {
1550 : : /* Reset state to show we're out of a subtransaction */
1551 : 21 : entry->xact_depth--;
1552 : :
1553 : : /* If in read_only_level, reset xact r/o state */
144 1554 [ + + ]: 21 : if (entry->xact_depth + 1 == read_only_level)
1555 : 4 : entry->xact_read_only = false;
1556 : : }
1645 1557 : 1407 : }
1558 : :
1559 : : /*
1560 : : * Cancel the currently-in-progress query (whose query text we do not have)
1561 : : * and ignore the result. Returns true if we successfully cancel the query
1562 : : * and discard any pending result, and false if not.
1563 : : *
1564 : : * It's not a huge problem if we throw an ERROR here, but if we get into error
1565 : : * recursion trouble, we'll end up slamming the connection shut, which will
1566 : : * necessitate failing the entire toplevel transaction even if subtransactions
1567 : : * were used. Try to use WARNING where we can.
1568 : : *
1569 : : * XXX: if the query was one sent by fetch_more_data_begin(), we could get the
1570 : : * query text from the pendingAreq saved in the per-connection state, then
1571 : : * report the query using it.
1572 : : */
1573 : : static bool
3368 rhaas@postgresql.org 1574 : 1 : pgfdw_cancel_query(PGconn *conn)
1575 : : {
612 tgl@sss.pgh.pa.us 1576 : 1 : TimestampTz now = GetCurrentTimestamp();
1577 : : TimestampTz endtime;
1578 : : TimestampTz retrycanceltime;
1579 : :
1580 : : /*
1581 : : * If it takes too long to cancel the query and discard the result, assume
1582 : : * the connection is dead.
1583 : : */
1584 : 1 : endtime = TimestampTzPlusMilliseconds(now, CONNECTION_CLEANUP_TIMEOUT);
1585 : :
1586 : : /*
1587 : : * Also, lose patience and re-issue the cancel request after a little bit.
1588 : : * (This serves to close some race conditions.)
1589 : : */
1590 : 1 : retrycanceltime = TimestampTzPlusMilliseconds(now, RETRY_CANCEL_TIMEOUT);
1591 : :
882 alvherre@alvh.no-ip. 1592 [ - + ]: 1 : if (!pgfdw_cancel_query_begin(conn, endtime))
1239 efujita@postgresql.o 1593 :UBC 0 : return false;
612 tgl@sss.pgh.pa.us 1594 :CBC 1 : return pgfdw_cancel_query_end(conn, endtime, retrycanceltime, false);
1595 : : }
1596 : :
1597 : : /*
1598 : : * Submit a cancel request to the given connection, waiting only until
1599 : : * the given time.
1600 : : *
1601 : : * We sleep interruptibly until we receive confirmation that the cancel
1602 : : * request has been accepted, and if it is, return true; if the timeout
1603 : : * lapses without that, or the request fails for whatever reason, return
1604 : : * false.
1605 : : */
1606 : : static bool
882 alvherre@alvh.no-ip. 1607 : 1 : pgfdw_cancel_query_begin(PGconn *conn, TimestampTz endtime)
1608 : : {
874 1609 : 1 : const char *errormsg = libpqsrv_cancel(conn, endtime);
1610 : :
882 1611 [ - + ]: 1 : if (errormsg != NULL)
882 alvherre@alvh.no-ip. 1612 [ # # ]:UBC 0 : ereport(WARNING,
1613 : : errcode(ERRCODE_CONNECTION_FAILURE),
1614 : : errmsg("could not send cancel request: %s", errormsg));
1615 : :
882 alvherre@alvh.no-ip. 1616 :CBC 1 : return errormsg == NULL;
1617 : : }
1618 : :
1619 : : static bool
612 tgl@sss.pgh.pa.us 1620 : 1 : pgfdw_cancel_query_end(PGconn *conn, TimestampTz endtime,
1621 : : TimestampTz retrycanceltime, bool consume_input)
1622 : : {
1623 : : PGresult *result;
1624 : : bool timed_out;
1625 : :
1626 : : /*
1627 : : * If requested, consume whatever data is available from the socket. (Note
1628 : : * that if all data is available, this allows pgfdw_get_cleanup_result to
1629 : : * call PQgetResult without forcing the overhead of WaitLatchOrSocket,
1630 : : * which would be large compared to the overhead of PQconsumeInput.)
1631 : : */
1239 efujita@postgresql.o 1632 [ - + - - ]: 1 : if (consume_input && !PQconsumeInput(conn))
1633 : : {
1239 efujita@postgresql.o 1634 [ # # ]:UBC 0 : ereport(WARNING,
1635 : : (errcode(ERRCODE_CONNECTION_FAILURE),
1636 : : errmsg("could not get result of cancel request: %s",
1637 : : pchomp(PQerrorMessage(conn)))));
1638 : 0 : return false;
1639 : : }
1640 : :
1641 : : /* Get and discard the result of the query. */
612 tgl@sss.pgh.pa.us 1642 [ - + ]:CBC 1 : if (pgfdw_get_cleanup_result(conn, endtime, retrycanceltime,
1643 : : &result, &timed_out))
1644 : : {
1723 fujii@postgresql.org 1645 [ # # ]:UBC 0 : if (timed_out)
1646 [ # # ]: 0 : ereport(WARNING,
1647 : : (errmsg("could not get result of cancel request due to timeout")));
1648 : : else
1649 [ # # ]: 0 : ereport(WARNING,
1650 : : (errcode(ERRCODE_CONNECTION_FAILURE),
1651 : : errmsg("could not get result of cancel request: %s",
1652 : : pchomp(PQerrorMessage(conn)))));
1653 : :
3368 rhaas@postgresql.org 1654 : 0 : return false;
1655 : : }
3368 rhaas@postgresql.org 1656 :CBC 1 : PQclear(result);
1657 : :
1658 : 1 : return true;
1659 : : }
1660 : :
1661 : : /*
1662 : : * Submit a query during (sub)abort cleanup and wait up to 30 seconds for the
1663 : : * result. If the query is executed without error, the return value is true.
1664 : : * If the query is executed successfully but returns an error, the return
1665 : : * value is true if and only if ignore_errors is set. If the query can't be
1666 : : * sent or times out, the return value is false.
1667 : : *
1668 : : * It's not a huge problem if we throw an ERROR here, but if we get into error
1669 : : * recursion trouble, we'll end up slamming the connection shut, which will
1670 : : * necessitate failing the entire toplevel transaction even if subtransactions
1671 : : * were used. Try to use WARNING where we can.
1672 : : */
1673 : : static bool
1674 : 88 : pgfdw_exec_cleanup_query(PGconn *conn, const char *query, bool ignore_errors)
1675 : : {
1676 : : TimestampTz endtime;
1677 : :
1678 : : /*
1679 : : * If it takes too long to execute a cleanup query, assume the connection
1680 : : * is dead. It's fairly likely that this is why we aborted in the first
1681 : : * place (e.g. statement timeout, user cancel), so the timeout shouldn't
1682 : : * be too long.
1683 : : */
1239 efujita@postgresql.o 1684 : 88 : endtime = TimestampTzPlusMilliseconds(GetCurrentTimestamp(),
1685 : : CONNECTION_CLEANUP_TIMEOUT);
1686 : :
1687 [ - + ]: 88 : if (!pgfdw_exec_cleanup_query_begin(conn, query))
1239 efujita@postgresql.o 1688 :UBC 0 : return false;
1239 efujita@postgresql.o 1689 :CBC 88 : return pgfdw_exec_cleanup_query_end(conn, query, endtime,
1690 : : false, ignore_errors);
1691 : : }
1692 : :
1693 : : static bool
1694 : 100 : pgfdw_exec_cleanup_query_begin(PGconn *conn, const char *query)
1695 : : {
875 1696 [ - + ]: 100 : Assert(query != NULL);
1697 : :
1698 : : /*
1699 : : * Submit a query. Since we don't use non-blocking mode, this also can
1700 : : * block. But its risk is relatively small, so we ignore that for now.
1701 : : */
3368 rhaas@postgresql.org 1702 [ - + ]: 100 : if (!PQsendQuery(conn, query))
1703 : : {
394 tgl@sss.pgh.pa.us 1704 :UBC 0 : pgfdw_report(WARNING, NULL, conn, query);
3368 rhaas@postgresql.org 1705 : 0 : return false;
1706 : : }
1707 : :
1239 efujita@postgresql.o 1708 :CBC 100 : return true;
1709 : : }
1710 : :
1711 : : static bool
1712 : 100 : pgfdw_exec_cleanup_query_end(PGconn *conn, const char *query,
1713 : : TimestampTz endtime, bool consume_input,
1714 : : bool ignore_errors)
1715 : : {
1716 : : PGresult *result;
1717 : : bool timed_out;
1718 : :
875 1719 [ - + ]: 100 : Assert(query != NULL);
1720 : :
1721 : : /*
1722 : : * If requested, consume whatever data is available from the socket. (Note
1723 : : * that if all data is available, this allows pgfdw_get_cleanup_result to
1724 : : * call PQgetResult without forcing the overhead of WaitLatchOrSocket,
1725 : : * which would be large compared to the overhead of PQconsumeInput.)
1726 : : */
1239 1727 [ + + - + ]: 100 : if (consume_input && !PQconsumeInput(conn))
1728 : : {
394 tgl@sss.pgh.pa.us 1729 :UBC 0 : pgfdw_report(WARNING, NULL, conn, query);
1239 efujita@postgresql.o 1730 : 0 : return false;
1731 : : }
1732 : :
1733 : : /* Get the result of the query. */
612 tgl@sss.pgh.pa.us 1734 [ + + ]:CBC 100 : if (pgfdw_get_cleanup_result(conn, endtime, endtime, &result, &timed_out))
1735 : : {
1723 fujii@postgresql.org 1736 [ - + ]: 3 : if (timed_out)
1723 fujii@postgresql.org 1737 [ # # ]:UBC 0 : ereport(WARNING,
1738 : : (errmsg("could not get query result due to timeout"),
1739 : : errcontext("remote SQL command: %s", query)));
1740 : : else
394 tgl@sss.pgh.pa.us 1741 :CBC 3 : pgfdw_report(WARNING, NULL, conn, query);
1742 : :
3368 rhaas@postgresql.org 1743 : 3 : return false;
1744 : : }
1745 : :
1746 : : /* Issue a warning if not successful. */
1747 [ - + ]: 97 : if (PQresultStatus(result) != PGRES_COMMAND_OK)
1748 : : {
394 tgl@sss.pgh.pa.us 1749 :UBC 0 : pgfdw_report(WARNING, result, conn, query);
3368 rhaas@postgresql.org 1750 : 0 : return ignore_errors;
1751 : : }
3360 tgl@sss.pgh.pa.us 1752 :CBC 97 : PQclear(result);
1753 : :
3368 rhaas@postgresql.org 1754 : 97 : return true;
1755 : : }
1756 : :
1757 : : /*
1758 : : * Get, during abort cleanup, the result of a query that is in progress.
1759 : : * This might be a query that is being interrupted by a cancel request or by
1760 : : * transaction abort, or it might be a query that was initiated as part of
1761 : : * transaction abort to get the remote side back to the appropriate state.
1762 : : *
1763 : : * endtime is the time at which we should give up and assume the remote side
1764 : : * is dead. retrycanceltime is the time at which we should issue a fresh
1765 : : * cancel request (pass the same value as endtime if this is not wanted).
1766 : : *
1767 : : * Returns true if the timeout expired or connection trouble occurred,
1768 : : * false otherwise. Sets *result except in case of a true result.
1769 : : * Sets *timed_out to true only when the timeout expired.
1770 : : */
1771 : : static bool
612 tgl@sss.pgh.pa.us 1772 : 101 : pgfdw_get_cleanup_result(PGconn *conn, TimestampTz endtime,
1773 : : TimestampTz retrycanceltime,
1774 : : PGresult **result,
1775 : : bool *timed_out)
1776 : : {
398 1777 : 101 : bool failed = false;
1778 : 101 : PGresult *last_res = NULL;
1779 : 101 : int canceldelta = RETRY_CANCEL_TIMEOUT * 2;
1780 : :
612 1781 : 101 : *result = NULL;
1723 fujii@postgresql.org 1782 : 101 : *timed_out = false;
1783 : : for (;;)
398 tgl@sss.pgh.pa.us 1784 : 113 : {
1785 : : PGresult *res;
1786 : :
1787 [ + + ]: 305 : while (PQisBusy(conn))
1788 : : {
1789 : : int wc;
1790 : 94 : TimestampTz now = GetCurrentTimestamp();
1791 : : long cur_timeout;
1792 : :
1793 : : /* If timeout has expired, give up. */
1794 [ - + ]: 94 : if (now >= endtime)
1795 : : {
398 tgl@sss.pgh.pa.us 1796 :UBC 0 : *timed_out = true;
1797 : 0 : failed = true;
1798 : 0 : goto exit;
1799 : : }
1800 : :
1801 : : /* If we need to re-issue the cancel request, do that. */
398 tgl@sss.pgh.pa.us 1802 [ - + ]:CBC 94 : if (now >= retrycanceltime)
1803 : : {
1804 : : /* We ignore failure to issue the repeated request. */
398 tgl@sss.pgh.pa.us 1805 :UBC 0 : (void) libpqsrv_cancel(conn, endtime);
1806 : :
1807 : : /* Recompute "now" in case that took measurable time. */
1808 : 0 : now = GetCurrentTimestamp();
1809 : :
1810 : : /* Adjust re-cancel timeout in increasing steps. */
1811 : 0 : retrycanceltime = TimestampTzPlusMilliseconds(now,
1812 : : canceldelta);
1813 : 0 : canceldelta += canceldelta;
1814 : : }
1815 : :
1816 : : /* If timeout has expired, give up, else get sleep time. */
398 tgl@sss.pgh.pa.us 1817 :CBC 94 : cur_timeout = TimestampDifferenceMilliseconds(now,
1818 : : Min(endtime,
1819 : : retrycanceltime));
1820 [ - + ]: 94 : if (cur_timeout <= 0)
1821 : : {
398 tgl@sss.pgh.pa.us 1822 :UBC 0 : *timed_out = true;
1823 : 0 : failed = true;
1824 : 0 : goto exit;
1825 : : }
1826 : :
1827 : : /* first time, allocate or get the custom wait event */
398 tgl@sss.pgh.pa.us 1828 [ + + ]:CBC 94 : if (pgfdw_we_cleanup_result == 0)
1829 : 2 : pgfdw_we_cleanup_result = WaitEventExtensionNew("PostgresFdwCleanupResult");
1830 : :
1831 : : /* Sleep until there's something to do */
1832 : 94 : wc = WaitLatchOrSocket(MyLatch,
1833 : : WL_LATCH_SET | WL_SOCKET_READABLE |
1834 : : WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
1835 : : PQsocket(conn),
1836 : : cur_timeout, pgfdw_we_cleanup_result);
1837 : 94 : ResetLatch(MyLatch);
1838 : :
1839 [ - + ]: 94 : CHECK_FOR_INTERRUPTS();
1840 : :
1841 : : /* Data available in socket? */
1842 [ + - ]: 94 : if (wc & WL_SOCKET_READABLE)
1843 : : {
1844 [ + + ]: 94 : if (!PQconsumeInput(conn))
1845 : : {
1846 : : /* connection trouble */
1847 : 3 : failed = true;
1848 : 3 : goto exit;
1849 : : }
1850 : : }
1851 : : }
1852 : :
1853 : 211 : res = PQgetResult(conn);
1854 [ + + ]: 211 : if (res == NULL)
1855 : 98 : break; /* query is complete */
1856 : :
1857 : 113 : PQclear(last_res);
1858 : 113 : last_res = res;
1859 : : }
1860 : 101 : exit:
1723 fujii@postgresql.org 1861 [ + + ]: 101 : if (failed)
3360 tgl@sss.pgh.pa.us 1862 : 3 : PQclear(last_res);
1863 : : else
1864 : 98 : *result = last_res;
1723 fujii@postgresql.org 1865 : 101 : return failed;
1866 : : }
1867 : :
1868 : : /*
1869 : : * Abort remote transaction or subtransaction.
1870 : : *
1871 : : * "toplevel" should be set to true if toplevel (main) transaction is
1872 : : * rollbacked, false otherwise.
1873 : : *
1874 : : * Set entry->changing_xact_state to false on success, true on failure.
1875 : : */
1876 : : static void
1616 efujita@postgresql.o 1877 : 66 : pgfdw_abort_cleanup(ConnCacheEntry *entry, bool toplevel)
1878 : : {
1879 : : char sql[100];
1880 : :
1881 : : /*
1882 : : * Don't try to clean up the connection if we're already in error
1883 : : * recursion trouble.
1884 : : */
1800 fujii@postgresql.org 1885 [ - + ]: 66 : if (in_error_recursion_trouble())
1800 fujii@postgresql.org 1886 :UBC 0 : entry->changing_xact_state = true;
1887 : :
1888 : : /*
1889 : : * If connection is already unsalvageable, don't touch it further.
1890 : : */
1800 fujii@postgresql.org 1891 [ + + ]:CBC 66 : if (entry->changing_xact_state)
1892 : 6 : return;
1893 : :
1894 : : /*
1895 : : * Mark this connection as in the process of changing transaction state.
1896 : : */
1897 : 63 : entry->changing_xact_state = true;
1898 : :
1899 : : /* Assume we might have lost track of prepared statements */
1900 : 63 : entry->have_error = true;
1901 : :
1902 : : /*
1903 : : * If a command has been submitted to the remote server by using an
1904 : : * asynchronous execution function, the command might not have yet
1905 : : * completed. Check to see if a command is still being processed by the
1906 : : * remote server, and if so, request cancellation of the command.
1907 : : */
1908 [ + + ]: 63 : if (PQtransactionStatus(entry->conn) == PQTRANS_ACTIVE &&
1909 [ - + ]: 1 : !pgfdw_cancel_query(entry->conn))
1800 fujii@postgresql.org 1910 :UBC 0 : return; /* Unable to cancel running query */
1911 : :
1239 efujita@postgresql.o 1912 [ + + ]:CBC 63 : CONSTRUCT_ABORT_COMMAND(sql, entry, toplevel);
1800 fujii@postgresql.org 1913 [ + + ]: 63 : if (!pgfdw_exec_cleanup_query(entry->conn, sql, false))
1616 efujita@postgresql.o 1914 : 3 : return; /* Unable to abort remote (sub)transaction */
1915 : :
1800 fujii@postgresql.org 1916 [ + + ]: 60 : if (toplevel)
1917 : : {
1918 [ + + + - ]: 52 : if (entry->have_prep_stmt && entry->have_error &&
1919 [ - + ]: 25 : !pgfdw_exec_cleanup_query(entry->conn,
1920 : : "DEALLOCATE ALL",
1921 : : true))
1800 fujii@postgresql.org 1922 :UBC 0 : return; /* Trouble clearing prepared statements */
1923 : :
1800 fujii@postgresql.org 1924 :CBC 52 : entry->have_prep_stmt = false;
1925 : 52 : entry->have_error = false;
1926 : : }
1927 : :
1928 : : /*
1929 : : * If pendingAreq of the per-connection state is not NULL, it means that
1930 : : * an asynchronous fetch begun by fetch_more_data_begin() was not done
1931 : : * successfully and thus the per-connection state was not reset in
1932 : : * fetch_more_data(); in that case reset the per-connection state here.
1933 : : */
1679 efujita@postgresql.o 1934 [ - + ]: 60 : if (entry->state.pendingAreq)
1679 efujita@postgresql.o 1935 :UBC 0 : memset(&entry->state, 0, sizeof(entry->state));
1936 : :
1937 : : /* Disarm changing_xact_state if it all worked */
1800 fujii@postgresql.org 1938 :CBC 60 : entry->changing_xact_state = false;
1939 : : }
1940 : :
1941 : : /*
1942 : : * Like pgfdw_abort_cleanup, submit an abort command or cancel request, but
1943 : : * don't wait for the result.
1944 : : *
1945 : : * Returns true if the abort command or cancel request is successfully issued,
1946 : : * false otherwise. If the abort command is successfully issued, the given
1947 : : * connection cache entry is appended to *pending_entries. Otherwise, if the
1948 : : * cancel request is successfully issued, it is appended to *cancel_requested.
1949 : : */
1950 : : static bool
1239 efujita@postgresql.o 1951 : 8 : pgfdw_abort_cleanup_begin(ConnCacheEntry *entry, bool toplevel,
1952 : : List **pending_entries, List **cancel_requested)
1953 : : {
1954 : : /*
1955 : : * Don't try to clean up the connection if we're already in error
1956 : : * recursion trouble.
1957 : : */
1958 [ - + ]: 8 : if (in_error_recursion_trouble())
1239 efujita@postgresql.o 1959 :UBC 0 : entry->changing_xact_state = true;
1960 : :
1961 : : /*
1962 : : * If connection is already unsalvageable, don't touch it further.
1963 : : */
1239 efujita@postgresql.o 1964 [ - + ]:CBC 8 : if (entry->changing_xact_state)
1239 efujita@postgresql.o 1965 :UBC 0 : return false;
1966 : :
1967 : : /*
1968 : : * Mark this connection as in the process of changing transaction state.
1969 : : */
1239 efujita@postgresql.o 1970 :CBC 8 : entry->changing_xact_state = true;
1971 : :
1972 : : /* Assume we might have lost track of prepared statements */
1973 : 8 : entry->have_error = true;
1974 : :
1975 : : /*
1976 : : * If a command has been submitted to the remote server by using an
1977 : : * asynchronous execution function, the command might not have yet
1978 : : * completed. Check to see if a command is still being processed by the
1979 : : * remote server, and if so, request cancellation of the command.
1980 : : */
1981 [ - + ]: 8 : if (PQtransactionStatus(entry->conn) == PQTRANS_ACTIVE)
1982 : : {
1983 : : TimestampTz endtime;
1984 : :
882 alvherre@alvh.no-ip. 1985 :UBC 0 : endtime = TimestampTzPlusMilliseconds(GetCurrentTimestamp(),
1986 : : CONNECTION_CLEANUP_TIMEOUT);
1987 [ # # ]: 0 : if (!pgfdw_cancel_query_begin(entry->conn, endtime))
1239 efujita@postgresql.o 1988 : 0 : return false; /* Unable to cancel running query */
1989 : 0 : *cancel_requested = lappend(*cancel_requested, entry);
1990 : : }
1991 : : else
1992 : : {
1993 : : char sql[100];
1994 : :
1239 efujita@postgresql.o 1995 [ + + ]:CBC 8 : CONSTRUCT_ABORT_COMMAND(sql, entry, toplevel);
1996 [ - + ]: 8 : if (!pgfdw_exec_cleanup_query_begin(entry->conn, sql))
1239 efujita@postgresql.o 1997 :UBC 0 : return false; /* Unable to abort remote transaction */
1239 efujita@postgresql.o 1998 :CBC 8 : *pending_entries = lappend(*pending_entries, entry);
1999 : : }
2000 : :
2001 : 8 : return true;
2002 : : }
2003 : :
2004 : : /*
2005 : : * Finish pre-commit cleanup of connections on each of which we've sent a
2006 : : * COMMIT command to the remote server.
2007 : : */
2008 : : static void
1645 2009 : 13 : pgfdw_finish_pre_commit_cleanup(List *pending_entries)
2010 : : {
2011 : : ConnCacheEntry *entry;
2012 : 13 : List *pending_deallocs = NIL;
2013 : : ListCell *lc;
2014 : :
2015 [ - + ]: 13 : Assert(pending_entries);
2016 : :
2017 : : /*
2018 : : * Get the result of the COMMIT command for each of the pending entries
2019 : : */
2020 [ + - + + : 29 : foreach(lc, pending_entries)
+ + ]
2021 : : {
2022 : 16 : entry = (ConnCacheEntry *) lfirst(lc);
2023 : :
2024 [ - + ]: 16 : Assert(entry->changing_xact_state);
2025 : :
2026 : : /*
2027 : : * We might already have received the result on the socket, so pass
2028 : : * consume_input=true to try to consume it first
2029 : : */
2030 : 16 : do_sql_command_end(entry->conn, "COMMIT TRANSACTION", true);
2031 : 16 : entry->changing_xact_state = false;
2032 : :
2033 : : /* Do a DEALLOCATE ALL in parallel if needed */
2034 [ + + + + ]: 16 : if (entry->have_prep_stmt && entry->have_error)
2035 : : {
2036 : : /* Ignore errors (see notes in pgfdw_xact_callback) */
2037 [ + - ]: 2 : if (PQsendQuery(entry->conn, "DEALLOCATE ALL"))
2038 : : {
2039 : 2 : pending_deallocs = lappend(pending_deallocs, entry);
2040 : 2 : continue;
2041 : : }
2042 : : }
2043 : 14 : entry->have_prep_stmt = false;
2044 : 14 : entry->have_error = false;
2045 : :
2046 : 14 : pgfdw_reset_xact_state(entry, true);
2047 : : }
2048 : :
2049 : : /* No further work if no pending entries */
2050 [ + + ]: 13 : if (!pending_deallocs)
2051 : 12 : return;
2052 : :
2053 : : /*
2054 : : * Get the result of the DEALLOCATE command for each of the pending
2055 : : * entries
2056 : : */
2057 [ + - + + : 3 : foreach(lc, pending_deallocs)
+ + ]
2058 : : {
2059 : : PGresult *res;
2060 : :
2061 : 2 : entry = (ConnCacheEntry *) lfirst(lc);
2062 : :
2063 : : /* Ignore errors (see notes in pgfdw_xact_callback) */
2064 [ + + ]: 4 : while ((res = PQgetResult(entry->conn)) != NULL)
2065 : : {
2066 : 2 : PQclear(res);
2067 : : /* Stop if the connection is lost (else we'll loop infinitely) */
2068 [ - + ]: 2 : if (PQstatus(entry->conn) == CONNECTION_BAD)
1645 efujita@postgresql.o 2069 :UBC 0 : break;
2070 : : }
1645 efujita@postgresql.o 2071 :CBC 2 : entry->have_prep_stmt = false;
2072 : 2 : entry->have_error = false;
2073 : :
2074 : 2 : pgfdw_reset_xact_state(entry, true);
2075 : : }
2076 : : }
2077 : :
2078 : : /*
2079 : : * Finish pre-subcommit cleanup of connections on each of which we've sent a
2080 : : * RELEASE command to the remote server.
2081 : : */
2082 : : static void
2083 : 1 : pgfdw_finish_pre_subcommit_cleanup(List *pending_entries, int curlevel)
2084 : : {
2085 : : ConnCacheEntry *entry;
2086 : : char sql[100];
2087 : : ListCell *lc;
2088 : :
2089 [ - + ]: 1 : Assert(pending_entries);
2090 : :
2091 : : /*
2092 : : * Get the result of the RELEASE command for each of the pending entries
2093 : : */
2094 : 1 : snprintf(sql, sizeof(sql), "RELEASE SAVEPOINT s%d", curlevel);
2095 [ + - + + : 3 : foreach(lc, pending_entries)
+ + ]
2096 : : {
2097 : 2 : entry = (ConnCacheEntry *) lfirst(lc);
2098 : :
2099 [ - + ]: 2 : Assert(entry->changing_xact_state);
2100 : :
2101 : : /*
2102 : : * We might already have received the result on the socket, so pass
2103 : : * consume_input=true to try to consume it first
2104 : : */
2105 : 2 : do_sql_command_end(entry->conn, sql, true);
2106 : 2 : entry->changing_xact_state = false;
2107 : :
2108 : 2 : pgfdw_reset_xact_state(entry, false);
2109 : : }
2110 : 1 : }
2111 : :
2112 : : /*
2113 : : * Finish abort cleanup of connections on each of which we've sent an abort
2114 : : * command or cancel request to the remote server.
2115 : : */
2116 : : static void
1239 2117 : 4 : pgfdw_finish_abort_cleanup(List *pending_entries, List *cancel_requested,
2118 : : bool toplevel)
2119 : : {
2120 : 4 : List *pending_deallocs = NIL;
2121 : : ListCell *lc;
2122 : :
2123 : : /*
2124 : : * For each of the pending cancel requests (if any), get and discard the
2125 : : * result of the query, and submit an abort command to the remote server.
2126 : : */
2127 [ - + ]: 4 : if (cancel_requested)
2128 : : {
1239 efujita@postgresql.o 2129 [ # # # # :UBC 0 : foreach(lc, cancel_requested)
# # ]
2130 : : {
2131 : 0 : ConnCacheEntry *entry = (ConnCacheEntry *) lfirst(lc);
612 tgl@sss.pgh.pa.us 2132 : 0 : TimestampTz now = GetCurrentTimestamp();
2133 : : TimestampTz endtime;
2134 : : TimestampTz retrycanceltime;
2135 : : char sql[100];
2136 : :
1239 efujita@postgresql.o 2137 [ # # ]: 0 : Assert(entry->changing_xact_state);
2138 : :
2139 : : /*
2140 : : * Set end time. You might think we should do this before issuing
2141 : : * cancel request like in normal mode, but that is problematic,
2142 : : * because if, for example, it took longer than 30 seconds to
2143 : : * process the first few entries in the cancel_requested list, it
2144 : : * would cause a timeout error when processing each of the
2145 : : * remaining entries in the list, leading to slamming that entry's
2146 : : * connection shut.
2147 : : */
612 tgl@sss.pgh.pa.us 2148 : 0 : endtime = TimestampTzPlusMilliseconds(now,
2149 : : CONNECTION_CLEANUP_TIMEOUT);
2150 : 0 : retrycanceltime = TimestampTzPlusMilliseconds(now,
2151 : : RETRY_CANCEL_TIMEOUT);
2152 : :
2153 [ # # ]: 0 : if (!pgfdw_cancel_query_end(entry->conn, endtime,
2154 : : retrycanceltime, true))
2155 : : {
2156 : : /* Unable to cancel running query */
1239 efujita@postgresql.o 2157 : 0 : pgfdw_reset_xact_state(entry, toplevel);
2158 : 0 : continue;
2159 : : }
2160 : :
2161 : : /* Send an abort command in parallel if needed */
2162 [ # # ]: 0 : CONSTRUCT_ABORT_COMMAND(sql, entry, toplevel);
2163 [ # # ]: 0 : if (!pgfdw_exec_cleanup_query_begin(entry->conn, sql))
2164 : : {
2165 : : /* Unable to abort remote (sub)transaction */
2166 : 0 : pgfdw_reset_xact_state(entry, toplevel);
2167 : : }
2168 : : else
2169 : 0 : pending_entries = lappend(pending_entries, entry);
2170 : : }
2171 : : }
2172 : :
2173 : : /* No further work if no pending entries */
1239 efujita@postgresql.o 2174 [ - + ]:CBC 4 : if (!pending_entries)
1239 efujita@postgresql.o 2175 :UBC 0 : return;
2176 : :
2177 : : /*
2178 : : * Get the result of the abort command for each of the pending entries
2179 : : */
1239 efujita@postgresql.o 2180 [ + - + + :CBC 12 : foreach(lc, pending_entries)
+ + ]
2181 : : {
2182 : 8 : ConnCacheEntry *entry = (ConnCacheEntry *) lfirst(lc);
2183 : : TimestampTz endtime;
2184 : : char sql[100];
2185 : :
2186 [ - + ]: 8 : Assert(entry->changing_xact_state);
2187 : :
2188 : : /*
2189 : : * Set end time. We do this now, not before issuing the command like
2190 : : * in normal mode, for the same reason as for the cancel_requested
2191 : : * entries.
2192 : : */
2193 : 8 : endtime = TimestampTzPlusMilliseconds(GetCurrentTimestamp(),
2194 : : CONNECTION_CLEANUP_TIMEOUT);
2195 : :
2196 [ + + ]: 8 : CONSTRUCT_ABORT_COMMAND(sql, entry, toplevel);
2197 [ - + ]: 8 : if (!pgfdw_exec_cleanup_query_end(entry->conn, sql, endtime,
2198 : : true, false))
2199 : : {
2200 : : /* Unable to abort remote (sub)transaction */
1239 efujita@postgresql.o 2201 :UBC 0 : pgfdw_reset_xact_state(entry, toplevel);
1239 efujita@postgresql.o 2202 :CBC 4 : continue;
2203 : : }
2204 : :
2205 [ + + ]: 8 : if (toplevel)
2206 : : {
2207 : : /* Do a DEALLOCATE ALL in parallel if needed */
2208 [ + - + - ]: 4 : if (entry->have_prep_stmt && entry->have_error)
2209 : : {
2210 [ - + ]: 4 : if (!pgfdw_exec_cleanup_query_begin(entry->conn,
2211 : : "DEALLOCATE ALL"))
2212 : : {
2213 : : /* Trouble clearing prepared statements */
1239 efujita@postgresql.o 2214 :UBC 0 : pgfdw_reset_xact_state(entry, toplevel);
2215 : : }
2216 : : else
1239 efujita@postgresql.o 2217 :CBC 4 : pending_deallocs = lappend(pending_deallocs, entry);
2218 : 4 : continue;
2219 : : }
1239 efujita@postgresql.o 2220 :UBC 0 : entry->have_prep_stmt = false;
2221 : 0 : entry->have_error = false;
2222 : : }
2223 : :
2224 : : /* Reset the per-connection state if needed */
1239 efujita@postgresql.o 2225 [ - + ]:CBC 4 : if (entry->state.pendingAreq)
1239 efujita@postgresql.o 2226 :UBC 0 : memset(&entry->state, 0, sizeof(entry->state));
2227 : :
2228 : : /* We're done with this entry; unset the changing_xact_state flag */
1239 efujita@postgresql.o 2229 :CBC 4 : entry->changing_xact_state = false;
2230 : 4 : pgfdw_reset_xact_state(entry, toplevel);
2231 : : }
2232 : :
2233 : : /* No further work if no pending entries */
2234 [ + + ]: 4 : if (!pending_deallocs)
2235 : 2 : return;
2236 [ - + ]: 2 : Assert(toplevel);
2237 : :
2238 : : /*
2239 : : * Get the result of the DEALLOCATE command for each of the pending
2240 : : * entries
2241 : : */
2242 [ + - + + : 6 : foreach(lc, pending_deallocs)
+ + ]
2243 : : {
2244 : 4 : ConnCacheEntry *entry = (ConnCacheEntry *) lfirst(lc);
2245 : : TimestampTz endtime;
2246 : :
2247 [ - + ]: 4 : Assert(entry->changing_xact_state);
2248 [ - + ]: 4 : Assert(entry->have_prep_stmt);
2249 [ - + ]: 4 : Assert(entry->have_error);
2250 : :
2251 : : /*
2252 : : * Set end time. We do this now, not before issuing the command like
2253 : : * in normal mode, for the same reason as for the cancel_requested
2254 : : * entries.
2255 : : */
2256 : 4 : endtime = TimestampTzPlusMilliseconds(GetCurrentTimestamp(),
2257 : : CONNECTION_CLEANUP_TIMEOUT);
2258 : :
2259 [ - + ]: 4 : if (!pgfdw_exec_cleanup_query_end(entry->conn, "DEALLOCATE ALL",
2260 : : endtime, true, true))
2261 : : {
2262 : : /* Trouble clearing prepared statements */
1239 efujita@postgresql.o 2263 :UBC 0 : pgfdw_reset_xact_state(entry, toplevel);
2264 : 0 : continue;
2265 : : }
1239 efujita@postgresql.o 2266 :CBC 4 : entry->have_prep_stmt = false;
2267 : 4 : entry->have_error = false;
2268 : :
2269 : : /* Reset the per-connection state if needed */
2270 [ - + ]: 4 : if (entry->state.pendingAreq)
1239 efujita@postgresql.o 2271 :UBC 0 : memset(&entry->state, 0, sizeof(entry->state));
2272 : :
2273 : : /* We're done with this entry; unset the changing_xact_state flag */
1239 efujita@postgresql.o 2274 :CBC 4 : entry->changing_xact_state = false;
2275 : 4 : pgfdw_reset_xact_state(entry, toplevel);
2276 : : }
2277 : : }
2278 : :
2279 : : /* Number of output arguments (columns) for various API versions */
2280 : : #define POSTGRES_FDW_GET_CONNECTIONS_COLS_V1_1 2
2281 : : #define POSTGRES_FDW_GET_CONNECTIONS_COLS_V1_2 6
2282 : : #define POSTGRES_FDW_GET_CONNECTIONS_COLS 6 /* maximum of above */
2283 : :
2284 : : /*
2285 : : * Internal function used by postgres_fdw_get_connections variants.
2286 : : *
2287 : : * For API version 1.1, this function takes no input parameter and
2288 : : * returns a set of records with the following values:
2289 : : *
2290 : : * - server_name - server name of active connection. In case the foreign server
2291 : : * is dropped but still the connection is active, then the server name will
2292 : : * be NULL in output.
2293 : : * - valid - true/false representing whether the connection is valid or not.
2294 : : * Note that connections can become invalid in pgfdw_inval_callback.
2295 : : *
2296 : : * For API version 1.2 and later, this function takes an input parameter
2297 : : * to check a connection status and returns the following
2298 : : * additional values along with the four values from version 1.1:
2299 : : *
2300 : : * - user_name - the local user name of the active connection. In case the
2301 : : * user mapping is dropped but the connection is still active, then the
2302 : : * user name will be NULL in the output.
2303 : : * - used_in_xact - true if the connection is used in the current transaction.
2304 : : * - closed - true if the connection is closed.
2305 : : * - remote_backend_pid - process ID of the remote backend, on the foreign
2306 : : * server, handling the connection.
2307 : : *
2308 : : * No records are returned when there are no cached connections at all.
2309 : : */
2310 : : static void
762 fujii@postgresql.org 2311 : 14 : postgres_fdw_get_connections_internal(FunctionCallInfo fcinfo,
2312 : : enum pgfdwVersion api_version)
2313 : : {
2047 2314 : 14 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
2315 : : HASH_SEQ_STATUS scan;
2316 : : ConnCacheEntry *entry;
2317 : :
1409 michael@paquier.xyz 2318 : 14 : InitMaterializedSRF(fcinfo, 0);
2319 : :
2320 : : /* If cache doesn't exist, we return no records */
2047 fujii@postgresql.org 2321 [ - + ]: 14 : if (!ConnectionHash)
762 fujii@postgresql.org 2322 :UBC 0 : return;
2323 : :
2324 : : /* Check we have the expected number of output arguments */
762 fujii@postgresql.org 2325 [ - + - ]:CBC 14 : switch (rsinfo->setDesc->natts)
2326 : : {
762 fujii@postgresql.org 2327 :UBC 0 : case POSTGRES_FDW_GET_CONNECTIONS_COLS_V1_1:
2328 [ # # ]: 0 : if (api_version != PGFDW_V1_1)
2329 [ # # ]: 0 : elog(ERROR, "incorrect number of output arguments");
2330 : 0 : break;
762 fujii@postgresql.org 2331 :CBC 14 : case POSTGRES_FDW_GET_CONNECTIONS_COLS_V1_2:
2332 [ - + ]: 14 : if (api_version != PGFDW_V1_2)
762 fujii@postgresql.org 2333 [ # # ]:UBC 0 : elog(ERROR, "incorrect number of output arguments");
762 fujii@postgresql.org 2334 :CBC 14 : break;
762 fujii@postgresql.org 2335 :UBC 0 : default:
2336 [ # # ]: 0 : elog(ERROR, "incorrect number of output arguments");
2337 : : }
2338 : :
2047 fujii@postgresql.org 2339 :CBC 14 : hash_seq_init(&scan, ConnectionHash);
2340 [ + + ]: 123 : while ((entry = (ConnCacheEntry *) hash_seq_search(&scan)))
2341 : : {
2342 : : ForeignServer *server;
1503 peter@eisentraut.org 2343 : 109 : Datum values[POSTGRES_FDW_GET_CONNECTIONS_COLS] = {0};
2344 : 109 : bool nulls[POSTGRES_FDW_GET_CONNECTIONS_COLS] = {0};
708 fujii@postgresql.org 2345 : 109 : int i = 0;
2346 : :
2347 : : /* We only look for open remote connections */
2047 2348 [ + + ]: 109 : if (!entry->conn)
2349 : 95 : continue;
2350 : :
2351 : 14 : server = GetForeignServerExtended(entry->serverid, FSV_MISSING_OK);
2352 : :
2353 : : /*
2354 : : * The foreign server may have been dropped in current explicit
2355 : : * transaction. It is not possible to drop the server from another
2356 : : * session when the connection associated with it is in use in the
2357 : : * current transaction, if tried so, the drop query in another session
2358 : : * blocks until the current transaction finishes.
2359 : : *
2360 : : * Even though the server is dropped in the current transaction, the
2361 : : * cache can still have associated active connection entry, say we
2362 : : * call such connections dangling. Since we can not fetch the server
2363 : : * name from system catalogs for dangling connections, instead we show
2364 : : * NULL value for server name in output.
2365 : : *
2366 : : * We could have done better by storing the server name in the cache
2367 : : * entry instead of server oid so that it could be used in the output.
2368 : : * But the server name in each cache entry requires 64 bytes of
2369 : : * memory, which is huge, when there are many cached connections and
2370 : : * the use case i.e. dropping the foreign server within the explicit
2371 : : * current transaction seems rare. So, we chose to show NULL value for
2372 : : * server name in output.
2373 : : *
2374 : : * Such dangling connections get closed either in next use or at the
2375 : : * end of current explicit transaction in pgfdw_xact_callback.
2376 : : */
2377 [ + + ]: 14 : if (!server)
2378 : : {
2379 : : /*
2380 : : * If the server has been dropped in the current explicit
2381 : : * transaction, then this entry would have been invalidated in
2382 : : * pgfdw_inval_callback at the end of drop server command. Note
2383 : : * that this connection would not have been closed in
2384 : : * pgfdw_inval_callback because it is still being used in the
2385 : : * current explicit transaction. So, assert that here.
2386 : : */
2387 [ + - + - : 1 : Assert(entry->conn && entry->xact_depth > 0 && entry->invalidated);
- + ]
2388 : :
2389 : : /* Show null, if no server name was found */
708 2390 : 1 : nulls[i++] = true;
2391 : : }
2392 : : else
2393 : 13 : values[i++] = CStringGetTextDatum(server->servername);
2394 : :
2395 [ + - ]: 14 : if (api_version >= PGFDW_V1_2)
2396 : : {
2397 : : HeapTuple tp;
2398 : :
2399 : : /* Use the system cache to obtain the user mapping */
2400 : 14 : tp = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(entry->key));
2401 : :
2402 : : /*
2403 : : * Just like in the foreign server case, user mappings can also be
2404 : : * dropped in the current explicit transaction. Therefore, the
2405 : : * similar check as in the server case is required.
2406 : : */
2407 [ + + ]: 14 : if (!HeapTupleIsValid(tp))
2408 : : {
2409 : : /*
2410 : : * If we reach here, this entry must have been invalidated in
2411 : : * pgfdw_inval_callback, same as in the server case.
2412 : : */
2413 [ + - + - : 1 : Assert(entry->conn && entry->xact_depth > 0 &&
- + ]
2414 : : entry->invalidated);
2415 : :
2416 : 1 : nulls[i++] = true;
2417 : : }
2418 : : else
2419 : : {
2420 : : Oid userid;
2421 : :
2422 : 13 : userid = ((Form_pg_user_mapping) GETSTRUCT(tp))->umuser;
2423 [ + + ]: 13 : values[i++] = CStringGetTextDatum(MappingUserName(userid));
2424 : 13 : ReleaseSysCache(tp);
2425 : : }
2426 : : }
2427 : :
2428 : 14 : values[i++] = BoolGetDatum(!entry->invalidated);
2429 : :
762 2430 [ + - ]: 14 : if (api_version >= PGFDW_V1_2)
2431 : : {
2432 : 14 : bool check_conn = PG_GETARG_BOOL(0);
2433 : :
2434 : : /* Is this connection used in the current transaction? */
708 2435 : 14 : values[i++] = BoolGetDatum(entry->xact_depth > 0);
2436 : :
2437 : : /*
2438 : : * If a connection status check is requested and supported, return
2439 : : * whether the connection is closed. Otherwise, return NULL.
2440 : : */
762 2441 [ + + + - ]: 14 : if (check_conn && pgfdw_conn_checkable())
708 2442 : 3 : values[i++] = BoolGetDatum(pgfdw_conn_check(entry->conn) != 0);
2443 : : else
2444 : 11 : nulls[i++] = true;
2445 : :
2446 : : /* Return process ID of remote backend */
542 2447 : 14 : values[i++] = Int32GetDatum(PQbackendPID(entry->conn));
2448 : : }
2449 : :
1633 michael@paquier.xyz 2450 : 14 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
2451 : : }
2452 : : }
2453 : :
2454 : : /*
2455 : : * Values in connection strings must be enclosed in single quotes. Single
2456 : : * quotes and backslashes must be escaped with backslash. NB: these rules are
2457 : : * different from the rules for escaping a SQL literal.
2458 : : */
2459 : : static void
174 jdavis@postgresql.or 2460 : 35 : appendEscapedValue(StringInfo str, const char *val)
2461 : : {
2462 : 35 : appendStringInfoChar(str, '\'');
2463 [ + + ]: 343 : for (int i = 0; val[i] != '\0'; i++)
2464 : : {
2465 [ + - - + ]: 308 : if (val[i] == '\\' || val[i] == '\'')
174 jdavis@postgresql.or 2466 :UBC 0 : appendStringInfoChar(str, '\\');
174 jdavis@postgresql.or 2467 :CBC 308 : appendStringInfoChar(str, val[i]);
2468 : : }
2469 : 35 : appendStringInfoChar(str, '\'');
2470 : 35 : }
2471 : :
2472 : : Datum
2473 : 8 : postgres_fdw_connection(PG_FUNCTION_ARGS)
2474 : : {
2475 : 8 : Oid userid = PG_GETARG_OID(0);
2476 : 8 : Oid serverid = PG_GETARG_OID(1);
2477 : 8 : ForeignServer *server = GetForeignServer(serverid);
2478 : 8 : UserMapping *user = GetUserMapping(userid, serverid);
2479 : : StringInfoData str;
2480 : : const char **keywords;
2481 : : const char **values;
2482 : : char *appname;
2483 : 8 : char *sep = "";
2484 : :
2485 : : /*
2486 : : * SCRAM pass-through cannot work for subscriptions because the connection
2487 : : * happens in a worker process.
2488 : : */
24 2489 [ + + ]: 8 : if (UseScramPassthrough(server, user))
2490 [ + - ]: 1 : ereport(ERROR,
2491 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2492 : : errmsg("SCRAM pass-through authentication is not supported for subscription connections"),
2493 : : errdetail("The foreign server or user mapping for user \"%s\" has \"use_scram_passthrough\" enabled.",
2494 : : GetUserNameFromId(userid, false)),
2495 : : errhint("Store a password in the user mapping instead.")));
2496 : :
174 2497 : 7 : construct_connection_params(server, user, &keywords, &values, &appname);
2498 : :
2499 : 7 : initStringInfo(&str);
2500 [ + + ]: 42 : for (int i = 0; keywords[i] != NULL; i++)
2501 : : {
2502 [ - + ]: 35 : if (values[i] == NULL)
174 jdavis@postgresql.or 2503 :UBC 0 : continue;
174 jdavis@postgresql.or 2504 :CBC 35 : appendStringInfo(&str, "%s%s = ", sep, keywords[i]);
2505 : 35 : appendEscapedValue(&str, values[i]);
2506 : 35 : sep = " ";
2507 : : }
2508 : :
2509 [ - + ]: 7 : if (appname != NULL)
174 jdavis@postgresql.or 2510 :UBC 0 : pfree(appname);
174 jdavis@postgresql.or 2511 :CBC 7 : pfree(keywords);
2512 : 7 : pfree(values);
2513 : 7 : PG_RETURN_TEXT_P(cstring_to_text(str.data));
2514 : : }
2515 : :
2516 : : /*
2517 : : * List active foreign server connections.
2518 : : *
2519 : : * The SQL API of this function has changed multiple times, and will likely
2520 : : * do so again in future. To support the case where a newer version of this
2521 : : * loadable module is being used with an old SQL declaration of the function,
2522 : : * we continue to support the older API versions.
2523 : : */
2524 : : Datum
762 fujii@postgresql.org 2525 : 14 : postgres_fdw_get_connections_1_2(PG_FUNCTION_ARGS)
2526 : : {
2527 : 14 : postgres_fdw_get_connections_internal(fcinfo, PGFDW_V1_2);
2528 : :
2529 : 14 : PG_RETURN_VOID();
2530 : : }
2531 : :
2532 : : Datum
762 fujii@postgresql.org 2533 :UBC 0 : postgres_fdw_get_connections(PG_FUNCTION_ARGS)
2534 : : {
2535 : 0 : postgres_fdw_get_connections_internal(fcinfo, PGFDW_V1_1);
2536 : :
2047 2537 : 0 : PG_RETURN_VOID();
2538 : : }
2539 : :
2540 : : /*
2541 : : * Disconnect the specified cached connections.
2542 : : *
2543 : : * This function discards the open connections that are established by
2544 : : * postgres_fdw from the local session to the foreign server with
2545 : : * the given name. Note that there can be multiple connections to
2546 : : * the given server using different user mappings. If the connections
2547 : : * are used in the current local transaction, they are not disconnected
2548 : : * and warning messages are reported. This function returns true
2549 : : * if it disconnects at least one connection, otherwise false. If no
2550 : : * foreign server with the given name is found, an error is reported.
2551 : : */
2552 : : Datum
2039 fujii@postgresql.org 2553 :CBC 4 : postgres_fdw_disconnect(PG_FUNCTION_ARGS)
2554 : : {
2555 : : ForeignServer *server;
2556 : : char *servername;
2557 : :
2558 : 4 : servername = text_to_cstring(PG_GETARG_TEXT_PP(0));
2559 : 4 : server = GetForeignServerByName(servername, false);
2560 : :
2561 : 3 : PG_RETURN_BOOL(disconnect_cached_connections(server->serverid));
2562 : : }
2563 : :
2564 : : /*
2565 : : * Disconnect all the cached connections.
2566 : : *
2567 : : * This function discards all the open connections that are established by
2568 : : * postgres_fdw from the local session to the foreign servers.
2569 : : * If the connections are used in the current local transaction, they are
2570 : : * not disconnected and warning messages are reported. This function
2571 : : * returns true if it disconnects at least one connection, otherwise false.
2572 : : */
2573 : : Datum
2574 : 6 : postgres_fdw_disconnect_all(PG_FUNCTION_ARGS)
2575 : : {
2576 : 6 : PG_RETURN_BOOL(disconnect_cached_connections(InvalidOid));
2577 : : }
2578 : :
2579 : : /*
2580 : : * Workhorse to disconnect cached connections.
2581 : : *
2582 : : * This function scans all the connection cache entries and disconnects
2583 : : * the open connections whose foreign server OID matches with
2584 : : * the specified one. If InvalidOid is specified, it disconnects all
2585 : : * the cached connections.
2586 : : *
2587 : : * This function emits a warning for each connection that's used in
2588 : : * the current transaction and doesn't close it. It returns true if
2589 : : * it disconnects at least one connection, otherwise false.
2590 : : *
2591 : : * Note that this function disconnects even the connections that are
2592 : : * established by other users in the same local session using different
2593 : : * user mappings. This leads even non-superuser to be able to close
2594 : : * the connections established by superusers in the same local session.
2595 : : *
2596 : : * XXX As of now we don't see any security risk doing this. But we should
2597 : : * set some restrictions on that, for example, prevent non-superuser
2598 : : * from closing the connections established by superusers even
2599 : : * in the same session?
2600 : : */
2601 : : static bool
2602 : 9 : disconnect_cached_connections(Oid serverid)
2603 : : {
2604 : : HASH_SEQ_STATUS scan;
2605 : : ConnCacheEntry *entry;
2606 : 9 : bool all = !OidIsValid(serverid);
2607 : 9 : bool result = false;
2608 : :
2609 : : /*
2610 : : * Connection cache hashtable has not been initialized yet in this
2611 : : * session, so return false.
2612 : : */
2613 [ - + ]: 9 : if (!ConnectionHash)
2039 fujii@postgresql.org 2614 :UBC 0 : return false;
2615 : :
2039 fujii@postgresql.org 2616 :CBC 9 : hash_seq_init(&scan, ConnectionHash);
2617 [ + + ]: 77 : while ((entry = (ConnCacheEntry *) hash_seq_search(&scan)))
2618 : : {
2619 : : /* Ignore cache entry if no open connection right now. */
2620 [ + + ]: 68 : if (!entry->conn)
2621 : 55 : continue;
2622 : :
2623 [ + + + + ]: 13 : if (all || entry->serverid == serverid)
2624 : : {
2625 : : /*
2626 : : * Emit a warning because the connection to close is used in the
2627 : : * current transaction and cannot be disconnected right now.
2628 : : */
2629 [ + + ]: 10 : if (entry->xact_depth > 0)
2630 : : {
2631 : : ForeignServer *server;
2632 : :
2633 : 3 : server = GetForeignServerExtended(entry->serverid,
2634 : : FSV_MISSING_OK);
2635 : :
2636 [ - + ]: 3 : if (!server)
2637 : : {
2638 : : /*
2639 : : * If the foreign server was dropped while its connection
2640 : : * was used in the current transaction, the connection
2641 : : * must have been marked as invalid by
2642 : : * pgfdw_inval_callback at the end of DROP SERVER command.
2643 : : */
2039 fujii@postgresql.org 2644 [ # # ]:UBC 0 : Assert(entry->invalidated);
2645 : :
2646 [ # # ]: 0 : ereport(WARNING,
2647 : : (errmsg("cannot close dropped server connection because it is still in use")));
2648 : : }
2649 : : else
2039 fujii@postgresql.org 2650 [ + - ]:CBC 3 : ereport(WARNING,
2651 : : (errmsg("cannot close connection for server \"%s\" because it is still in use",
2652 : : server->servername)));
2653 : : }
2654 : : else
2655 : : {
2656 [ - + ]: 7 : elog(DEBUG3, "discarding connection %p", entry->conn);
2657 : 7 : disconnect_pg_server(entry);
2658 : 7 : result = true;
2659 : : }
2660 : : }
2661 : : }
2662 : :
2663 : 9 : return result;
2664 : : }
2665 : :
2666 : : /*
2667 : : * Check if the remote server closed the connection.
2668 : : *
2669 : : * Returns 1 if the connection is closed, -1 if an error occurred,
2670 : : * and 0 if it's not closed or if the connection check is unavailable
2671 : : * on this platform.
2672 : : */
2673 : : static int
762 2674 : 3 : pgfdw_conn_check(PGconn *conn)
2675 : : {
2676 : 3 : int sock = PQsocket(conn);
2677 : :
2678 [ + - - + ]: 3 : if (PQstatus(conn) != CONNECTION_OK || sock == -1)
762 fujii@postgresql.org 2679 :UBC 0 : return -1;
2680 : :
2681 : : #if (defined(HAVE_POLL) && defined(POLLRDHUP))
2682 : : {
2683 : : struct pollfd input_fd;
2684 : : int result;
2685 : :
762 fujii@postgresql.org 2686 :CBC 3 : input_fd.fd = sock;
2687 : 3 : input_fd.events = POLLRDHUP;
2688 : 3 : input_fd.revents = 0;
2689 : :
2690 : : do
2691 : 3 : result = poll(&input_fd, 1, 0);
2692 [ - + - - ]: 3 : while (result < 0 && errno == EINTR);
2693 : :
2694 [ - + ]: 3 : if (result < 0)
762 fujii@postgresql.org 2695 :UBC 0 : return -1;
2696 : :
761 fujii@postgresql.org 2697 :CBC 3 : return (input_fd.revents &
2698 : 3 : (POLLRDHUP | POLLHUP | POLLERR | POLLNVAL)) ? 1 : 0;
2699 : : }
2700 : : #else
2701 : : return 0;
2702 : : #endif
2703 : : }
2704 : :
2705 : : /*
2706 : : * Check if connection status checking is available on this platform.
2707 : : *
2708 : : * Returns true if available, false otherwise.
2709 : : */
2710 : : static bool
762 2711 : 3 : pgfdw_conn_checkable(void)
2712 : : {
2713 : : #if (defined(HAVE_POLL) && defined(POLLRDHUP))
2714 : 3 : return true;
2715 : : #else
2716 : : return false;
2717 : : #endif
2718 : : }
2719 : :
2720 : : /*
2721 : : * Ensure that require_auth and SCRAM keys are correctly set on values. SCRAM
2722 : : * keys used to pass-through are coming from the initial connection from the
2723 : : * client with the server.
2724 : : *
2725 : : * All required SCRAM options are set by postgres_fdw, so we just need to
2726 : : * ensure that these options are not overwritten by the user.
2727 : : */
2728 : : static bool
521 peter@eisentraut.org 2729 : 10 : pgfdw_has_required_scram_options(const char **keywords, const char **values)
2730 : : {
2731 : 10 : bool has_scram_server_key = false;
2732 : 10 : bool has_scram_client_key = false;
2733 : 10 : bool has_require_auth = false;
2734 : 10 : bool has_scram_keys = false;
2735 : :
2736 : : /*
2737 : : * Continue iterating even if we found the keys that we need to validate
2738 : : * to make sure that there is no other declaration of these keys that can
2739 : : * overwrite the first.
2740 : : */
2741 [ + + ]: 97 : for (int i = 0; keywords[i] != NULL; i++)
2742 : : {
2743 [ + + ]: 87 : if (strcmp(keywords[i], "scram_client_key") == 0)
2744 : : {
2745 [ + - + - ]: 9 : if (values[i] != NULL && values[i][0] != '\0')
2746 : 9 : has_scram_client_key = true;
2747 : : else
521 peter@eisentraut.org 2748 :UBC 0 : has_scram_client_key = false;
2749 : : }
2750 : :
521 peter@eisentraut.org 2751 [ + + ]:CBC 87 : if (strcmp(keywords[i], "scram_server_key") == 0)
2752 : : {
2753 [ + - + - ]: 9 : if (values[i] != NULL && values[i][0] != '\0')
2754 : 9 : has_scram_server_key = true;
2755 : : else
521 peter@eisentraut.org 2756 :UBC 0 : has_scram_server_key = false;
2757 : : }
2758 : :
521 peter@eisentraut.org 2759 [ + + ]:CBC 87 : if (strcmp(keywords[i], "require_auth") == 0)
2760 : : {
2761 [ + - + - ]: 9 : if (values[i] != NULL && strcmp(values[i], "scram-sha-256") == 0)
2762 : 9 : has_require_auth = true;
2763 : : else
521 peter@eisentraut.org 2764 :UBC 0 : has_require_auth = false;
2765 : : }
2766 : : }
2767 : :
384 peter@eisentraut.org 2768 [ + + + - :CBC 10 : has_scram_keys = has_scram_client_key && has_scram_server_key && MyProcPort != NULL && MyProcPort->has_scram_keys;
+ - + - ]
2769 : :
521 2770 [ + + + - ]: 10 : return (has_scram_keys && has_require_auth);
2771 : : }
|