diff options
Diffstat (limited to 'src/interfaces/libpq/fe-protocol3.c')
-rw-r--r-- | src/interfaces/libpq/fe-protocol3.c | 197 |
1 files changed, 122 insertions, 75 deletions
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index 3034773972a..0b8c62f6ce2 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -876,11 +876,9 @@ int pqGetErrorNotice3(PGconn *conn, bool isError) { PGresult *res = NULL; + bool have_position = false; PQExpBufferData workBuf; char id; - const char *val; - const char *querytext = NULL; - int querypos = 0; /* * Since the fields might be pretty long, we create a temporary @@ -905,6 +903,9 @@ pqGetErrorNotice3(PGconn *conn, bool isError) /* * Read the fields and save into res. + * + * While at it, save the SQLSTATE in conn->last_sqlstate, and note whether + * we saw a PG_DIAG_STATEMENT_POSITION field. */ for (;;) { @@ -915,42 +916,123 @@ pqGetErrorNotice3(PGconn *conn, bool isError) if (pqGets(&workBuf, conn)) goto fail; pqSaveMessageField(res, id, workBuf.data); + if (id == PG_DIAG_SQLSTATE) + strlcpy(conn->last_sqlstate, workBuf.data, + sizeof(conn->last_sqlstate)); + else if (id == PG_DIAG_STATEMENT_POSITION) + have_position = true; } /* + * Save the active query text, if any, into res as well; but only if we + * might need it for an error cursor display, which is only true if there + * is a PG_DIAG_STATEMENT_POSITION field. + */ + if (have_position && conn->last_query && res) + res->errQuery = pqResultStrdup(res, conn->last_query); + + /* * Now build the "overall" error message for PQresultErrorMessage. - * - * Also, save the SQLSTATE in conn->last_sqlstate. */ resetPQExpBuffer(&workBuf); + pqBuildErrorMessage3(&workBuf, res, conn->verbosity, conn->show_context); + + /* + * Either save error as current async result, or just emit the notice. + */ + if (isError) + { + if (res) + res->errMsg = pqResultStrdup(res, workBuf.data); + pqClearAsyncResult(conn); + conn->result = res; + if (PQExpBufferDataBroken(workBuf)) + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory")); + else + appendPQExpBufferStr(&conn->errorMessage, workBuf.data); + } + else + { + /* if we couldn't allocate the result set, just discard the NOTICE */ + if (res) + { + /* We can cheat a little here and not copy the message. */ + res->errMsg = workBuf.data; + if (res->noticeHooks.noticeRec != NULL) + (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); + PQclear(res); + } + } + + termPQExpBuffer(&workBuf); + return 0; + +fail: + PQclear(res); + termPQExpBuffer(&workBuf); + return EOF; +} + +/* + * Construct an error message from the fields in the given PGresult, + * appending it to the contents of "msg". + */ +void +pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res, + PGVerbosity verbosity, PGContextVisibility show_context) +{ + const char *val; + const char *querytext = NULL; + int querypos = 0; + + /* If we couldn't allocate a PGresult, just say "out of memory" */ + if (res == NULL) + { + appendPQExpBuffer(msg, libpq_gettext("out of memory\n")); + return; + } + + /* + * If we don't have any broken-down fields, just return the base message. + * This mainly applies if we're given a libpq-generated error result. + */ + if (res->errFields == NULL) + { + if (res->errMsg && res->errMsg[0]) + appendPQExpBufferStr(msg, res->errMsg); + else + appendPQExpBuffer(msg, libpq_gettext("no error message available\n")); + return; + } + + /* Else build error message from relevant fields */ val = PQresultErrorField(res, PG_DIAG_SEVERITY); if (val) - appendPQExpBuffer(&workBuf, "%s: ", val); - val = PQresultErrorField(res, PG_DIAG_SQLSTATE); - if (val) + appendPQExpBuffer(msg, "%s: ", val); + if (verbosity == PQERRORS_VERBOSE) { - if (strlen(val) < sizeof(conn->last_sqlstate)) - strcpy(conn->last_sqlstate, val); - if (conn->verbosity == PQERRORS_VERBOSE) - appendPQExpBuffer(&workBuf, "%s: ", val); + val = PQresultErrorField(res, PG_DIAG_SQLSTATE); + if (val) + appendPQExpBuffer(msg, "%s: ", val); } val = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); if (val) - appendPQExpBufferStr(&workBuf, val); + appendPQExpBufferStr(msg, val); val = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION); if (val) { - if (conn->verbosity != PQERRORS_TERSE && conn->last_query != NULL) + if (verbosity != PQERRORS_TERSE && res->errQuery != NULL) { /* emit position as a syntax cursor display */ - querytext = conn->last_query; + querytext = res->errQuery; querypos = atoi(val); } else { /* emit position as text addition to primary message */ /* translator: %s represents a digit string */ - appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"), + appendPQExpBuffer(msg, libpq_gettext(" at character %s"), val); } } @@ -960,7 +1042,7 @@ pqGetErrorNotice3(PGconn *conn, bool isError) if (val) { querytext = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); - if (conn->verbosity != PQERRORS_TERSE && querytext != NULL) + if (verbosity != PQERRORS_TERSE && querytext != NULL) { /* emit position as a syntax cursor display */ querypos = atoi(val); @@ -969,59 +1051,60 @@ pqGetErrorNotice3(PGconn *conn, bool isError) { /* emit position as text addition to primary message */ /* translator: %s represents a digit string */ - appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"), + appendPQExpBuffer(msg, libpq_gettext(" at character %s"), val); } } } - appendPQExpBufferChar(&workBuf, '\n'); - if (conn->verbosity != PQERRORS_TERSE) + appendPQExpBufferChar(msg, '\n'); + if (verbosity != PQERRORS_TERSE) { if (querytext && querypos > 0) - reportErrorPosition(&workBuf, querytext, querypos, - conn->client_encoding); + reportErrorPosition(msg, querytext, querypos, + res->client_encoding); val = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL); if (val) - appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL: %s\n"), val); + appendPQExpBuffer(msg, libpq_gettext("DETAIL: %s\n"), val); val = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT); if (val) - appendPQExpBuffer(&workBuf, libpq_gettext("HINT: %s\n"), val); + appendPQExpBuffer(msg, libpq_gettext("HINT: %s\n"), val); val = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); if (val) - appendPQExpBuffer(&workBuf, libpq_gettext("QUERY: %s\n"), val); - if (conn->show_context == PQSHOW_CONTEXT_ALWAYS || - (conn->show_context == PQSHOW_CONTEXT_ERRORS && isError)) + appendPQExpBuffer(msg, libpq_gettext("QUERY: %s\n"), val); + if (show_context == PQSHOW_CONTEXT_ALWAYS || + (show_context == PQSHOW_CONTEXT_ERRORS && + res->resultStatus == PGRES_FATAL_ERROR)) { val = PQresultErrorField(res, PG_DIAG_CONTEXT); if (val) - appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT: %s\n"), + appendPQExpBuffer(msg, libpq_gettext("CONTEXT: %s\n"), val); } } - if (conn->verbosity == PQERRORS_VERBOSE) + if (verbosity == PQERRORS_VERBOSE) { val = PQresultErrorField(res, PG_DIAG_SCHEMA_NAME); if (val) - appendPQExpBuffer(&workBuf, + appendPQExpBuffer(msg, libpq_gettext("SCHEMA NAME: %s\n"), val); val = PQresultErrorField(res, PG_DIAG_TABLE_NAME); if (val) - appendPQExpBuffer(&workBuf, + appendPQExpBuffer(msg, libpq_gettext("TABLE NAME: %s\n"), val); val = PQresultErrorField(res, PG_DIAG_COLUMN_NAME); if (val) - appendPQExpBuffer(&workBuf, + appendPQExpBuffer(msg, libpq_gettext("COLUMN NAME: %s\n"), val); val = PQresultErrorField(res, PG_DIAG_DATATYPE_NAME); if (val) - appendPQExpBuffer(&workBuf, + appendPQExpBuffer(msg, libpq_gettext("DATATYPE NAME: %s\n"), val); val = PQresultErrorField(res, PG_DIAG_CONSTRAINT_NAME); if (val) - appendPQExpBuffer(&workBuf, + appendPQExpBuffer(msg, libpq_gettext("CONSTRAINT NAME: %s\n"), val); } - if (conn->verbosity == PQERRORS_VERBOSE) + if (verbosity == PQERRORS_VERBOSE) { const char *valf; const char *vall; @@ -1031,51 +1114,15 @@ pqGetErrorNotice3(PGconn *conn, bool isError) val = PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION); if (val || valf || vall) { - appendPQExpBufferStr(&workBuf, libpq_gettext("LOCATION: ")); + appendPQExpBufferStr(msg, libpq_gettext("LOCATION: ")); if (val) - appendPQExpBuffer(&workBuf, libpq_gettext("%s, "), val); + appendPQExpBuffer(msg, libpq_gettext("%s, "), val); if (valf && vall) /* unlikely we'd have just one */ - appendPQExpBuffer(&workBuf, libpq_gettext("%s:%s"), + appendPQExpBuffer(msg, libpq_gettext("%s:%s"), valf, vall); - appendPQExpBufferChar(&workBuf, '\n'); + appendPQExpBufferChar(msg, '\n'); } } - - /* - * Either save error as current async result, or just emit the notice. - */ - if (isError) - { - if (res) - res->errMsg = pqResultStrdup(res, workBuf.data); - pqClearAsyncResult(conn); - conn->result = res; - if (PQExpBufferDataBroken(workBuf)) - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory")); - else - appendPQExpBufferStr(&conn->errorMessage, workBuf.data); - } - else - { - /* if we couldn't allocate the result set, just discard the NOTICE */ - if (res) - { - /* We can cheat a little here and not copy the message. */ - res->errMsg = workBuf.data; - if (res->noticeHooks.noticeRec != NULL) - (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); - PQclear(res); - } - } - - termPQExpBuffer(&workBuf); - return 0; - -fail: - PQclear(res); - termPQExpBuffer(&workBuf); - return EOF; } /* |