@@ -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
@@ -1335,6 +1336,9 @@ dblink_get_connections(PG_FUNCTION_ARGS)
1335
1336
hash_seq_init (& status ,remoteConnHash );
1336
1337
while ((hentry = (remoteConnHashEnt * )hash_seq_search (& status ))!= NULL )
1337
1338
{
1339
+ /* ignore it if it's not an open connection */
1340
+ if (hentry -> rconn .conn == NULL )
1341
+ continue ;
1338
1342
/* stash away current value */
1339
1343
astate = accumArrayResult (astate ,
1340
1344
CStringGetTextDatum (hentry -> name ),
@@ -2596,8 +2600,8 @@ getConnectionByName(const char *name)
2596
2600
hentry = (remoteConnHashEnt * )hash_search (remoteConnHash ,
2597
2601
key ,HASH_FIND ,NULL );
2598
2602
2599
- if (hentry )
2600
- return hentry -> rconn ;
2603
+ if (hentry && hentry -> rconn . conn != NULL )
2604
+ return & hentry -> rconn ;
2601
2605
2602
2606
return NULL ;
2603
2607
}
@@ -2614,8 +2618,8 @@ createConnHash(void)
2614
2618
HASH_ELEM |HASH_STRINGS );
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 ),