]> granicus.if.org Git - postgresql/commitdiff
When an ERROR happens on a dblink remote connection, take
authorJoe Conway <mail@joeconway.com>
Thu, 3 Jul 2008 03:56:57 +0000 (03:56 +0000)
committerJoe Conway <mail@joeconway.com>
Thu, 3 Jul 2008 03:56:57 +0000 (03:56 +0000)
pains to pass the ERROR message components locally, including
using the passed SQLSTATE. Also wrap the passed info in an
appropriate CONTEXT message. Addresses complaint by Henry
Combrinck. Joe Conway, with much good advice from Tom Lane.

contrib/dblink/dblink.c
contrib/dblink/expected/dblink.out

index 4e29ee94072f1154489db3c0e4afa519c33553b9..226e737e2d9b0762f43806be9422f2a0eaccf37b 100644 (file)
@@ -8,7 +8,7 @@
  * Darko Prenosil <Darko.Prenosil@finteh.hr>
  * Shridhar Daithankar <shridhar_daithankar@persistent.co.in>
  *
- * $PostgreSQL: pgsql/contrib/dblink/dblink.c,v 1.73 2008/04/04 17:02:56 momjian Exp $
+ * $PostgreSQL: pgsql/contrib/dblink/dblink.c,v 1.74 2008/07/03 03:56:57 joe Exp $
  * Copyright (c) 2001-2008, PostgreSQL Global Development Group
  * ALL RIGHTS RESERVED;
  *
@@ -94,6 +94,7 @@ static HeapTuple get_tuple_of_interest(Oid relid, int2vector *pkattnums, int16 p
 static Oid     get_relid_from_relname(text *relname_text);
 static char *generate_relation_name(Oid relid);
 static void dblink_security_check(PGconn *conn, remoteConn *rconn);
+static void dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_msg, bool fail);
 
 /* Global */
 static remoteConn *pconn = NULL;
@@ -125,34 +126,20 @@ typedef struct remoteConnHashEnt
                } \
        } while (0)
 
-#define DBLINK_RES_INTERNALERROR(p2) \
-       do { \
-                       msg = pstrdup(PQerrorMessage(conn)); \
-                       if (res) \
-                               PQclear(res); \
-                       elog(ERROR, "%s: %s", p2, msg); \
-       } while (0)
-
-#define DBLINK_RES_ERROR(p2) \
+#define xpstrdup(var_c, var_) \
        do { \
-                       msg = pstrdup(PQerrorMessage(conn)); \
-                       if (res) \
-                               PQclear(res); \
-                       ereport(ERROR, \
-                                       (errcode(ERRCODE_SYNTAX_ERROR), \
-                                        errmsg("%s", p2), \
-                                        errdetail("%s", msg))); \
+               if (var_ != NULL) \
+                       var_c = pstrdup(var_); \
+               else \
+                       var_c = NULL; \
        } while (0)
 
-#define DBLINK_RES_ERROR_AS_NOTICE(p2) \
+#define DBLINK_RES_INTERNALERROR(p2) \
        do { \
                        msg = pstrdup(PQerrorMessage(conn)); \
                        if (res) \
                                PQclear(res); \
-                       ereport(NOTICE, \
-                                       (errcode(ERRCODE_SYNTAX_ERROR), \
-                                        errmsg("%s", p2), \
-                                        errdetail("%s", msg))); \
+                       elog(ERROR, "%s: %s", p2, msg); \
        } while (0)
 
 #define DBLINK_CONN_NOT_AVAIL \
@@ -396,13 +383,8 @@ dblink_open(PG_FUNCTION_ARGS)
        res = PQexec(conn, buf.data);
        if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
        {
-               if (fail)
-                       DBLINK_RES_ERROR("sql error");
-               else
-               {
-                       DBLINK_RES_ERROR_AS_NOTICE("sql error");
-                       PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
-               }
+               dblink_res_error(conname, res, "could not open cursor", fail);
+               PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
        }
 
        PQclear(res);
@@ -470,13 +452,8 @@ dblink_close(PG_FUNCTION_ARGS)
        res = PQexec(conn, buf.data);
        if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
        {
-               if (fail)
-                       DBLINK_RES_ERROR("sql error");
-               else
-               {
-                       DBLINK_RES_ERROR_AS_NOTICE("sql error");
-                       PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
-               }
+               dblink_res_error(conname, res, "could not close cursor", fail);
+               PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
        }
 
        PQclear(res);
@@ -513,7 +490,6 @@ dblink_fetch(PG_FUNCTION_ARGS)
        int                     call_cntr;
        int                     max_calls;
        AttInMetadata *attinmeta;
