/* * Store a PGresult object ptr in the result buffer. * NB: Caller must not PQclear() the PGresult object. */ void cdbdisp_appendResult(CdbDispatchResult *dispatchResult, struct pg_result *res) { Assert(dispatchResult && res); /* * Attach the QE identification string to the PGresult */ if (dispatchResult->segdbDesc && dispatchResult->segdbDesc->whoami) pqSaveMessageField(res, PG_DIAG_GP_PROCESS_TAG, dispatchResult->segdbDesc->whoami); appendBinaryPQExpBuffer(dispatchResult->resultbuf, (char *) &res, sizeof(res)); }
/* * Attempt to read an Error or Notice response message. * This is possible in several places, so we break it out as a subroutine. * Entry: 'E' or 'N' message type has already been consumed. * Exit: returns 0 if successfully consumed message. * returns EOF if not enough data. */ static int pqGetErrorNotice2(PGconn *conn, bool isError) { PGresult *res = NULL; PQExpBufferData workBuf; char *startp; char *splitp; /* * Since the message might be pretty long, we create a temporary * PQExpBuffer rather than using conn->workBuffer. workBuffer is intended * for stuff that is expected to be short. */ initPQExpBuffer(&workBuf); if (pqGets(&workBuf, conn)) goto failure; /* * Make a PGresult to hold the message. We temporarily lie about the * result status, so that PQmakeEmptyPGresult doesn't uselessly copy * conn->errorMessage. * * NB: This allocation can fail, if you run out of memory. The rest of the * function handles that gracefully, and we still try to set the error * message as the connection's error message. */ res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); if (res) { res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; res->errMsg = pqResultStrdup(res, workBuf.data); } /* * Break the message into fields. We can't do very much here, but we can * split the severity code off, and remove trailing newlines. Also, we use * the heuristic that the primary message extends only to the first * newline --- anything after that is detail message. (In some cases it'd * be better classed as hint, but we can hardly be expected to guess that * here.) */ while (workBuf.len > 0 && workBuf.data[workBuf.len - 1] == '\n') workBuf.data[--workBuf.len] = '\0'; splitp = strstr(workBuf.data, ": "); if (splitp) { /* what comes before the colon is severity */ *splitp = '\0'; pqSaveMessageField(res, PG_DIAG_SEVERITY, workBuf.data); startp = splitp + 3; } else { /* can't find a colon? oh well... */ startp = workBuf.data; } splitp = strchr(startp, '\n'); if (splitp) { /* what comes before the newline is primary message */ *splitp++ = '\0'; pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp); /* the rest is detail; strip any leading whitespace */ while (*splitp && isspace((unsigned char) *splitp)) splitp++; pqSaveMessageField(res, PG_DIAG_MESSAGE_DETAIL, splitp); } else { /* single-line message, so all primary */ pqSaveMessageField(res, PG_DIAG_MESSAGE_PRIMARY, startp); } /* * Either save error as current async result, or just emit the notice. * Also, if it's an error and we were in a transaction block, assume the * server has now gone to error-in-transaction state. */ if (isError) { pqClearAsyncResult(conn); conn->result = res; resetPQExpBuffer(&conn->errorMessage); if (res && !PQExpBufferDataBroken(workBuf) && res->errMsg) appendPQExpBufferStr(&conn->errorMessage, res->errMsg); else printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory")); if (conn->xactStatus == PQTRANS_INTRANS) conn->xactStatus = PQTRANS_INERROR; } else { if (res) { if (res->noticeHooks.noticeRec != NULL) (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res); PQclear(res); } } termPQExpBuffer(&workBuf); return 0; failure: if (res) PQclear(res); termPQExpBuffer(&workBuf); return EOF; }
/* * Attempt to read an Error or Notice response message. * This is possible in several places, so we break it out as a subroutine. * Entry: 'E' or 'N' message type and length have already been consumed. * Exit: returns 0 if successfully consumed message. * returns EOF if not enough data. */ int pqGetErrorNotice3(PGconn *conn, bool isError) { PGresult *res = NULL; 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 * PQExpBuffer rather than using conn->workBuffer. workBuffer is intended * for stuff that is expected to be short. We shouldn't use * conn->errorMessage either, since this might be only a notice. */ initPQExpBuffer(&workBuf); /* * Make a PGresult to hold the accumulated fields. We temporarily lie * about the result status, so that PQmakeEmptyPGresult doesn't uselessly * copy conn->errorMessage. */ res = PQmakeEmptyPGresult(conn, PGRES_EMPTY_QUERY); if (!res) goto fail; res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR; /* * Read the fields and save into res. */ for (;;) { if (pqGetc(&id, conn)) goto fail; if (id == '\0') break; /* terminator found */ if (pqGets(&workBuf, conn)) goto fail; pqSaveMessageField(res, id, workBuf.data); } /* * Now build the "overall" error message for PQresultErrorMessage. */ resetPQExpBuffer(&workBuf); val = PQresultErrorField(res, PG_DIAG_SEVERITY); if (val) appendPQExpBuffer(&workBuf, "%s: ", val); if (conn->verbosity == PQERRORS_VERBOSE) { val = PQresultErrorField(res, PG_DIAG_SQLSTATE); if (val) appendPQExpBuffer(&workBuf, "%s: ", val); } val = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); if (val) appendPQExpBufferStr(&workBuf, val); val = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION); if (val) { if (conn->verbosity != PQERRORS_TERSE && conn->last_query != NULL) { /* emit position as a syntax cursor display */ querytext = conn->last_query; 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"), val); } } else { val = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION); if (val) { querytext = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); if (conn->verbosity != PQERRORS_TERSE && querytext != NULL) { /* emit position as a syntax cursor display */ 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"), val); } } } appendPQExpBufferChar(&workBuf, '\n'); if (conn->verbosity != PQERRORS_TERSE) { if (querytext && querypos > 0) reportErrorPosition(&workBuf, querytext, querypos, conn->client_encoding); val = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL); if (val) appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL: %s\n"), val); val = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT); if (val) appendPQExpBuffer(&workBuf, libpq_gettext("HINT: %s\n"), val); val = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); if (val) appendPQExpBuffer(&workBuf, libpq_gettext("QUERY: %s\n"), val); val = PQresultErrorField(res, PG_DIAG_CONTEXT); if (val) appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT: %s\n"), val); } if (conn->verbosity == PQERRORS_VERBOSE) { const char *valf; const char *vall; valf = PQresultErrorField(res, PG_DIAG_SOURCE_FILE); vall = PQresultErrorField(res, PG_DIAG_SOURCE_LINE); val = PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION); if (val || valf || vall) { appendPQExpBufferStr(&workBuf, libpq_gettext("LOCATION: ")); if (val) appendPQExpBuffer(&workBuf, libpq_gettext("%s, "), val); if (valf && vall) /* unlikely we'd have just one */ appendPQExpBuffer(&workBuf, libpq_gettext("%s:%s"), valf, vall); appendPQExpBufferChar(&workBuf, '\n'); } } /* * Either save error as current async result, or just emit the notice. */ if (isError) { res->errMsg = pqResultStrdup(res, workBuf.data); if (!res->errMsg) goto fail; pqClearAsyncResult(conn); conn->result = res; resetPQExpBuffer(&conn->errorMessage); appendPQExpBufferStr(&conn->errorMessage, workBuf.data); } else { /* 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; }