Commit c0241b95 authored by Joe Conway's avatar Joe Conway

When an ERROR happens on a dblink remote connection, take

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.
parent 0a8f6b79
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Darko Prenosil <Darko.Prenosil@finteh.hr> * Darko Prenosil <Darko.Prenosil@finteh.hr>
* Shridhar Daithankar <shridhar_daithankar@persistent.co.in> * 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 * Copyright (c) 2001-2008, PostgreSQL Global Development Group
* ALL RIGHTS RESERVED; * ALL RIGHTS RESERVED;
* *
...@@ -94,6 +94,7 @@ static HeapTuple get_tuple_of_interest(Oid relid, int2vector *pkattnums, int16 p ...@@ -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 Oid get_relid_from_relname(text *relname_text);
static char *generate_relation_name(Oid relid); static char *generate_relation_name(Oid relid);
static void dblink_security_check(PGconn *conn, remoteConn *rconn); 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 */ /* Global */
static remoteConn *pconn = NULL; static remoteConn *pconn = NULL;
...@@ -125,34 +126,20 @@ typedef struct remoteConnHashEnt ...@@ -125,34 +126,20 @@ typedef struct remoteConnHashEnt
} \ } \
} while (0) } while (0)
#define DBLINK_RES_INTERNALERROR(p2) \ #define xpstrdup(var_c, var_) \
do { \
msg = pstrdup(PQerrorMessage(conn)); \
if (res) \
PQclear(res); \
elog(ERROR, "%s: %s", p2, msg); \
} while (0)
#define DBLINK_RES_ERROR(p2) \
do { \ do { \
msg = pstrdup(PQerrorMessage(conn)); \ if (var_ != NULL) \
if (res) \ var_c = pstrdup(var_); \
PQclear(res); \ else \
ereport(ERROR, \ var_c = NULL; \
(errcode(ERRCODE_SYNTAX_ERROR), \
errmsg("%s", p2), \
errdetail("%s", msg))); \
} while (0) } while (0)
#define DBLINK_RES_ERROR_AS_NOTICE(p2) \ #define DBLINK_RES_INTERNALERROR(p2) \
do { \ do { \
msg = pstrdup(PQerrorMessage(conn)); \ msg = pstrdup(PQerrorMessage(conn)); \
if (res) \ if (res) \
PQclear(res); \ PQclear(res); \
ereport(NOTICE, \ elog(ERROR, "%s: %s", p2, msg); \
(errcode(ERRCODE_SYNTAX_ERROR), \
errmsg("%s", p2), \
errdetail("%s", msg))); \
} while (0) } while (0)
#define DBLINK_CONN_NOT_AVAIL \ #define DBLINK_CONN_NOT_AVAIL \
...@@ -396,13 +383,8 @@ dblink_open(PG_FUNCTION_ARGS) ...@@ -396,13 +383,8 @@ dblink_open(PG_FUNCTION_ARGS)
res = PQexec(conn, buf.data); res = PQexec(conn, buf.data);
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
{ {
if (fail) dblink_res_error(conname, res, "could not open cursor", fail);
DBLINK_RES_ERROR("sql error"); PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
else
{
DBLINK_RES_ERROR_AS_NOTICE("sql error");
PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
}
} }
PQclear(res); PQclear(res);
...@@ -470,13 +452,8 @@ dblink_close(PG_FUNCTION_ARGS) ...@@ -470,13 +452,8 @@ dblink_close(PG_FUNCTION_ARGS)
res = PQexec(conn, buf.data); res = PQexec(conn, buf.data);
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
{ {
if (fail) dblink_res_error(conname, res, "could not close cursor", fail);
DBLINK_RES_ERROR("sql error"); PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
else
{
DBLINK_RES_ERROR_AS_NOTICE("sql error");
PG_RETURN_TEXT_P(cstring_to_text("ERROR"));
}
} }
PQclear(res); PQclear(res);
...@@ -513,7 +490,6 @@ dblink_fetch(PG_FUNCTION_ARGS) ...@@ -513,7 +490,6 @@ dblink_fetch(PG_FUNCTION_ARGS)
int call_cntr; int call_cntr;
int max_calls; int max_calls;
AttInMetadata *attinmeta; AttInMetadata *attinmeta;
char *msg;
PGresult *res = NULL; PGresult *res = NULL;
MemoryContext oldcontext; MemoryContext oldcontext;
char *conname = NULL; char *conname = NULL;
...@@ -590,13 +566,8 @@ dblink_fetch(PG_FUNCTION_ARGS) ...@@ -590,13 +566,8 @@ dblink_fetch(PG_FUNCTION_ARGS)
(PQresultStatus(res) != PGRES_COMMAND_OK && (PQresultStatus(res) != PGRES_COMMAND_OK &&
PQresultStatus(res) != PGRES_TUPLES_OK)) PQresultStatus(res) != PGRES_TUPLES_OK))
{ {
if (fail) dblink_res_error(conname, res, "could not fetch from cursor", fail);
DBLINK_RES_ERROR("sql error"); SRF_RETURN_DONE(funcctx);
else
{
DBLINK_RES_ERROR_AS_NOTICE("sql error");
SRF_RETURN_DONE(funcctx);
}
} }
else if (PQresultStatus(res) == PGRES_COMMAND_OK) else if (PQresultStatus(res) == PGRES_COMMAND_OK)
{ {
...@@ -846,15 +817,10 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async, bool do_get) ...@@ -846,15 +817,10 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async, bool do_get)
(PQresultStatus(res) != PGRES_COMMAND_OK && (PQresultStatus(res) != PGRES_COMMAND_OK &&
PQresultStatus(res) != PGRES_TUPLES_OK)) PQresultStatus(res) != PGRES_TUPLES_OK))
{ {
if (fail) dblink_res_error(conname, res, "could not execute query", fail);
DBLINK_RES_ERROR("sql error"); if (freeconn)
else PQfinish(conn);
{ SRF_RETURN_DONE(funcctx);
DBLINK_RES_ERROR_AS_NOTICE("sql error");
if (freeconn)
PQfinish(conn);
SRF_RETURN_DONE(funcctx);
}
} }
if (PQresultStatus(res) == PGRES_COMMAND_OK) if (PQresultStatus(res) == PGRES_COMMAND_OK)
...@@ -1180,10 +1146,7 @@ dblink_exec(PG_FUNCTION_ARGS) ...@@ -1180,10 +1146,7 @@ dblink_exec(PG_FUNCTION_ARGS)
(PQresultStatus(res) != PGRES_COMMAND_OK && (PQresultStatus(res) != PGRES_COMMAND_OK &&
PQresultStatus(res) != PGRES_TUPLES_OK)) PQresultStatus(res) != PGRES_TUPLES_OK))
{ {
if (fail) dblink_res_error(conname, res, "could not execute command", fail);
DBLINK_RES_ERROR("sql error");
else
DBLINK_RES_ERROR_AS_NOTICE("sql error");
/* need a tuple descriptor representing one TEXT column */ /* need a tuple descriptor representing one TEXT column */
tupdesc = CreateTemplateTupleDesc(1, false); tupdesc = CreateTemplateTupleDesc(1, false);
...@@ -1195,7 +1158,6 @@ dblink_exec(PG_FUNCTION_ARGS) ...@@ -1195,7 +1158,6 @@ dblink_exec(PG_FUNCTION_ARGS)
* result tuple * result tuple
*/ */
sql_cmd_status = cstring_to_text("ERROR"); sql_cmd_status = cstring_to_text("ERROR");
} }
else if (PQresultStatus(res) == PGRES_COMMAND_OK) else if (PQresultStatus(res) == PGRES_COMMAND_OK)
{ {
...@@ -2288,3 +2250,54 @@ dblink_security_check(PGconn *conn, remoteConn *rconn) ...@@ -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)));
}
...@@ -125,9 +125,8 @@ WHERE t.a > 7; ...@@ -125,9 +125,8 @@ WHERE t.a > 7;
-- open a cursor with bad SQL and fail_on_error set to false -- open a cursor with bad SQL and fail_on_error set to false
SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foobar',false); SELECT dblink_open('rmt_foo_cursor','SELECT * FROM foobar',false);
NOTICE: sql error NOTICE: relation "foobar" does not exist
DETAIL: ERROR: relation "foobar" does not exist CONTEXT: Error occurred on dblink connection named "unnamed": could not open cursor.
dblink_open dblink_open
------------- -------------
ERROR ERROR
...@@ -194,9 +193,8 @@ FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]); ...@@ -194,9 +193,8 @@ FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]);
-- intentionally botch a fetch -- intentionally botch a fetch
SELECT * SELECT *
FROM dblink_fetch('rmt_foobar_cursor',4,false) AS t(a int, b text, c text[]); FROM dblink_fetch('rmt_foobar_cursor',4,false) AS t(a int, b text, c text[]);
NOTICE: sql error NOTICE: cursor "rmt_foobar_cursor" does not exist
DETAIL: ERROR: cursor "rmt_foobar_cursor" does not exist CONTEXT: Error occurred on dblink connection named "unnamed": could not fetch from cursor.
a | b | c a | b | c
---+---+--- ---+---+---
(0 rows) (0 rows)
...@@ -210,9 +208,8 @@ SELECT dblink_exec('ABORT'); ...@@ -210,9 +208,8 @@ SELECT dblink_exec('ABORT');
-- close the wrong cursor -- close the wrong cursor
SELECT dblink_close('rmt_foobar_cursor',false); SELECT dblink_close('rmt_foobar_cursor',false);
NOTICE: sql error NOTICE: cursor "rmt_foobar_cursor" does not exist
DETAIL: ERROR: cursor "rmt_foobar_cursor" does not exist CONTEXT: Error occurred on dblink connection named "unnamed": could not close cursor.
dblink_close dblink_close
-------------- --------------
ERROR ERROR
...@@ -221,15 +218,13 @@ DETAIL: ERROR: cursor "rmt_foobar_cursor" does not exist ...@@ -221,15 +218,13 @@ DETAIL: ERROR: cursor "rmt_foobar_cursor" does not exist
-- should generate 'cursor "rmt_foo_cursor" not found' error -- should generate 'cursor "rmt_foo_cursor" not found' error
SELECT * SELECT *
FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]); FROM dblink_fetch('rmt_foo_cursor',4) AS t(a int, b text, c text[]);
ERROR: sql error ERROR: cursor "rmt_foo_cursor" does not exist
DETAIL: 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 -- this time, 'cursor "rmt_foo_cursor" not found' as a notice
SELECT * SELECT *
FROM dblink_fetch('rmt_foo_cursor',4,false) AS t(a int, b text, c text[]); FROM dblink_fetch('rmt_foo_cursor',4,false) AS t(a int, b text, c text[]);
NOTICE: sql error NOTICE: cursor "rmt_foo_cursor" does not exist
DETAIL: ERROR: cursor "rmt_foo_cursor" does not exist CONTEXT: Error occurred on dblink connection named "unnamed": could not fetch from cursor.
a | b | c a | b | c
---+---+--- ---+---+---
(0 rows) (0 rows)
...@@ -291,9 +286,8 @@ FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[]); ...@@ -291,9 +286,8 @@ FROM dblink('SELECT * FROM foo') AS t(a int, b text, c text[]);
-- bad remote select -- bad remote select
SELECT * SELECT *
FROM dblink('SELECT * FROM foobar',false) AS t(a int, b text, c text[]); FROM dblink('SELECT * FROM foobar',false) AS t(a int, b text, c text[]);
NOTICE: sql error NOTICE: relation "foobar" does not exist
DETAIL: ERROR: relation "foobar" does not exist CONTEXT: Error occurred on dblink connection named "unnamed": could not execute query.
a | b | c a | b | c
---+---+--- ---+---+---
(0 rows) (0 rows)
...@@ -316,9 +310,8 @@ WHERE a = 11; ...@@ -316,9 +310,8 @@ WHERE a = 11;
-- botch a change to some other data -- botch a change to some other data
SELECT dblink_exec('UPDATE foobar SET f3[2] = ''b99'' WHERE f1 = 11',false); SELECT dblink_exec('UPDATE foobar SET f3[2] = ''b99'' WHERE f1 = 11',false);
NOTICE: sql error NOTICE: relation "foobar" does not exist
DETAIL: ERROR: relation "foobar" does not exist CONTEXT: Error occurred on dblink connection named "unnamed": could not execute command.
dblink_exec dblink_exec
------------- -------------
ERROR ERROR
...@@ -378,9 +371,8 @@ WHERE t.a > 7; ...@@ -378,9 +371,8 @@ WHERE t.a > 7;
SELECT * SELECT *
FROM dblink('myconn','SELECT * FROM foobar',false) AS t(a int, b text, c text[]) FROM dblink('myconn','SELECT * FROM foobar',false) AS t(a int, b text, c text[])
WHERE t.a > 7; WHERE t.a > 7;
NOTICE: sql error NOTICE: relation "foobar" does not exist
DETAIL: ERROR: relation "foobar" does not exist CONTEXT: Error occurred on dblink connection named "unnamed": could not execute query.
a | b | c a | b | c
---+---+--- ---+---+---
(0 rows) (0 rows)
...@@ -416,9 +408,8 @@ SELECT dblink_disconnect('myconn2'); ...@@ -416,9 +408,8 @@ SELECT dblink_disconnect('myconn2');
-- open a cursor incorrectly -- open a cursor incorrectly
SELECT dblink_open('myconn','rmt_foo_cursor','SELECT * FROM foobar',false); SELECT dblink_open('myconn','rmt_foo_cursor','SELECT * FROM foobar',false);
NOTICE: sql error NOTICE: relation "foobar" does not exist
DETAIL: ERROR: relation "foobar" does not exist CONTEXT: Error occurred on dblink connection named "myconn": could not open cursor.
dblink_open dblink_open
------------- -------------
ERROR ERROR
...@@ -503,9 +494,8 @@ SELECT dblink_close('myconn','rmt_foo_cursor'); ...@@ -503,9 +494,8 @@ SELECT dblink_close('myconn','rmt_foo_cursor');
-- this should fail because there is no open transaction -- this should fail because there is no open transaction
SELECT dblink_exec('myconn','DECLARE xact_test CURSOR FOR SELECT * FROM foo'); SELECT dblink_exec('myconn','DECLARE xact_test CURSOR FOR SELECT * FROM foo');
ERROR: sql error ERROR: DECLARE CURSOR can only be used in transaction blocks
DETAIL: 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 -- reset remote transaction state
SELECT dblink_exec('myconn','ABORT'); SELECT dblink_exec('myconn','ABORT');
dblink_exec dblink_exec
...@@ -554,9 +544,8 @@ FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]); ...@@ -554,9 +544,8 @@ FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]);
-- fetch some data incorrectly -- fetch some data incorrectly
SELECT * SELECT *
FROM dblink_fetch('myconn','rmt_foobar_cursor',4,false) AS t(a int, b text, c text[]); FROM dblink_fetch('myconn','rmt_foobar_cursor',4,false) AS t(a int, b text, c text[]);
NOTICE: sql error NOTICE: cursor "rmt_foobar_cursor" does not exist
DETAIL: ERROR: cursor "rmt_foobar_cursor" does not exist CONTEXT: Error occurred on dblink connection named "myconn": could not fetch from cursor.
a | b | c a | b | c
---+---+--- ---+---+---
(0 rows) (0 rows)
...@@ -571,9 +560,8 @@ SELECT dblink_exec('myconn','ABORT'); ...@@ -571,9 +560,8 @@ SELECT dblink_exec('myconn','ABORT');
-- should generate 'cursor "rmt_foo_cursor" not found' error -- should generate 'cursor "rmt_foo_cursor" not found' error
SELECT * SELECT *
FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]); FROM dblink_fetch('myconn','rmt_foo_cursor',4) AS t(a int, b text, c text[]);
ERROR: sql error ERROR: cursor "rmt_foo_cursor" does not exist
DETAIL: 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 -- close the named persistent connection
SELECT dblink_disconnect('myconn'); SELECT dblink_disconnect('myconn');
dblink_disconnect dblink_disconnect
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment