Re: [BUGS] BUG #4203: perform dblink() in begin/exception returns wrong SQLSTATE code - Mailing list pgsql-patches

From Joe Conway
Subject Re: [BUGS] BUG #4203: perform dblink() in begin/exception returns wrong SQLSTATE code
Date
Msg-id 4843378D.2080305@joeconway.com
Whole thread Raw
In response to Re: [BUGS] BUG #4203: perform dblink() in begin/exception returns wrong SQLSTATE code  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: Re: [BUGS] BUG #4203: perform dblink() in begin/exception returns wrong SQLSTATE code  (Joe Conway <mail@joeconway.com>)
List pgsql-patches
Tom Lane wrote:
> Joe Conway <mail@joeconway.com> writes:
>> [ improved patch ]
>
> Still a couple quibbles:

[ more good feedback ]

All valid complaints, and noticeably improved/simplified code as a
result. Third patch attached.

Joe
Index: dblink.c
===================================================================
RCS file: /opt/src/cvs/pgsql/contrib/dblink/dblink.c,v
retrieving revision 1.73
diff -c -r1.73 dblink.c
*** dblink.c    4 Apr 2008 17:02:56 -0000    1.73
--- dblink.c    1 Jun 2008 23:52:39 -0000
***************
*** 94,99 ****
--- 94,100 ----
  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,158 ****
          } \
      } 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) \
      do { \
!             msg = pstrdup(PQerrorMessage(conn)); \
!             if (res) \
!                 PQclear(res); \
!             ereport(ERROR, \
!                     (errcode(ERRCODE_SYNTAX_ERROR), \
!                      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 \
--- 126,145 ----
          } \
      } while (0)

! #define xpstrdup(var_c, var_) \
      do { \
!         if (var_ != NULL) \
!             var_c = pstrdup(var_); \
!         else \
!             var_c = 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_CONN_NOT_AVAIL \
***************
*** 396,408 ****
      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"));
!         }
      }

      PQclear(res);
--- 383,390 ----
      res = PQexec(conn, buf.data);
      if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
      {
!         dblink_res_error(conname, res, "could not open cursor", fail);
!         PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
      }

      PQclear(res);
***************
*** 470,482 ****
      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"));
!         }
      }

      PQclear(res);
--- 452,459 ----
      res = PQexec(conn, buf.data);
      if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
      {
!         dblink_res_error(conname, res, "could not close cursor", fail);
!         PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
      }

      PQclear(res);
***************
*** 513,519 ****
      int            call_cntr;
      int            max_calls;
      AttInMetadata *attinmeta;
-     char       *msg;
      PGresult   *res = NULL;
      MemoryContext oldcontext;
      char       *conname = NULL;
--- 490,495 ----
***************
*** 590,602 ****
              (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);
!             }
          }
          else if (PQresultStatus(res) == PGRES_COMMAND_OK)
          {
--- 566,573 ----
              (PQresultStatus(res) != PGRES_COMMAND_OK &&
               PQresultStatus(res) != PGRES_TUPLES_OK))
          {
!             dblink_res_error(conname, res, "could not fetch from cursor", fail);
!             SRF_RETURN_DONE(funcctx);
          }
          else if (PQresultStatus(res) == PGRES_COMMAND_OK)
          {
***************
*** 846,860 ****
                  (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);
!                 }
              }

              if (PQresultStatus(res) == PGRES_COMMAND_OK)
--- 817,826 ----
                  (PQresultStatus(res) != PGRES_COMMAND_OK &&
                   PQresultStatus(res) != PGRES_TUPLES_OK))
              {
!                 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,1189 ****
          (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");

          /* need a tuple descriptor representing one TEXT column */
          tupdesc = CreateTemplateTupleDesc(1, false);
--- 1146,1152 ----
          (PQresultStatus(res) != PGRES_COMMAND_OK &&
           PQresultStatus(res) != PGRES_TUPLES_OK))
      {
!         dblink_res_error(conname, res, "could not execute command", fail);

          /* need a tuple descriptor representing one TEXT column */
          tupdesc = CreateTemplateTupleDesc(1, false);
***************
*** 1195,1201 ****
           * result tuple
           */
          sql_cmd_status = cstring_to_text("ERROR");
-
      }
      else if (PQresultStatus(res) == PGRES_COMMAND_OK)
      {
--- 1158,1163 ----
***************
*** 2288,2290 ****
--- 2250,2303 ----
          }
      }
  }
+
+ 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: expected/dblink.out
===================================================================
RCS file: /opt/src/cvs/pgsql/contrib/dblink/expected/dblink.out,v
retrieving revision 1.23
diff -c -r1.23 dblink.out
*** expected/dblink.out    6 Apr 2008 16:54:48 -0000    1.23
--- expected/dblink.out    1 Jun 2008 21:59:11 -0000
***************
*** 125,133 ****

  -- 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
--- 125,132 ----

  -- open a cursor with bad SQL and fail_on_error set to false
  SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foobar',false);
! NOTICE:  relation "foobar" does not exist
! CONTEXT:  Error occurred on dblink connection named "unnamed": could not open cursor.
   dblink_open
  -------------
   ERROR
***************
*** 194,202 ****
  -- 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)
--- 193,200 ----
  -- intentionally botch a fetch
  SELECT *
  FROM dblink_fetch('rmt_foobar_cursor',4,false) AS t(a int, b text, c text[]);
! 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,218 ****

  -- 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
  --------------
   ERROR
--- 208,215 ----

  -- close the wrong cursor
  SELECT dblink_close('rmt_foobar_cursor',false);
! NOTICE:  cursor "rmt_foobar_cursor" does not exist
! CONTEXT:  Error occurred on dblink connection named "unnamed": could not close cursor.
   dblink_close
  --------------
   ERROR
***************
*** 221,235 ****
  -- 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
!
  -- 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)
--- 218,230 ----
  -- 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:  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:  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,299 ****
  -- 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)
--- 286,293 ----
  -- bad remote select
  SELECT *
  FROM dblink('SELECT * FROM foobar',false) AS t(a int, b text, c text[]);
! NOTICE:  relation "foobar" does not exist
! CONTEXT:  Error occurred on dblink connection named "unnamed": could not execute query.
   a | b | c
  ---+---+---
  (0 rows)
***************
*** 316,324 ****

  -- 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
--- 310,317 ----

  -- botch a change to some other data
  SELECT dblink_exec('UPDATE foobar SET f3[2] = ''b99'' WHERE f1 = 11',false);
! NOTICE:  relation "foobar" does not exist
! CONTEXT:  Error occurred on dblink connection named "unnamed": could not execute command.
   dblink_exec
  -------------
   ERROR
***************
*** 378,386 ****
  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)
--- 371,378 ----
  SELECT *
  FROM dblink('myconn','SELECT * FROM foobar',false) AS t(a int, b text, c text[])
  WHERE t.a > 7;
! NOTICE:  relation "foobar" does not exist
! CONTEXT:  Error occurred on dblink connection named "unnamed": could not execute query.
   a | b | c
  ---+---+---
  (0 rows)
***************
*** 416,424 ****

  -- 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
--- 408,415 ----

  -- open a cursor incorrectly
  SELECT dblink_open('myconn','rmt_foo_cursor','SELECT * FROM foobar',false);
! NOTICE:  relation "foobar" does not exist
! CONTEXT:  Error occurred on dblink connection named "myconn": could not open cursor.
   dblink_open
  -------------
   ERROR
***************
*** 503,511 ****

  -- 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
!
  -- reset remote transaction state
  SELECT dblink_exec('myconn','ABORT');
   dblink_exec
--- 494,501 ----

  -- this should fail because there is no open transaction
  SELECT dblink_exec('myconn','DECLARE xact_test CURSOR FOR SELECT * FROM foo');
! 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,562 ****
  -- 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)
--- 544,551 ----
  -- fetch some data incorrectly
  SELECT *
  FROM dblink_fetch('myconn','rmt_foobar_cursor',4,false) AS t(a int, b text, c text[]);
! 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,579 ****
  -- 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
!
  -- close the named persistent connection
  SELECT dblink_disconnect('myconn');
   dblink_disconnect
--- 560,567 ----
  -- 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:  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

pgsql-patches by date:

Previous
From: Tom Lane
Date:
Subject: Re: [BUGS] BUG #4203: perform dblink() in begin/exception returns wrong SQLSTATE code
Next
From: Stephen Frost
Date:
Subject: Re: Feature: give pg_dump a WHERE clause expression