*
- * $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;
  *
 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;
        } \
    } 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 \
    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);
    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);
    int         call_cntr;
    int         max_calls;
    AttInMetadata *attinmeta;
-   char       *msg;
    PGresult   *res = NULL;
    MemoryContext oldcontext;
    char       *conname = NULL;
            (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)
        {
                (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)
        (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);
         * result tuple
         */
        sql_cmd_status = cstring_to_text("ERROR");
-
    }
    else if (PQresultStatus(res) == PGRES_COMMAND_OK)
    {
        }
    }
 }
+
+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)));
+}
 
 
 -- 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
 -- 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)
 
 -- 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
 -- 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)
 -- 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)
 
 -- 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
 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)
 
 -- 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
 
 -- 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 
 -- 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)
 -- 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