Please apply attached patch to contrib/dblink. It adds named persistent
authorBruce Momjian
Wed, 25 Jun 2003 01:10:15 +0000 (01:10 +0000)
committerBruce Momjian
Wed, 25 Jun 2003 01:10:15 +0000 (01:10 +0000)
connections to dblink.

Shridhar Daithanka

contrib/dblink/README.dblink
contrib/dblink/dblink.c
contrib/dblink/dblink.h
contrib/dblink/dblink.sql.in
contrib/dblink/doc/connection
contrib/dblink/doc/cursor
contrib/dblink/doc/execute
contrib/dblink/doc/query
contrib/dblink/expected/dblink.out
contrib/dblink/sql/dblink.sql

index af627901dc823a612061d09f2d45cdcc8438a3ce..7724aa9e9174a049cead08bf51a3f4beff7192ff 100644 (file)
@@ -4,8 +4,11 @@
  * Functions returning results from a remote database
  *
  * Joe Conway 
+ * And contributors:
+ * Darko Prenosil 
+ * Shridhar Daithankar 
  *
- * Copyright (c) 2001, 2002 by PostgreSQL Global Development Group
+ * Copyright (c) 2001, 2002, 2003 by PostgreSQL Global Development Group
  * ALL RIGHTS RESERVED;
  * 
  * Permission to use, copy, modify, and distribute this software and its
  *
  */
 
-Version 0.5 (25 August, 2002):
-  Major overhaul to work with new backend "table function" capability. Removed
-  dblink_strtok() and dblink_replace() functions because they are now
-  available as backend functions (split() and replace() respectively).
-  Tested under Linux (Red Hat 7.3) and PostgreSQL 7.3devel. This version
-  is no longer backwards portable to PostgreSQL 7.2.
+Version 0.6 (14 June, 2003):
+  Completely removed previously deprecated functions. Added ability
+  to create "named" persistent connections in addition to the single global
+  "unnamed" persistent connection.
+  Tested under Linux (Red Hat 9) and PostgreSQL 7.4devel.
 
 Release Notes:
+  Version 0.6
+    - functions deprecated in 0.5 have been removed
+    - added ability to create "named" persistent connections
   Version 0.5
     - dblink now supports use directly as a table function; this is the new
       preferred usage going forward
@@ -87,35 +92,51 @@ Installation:
      connection
      ------------
      dblink_connect(text) RETURNS text
-       - opens a connection that will persist for duration of current
+       - opens an unnamed connection that will persist for duration of
+         current backend or until it is disconnected
+     dblink_connect(text,text) RETURNS text
+       - opens a named connection that will persist for duration of current
          backend or until it is disconnected
      dblink_disconnect() RETURNS text
-       - disconnects a persistent connection
+       - disconnects the unnamed persistent connection
+     dblink_disconnect(text) RETURNS text
+       - disconnects a named persistent connection
 
      cursor
      ------------
      dblink_open(text,text) RETURNS text
-       - opens a cursor using connection already opened with dblink_connect()
-         that will persist for duration of current backend or until it is
-         closed
+       - opens a cursor using unnamed connection already opened with
+         dblink_connect() that will persist for duration of current backend
+         or until it is closed
+     dblink_open(text,text,text) RETURNS text
+       - opens a cursor using a named connection already opened with
+         dblink_connect() that will persist for duration of current backend
+         or until it is closed
      dblink_fetch(text, int) RETURNS setof record
-       - fetches data from an already opened cursor
+       - fetches data from an already opened cursor on the unnamed connection
+     dblink_fetch(text, text, int) RETURNS setof record
+       - fetches data from an already opened cursor on a named connection
      dblink_close(text) RETURNS text
-       - closes a cursor
+       - closes a cursor on the unnamed connection
+     dblink_close(text,text) RETURNS text
+       - closes a cursor on a named connection
 
      query
      ------------
      dblink(text,text) RETURNS setof record
-       - returns a set of results from remote SELECT query
-         (Note: comment out in dblink.sql to use deprecated version)
+       - returns a set of results from remote SELECT query; the first argument
+         is either a connection string, or the name of an already opened
+         persistant connection
      dblink(text) RETURNS setof record
-       - returns a set of results from remote SELECT query, using connection
-         already opened with dblink_connect()
+       - returns a set of results from remote SELECT query, using the unnamed
+         connection already opened with dblink_connect()
 
      execute
      ------------
      dblink_exec(text, text) RETURNS text
-       - executes an INSERT/UPDATE/DELETE query remotely
+       - executes an INSERT/UPDATE/DELETE query remotely; the first argument
+         is either a connection string, or the name of an already opened
+         persistant connection
      dblink_exec(text) RETURNS text
        - executes an INSERT/UPDATE/DELETE query remotely, using connection
          already opened with dblink_connect()
@@ -136,19 +157,6 @@ Installation:
        - builds an update statement using a local tuple, replacing the
          selection key field values with alternate supplied values
 
-  Not installed by default
-     deprecated
-     ------------
-     dblink(text,text) RETURNS setof int
-       - *DEPRECATED* returns a resource id for results from remote query
-         (Note: must uncomment in dblink.sql to use)
-     dblink_tok(int,int) RETURNS text
-       - *DEPRECATED* extracts and returns individual field results; used
-         only in conjunction with the *DEPRECATED* form of dblink
-         (Note: must uncomment in dblink.sql to use)
-     dblink_last_oid(int) RETURNS oid
-       - *DEPRECATED* returns the last inserted oid
-
 Documentation:
 
   Note: Parameters representing relation names must include double
index a8e9c5ab50e78c68563a24ae047a1c403c67d5bb..acddd1d46989d55a9f1c31b58bd3dd73ec257cec 100644 (file)
@@ -4,8 +4,11 @@
  * Functions returning results from a remote database
  *
  * Joe Conway 
+ * And contributors:
+ * Darko Prenosil 
+ * Shridhar Daithankar 
  *
- * Copyright (c) 2001, 2002 by PostgreSQL Global Development Group
+ * Copyright (c) 2001, 2002, 2003 by PostgreSQL Global Development Group
  * ALL RIGHTS RESERVED;
  *
  * Permission to use, copy, modify, and distribute this software and its
@@ -27,9 +30,7 @@
  *
  */
 #include "postgres.h"
-
 #include "libpq-fe.h"
-
 #include "fmgr.h"
 #include "funcapi.h"
 #include "access/tupdesc.h"
 #include "utils/array.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
+#include "utils/palloc.h"
+#include "utils/dynahash.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
 
 #include "dblink.h"
 
+typedef struct remoteConn
+{
+   PGconn *con;            /* Hold the remote connection */
+   bool remoteTrFlag;      /* Indicates whether or not a transaction
+                            * on remote database is in progress*/
+} remoteConn;
+
 /*
  * Internal declarations
  */
-static dblink_results *init_dblink_results(MemoryContext fn_mcxt);
+static remoteConn *getConnectionByName(const char *name);
+static HTAB *createConnHash(void);
+static bool createNewConnection(const char *name,remoteConn *con);
+static void deleteConnection(const char *name);
 static char **get_pkey_attnames(Oid relid, int16 *numatts);
 static char *get_sql_insert(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals, char **tgt_pkattvals);
 static char *get_sql_delete(Oid relid, int16 *pkattnums, int16 pknumatts, char **tgt_pkattvals);
@@ -67,17 +82,32 @@ static char *quote_ident_cstr(char *rawstr);
 static int16 get_attnum_pk_pos(int16 *pkattnums, int16 pknumatts, int16 key);
 static HeapTuple get_tuple_of_interest(Oid relid, int16 *pkattnums, int16 pknumatts, char **src_pkattvals);
 static Oid get_relid_from_relname(text *relname_text);
-static dblink_results *get_res_ptr(int32 res_id_index);
-static void append_res_ptr(dblink_results * results);
-static void remove_res_ptr(dblink_results * results);
 static TupleDesc pgresultGetTupleDesc(PGresult *res);
 static char *generate_relation_name(Oid relid);
 
 /* Global */
-List      *res_id = NIL;
-int            res_id_index = 0;
-PGconn    *persistent_conn = NULL;
+List   *res_id = NIL;
+int        res_id_index = 0;
+PGconn *persistent_conn = NULL;
+static HTAB *remoteConnHash=NULL;
+
+/* 
+Following is list that holds multiple remote connections.
+Calling convention of each dblink function changes to accept
+connection name as the first parameter. The connection list is 
+much like ecpg e.g. a mapping between a name and a PGconn object.
+*/
+
+typedef struct remoteConnHashEnt
+{
+   char        name[NAMEDATALEN];
+   remoteConn *rcon;
+} remoteConnHashEnt;
+
+/* initial number of connection hashes */
+#define NUMCONN 16
 