-       char       *msg;
        PGresult   *res = NULL;
        MemoryContext oldcontext;
        char       *conname = NULL;
@@ -590,13 +566,8 @@ dblink_fetch(PG_FUNCTION_ARGS)
                        (PQresultStatus(res) != PGRES_COMMAND_OK &&
                         PQresultStatus(res) != PGRES_TUPLES_OK))
                {
-                       if (fail)
-                               DBLINK_RES_ERROR("sql error");
-                       else
-                       {
-                               DBLINK_RES_ERROR_AS_NOTICE("sql error");
-                               SRF_RETURN_DONE(funcctx);
-                       }
+                       dblink_res_error(conname, res, "could not fetch from cursor", fail);
+                       SRF_RETURN_DONE(funcctx);
                }
                else if (PQresultStatus(res) == PGRES_COMMAND_OK)
                {
@@ -846,15 +817,10 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async, bool do_get)
                                (PQresultStatus(res) != PGRES_COMMAND_OK &&
                                 PQresultStatus(res) != PGRES_TUPLES_OK))
                        {
-                               if (fail)
-                                       DBLINK_RES_ERROR("sql error");
-                               else
-                               {
-                                       DBLINK_RES_ERROR_AS_NOTICE("sql error");
-                                       if (freeconn)
-                                               PQfinish(conn);
-                                       SRF_RETURN_DONE(funcctx);
-                               }
+                               dblink_res_error(conname, res, "could not execute query", fail);
+                               if (freeconn)
+                                       PQfinish(conn);
+                               SRF_RETURN_DONE(funcctx);
                        }
 
                        if (PQresultStatus(res) == PGRES_COMMAND_OK)
@@ -1180,10 +1146,7 @@ dblink_exec(PG_FUNCTION_ARGS)
                (PQresultStatus(res) != PGRES_COMMAND_OK &&
                 PQresultStatus(res) != PGRES_TUPLES_OK))
        {
-               if (fail)
-                       DBLINK_RES_ERROR("sql error");
-               else
-                       DBLINK_RES_ERROR_AS_NOTICE("sql error");
+               dblink_res_error(conname, res, "could not execute command", fail);
 
                /* need a tuple descriptor representing one TEXT column */
                tupdesc = CreateTemplateTupleDesc(1, false);
@@ -1195,7 +1158,6 @@ dblink_exec(PG_FUNCTION_ARGS)
                 * result tuple
                 */
                sql_cmd_status = cstring_to_text("ERROR");
-
        }
        else if (PQresultStatus(res) == PGRES_COMMAND_OK)
        {
@@ -2288,3 +2250,54 @@ dblink_security_check(PGconn *conn, remoteConn *rconn)
                }
        }
 }
+
+static void
+dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_msg, bool fail)
+{
+       int                     level;
+       char       *pg_diag_sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
+       char       *pg_diag_message_primary = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
+       char       *pg_diag_message_detail = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL);
+       char       *pg_diag_message_hint = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT);
+       char       *pg_diag_context = PQresultErrorField(res, PG_DIAG_CONTEXT);
+       int                     sqlstate;
+       char       *message_primary;
+       char       *message_detail;
+       char       *message_hint;
+       char       *message_context;
+       const char *dblink_context_conname = "unnamed";
+
+       if (fail)
+               level = ERROR;
+       else
+               level = NOTICE;
+
+       if (pg_diag_sqlstate)
+               sqlstate = MAKE_SQLSTATE(pg_diag_sqlstate[0],
+                                                                pg_diag_sqlstate[1],
+                                                                pg_diag_sqlstate[2],
+                                                                pg_diag_sqlstate[3],
+                                                                pg_diag_sqlstate[4]);
+       else
+               sqlstate = ERRCODE_CONNECTION_FAILURE;
+
+       xpstrdup(message_primary, pg_diag_message_primary);
+       xpstrdup(message_detail, pg_diag_message_detail);
+       xpstrdup(message_hint, pg_diag_message_hint);
+       xpstrdup(message_context, pg_diag_context);
+
+       if (res)
+               PQclear(res);
+
+       if (conname)
+               dblink_context_conname = conname;
+
+       ereport(level,
+               (errcode(sqlstate),
+                message_primary ? errmsg("%s", message_primary) : errmsg("unknown error"),
+                message_detail ? errdetail("%s", message_detail) : 0,
+                message_hint ? errhint("%s", message_hint) : 0,
+                message_context ? errcontext("%s", message_context) : 0,
+                errcontext("Error occurred on dblink connection named \"%s\": %s.",
+                                       dblink_context_conname, dblink_context_msg)));
+}
index 170d69c286c155ff55a28a4f44369fd063b17eba..f2eb6aa5e2fc65b5fd8f5b17455b1e544b10df51 100644 (file)
@@ -125,9 +125,8 @@ 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);
-NOTICE:  sql error
-DETAIL:  ERROR:  relation "foobar" does not exist
-
+NOTICE:  relation "foobar" does not exist
+CONTEXT:  Error occurred on dblink connection named "unnamed": could not open cursor.
  dblink_open 
 -------------
  ERROR
