]> granicus.if.org Git - postgresql/commitdiff
Added new versions of dblink, dblink_exec, dblink_open, dblink_close,
authorJoe Conway <mail@joeconway.com>
Sun, 7 Mar 2004 02:27:00 +0000 (02:27 +0000)
committerJoe Conway <mail@joeconway.com>
Sun, 7 Mar 2004 02:27:00 +0000 (02:27 +0000)
and, dblink_fetch -- allows ERROR on remote side of connection to
throw NOTICE locally instead of ERROR. Also removed documentation for
previously deprecated, now removed, functions.

contrib/dblink/README.dblink
contrib/dblink/dblink.c
contrib/dblink/dblink.sql.in
contrib/dblink/doc/cursor
contrib/dblink/doc/deprecated [deleted file]
contrib/dblink/doc/execute
contrib/dblink/doc/query
contrib/dblink/expected/dblink.out
contrib/dblink/sql/dblink.sql

index c315cdaa36361d4d3871c309cc86bd20c7e0a73d..f5f07af641b1bb291a0c24adbe7c291aee28196b 100644 (file)
  *
  */
 
-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.7 (as of 25 Feb, 2004)
+    - Added new version of dblink, dblink_exec, dblink_open, dblink_close,
+      and, dblink_fetch -- allows ERROR on remote side of connection to
+      throw NOTICE locally instead of ERROR
   Version 0.6
     - functions deprecated in 0.5 have been removed
     - added ability to create "named" persistent connections
@@ -85,7 +83,7 @@ Installation:
 
   You can use dblink.sql to create the functions in your database of choice, e.g.
 
-    psql -U postgres template1 < dblink.sql
+    psql template1 < dblink.sql
 
   installs following functions into database template1:
 
@@ -104,40 +102,40 @@ Installation:
 
      cursor
      ------------
-     dblink_open(text,text) RETURNS text
+     dblink_open(text,text [, bool fail_on_error]) RETURNS text
        - 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
+     dblink_open(text,text,text [, bool fail_on_error]) 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
+     dblink_fetch(text, int [, bool fail_on_error]) RETURNS setof record
        - fetches data from an already opened cursor on the unnamed connection
-     dblink_fetch(text, text, int) RETURNS setof record
+     dblink_fetch(text, text, int [, bool fail_on_error]) RETURNS setof record
        - fetches data from an already opened cursor on a named connection
-     dblink_close(text) RETURNS text
+     dblink_close(text [, bool fail_on_error]) RETURNS text
        - closes a cursor on the unnamed connection
-     dblink_close(text,text) RETURNS text
+     dblink_close(text,text [, bool fail_on_error]) RETURNS text
        - closes a cursor on a named connection
 
      query
      ------------
-     dblink(text,text) RETURNS setof record
+     dblink(text,text [, bool fail_on_error]) RETURNS setof record
        - 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
+     dblink(text [, bool fail_on_error]) RETURNS setof record
        - 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
+     dblink_exec(text, text [, bool fail_on_error]) RETURNS text
        - 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
+     dblink_exec(text [, bool fail_on_error]) RETURNS text
        - executes an INSERT/UPDATE/DELETE query remotely, using connection
          already opened with dblink_connect()
 
@@ -169,7 +167,6 @@ Documentation:
      doc/query
      doc/execute
      doc/misc
-     doc/deprecated
 
 ==================================================================
 -- Joe Conway
index 4ecca8eff8be1fc418370a8cd2edea45bb228896..ac941598be7546dc395005606f502e2b548b534f 100644 (file)
@@ -134,6 +134,16 @@ typedef struct remoteConnHashEnt
                                         errmsg("%s", p2), \
                                         errdetail("%s", msg))); \
        } while (0)
+#define DBLINK_RES_ERROR_AS_NOTICE(p2) \
+       do { \
+                       msg = pstrdup(PQerrorMessage(conn)); \
+                       if (res) \
+                               PQclear(res); \
+                       ereport(NOTICE, \
+                                       (errcode(ERRCODE_SYNTAX_ERROR), \
+                                        errmsg("%s", p2), \
+                                        errdetail("%s", msg))); \
+       } while (0)
 #define DBLINK_CONN_NOT_AVAIL \
        do { \
                if(conname) \
@@ -152,7 +162,6 @@ typedef struct remoteConnHashEnt
                        if(rcon) \
                        { \
                                conn = rcon->con; \
-                               freeconn = false; \
                        } \
                        else \
                        { \
@@ -167,6 +176,7 @@ typedef struct remoteConnHashEnt
                                                         errmsg("could not establish connection"), \
                                                         errdetail("%s", msg))); \
                                } \
+                               freeconn = true; \
                        } \
        } while (0)
 
@@ -276,18 +286,42 @@ dblink_open(PG_FUNCTION_ARGS)
        char       *conname = NULL;
        StringInfo      str = makeStringInfo();
        remoteConn *rcon = NULL;
+       bool            fail = true;    /* default to backward compatible behavior */
 
        if (PG_NARGS() == 2)
        {
+               /* text,text */
                curname = GET_STR(PG_GETARG_TEXT_P(0));
                sql = GET_STR(PG_GETARG_TEXT_P(1));
                conn = persistent_conn;
        }
        else if (PG_NARGS() == 3)
        {
+               /* might be text,text,text or text,text,bool */
+               if (get_fn_expr_argtype(fcinfo->flinfo, 2) == BOOLOID)
+               {
+                       curname = GET_STR(PG_GETARG_TEXT_P(0));
+                       sql = GET_STR(PG_GETARG_TEXT_P(1));
+                       fail = PG_GETARG_BOOL(2);
+                       conn = persistent_conn;
+               }
+               else
+               {
+                       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;
+       }
+       else if (PG_NARGS() == 4)
+       {
+               /* text,text,text,bool */
                conname = GET_STR(PG_GETARG_TEXT_P(0));
                curname = GET_STR(PG_GETARG_TEXT_P(1));
                sql = GET_STR(PG_GETARG_TEXT_P(2));
+               fail = PG_GETARG_BOOL(3);
                rcon = getConnectionByName(conname);
                if (rcon)
                        conn = rcon->con;
@@ -304,13 +338,19 @@ dblink_open(PG_FUNCTION_ARGS)
 
        appendStringInfo(str, "DECLARE %s CURSOR FOR %s", curname, sql);
        res = PQexec(conn, str->data);
-       if (!res ||
-               (PQresultStatus(res) != PGRES_COMMAND_OK &&
-                PQresultStatus(res) != PGRES_TUPLES_OK))
-               DBLINK_RES_ERROR("sql error");
+       if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
+       {
+               if (fail)
+                       DBLINK_RES_ERROR("sql error");
+               else
+               {
+                       DBLINK_RES_ERROR_AS_NOTICE("sql error");
+                       PQclear(res);
+                       PG_RETURN_TEXT_P(GET_TEXT("ERROR"));
+               }
+       }
 
        PQclear(res);
-
        PG_RETURN_TEXT_P(GET_TEXT("OK"));
 }
 
@@ -328,16 +368,38 @@ dblink_close(PG_FUNCTION_ARGS)
        StringInfo      str = makeStringInfo();
        char       *msg;
        remoteConn *rcon = NULL;
+       bool            fail = true;    /* default to backward compatible behavior */
 
        if (PG_NARGS() == 1)
        {
+               /* text */
                curname = GET_STR(PG_GETARG_TEXT_P(0));
                conn = persistent_conn;
        }
        else if (PG_NARGS() == 2)
        {
+               /* might be text,text or text,bool */
+               if (get_fn_expr_argtype(fcinfo->flinfo, 1) == BOOLOID)
+               {
+                       curname = GET_STR(PG_GETARG_TEXT_P(0));
+                       fail = PG_GETARG_BOOL(1);
+                       conn = persistent_conn;
+               }
+               else
+               {
+                       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 (PG_NARGS() == 3)
+       {
+               /* text,text,bool */
                conname = GET_STR(PG_GETARG_TEXT_P(0));
                curname = GET_STR(PG_GETARG_TEXT_P(1));
+               fail = PG_GETARG_BOOL(2);
                rcon = getConnectionByName(conname);
                if (rcon)
                        conn = rcon->con;
@@ -351,7 +413,16 @@ dblink_close(PG_FUNCTION_ARGS)
        /* close the cursor */
        res = PQexec(conn, str->data);
        if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
-               DBLINK_RES_ERROR("sql error");
+       {
+               if (fail)
+                       DBLINK_RES_ERROR("sql error");
+               else
+               {
+                       DBLINK_RES_ERROR_AS_NOTICE("sql error");
+                       PQclear(res);
+                       PG_RETURN_TEXT_P(GET_TEXT("ERROR"));
+               }
+       }
 
        PQclear(res);
 
@@ -395,19 +466,44 @@ dblink_fetch(PG_FUNCTION_ARGS)
                char       *curname = NULL;
                int                     howmany = 0;
                ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+               bool            fail = true;    /* default to backward compatible */
 
-               if (PG_NARGS() == 3)
+               if (PG_NARGS() == 4)
                {
+                       /* text,text,int,bool */
                        conname = GET_STR(PG_GETARG_TEXT_P(0));
                        curname = GET_STR(PG_GETARG_TEXT_P(1));
                        howmany = PG_GETARG_INT32(2);
+                       fail = PG_GETARG_BOOL(3);
 
                        rcon = getConnectionByName(conname);
                        if (rcon)
                                conn = rcon->con;
                }
+               else if (PG_NARGS() == 3)
+               {
+                       /* text,text,int or text,int,bool */
+                       if (get_fn_expr_argtype(fcinfo->flinfo, 2) == BOOLOID)
+                       {
+                               curname = GET_STR(PG_GETARG_TEXT_P(0));
+                               howmany = PG_GETARG_INT32(1);
+                               fail = PG_GETARG_BOOL(2);
+                               conn = persistent_conn;
+                       }
+                       else
+                       {
+                               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)
                {
+                       /* text,int */
                        curname = GET_STR(PG_GETARG_TEXT_P(0));
                        howmany = PG_GETARG_INT32(1);
                        conn = persistent_conn;
@@ -431,7 +527,17 @@ dblink_fetch(PG_FUNCTION_ARGS)
                if (!res ||
                        (PQresultStatus(res) != PGRES_COMMAND_OK &&
                         PQresultStatus(res) != PGRES_TUPLES_OK))
-                       DBLINK_RES_ERROR("sql error");
+               {
+                       if (fail)
+                               DBLINK_RES_ERROR("sql error");
+                       else
+                       {
+                               if (res)
+                                       PQclear(res);
+                               DBLINK_RES_ERROR_AS_NOTICE("sql error");
+                               SRF_RETURN_DONE(funcctx);
+                       }
+               }
                else if (PQresultStatus(res) == PGRES_COMMAND_OK)
                {
                        /* cursor does not exist - closed already or bad name */
@@ -448,7 +554,11 @@ dblink_fetch(PG_FUNCTION_ARGS)
 
                /* fast track when no results */
                if (funcctx->max_calls < 1)
+               {
+                       if (res)
+                               PQclear(res);
                        SRF_RETURN_DONE(funcctx);
+               }
 
                /* check typtype to see if we have a predetermined return type */
                functypeid = get_func_rettype(funcid);
@@ -546,7 +656,7 @@ dblink_record(PG_FUNCTION_ARGS)
        bool            is_sql_cmd = false;
        char       *sql_cmd_status = NULL;
        MemoryContext oldcontext;
-       bool            freeconn = true;
+       bool            freeconn = false;
 
        /* stuff done only on the first call of the function */
        if (SRF_IS_FIRSTCALL())
@@ -560,6 +670,7 @@ dblink_record(PG_FUNCTION_ARGS)
                char       *conname = NULL;
                remoteConn *rcon = NULL;
                ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+               bool            fail = true;    /* default to backward compatible */
 
                /* create a function context for cross-call persistence */
                funcctx = SRF_FIRSTCALL_INIT();
@@ -570,13 +681,31 @@ dblink_record(PG_FUNCTION_ARGS)
                 */
                oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
-               if (PG_NARGS() == 2)
+               if (PG_NARGS() == 3)
                {
+                       /* text,text,bool */
                        DBLINK_GET_CONN;
                        sql = GET_STR(PG_GETARG_TEXT_P(1));
+                       fail = PG_GETARG_BOOL(2);
+               }
+               else if (PG_NARGS() == 2)
+               {
+                       /* text,text or text,bool */
+                       if (get_fn_expr_argtype(fcinfo->flinfo, 1) == BOOLOID)
+                       {
+                               conn = persistent_conn;
+                               sql = GET_STR(PG_GETARG_TEXT_P(0));
+                               fail = PG_GETARG_BOOL(1);
+                       }
+                       else
+                       {
+                               DBLINK_GET_CONN;
+                               sql = GET_STR(PG_GETARG_TEXT_P(1));
+                       }
                }
                else if (PG_NARGS() == 1)
                {
+                       /* text */
                        conn = persistent_conn;
                        sql = GET_STR(PG_GETARG_TEXT_P(0));
                }
@@ -588,8 +717,22 @@ dblink_record(PG_FUNCTION_ARGS)
                        DBLINK_CONN_NOT_AVAIL;
 
                res = PQexec(conn, sql);
-               if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK))
-                       DBLINK_RES_ERROR("sql error");
+               if (!res ||
+                       (PQresultStatus(res) != PGRES_COMMAND_OK &&
+                        PQresultStatus(res) != PGRES_TUPLES_OK))
+               {
+                       if (fail)
+                               DBLINK_RES_ERROR("sql error");
+                       else
+                       {
+                               if (res)
+                                       PQclear(res);
+                               if (freeconn)
+                                       PQfinish(conn);
+                               DBLINK_RES_ERROR_AS_NOTICE("sql error");
+                               SRF_RETURN_DONE(funcctx);
+                       }
+               }
 
                if (PQresultStatus(res) == PGRES_COMMAND_OK)
                {
@@ -614,12 +757,16 @@ dblink_record(PG_FUNCTION_ARGS)
                funcctx->user_fctx = res;
 
                /* if needed, close the connection to the database and cleanup */
-               if (freeconn && PG_NARGS() == 2)
+               if (freeconn)
                        PQfinish(conn);
 
                /* fast track when no results */
                if (funcctx->max_calls < 1)
+               {
+                       if (res)
+                               PQclear(res);
                        SRF_RETURN_DONE(funcctx);
+               }
 
                /* check typtype to see if we have a predetermined return type */
                functypeid = get_func_rettype(funcid);
@@ -727,15 +874,34 @@ dblink_exec(PG_FUNCTION_ARGS)
        char       *sql = NULL;
        char       *conname = NULL;
        remoteConn *rcon = NULL;
-       bool            freeconn = true;
+       bool            freeconn = false;
+       bool            fail = true;    /* default to backward compatible behavior */
 
-       if (PG_NARGS() == 2)
+       if (PG_NARGS() == 3)
        {
+               /* must be text,text,bool */
                DBLINK_GET_CONN;
                sql = GET_STR(PG_GETARG_TEXT_P(1));
+               fail = PG_GETARG_BOOL(2);
+       }
+       else if (PG_NARGS() == 2)
+       {
+               /* might be text,text or text,bool */
+               if (get_fn_expr_argtype(fcinfo->flinfo, 1) == BOOLOID)
+               {
+                       conn = persistent_conn;
+                       sql = GET_STR(PG_GETARG_TEXT_P(0));
+                       fail = PG_GETARG_BOOL(1);
+               }
+               else
+               {
+                       DBLINK_GET_CONN;
+                       sql = GET_STR(PG_GETARG_TEXT_P(1));
+               }
        }
        else if (PG_NARGS() == 1)
        {
+               /* must be single text argument */
                conn = persistent_conn;
                sql = GET_STR(PG_GETARG_TEXT_P(0));
        }
@@ -750,9 +916,25 @@ dblink_exec(PG_FUNCTION_ARGS)
        if (!res ||
                (PQresultStatus(res) != PGRES_COMMAND_OK &&
                 PQresultStatus(res) != PGRES_TUPLES_OK))
-               DBLINK_RES_ERROR("sql error");
+       {
+               if (fail)
+                       DBLINK_RES_ERROR("sql error");
+               else
+                       DBLINK_RES_ERROR_AS_NOTICE("sql error");
 
-       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);
+
+               /*
+                * and save a copy of the command status string to return as our
+                * result tuple
+                */
+               sql_cmd_status = GET_TEXT("ERROR");
+
+       }
+       else if (PQresultStatus(res) == PGRES_COMMAND_OK)
        {
                /* need a tuple descriptor representing one TEXT column */
                tupdesc = CreateTemplateTupleDesc(1, false);
@@ -773,7 +955,7 @@ dblink_exec(PG_FUNCTION_ARGS)
        PQclear(res);
 
        /* if needed, close the connection to the database and cleanup */
-       if (freeconn && fcinfo->nargs == 2)
+       if (freeconn)
                PQfinish(conn);
 
        PG_RETURN_TEXT_P(sql_cmd_status);
index cd670390d72d11c4b82f5ed6fa35e95a181830a6..c38f8be8882edb346373d12268448f150436c7d9 100644 (file)
 CREATE OR REPLACE FUNCTION dblink_connect (text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_connect'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink_connect (text, text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_connect'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink_disconnect ()
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_disconnect'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink_disconnect (text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_disconnect'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink_open (text,text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_open'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
+
+CREATE OR REPLACE FUNCTION dblink_open (text,text,bool)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_open'
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink_open (text,text,text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_open'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
+
+CREATE OR REPLACE FUNCTION dblink_open (text,text,text,bool)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_open'
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink_fetch (text,int)
 RETURNS setof record
 AS 'MODULE_PATHNAME','dblink_fetch'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
+
+CREATE OR REPLACE FUNCTION dblink_fetch (text,int,bool)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_fetch'
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink_fetch (text,text,int)
 RETURNS setof record
 AS 'MODULE_PATHNAME','dblink_fetch'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
+
+CREATE OR REPLACE FUNCTION dblink_fetch (text,text,int,bool)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_fetch'
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink_close (text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_close'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
+
+CREATE OR REPLACE FUNCTION dblink_close (text,bool)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_close'
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink_close (text,text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_close'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
+
+CREATE OR REPLACE FUNCTION dblink_close (text,text,bool)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_close'
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink (text,text)
 RETURNS setof record
 AS 'MODULE_PATHNAME','dblink_record'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
+
+CREATE OR REPLACE FUNCTION dblink (text,text,bool)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_record'
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink (text)
 RETURNS setof record
 AS 'MODULE_PATHNAME','dblink_record'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
+
+CREATE OR REPLACE FUNCTION dblink (text,bool)
+RETURNS setof record
+AS 'MODULE_PATHNAME','dblink_record'
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink_exec (text,text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_exec'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
+
+CREATE OR REPLACE FUNCTION dblink_exec (text,text,bool)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_exec'
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink_exec (text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_exec'
-LANGUAGE 'c' WITH (isstrict);
+LANGUAGE 'c' STRICT;
+
+CREATE OR REPLACE FUNCTION dblink_exec (text,bool)
+RETURNS text
+AS 'MODULE_PATHNAME','dblink_exec'
+LANGUAGE 'c' STRICT;
 
 CREATE TYPE dblink_pkey_results AS (position int4, colname text);
 
 CREATE OR REPLACE FUNCTION dblink_get_pkey (text)
 RETURNS setof dblink_pkey_results
 AS 'MODULE_PATHNAME','dblink_get_pkey'
-LANGUAGE 'c' WITH (isstrict);
+LANGUAGE 'c' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink_build_sql_insert (text, int2vector, int4, _text, _text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_build_sql_insert'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink_build_sql_delete (text, int2vector, int4, _text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_build_sql_delete'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink_build_sql_update (text, int2vector, int4, _text, _text)
 RETURNS text
 AS 'MODULE_PATHNAME','dblink_build_sql_update'
-LANGUAGE 'C' WITH (isstrict);
+LANGUAGE 'C' STRICT;
 
 CREATE OR REPLACE FUNCTION dblink_current_query ()
 RETURNS text
index 7c9cc3cde25554b1bd0c7438a2d8ec5961475225..23a3ce89ba55b4e8b5860455a59f4f7237a48928 100644 (file)
@@ -5,8 +5,8 @@ dblink_open -- Opens a cursor on a remote database
 
 Synopsis
 
-dblink_open(text cursorname, text sql)
-dblink_open(text connname, text cursorname, text sql)
+dblink_open(text cursorname, text sql [, bool fail_on_error])
+dblink_open(text connname, text cursorname, text sql [, bool fail_on_error])
 
 Inputs
 
@@ -23,6 +23,13 @@ Inputs
     sql statement that you wish to execute on the remote host
     e.g. "select * from pg_class"
 
+  fail_on_error
+
+    If true (default when not present) then an ERROR thrown on the remote side
+    of the connection causes an ERROR to also be thrown locally. If false, the
+    remote ERROR is locally treated as a NOTICE, and the return value is set
+    to 'ERROR'.
+
 Outputs
 
   Returns status = "OK"
@@ -56,8 +63,8 @@ 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)
+dblink_fetch(text cursorname, int32 howmany [, bool fail_on_error])
+dblink_fetch(text connname, text cursorname, int32 howmany [, bool fail_on_error])
 
 Inputs
 
@@ -75,6 +82,12 @@ Inputs
     starting at the current cursor position, moving forward. Once the cursor
     has positioned to the end, no more rows are produced.
 
+  fail_on_error
+
+    If true (default when not present) then an ERROR thrown on the remote side
+    of the connection causes an ERROR to also be thrown locally. If false, the
+    remote ERROR is locally treated as a NOTICE, and no rows are returned.
+
 Outputs
 
   Returns setof record
@@ -132,8 +145,8 @@ dblink_close -- Closes a cursor on a remote database
 
 Synopsis
 
-dblink_close(text cursorname)
-dblink_close(text connname, text cursorname)
+dblink_close(text cursorname [, bool fail_on_error])
+dblink_close(text connname, text cursorname [, bool fail_on_error])
 
 Inputs
 
@@ -145,6 +158,13 @@ Inputs
 
     a reference name for the cursor
 
+  fail_on_error
+
+    If true (default when not present) then an ERROR thrown on the remote side
+    of the connection causes an ERROR to also be thrown locally. If false, the
+    remote ERROR is locally treated as a NOTICE, and the return value is set
+    to 'ERROR'.
+
 Outputs
 
   Returns status = "OK"
diff --git a/contrib/dblink/doc/deprecated b/contrib/dblink/doc/deprecated
deleted file mode 100644 (file)
index 09a9998..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-==================================================================
-Name
-
-*DEPRECATED* use new dblink syntax
-dblink -- Returns a resource id for a data set from a remote database
-
-Synopsis
-
-dblink(text connstr, text sql)
-
-Inputs
-
-  connstr
-
-    standard libpq format connection srting, 
-    e.g. "hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres password=mypasswd"
-
-  sql
-
-    sql statement that you wish to execute on the remote host
-    e.g. "select * from pg_class"
-
-Outputs
-
-  Returns setof int (res_id)
-
-Example usage
-
-  select dblink('hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres password=mypasswd'
-               ,'select f1, f2 from mytable');
-
-==================================================================
-
-Name
-
-*DEPRECATED* use new dblink syntax
-dblink_tok -- Returns individual select field results from a dblink remote query
-
-Synopsis
-
-dblink_tok(int res_id, int fnumber)
-
-Inputs
-
-  res_id
-
-    a resource id returned by a call to dblink()
-
-  fnumber
-
-    the ordinal position (zero based) of the field to be returned from the dblink result set
-
-Outputs
-
-  Returns text
-
-Example usage
-
-  select dblink_tok(t1.dblink_p,0) as f1, dblink_tok(t1.dblink_p,1) as f2
-  from (select dblink('hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres password=mypasswd'
-                     ,'select f1, f2 from mytable') as dblink_p) as t1;
-
-
-==================================================================
-*DEPRECATED* use new dblink syntax
-A more convenient way to use dblink may be to create a view:
-
- create view myremotetable as
- select dblink_tok(t1.dblink_p,0) as f1, dblink_tok(t1.dblink_p,1) as f2
- from (select dblink('hostaddr=127.0.0.1 port=5432 dbname=template1 user=postgres password=postgres'
-                    ,'select proname, prosrc from pg_proc') as dblink_p) as t1;
-
-Then you can simply write:
-
-   select f1, f2 from myremotetable where f1 like 'bytea%';
-
-==================================================================
-Name
-*DEPRECATED* use new dblink_exec syntax
-dblink_last_oid -- Returns last inserted oid
-
-Synopsis
-
-dblink_last_oid(int res_id) RETURNS oid
-
-Inputs
-
-  res_id
-
-    any resource id returned by dblink function;
-
-Outputs
-
-  Returns oid of last inserted tuple
-
-Example usage
-
-test=# select dblink_last_oid(dblink('hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres password=mypasswd'
-               ,'insert into mytable (f1, f2) values (1,2)'));
-
- dblink_last_oid
-----------------
- 16553
-(1 row)
-
index 72a21276d950e9d99004002ab4123c2817757c2a..d4c09b22de2e0417fa3809add17cc5e72cc86188 100644 (file)
@@ -5,14 +5,15 @@ dblink_exec -- Executes an UPDATE/INSERT/DELETE on a remote database
 
 Synopsis
 
-dblink_exec(text connstr, text sql)
-dblink_exec(text connname, text sql)
-dblink_exec(text sql)
+dblink_exec(text connstr, text sql [, bool fail_on_error])
+dblink_exec(text connname, text sql [, bool fail_on_error])
+dblink_exec(text sql [, bool fail_on_error])
 
 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,
@@ -25,9 +26,16 @@ Inputs
     sql statement that you wish to execute on the remote host, e.g.:
        insert into foo values(0,'a','{"a0","b0","c0"}');
 
+  fail_on_error
+
+    If true (default when not present) then an ERROR thrown on the remote side
+    of the connection causes an ERROR to also be thrown locally. If false, the
+    remote ERROR is locally treated as a NOTICE, and the return value is set
+    to 'ERROR'.
+
 Outputs
 
-  Returns status of the command
+  Returns status of the command, or 'ERROR' if the command failed.
 
 Notes
   1) dblink_open starts an explicit transaction. If, after using dblink_open,
@@ -60,3 +68,12 @@ select dblink_exec('myconn','insert into foo values(21,''z'',''{"a0","b0","c0"}'
 ------------------
  INSERT 6432584 1
 (1 row)
+
+select dblink_exec('myconn','insert into pg_class values (''foo'')',false);
+NOTICE:  sql error
+DETAIL:  ERROR:  null value in column "relnamespace" violates not-null constraint
+
+ dblink_exec
+-------------
+ ERROR
+(1 row)
index 9c81417741744a1a18ca393a5ca3855d7c41371f..d232f80dbd93d340ac40030767ed06b46beeb75f 100644 (file)
@@ -5,9 +5,9 @@ dblink -- Returns a set from a remote database
 
 Synopsis
 
-dblink(text connstr, text sql)
-dblink(text connname, text sql)
-dblink(text sql)
+dblink(text connstr, text sql [, bool fail_on_error])
+dblink(text connname, text sql [, bool fail_on_error])
+dblink(text sql [, bool fail_on_error])
 
 Inputs
 
@@ -25,6 +25,12 @@ Inputs
     sql statement that you wish to execute on the remote host
     e.g. "select * from pg_class"
 
+  fail_on_error
+
+    If true (default when not present) then an ERROR thrown on the remote side
+    of the connection causes an ERROR to also be thrown locally. If false, the
+    remote ERROR is locally treated as a NOTICE, and no rows are returned.
+
 Outputs
 
   Returns setof record
index 7ef40b06d796ad224a256d51cecd515a5e732640..4aebfd974da57d4ce939699c8c92b2fe6ca5d072 100644 (file)
@@ -128,6 +128,23 @@ WHERE t.a > 7;
  9 | j | {a9,b9,c9}
 (2 rows)
 
+-- open a cursor with bad SQL and fail_on_error set to false
+SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foobar',false);
+NOTICE:  sql error
+DETAIL:  ERROR:  relation "foobar" does not exist
+
+ dblink_open 
+-------------
+ ERROR
+(1 row)
+
+-- reset remote transaction state
+SELECT dblink_exec('ABORT');
+ dblink_exec 
+-------------
+ ROLLBACK
+(1 row)
+
 -- open a cursor
 SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foo');
  dblink_open 
@@ -135,6 +152,20 @@ SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foo');
  OK
 (1 row)
 
+-- close the cursor
+SELECT dblink_close('rmt_foo_cursor',false);
+ dblink_close 
+--------------
+ OK
+(1 row)
+
+-- open the cursor again
+SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foo');
+ dblink_open 
+-------------
+ OK
+(1 row)
+
 -- fetch some data
 SELECT *
 FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]);
@@ -165,11 +196,38 @@ FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]);
  9 | j | {a9,b9,c9}
 (2 rows)
 
--- close the cursor
-SELECT dblink_close('rmt_foo_cursor');
+-- intentionally botch a fetch
+SELECT *
+FROM dblink_fetch('rmt_foobar_cursor',4,false) AS t(a int, b text, c text[]);
+NOTICE:  sql error
+DETAIL:  ERROR:  cursor "rmt_foobar_cursor" does not exist
+
+ a | b | c 
+---+---+---
+(0 rows)
+
+-- reset remote transaction state
+SELECT dblink_exec('ABORT');
+ dblink_exec 
+-------------
+ ROLLBACK
+(1 row)
+
+-- close the wrong cursor
+SELECT dblink_close('rmt_foobar_cursor',false);
+NOTICE:  sql error
+DETAIL:  ERROR:  cursor "rmt_foobar_cursor" does not exist
+
  dblink_close 
 --------------
- OK
+ ERROR
+(1 row)
+
+-- reset remote transaction state
+SELECT dblink_exec('ABORT');
+ dblink_exec 
+-------------
+ ROLLBACK
 (1 row)
 
 -- should generate 'cursor "rmt_foo_cursor" not found' error
@@ -178,6 +236,16 @@ FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]);
 ERROR:  sql error
 DETAIL:  ERROR:  cursor "rmt_foo_cursor" does not exist
 
+-- this time, 'cursor "rmt_foo_cursor" not found' as a notice
+SELECT *
+FROM dblink_fetch('rmt_foo_cursor',4,false) AS t(a int, b text, c text[]);
+NOTICE:  sql error
+DETAIL:  ERROR:  cursor "rmt_foo_cursor" does not exist
+
+ a | b | c 
+---+---+---
+(0 rows)
+
 -- close the persistent connection
 SELECT dblink_disconnect();
  dblink_disconnect 
@@ -232,6 +300,23 @@ FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[]);
  11 | l | {a11,b11,c11}
 (12 rows)
 
+-- bad remote select
+SELECT *
+FROM dblink('SELECT * FROM foobar',false) AS t(a int, b text, c text[]);
+NOTICE:  sql error
+DETAIL:  ERROR:  relation "foobar" does not exist
+
+ a | b | c 
+---+---+---
+(0 rows)
+
+-- reset remote transaction state
+SELECT dblink_exec('ABORT');
+ dblink_exec 
+-------------
+ ROLLBACK
+(1 row)
+
 -- change some data
 SELECT dblink_exec('UPDATE foo SET f3[2] = ''b99'' WHERE f1 = 11');
  dblink_exec 
@@ -248,6 +333,23 @@ WHERE a = 11;
  11 | l | {a11,b99,c11}
 (1 row)
 
+-- botch a change to some other data
+SELECT dblink_exec('UPDATE foobar SET f3[2] = ''b99'' WHERE f1 = 11',false);
+NOTICE:  sql error
+DETAIL:  ERROR:  relation "foobar" does not exist
+
+ dblink_exec 
+-------------
+ ERROR
+(1 row)
+
+-- reset remote transaction state
+SELECT dblink_exec('ABORT');
+ dblink_exec 
+-------------
+ ROLLBACK
+(1 row)
+
 -- delete some data
 SELECT dblink_exec('DELETE FROM foo WHERE f1 = 11');
  dblink_exec 
@@ -298,6 +400,24 @@ WHERE t.a > 7;
  10 | k | {a10,b10,c10}
 (3 rows)
 
+-- use the named persistent connection, but get it wrong
+SELECT *
+FROM dblink('myconn','SELECT * FROM foobar',false) AS t(a int, b text, c text[])
+WHERE t.a > 7;
+NOTICE:  sql error
+DETAIL:  ERROR:  relation "foobar" does not exist
+
+ a | b | c 
+---+---+---
+(0 rows)
+
+-- reset remote transaction state
+SELECT dblink_exec('myconn','ABORT');
+ dblink_exec 
+-------------
+ ROLLBACK
+(1 row)
+
 -- create a second named persistent connection
 -- should error with "duplicate connection name"
 SELECT dblink_connect('myconn','dbname=regression');
@@ -327,6 +447,23 @@ SELECT dblink_disconnect('myconn2');
  OK
 (1 row)
 
+-- open a cursor incorrectly
+SELECT dblink_open('myconn','rmt_foo_cursor','SELECT * FROM foobar',false);
+NOTICE:  sql error
+DETAIL:  ERROR:  relation "foobar" does not exist
+
+ dblink_open 
+-------------
+ ERROR
+(1 row)
+
+-- reset remote transaction state
+SELECT dblink_exec('myconn','ABORT');
+ dblink_exec 
+-------------
+ ROLLBACK
+(1 row)
+
 -- open a cursor
 SELECT dblink_open('myconn','rmt_foo_cursor','SELECT * FROM foo');
  dblink_open 
@@ -365,11 +502,21 @@ FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]);
  10 | k | {a10,b10,c10}
 (3 rows)
 
--- close the cursor
-SELECT dblink_close('myconn','rmt_foo_cursor');
- dblink_close 
---------------
- OK
+-- fetch some data incorrectly
+SELECT *
+FROM dblink_fetch('myconn','rmt_foobar_cursor',4,false) AS t(a int, b text, c text[]);
+NOTICE:  sql error
+DETAIL:  ERROR:  cursor "rmt_foobar_cursor" does not exist
+
+ a | b | c 
+---+---+---
+(0 rows)
+
+-- reset remote transaction state
+SELECT dblink_exec('myconn','ABORT');
+ dblink_exec 
+-------------
+ ROLLBACK
 (1 row)
 
 -- should generate 'cursor "rmt_foo_cursor" not found' error
index d0e448fc0b003250eb4ddc16c89dbc1a6d06d3f8..61d0adb02099fb585b98964a1c85bcbee1b140d7 100644 (file)
@@ -81,9 +81,21 @@ SELECT *
 FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[])
 WHERE t.a > 7;
 
+-- open a cursor with bad SQL and fail_on_error set to false
+SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foobar',false);
+
+-- reset remote transaction state
+SELECT dblink_exec('ABORT');
+
 -- open a cursor
 SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foo');
 
+-- close the cursor
+SELECT dblink_close('rmt_foo_cursor',false);
+
+-- open the cursor again
+SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foo');
+
 -- fetch some data
 SELECT *
 FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]);
@@ -95,13 +107,27 @@ FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]);
 SELECT *
 FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]);
 
--- close the cursor
-SELECT dblink_close('rmt_foo_cursor');
+-- intentionally botch a fetch
+SELECT *
+FROM dblink_fetch('rmt_foobar_cursor',4,false) AS t(a int, b text, c text[]);
+
+-- reset remote transaction state
+SELECT dblink_exec('ABORT');
+
+-- close the wrong cursor
+SELECT dblink_close('rmt_foobar_cursor',false);
+
+-- reset remote transaction state
+SELECT dblink_exec('ABORT');
 
 -- should generate 'cursor "rmt_foo_cursor" not found' error
 SELECT *
 FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]);
 
+-- this time, 'cursor "rmt_foo_cursor" not found' as a notice
+SELECT *
+FROM dblink_fetch('rmt_foo_cursor',4,false) AS t(a int, b text, c text[]);
+
 -- close the persistent connection
 SELECT dblink_disconnect();
 
@@ -125,6 +151,13 @@ SELECT substr(dblink_exec('INSERT INTO foo VALUES(11,''l'',''{"a11","b11","c11"}
 SELECT *
 FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[]);
 
+-- bad remote select
+SELECT *
+FROM dblink('SELECT * FROM foobar',false) AS t(a int, b text, c text[]);
+
+-- reset remote transaction state
+SELECT dblink_exec('ABORT');
+
 -- change some data
 SELECT dblink_exec('UPDATE foo SET f3[2] = ''b99'' WHERE f1 = 11');
 
@@ -133,6 +166,12 @@ SELECT *
 FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[])
 WHERE a = 11;
 
