#include "fsconnection.h"
static void check_conn_params(const char **keywords, const char **values);
-static PGconn *get_cached_connection(const char *name);
-static void register_connection(PGconn *conn, const char *conname);
+static PGconn *connect_pg_server(ForeignServer *server, UserMapping *user);
static void cleanup_connection(ResourceReleasePhase phase,
bool isCommit,
bool isTopLevel,
PGconn *
GetConnection(ForeignServer *server, UserMapping *user)
{
- PGconn *conn;
- const char **all_keywords;
- const char **all_values;
- const char **keywords;
- const char **values;
- int n;
- int i, j;
+ const char *conname = server->servername;
+ bool found;
+ ConnCacheEntry *entry;
+ PGconn *conn = NULL;
- /* First of all, lookup connection cache by name */
- conn = get_cached_connection(server->servername);
- if (conn != NULL)
- return conn;
+ /* initialize connection cache if it isn't */
+ if (FSConnectionHash == NULL)
+ {
+ HASHCTL ctl;
- /*
- * Construct connection params from generic options of ForeignServer and
- * UserMapping. Generic options might not be a one of connection options.
- */
- n = list_length(server->options) + list_length(user->options) + 1;
- all_keywords = (const char **) palloc(sizeof(char *) * n);
- all_values = (const char **) palloc(sizeof(char *) * n);
- keywords = (const char **) palloc(sizeof(char *) * n);
- values = (const char **) palloc(sizeof(char *) * n);
- n = 0;
- n += flatten_generic_options(server->options,
- all_keywords + n, all_values + n);
- n += flatten_generic_options(user->options,
- all_keywords + n, all_values + n);
- all_keywords[n] = all_values[n] = NULL;
+ elog(DEBUG1, "%s() cache not initialized(%s)", __FUNCTION__, conname);
- for (i = 0, j = 0; all_keywords[i]; i++)
+ /* hash key is the name of the connection */
+ MemSet(&ctl, 0, sizeof(ctl));
+ ctl.keysize = NAMEDATALEN;
+ ctl.entrysize = sizeof(ConnCacheEntry);
+ /* allocate FSConnectionHash in the cache context */
+ ctl.hcxt = CacheMemoryContext;
+ FSConnectionHash = hash_create("Foreign Connections", 32,
+ &ctl,
+ HASH_ELEM | HASH_CONTEXT);
+ }
+
+ /* Is there any cached and valid connection with such name? */
+ entry = hash_search(FSConnectionHash, conname, HASH_ENTER, &found);
+ if (found)
{
- /* Use only libpq connection options. */
- if (!is_libpq_connection_option(all_keywords[i]))
- continue;
- keywords[j] = all_keywords[i];
- values[j] = all_values[i];
- j++;
+ if (entry->conn != NULL)
+ {
+ entry->refs++;
+ elog(DEBUG1, "%s() ref %d for %s", __FUNCTION__,
+ entry->refs, entry->name);
+ return entry->conn;
+ }
+
+ /*
+ * Connection cache entry was found but connection in it is invalid.
+ * We reuse entry to store newly established connection later.
+ */
}
- keywords[j] = values[j] = NULL;
- pfree(all_keywords);
- pfree(all_values);
- /* verify connection parameters and do connect */
- check_conn_params(keywords, values);
- conn = PQconnectdbParams(keywords, values, 0);
- if (!conn || PQstatus(conn) != CONNECTION_OK)
- ereport(ERROR,
- (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION),
- errmsg("could not connect to server \"%s\"",
- server->servername),
- errdetail("%s", PQerrorMessage(conn))));
- pfree(keywords);
- pfree(values);
+ /*
+ * Here we have to establish new connection.
+ * Use PG_TRY block to ensure closing connection on error.
+ */
+ PG_TRY();
+ {
+ /* Connect to the foreign PostgreSQL server */
+ conn = connect_pg_server(server, user);
- /* register the connection into connection cache. */
- register_connection(conn, server->servername);
+ /*
+ * Initialize the cache entry to keep new connection.
+ * Note: entry->name has been initialized in hash_search(HASH_ENTER).
+ */
+ entry->refs = 1;
+ entry->conn = conn;
+ elog(DEBUG1, "%s() ref %d for %s", __FUNCTION__,
+ entry->refs, entry->name);
+
+ /*
+ * Use ResourceOner to clean the connection up on error including
+ * user interrupt.
+ */
+ RegisterResourceReleaseCallback(cleanup_connection, entry);
+ elog(DEBUG1, "%s() register %p(%s)", __FUNCTION__, entry, entry->name);
+ }
+ PG_CATCH();
+ {
+ PQfinish(conn);
+ entry->conn = NULL;
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
return conn;
}
errdetail("Non-superusers must provide a password in the connection string.")));
}
-/* Find a connection from the cache by name. */
static PGconn *
-get_cached_connection(const char *conname)
+connect_pg_server(ForeignServer *server, UserMapping *user)
{
- ConnCacheEntry *entry;
-
- AssertArg(conname != NULL);
- AssertArg(strlen(conname) < NAMEDATALEN);
-
- /* Is the connection cache has been initialized? */
- if (FSConnectionHash == NULL)
- {
- elog(DEBUG1, "%s() cache not initialized(%s)", __FUNCTION__, conname);
- return NULL;
- }
-
- /* Is there any cached connection with such name? */
- entry = (ConnCacheEntry *) hash_search(FSConnectionHash,
- conname,
- HASH_FIND,
- NULL);
- if (entry == NULL)
- {
- elog(DEBUG1, "%s() \"%s\" isn't cached", __FUNCTION__, conname);
- return NULL;
- }
-
- /* Is the cached connection valid? */
- if (entry->conn == NULL)
- {
- elog(DEBUG1, "%s() \"%s\" has been closed", __FUNCTION__, conname);
- return NULL;
- }
+ const char *conname = server->servername;
+ PGconn *conn;
+ const char **all_keywords;
+ const char **all_values;
+ const char **keywords;
+ const char **values;
+ int n;
+ int i, j;
/*
- * Now the appropriate connection has been found, return it with
- * incrementing reference count.
+ * Construct connection params from generic options of ForeignServer and
+ * UserMapping. Generic options might not be a one of connection options.
*/
- entry->refs++;
- elog(DEBUG1, "%s() ref %d for %s", __FUNCTION__, entry->refs, entry->name);
-
- return entry->conn;
-}
-
-/*
- * Regsiter a named PGconn into connection cache.
- */
-static void
-register_connection(PGconn *conn, const char *conname)
-{
- bool found;
- ConnCacheEntry *entry;
-
- AssertArg(conn != NULL);
- AssertArg(conname != NULL);
- AssertArg(strlen(conname) < NAMEDATALEN);
-
- /* Use PG_TRY block to ensure closing connection on error. */
- PG_TRY();
- {
- /* initialize connection cache */
- if (FSConnectionHash == NULL)
- {
- HASHCTL ctl;
-
- /* hash key is the name of the connection */
- MemSet(&ctl, 0, sizeof(ctl));
- ctl.keysize = NAMEDATALEN;
- ctl.entrysize = sizeof(ConnCacheEntry);
- /* allocate FSConnectionHash in the cache context */
- ctl.hcxt = CacheMemoryContext;
- FSConnectionHash = hash_create("Foreign Connections", 32,
- &ctl,
- HASH_ELEM | HASH_CONTEXT);
- }
-
- /*
- * Search from cache first. When an apporopriate connection was found in
- * the connection cache, use it.
- */
- entry = (ConnCacheEntry *) hash_search(FSConnectionHash,
- conname,
- HASH_ENTER,
- &found);
- if (found)
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_OBJECT),
- errmsg("connection \"%s\" already exists", conname)));
-
- entry->refs = 1;
- entry->conn = conn;
+ n = list_length(server->options) + list_length(user->options) + 1;
+ all_keywords = (const char **) palloc(sizeof(char *) * n);
+ all_values = (const char **) palloc(sizeof(char *) * n);
+ keywords = (const char **) palloc(sizeof(char *) * n);
+ values = (const char **) palloc(sizeof(char *) * n);
+ n = 0;
+ n += flatten_generic_options(server->options,
+ all_keywords + n, all_values + n);
+ n += flatten_generic_options(user->options,
+ all_keywords + n, all_values + n);
+ all_keywords[n] = all_values[n] = NULL;
- /* Use ResourceOner to cleanup on error, including user interrupt. */
- RegisterResourceReleaseCallback(cleanup_connection, entry);
- elog(DEBUG1, "%s() register %p(%s)", __FUNCTION__, entry, entry->name);
- }
- PG_CATCH();
+ for (i = 0, j = 0; all_keywords[i]; i++)
{
- PQfinish(conn);
- PG_RE_THROW();
+ /* Use only libpq connection options. */
+ if (!is_libpq_connection_option(all_keywords[i]))
+ continue;
+ keywords[j] = all_keywords[i];
+ values[j] = all_values[i];
+ j++;
}
- PG_END_TRY();
+ keywords[j] = values[j] = NULL;
+ pfree(all_keywords);
+ pfree(all_values);
+ /* verify connection parameters and do connect */
+ check_conn_params(keywords, values);
+ conn = PQconnectdbParams(keywords, values, 0);
+ if (!conn || PQstatus(conn) != CONNECTION_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION),
+ errmsg("could not connect to server \"%s\"", conname),
+ errdetail("%s", PQerrorMessage(conn))));
+ pfree(keywords);
+ pfree(values);
+
+ return conn;
}
/*
PQfinish(entry->conn);
entry->conn = NULL;
//UnregisterResourceReleaseCallback(cleanup_connection, entry);
+ hash_search(FSConnectionHash, entry->name, HASH_REMOVE, NULL);
}
}