+/* general utility */
 #define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstrp)))
 #define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
 #define xpfree(var_) \
@@ -88,6 +118,41 @@ PGconn     *persistent_conn = NULL;
            var_ = NULL; \
        } \
    } while (0)
+#define DBLINK_RES_ERROR(p1, p2) \
+   do { \
+           msg = pstrdup(PQerrorMessage(conn)); \
+           if (res) \
+               PQclear(res); \
+           elog(ERROR, "%s: %s: %s", p1, p2, msg); \
+   } while (0)
+#define DBLINK_CONN_NOT_AVAIL(p1) \
+   do { \
+       if(conname) \
+           elog(ERROR, "%s: connection %s not available", p1, conname); \
+       else \
+           elog(ERROR, "%s: connection not available", p1); \
+   } while (0)
+#define DBLINK_GET_CONN(p1) \
+   do { \
+           char *conname_or_str = GET_STR(PG_GETARG_TEXT_P(0)); \
+           rcon = getConnectionByName(conname_or_str); \
+           if(rcon) \
+           { \
+               conn = rcon->con; \
+               freeconn = false; \
+           } \
+           else \
+           { \
+               connstr = conname_or_str; \
+               conn = PQconnectdb(connstr); \
+               if (PQstatus(conn) == CONNECTION_BAD) \
+               { \
+                   msg = pstrdup(PQerrorMessage(conn)); \
+                   PQfinish(conn); \
+                   elog(ERROR, "%s: connection error: %s", p1, msg); \
+               } \
+           } \
+   } while (0)
 
 
 /*
@@ -97,28 +162,52 @@ PG_FUNCTION_INFO_V1(dblink_connect);
 Datum
 dblink_connect(PG_FUNCTION_ARGS)
 {
-   char       *connstr = GET_STR(PG_GETARG_TEXT_P(0));
+   char       *connstr = NULL;
+   char       *connname = NULL;
    char       *msg;
-   text       *result_text;
    MemoryContext oldcontext;
+   PGconn     *conn = NULL;
+   remoteConn *rcon = NULL;
 
-   if (persistent_conn != NULL)
-       PQfinish(persistent_conn);
+   if(PG_NARGS()==2)
+   {
+       connstr = GET_STR(PG_GETARG_TEXT_P(1));
+       connname = GET_STR(PG_GETARG_TEXT_P(0));
+   }
+   else if(PG_NARGS()==1)
+       connstr = GET_STR(PG_GETARG_TEXT_P(0));
 
    oldcontext = MemoryContextSwitchTo(TopMemoryContext);
-   persistent_conn = PQconnectdb(connstr);
+
+   if(connname)
+       rcon=(remoteConn *) palloc(sizeof(remoteConn));
+   conn = PQconnectdb(connstr);
+
    MemoryContextSwitchTo(oldcontext);
 
-   if (PQstatus(persistent_conn) == CONNECTION_BAD)
+   if (PQstatus(conn) == CONNECTION_BAD)
    {
-       msg = pstrdup(PQerrorMessage(persistent_conn));
-       PQfinish(persistent_conn);
-       persistent_conn = NULL;
+       msg = pstrdup(PQerrorMessage(conn));
+       PQfinish(conn);
+       if(rcon)
+           pfree(rcon);
        elog(ERROR, "dblink_connect: connection error: %s", msg);
    }
 
-   result_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum("OK")));
-   PG_RETURN_TEXT_P(result_text);
+   if(connname)
+   {
+       rcon->con = conn;
+       if(createNewConnection(connname, rcon) == false)
+       {
+           PQfinish(conn);
+           pfree(rcon);
+           elog(ERROR, "dblink_connect: cannot save named connection");
+       }
+   }
+   else
+       persistent_conn = conn;
+
+   PG_RETURN_TEXT_P(GET_TEXT("OK"));
 }
 
 /*
@@ -128,15 +217,37 @@ PG_FUNCTION_INFO_V1(dblink_disconnect);
 Datum
 dblink_disconnect(PG_FUNCTION_ARGS)
 {
-   text       *result_text;
+   char       *str = NULL;
+   remoteConn *rcon = NULL;
+   PGconn     *conn = NULL;
+
+   if (PG_NARGS() ==1 )
+   {
+       str = GET_STR(PG_GETARG_TEXT_P(0));
+       rcon = getConnectionByName(str);
+       if (rcon)
+           conn = rcon->con;
+   }
+   else
+       conn = persistent_conn;
 
-   if (persistent_conn != NULL)
-       PQfinish(persistent_conn);
+   if (!conn)
+   {
+       if (str)
+           elog(ERROR,"dblink_disconnect: connection named \"%s\" not found",
+                                                                        str);
+       else
+           elog(ERROR,"dblink_disconnect: connection not found");
+   }
 
-   persistent_conn = NULL;
+   PQfinish(conn);
+   if (rcon)
+   {
+       deleteConnection(str);
+       pfree(rcon);
+   }
 
-   result_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum("OK")));
-   PG_RETURN_TEXT_P(result_text);
+   PG_RETURN_TEXT_P(GET_TEXT("OK"));
 }
 
 /*
@@ -149,27 +260,35 @@ dblink_open(PG_FUNCTION_ARGS)
    char       *msg;
    PGresult   *res = NULL;
    PGconn     *conn = NULL;
-   text       *result_text;
-   char       *curname = GET_STR(PG_GETARG_TEXT_P(0));
-   char       *sql = GET_STR(PG_GETARG_TEXT_P(1));
+   char       *curname = NULL;
+   char       *sql = NULL;
+   char       *conname = NULL;
    StringInfo  str = makeStringInfo();
+   remoteConn *rcon = NULL;
 
-   if (persistent_conn != NULL)
+   if(PG_NARGS() == 2)
+   {
+       curname = GET_STR(PG_GETARG_TEXT_P(0));
+       sql = GET_STR(PG_GETARG_TEXT_P(1));
        conn = persistent_conn;
-   else
-       elog(ERROR, "dblink_open: no connection available");
+   }
+   else if(PG_NARGS() == 3)
+   {
+       conname = GET_STR(PG_GETARG_TEXT_P(0));
+       curname = GET_STR(PG_GETARG_TEXT_P(1));
+       sql = GET_STR(PG_GETARG_TEXT_P(2));
+       rcon = getConnectionByName(conname);
+       if (rcon)
+           conn = rcon->con;
+   }
+
+   if (!conn)
+       DBLINK_CONN_NOT_AVAIL("dblink_open");
 
    res = PQexec(conn, "BEGIN");
    if (PQresultStatus(res) != PGRES_COMMAND_OK)
-   {
-       msg = pstrdup(PQerrorMessage(conn));
-       PQclear(res);
+       DBLINK_RES_ERROR("dblink_open", "begin error");
 
-       PQfinish(conn);
-       persistent_conn = NULL;
-
-       elog(ERROR, "dblink_open: begin error: %s", msg);
-   }
    PQclear(res);
 
    appendStringInfo(str, "DECLARE %s CURSOR FOR %s", curname, sql);
@@ -177,19 +296,11 @@ dblink_open(PG_FUNCTION_ARGS)
    if (!res ||
        (PQresultStatus(res) != PGRES_COMMAND_OK &&
         PQresultStatus(res) != PGRES_TUPLES_OK))
-   {
-       msg = pstrdup(PQerrorMessage(conn));
-
-       PQclear(res);
-
-       PQfinish(conn);
-       persistent_conn = NULL;
+       DBLINK_RES_ERROR("dblink_open", "sql error");
 
-       elog(ERROR, "dblink: sql error: %s", msg);
-   }
+   PQclear(res);
 
-   result_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum("OK")));
-   PG_RETURN_TEXT_P(result_text);
+   PG_RETURN_TEXT_P(GET_TEXT("OK"));
 }
 
 /*
@@ -201,49 +312,46 @@ dblink_close(PG_FUNCTION_ARGS)
 {
    PGconn     *conn = NULL;
    PGresult   *res = NULL;
-   char       *curname = GET_STR(PG_GETARG_TEXT_P(0));
+   char       *curname = NULL;
+   char       *conname = NULL;
    StringInfo  str = makeStringInfo();
-   text       *result_text;
    char       *msg;
+   remoteConn *rcon = NULL;
 
-   if (persistent_conn != NULL)
+   if (PG_NARGS() == 1)
+   {
+       curname = GET_STR(PG_GETARG_TEXT_P(0));
        conn = persistent_conn;
-   else
-       elog(ERROR, "dblink_close: no connection available");
+   }
+   else if (PG_NARGS()==2)
+   {
+       conname = GET_STR(PG_GETARG_TEXT_P(0));
+       curname = GET_STR(PG_GETARG_TEXT_P(1));
+       rcon = getConnectionByName(conname);
+       if(rcon)
+           conn = rcon->con;
+   }
+
+   if (!conn)
+       DBLINK_CONN_NOT_AVAIL("dblink_close");
 
    appendStringInfo(str, "CLOSE %s", curname);
 
    /* close the cursor */
    res = PQexec(conn, str->data);
    if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
-   {
-       msg = pstrdup(PQerrorMessage(conn));
-       PQclear(res);
-
-       PQfinish(persistent_conn);
-       persistent_conn = NULL;
-
-       elog(ERROR, "dblink_close: sql error: %s", msg);
-   }
+       DBLINK_RES_ERROR("dblink_close", "sql error");
 
    PQclear(res);
 
    /* commit the transaction */
    res = PQexec(conn, "COMMIT");
    if (PQresultStatus(res) != PGRES_COMMAND_OK)
-   {
-       msg = pstrdup(PQerrorMessage(conn));
-       PQclear(res);
-
-       PQfinish(persistent_conn);
-       persistent_conn = NULL;
+       DBLINK_RES_ERROR("dblink_close", "commit error");
 
-       elog(ERROR, "dblink_close: commit error: %s", msg);
-   }
    PQclear(res);
 
-   result_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum("OK")));
-   PG_RETURN_TEXT_P(result_text);
+   PG_RETURN_TEXT_P(GET_TEXT("OK"));
 }
 
 /*
@@ -262,6 +370,8 @@ dblink_fetch(PG_FUNCTION_ARGS)
    char       *msg;
    PGresult   *res = NULL;
    MemoryContext oldcontext;
+   char       *conname = NULL;
+   remoteConn *rcon=NULL;
 
    /* stuff done only on the first call of the function */
    if (SRF_IS_FIRSTCALL())
@@ -271,8 +381,28 @@ dblink_fetch(PG_FUNCTION_ARGS)
        Oid         funcid = fcinfo->flinfo->fn_oid;
        PGconn     *conn = NULL;
        StringInfo  str = makeStringInfo();
-       char       *curname = GET_STR(PG_GETARG_TEXT_P(0));
-       int         howmany = PG_GETARG_INT32(1);
+       char       *curname = NULL;
+       int         howmany = 0;
+
+       if (PG_NARGS() == 3)
+       {
+           conname = GET_STR(PG_GETARG_TEXT_P(0));
+           curname = GET_STR(PG_GETARG_TEXT_P(1));
+           howmany = PG_GETARG_INT32(2);
+
+           rcon = getConnectionByName(conname);
+           if(rcon)
+               conn = rcon->con;
+       }
+       else if (PG_NARGS() == 2)
+       {
+           curname = GET_STR(PG_GETARG_TEXT_P(0));
+           howmany = PG_GETARG_INT32(1);
+           conn = persistent_conn;
+       }
+
+       if(!conn)
+           DBLINK_CONN_NOT_AVAIL("dblink_fetch");
 
        /* create a function context for cross-call persistence */
        funcctx = SRF_FIRSTCALL_INIT();
@@ -283,11 +413,6 @@ dblink_fetch(PG_FUNCTION_ARGS)
         */
        oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
-       if (persistent_conn != NULL)
-           conn = persistent_conn;
-       else
-           elog(ERROR, "dblink_fetch: no connection available");
-
        appendStringInfo(str, "FETCH %d FROM %s", howmany, curname);
 
        res = PQexec(conn, str->data);
@@ -295,19 +420,13 @@ dblink_fetch(PG_FUNCTION_ARGS)
            (PQresultStatus(res) != PGRES_COMMAND_OK &&
             PQresultStatus(res) != PGRES_TUPLES_OK))
        {
-           msg = pstrdup(PQerrorMessage(conn));
-           PQclear(res);
-
-           PQfinish(persistent_conn);
-           persistent_conn = NULL;
-
-           elog(ERROR, "dblink_fetch: sql error: %s", msg);
+           DBLINK_RES_ERROR("dblink_fetch", "sql error");
        }
        else if (PQresultStatus(res) == PGRES_COMMAND_OK)
        {
            /* cursor does not exist - closed already or bad name */
            PQclear(res);
-           elog(ERROR, "dblink_fetch: cursor %s does not exist", curname);
+           elog(ERROR, "dblink_fetch: cursor not found: %s", curname);
        }
 
        funcctx->max_calls = PQntuples(res);
@@ -380,8 +499,8 @@ dblink_fetch(PG_FUNCTION_ARGS)
        SRF_RETURN_NEXT(funcctx, result);
    }
    else
-/* do when there is no more left */
    {
+       /* do when there is no more left */
        PQclear(res);
        SRF_RETURN_DONE(funcctx);
    }
@@ -405,6 +524,7 @@ dblink_record(PG_FUNCTION_ARGS)
    bool        is_sql_cmd = false;
    char       *sql_cmd_status = NULL;
    MemoryContext oldcontext;
+   bool        freeconn = true;
 
    /* stuff done only on the first call of the function */
    if (SRF_IS_FIRSTCALL())
@@ -415,6 +535,8 @@ dblink_record(PG_FUNCTION_ARGS)
        PGconn     *conn = NULL;
        char       *connstr = NULL;
        char       *sql = NULL;
+       char       *conname = NULL;
+       remoteConn *rcon=NULL;
 
        /* create a function context for cross-call persistence */
        funcctx = SRF_FIRSTCALL_INIT();
@@ -425,70 +547,51 @@ dblink_record(PG_FUNCTION_ARGS)
         */
        oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
-       if (fcinfo->nargs == 2)
+       if (PG_NARGS() == 2)
        {
-           connstr = GET_STR(PG_GETARG_TEXT_P(0));
+           DBLINK_GET_CONN("dblink");
            sql = GET_STR(PG_GETARG_TEXT_P(1));
-
-           conn = PQconnectdb(connstr);
-           if (PQstatus(conn) == CONNECTION_BAD)
-           {
-               msg = pstrdup(PQerrorMessage(conn));
-               PQfinish(conn);
-               elog(ERROR, "dblink: connection error: %s", msg);
-           }
        }
-       else if (fcinfo->nargs == 1)
+       else if (PG_NARGS() == 1)
        {
+           conn = persistent_conn;
            sql = GET_STR(PG_GETARG_TEXT_P(0));
-
-           if (persistent_conn != NULL)
-               conn = persistent_conn;
-           else
-               elog(ERROR, "dblink: no connection available");
        }
        else
            elog(ERROR, "dblink: wrong number of arguments");
 
+       if(!conn)
+           DBLINK_CONN_NOT_AVAIL("dblink_record");
+
        res = PQexec(conn, sql);
        if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK))
+           DBLINK_RES_ERROR("dblink", "sql error");
+
+       if (PQresultStatus(res) == PGRES_COMMAND_OK)
        {
-           msg = pstrdup(PQerrorMessage(conn));
-           PQclear(res);
-           PQfinish(conn);
-           if (fcinfo->nargs == 1)
-               persistent_conn = NULL;
+           is_sql_cmd = true;
+
+           /* need a tuple descriptor representing one TEXT column */
+           tupdesc = CreateTemplateTupleDesc(1, false);
+           TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status",
+                              TEXTOID, -1, 0, false);
 
-           elog(ERROR, "dblink: sql error: %s", msg);
+           /*
+            * and save a copy of the command status string to return
+            * as our result tuple
+            */
+           sql_cmd_status = PQcmdStatus(res);
+           funcctx->max_calls = 1;
        }
        else
-       {
-           if (PQresultStatus(res) == PGRES_COMMAND_OK)
-           {
-               is_sql_cmd = true;
-
-               /* need a tuple descriptor representing one TEXT column */
-               tupdesc = CreateTemplateTupleDesc(1, false);
-               TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status",
-                                  TEXTOID, -1, 0, false);
-
-               /*
-                * and save a copy of the command status string to return
-                * as our result tuple
-                */
-               sql_cmd_status = PQcmdStatus(res);
-               funcctx->max_calls = 1;
-           }
-           else
-               funcctx->max_calls = PQntuples(res);
+           funcctx->max_calls = PQntuples(res);
 
-           /* got results, keep track of them */
-           funcctx->user_fctx = res;
+       /* got results, keep track of them */
+       funcctx->user_fctx = res;
 
-           /* if needed, close the connection to the database and cleanup */
-           if (fcinfo->nargs == 2)
-               PQfinish(conn);
-       }
+       /* if needed, close the connection to the database and cleanup */
+       if (freeconn && PG_NARGS() == 2)
+           PQfinish(conn);
 
        /* fast track when no results */
        if (funcctx->max_calls < 1)
@@ -567,8 +670,8 @@ dblink_record(PG_FUNCTION_ARGS)
        SRF_RETURN_NEXT(funcctx, result);
    }
    else
