@@ -100,7 +100,7 @@ static PGresult *storeQueryResult(volatile storeInfo *sinfo, PGconn *conn, const
100
100
static void storeRow (volatile storeInfo * sinfo ,PGresult * res ,bool first );
101
101
static remoteConn * getConnectionByName (const char * name );
102
102
static HTAB * createConnHash (void );
103
- static void createNewConnection (const char * name , remoteConn * rconn );
103
+ static remoteConn * createNewConnection (const char * name );
104
104
static void deleteConnection (const char * name );
105
105
static char * * get_pkey_attnames (Relation rel ,int16 * indnkeyatts );
106
106
static char * * get_text_array_contents (ArrayType * array ,int * numitems );
@@ -113,7 +113,7 @@ static HeapTuple get_tuple_of_interest(Relation rel, int *pkattnums, int pknumat
113
113
static Relation get_rel_from_relname (text * relname_text ,LOCKMODE lockmode ,AclMode aclmode );
114
114
static char * generate_relation_name (Relation rel );
115
115
static void dblink_connstr_check (const char * connstr );
116
- static void dblink_security_check (PGconn * conn ,remoteConn * rconn );
116
+ static void dblink_security_check (PGconn * conn ,const char * connname );
117
117
static void dblink_res_error (PGconn * conn ,const char * conname ,PGresult * res ,
118
118
bool fail ,const char * fmt ,...)pg_attribute_printf (5 ,6 );
119
119
static char * get_connect_string (const char * servername );
@@ -131,16 +131,22 @@ static remoteConn *pconn = NULL;
131
131
static HTAB * remoteConnHash = NULL ;
132
132
133
133
/*
134
- *Following islist that holds multiple remote connections.
134
+ *Following ishash that holds multiple remote connections.
135
135
*Calling convention of each dblink function changes to accept
136
- *connection name as the first parameter. The connectionlist is
136
+ *connection name as the first parameter. The connectionhash is
137
137
*much like ecpg e.g. a mapping between a name and a PGconn object.
138
+ *
139
+ *To avoid potentially leaking a PGconn object in case of out-of-memory
140
+ *errors, we first create the hash entry, then open the PGconn.
141
+ *Hence, a hash entry whose rconn.conn pointer is NULL must be
142
+ *understood as a leftover from a failed create; it should be ignored
143
+ *by lookup operations, and silently replaced by create operations.
138
144
*/
139
145
140
146
typedef struct remoteConnHashEnt
141
147
{
142
148
char name [NAMEDATALEN ];
143
- remoteConn * rconn ;
149
+ remoteConn rconn ;
144
150
}remoteConnHashEnt ;
145
151
146
152
/* initial number of connection hashes */
@@ -239,7 +245,7 @@ dblink_get_conn(char *conname_or_str,
239
245
errmsg ("could not establish connection" ),
240
246
errdetail_internal ("%s" ,msg )));
241
247
}
242
- dblink_security_check (conn ,rconn );
248
+ dblink_security_check (conn ,NULL );
243
249
if (PQclientEncoding (conn )!= GetDatabaseEncoding ())
244
250
PQsetClientEncoding (conn ,GetDatabaseEncodingName ());
245
251
freeconn = true;
@@ -299,15 +305,6 @@ dblink_connect(PG_FUNCTION_ARGS)
299
305
else if (PG_NARGS ()== 1 )
300
306
conname_or_str = text_to_cstring (PG_GETARG_TEXT_PP (0 ));
301
307
302
- if (connname )
303
- {
304
- rconn = (remoteConn * )MemoryContextAlloc (TopMemoryContext ,
305
- sizeof (remoteConn ));
306
- rconn -> conn = NULL ;
307
- rconn -> openCursorCount = 0 ;
308
- rconn -> newXactForCursor = false;
309
- }
310
-
311
308
/* first check for valid foreign data server */
312
309
connstr = get_connect_string (conname_or_str );
313
310
if (connstr == NULL )
@@ -338,6 +335,13 @@ dblink_connect(PG_FUNCTION_ARGS)
338
335
#endif
339
336
}
340
337
338
+ /* if we need a hashtable entry, make that first, since it might fail */
339
+ if (connname )
340
+ {
341
+ rconn = createNewConnection (connname );
342
+ Assert (rconn -> conn == NULL );
343
+ }
344
+
341
345
/* OK to make connection */
342
346
conn = PQconnectdb (connstr );
343
347
@@ -346,8 +350,8 @@ dblink_connect(PG_FUNCTION_ARGS)
346
350
msg = pchomp (PQerrorMessage (conn ));
347
351
PQfinish (conn );
348
352
ReleaseExternalFD ();
349
- if (rconn )
350
- pfree ( rconn );
353
+ if (connname )
354
+ deleteConnection ( connname );
351
355
352
356
ereport (ERROR ,
353
357
(errcode (ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION ),
@@ -356,16 +360,16 @@ dblink_connect(PG_FUNCTION_ARGS)
356
360
}
357
361
358
362
/* check password actually used if not superuser */
359
- dblink_security_check (conn ,rconn );
363
+ dblink_security_check (conn ,connname );
360
364
361
365
/* attempt to set client encoding to match server encoding, if needed */
362
366
if (PQclientEncoding (conn )!= GetDatabaseEncoding ())
363
367
PQsetClientEncoding (conn ,GetDatabaseEncodingName ());
364
368
369
+ /* all OK, save away the conn */
365
370
if (connname )
366
371
{
367
372
rconn -> conn = conn ;
368
- createNewConnection (connname ,rconn );
369
373
}
370
374
else
371
375
{
@@ -409,10 +413,7 @@ dblink_disconnect(PG_FUNCTION_ARGS)
409
413
PQfinish (conn );
410
414
ReleaseExternalFD ();
411
415
if (rconn )
412
- {
413
416
deleteConnection (conname );
414
- pfree (rconn );
415
- }
416
417
else
417
418
pconn -> conn = NULL ;
418
419
@@ -1336,6 +1337,9 @@ dblink_get_connections(PG_FUNCTION_ARGS)
1336
1337
hash_seq_init (& status ,remoteConnHash );
1337
1338
while ((hentry = (remoteConnHashEnt * )hash_seq_search (& status ))!= NULL )
1338
1339
{
1340
+ /* ignore it if it's not an open connection */
1341
+ if (hentry -> rconn .conn == NULL )
1342
+ continue ;
1339
1343
/* stash away current value */
1340
1344
astate = accumArrayResult (astate ,
1341
1345
CStringGetTextDatum (hentry -> name ),
@@ -2597,8 +2601,8 @@ getConnectionByName(const char *name)
2597
2601
hentry = (remoteConnHashEnt * )hash_search (remoteConnHash ,
2598
2602
key ,HASH_FIND ,NULL );
2599
2603
2600
- if (hentry )
2601
- return hentry -> rconn ;
2604
+ if (hentry && hentry -> rconn . conn != NULL )
2605
+ return & hentry -> rconn ;
2602
2606
2603
2607
return NULL ;
2604
2608
}
@@ -2614,8 +2618,8 @@ createConnHash(void)
2614
2618
return hash_create ("Remote Con hash" ,NUMCONN ,& ctl ,HASH_ELEM );
2615
2619
}
2616
2620
2617
- static void
2618
- createNewConnection (const char * name , remoteConn * rconn )
2621
+ static remoteConn *
2622
+ createNewConnection (const char * name )
2619
2623
{
2620
2624
remoteConnHashEnt * hentry ;
2621
2625
bool found ;
@@ -2629,19 +2633,15 @@ createNewConnection(const char *name, remoteConn *rconn)
2629
2633
hentry = (remoteConnHashEnt * )hash_search (remoteConnHash ,key ,
2630
2634
HASH_ENTER ,& found );
2631
2635
2632
- if (found )
2633
- {
2634
- PQfinish (rconn -> conn );
2635
- ReleaseExternalFD ();
2636
- pfree (rconn );
2637
-
2636
+ if (found && hentry -> rconn .conn != NULL )
2638
2637
ereport (ERROR ,
2639
2638
(errcode (ERRCODE_DUPLICATE_OBJECT ),
2640
2639
errmsg ("duplicate connection name" )));
2641
- }
2642
2640
2643
- hentry -> rconn = rconn ;
2644
- strlcpy (hentry -> name ,name ,sizeof (hentry -> name ));
2641
+ /* New, or reusable, so initialize the rconn struct to zeroes */
2642
+ memset (& hentry -> rconn ,0 ,sizeof (remoteConn ));
2643
+
2644
+ return & hentry -> rconn ;
2645
2645
}
2646
2646
2647
2647
static void
@@ -2667,16 +2667,16 @@ deleteConnection(const char *name)
2667
2667
}
2668
2668
2669
2669
static void
2670
- dblink_security_check (PGconn * conn ,remoteConn * rconn )
2670
+ dblink_security_check (PGconn * conn ,const char * connname )
2671
2671
{
2672
2672
if (!superuser ())
2673
2673
{
2674
2674
if (!PQconnectionUsedPassword (conn ))
2675
2675
{
2676
2676
PQfinish (conn );
2677
2677
ReleaseExternalFD ();
2678
- if (rconn )
2679
- pfree ( rconn );
2678
+ if (connname )
2679
+ deleteConnection ( connname );
2680
2680
2681
2681
ereport (ERROR ,
2682
2682
(errcode (ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED ),