diff options
Diffstat (limited to 'src/interfaces/libpq/fe-exec.c')
-rw-r--r-- | src/interfaces/libpq/fe-exec.c | 281 |
1 files changed, 126 insertions, 155 deletions
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index d48f0fd587b..e7307533876 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -53,7 +53,8 @@ static bool static_std_strings = false; static PGEvent *dupEvents(PGEvent *events, int count, size_t *memSize); static bool pqAddTuple(PGresult *res, PGresAttValue *tup, const char **errmsgp); -static bool PQsendQueryStart(PGconn *conn); +static int PQsendQueryInternal(PGconn *conn, const char *query, bool newQuery); +static bool PQsendQueryStart(PGconn *conn, bool newQuery); static int PQsendQueryGuts(PGconn *conn, const char *command, const char *stmtName, @@ -668,25 +669,6 @@ pqSetResultError(PGresult *res, const char *msg) } /* - * pqCatenateResultError - - * concatenate a new error message to the one already in a PGresult - */ -void -pqCatenateResultError(PGresult *res, const char *msg) -{ - PQExpBufferData errorBuf; - - if (!res || !msg) - return; - initPQExpBuffer(&errorBuf); - if (res->errMsg) - appendPQExpBufferStr(&errorBuf, res->errMsg); - appendPQExpBufferStr(&errorBuf, msg); - pqSetResultError(res, errorBuf.data); - termPQExpBuffer(&errorBuf); -} - -/* * PQclear - * free's the memory associated with a PGresult */ @@ -759,68 +741,46 @@ pqClearAsyncResult(PGconn *conn) /* * This subroutine deletes any existing async result, sets conn->result * to a PGresult with status PGRES_FATAL_ERROR, and stores the current - * contents of conn->errorMessage into that result. It differs from a - * plain call on PQmakeEmptyPGresult() in that if there is already an - * async result with status PGRES_FATAL_ERROR, the current error message - * is APPENDED to the old error message instead of replacing it. This - * behavior lets us report multiple error conditions properly, if necessary. - * (An example where this is needed is when the backend sends an 'E' message - * and immediately closes the connection --- we want to report both the - * backend error and the connection closure error.) + * contents of conn->errorMessage into that result. */ void pqSaveErrorResult(PGconn *conn) { - /* - * If no old async result, just let PQmakeEmptyPGresult make one. Likewise - * if old result is not an error message. - */ - if (conn->result == NULL || - conn->result->resultStatus != PGRES_FATAL_ERROR || - conn->result->errMsg == NULL) - { - pqClearAsyncResult(conn); - conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); - } - else - { - /* Else, concatenate error message to existing async result. */ - pqCatenateResultError(conn->result, conn->errorMessage.data); - } + pqClearAsyncResult(conn); + conn->result = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); } /* - * As above, and append conn->write_err_msg to whatever other error we have. - * This is used when we've detected a write failure and have exhausted our - * chances of reporting something else instead. + * As above, after appending conn->write_err_msg to whatever other error we + * have. This is used when we've detected a write failure and have exhausted + * our chances of reporting something else instead. */ static void pqSaveWriteError(PGconn *conn) { /* - * Ensure conn->result is an error result, and add anything in - * conn->errorMessage to it. + * If write_err_msg is null because of previous strdup failure, do what we + * can. (It's likely our machinations here will get OOM failures as well, + * but might as well try.) */ - pqSaveErrorResult(conn); - - /* - * Now append write_err_msg to that. If it's null because of previous - * strdup failure, do what we can. (It's likely our machinations here are - * all getting OOM failures as well, but ...) - */ - if (conn->write_err_msg && conn->write_err_msg[0] != '\0') - pqCatenateResultError(conn->result, conn->write_err_msg); + if (conn->write_err_msg) + { + appendPQExpBufferStr(&conn->errorMessage, conn->write_err_msg); + /* Avoid possibly appending the same message twice */ + conn->write_err_msg[0] = '\0'; + } else - pqCatenateResultError(conn->result, - libpq_gettext("write to server failed\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("write to server failed\n")); + + pqSaveErrorResult(conn); } /* * This subroutine prepares an async result object for return to the caller. * If there is not already an async result object, build an error object * using whatever is in conn->errorMessage. In any case, clear the async - * result storage and make sure PQerrorMessage will agree with the result's - * error string. + * result storage. */ PGresult * pqPrepareAsyncResult(PGconn *conn) @@ -835,16 +795,6 @@ pqPrepareAsyncResult(PGconn *conn) res = conn->result; if (!res) res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); - else - { - /* - * Make sure PQerrorMessage agrees with result; it could be different - * if we have concatenated messages. - */ - resetPQExpBuffer(&conn->errorMessage); - appendPQExpBufferStr(&conn->errorMessage, - PQresultErrorMessage(res)); - } /* * Replace conn->result with next_result, if any. In the normal case @@ -1229,18 +1179,33 @@ fail: * * Returns: 1 if successfully submitted * 0 if error (conn->errorMessage is set) + * + * PQsendQueryContinue is a non-exported version that behaves identically + * except that it doesn't reset conn->errorMessage. */ int PQsendQuery(PGconn *conn, const char *query) { - if (!PQsendQueryStart(conn)) + return PQsendQueryInternal(conn, query, true); +} + +int +PQsendQueryContinue(PGconn *conn, const char *query) +{ + return PQsendQueryInternal(conn, query, false); +} + +static int +PQsendQueryInternal(PGconn *conn, const char *query, bool newQuery) +{ + if (!PQsendQueryStart(conn, newQuery)) return 0; /* check the argument */ if (!query) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("command string is a null pointer\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("command string is a null pointer\n")); return 0; } @@ -1291,20 +1256,20 @@ PQsendQueryParams(PGconn *conn, const int *paramFormats, int resultFormat) { - if (!PQsendQueryStart(conn)) + if (!PQsendQueryStart(conn, true)) return 0; /* check the arguments */ if (!command) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("command string is a null pointer\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("command string is a null pointer\n")); return 0; } if (nParams < 0 || nParams > 65535) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("number of parameters must be between 0 and 65535\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("number of parameters must be between 0 and 65535\n")); return 0; } @@ -1331,34 +1296,34 @@ PQsendPrepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes) { - if (!PQsendQueryStart(conn)) + if (!PQsendQueryStart(conn, true)) return 0; /* check the arguments */ if (!stmtName) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("statement name is a null pointer\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("statement name is a null pointer\n")); return 0; } if (!query) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("command string is a null pointer\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("command string is a null pointer\n")); return 0; } if (nParams < 0 || nParams > 65535) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("number of parameters must be between 0 and 65535\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("number of parameters must be between 0 and 65535\n")); return 0; } /* This isn't gonna work on a 2.0 server */ if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("function requires at least protocol version 3.0\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("function requires at least protocol version 3.0\n")); return 0; } @@ -1432,20 +1397,20 @@ PQsendQueryPrepared(PGconn *conn, const int *paramFormats, int resultFormat) { - if (!PQsendQueryStart(conn)) + if (!PQsendQueryStart(conn, true)) return 0; /* check the arguments */ if (!stmtName) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("statement name is a null pointer\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("statement name is a null pointer\n")); return 0; } if (nParams < 0 || nParams > 65535) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("number of parameters must be between 0 and 65535\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("number of parameters must be between 0 and 65535\n")); return 0; } @@ -1464,26 +1429,29 @@ PQsendQueryPrepared(PGconn *conn, * Common startup code for PQsendQuery and sibling routines */ static bool -PQsendQueryStart(PGconn *conn) +PQsendQueryStart(PGconn *conn, bool newQuery) { if (!conn) return false; - /* clear the error string */ - resetPQExpBuffer(&conn->errorMessage); + /* + * If this is the beginning of a query cycle, reset the error buffer. + */ + if (newQuery) + resetPQExpBuffer(&conn->errorMessage); /* Don't try to send if we know there's no live connection. */ if (conn->status != CONNECTION_OK) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("no connection to the server\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("no connection to the server\n")); return false; } /* Can't send while already busy, either. */ if (conn->asyncStatus != PGASYNC_IDLE) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("another command is already in progress\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("another command is already in progress\n")); return false; } @@ -1520,8 +1488,8 @@ PQsendQueryGuts(PGconn *conn, /* This isn't gonna work on a 2.0 server */ if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("function requires at least protocol version 3.0\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("function requires at least protocol version 3.0\n")); return 0; } @@ -1596,8 +1564,8 @@ PQsendQueryGuts(PGconn *conn, nbytes = paramLengths[i]; else { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("length must be given for binary parameter\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("length must be given for binary parameter\n")); goto sendFailed; } } @@ -1859,7 +1827,7 @@ PQgetResult(PGconn *conn) res = getCopyResult(conn, PGRES_COPY_BOTH); break; default: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("unexpected asyncStatus: %d\n"), (int) conn->asyncStatus); res = PQmakeEmptyPGresult(conn, PGRES_FATAL_ERROR); @@ -1879,7 +1847,7 @@ PQgetResult(PGconn *conn) if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt, res->events[i].passThrough)) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"), res->events[i].name); pqSetResultError(res, conn->errorMessage.data); @@ -2026,6 +1994,11 @@ PQexecStart(PGconn *conn) return false; /* + * Since this is the beginning of a query cycle, reset the error buffer. + */ + resetPQExpBuffer(&conn->errorMessage); + + /* * Silently discard any prior query result that application didn't eat. * This is probably poor design, but it's here for backward compatibility. */ @@ -2047,8 +2020,8 @@ PQexecStart(PGconn *conn) else { /* In older protocols we have to punt */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("COPY IN state must be terminated first\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("COPY IN state must be terminated first\n")); return false; } } @@ -2067,16 +2040,16 @@ PQexecStart(PGconn *conn) else { /* In older protocols we have to punt */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("COPY OUT state must be terminated first\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("COPY OUT state must be terminated first\n")); return false; } } else if (resultStatus == PGRES_COPY_BOTH) { /* We don't allow PQexec during COPY BOTH */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("PQexec not allowed during COPY BOTH\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("PQexec not allowed during COPY BOTH\n")); return false; } /* check for loss of connection, too */ @@ -2099,8 +2072,9 @@ PQexecFinish(PGconn *conn) /* * For backwards compatibility, return the last result if there are more - * than one --- but merge error messages if we get more than one error - * result. + * than one. (We used to have logic here to concatenate successive error + * messages, but now that happens automatically, since conn->errorMessage + * will continue to accumulate errors throughout this loop.) * * We have to stop if we see copy in/out/both, however. We will resume * parsing after application performs the data transfer. @@ -2111,23 +2085,7 @@ PQexecFinish(PGconn *conn) while ((result = PQgetResult(conn)) != NULL) { if (lastResult) - { - if (lastResult->resultStatus == PGRES_FATAL_ERROR && - result->resultStatus == PGRES_FATAL_ERROR) - { - pqCatenateResultError(lastResult, result->errMsg); - PQclear(result); - result = lastResult; - - /* - * Make sure PQerrorMessage agrees with concatenated result - */ - resetPQExpBuffer(&conn->errorMessage); - appendPQExpBufferStr(&conn->errorMessage, result->errMsg); - } - else - PQclear(lastResult); - } + PQclear(lastResult); lastResult = result; if (result->resultStatus == PGRES_COPY_IN || result->resultStatus == PGRES_COPY_OUT || @@ -2223,14 +2181,14 @@ PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target) if (!desc_target) desc_target = ""; - if (!PQsendQueryStart(conn)) + if (!PQsendQueryStart(conn, true)) return 0; /* This isn't gonna work on a 2.0 server */ if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("function requires at least protocol version 3.0\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("function requires at least protocol version 3.0\n")); return 0; } @@ -2321,8 +2279,8 @@ PQputCopyData(PGconn *conn, const char *buffer, int nbytes) if (conn->asyncStatus != PGASYNC_COPY_IN && conn->asyncStatus != PGASYNC_COPY_BOTH) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("no COPY in progress\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); return -1; } @@ -2388,8 +2346,8 @@ PQputCopyEnd(PGconn *conn, const char *errormsg) if (conn->asyncStatus != PGASYNC_COPY_IN && conn->asyncStatus != PGASYNC_COPY_BOTH) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("no COPY in progress\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); return -1; } @@ -2431,8 +2389,8 @@ PQputCopyEnd(PGconn *conn, const char *errormsg) if (errormsg) { /* Oops, no way to do this in 2.0 */ - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("function requires at least protocol version 3.0\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("function requires at least protocol version 3.0\n")); return -1; } else @@ -2450,7 +2408,6 @@ PQputCopyEnd(PGconn *conn, const char *errormsg) conn->asyncStatus = PGASYNC_COPY_OUT; else conn->asyncStatus = PGASYNC_BUSY; - resetPQExpBuffer(&conn->errorMessage); /* Try to flush data */ if (pqFlush(conn) < 0) @@ -2478,8 +2435,8 @@ PQgetCopyData(PGconn *conn, char **buffer, int async) if (conn->asyncStatus != PGASYNC_COPY_OUT && conn->asyncStatus != PGASYNC_COPY_BOTH) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("no COPY in progress\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("no COPY in progress\n")); return -2; } if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3) @@ -2662,14 +2619,16 @@ PQfn(PGconn *conn, if (!conn) return NULL; - /* clear the error string */ + /* + * Since this is the beginning of a query cycle, reset the error buffer. + */ resetPQExpBuffer(&conn->errorMessage); if (conn->sock == PGINVALID_SOCKET || conn->asyncStatus != PGASYNC_IDLE || conn->result != NULL) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("connection in wrong state\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("connection in wrong state\n")); return NULL; } @@ -3246,7 +3205,11 @@ PQsetnonblocking(PGconn *conn, int arg) * need to flush the send queue at this point in order to guarantee proper * behavior. this is ok because either they are making a transition _from_ * or _to_ blocking mode, either way we can block them. + * + * Clear errorMessage in case pqFlush adds to it. */ + resetPQExpBuffer(&conn->errorMessage); + /* if we are going from blocking to non-blocking flush here */ if (pqFlush(conn)) return -1; @@ -3388,8 +3351,8 @@ PQescapeStringInternal(PGconn *conn, if (error) *error = 1; if (conn) - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("incomplete multibyte character\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("incomplete multibyte character\n")); for (; i < len; i++) { if (((size_t) (target - to)) / 2 >= length) @@ -3419,6 +3382,9 @@ PQescapeStringConn(PGconn *conn, *error = 1; return 0; } + + resetPQExpBuffer(&conn->errorMessage); + return PQescapeStringInternal(conn, to, from, length, error, conn->client_encoding, conn->std_strings); @@ -3455,6 +3421,8 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident) if (!conn) return NULL; + resetPQExpBuffer(&conn->errorMessage); + /* Scan the string for characters that must be escaped. */ for (s = str; (s - str) < len && *s != '\0'; ++s) { @@ -3472,8 +3440,8 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident) /* Multibyte character overruns allowable length. */ if ((s - str) + charlen > len || memchr(s, 0, charlen) != NULL) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("incomplete multibyte character\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("incomplete multibyte character\n")); return NULL; } @@ -3490,8 +3458,8 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident) result = rp = (char *) malloc(result_size); if (rp == NULL) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("out of memory\n")); return NULL; } @@ -3655,8 +3623,8 @@ PQescapeByteaInternal(PGconn *conn, if (rp == NULL) { if (conn) - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory\n")); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("out of memory\n")); return NULL; } @@ -3717,6 +3685,9 @@ PQescapeByteaConn(PGconn *conn, { if (!conn) return NULL; + + resetPQExpBuffer(&conn->errorMessage); + return PQescapeByteaInternal(conn, from, from_length, to_length, conn->std_strings, (conn->sversion >= 90000)); |