-/* do when there is no more left */
    {
+       /* do when there is no more left */
        PQclear(res);
        SRF_RETURN_DONE(funcctx);
    }
@@ -583,272 +686,62 @@ dblink_exec(PG_FUNCTION_ARGS)
 {
    char       *msg;
    PGresult   *res = NULL;
-   char       *sql_cmd_status = NULL;
+   text       *sql_cmd_status = NULL;
    TupleDesc   tupdesc = NULL;
-   text       *result_text;
    PGconn     *conn = NULL;
    char       *connstr = NULL;
    char       *sql = NULL;
+   char       *conname = NULL;
+   remoteConn *rcon=NULL;
+   bool        freeconn = true;
 
-   if (fcinfo->nargs == 2)
+   if (PG_NARGS() == 2)
    {
-       connstr = GET_STR(PG_GETARG_TEXT_P(0));
+       DBLINK_GET_CONN("dblink_exec");
        sql = GET_STR(PG_GETARG_TEXT_P(1));
-
-       conn = PQconnectdb(connstr);
-       if (PQstatus(conn) == CONNECTION_BAD)
-       {
-           msg = pstrdup(PQerrorMessage(conn));
-           PQfinish(conn);
-           elog(ERROR, "dblink_exec: connection error: %s", msg);
-       }
    }
-   else if (fcinfo->nargs == 1)
+   else if (PG_NARGS() == 1)
    {
+       conn = persistent_conn;
        sql = GET_STR(PG_GETARG_TEXT_P(0));
-
-       if (persistent_conn != NULL)
-           conn = persistent_conn;
-       else
-           elog(ERROR, "dblink_exec: no connection available");
    }
    else
        elog(ERROR, "dblink_exec: wrong number of arguments");
 
+   if(!conn)
+       DBLINK_CONN_NOT_AVAIL("dblink_exec");
 
    res = PQexec(conn, sql);
-   if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK))
+   if (!res ||
+       (PQresultStatus(res) != PGRES_COMMAND_OK &&
+        PQresultStatus(res) != PGRES_TUPLES_OK))
+       DBLINK_RES_ERROR("dblink_exec", "sql error");
+
+   if (PQresultStatus(res) == PGRES_COMMAND_OK)
    {
-       msg = pstrdup(PQerrorMessage(conn));
-       PQclear(res);
-       PQfinish(conn);
-       if (fcinfo->nargs == 1)
-           persistent_conn = NULL;
+       /* need a tuple descriptor representing one TEXT column */
+       tupdesc = CreateTemplateTupleDesc(1, false);
+       TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status",
+                          TEXTOID, -1, 0, false);
 
-       elog(ERROR, "dblink_exec: sql error: %s", msg);
+       /*
+        * and save a copy of the command status string to return as
+        * our result tuple
+        */
+       sql_cmd_status = GET_TEXT(PQcmdStatus(res));
    }
    else
-   {
-       if (PQresultStatus(res) == PGRES_COMMAND_OK)
-       {
-           /* need a tuple descriptor representing one TEXT column */
-           tupdesc = CreateTemplateTupleDesc(1, false);
-           TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status",
-                              TEXTOID, -1, 0, false);
+       elog(ERROR, "dblink_exec: queries returning results not allowed");
 
-           /*
-            * and save a copy of the command status string to return as
-            * our result tuple
-            */
-           sql_cmd_status = PQcmdStatus(res);
-       }
-       else
-           elog(ERROR, "dblink_exec: queries returning results not allowed");
-   }
    PQclear(res);
 
    /* if needed, close the connection to the database and cleanup */
-   if (fcinfo->nargs == 2)
+   if (freeconn && fcinfo->nargs == 2)
        PQfinish(conn);
 
-   result_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(sql_cmd_status)));
-   PG_RETURN_TEXT_P(result_text);
+   PG_RETURN_TEXT_P(sql_cmd_status);
 }
 
-/*
- * Note: this original version of dblink is DEPRECATED;
- * it *will* be removed in favor of the new version on next release
- */
-PG_FUNCTION_INFO_V1(dblink);
-Datum
-dblink(PG_FUNCTION_ARGS)
-{
-   PGconn     *conn = NULL;
-   PGresult   *res = NULL;
-   dblink_results *results;
-   char       *optstr;
-   char       *sqlstatement;
-   char       *execstatement;
-   char       *msg;
-   int         ntuples = 0;
-   ReturnSetInfo *rsi;
-
-   if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo))
-       elog(ERROR, "dblink: function called in context that does not accept a set result");
-
-   optstr = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(PG_GETARG_TEXT_P(0))));
-   sqlstatement = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(PG_GETARG_TEXT_P(1))));
-
-   if (fcinfo->flinfo->fn_extra == NULL)
-   {
-
-       conn = PQconnectdb(optstr);
-       if (PQstatus(conn) == CONNECTION_BAD)
-       {
-           msg = pstrdup(PQerrorMessage(conn));
-           PQfinish(conn);
-           elog(ERROR, "dblink: connection error: %s", msg);
-       }
-
-       execstatement = (char *) palloc(strlen(sqlstatement) + 1);
-       if (execstatement != NULL)
-       {
-           strcpy(execstatement, sqlstatement);
-           strcat(execstatement, "\0");
-       }
-       else
-           elog(ERROR, "dblink: insufficient memory");
-
-       res = PQexec(conn, execstatement);
-       if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK))
-       {
-           msg = pstrdup(PQerrorMessage(conn));
-           PQclear(res);
-           PQfinish(conn);
-           elog(ERROR, "dblink: sql error: %s", msg);
-       }
-       else
-       {
-           /*
-            * got results, start fetching them
-            */
-           ntuples = PQntuples(res);
-
-           /*
-            * increment resource index
-            */
-           res_id_index++;
-
-           results = init_dblink_results(fcinfo->flinfo->fn_mcxt);
-           results->tup_num = 0;
-           results->res_id_index = res_id_index;
-           results->res = res;
-
-           /*
-            * Append node to res_id to hold pointer to results. Needed by
-            * dblink_tok to access the data
-            */
-           append_res_ptr(results);
-
-           /*
-            * save pointer to results for the next function manager call
-            */
-           fcinfo->flinfo->fn_extra = (void *) results;
-
-           /* close the connection to the database and cleanup */
-           PQfinish(conn);
-
-           rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-           rsi->isDone = ExprMultipleResult;
-
-           PG_RETURN_INT32(res_id_index);
-       }
-   }
-   else
-   {
-       /*
-        * check for more results
-        */
-       results = fcinfo->flinfo->fn_extra;
-
-       results->tup_num++;
-       res_id_index = results->res_id_index;
-       ntuples = PQntuples(results->res);
-
-       if (results->tup_num < ntuples)
-       {
-           /*
-            * fetch them if available
-            */
-
-           rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-           rsi->isDone = ExprMultipleResult;
-
-           PG_RETURN_INT32(res_id_index);
-       }
-       else
-       {
-           /*
-            * or if no more, clean things up
-            */
-           results = fcinfo->flinfo->fn_extra;
-
-           remove_res_ptr(results);
-           PQclear(results->res);
-           pfree(results);
-           fcinfo->flinfo->fn_extra = NULL;
-
-           rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-           rsi->isDone = ExprEndResult;
-
-           PG_RETURN_NULL();
-       }
-   }
-   PG_RETURN_NULL();
-}
-
-/*
- * Note: dblink_tok is DEPRECATED;
- * it *will* be removed in favor of the new version on next release
- *
- * dblink_tok
- * parse dblink output string
- * return fldnum item (0 based)
- * based on provided field separator
- */
-PG_FUNCTION_INFO_V1(dblink_tok);
-Datum
-dblink_tok(PG_FUNCTION_ARGS)
-{
-   dblink_results *results;
-   int         fldnum;
-   text       *result_text;
-   char       *result;
-   int         nfields = 0;
-   int         text_len = 0;
-
-   results = get_res_ptr(PG_GETARG_INT32(0));
-   if (results == NULL)
-   {
-       if (res_id != NIL)
-       {
-           freeList(res_id);
-           res_id = NIL;
-           res_id_index = 0;
-       }
-
-       elog(ERROR, "dblink_tok: function called with invalid resource id");
-   }
-
-   fldnum = PG_GETARG_INT32(1);
-   if (fldnum < 0)
-       elog(ERROR, "dblink_tok: field number < 0 not permitted");
-
-   nfields = PQnfields(results->res);
-   if (fldnum > (nfields - 1))
-       elog(ERROR, "dblink_tok: field number %d does not exist", fldnum);
-
-   if (PQgetisnull(results->res, results->tup_num, fldnum) == 1)
-       PG_RETURN_NULL();
-   else
-   {
-       text_len = PQgetlength(results->res, results->tup_num, fldnum);
-
-       result = (char *) palloc(text_len + 1);
-
-       if (result != NULL)
-       {
-           strcpy(result, PQgetvalue(results->res, results->tup_num, fldnum));
-           strcat(result, "\0");
-       }
-       else
-           elog(ERROR, "dblink: insufficient memory");
-
-       result_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(result)));
-
-       PG_RETURN_TEXT_P(result_text);
-   }
-}
 
 /*
  * dblink_get_pkey
@@ -923,7 +816,7 @@ dblink_get_pkey(PG_FUNCTION_ARGS)
            funcctx->user_fctx = results;
        }
        else
-/* fast track when no results */
+           /* fast track when no results */
            SRF_RETURN_DONE(funcctx);
 
        MemoryContextSwitchTo(oldcontext);
