@@ -100,7 +100,7 @@ static PGresult *storeQueryResult(volatile storeInfo *sinfo, PGconn *conn, const
100100static void storeRow (volatile storeInfo * sinfo , PGresult * res , bool first );
101101static remoteConn * getConnectionByName (const char * name );
102102static HTAB * createConnHash (void );
103- static void createNewConnection (const char * name , remoteConn * rconn );
103+ static remoteConn * createNewConnection (const char * name );
104104static void deleteConnection (const char * name );
105105static char * * get_pkey_attnames (Relation rel , int16 * indnkeyatts );
106106static char * * get_text_array_contents (ArrayType * array , int * numitems );
@@ -114,7 +114,8 @@ static Relation get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclM
114114static char * generate_relation_name (Relation rel );
115115static void dblink_connstr_check (const char * connstr );
116116static bool dblink_connstr_has_pw (const char * connstr );
117- static void dblink_security_check (PGconn * conn , remoteConn * rconn , const char * connstr );
117+ static void dblink_security_check (PGconn * conn , const char * connname ,
118+ const char * connstr );
118119static void dblink_res_error (PGconn * conn , const char * conname , PGresult * res ,
119120 bool fail , const char * fmt ,...) pg_attribute_printf (5 , 6 );
120121static char * get_connect_string (const char * servername );
@@ -137,16 +138,22 @@ static uint32 dblink_we_get_conn = 0;
137138static uint32 dblink_we_get_result = 0 ;
138139
139140/*
140- * Following is list that holds multiple remote connections.
141+ * Following is hash that holds multiple remote connections.
141142 * Calling convention of each dblink function changes to accept
142- * connection name as the first parameter. The connection list is
143+ * connection name as the first parameter. The connection hash is
143144 * much like ecpg e.g. a mapping between a name and a PGconn object.
145+ *
146+ * To avoid potentially leaking a PGconn object in case of out-of-memory
147+ * errors, we first create the hash entry, then open the PGconn.
148+ * Hence, a hash entry whose rconn.conn pointer is NULL must be
149+ * understood as a leftover from a failed create; it should be ignored
150+ * by lookup operations, and silently replaced by create operations.
144151 */
145152
146153typedef struct remoteConnHashEnt
147154{
148155 char name [NAMEDATALEN ];
149- remoteConn * rconn ;
156+ remoteConn rconn ;
150157} remoteConnHashEnt ;
151158
152159/* initial number of connection hashes */
@@ -225,7 +232,7 @@ dblink_get_conn(char *conname_or_str,
225232 errmsg ("could not establish connection" ),
226233 errdetail_internal ("%s" , msg )));
227234 }
228- dblink_security_check (conn , rconn , connstr );
235+ dblink_security_check (conn , NULL , connstr );
229236 if (PQclientEncoding (conn ) != GetDatabaseEncoding ())
230237 PQsetClientEncoding (conn , GetDatabaseEncodingName ());
231238 freeconn = true;
@@ -288,15 +295,6 @@ dblink_connect(PG_FUNCTION_ARGS)
288295 else if (PG_NARGS () == 1 )
289296 conname_or_str = text_to_cstring (PG_GETARG_TEXT_PP (0 ));
290297
291- if (connname )
292- {
293- rconn = (remoteConn * ) MemoryContextAlloc (TopMemoryContext ,
294- sizeof (remoteConn ));
295- rconn -> conn = NULL ;
296- rconn -> openCursorCount = 0 ;
297- rconn -> newXactForCursor = false;
298- }
299-
300298 /* first check for valid foreign data server */
301299 connstr = get_connect_string (conname_or_str );
302300 if (connstr == NULL )
@@ -309,15 +307,22 @@ dblink_connect(PG_FUNCTION_ARGS)
309307 if (dblink_we_connect == 0 )
310308 dblink_we_connect = WaitEventExtensionNew ("DblinkConnect" );
311309
310+ /* if we need a hashtable entry, make that first, since it might fail */
311+ if (connname )
312+ {
313+ rconn = createNewConnection (connname );
314+ Assert (rconn -> conn == NULL );
315+ }
316+
312317 /* OK to make connection */
313318 conn = libpqsrv_connect (connstr , dblink_we_connect );
314319
315320 if (PQstatus (conn ) == CONNECTION_BAD )
316321 {
317322 msg = pchomp (PQerrorMessage (conn ));
318323 libpqsrv_disconnect (conn );
319- if (rconn )
320- pfree ( rconn );
324+ if (connname )
325+ deleteConnection ( connname );
321326
322327 ereport (ERROR ,
323328 (errcode (ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION ),
@@ -326,16 +331,16 @@ dblink_connect(PG_FUNCTION_ARGS)
326331 }
327332
328333 /* check password actually used if not superuser */
329- dblink_security_check (conn , rconn , connstr );
334+ dblink_security_check (conn , connname , connstr );
330335
331336 /* attempt to set client encoding to match server encoding, if needed */
332337 if (PQclientEncoding (conn ) != GetDatabaseEncoding ())
333338 PQsetClientEncoding (conn , GetDatabaseEncodingName ());
334339
340+ /* all OK, save away the conn */
335341 if (connname )
336342 {
337343 rconn -> conn = conn ;
338- createNewConnection (connname , rconn );
339344 }
340345 else
341346 {
@@ -375,10 +380,7 @@ dblink_disconnect(PG_FUNCTION_ARGS)
375380
376381 libpqsrv_disconnect (conn );
377382 if (rconn )
378- {
379383 deleteConnection (conname );
380- pfree (rconn );
381- }
382384 else
383385 pconn -> conn = NULL ;
384386
@@ -1296,6 +1298,9 @@ dblink_get_connections(PG_FUNCTION_ARGS)
12961298 hash_seq_init (& status , remoteConnHash );
12971299 while ((hentry = (remoteConnHashEnt * ) hash_seq_search (& status )) != NULL )
12981300 {
1301+ /* ignore it if it's not an open connection */
1302+ if (hentry -> rconn .conn == NULL )
1303+ continue ;
12991304 /* stash away current value */
13001305 astate = accumArrayResult (astate ,
13011306 CStringGetTextDatum (hentry -> name ),
@@ -2533,8 +2538,8 @@ getConnectionByName(const char *name)
25332538 hentry = (remoteConnHashEnt * ) hash_search (remoteConnHash ,
25342539 key , HASH_FIND , NULL );
25352540
2536- if (hentry )
2537- return hentry -> rconn ;
2541+ if (hentry && hentry -> rconn . conn != NULL )
2542+ return & hentry -> rconn ;
25382543
25392544 return NULL ;
25402545}
@@ -2551,8 +2556,8 @@ createConnHash(void)
25512556 HASH_ELEM | HASH_STRINGS );
25522557}
25532558
2554- static void
2555- createNewConnection (const char * name , remoteConn * rconn )
2559+ static remoteConn *
2560+ createNewConnection (const char * name )
25562561{
25572562 remoteConnHashEnt * hentry ;
25582563 bool found ;
@@ -2566,17 +2571,15 @@ createNewConnection(const char *name, remoteConn *rconn)
25662571 hentry = (remoteConnHashEnt * ) hash_search (remoteConnHash , key ,
25672572 HASH_ENTER , & found );
25682573
2569- if (found )
2570- {
2571- libpqsrv_disconnect (rconn -> conn );
2572- pfree (rconn );
2573-
2574+ if (found && hentry -> rconn .conn != NULL )
25742575 ereport (ERROR ,
25752576 (errcode (ERRCODE_DUPLICATE_OBJECT ),
25762577 errmsg ("duplicate connection name" )));
2577- }
25782578
2579- hentry -> rconn = rconn ;
2579+ /* New, or reusable, so initialize the rconn struct to zeroes */
2580+ memset (& hentry -> rconn , 0 , sizeof (remoteConn ));
2581+
2582+ return & hentry -> rconn ;
25802583}
25812584
25822585static void
@@ -2604,9 +2607,12 @@ deleteConnection(const char *name)
26042607 * We need to make sure that the connection made used credentials
26052608 * which were provided by the user, so check what credentials were
26062609 * used to connect and then make sure that they came from the user.
2610+ *
2611+ * On failure, we close "conn" and also delete the hashtable entry
2612+ * identified by "connname" (if that's not NULL).
26072613 */
26082614static void
2609- dblink_security_check (PGconn * conn , remoteConn * rconn , const char * connstr )
2615+ dblink_security_check (PGconn * conn , const char * connname , const char * connstr )
26102616{
26112617 /* Superuser bypasses security check */
26122618 if (superuser ())
@@ -2624,8 +2630,8 @@ dblink_security_check(PGconn *conn, remoteConn *rconn, const char *connstr)
26242630
26252631 /* Otherwise, fail out */
26262632 libpqsrv_disconnect (conn );
2627- if (rconn )
2628- pfree ( rconn );
2633+ if (connname )
2634+ deleteConnection ( connname );
26292635
26302636 ereport (ERROR ,
26312637 (errcode (ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED ),
0 commit comments