@@ -194,9 +193,8 @@ FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]);
 -- 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
-
+NOTICE:  cursor "rmt_foobar_cursor" does not exist
+CONTEXT:  Error occurred on dblink connection named "unnamed": could not fetch from cursor.
  a | b | c 
 ---+---+---
 (0 rows)
@@ -210,9 +208,8 @@ SELECT dblink_exec('ABORT');
 
 -- close the wrong cursor
 SELECT dblink_close('rmt_foobar_cursor',false);
-NOTICE:  sql error
-DETAIL:  ERROR:  cursor "rmt_foobar_cursor" does not exist
-
+NOTICE:  cursor "rmt_foobar_cursor" does not exist
+CONTEXT:  Error occurred on dblink connection named "unnamed": could not close cursor.
  dblink_close 
 --------------
  ERROR
@@ -221,15 +218,13 @@ DETAIL:  ERROR:  cursor "rmt_foobar_cursor" does not exist
 -- 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[]);
-ERROR:  sql error
-DETAIL:  ERROR:  cursor "rmt_foo_cursor" does not exist
-
+ERROR:  cursor "rmt_foo_cursor" does not exist
+CONTEXT:  Error occurred on dblink connection named "unnamed": could not fetch from cursor.
 -- 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
-
+NOTICE:  cursor "rmt_foo_cursor" does not exist
+CONTEXT:  Error occurred on dblink connection named "unnamed": could not fetch from cursor.
  a | b | c 
 ---+---+---
 (0 rows)
@@ -291,9 +286,8 @@ 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[]);
-NOTICE:  sql error
-DETAIL:  ERROR:  relation "foobar" does not exist
-
+NOTICE:  relation "foobar" does not exist
+CONTEXT:  Error occurred on dblink connection named "unnamed": could not execute query.
  a | b | c 
 ---+---+---
 (0 rows)
@@ -316,9 +310,8 @@ WHERE a = 11;
 
 -- 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
-
+NOTICE:  relation "foobar" does not exist
+CONTEXT:  Error occurred on dblink connection named "unnamed": could not execute command.
  dblink_exec 
 -------------
  ERROR
@@ -378,9 +371,8 @@ WHERE t.a > 7;
 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
-
+NOTICE:  relation "foobar" does not exist
+CONTEXT:  Error occurred on dblink connection named "unnamed": could not execute query.
  a | b | c 
 ---+---+---
 (0 rows)
@@ -416,9 +408,8 @@ SELECT dblink_disconnect('myconn2');
 
 -- 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
-
+NOTICE:  relation "foobar" does not exist
+CONTEXT:  Error occurred on dblink connection named "myconn": could not open cursor.
  dblink_open 
 -------------
  ERROR
@@ -503,9 +494,8 @@ SELECT dblink_close('myconn','rmt_foo_cursor');
 
 -- this should fail because there is no open transaction
 SELECT dblink_exec('myconn','DECLARE xact_test CURSOR FOR SELECT * FROM foo');
-ERROR:  sql error
-DETAIL:  ERROR:  DECLARE CURSOR can only be used in transaction blocks
-
+ERROR:  DECLARE CURSOR can only be used in transaction blocks
+CONTEXT:  Error occurred on dblink connection named "unnamed": could not execute command.
 -- reset remote transaction state
 SELECT dblink_exec('myconn','ABORT');
  dblink_exec 
@@ -554,9 +544,8 @@ FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]);
 -- 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
-
+NOTICE:  cursor "rmt_foobar_cursor" does not exist
+CONTEXT:  Error occurred on dblink connection named "myconn": could not fetch from cursor.
  a | b | c 
 ---+---+---
 (0 rows)
@@ -571,9 +560,8 @@ SELECT dblink_exec('myconn','ABORT');
 -- should generate 'cursor "rmt_foo_cursor" not found' error
 SELECT *
 FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]);
-ERROR:  sql error
-DETAIL:  ERROR:  cursor "rmt_foo_cursor" does not exist
-
+ERROR:  cursor "rmt_foo_cursor" does not exist
+CONTEXT:  Error occurred on dblink connection named "myconn": could not fetch from cursor.
 -- close the named persistent connection
 SELECT dblink_disconnect('myconn');
  dblink_disconnect