@@ -965,37 +858,10 @@ dblink_get_pkey(PG_FUNCTION_ARGS)
        SRF_RETURN_NEXT(funcctx, result);
    }
    else
-/* do when there is no more left */
-       SRF_RETURN_DONE(funcctx);
-}
-
-/*
- * Note: dblink_last_oid is DEPRECATED;
- * it *will* be removed on next release
- *
- * dblink_last_oid
- * return last inserted oid
- */
-PG_FUNCTION_INFO_V1(dblink_last_oid);
-Datum
-dblink_last_oid(PG_FUNCTION_ARGS)
-{
-   dblink_results *results;
-
-   results = get_res_ptr(PG_GETARG_INT32(0));
-   if (results == NULL)
    {
-       if (res_id != NIL)
-       {
-           freeList(res_id);
-           res_id = NIL;
-           res_id_index = 0;
-       }
-
-       elog(ERROR, "dblink_tok: function called with invalid resource id");
+       /* do when there is no more left */
+       SRF_RETURN_DONE(funcctx);
    }
-
-   PG_RETURN_OID(PQoidValue(results->res));
 }
 
 
@@ -1043,7 +909,6 @@ dblink_build_sql_insert(PG_FUNCTION_ARGS)
    int         i;
    char       *ptr;
    char       *sql;
-   text       *sql_text;
    int16       typlen;
    bool        typbyval;
    char        typalign;
@@ -1138,15 +1003,10 @@ dblink_build_sql_insert(PG_FUNCTION_ARGS)
     */
    sql = get_sql_insert(relid, pkattnums, pknumatts, src_pkattvals, tgt_pkattvals);
 
-   /*
-    * Make it into TEXT for return to the client
-    */
-   sql_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(sql)));
-
    /*
     * And send it
     */
-   PG_RETURN_TEXT_P(sql_text);
+   PG_RETURN_TEXT_P(GET_TEXT(sql));
 }
 
 
@@ -1182,7 +1042,6 @@ dblink_build_sql_delete(PG_FUNCTION_ARGS)
    int         i;
    char       *ptr;
    char       *sql;
-   text       *sql_text;
    int16       typlen;
    bool        typbyval;
    char        typalign;
@@ -1246,15 +1105,10 @@ dblink_build_sql_delete(PG_FUNCTION_ARGS)
     */
    sql = get_sql_delete(relid, pkattnums, pknumatts, tgt_pkattvals);
 
-   /*
-    * Make it into TEXT for return to the client
-    */
-   sql_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(sql)));
-
    /*
     * And send it
     */
-   PG_RETURN_TEXT_P(sql_text);
+   PG_RETURN_TEXT_P(GET_TEXT(sql));
 }
 
 
@@ -1299,7 +1153,6 @@ dblink_build_sql_update(PG_FUNCTION_ARGS)
    int         i;
    char       *ptr;
    char       *sql;
-   text       *sql_text;
    int16       typlen;
    bool        typbyval;
    char        typalign;
@@ -1394,15 +1247,10 @@ dblink_build_sql_update(PG_FUNCTION_ARGS)
     */
    sql = get_sql_update(relid, pkattnums, pknumatts, src_pkattvals, tgt_pkattvals);
 
-   /*
-    * Make it into TEXT for return to the client
-    */
-   sql_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(sql)));
-
    /*
     * And send it
     */
-   PG_RETURN_TEXT_P(sql_text);
+   PG_RETURN_TEXT_P(GET_TEXT(sql));
 }
 
 /*
@@ -1415,10 +1263,7 @@ PG_FUNCTION_INFO_V1(dblink_current_query);
 Datum
 dblink_current_query(PG_FUNCTION_ARGS)
 {
-   text       *result_text;
-
-   result_text = DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(debug_query_string)));
-   PG_RETURN_TEXT_P(result_text);
+   PG_RETURN_TEXT_P(GET_TEXT(debug_query_string));
 }
 
 
@@ -1427,29 +1272,6 @@ dblink_current_query(PG_FUNCTION_ARGS)
  */
 
 
