Refactor postgresql_fdw.
authorShigeru Hanada <[email protected]>
Thu, 11 Nov 2010 05:44:04 +0000 (14:44 +0900)
committerShigeru Hanada <[email protected]>
Thu, 11 Nov 2010 05:44:04 +0000 (14:44 +0900)
Simplify GetConnection() in fsconnection.c with merging related functions.

contrib/postgresql_fdw/fsconnection.c

index a9d5732c4b0ad4bb3f645dbb708777fd21bc2744..f56b30eafd2ad9ae68514bac0ea2068c0da8e3d1 100644 (file)
@@ -24,8 +24,7 @@
 #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,
@@ -56,62 +55,79 @@ static HTAB *FSConnectionHash;
 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;
 }
@@ -144,109 +160,59 @@ check_conn_params(const char **keywords, const char **values)
           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;
 }
 
 /*
@@ -325,5 +291,6 @@ cleanup_connection(ResourceReleasePhase phase,
        PQfinish(entry->conn);
        entry->conn = NULL;
        //UnregisterResourceReleaseCallback(cleanup_connection, entry);
+       hash_search(FSConnectionHash, entry->name, HASH_REMOVE, NULL);
    }
 }