+-- botch a change to some other data
+SELECT dblink_exec('UPDATE foobar SET f3[2] = ''b99'' WHERE f1 = 11',false);
+
+-- reset remote transaction state
+SELECT dblink_exec('ABORT');
+
 -- delete some data
 SELECT dblink_exec('DELETE FROM foo WHERE f1 = 11');
 
@@ -161,6 +200,14 @@ SELECT *
 FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[])
 WHERE t.a > 7;
 
+-- use the named persistent connection, but get it wrong
+SELECT *
+FROM dblink('myconn','SELECT * FROM foobar',false) AS t(a int, b text, c text[])
+WHERE t.a > 7;
+
+-- reset remote transaction state
+SELECT dblink_exec('myconn','ABORT');
+
 -- create a second named persistent connection
 -- should error with "duplicate connection name"
 SELECT dblink_connect('myconn','dbname=regression');
@@ -176,6 +223,12 @@ WHERE t.a > 7;
 -- close the second named persistent connection
 SELECT dblink_disconnect('myconn2');
 
+-- open a cursor incorrectly
+SELECT dblink_open('myconn','rmt_foo_cursor','SELECT * FROM foobar',false);
+
+-- reset remote transaction state
+SELECT dblink_exec('myconn','ABORT');
+
 -- open a cursor
 SELECT dblink_open('myconn','rmt_foo_cursor','SELECT * FROM foo');
 
@@ -190,8 +243,12 @@ 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[]);
 
--- close the cursor
-SELECT dblink_close('myconn','rmt_foo_cursor');
+-- fetch some data incorrectly
+SELECT *
+FROM dblink_fetch('myconn','rmt_foobar_cursor',4,false) AS t(a int, b text, c text[]);
+
+-- reset remote transaction state
+SELECT dblink_exec('myconn','ABORT');
 
 -- should generate 'cursor "rmt_foo_cursor" not found' error
 SELECT *