-/*
- * init_dblink_results
- *  - create an empty dblink_results data structure
- */
-static dblink_results *
-init_dblink_results(MemoryContext fn_mcxt)
-{
-   MemoryContext oldcontext;
-   dblink_results *retval;
-
-   oldcontext = MemoryContextSwitchTo(fn_mcxt);
-
-   retval = (dblink_results *) palloc0(sizeof(dblink_results));
-
-   retval->tup_num = -1;
-   retval->res_id_index = -1;
-   retval->res = NULL;
-
-   MemoryContextSwitchTo(oldcontext);
-
-   return retval;
-}
-
 /*
  * get_pkey_attnames
  *
@@ -1488,7 +1310,10 @@ get_pkey_attnames(Oid relid, int16 *numatts)
        /* we're only interested if it is the primary key */
        if (index->indisprimary == TRUE)
        {
-           *numatts = index->indnatts;
+           i = 0;
+           while (index->indkey[i++] != 0)
+               (*numatts)++;
+
            if (*numatts > 0)
            {
                result = (char **) palloc(*numatts * sizeof(char *));
@@ -1907,52 +1732,6 @@ get_relid_from_relname(text *relname_text)
    return relid;
 }
 
-static dblink_results *
-get_res_ptr(int32 res_id_index)
-{
-   List       *ptr;
-
-   /*
-    * short circuit empty list
-    */
-   if (res_id == NIL)
-       return NULL;
-
-   /*
-    * OK, should be good to go
-    */
-   foreach(ptr, res_id)
-   {
-       dblink_results *this_res_id = (dblink_results *) lfirst(ptr);
-
-       if (this_res_id->res_id_index == res_id_index)
-           return this_res_id;
-   }
-   return NULL;
-}
-
-/*
- * Add node to global List res_id
- */
-static void
-append_res_ptr(dblink_results * results)
-{
-   res_id = lappend(res_id, results);
-}
-
-/*
- * Remove node from global List
- * using res_id_index
- */
-static void
-remove_res_ptr(dblink_results * results)
-{
-   res_id = lremove(results, res_id);
-
-   if (res_id == NIL)
-       res_id_index = 0;
-}
-
 static TupleDesc
 pgresultGetTupleDesc(PGresult *res)
 {
@@ -2039,3 +1818,91 @@ generate_relation_name(Oid relid)
 
    return result;
 }
+
+
+static remoteConn *
+getConnectionByName(const char *name)
+{
+   remoteConnHashEnt  *hentry;
+   char                key[NAMEDATALEN];
+
+   if(!remoteConnHash)
+       remoteConnHash=createConnHash();
+
+   MemSet(key, 0, NAMEDATALEN);
+   snprintf(key, NAMEDATALEN - 1, "%s", name);
+   hentry = (remoteConnHashEnt*) hash_search(remoteConnHash,
+                                             key, HASH_FIND, NULL);
+
+   if(hentry)
+       return(hentry->rcon);
+
+   return(NULL);
+}
+
+static HTAB *
+createConnHash(void)
+{
+   HASHCTL ctl;
+   HTAB   *ptr;
+
+   ctl.keysize = NAMEDATALEN;
+   ctl.entrysize = sizeof(remoteConnHashEnt);
+
+   ptr=hash_create("Remote Con hash", NUMCONN, &ctl, HASH_ELEM);
+
+   if(!ptr)
+       elog(ERROR,"Can not create connections hash table. Out of memory");
+
+   return(ptr);
+}
+
+static bool
+createNewConnection(const char *name, remoteConn *con)
+{
+   remoteConnHashEnt  *hentry;
+   bool                found;
+   char                key[NAMEDATALEN];
+
+   if(!remoteConnHash)
+       remoteConnHash=createConnHash();
+
+   MemSet(key, 0, NAMEDATALEN);
+   snprintf(key, NAMEDATALEN - 1, "%s", name);
+   hentry = (remoteConnHashEnt *) hash_search(remoteConnHash, key,
+                                              HASH_ENTER, &found);
+
+   if(!hentry)
+       elog(ERROR, "failed to create connection");
+
+   if(found)
+   {
+       elog(NOTICE, "cannot use a connection name more than once");
+       return false;
+   }
+
+   hentry->rcon = con;
+   strncpy(hentry->name, name, NAMEDATALEN - 1);
+
+   return true;
+}
+
+static void
+deleteConnection(const char *name)
+{
+   remoteConnHashEnt  *hentry;
+   bool                found;
+   char                key[NAMEDATALEN];
+
+   if(!remoteConnHash)
+       remoteConnHash=createConnHash();
+
+   MemSet(key, 0, NAMEDATALEN);
+   snprintf(key, NAMEDATALEN - 1, "%s", name);
+
+   hentry = (remoteConnHashEnt *) hash_search(remoteConnHash,
+                                              key, HASH_REMOVE, &found);
+
+   if(!hentry)
+       elog(WARNING,"Trying to delete a connection that does not exist");
+}
index 3e9119f81ace347455f03cb7656639bb8c19ef50..4da345cbaedb58caba63ae80e523f88adab184ae 100644 (file)
@@ -4,8 +4,11 @@
  * Functions returning results from a remote database
  *
  * Joe Conway 
+ * And contributors:
+ * Darko Prenosil 
+ * Shridhar Daithankar 
  *
- * Copyright (c) 2001, 2002 by PostgreSQL Global Development Group
+ * Copyright (c) 2001, 2002, 2003 by PostgreSQL Global Development Group
  * ALL RIGHTS RESERVED;
  *
  * Permission to use, copy, modify, and distribute this software and its
 #ifndef DBLINK_H
 #define DBLINK_H
 
-/*
- * This struct holds the results of the remote query.
- * Use fn_extra to hold a pointer to it across calls
- */
-typedef struct
-{
-   /*
-    * last tuple number accessed
-    */
-   int         tup_num;
-
-   /*
-    * resource index number for this context
-    */
-   int         res_id_index;
-
-   /*
-    * the actual query results
-    */
-   PGresult   *res;
-}  dblink_results;
-
 /*
  * External declarations
  */
-/* deprecated */
-extern Datum dblink(PG_FUNCTION_ARGS);
-extern Datum dblink_tok(PG_FUNCTION_ARGS);
-
-/* supported */
 extern Datum dblink_connect(PG_FUNCTION_ARGS);
 extern Datum dblink_disconnect(PG_FUNCTION_ARGS);
 extern Datum dblink_open(PG_FUNCTION_ARGS);
@@ -68,7 +44,6 @@ extern Datum dblink_fetch(PG_FUNCTION_ARGS);
 extern Datum dblink_record(PG_FUNCTION_ARGS);
 extern Datum dblink_exec(PG_FUNCTION_ARGS);
 extern Datum dblink_get_pkey(PG_FUNCTION_ARGS);
-extern Datum dblink_last_oid(PG_FUNCTION_ARGS);
 extern Datum dblink_build_sql_insert(PG_FUNCTION_ARGS);
 extern Datum dblink_build_sql_delete(PG_FUNCTION_ARGS);
 extern Datum dblink_build_sql_update(PG_FUNCTION_ARGS);
index 42e483de34e80d70450f643cf10fffe1a4a596f7..cd670390d72d11c4b82f5ed6fa35e95a181830a6 100644 (file)
@@ -1,50 +1,53 @@
---
--- Uncomment the following commented lines to use original DEPRECATED functions
---
---CREATE OR REPLACE FUNCTION dblink (text,text)
---RETURNS setof int
---AS 'MODULE_PATHNAME','dblink'
---LANGUAGE 'C' WITH (isstrict);
---CREATE OR REPLACE FUNCTION dblink_tok (int,int)
---RETURNS text
---AS 'MODULE_PATHNAME','dblink_tok'
---LANGUAGE 'C' WITH (isstrict);
---CREATE OR REPLACE FUNCTION dblink_last_oid (int)
---RETURNS oid
---AS 'MODULE_PATHNAME','dblink_last_oid'
---LANGUAGE 'C' WITH (isstrict);
-
 CREATE OR REPLACE FUNCTION dblink_connect (text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_connect'
 LANGUAGE 'C' WITH (isstrict);
 
+CREATE OR REPLACE FUNCTION dblink_connect (text, text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_connect'
+LANGUAGE 'C' WITH (isstrict);
+
 CREATE OR REPLACE FUNCTION dblink_disconnect ()
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_disconnect'
 LANGUAGE 'C' WITH (isstrict);
 
+CREATE OR REPLACE FUNCTION dblink_disconnect (text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_disconnect'
+LANGUAGE 'C' WITH (isstrict);
+
 CREATE OR REPLACE FUNCTION dblink_open (text,text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_open'
 LANGUAGE 'C' WITH (isstrict);
 
+CREATE OR REPLACE FUNCTION dblink_open (text,text,text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_open'
+LANGUAGE 'C' WITH (isstrict);
+
 CREATE OR REPLACE FUNCTION dblink_fetch (text,int)
 RETURNS setof record
 AS 'MODULE_PATHNAME','dblink_fetch'
 LANGUAGE 'C' WITH (isstrict);
 
+CREATE OR REPLACE FUNCTION dblink_fetch (text,text,int)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_fetch'
+LANGUAGE 'C' WITH (isstrict);
+
 CREATE OR REPLACE FUNCTION dblink_close (text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_close'
 LANGUAGE 'C' WITH (isstrict);
 
--- Note: if this is not a first time install of dblink, uncomment the
--- following DROP which prepares the database for the new, non-deprecated
--- version.
---DROP FUNCTION dblink (text,text);
+CREATE OR REPLACE FUNCTION dblink_close (text,text)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_close'
+LANGUAGE 'C' WITH (isstrict);
 
--- Comment out the following 3 lines if the DEPRECATED functions are used.
 CREATE OR REPLACE FUNCTION dblink (text,text)
 RETURNS setof record
 AS 'MODULE_PATHNAME','dblink_record'
index 3a749d8903b9cbdf52e312a036f316dd8a8f69a3..251bd9396181f2256c550b9495b2e06dff1f1c27 100644 (file)
@@ -6,21 +6,35 @@ dblink_connect -- Opens a persistent connection to a remote database
 Synopsis
 
 dblink_connect(text connstr)
+dblink_connect(text connname, text connstr)
 
 Inputs
 
+  connname
+    if 2 arguments are given, the first is used as a name for a persistent
+    connection
+
   connstr
 
     standard libpq format connection string, 
     e.g. "hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres password=mypasswd"
 
+    if only one argument is given, the connection is unnamed; only one unnamed
+    connection can exist at a time
+
 Outputs
 
   Returns status = "OK"
 
 Example usage
 
-test=# select dblink_connect('dbname=template1');
+select dblink_connect('dbname=template1');
+ dblink_connect
+----------------
+ OK
+(1 row)
+
+select dblink_connect('myconn','dbname=template1');
  dblink_connect
 ----------------
  OK
@@ -29,15 +43,18 @@ test=# select dblink_connect('dbname=template1');
 ==================================================================
 Name
 
-dblink_disconnect -- Closes the persistent connection to a remote database
+dblink_disconnect -- Closes a persistent connection to a remote database
 
 Synopsis
 
 dblink_disconnect()
+dblink_disconnect(text connname)
 
 Inputs
 
-  none
+  connname
+    if an argument is given, it is used as a name for a persistent
+    connection to close; otherwiase the unnamed connection is closed
 
 Outputs
 
@@ -51,3 +68,8 @@ test=# select dblink_disconnect();
  OK
 (1 row)
 
+select dblink_disconnect('myconn');
+ dblink_disconnect
+-------------------
+ OK
+(1 row)
index 3bc6bdb2fe5d94f4c2324e66bea2696f5dc91e6f..7c9cc3cde25554b1bd0c7438a2d8ec5961475225 100644 (file)
@@ -6,9 +6,14 @@ dblink_open -- Opens a cursor on a remote database
 Synopsis
 
 dblink_open(text cursorname, text sql)
+dblink_open(text connname, text cursorname, text sql)
 
 Inputs
 
+  connname
+    if three arguments are present, the first is taken as the specific
+    connection name to use; otherwise the unnamed connection is assumed
+
   cursorname
 
     a reference name for the cursor
@@ -52,9 +57,14 @@ dblink_fetch -- Returns a set from an open cursor on a remote database
 Synopsis
 
 dblink_fetch(text cursorname, int32 howmany)
+dblink_fetch(text connname, text cursorname, int32 howmany)
 
 Inputs
 
+  connname
+    if three arguments are present, the first is taken as the specific
+    connection name to use; otherwise the unnamed connection is assumed
+
   cursorname
 
     The reference name for the cursor
@@ -123,9 +133,14 @@ dblink_close -- Closes a cursor on a remote database
 Synopsis
 
 dblink_close(text cursorname)
+dblink_close(text connname, text cursorname)
 
 Inputs
 
+  connname
+    if two arguments are present, the first is taken as the specific
+    connection name to use; otherwise the unnamed connection is assumed
+
   cursorname
 
     a reference name for the cursor
@@ -135,7 +150,8 @@ Outputs
   Returns status = "OK"
 
 Note
-  dblink_connect(text connstr) must be executed first.
+  dblink_connect(text connstr) or dblink_connect(text connname, text connstr)
+  must be executed first.
 
 Example usage
 
@@ -157,3 +173,20 @@ test=# select dblink_close('foo');
  OK
 (1 row)
 
+select dblink_connect('myconn','dbname=regression');
+ dblink_connect
+----------------
+ OK
+(1 row)
+
+select dblink_open('myconn','foo','select proname, prosrc from pg_proc');
+ dblink_open
+-------------
+ OK
+(1 row)
+
+select dblink_close('myconn','foo');
+ dblink_close
+--------------
+ OK
+(1 row)
index 27ed5e35a01e2e0250fddda31754cbcbb4aca338..72a21276d950e9d99004002ab4123c2817757c2a 100644 (file)
@@ -6,22 +6,23 @@ dblink_exec -- Executes an UPDATE/INSERT/DELETE on a remote database
 Synopsis
 
 dblink_exec(text connstr, text sql)
-- or -
+dblink_exec(text connname, text sql)
 dblink_exec(text sql)
 
 Inputs
 
+  connname
   connstr
+    If two arguments are present, the first is first assumed to be a specific
+    connection name to use. If the name is not found, the argument is then
+    assumed to be a valid connection string, of standard libpq format,
+    e.g.: "hostaddr=127.0.0.1 dbname=mydb user=postgres password=mypasswd"
 
-    standard libpq format connection string, 
-    e.g. "hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres password=mypasswd"
-    If the second form is used, then the dblink_connect(text connstr) must be
-    executed first.
+    If only one argument is used, then the unnamed connection is used.
 
   sql
 
     sql statement that you wish to execute on the remote host, e.g.:
-
        insert into foo values(0,'a','{"a0","b0","c0"}');
 
 Outputs
@@ -36,14 +37,26 @@ Notes
 
 Example usage
 
-test=# select dblink_connect('dbname=dblink_test_slave');
+select dblink_connect('dbname=dblink_test_slave');
  dblink_connect
 ----------------
  OK
 (1 row)
 
-test=# select dblink_exec('insert into foo values(21,''z'',''{"a0","b0","c0"}'');');
+select dblink_exec('insert into foo values(21,''z'',''{"a0","b0","c0"}'');');
    dblink_exec
 -----------------
  INSERT 943366 1
 (1 row)
+
+select dblink_connect('myconn','dbname=regression');
+ dblink_connect
+----------------
+ OK
+(1 row)
+
+select dblink_exec('myconn','insert into foo values(21,''z'',''{"a0","b0","c0"}'');');
+   dblink_exec
+------------------
+ INSERT 6432584 1
+(1 row)
index 525ffab45a27ad2f6b5bfc00f8be096631ccef7d..9c81417741744a1a18ca393a5ca3855d7c41371f 100644 (file)
@@ -6,17 +6,19 @@ dblink -- Returns a set from a remote database
 Synopsis
 
 dblink(text connstr, text sql)
-- or -
+dblink(text connname, text sql)
 dblink(text sql)
 
 Inputs
 
+  connname
   connstr
+    If two arguments are present, the first is first assumed to be a specific
+    connection name to use. If the name is not found, the argument is then
+    assumed to be a valid connection string, of standard libpq format,
+    e.g.: "hostaddr=127.0.0.1 dbname=mydb user=postgres password=mypasswd"
 
-    standard libpq format connection string, 
-    e.g. "hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres password=mypasswd"
-    If the second form is used, then the dblink_connect(text connstr) must be
-    executed first.
+    If only one argument is used, then the unnamed connection is used.
 
   sql
 
@@ -29,7 +31,7 @@ Outputs
 
 Example usage
 
-test=# select * from dblink('dbname=template1','select proname, prosrc from pg_proc')
+select * from dblink('dbname=template1','select proname, prosrc from pg_proc')
  as t1(proname name, prosrc text) where proname like 'bytea%';
   proname   |   prosrc
 ------------+------------
@@ -47,13 +49,13 @@ test=# select * from dblink('dbname=template1','select proname, prosrc from pg_p
  byteaout   | byteaout
 (12 rows)
 
-test=# select dblink_connect('dbname=template1');
+select dblink_connect('dbname=template1');
  dblink_connect
 ----------------
  OK
 (1 row)
 
-test=# select * from dblink('select proname, prosrc from pg_proc')
+select * from dblink('select proname, prosrc from pg_proc')
  as t1(proname name, prosrc text) where proname like 'bytea%';
   proname   |   prosrc
 ------------+------------
@@ -71,6 +73,33 @@ test=# select * from dblink('select proname, prosrc from pg_proc')
  byteaout   | byteaout
 (12 rows)
 
+select dblink_connect('myconn','dbname=regression');
+ dblink_connect
+----------------
+ OK
+(1 row)
+
+select * from dblink('myconn','select proname, prosrc from pg_proc')
+ as t1(proname name, prosrc text) where proname like 'bytea%';
+  proname   |   prosrc
+------------+------------
+ bytearecv  | bytearecv
+ byteasend  | byteasend
+ byteale    | byteale
+ byteagt    | byteagt
+ byteage    | byteage
+ byteane    | byteane
+ byteacmp   | byteacmp
+ bytealike  | bytealike
+ byteanlike | byteanlike
+ byteacat   | byteacat
+ byteaeq    | byteaeq
+ bytealt    | bytealt
+ byteain    | byteain
+ byteaout   | byteaout
+(14 rows)
+
+
 ==================================================================
 A more convenient way to use dblink may be to create a view:
 
index 945f1bd16a4d0e357af434a7e2ddba8cbdfcd1a2..0beeeeeb84e7f5b0d6ec46de5df94f26f872cbe1 100644 (file)
@@ -106,11 +106,11 @@ WHERE t.a > 7;
  9 | j | {a9,b9,c9}
 (2 rows)
 
--- should generate "no connection available" error
+-- should generate "connection not available" error
 SELECT *
 FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[])
 WHERE t.a > 7;
-ERROR:  dblink: no connection available
+ERROR:  dblink_record: connection not available
 -- create a persistent connection
 SELECT dblink_connect('dbname=regression');
  dblink_connect 
@@ -172,10 +172,10 @@ SELECT dblink_close('rmt_foo_cursor');
  OK
 (1 row)
 
--- should generate "cursor rmt_foo_cursor does not exist" error
+-- should generate "cursor not found: rmt_foo_cursor" error
 SELECT *
 FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]);
-ERROR:  dblink_fetch: cursor rmt_foo_cursor does not exist
+ERROR:  dblink_fetch: cursor not found: rmt_foo_cursor
 -- close the persistent connection
 SELECT dblink_disconnect();
  dblink_disconnect 
@@ -183,11 +183,12 @@ SELECT dblink_disconnect();
  OK
 (1 row)
 
--- should generate "no connection available" error
+-- should generate "no connection to the server" error
 SELECT *
 FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[])
 WHERE t.a > 7;
-ERROR:  dblink: no connection available
+ERROR:  dblink: sql error: no connection to the server
+
 -- put more data into our slave table, first using arbitrary connection syntax
 -- but truncate the actual return value so we can use diff to check for success
 SELECT substr(dblink_exec('dbname=regression','INSERT INTO foo VALUES(10,''k'',''{"a10","b10","c10"}'')'),1,6);
@@ -268,3 +269,198 @@ SELECT dblink_disconnect();
  OK
 (1 row)
 
+--
+-- tests for the new named persistent connection syntax
+--
+-- should generate "missing "=" after "myconn" in connection info string" error
+SELECT *
+FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
+WHERE t.a > 7;
+ERROR:  dblink: connection error: missing "=" after "myconn" in connection info string
+
+-- create a named persistent connection
+SELECT dblink_connect('myconn','dbname=regression');
+ dblink_connect 
+----------------
+ OK
+(1 row)
+
+-- use the named persistent connection
+SELECT *
+FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
+WHERE t.a > 7;
+ a  | b |       c       
+----+---+---------------
+  8 | i | {a8,b8,c8}
+  9 | j | {a9,b9,c9}
+ 10 | k | {a10,b10,c10}
+(3 rows)
+
+-- create a second named persistent connection
+-- should error with "cannot save named connection"
+SELECT dblink_connect('myconn','dbname=regression');
+NOTICE:  cannot use a connection name more than once
+ERROR:  dblink_connect: cannot save named connection
+-- create a second named persistent connection with a new name
+SELECT dblink_connect('myconn2','dbname=regression');
+ dblink_connect 
+----------------
+ OK
+(1 row)
+
+-- use the second named persistent connection
+SELECT *
+FROM dblink('myconn2','SELECT * FROM foo') AS t(a int, b text, c text[])
+WHERE t.a > 7;
+ a  | b |       c       
+----+---+---------------
+  8 | i | {a8,b8,c8}
+  9 | j | {a9,b9,c9}
+ 10 | k | {a10,b10,c10}
+(3 rows)
+
+-- close the second named persistent connection
+SELECT dblink_disconnect('myconn2');
+ dblink_disconnect 
+-------------------
+ OK
+(1 row)
+
+-- open a cursor
+SELECT dblink_open('myconn','rmt_foo_cursor','SELECT * FROM foo');
+ dblink_open 
+-------------
+ OK
+(1 row)
+
+-- fetch some data
+SELECT *
+FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]);
+ a | b |     c      
+---+---+------------
+ 0 | a | {a0,b0,c0}
+ 1 | b | {a1,b1,c1}
+ 2 | c | {a2,b2,c2}
+ 3 | d | {a3,b3,c3}
+(4 rows)
+
+SELECT *
+FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]);
+ a | b |     c      
+---+---+------------
+ 4 | e | {a4,b4,c4}
+ 5 | f | {a5,b5,c5}
+ 6 | g | {a6,b6,c6}
+ 7 | h | {a7,b7,c7}
+(4 rows)
+
+-- this one only finds three rows left
+SELECT *
+FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]);
+ a  | b |       c       
+----+---+---------------
+  8 | i | {a8,b8,c8}
+  9 | j | {a9,b9,c9}
+ 10 | k | {a10,b10,c10}
+(3 rows)
+
+-- close the cursor
+SELECT dblink_close('myconn','rmt_foo_cursor');
+ dblink_close 
+--------------
+ OK
+(1 row)
+
+-- should generate "cursor not found: rmt_foo_cursor" error
+SELECT *
+FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]);
+ERROR:  dblink_fetch: cursor not found: rmt_foo_cursor
+-- close the named persistent connection
+SELECT dblink_disconnect('myconn');
+ dblink_disconnect 
+-------------------
+ OK
+(1 row)
+
+-- should generate "missing "=" after "myconn" in connection info string" error
+SELECT *
+FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
+WHERE t.a > 7;
+ERROR:  dblink: connection error: missing "=" after "myconn" in connection info string
+
+-- create a named persistent connection
+SELECT dblink_connect('myconn','dbname=regression');
+ dblink_connect 
+----------------
+ OK
+(1 row)
+
+-- put more data into our slave table, using named persistent connection syntax
+-- but truncate the actual return value so we can use diff to check for success
+SELECT substr(dblink_exec('myconn','INSERT INTO foo VALUES(11,''l'',''{"a11","b11","c11"}'')'),1,6);
+ substr 
+--------
+ INSERT
+(1 row)
+
+-- let's see it
+SELECT *
+FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[]);
+ a  | b |       c       
+----+---+---------------
+  0 | a | {a0,b0,c0}
+  1 | b | {a1,b1,c1}
+  2 | c | {a2,b2,c2}
+  3 | d | {a3,b3,c3}
+  4 | e | {a4,b4,c4}
+  5 | f | {a5,b5,c5}
+  6 | g | {a6,b6,c6}
+  7 | h | {a7,b7,c7}
+  8 | i | {a8,b8,c8}
+  9 | j | {a9,b9,c9}
+ 10 | k | {a10,b10,c10}
+ 11 | l | {a11,b11,c11}
+(12 rows)
+
+-- change some data
+SELECT dblink_exec('myconn','UPDATE foo SET f3[2] = ''b99'' WHERE f1 = 11');
+ dblink_exec 
+-------------
+ UPDATE 1
+(1 row)
+
+-- let's see it
+SELECT *
+FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
+WHERE a = 11;
+ a  | b |       c       
+----+---+---------------
+ 11 | l | {a11,b99,c11}
+(1 row)
+
+-- delete some data
+SELECT dblink_exec('myconn','DELETE FROM foo WHERE f1 = 11');
+ dblink_exec 
+-------------
+ DELETE 1
+(1 row)
+
+-- let's see it
+SELECT *
+FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
+WHERE a = 11;
+ a | b | c 
+---+---+---
+(0 rows)
+
+-- close the named persistent connection
+SELECT dblink_disconnect('myconn');
+ dblink_disconnect 
+-------------------
+ OK
+(1 row)
+
+-- close the named persistent connection again
+-- should get "connection named "myconn" not found" error
+SELECT dblink_disconnect('myconn');
+ERROR:  dblink_disconnect: connection named "myconn" not found
index f041e0a770f7783a3ab081f067aa2aa10a82df88..6385a79e2baf86492c16964447819454e05a3cf4 100644 (file)
@@ -68,7 +68,7 @@ SELECT *
 FROM dblink('dbname=regression','SELECT * FROM foo') AS t(a int, b text, c text[])
 WHERE t.a > 7;
 
