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