--- should generate "no connection available" error
+-- should generate "connection not available" error
 SELECT *
 FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[])
 WHERE t.a > 7;
@@ -98,14 +98,14 @@ FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]);
 -- close the cursor
 SELECT dblink_close('rmt_foo_cursor');
 
--- should generate "cursor rmt_foo_cursor does not exist" error
+-- should generate "cursor not found: rmt_foo_cursor" error
 SELECT *
 FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]);
 
 -- close the persistent connection
 SELECT dblink_disconnect();
 
--- should generate "no connection available" error
+-- should generate "no connection to the server" error
 SELECT *
 FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[])
 WHERE t.a > 7;
@@ -143,3 +143,98 @@ WHERE a = 11;
 
 -- close the persistent connection
 SELECT dblink_disconnect();
+
+--
+-- tests for the new named persistent connection syntax
+--
+
+-- should generate "missing "=" after "myconn" in connection info string" error
+SELECT *
+FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
+WHERE t.a > 7;
+
+-- create a named persistent connection
+SELECT dblink_connect('myconn','dbname=regression');
+
+-- use the named persistent connection
+SELECT *
+FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
+WHERE t.a > 7;
+
+-- create a second named persistent connection
+-- should error with "cannot save named connection"
+SELECT dblink_connect('myconn','dbname=regression');
+
+-- create a second named persistent connection with a new name
+SELECT dblink_connect('myconn2','dbname=regression');
+
+-- use the second named persistent connection
+SELECT *
+FROM dblink('myconn2','SELECT * FROM foo') AS t(a int, b text, c text[])
+WHERE t.a > 7;
+
+-- close the second named persistent connection
+SELECT dblink_disconnect('myconn2');
+
+-- open a cursor
+SELECT dblink_open('myconn','rmt_foo_cursor','SELECT * FROM foo');
+
+-- fetch some data
+SELECT *
+FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]);
+
+SELECT *
+FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]);
+
+-- this one only finds three rows left
+SELECT *
+FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]);
+
+-- close the cursor
+SELECT dblink_close('myconn','rmt_foo_cursor');
+
+-- should generate "cursor not found: rmt_foo_cursor" error
+SELECT *
+FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]);
+
+-- close the named persistent connection
+SELECT dblink_disconnect('myconn');
+
+-- should generate "missing "=" after "myconn" in connection info string" error
+SELECT *
+FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
+WHERE t.a > 7;
+
+-- create a named persistent connection
+SELECT dblink_connect('myconn','dbname=regression');
+
+-- put more data into our slave table, using named persistent connection syntax
+-- but truncate the actual return value so we can use diff to check for success
+SELECT substr(dblink_exec('myconn','INSERT INTO foo VALUES(11,''l'',''{"a11","b11","c11"}'')'),1,6);
+
+-- let's see it
+SELECT *
+FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[]);
+
+-- change some data
+SELECT dblink_exec('myconn','UPDATE foo SET f3[2] = ''b99'' WHERE f1 = 11');
+
+-- let's see it
+SELECT *
+FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
+WHERE a = 11;
+
+-- delete some data
+SELECT dblink_exec('myconn','DELETE FROM foo WHERE f1 = 11');
+
+-- let's see it
+SELECT *
+FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
+WHERE a = 11;
+
+-- close the named persistent connection
+SELECT dblink_disconnect('myconn');
+
+-- close the named persistent connection again
+-- should get "connection named "myconn" not found" error
+SELECT dblink_disconnect('myconn');