Re: Multiple hosts in connection string failed to failover in non-hot standby mode
От | Tom Lane |
---|---|
Тема | Re: Multiple hosts in connection string failed to failover in non-hot standby mode |
Дата | |
Msg-id | 810463.1610333794@sss.pgh.pa.us обсуждение исходный текст |
Ответ на | Re: Multiple hosts in connection string failed to failover in non-hot standby mode (Tom Lane <tgl@sss.pgh.pa.us>) |
Ответы |
Re: Multiple hosts in connection string failed to failover in non-hot standby mode
(Hubert Zhang <zhubert@vmware.com>)
|
Список | pgsql-hackers |
I wrote: > I feel pretty good about 0001: it might be committable as-is. 0002 is > probably subject to bikeshedding, plus it has a problem in the ECPG tests. > Two of the error messages are now unstable because they expose > chosen-at-random socket paths: > ... > I don't have any non-hacky ideas what to do about that. The extra detail > seems useful to end users, but we don't have any infrastructure that > would allow filtering it out in the ECPG tests. So far the only solution that comes to mind is to introduce some infrastructure to do that filtering. 0001-0003 below are unchanged, 0004 patches up the ecpg test framework with a rather ad-hoc filtering function. I'd feel worse about this if there weren't already a very ad-hoc filtering function there ;-) This set passes check-world for me; we'll soon see what the cfbot thinks. regards, tom lane diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c index b76f0befd0..8b60378379 100644 --- a/src/interfaces/libpq/fe-auth-scram.c +++ b/src/interfaces/libpq/fe-auth-scram.c @@ -208,13 +208,13 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen, { if (inputlen == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("malformed SCRAM message (empty message)\n")); goto error; } if (inputlen != strlen(input)) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("malformed SCRAM message (length mismatch)\n")); goto error; } @@ -258,14 +258,14 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen, */ if (!verify_server_signature(state, success)) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not verify server signature\n")); goto error; } if (!*success) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("incorrect server signature\n")); } *done = true; @@ -274,7 +274,7 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen, default: /* shouldn't happen */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid SCRAM exchange state\n")); goto error; } @@ -287,6 +287,11 @@ error: /* * Read value for an attribute part of a SCRAM message. + * + * The buffer at **input is destructively modified, and *input is + * advanced over the "attr=value" string and any following comma. + * + * On failure, append an error message to *errorMessage and return NULL. */ static char * read_attr_value(char **input, char attr, PQExpBuffer errorMessage) @@ -296,7 +301,7 @@ read_attr_value(char **input, char attr, PQExpBuffer errorMessage) if (*begin != attr) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("malformed SCRAM message (attribute \"%c\" expected)\n"), attr); return NULL; @@ -305,7 +310,7 @@ read_attr_value(char **input, char attr, PQExpBuffer errorMessage) if (*begin != '=') { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"), attr); return NULL; @@ -346,7 +351,7 @@ build_client_first_message(fe_scram_state *state) */ if (!pg_strong_random(raw_nonce, SCRAM_RAW_NONCE_LEN)) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not generate nonce\n")); return NULL; } @@ -356,7 +361,7 @@ build_client_first_message(fe_scram_state *state) state->client_nonce = malloc(encoded_len + 1); if (state->client_nonce == NULL) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return NULL; } @@ -364,7 +369,7 @@ build_client_first_message(fe_scram_state *state) state->client_nonce, encoded_len); if (encoded_len < 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not encode nonce\n")); return NULL; } @@ -431,7 +436,7 @@ build_client_first_message(fe_scram_state *state) oom_error: termPQExpBuffer(&buf); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return NULL; } @@ -508,7 +513,7 @@ build_client_final_message(fe_scram_state *state) free(cbind_data); free(cbind_input); termPQExpBuffer(&buf); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, "could not encode cbind data for channel binding\n"); return NULL; } @@ -523,7 +528,7 @@ build_client_final_message(fe_scram_state *state) * Shouldn't happen. */ termPQExpBuffer(&buf); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, "channel binding not supported by this build\n"); return NULL; #endif /* HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH */ @@ -553,7 +558,7 @@ build_client_final_message(fe_scram_state *state) client_proof)) { termPQExpBuffer(&buf); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not calculate client proof\n")); return NULL; } @@ -569,7 +574,7 @@ build_client_final_message(fe_scram_state *state) if (encoded_len < 0) { termPQExpBuffer(&buf); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not encode client proof\n")); return NULL; } @@ -585,7 +590,7 @@ build_client_final_message(fe_scram_state *state) oom_error: termPQExpBuffer(&buf); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return NULL; } @@ -606,7 +611,7 @@ read_server_first_message(fe_scram_state *state, char *input) state->server_first_message = strdup(input); if (state->server_first_message == NULL) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return false; } @@ -616,7 +621,7 @@ read_server_first_message(fe_scram_state *state, char *input) &conn->errorMessage); if (nonce == NULL) { - /* read_attr_value() has generated an error string */ + /* read_attr_value() has appended an error string */ return false; } @@ -624,7 +629,7 @@ read_server_first_message(fe_scram_state *state, char *input) if (strlen(nonce) < strlen(state->client_nonce) || memcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid SCRAM response (nonce mismatch)\n")); return false; } @@ -632,7 +637,7 @@ read_server_first_message(fe_scram_state *state, char *input) state->nonce = strdup(nonce); if (state->nonce == NULL) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return false; } @@ -640,14 +645,14 @@ read_server_first_message(fe_scram_state *state, char *input) encoded_salt = read_attr_value(&input, 's', &conn->errorMessage); if (encoded_salt == NULL) { - /* read_attr_value() has generated an error string */ + /* read_attr_value() has appended an error string */ return false; } decoded_salt_len = pg_b64_dec_len(strlen(encoded_salt)); state->salt = malloc(decoded_salt_len); if (state->salt == NULL) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return false; } @@ -657,7 +662,7 @@ read_server_first_message(fe_scram_state *state, char *input) decoded_salt_len); if (state->saltlen < 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("malformed SCRAM message (invalid salt)\n")); return false; } @@ -665,19 +670,19 @@ read_server_first_message(fe_scram_state *state, char *input) iterations_str = read_attr_value(&input, 'i', &conn->errorMessage); if (iterations_str == NULL) { - /* read_attr_value() has generated an error string */ + /* read_attr_value() has appended an error string */ return false; } state->iterations = strtol(iterations_str, &endptr, 10); if (*endptr != '\0' || state->iterations < 1) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("malformed SCRAM message (invalid iteration count)\n")); return false; } if (*input != '\0') - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("malformed SCRAM message (garbage at end of server-first-message)\n")); return true; @@ -697,7 +702,7 @@ read_server_final_message(fe_scram_state *state, char *input) state->server_final_message = strdup(input); if (!state->server_final_message) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return false; } @@ -708,7 +713,12 @@ read_server_final_message(fe_scram_state *state, char *input) char *errmsg = read_attr_value(&input, 'e', &conn->errorMessage); - printfPQExpBuffer(&conn->errorMessage, + if (errmsg == NULL) + { + /* read_attr_value() has appended an error message */ + return false; + } + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("error received from server in SCRAM exchange: %s\n"), errmsg); return false; @@ -719,19 +729,19 @@ read_server_final_message(fe_scram_state *state, char *input) &conn->errorMessage); if (encoded_server_signature == NULL) { - /* read_attr_value() has generated an error message */ + /* read_attr_value() has appended an error message */ return false; } if (*input != '\0') - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("malformed SCRAM message (garbage at end of server-final-message)\n")); server_signature_len = pg_b64_dec_len(strlen(encoded_server_signature)); decoded_server_signature = malloc(server_signature_len); if (!decoded_server_signature) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return false; } @@ -743,7 +753,7 @@ read_server_final_message(fe_scram_state *state, char *input) if (server_signature_len != SCRAM_KEY_LEN) { free(decoded_server_signature); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("malformed SCRAM message (invalid server signature)\n")); return false; } diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index a25fe4dd17..089226dc8a 100644 --- a/src/interfaces/libpq/fe-auth.c +++ b/src/interfaces/libpq/fe-auth.c @@ -72,7 +72,7 @@ pg_GSS_continue(PGconn *conn, int payloadlen) ginbuf.value = malloc(payloadlen); if (!ginbuf.value) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory allocating GSSAPI buffer (%d)\n"), payloadlen); return STATUS_ERROR; @@ -154,14 +154,14 @@ pg_GSS_startup(PGconn *conn, int payloadlen) if (!(host && host[0] != '\0')) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("host name must be specified\n")); return STATUS_ERROR; } if (conn->gctx) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("duplicate GSS authentication request\n")); return STATUS_ERROR; } @@ -195,10 +195,10 @@ pg_SSPI_error(PGconn *conn, const char *mprefix, SECURITY_STATUS r) FORMAT_MESSAGE_FROM_SYSTEM, NULL, r, 0, sysmsg, sizeof(sysmsg), NULL) == 0) - printfPQExpBuffer(&conn->errorMessage, "%s: SSPI error %x\n", + appendPQExpBuffer(&conn->errorMessage, "%s: SSPI error %x\n", mprefix, (unsigned int) r); else - printfPQExpBuffer(&conn->errorMessage, "%s: %s (%x)\n", + appendPQExpBuffer(&conn->errorMessage, "%s: %s (%x)\n", mprefix, sysmsg, (unsigned int) r); } @@ -226,7 +226,7 @@ pg_SSPI_continue(PGconn *conn, int payloadlen) inputbuf = malloc(payloadlen); if (!inputbuf) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory allocating SSPI buffer (%d)\n"), payloadlen); return STATUS_ERROR; @@ -286,7 +286,7 @@ pg_SSPI_continue(PGconn *conn, int payloadlen) conn->sspictx = malloc(sizeof(CtxtHandle)); if (conn->sspictx == NULL) { - printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return STATUS_ERROR; } memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle)); @@ -305,7 +305,7 @@ pg_SSPI_continue(PGconn *conn, int payloadlen) * authentication. Keep check in case it shows up with other * authentication methods later. */ - printfPQExpBuffer(&conn->errorMessage, "SSPI returned invalid number of output buffers\n"); + appendPQExpBuffer(&conn->errorMessage, "SSPI returned invalid number of output buffers\n"); return STATUS_ERROR; } @@ -345,7 +345,7 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadlen) if (conn->sspictx) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("duplicate SSPI authentication request\n")); return STATUS_ERROR; } @@ -356,7 +356,7 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadlen) conn->sspicred = malloc(sizeof(CredHandle)); if (conn->sspicred == NULL) { - printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return STATUS_ERROR; } @@ -384,14 +384,14 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadlen) */ if (!(host && host[0] != '\0')) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("host name must be specified\n")); return STATUS_ERROR; } conn->sspitarget = malloc(strlen(conn->krbsrvname) + strlen(host) + 2); if (!conn->sspitarget) { - printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return STATUS_ERROR; } sprintf(conn->sspitarget, "%s/%s", conn->krbsrvname, host); @@ -425,14 +425,14 @@ pg_SASL_init(PGconn *conn, int payloadlen) if (conn->channel_binding[0] == 'r' && /* require */ !conn->ssl_in_use) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("channel binding required, but SSL not in use\n")); goto error; } if (conn->sasl_state) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("duplicate SASL authentication request\n")); goto error; } @@ -448,7 +448,7 @@ pg_SASL_init(PGconn *conn, int payloadlen) { if (pqGets(&mechanism_buf, conn)) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, "fe_sendauth: invalid authentication request from server: invalid list of authentication mechanisms\n"); goto error; } @@ -488,7 +488,7 @@ pg_SASL_init(PGconn *conn, int payloadlen) */ if (conn->channel_binding[0] == 'r') /* require */ { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("channel binding is required, but client does not support it\n")); goto error; } @@ -505,7 +505,7 @@ pg_SASL_init(PGconn *conn, int payloadlen) * the client and server supported it. The SCRAM exchange * checks for that, to prevent downgrade attacks. */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n")); goto error; } @@ -517,7 +517,7 @@ pg_SASL_init(PGconn *conn, int payloadlen) if (!selected_mechanism) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("none of the server's SASL authentication mechanisms are supported\n")); goto error; } @@ -525,7 +525,7 @@ pg_SASL_init(PGconn *conn, int payloadlen) if (conn->channel_binding[0] == 'r' && /* require */ strcmp(selected_mechanism, SCRAM_SHA_256_PLUS_NAME) != 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("channel binding is required, but server did not offer an authentication methodthat supports channel binding\n")); goto error; } @@ -546,7 +546,7 @@ pg_SASL_init(PGconn *conn, int payloadlen) password = conn->pgpass; if (password == NULL || password[0] == '\0') { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, PQnoPasswordSupplied); goto error; } @@ -607,7 +607,7 @@ oom_error: termPQExpBuffer(&mechanism_buf); if (initialresponse) free(initialresponse); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return STATUS_ERROR; } @@ -631,7 +631,7 @@ pg_SASL_continue(PGconn *conn, int payloadlen, bool final) challenge = malloc(payloadlen + 1); if (!challenge) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory allocating SASL buffer (%d)\n"), payloadlen); return STATUS_ERROR; @@ -656,7 +656,7 @@ pg_SASL_continue(PGconn *conn, int payloadlen, bool final) if (outputlen != 0) free(output); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("AuthenticationSASLFinal received from server, but SASL authentication was not completed\n")); return STATUS_ERROR; } @@ -726,14 +726,14 @@ pg_local_sendauth(PGconn *conn) { char sebuf[PG_STRERROR_R_BUFLEN]; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, "pg_local_sendauth: sendmsg: %s\n", strerror_r(errno, sebuf, sizeof(sebuf))); return STATUS_ERROR; } return STATUS_OK; #else - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("SCM_CRED authentication method not supported\n")); return STATUS_ERROR; #endif @@ -766,7 +766,7 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq) crypt_pwd = malloc(2 * (MD5_PASSWD_LEN + 1)); if (!crypt_pwd) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return STATUS_ERROR; } @@ -832,13 +832,13 @@ check_expected_areq(AuthRequest areq, PGconn *conn) case AUTH_REQ_OK: if (!pg_fe_scram_channel_bound(conn->sasl_state)) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("channel binding required, but server authenticated client without channelbinding\n")); result = false; } break; default: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("channel binding required but not supported by server's authentication request\n")); result = false; break; @@ -862,6 +862,8 @@ check_expected_areq(AuthRequest areq, PGconn *conn) int pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn) { + int oldmsglen; + if (!check_expected_areq(areq, conn)) return STATUS_ERROR; @@ -871,12 +873,12 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn) break; case AUTH_REQ_KRB4: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("Kerberos 4 authentication not supported\n")); return STATUS_ERROR; case AUTH_REQ_KRB5: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("Kerberos 5 authentication not supported\n")); return STATUS_ERROR; @@ -947,7 +949,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn) /* No GSSAPI *or* SSPI support */ case AUTH_REQ_GSS: case AUTH_REQ_GSS_CONT: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("GSSAPI authentication not supported\n")); return STATUS_ERROR; #endif /* defined(ENABLE_GSS) || defined(ENABLE_SSPI) */ @@ -979,7 +981,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn) */ #if !defined(ENABLE_GSS) case AUTH_REQ_SSPI: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("SSPI authentication not supported\n")); return STATUS_ERROR; #endif /* !define(ENABLE_GSS) */ @@ -987,7 +989,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn) case AUTH_REQ_CRYPT: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("Crypt authentication not supported\n")); return STATUS_ERROR; @@ -1002,13 +1004,13 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn) password = conn->pgpass; if (password == NULL || password[0] == '\0') { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, PQnoPasswordSupplied); return STATUS_ERROR; } if (pg_password_sendauth(conn, password, areq) != STATUS_OK) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, "fe_sendauth: error sending password authentication\n"); return STATUS_ERROR; } @@ -1032,16 +1034,17 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn) case AUTH_REQ_SASL_FIN: if (conn->sasl_state == NULL) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, "fe_sendauth: invalid authentication request from server: AUTH_REQ_SASL_CONT without AUTH_REQ_SASL\n"); return STATUS_ERROR; } + oldmsglen = conn->errorMessage.len; if (pg_SASL_continue(conn, payloadlen, (areq == AUTH_REQ_SASL_FIN)) != STATUS_OK) { - /* Use error message, if set already */ - if (conn->errorMessage.len == 0) - printfPQExpBuffer(&conn->errorMessage, + /* Use this message if pg_SASL_continue didn't supply one */ + if (conn->errorMessage.len == oldmsglen) + appendPQExpBuffer(&conn->errorMessage, "fe_sendauth: error in SASL authentication\n"); return STATUS_ERROR; } @@ -1053,7 +1056,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn) break; default: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("authentication method %u not supported\n"), areq); return STATUS_ERROR; } @@ -1067,7 +1070,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn) * * Returns a pointer to malloc'd space containing whatever name the user * has authenticated to the system. If there is an error, return NULL, - * and put a suitable error message in *errorMessage if that's not NULL. + * and append a suitable error message to *errorMessage if that's not NULL. */ char * pg_fe_getauthname(PQExpBuffer errorMessage) @@ -1100,7 +1103,7 @@ pg_fe_getauthname(PQExpBuffer errorMessage) if (GetUserName(username, &namesize)) name = username; else if (errorMessage) - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("user name lookup failure: error code %lu\n"), GetLastError()); #else @@ -1110,12 +1113,12 @@ pg_fe_getauthname(PQExpBuffer errorMessage) else if (errorMessage) { if (pwerr != 0) - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("could not look up local user ID %d: %s\n"), (int) user_id, strerror_r(pwerr, pwdbuf, sizeof(pwdbuf))); else - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("local user with ID %d does not exist\n"), (int) user_id); } @@ -1125,7 +1128,7 @@ pg_fe_getauthname(PQExpBuffer errorMessage) { result = strdup(name); if (result == NULL && errorMessage) - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); } @@ -1196,6 +1199,8 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, if (!conn) return NULL; + resetPQExpBuffer(&conn->errorMessage); + /* If no algorithm was given, ask the server. */ if (algorithm == NULL) { @@ -1217,7 +1222,7 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, if (PQntuples(res) != 1 || PQnfields(res) != 1) { PQclear(res); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("unexpected shape of result set returned for SHOW\n")); return NULL; } @@ -1226,7 +1231,7 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, if (strlen(val) > MAX_ALGORITHM_NAME_LEN) { PQclear(res); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("password_encryption value too long\n")); return NULL; } @@ -1266,14 +1271,14 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, } else { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("unrecognized password encryption algorithm \"%s\"\n"), algorithm); return NULL; } if (!crypt_pwd) - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return crypt_pwd; diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index a834ce8cf0..84e2868104 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -755,7 +755,9 @@ PQconnectStartParams(const char *const *keywords, PQconninfoOption *connOptions; /* - * Allocate memory for the conn structure + * Allocate memory for the conn structure. Note that we also expect this + * to initialize conn->errorMessage to empty. All subsequent steps during + * connection initialization will only append to that buffer. */ conn = makeEmptyPGconn(); if (conn == NULL) @@ -831,7 +833,9 @@ PQconnectStart(const char *conninfo) PGconn *conn; /* - * Allocate memory for the conn structure + * Allocate memory for the conn structure. Note that we also expect this + * to initialize conn->errorMessage to empty. All subsequent steps during + * connection initialization will only append to that buffer. */ conn = makeEmptyPGconn(); if (conn == NULL) @@ -889,7 +893,7 @@ fillPGconn(PGconn *conn, PQconninfoOption *connOptions) *connmember = strdup(tmp); if (*connmember == NULL) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return false; } @@ -1072,7 +1076,7 @@ connectOptions2(PGconn *conn) if (more || i != conn->nconnhost) { conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not match %d host names to %d hostaddr values\n"), count_comma_separated_elems(conn->pghost), conn->nconnhost); return false; @@ -1153,7 +1157,7 @@ connectOptions2(PGconn *conn) else if (more || i != conn->nconnhost) { conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not match %d port numbers to %d hosts\n"), count_comma_separated_elems(conn->pgport), conn->nconnhost); return false; @@ -1246,7 +1250,7 @@ connectOptions2(PGconn *conn) && strcmp(conn->channel_binding, "require") != 0) { conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid %s value: \"%s\"\n"), "channel_binding", conn->channel_binding); return false; @@ -1272,7 +1276,7 @@ connectOptions2(PGconn *conn) && strcmp(conn->sslmode, "verify-full") != 0) { conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid %s value: \"%s\"\n"), "sslmode", conn->sslmode); return false; @@ -1293,7 +1297,7 @@ connectOptions2(PGconn *conn) case 'r': /* "require" */ case 'v': /* "verify-ca" or "verify-full" */ conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("sslmode value \"%s\" invalid when SSL support is not compiled in\n"), conn->sslmode); return false; @@ -1314,7 +1318,7 @@ connectOptions2(PGconn *conn) if (!sslVerifyProtocolVersion(conn->ssl_min_protocol_version)) { conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid %s value: \"%s\"\n"), "ssl_min_protocol_version", conn->ssl_min_protocol_version); @@ -1323,7 +1327,7 @@ connectOptions2(PGconn *conn) if (!sslVerifyProtocolVersion(conn->ssl_max_protocol_version)) { conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid %s value: \"%s\"\n"), "ssl_max_protocol_version", conn->ssl_max_protocol_version); @@ -1341,7 +1345,7 @@ connectOptions2(PGconn *conn) conn->ssl_max_protocol_version)) { conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid SSL protocol version range\n")); return false; } @@ -1356,7 +1360,7 @@ connectOptions2(PGconn *conn) strcmp(conn->gssencmode, "require") != 0) { conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid %s value: \"%s\"\n"), "gssencmode", conn->gssencmode); @@ -1366,7 +1370,7 @@ connectOptions2(PGconn *conn) if (strcmp(conn->gssencmode, "require") == 0) { conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("gssencmode value \"%s\" invalid when GSSAPI support is not compiled in\n"), conn->gssencmode); return false; @@ -1401,7 +1405,7 @@ connectOptions2(PGconn *conn) && strcmp(conn->target_session_attrs, "read-write") != 0) { conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid %s value: \"%s\"\n"), "target_settion_attrs", conn->target_session_attrs); @@ -1420,7 +1424,7 @@ connectOptions2(PGconn *conn) oom_error: conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return false; } @@ -1487,7 +1491,9 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, PGconn *conn; /* - * Allocate memory for the conn structure + * Allocate memory for the conn structure. Note that we also expect this + * to initialize conn->errorMessage to empty. All subsequent steps during + * connection initialization will only append to that buffer. */ conn = makeEmptyPGconn(); if (conn == NULL) @@ -1596,7 +1602,7 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, oom_error: conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return conn; } @@ -2017,7 +2023,7 @@ connectDBStart(PGconn *conn) */ if (!pg_link_canary_is_frontend()) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, "libpq is incorrectly linked to backend functions\n"); goto connect_errReturn; } @@ -2026,14 +2032,6 @@ connectDBStart(PGconn *conn) conn->inStart = conn->inCursor = conn->inEnd = 0; conn->outCount = 0; - /* - * Ensure errorMessage is empty, too. PQconnectPoll will append messages - * to it in the process of scanning for a working server. Thus, if we - * fail to connect to multiple hosts, the final error message will include - * details about each failure. - */ - resetPQExpBuffer(&conn->errorMessage); - /* * Set up to try to connect to the first host. (Setting whichhost = -1 is * a bit of a cheat, but PQconnectPoll will advance it to 0 before @@ -2139,12 +2137,6 @@ connectDBComplete(PGconn *conn) switch (flag) { case PGRES_POLLING_OK: - - /* - * Reset stored error messages since we now have a working - * connection - */ - resetPQExpBuffer(&conn->errorMessage); return 1; /* success! */ case PGRES_POLLING_READING: @@ -2189,46 +2181,6 @@ connectDBComplete(PGconn *conn) } } -/* - * This subroutine saves conn->errorMessage, which will be restored back by - * restoreErrorMessage subroutine. Returns false on OOM failure. - */ -static bool -saveErrorMessage(PGconn *conn, PQExpBuffer savedMessage) -{ - initPQExpBuffer(savedMessage); - appendPQExpBufferStr(savedMessage, - conn->errorMessage.data); - if (PQExpBufferBroken(savedMessage)) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory\n")); - return false; - } - /* Clear whatever is in errorMessage now */ - resetPQExpBuffer(&conn->errorMessage); - return true; -} - -/* - * Restores saved error messages back to conn->errorMessage, prepending them - * to whatever is in conn->errorMessage already. (This does the right thing - * if anything's been added to conn->errorMessage since saveErrorMessage.) - */ -static void -restoreErrorMessage(PGconn *conn, PQExpBuffer savedMessage) -{ - appendPQExpBufferStr(savedMessage, conn->errorMessage.data); - resetPQExpBuffer(&conn->errorMessage); - appendPQExpBufferStr(&conn->errorMessage, savedMessage->data); - /* If any step above hit OOM, just report that */ - if (PQExpBufferBroken(savedMessage) || - PQExpBufferBroken(&conn->errorMessage)) - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("out of memory\n")); - termPQExpBuffer(savedMessage); -} - /* ---------------- * PQconnectPoll * @@ -2264,7 +2216,6 @@ PQconnectPoll(PGconn *conn) PGresult *res; char sebuf[PG_STRERROR_R_BUFLEN]; int optval; - PQExpBufferData savedMessage; if (conn == NULL) return PGRES_POLLING_FAILED; @@ -2954,11 +2905,7 @@ keep_going: /* We will come back to here until there is EnvironmentOptions); if (!startpacket) { - /* - * will not appendbuffer here, since it's likely to also - * run out of memory - */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); goto error_return; } @@ -3448,7 +3395,6 @@ keep_going: /* We will come back to here until there is * avoid the Kerberos code doing a hostname look-up. */ res = pg_fe_sendauth(areq, msgLength, conn); - conn->errorMessage.len = strlen(conn->errorMessage.data); /* OK, we have processed the message; mark data consumed */ conn->inStart = conn->inCursor; @@ -3576,24 +3522,16 @@ keep_going: /* We will come back to here until there is strcmp(conn->target_session_attrs, "read-write") == 0) { /* - * Save existing error messages across the PQsendQuery - * attempt. This is necessary because PQsendQuery is - * going to reset conn->errorMessage, so we would lose - * error messages related to previous hosts we have tried - * and failed to connect to. + * We use PQsendQueryContinue so that conn->errorMessage + * does not get cleared. We need to preserve any error + * messages related to previous hosts we have tried and + * failed to connect to. */ - if (!saveErrorMessage(conn, &savedMessage)) - goto error_return; - conn->status = CONNECTION_OK; - if (!PQsendQuery(conn, - "SHOW transaction_read_only")) - { - restoreErrorMessage(conn, &savedMessage); + if (!PQsendQueryContinue(conn, + "SHOW transaction_read_only")) goto error_return; - } conn->status = CONNECTION_CHECK_WRITABLE; - restoreErrorMessage(conn, &savedMessage); return PGRES_POLLING_READING; } @@ -3673,20 +3611,13 @@ keep_going: /* We will come back to here until there is const char *displayed_host; const char *displayed_port; - if (!saveErrorMessage(conn, &savedMessage)) - goto error_return; - conn->status = CONNECTION_OK; if (!PQconsumeInput(conn)) - { - restoreErrorMessage(conn, &savedMessage); goto error_return; - } if (PQisBusy(conn)) { conn->status = CONNECTION_CHECK_WRITABLE; - restoreErrorMessage(conn, &savedMessage); return PGRES_POLLING_READING; } @@ -3701,7 +3632,6 @@ keep_going: /* We will come back to here until there is { /* Not writable; fail this connection. */ PQclear(res); - restoreErrorMessage(conn, &savedMessage); /* Append error report to conn->errorMessage. */ if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS) @@ -3732,7 +3662,6 @@ keep_going: /* We will come back to here until there is /* Session is read-write, so we're good. */ PQclear(res); - termPQExpBuffer(&savedMessage); /* * Finish reading any remaining messages before being @@ -3748,7 +3677,6 @@ keep_going: /* We will come back to here until there is */ if (res) PQclear(res); - restoreErrorMessage(conn, &savedMessage); /* Append error report to conn->errorMessage. */ if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS) @@ -4157,6 +4085,9 @@ closePGconn(PGconn *conn) /* * Close the connection, reset all transient state, flush I/O buffers. + * Note that this includes clearing conn->errorMessage; we're no longer + * interested in any failures associated with the old connection, and we + * want a clean slate for any new connection attempt. */ pqDropConnection(conn, true); conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just absent */ @@ -4212,7 +4143,7 @@ PQreset(PGconn *conn) conn->events[i].passThrough)) { conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"), conn->events[i].name); break; @@ -4272,7 +4203,7 @@ PQresetPoll(PGconn *conn) conn->events[i].passThrough)) { conn->status = CONNECTION_BAD; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("PGEventProc \"%s\" failed during PGEVT_CONNRESET event\n"), conn->events[i].name); return PGRES_POLLING_FAILED; @@ -4569,7 +4500,7 @@ pqPacketSend(PGconn *conn, char pack_type, * 2 if a connection could not be established, and * 3 if a fatal error occurred. * - * An error message is returned in the third argument for return codes 1 and 3. + * An error message is appended to *errorMessage for return codes 1 and 3. */ static int ldapServiceLookup(const char *purl, PQconninfoOption *options, @@ -4607,7 +4538,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, if ((url = strdup(purl)) == NULL) { - printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); return 3; } @@ -4619,7 +4550,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, if (pg_strncasecmp(url, LDAP_URL, strlen(LDAP_URL)) != 0) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": scheme must be ldap://\n"), purl); free(url); return 3; @@ -4634,7 +4565,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, p = strchr(url + strlen(LDAP_URL), '/'); if (p == NULL || *(p + 1) == '\0' || *(p + 1) == '?') { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": missing distinguished name\n"), purl); free(url); @@ -4646,7 +4577,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, /* attribute */ if ((p = strchr(dn, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?') { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have exactly one attribute\n"), purl); free(url); @@ -4658,7 +4589,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, /* scope */ if ((p = strchr(attrs[0], '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?') { - printfPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"),purl); + appendPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"),purl); free(url); return 3; } @@ -4668,7 +4599,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, /* filter */ if ((p = strchr(scopestr, '?')) == NULL || *(p + 1) == '\0' || *(p + 1) == '?') { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": no filter\n"), purl); free(url); return 3; @@ -4689,7 +4620,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, lport = strtol(portstr, &endptr, 10); if (*portstr == '\0' || *endptr != '\0' || errno || lport < 0 || lport > 65535) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": invalid port number\n"), purl); free(url); @@ -4701,7 +4632,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, /* Allow only one attribute */ if (strchr(attrs[0], ',') != NULL) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have exactly one attribute\n"), purl); free(url); @@ -4717,7 +4648,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, scope = LDAP_SCOPE_SUBTREE; else { - printfPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"),purl); + appendPQExpBuffer(errorMessage, libpq_gettext("invalid LDAP URL \"%s\": must have search scope (base/one/sub)\n"),purl); free(url); return 3; } @@ -4725,7 +4656,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, /* initialize LDAP structure */ if ((ld = ldap_init(hostname, port)) == NULL) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("could not create LDAP structure\n")); free(url); return 3; @@ -4801,7 +4732,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, { if (res != NULL) ldap_msgfree(res); - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("lookup on LDAP server failed: %s\n"), ldap_err2string(rc)); ldap_unbind(ld); @@ -4812,7 +4743,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, /* complain if there was not exactly one result */ if ((rc = ldap_count_entries(ld, res)) != 1) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, rc ? libpq_gettext("more than one entry found on LDAP lookup\n") : libpq_gettext("no entry found on LDAP lookup\n")); ldap_msgfree(res); @@ -4825,7 +4756,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, if ((entry = ldap_first_entry(ld, res)) == NULL) { /* should never happen */ - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("no entry found on LDAP lookup\n")); ldap_msgfree(res); ldap_unbind(ld); @@ -4836,7 +4767,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, /* get values */ if ((values = ldap_get_values_len(ld, entry, attrs[0])) == NULL) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("attribute has no values on LDAP lookup\n")); ldap_msgfree(res); ldap_unbind(ld); @@ -4849,7 +4780,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, if (values[0] == NULL) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("attribute has no values on LDAP lookup\n")); ldap_value_free_len(values); ldap_unbind(ld); @@ -4862,7 +4793,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, size += values[i]->bv_len + 1; if ((result = malloc(size)) == NULL) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); ldap_value_free_len(values); ldap_unbind(ld); @@ -4901,7 +4832,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, } else if (ld_is_nl_cr(*p)) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"), optname); free(result); @@ -4920,7 +4851,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, } else if (!ld_is_sp_tab(*p)) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"), optname); free(result); @@ -4981,7 +4912,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, options[i].val = strdup(optval); if (!options[i].val) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); free(result); return 3; @@ -4993,7 +4924,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, } if (!found_keyword) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("invalid connection option \"%s\"\n"), optname); free(result); @@ -5009,7 +4940,7 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, if (state == 5 || state == 6) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("unterminated quoted string in connection info string\n")); return 3; } @@ -5090,7 +5021,7 @@ next_file: last_file: if (!group_found) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("definition of service \"%s\" not found\n"), service); return 3; } @@ -5117,7 +5048,7 @@ parseServiceFile(const char *serviceFile, f = fopen(serviceFile, "r"); if (f == NULL) { - printfPQExpBuffer(errorMessage, libpq_gettext("service file \"%s\" not found\n"), + appendPQExpBuffer(errorMessage, libpq_gettext("service file \"%s\" not found\n"), serviceFile); return 1; } @@ -5193,7 +5124,7 @@ parseServiceFile(const char *serviceFile, val = strchr(line, '='); if (val == NULL) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("syntax error in service file \"%s\", line %d\n"), serviceFile, linenr); @@ -5204,7 +5135,7 @@ parseServiceFile(const char *serviceFile, if (strcmp(key, "service") == 0) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("nested service specifications not supported in service file \"%s\",line %d\n"), serviceFile, linenr); @@ -5225,7 +5156,7 @@ parseServiceFile(const char *serviceFile, options[i].val = strdup(val); if (!options[i].val) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); result = 3; goto exit; @@ -5237,7 +5168,7 @@ parseServiceFile(const char *serviceFile, if (!found_keyword) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("syntax error in service file \"%s\", line %d\n"), serviceFile, linenr); @@ -5307,7 +5238,7 @@ conninfo_init(PQExpBuffer errorMessage) options = (PQconninfoOption *) malloc(sizeof(PQconninfoOption) * sizeof(PQconninfoOptions) / sizeof(PQconninfoOptions[0])); if (options == NULL) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); return NULL; } @@ -5328,7 +5259,7 @@ conninfo_init(PQExpBuffer errorMessage) * Connection string parser * * Returns a malloc'd PQconninfoOption array, if parsing is successful. - * Otherwise, NULL is returned and an error message is left in errorMessage. + * Otherwise, NULL is returned and an error message is added to errorMessage. * * If use_defaults is true, default values are filled in (from a service file, * environment variables, etc). @@ -5406,7 +5337,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage, /* Need a modifiable copy of the input string */ if ((buf = strdup(conninfo)) == NULL) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); PQconninfoFree(options); return NULL; @@ -5445,7 +5376,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage, /* Check that there is a following '=' */ if (*cp != '=') { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("missing \"=\" after \"%s\" in connection info string\n"), pname); PQconninfoFree(options); @@ -5494,7 +5425,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage, { if (*cp == '\0') { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("unterminated quoted string in connection info string\n")); PQconninfoFree(options); free(buf); @@ -5551,7 +5482,7 @@ conninfo_parse(const char *conninfo, PQExpBuffer errorMessage, * * If successful, a malloc'd PQconninfoOption array is returned. * If not successful, NULL is returned and an error message is - * left in errorMessage. + * appended to errorMessage. * Defaults are supplied (from a service file, environment variables, etc) * for unspecified options, but only if use_defaults is true. * @@ -5630,7 +5561,7 @@ conninfo_array_parse(const char *const *keywords, const char *const *values, /* Check for invalid connection option */ if (option->keyword == NULL) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("invalid connection option \"%s\"\n"), pname); PQconninfoFree(options); @@ -5662,7 +5593,7 @@ conninfo_array_parse(const char *const *keywords, const char *const *values, options[k].val = strdup(str_option->val); if (!options[k].val) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); PQconninfoFree(options); PQconninfoFree(dbname_options); @@ -5691,7 +5622,7 @@ conninfo_array_parse(const char *const *keywords, const char *const *values, option->val = strdup(pvalue); if (!option->val) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); PQconninfoFree(options); PQconninfoFree(dbname_options); @@ -5763,7 +5694,7 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage) if (!option->val) { if (errorMessage) - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); return false; } @@ -5787,7 +5718,7 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage) if (!option->val) { if (errorMessage) - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); return false; } @@ -5805,7 +5736,7 @@ conninfo_add_defaults(PQconninfoOption *options, PQExpBuffer errorMessage) if (!option->val) { if (errorMessage) - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); return false; } @@ -5906,7 +5837,7 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri, initPQExpBuffer(&portbuf); if (PQExpBufferDataBroken(hostbuf) || PQExpBufferDataBroken(portbuf)) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); goto cleanup; } @@ -5915,7 +5846,7 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri, buf = strdup(uri); if (buf == NULL) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); goto cleanup; } @@ -5926,7 +5857,7 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri, if (prefix_len == 0) { /* Should never happen */ - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("invalid URI propagated to internal parser routine: \"%s\"\n"), uri); goto cleanup; @@ -6003,14 +5934,14 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri, ++p; if (!*p) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("end of string reached when looking for matching \"]\" in IPv6 host addressin URI: \"%s\"\n"), uri); goto cleanup; } if (p == host) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("IPv6 host address may not be empty in URI: \"%s\"\n"), uri); goto cleanup; @@ -6025,7 +5956,7 @@ conninfo_uri_parse_options(PQconninfoOption *options, const char *uri, */ if (*p && *p != ':' && *p != '/' && *p != '?' && *p != ',') { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("unexpected character \"%c\" at position %d in URI (expected \":\" or \"/\"):\"%s\"\n"), *p, (int) (p - buf + 1), uri); goto cleanup; @@ -6142,6 +6073,7 @@ conninfo_uri_parse_params(char *params, char *value = NULL; char *p = params; bool malloced = false; + int oldmsglen; /* * Scan the params string for '=' and '&', marking the end of keyword @@ -6154,7 +6086,7 @@ conninfo_uri_parse_params(char *params, /* Was there '=' already? */ if (value != NULL) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("extra key/value separator \"=\" in URI query parameter: \"%s\"\n"), keyword); return false; @@ -6174,7 +6106,7 @@ conninfo_uri_parse_params(char *params, /* Was there '=' at all? */ if (value == NULL) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("missing key/value separator \"=\" in URI query parameter: \"%s\"\n"), keyword); return false; @@ -6220,12 +6152,13 @@ conninfo_uri_parse_params(char *params, * otherwise. At this point both keyword and value are not * URI-encoded. */ + oldmsglen = errorMessage->len; if (!conninfo_storeval(connOptions, keyword, value, errorMessage, true, false)) { /* Insert generic message if conninfo_storeval didn't give one. */ - if (errorMessage->len == 0) - printfPQExpBuffer(errorMessage, + if (errorMessage->len == oldmsglen) + appendPQExpBuffer(errorMessage, libpq_gettext("invalid URI query parameter: \"%s\"\n"), keyword); /* And fail. */ @@ -6272,7 +6205,7 @@ conninfo_uri_decode(const char *str, PQExpBuffer errorMessage) buf = malloc(strlen(str) + 1); if (buf == NULL) { - printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); return NULL; } p = buf; @@ -6299,7 +6232,7 @@ conninfo_uri_decode(const char *str, PQExpBuffer errorMessage) */ if (!(get_hexdigit(*q++, &hi) && get_hexdigit(*q++, &lo))) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("invalid percent-encoded token: \"%s\"\n"), str); free(buf); @@ -6309,7 +6242,7 @@ conninfo_uri_decode(const char *str, PQExpBuffer errorMessage) c = (hi << 4) | lo; if (c == 0) { - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("forbidden value %%00 in percent-encoded value: \"%s\"\n"), str); free(buf); @@ -6404,7 +6337,7 @@ conninfo_storeval(PQconninfoOption *connOptions, if (option == NULL) { if (!ignoreMissing) - printfPQExpBuffer(errorMessage, + appendPQExpBuffer(errorMessage, libpq_gettext("invalid connection option \"%s\"\n"), keyword); return NULL; @@ -6422,7 +6355,7 @@ conninfo_storeval(PQconninfoOption *connOptions, value_copy = strdup(value); if (value_copy == NULL) { - printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); + appendPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); return NULL; } } @@ -6469,7 +6402,10 @@ PQconninfo(PGconn *conn) if (conn == NULL) return NULL; - /* We don't actually report any errors here, but callees want a buffer */ + /* + * We don't actually report any errors here, but callees want a buffer, + * and we prefer not to trash the conn's errorMessage. + */ initPQExpBuffer(&errorBuf); if (PQExpBufferDataBroken(errorBuf)) return NULL; /* out of memory already :-( */ diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index d48f0fd587..39f7fefb3a 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, @@ -667,25 +668,6 @@ pqSetResultError(PGresult *res, const char *msg) res->errMsg = NULL; } -/* - * 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,17 +1179,32 @@ 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, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("command string is a null pointer\n")); return 0; } @@ -1291,19 +1256,19 @@ 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, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("command string is a null pointer\n")); return 0; } if (nParams < 0 || nParams > 65535) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("number of parameters must be between 0 and 65535\n")); return 0; } @@ -1331,25 +1296,25 @@ 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, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("statement name is a null pointer\n")); return 0; } if (!query) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("command string is a null pointer\n")); return 0; } if (nParams < 0 || nParams > 65535) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("number of parameters must be between 0 and 65535\n")); return 0; } @@ -1357,7 +1322,7 @@ PQsendPrepare(PGconn *conn, /* This isn't gonna work on a 2.0 server */ if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("function requires at least protocol version 3.0\n")); return 0; } @@ -1432,19 +1397,19 @@ 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, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("statement name is a null pointer\n")); return 0; } if (nParams < 0 || nParams > 65535) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("number of parameters must be between 0 and 65535\n")); return 0; } @@ -1464,25 +1429,28 @@ 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, + appendPQExpBuffer(&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, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("another command is already in progress\n")); return false; } @@ -1520,7 +1488,7 @@ PQsendQueryGuts(PGconn *conn, /* This isn't gonna work on a 2.0 server */ if (PG_PROTOCOL_MAJOR(conn->pversion) < 3) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("function requires at least protocol version 3.0\n")); return 0; } @@ -1596,7 +1564,7 @@ PQsendQueryGuts(PGconn *conn, nbytes = paramLengths[i]; else { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&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); @@ -2025,6 +1993,11 @@ PQexecStart(PGconn *conn) if (!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,7 +2020,7 @@ PQexecStart(PGconn *conn) else { /* In older protocols we have to punt */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("COPY IN state must be terminated first\n")); return false; } @@ -2067,7 +2040,7 @@ PQexecStart(PGconn *conn) else { /* In older protocols we have to punt */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("COPY OUT state must be terminated first\n")); return false; } @@ -2075,7 +2048,7 @@ PQexecStart(PGconn *conn) else if (resultStatus == PGRES_COPY_BOTH) { /* We don't allow PQexec during COPY BOTH */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("PQexec not allowed during COPY BOTH\n")); return false; } @@ -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,13 +2181,13 @@ 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, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("function requires at least protocol version 3.0\n")); return 0; } @@ -2321,7 +2279,7 @@ PQputCopyData(PGconn *conn, const char *buffer, int nbytes) if (conn->asyncStatus != PGASYNC_COPY_IN && conn->asyncStatus != PGASYNC_COPY_BOTH) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("no COPY in progress\n")); return -1; } @@ -2388,7 +2346,7 @@ PQputCopyEnd(PGconn *conn, const char *errormsg) if (conn->asyncStatus != PGASYNC_COPY_IN && conn->asyncStatus != PGASYNC_COPY_BOTH) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("no COPY in progress\n")); return -1; } @@ -2431,7 +2389,7 @@ PQputCopyEnd(PGconn *conn, const char *errormsg) if (errormsg) { /* Oops, no way to do this in 2.0 */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("function requires at least protocol version 3.0\n")); return -1; } @@ -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,7 +2435,7 @@ PQgetCopyData(PGconn *conn, char **buffer, int async) if (conn->asyncStatus != PGASYNC_COPY_OUT && conn->asyncStatus != PGASYNC_COPY_BOTH) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("no COPY in progress\n")); return -2; } @@ -2662,13 +2619,15 @@ 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, + appendPQExpBuffer(&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,7 +3351,7 @@ PQescapeStringInternal(PGconn *conn, if (error) *error = 1; if (conn) - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("incomplete multibyte character\n")); for (; i < len; i++) { @@ -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,7 +3440,7 @@ 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, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("incomplete multibyte character\n")); return NULL; } @@ -3490,7 +3458,7 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident) result = rp = (char *) malloc(result_size); if (rp == NULL) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return NULL; } @@ -3655,7 +3623,7 @@ PQescapeByteaInternal(PGconn *conn, if (rp == NULL) { if (conn) - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&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)); diff --git a/src/interfaces/libpq/fe-gssapi-common.c b/src/interfaces/libpq/fe-gssapi-common.c index c2e79bb55c..b7661dd659 100644 --- a/src/interfaces/libpq/fe-gssapi-common.c +++ b/src/interfaces/libpq/fe-gssapi-common.c @@ -46,7 +46,6 @@ void pg_GSS_error(const char *mprefix, PGconn *conn, OM_uint32 maj_stat, OM_uint32 min_stat) { - resetPQExpBuffer(&conn->errorMessage); appendPQExpBuffer(&conn->errorMessage, "%s:", mprefix); pg_GSS_error_int(&conn->errorMessage, maj_stat, GSS_C_GSS_CODE); appendPQExpBufferChar(&conn->errorMessage, ':'); @@ -94,7 +93,7 @@ pg_GSS_load_servicename(PGconn *conn) host = PQhost(conn); if (!(host && host[0] != '\0')) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("host name must be specified\n")); return STATUS_ERROR; } @@ -107,7 +106,7 @@ pg_GSS_load_servicename(PGconn *conn) temp_gbuf.value = (char *) malloc(maxlen); if (!temp_gbuf.value) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return STATUS_ERROR; } diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c index 432935061f..5a6e4df3a0 100644 --- a/src/interfaces/libpq/fe-lobj.c +++ b/src/interfaces/libpq/fe-lobj.c @@ -61,11 +61,8 @@ lo_open(PGconn *conn, Oid lobjId, int mode) PQArgBlock argv[2]; PGresult *res; - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } + if (lo_initialize(conn) < 0) + return -1; argv[0].isint = 1; argv[0].len = 4; @@ -103,11 +100,8 @@ lo_close(PGconn *conn, int fd) int retval; int result_len; - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } + if (lo_initialize(conn) < 0) + return -1; argv[0].isint = 1; argv[0].len = 4; @@ -141,16 +135,13 @@ lo_truncate(PGconn *conn, int fd, size_t len) int retval; int result_len; - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } + if (lo_initialize(conn) < 0) + return -1; /* Must check this on-the-fly because it's not there pre-8.3 */ if (conn->lobjfuncs->fn_lo_truncate == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("cannot determine OID of function lo_truncate\n")); return -1; } @@ -166,7 +157,7 @@ lo_truncate(PGconn *conn, int fd, size_t len) */ if (len > (size_t) INT_MAX) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("argument of lo_truncate exceeds integer range\n")); return -1; } @@ -209,15 +200,12 @@ lo_truncate64(PGconn *conn, int fd, pg_int64 len) int retval; int result_len; - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } + if (lo_initialize(conn) < 0) + return -1; if (conn->lobjfuncs->fn_lo_truncate64 == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("cannot determine OID of function lo_truncate64\n")); return -1; } @@ -261,11 +249,8 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len) PGresult *res; int result_len; - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } + if (lo_initialize(conn) < 0) + return -1; /* * Long ago, somebody thought it'd be a good idea to declare this function @@ -275,7 +260,7 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len) */ if (len > (size_t) INT_MAX) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("argument of lo_read exceeds integer range\n")); return -1; } @@ -316,11 +301,8 @@ lo_write(PGconn *conn, int fd, const char *buf, size_t len) int result_len; int retval; - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } + if (lo_initialize(conn) < 0) + return -1; /* * Long ago, somebody thought it'd be a good idea to declare this function @@ -330,7 +312,7 @@ lo_write(PGconn *conn, int fd, const char *buf, size_t len) */ if (len > (size_t) INT_MAX) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("argument of lo_write exceeds integer range\n")); return -1; } @@ -369,11 +351,8 @@ lo_lseek(PGconn *conn, int fd, int offset, int whence) int retval; int result_len; - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } + if (lo_initialize(conn) < 0) + return -1; argv[0].isint = 1; argv[0].len = 4; @@ -413,15 +392,12 @@ lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence) pg_int64 retval; int result_len; - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } + if (lo_initialize(conn) < 0) + return -1; if (conn->lobjfuncs->fn_lo_lseek64 == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("cannot determine OID of function lo_lseek64\n")); return -1; } @@ -469,11 +445,8 @@ lo_creat(PGconn *conn, int mode) int retval; int result_len; - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return InvalidOid; - } + if (lo_initialize(conn) < 0) + return InvalidOid; argv[0].isint = 1; argv[0].len = 4; @@ -508,16 +481,13 @@ lo_create(PGconn *conn, Oid lobjId) int retval; int result_len; - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return InvalidOid; - } + if (lo_initialize(conn) < 0) + return InvalidOid; /* Must check this on-the-fly because it's not there pre-8.1 */ if (conn->lobjfuncs->fn_lo_create == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("cannot determine OID of function lo_create\n")); return InvalidOid; } @@ -552,11 +522,8 @@ lo_tell(PGconn *conn, int fd) PGresult *res; int result_len; - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } + if (lo_initialize(conn) < 0) + return -1; argv[0].isint = 1; argv[0].len = 4; @@ -588,15 +555,12 @@ lo_tell64(PGconn *conn, int fd) PGresult *res; int result_len; - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } + if (lo_initialize(conn) < 0) + return -1; if (conn->lobjfuncs->fn_lo_tell64 == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("cannot determine OID of function lo_tell64\n")); return -1; } @@ -632,11 +596,8 @@ lo_unlink(PGconn *conn, Oid lobjId) int result_len; int retval; - if (conn == NULL || conn->lobjfuncs == NULL) - { - if (lo_initialize(conn) < 0) - return -1; - } + if (lo_initialize(conn) < 0) + return -1; argv[0].isint = 1; argv[0].len = 4; @@ -696,13 +657,19 @@ lo_import_internal(PGconn *conn, const char *filename, Oid oid) int lobj; char sebuf[PG_STRERROR_R_BUFLEN]; + if (conn == NULL) + return InvalidOid; + + /* Since this is the beginning of a query cycle, reset the error buffer */ + resetPQExpBuffer(&conn->errorMessage); + /* * open the file to be read in */ fd = open(filename, O_RDONLY | PG_BINARY, 0666); if (fd < 0) { /* error */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not open file \"%s\": %s\n"), filename, strerror_r(errno, sebuf, sizeof(sebuf))); return InvalidOid; @@ -757,6 +724,7 @@ lo_import_internal(PGconn *conn, const char *filename, Oid oid) (void) lo_close(conn, lobj); (void) close(fd); + /* deliberately overwrite any error from lo_close */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not read from file \"%s\": %s\n"), filename, @@ -811,6 +779,7 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename) int save_errno = errno; (void) lo_close(conn, lobj); + /* deliberately overwrite any error from lo_close */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not open file \"%s\": %s\n"), filename, @@ -831,6 +800,7 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename) (void) lo_close(conn, lobj); (void) close(fd); + /* deliberately overwrite any error from lo_close */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not write to file \"%s\": %s\n"), filename, @@ -855,7 +825,7 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename) /* if we already failed, don't overwrite that msg with a close error */ if (close(fd) != 0 && result >= 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not write to file \"%s\": %s\n"), filename, strerror_r(errno, sebuf, sizeof(sebuf))); result = -1; @@ -868,9 +838,11 @@ lo_export(PGconn *conn, Oid lobjId, const char *filename) /* * lo_initialize * - * Initialize the large object interface for an existing connection. - * We ask the backend about the functions OID's in pg_proc for all - * functions that are required for large object operations. + * Initialize for a new large-object operation on an existing connection. + * Return 0 if OK, -1 on failure. + * + * If we haven't previously done so, we collect the function OIDs from + * pg_proc for all functions that are required for large object operations. */ static int lo_initialize(PGconn *conn) @@ -882,16 +854,25 @@ lo_initialize(PGconn *conn) const char *fname; Oid foid; - if (!conn) + /* Nothing we can do with no connection */ + if (conn == NULL) return -1; + /* Since this is the beginning of a query cycle, reset the error buffer */ + resetPQExpBuffer(&conn->errorMessage); + + /* Nothing else to do if we already collected info */ + if (conn->lobjfuncs != NULL) + return 0; + /* - * Allocate the structure to hold the functions OID's + * Allocate the structure to hold the function OIDs. We don't store it + * into the PGconn until it's successfully filled. */ lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs)); if (lobjfuncs == NULL) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return -1; } @@ -942,7 +923,7 @@ lo_initialize(PGconn *conn) { free(lobjfuncs); PQclear(res); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("query to initialize large object functions did not return data\n")); return -1; } @@ -991,56 +972,56 @@ lo_initialize(PGconn *conn) */ if (lobjfuncs->fn_lo_open == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("cannot determine OID of function lo_open\n")); free(lobjfuncs); return -1; } if (lobjfuncs->fn_lo_close == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("cannot determine OID of function lo_close\n")); free(lobjfuncs); return -1; } if (lobjfuncs->fn_lo_creat == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("cannot determine OID of function lo_creat\n")); free(lobjfuncs); return -1; } if (lobjfuncs->fn_lo_unlink == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("cannot determine OID of function lo_unlink\n")); free(lobjfuncs); return -1; } if (lobjfuncs->fn_lo_lseek == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("cannot determine OID of function lo_lseek\n")); free(lobjfuncs); return -1; } if (lobjfuncs->fn_lo_tell == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("cannot determine OID of function lo_tell\n")); free(lobjfuncs); return -1; } if (lobjfuncs->fn_lo_read == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("cannot determine OID of function loread\n")); free(lobjfuncs); return -1; } if (lobjfuncs->fn_lo_write == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("cannot determine OID of function lowrite\n")); free(lobjfuncs); return -1; diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index 6094f048f3..9d57f21119 100644 --- a/src/interfaces/libpq/fe-misc.c +++ b/src/interfaces/libpq/fe-misc.c @@ -379,7 +379,7 @@ pqCheckOutBufferSpace(size_t bytes_needed, PGconn *conn) } /* realloc failed. Probably out of memory */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, "cannot allocate memory for output buffer\n"); return EOF; } @@ -473,7 +473,7 @@ pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn) } /* realloc failed. Probably out of memory */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, "cannot allocate memory for input buffer\n"); return EOF; } @@ -619,7 +619,7 @@ pqReadData(PGconn *conn) if (conn->sock == PGINVALID_SOCKET) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("connection not open\n")); return -1; } @@ -798,7 +798,7 @@ retry4: * means the connection has been closed. Cope. */ definitelyEOF: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("server closed the connection unexpectedly\n" "\tThis probably means the server terminated abnormally\n" "\tbefore or while processing the request.\n")); @@ -836,6 +836,7 @@ pqSendSome(PGconn *conn, int len) { char *ptr = conn->outBuffer; int remaining = conn->outCount; + int oldmsglen = conn->errorMessage.len; int result = 0; /* @@ -862,13 +863,10 @@ pqSendSome(PGconn *conn, int len) if (conn->sock == PGINVALID_SOCKET) { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("connection not open\n")); conn->write_failed = true; - /* Transfer error message to conn->write_err_msg, if possible */ + /* Insert error message into conn->write_err_msg, if possible */ /* (strdup failure is OK, we'll cope later) */ - conn->write_err_msg = strdup(conn->errorMessage.data); - resetPQExpBuffer(&conn->errorMessage); + conn->write_err_msg = strdup(libpq_gettext("connection not open\n")); /* Discard queued data; no chance it'll ever be sent */ conn->outCount = 0; return 0; @@ -915,14 +913,16 @@ pqSendSome(PGconn *conn, int len) * Transfer error message to conn->write_err_msg, if * possible (strdup failure is OK, we'll cope later). * - * Note: this assumes that pqsecure_write and its children - * will overwrite not append to conn->errorMessage. If - * that's ever changed, we could remember the length of - * conn->errorMessage at entry to this routine, and then - * save and delete just what was appended. + * We only want to transfer whatever has been appended to + * conn->errorMessage since we entered this routine. */ - conn->write_err_msg = strdup(conn->errorMessage.data); - resetPQExpBuffer(&conn->errorMessage); + if (!PQExpBufferBroken(&conn->errorMessage)) + { + conn->write_err_msg = strdup(conn->errorMessage.data + + oldmsglen); + conn->errorMessage.len = oldmsglen; + conn->errorMessage.data[oldmsglen] = '\0'; + } /* Discard queued data; no chance it'll ever be sent */ conn->outCount = 0; @@ -1056,7 +1056,7 @@ pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time) if (result == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("timeout expired\n")); return 1; } @@ -1101,7 +1101,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time) return -1; if (conn->sock == PGINVALID_SOCKET) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid socket\n")); return -1; } @@ -1124,7 +1124,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time) { char sebuf[PG_STRERROR_R_BUFLEN]; - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("select() failed: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); } diff --git a/src/interfaces/libpq/fe-protocol2.c b/src/interfaces/libpq/fe-protocol2.c index ad6587f924..fa0614a92b 100644 --- a/src/interfaces/libpq/fe-protocol2.c +++ b/src/interfaces/libpq/fe-protocol2.c @@ -83,7 +83,7 @@ pqSetenvPoll(PGconn *conn) return PGRES_POLLING_OK; default: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid setenv state %c, probably indicative of memory corruption\n"), conn->setenv_state); goto error_return; @@ -380,7 +380,7 @@ pqSetenvPoll(PGconn *conn) } default: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid state %c, " "probably indicative of memory corruption\n"), conn->setenv_state); @@ -493,7 +493,7 @@ pqParseInput2(PGconn *conn) PGRES_COMMAND_OK); if (!conn->result) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory")); pqSaveErrorResult(conn); } @@ -528,7 +528,7 @@ pqParseInput2(PGconn *conn) PGRES_EMPTY_QUERY); if (!conn->result) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory")); pqSaveErrorResult(conn); } @@ -622,7 +622,7 @@ pqParseInput2(PGconn *conn) * never arrives from the server during protocol 2.0. */ default: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("unexpected response from server; first received character was \"%c\"\n"), id); /* build an error result holding the error message */ @@ -754,7 +754,7 @@ advance_and_error: if (!errmsg) errmsg = libpq_gettext("out of memory for query result"); - printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); + appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); /* * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can @@ -929,7 +929,7 @@ set_error_result: if (!errmsg) errmsg = libpq_gettext("out of memory for query result"); - printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); + appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); /* * XXX: if PQmakeEmptyPGresult() fails, there's probably not much we can @@ -1042,11 +1042,10 @@ pqGetErrorNotice2(PGconn *conn, bool isError) { pqClearAsyncResult(conn); /* redundant, but be safe */ conn->result = res; - resetPQExpBuffer(&conn->errorMessage); if (res && !PQExpBufferDataBroken(workBuf) && res->errMsg) appendPQExpBufferStr(&conn->errorMessage, res->errMsg); else - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory")); if (conn->xactStatus == PQTRANS_INTRANS) conn->xactStatus = PQTRANS_INERROR; @@ -1203,7 +1202,7 @@ pqGetCopyData2(PGconn *conn, char **buffer, int async) *buffer = (char *) malloc(msgLength + 1); if (*buffer == NULL) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return -2; } @@ -1349,7 +1348,7 @@ pqEndcopy2(PGconn *conn) if (conn->asyncStatus != PGASYNC_COPY_IN && conn->asyncStatus != PGASYNC_COPY_OUT) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("no COPY in progress\n")); return 1; } @@ -1367,7 +1366,6 @@ pqEndcopy2(PGconn *conn) /* Return to active duty */ conn->asyncStatus = PGASYNC_BUSY; - resetPQExpBuffer(&conn->errorMessage); /* Wait for the completion response */ result = PQgetResult(conn); @@ -1526,7 +1524,7 @@ pqFunctionCall2(PGconn *conn, Oid fnid, else { /* The backend violates the protocol. */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("protocol error: id=0x%x\n"), id); pqSaveErrorResult(conn); @@ -1558,7 +1556,7 @@ pqFunctionCall2(PGconn *conn, Oid fnid, return PQmakeEmptyPGresult(conn, status); default: /* The backend violates the protocol. */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("protocol error: id=0x%x\n"), id); pqSaveErrorResult(conn); diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index a4d6ee2674..13cf7bc8c4 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -202,7 +202,7 @@ pqParseInput3(PGconn *conn) PGRES_COMMAND_OK); if (!conn->result) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory")); pqSaveErrorResult(conn); } @@ -229,7 +229,7 @@ pqParseInput3(PGconn *conn) PGRES_EMPTY_QUERY); if (!conn->result) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory")); pqSaveErrorResult(conn); } @@ -246,7 +246,7 @@ pqParseInput3(PGconn *conn) PGRES_COMMAND_OK); if (!conn->result) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory")); pqSaveErrorResult(conn); } @@ -326,7 +326,7 @@ pqParseInput3(PGconn *conn) PGRES_COMMAND_OK); if (!conn->result) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory")); pqSaveErrorResult(conn); } @@ -361,7 +361,7 @@ pqParseInput3(PGconn *conn) else { /* Set up to report error at end of query */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("server sent data (\"D\" message) without prior row description(\"T\" message)\n")); pqSaveErrorResult(conn); /* Discard the unexpected message */ @@ -404,7 +404,7 @@ pqParseInput3(PGconn *conn) */ break; default: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("unexpected response from server; first received character was \"%c\"\n"), id); /* build an error result holding the error message */ @@ -425,7 +425,7 @@ pqParseInput3(PGconn *conn) else { /* Trouble --- report it */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("message contents do not agree with length in message type \"%c\"\n"), id); /* build an error result holding the error message */ @@ -445,7 +445,7 @@ pqParseInput3(PGconn *conn) static void handleSyncLoss(PGconn *conn, char id, int msgLength) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("lost synchronization with server: got message type \"%c\", length %d\n"), id, msgLength); /* build an error result holding the error message */ @@ -621,7 +621,7 @@ advance_and_error: if (!errmsg) errmsg = libpq_gettext("out of memory for query result"); - printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); + appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); pqSaveErrorResult(conn); /* @@ -721,7 +721,7 @@ advance_and_error: */ if (!errmsg) errmsg = libpq_gettext("out of memory"); - printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); + appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); pqSaveErrorResult(conn); /* @@ -848,7 +848,7 @@ set_error_result: if (!errmsg) errmsg = libpq_gettext("out of memory for query result"); - printfPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); + appendPQExpBuffer(&conn->errorMessage, "%s\n", errmsg); pqSaveErrorResult(conn); /* @@ -950,7 +950,7 @@ pqGetErrorNotice3(PGconn *conn, bool isError) pqClearAsyncResult(conn); /* redundant, but be safe */ conn->result = res; if (PQExpBufferDataBroken(workBuf)) - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory")); else appendPQExpBufferStr(&conn->errorMessage, workBuf.data); @@ -1695,7 +1695,7 @@ pqGetCopyData3(PGconn *conn, char **buffer, int async) *buffer = (char *) malloc(msgLength + 1); if (*buffer == NULL) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return -2; } @@ -1728,7 +1728,7 @@ pqGetline3(PGconn *conn, char *s, int maxlen) conn->asyncStatus != PGASYNC_COPY_BOTH) || conn->copy_is_binary) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("PQgetline: not doing text COPY OUT\n")); *s = '\0'; return EOF; @@ -1834,7 +1834,7 @@ pqEndcopy3(PGconn *conn) conn->asyncStatus != PGASYNC_COPY_OUT && conn->asyncStatus != PGASYNC_COPY_BOTH) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("no COPY in progress\n")); return 1; } @@ -1868,7 +1868,6 @@ pqEndcopy3(PGconn *conn) /* Return to active duty */ conn->asyncStatus = PGASYNC_BUSY; - resetPQExpBuffer(&conn->errorMessage); /* * Non blocking connections may have to abort at this point. If everyone @@ -2091,7 +2090,7 @@ pqFunctionCall3(PGconn *conn, Oid fnid, break; default: /* The backend violates the protocol. */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("protocol error: id=0x%x\n"), id); pqSaveErrorResult(conn); diff --git a/src/interfaces/libpq/fe-secure-common.c b/src/interfaces/libpq/fe-secure-common.c index 45d36359a5..44c69e1d19 100644 --- a/src/interfaces/libpq/fe-secure-common.c +++ b/src/interfaces/libpq/fe-secure-common.c @@ -94,7 +94,7 @@ pq_verify_peer_name_matches_certificate_name(PGconn *conn, if (!(host && host[0] != '\0')) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("host name must be specified\n")); return -1; } @@ -106,7 +106,7 @@ pq_verify_peer_name_matches_certificate_name(PGconn *conn, name = malloc(namelen + 1); if (name == NULL) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return -1; } @@ -120,7 +120,7 @@ pq_verify_peer_name_matches_certificate_name(PGconn *conn, if (namelen != strlen(name)) { free(name); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL certificate's name contains embedded null\n")); return -1; } @@ -167,7 +167,7 @@ pq_verify_peer_name_matches_certificate(PGconn *conn) /* Check that we have a hostname to compare with. */ if (!(host && host[0] != '\0')) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("host name must be specified for a verified SSL connection\n")); return false; } @@ -184,7 +184,7 @@ pq_verify_peer_name_matches_certificate(PGconn *conn) */ if (names_examined > 1) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_ngettext("server certificate for \"%s\" (and %d other name) does not match host name\"%s\"\n", "server certificate for \"%s\" (and %d other names) does not match host name\"%s\"\n", names_examined - 1), @@ -192,13 +192,13 @@ pq_verify_peer_name_matches_certificate(PGconn *conn) } else if (names_examined == 1) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("server certificate for \"%s\" does not match host name \"%s\"\n"), first_name, host); } else { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not get server's host name from server certificate\n")); } } diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c index 8c0ba69b7c..27e29a4779 100644 --- a/src/interfaces/libpq/fe-secure-gssapi.c +++ b/src/interfaces/libpq/fe-secure-gssapi.c @@ -78,7 +78,7 @@ * * On success, returns the number of data bytes consumed (possibly less than * len). On failure, returns -1 with errno set appropriately. If the errno - * indicates a non-retryable error, a message is put into conn->errorMessage. + * indicates a non-retryable error, a message is added to conn->errorMessage. * For retryable errors, caller should call again (passing the same data) * once the socket is ready. */ @@ -106,7 +106,7 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len) */ if (len < PqGSSSendConsumed) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, "GSSAPI caller failed to retransmit all data needing to be retried\n"); errno = EINVAL; return -1; @@ -205,7 +205,7 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len) if (conf_state == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("outgoing GSSAPI message would not use confidentiality\n")); errno = EIO; /* for lack of a better idea */ goto cleanup; @@ -213,7 +213,7 @@ pg_GSS_write(PGconn *conn, const void *ptr, size_t len) if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32)) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("client tried to send oversize GSSAPI packet (%zu > %zu)\n"), (size_t) output.length, PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32)); @@ -258,7 +258,7 @@ cleanup: * * Returns the number of data bytes read, or on failure, returns -1 * with errno set appropriately. If the errno indicates a non-retryable - * error, a message is put into conn->errorMessage. For retryable errors, + * error, a message is added to conn->errorMessage. For retryable errors, * caller should call again once the socket is ready. */ ssize_t @@ -350,7 +350,7 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len) if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32)) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("oversize GSSAPI packet sent by the server (%zu > %zu)\n"), (size_t) input.length, PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32)); @@ -399,7 +399,7 @@ pg_GSS_read(PGconn *conn, void *ptr, size_t len) if (conf_state == 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("incoming GSSAPI message did not use confidentiality\n")); ret = -1; errno = EIO; /* for lack of a better idea */ @@ -500,7 +500,7 @@ pqsecure_open_gss(PGconn *conn) PqGSSResultBuffer = malloc(PQ_GSS_RECV_BUFFER_SIZE); if (!PqGSSSendBuffer || !PqGSSRecvBuffer || !PqGSSResultBuffer) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return PGRES_POLLING_FAILED; } @@ -578,7 +578,7 @@ pqsecure_open_gss(PGconn *conn) PqGSSRecvLength += ret; - printfPQExpBuffer(&conn->errorMessage, "%s\n", PqGSSRecvBuffer + 1); + appendPQExpBuffer(&conn->errorMessage, "%s\n", PqGSSRecvBuffer + 1); return PGRES_POLLING_FAILED; } @@ -592,7 +592,7 @@ pqsecure_open_gss(PGconn *conn) input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer); if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32)) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("oversize GSSAPI packet sent by the server (%zu > %zu)\n"), (size_t) input.length, PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32)); diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c index d63e4bb279..539e2053b7 100644 --- a/src/interfaces/libpq/fe-secure-openssl.c +++ b/src/interfaces/libpq/fe-secure-openssl.c @@ -181,7 +181,7 @@ rloop: if (n < 0) { /* Not supposed to happen, so we don't translate the msg */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, "SSL_read failed but did not provide error information\n"); /* assume the connection is broken */ result_errno = ECONNRESET; @@ -205,19 +205,19 @@ rloop: result_errno = SOCK_ERRNO; if (result_errno == EPIPE || result_errno == ECONNRESET) - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("server closed the connection unexpectedly\n" "\tThis probably means the server terminated abnormally\n" "\tbefore or while processing the request.\n")); else - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL SYSCALL error: %s\n"), SOCK_STRERROR(result_errno, sebuf, sizeof(sebuf))); } else { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL SYSCALL error: EOF detected\n")); /* assume the connection is broken */ result_errno = ECONNRESET; @@ -228,7 +228,7 @@ rloop: { char *errm = SSLerrmessage(ecode); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL error: %s\n"), errm); SSLerrfree(errm); /* assume the connection is broken */ @@ -243,13 +243,13 @@ rloop: * a clean connection closure, so we should not report it as a * server crash. */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL connection has been closed unexpectedly\n")); result_errno = ECONNRESET; n = -1; break; default: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("unrecognized SSL error code: %d\n"), err); /* assume the connection is broken */ @@ -290,7 +290,7 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len) if (n < 0) { /* Not supposed to happen, so we don't translate the msg */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, "SSL_write failed but did not provide error information\n"); /* assume the connection is broken */ result_errno = ECONNRESET; @@ -312,19 +312,19 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len) { result_errno = SOCK_ERRNO; if (result_errno == EPIPE || result_errno == ECONNRESET) - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("server closed the connection unexpectedly\n" "\tThis probably means the server terminated abnormally\n" "\tbefore or while processing the request.\n")); else - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL SYSCALL error: %s\n"), SOCK_STRERROR(result_errno, sebuf, sizeof(sebuf))); } else { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL SYSCALL error: EOF detected\n")); /* assume the connection is broken */ result_errno = ECONNRESET; @@ -335,7 +335,7 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len) { char *errm = SSLerrmessage(ecode); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL error: %s\n"), errm); SSLerrfree(errm); /* assume the connection is broken */ @@ -350,13 +350,13 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len) * a clean connection closure, so we should not report it as a * server crash. */ - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL connection has been closed unexpectedly\n")); result_errno = ECONNRESET; n = -1; break; default: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("unrecognized SSL error code: %d\n"), err); /* assume the connection is broken */ @@ -396,7 +396,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len) if (!OBJ_find_sigid_algs(X509_get_signature_nid(peer_cert), &algo_nid, NULL)) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not determine server certificate signature algorithm\n")); return NULL; } @@ -417,7 +417,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len) algo_type = EVP_get_digestbynid(algo_nid); if (algo_type == NULL) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not find digest for NID %s\n"), OBJ_nid2sn(algo_nid)); return NULL; @@ -427,7 +427,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len) if (!X509_digest(peer_cert, algo_type, hash, &hash_size)) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not generate peer certificate hash\n")); return NULL; } @@ -436,7 +436,7 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len) cert_hash = malloc(hash_size); if (cert_hash == NULL) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return NULL; } @@ -484,7 +484,7 @@ openssl_verify_peer_name_matches_certificate_name(PGconn *conn, ASN1_STRING *nam /* Should not happen... */ if (name_entry == NULL) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL certificate's name entry is missing\n")); return -1; } @@ -811,7 +811,7 @@ initialize_SSL(PGconn *conn) { char *err = SSLerrmessage(ERR_get_error()); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not create SSL context: %s\n"), err); SSLerrfree(err); @@ -850,7 +850,7 @@ initialize_SSL(PGconn *conn) if (ssl_min_ver == -1) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid value \"%s\" for minimum SSL protocol version\n"), conn->ssl_min_protocol_version); SSL_CTX_free(SSL_context); @@ -861,7 +861,7 @@ initialize_SSL(PGconn *conn) { char *err = SSLerrmessage(ERR_get_error()); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not set minimum SSL protocol version: %s\n"), err); SSLerrfree(err); @@ -879,7 +879,7 @@ initialize_SSL(PGconn *conn) if (ssl_max_ver == -1) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("invalid value \"%s\" for maximum SSL protocol version\n"), conn->ssl_max_protocol_version); SSL_CTX_free(SSL_context); @@ -890,7 +890,7 @@ initialize_SSL(PGconn *conn) { char *err = SSLerrmessage(ERR_get_error()); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not set maximum SSL protocol version: %s\n"), err); SSLerrfree(err); @@ -926,7 +926,7 @@ initialize_SSL(PGconn *conn) { char *err = SSLerrmessage(ERR_get_error()); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not read root certificate file \"%s\": %s\n"), fnbuf, err); SSLerrfree(err); @@ -970,11 +970,11 @@ initialize_SSL(PGconn *conn) * that it seems worth having a specialized error message for it. */ if (fnbuf[0] == '\0') - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not get home directory to locate root certificate file\n" "Either provide the file or change sslmode to disable server certificateverification.\n")); else - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("root certificate file \"%s\" does not exist\n" "Either provide the file or change sslmode to disable server certificateverification.\n"), fnbuf); SSL_CTX_free(SSL_context); @@ -1005,7 +1005,7 @@ initialize_SSL(PGconn *conn) */ if (errno != ENOENT && errno != ENOTDIR) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not open certificate file \"%s\": %s\n"), fnbuf, strerror_r(errno, sebuf, sizeof(sebuf))); SSL_CTX_free(SSL_context); @@ -1024,7 +1024,7 @@ initialize_SSL(PGconn *conn) { char *err = SSLerrmessage(ERR_get_error()); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not read certificate file \"%s\": %s\n"), fnbuf, err); SSLerrfree(err); @@ -1049,7 +1049,7 @@ initialize_SSL(PGconn *conn) { char *err = SSLerrmessage(ERR_get_error()); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not establish SSL connection: %s\n"), err); SSLerrfree(err); @@ -1087,7 +1087,7 @@ initialize_SSL(PGconn *conn) if (engine_str == NULL) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); return -1; } @@ -1103,7 +1103,7 @@ initialize_SSL(PGconn *conn) { char *err = SSLerrmessage(ERR_get_error()); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not load SSL engine \"%s\": %s\n"), engine_str, err); SSLerrfree(err); @@ -1115,7 +1115,7 @@ initialize_SSL(PGconn *conn) { char *err = SSLerrmessage(ERR_get_error()); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not initialize SSL engine \"%s\": %s\n"), engine_str, err); SSLerrfree(err); @@ -1131,7 +1131,7 @@ initialize_SSL(PGconn *conn) { char *err = SSLerrmessage(ERR_get_error()); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"), engine_colon, engine_str, err); SSLerrfree(err); @@ -1145,7 +1145,7 @@ initialize_SSL(PGconn *conn) { char *err = SSLerrmessage(ERR_get_error()); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"), engine_colon, engine_str, err); SSLerrfree(err); @@ -1182,7 +1182,7 @@ initialize_SSL(PGconn *conn) if (stat(fnbuf, &buf) != 0) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("certificate present, but not private key file \"%s\"\n"), fnbuf); return -1; @@ -1190,7 +1190,7 @@ initialize_SSL(PGconn *conn) #ifndef WIN32 if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO)) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw(0600) or less\n"), fnbuf); return -1; @@ -1215,7 +1215,7 @@ initialize_SSL(PGconn *conn) */ if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_ASN1) != 1) { - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not load private key file \"%s\": %s\n"), fnbuf, err); SSLerrfree(err); @@ -1233,7 +1233,7 @@ initialize_SSL(PGconn *conn) { char *err = SSLerrmessage(ERR_get_error()); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("certificate does not match private key file \"%s\": %s\n"), fnbuf, err); SSLerrfree(err); @@ -1287,11 +1287,11 @@ open_client_SSL(PGconn *conn) char sebuf[PG_STRERROR_R_BUFLEN]; if (r == -1) - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL SYSCALL error: %s\n"), SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); else - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL SYSCALL error: EOF detected\n")); pgtls_close(conn); return PGRES_POLLING_FAILED; @@ -1300,7 +1300,7 @@ open_client_SSL(PGconn *conn) { char *err = SSLerrmessage(ecode); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("SSL error: %s\n"), err); SSLerrfree(err); @@ -1350,7 +1350,7 @@ open_client_SSL(PGconn *conn) } default: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("unrecognized SSL error code: %d\n"), err); pgtls_close(conn); @@ -1369,7 +1369,7 @@ open_client_SSL(PGconn *conn) { char *err = SSLerrmessage(ERR_get_error()); - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("certificate could not be obtained: %s\n"), err); SSLerrfree(err); diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c index 373c59cb0d..15a37fad30 100644 --- a/src/interfaces/libpq/fe-secure.c +++ b/src/interfaces/libpq/fe-secure.c @@ -205,8 +205,8 @@ pqsecure_close(PGconn *conn) /* * Read data from a secure connection. * - * On failure, this function is responsible for putting a suitable message - * into conn->errorMessage. The caller must still inspect errno, but only + * On failure, this function is responsible for appending a suitable message + * to conn->errorMessage. The caller must still inspect errno, but only * to determine whether to continue/retry after error. */ ssize_t @@ -263,14 +263,14 @@ pqsecure_raw_read(PGconn *conn, void *ptr, size_t len) case EPIPE: case ECONNRESET: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("server closed the connection unexpectedly\n" "\tThis probably means the server terminated abnormally\n" "\tbefore or while processing the request.\n")); break; default: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not receive data from server: %s\n"), SOCK_STRERROR(result_errno, sebuf, sizeof(sebuf))); @@ -287,8 +287,8 @@ pqsecure_raw_read(PGconn *conn, void *ptr, size_t len) /* * Write data to a secure connection. * - * On failure, this function is responsible for putting a suitable message - * into conn->errorMessage. The caller must still inspect errno, but only + * On failure, this function is responsible for appending a suitable message + * to conn->errorMessage. The caller must still inspect errno, but only * to determine whether to continue/retry after error. */ ssize_t @@ -376,14 +376,14 @@ retry_masked: /* FALL THRU */ case ECONNRESET: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("server closed the connection unexpectedly\n" "\tThis probably means the server terminated abnormally\n" "\tbefore or while processing the request.\n")); break; default: - printfPQExpBuffer(&conn->errorMessage, + appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not send data to server: %s\n"), SOCK_STRERROR(result_errno, sebuf, sizeof(sebuf))); diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index e1018adb9e..4db498369c 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -522,7 +522,11 @@ struct pg_conn * connection */ #endif - /* Buffer for current error message */ + /* + * Buffer for current error message. This is cleared at the start of any + * connection attempt or query cycle; after that, all code should append + * messages to it, never overwrite. + */ PQExpBufferData errorMessage; /* expansible string */ /* Buffer for receiving various parts of messages */ @@ -600,7 +604,6 @@ extern pgthreadlock_t pg_g_threadlock; /* === in fe-exec.c === */ extern void pqSetResultError(PGresult *res, const char *msg); -extern void pqCatenateResultError(PGresult *res, const char *msg); extern void *pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary); extern char *pqResultStrdup(PGresult *res, const char *str); extern void pqClearAsyncResult(PGconn *conn); @@ -612,6 +615,7 @@ extern void pqSaveMessageField(PGresult *res, char code, extern void pqSaveParameterStatus(PGconn *conn, const char *name, const char *value); extern int pqRowProcessor(PGconn *conn, const char **errmsgp); +extern int PQsendQueryContinue(PGconn *conn, const char *query); /* === in fe-protocol2.c === */ @@ -708,7 +712,7 @@ extern void pgtls_init_library(bool do_ssl, int do_crypto); * The conn parameter is only used to be able to pass back an error * message - no connection-local setup is made here. * - * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage). + * Returns 0 if OK, -1 on failure (adding a message to conn->errorMessage). */ extern int pgtls_init(PGconn *conn); @@ -725,8 +729,8 @@ extern void pgtls_close(PGconn *conn); /* * Read data from a secure connection. * - * On failure, this function is responsible for putting a suitable message - * into conn->errorMessage. The caller must still inspect errno, but only + * On failure, this function is responsible for appending a suitable message + * to conn->errorMessage. The caller must still inspect errno, but only * to determine whether to continue/retry after error. */ extern ssize_t pgtls_read(PGconn *conn, void *ptr, size_t len); @@ -739,8 +743,8 @@ extern bool pgtls_read_pending(PGconn *conn); /* * Write data to a secure connection. * - * On failure, this function is responsible for putting a suitable message - * into conn->errorMessage. The caller must still inspect errno, but only + * On failure, this function is responsible for appending a suitable message + * to conn->errorMessage. The caller must still inspect errno, but only * to determine whether to continue/retry after error. */ extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len); diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index 11dc98ee0a..2b501166b8 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -3460,7 +3460,7 @@ foreach my $db (sort keys %create_sql) command_fails_like( [ 'pg_dump', '-p', "$port", 'qqq' ], - qr/\Qpg_dump: error: connection to database "qqq" failed: FATAL: database "qqq" does not exist\E/, + qr/pg_dump: error: connection to database "qqq" failed: could not connect to .*: FATAL: database "qqq" does not exist/, 'connecting to a non-existent database'); ######################################### diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 84e2868104..5b0770d8cc 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -1669,15 +1669,17 @@ getHostaddr(PGconn *conn, char *host_addr, int host_addr_len) } /* ---------- - * connectFailureMessage - - * create a friendly error message on connection failure. + * emitCouldNotConnect - + * Speculatively append "could not connect to ...: " to conn->errorMessage + * once we've identified the current connection target address. This ensures + * that any subsequent error message will be properly attributed to the + * server we couldn't connect to. conn->raddr must be valid, and the result + * of getHostaddr() must be supplied. * ---------- */ static void -connectFailureMessage(PGconn *conn, int errorno) +emitCouldNotConnect(PGconn *conn, const char *host_addr) { - char sebuf[PG_STRERROR_R_BUFLEN]; - #ifdef HAVE_UNIX_SOCKETS if (IS_AF_UNIX(conn->raddr.addr.ss_family)) { @@ -1688,25 +1690,15 @@ connectFailureMessage(PGconn *conn, int errorno) service, sizeof(service), NI_NUMERICSERV); appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not connect to server: %s\n" - "\tIs the server running locally and accepting\n" - "\tconnections on Unix domain socket \"%s\"?\n"), - SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)), + libpq_gettext("could not connect to socket \"%s\": "), service); } else #endif /* HAVE_UNIX_SOCKETS */ { - char host_addr[NI_MAXHOST]; const char *displayed_host; const char *displayed_port; - /* - * Optionally display the network address with the hostname. This is - * useful to distinguish between IPv4 and IPv6 connections. - */ - getHostaddr(conn, host_addr, NI_MAXHOST); - /* To which host and port were we actually connecting? */ if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS) displayed_host = conn->connhost[conn->whichhost].hostaddr; @@ -1722,26 +1714,46 @@ connectFailureMessage(PGconn *conn, int errorno) * looked-up IP address. */ if (conn->connhost[conn->whichhost].type != CHT_HOST_ADDRESS && - strlen(host_addr) > 0 && + host_addr[0] && strcmp(displayed_host, host_addr) != 0) appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not connect to server: %s\n" - "\tIs the server running on host \"%s\" (%s) and accepting\n" - "\tTCP/IP connections on port %s?\n"), - SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)), + libpq_gettext("could not connect to host \"%s\" (%s), port %s: "), displayed_host, host_addr, displayed_port); else appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not connect to server: %s\n" - "\tIs the server running on host \"%s\" and accepting\n" - "\tTCP/IP connections on port %s?\n"), - SOCK_STRERROR(errorno, sebuf, sizeof(sebuf)), + libpq_gettext("could not connect to host \"%s\", port %s: "), displayed_host, displayed_port); } } +/* ---------- + * connectFailureMessage - + * create a friendly error message on connection failure, + * using the given errno value. Use this for error cases that + * imply that there's no server there. + * ---------- + */ +static void +connectFailureMessage(PGconn *conn, int errorno) +{ + char sebuf[PG_STRERROR_R_BUFLEN]; + + appendPQExpBuffer(&conn->errorMessage, + "%s\n", + SOCK_STRERROR(errorno, sebuf, sizeof(sebuf))); + +#ifdef HAVE_UNIX_SOCKETS + if (IS_AF_UNIX(conn->raddr.addr.ss_family)) + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("\tIs the server running locally and accepting connections on that socket?\n")); + else +#endif + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("\tIs the server running on that host and accepting TCP/IP connections?\n")); +} + /* * Should we use keepalives? Returns 1 if yes, 0 if no, and -1 if * conn->keepalives is set to a value which is not parseable as an @@ -2476,30 +2488,30 @@ keep_going: /* We will come back to here until there is goto keep_going; } - /* Remember current address for possible error msg */ + /* Remember current address for possible use later */ memcpy(&conn->raddr.addr, addr_cur->ai_addr, addr_cur->ai_addrlen); conn->raddr.salen = addr_cur->ai_addrlen; - /* set connip */ + /* + * Set connip, too. Note we purposely ignore strdup + * failure; not a big problem if it fails. + */ if (conn->connip != NULL) { free(conn->connip); conn->connip = NULL; } - getHostaddr(conn, host_addr, NI_MAXHOST); - if (strlen(host_addr) > 0) + if (host_addr[0]) conn->connip = strdup(host_addr); - /* - * purposely ignore strdup failure; not a big problem if - * it fails anyway. - */ - + /* Try to create the socket */ conn->sock = socket(addr_cur->ai_family, SOCK_STREAM, 0); if (conn->sock == PGINVALID_SOCKET) { + int errorno = SOCK_ERRNO; + /* * Silently ignore socket() failure if we have more * addresses to try; this reduces useless chatter in @@ -2512,12 +2524,20 @@ keep_going: /* We will come back to here until there is conn->try_next_addr = true; goto keep_going; } + emitCouldNotConnect(conn, host_addr); appendPQExpBuffer(&conn->errorMessage, libpq_gettext("could not create socket: %s\n"), - SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf))); + SOCK_STRERROR(errorno, sebuf, sizeof(sebuf))); goto error_return; } + /* + * Once we've identified a target address, all errors + * except the preceding socket()-failure case should be + * prefixed with "could not connect to <target>: ". + */ + emitCouldNotConnect(conn, host_addr); + /* * Select socket options: no delay of outgoing data for * TCP sockets, nonblock mode, close-on-exec. Try the @@ -3608,9 +3628,6 @@ keep_going: /* We will come back to here until there is } case CONNECTION_CHECK_WRITABLE: { - const char *displayed_host; - const char *displayed_port; - conn->status = CONNECTION_OK; if (!PQconsumeInput(conn)) goto error_return; @@ -3634,19 +3651,8 @@ keep_going: /* We will come back to here until there is PQclear(res); /* Append error report to conn->errorMessage. */ - if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS) - displayed_host = conn->connhost[conn->whichhost].hostaddr; - else - displayed_host = conn->connhost[conn->whichhost].host; - displayed_port = conn->connhost[conn->whichhost].port; - if (displayed_port == NULL || displayed_port[0] == '\0') - displayed_port = DEF_PGPORT_STR; - - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("could not make a writable " - "connection to server " - "\"%s:%s\"\n"), - displayed_host, displayed_port); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("session is read-only\n")); /* Close connection politely. */ conn->status = CONNECTION_OK; @@ -3679,17 +3685,8 @@ keep_going: /* We will come back to here until there is PQclear(res); /* Append error report to conn->errorMessage. */ - if (conn->connhost[conn->whichhost].type == CHT_HOST_ADDRESS) - displayed_host = conn->connhost[conn->whichhost].hostaddr; - else - displayed_host = conn->connhost[conn->whichhost].host; - displayed_port = conn->connhost[conn->whichhost].port; - if (displayed_port == NULL || displayed_port[0] == '\0') - displayed_port = DEF_PGPORT_STR; - appendPQExpBuffer(&conn->errorMessage, - libpq_gettext("test \"SHOW transaction_read_only\" failed " - "on server \"%s:%s\"\n"), - displayed_host, displayed_port); + appendPQExpBufferStr(&conn->errorMessage, + libpq_gettext("test \"SHOW transaction_read_only\" failed\n")); /* Close connection politely. */ conn->status = CONNECTION_OK; diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 5b0770d8cc..8391e2c46a 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -3315,6 +3315,20 @@ keep_going: /* We will come back to here until there is /* OK, we read the message; mark data consumed */ conn->inStart = conn->inCursor; + /* + * If error is "cannot connect now", try the next host if + * any (but we don't want to consider additional addresses + * for this host, nor is there much point in changing SSL + * or GSS mode). This is helpful when dealing with + * standby servers that might not be in hot-standby state. + */ + if (strcmp(conn->last_sqlstate, + ERRCODE_CANNOT_CONNECT_NOW) == 0) + { + conn->try_next_host = true; + goto keep_going; + } + /* Check to see if we should mention pgpassfile */ pgpassfileWarning(conn); diff --git a/src/interfaces/ecpg/test/expected/connect-test5.stderr b/src/interfaces/ecpg/test/expected/connect-test5.stderr index a54df175fb..4dbf2c0fc4 100644 --- a/src/interfaces/ecpg/test/expected/connect-test5.stderr +++ b/src/interfaces/ecpg/test/expected/connect-test5.stderr @@ -36,7 +36,7 @@ [NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: ECPGconnect: opening database <DEFAULT> on <DEFAULT> port <DEFAULT> for user regress_ecpg_user2 [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ECPGconnect: could not open database: FATAL: database "regress_ecpg_user2" does not exist +[NO_PID]: ECPGconnect: could not open database: could not connect: FATAL: database "regress_ecpg_user2" does not exist [NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: ecpg_finish: connection main closed @@ -73,7 +73,7 @@ [NO_PID]: sqlca: code: -220, state: 08003 [NO_PID]: ECPGconnect: opening database <DEFAULT> on <DEFAULT> port <DEFAULT> for user regress_ecpg_user2 [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ECPGconnect: could not open database: FATAL: database "regress_ecpg_user2" does not exist +[NO_PID]: ECPGconnect: could not open database: could not connect: FATAL: database "regress_ecpg_user2" does not exist [NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: ecpg_finish: connection main closed diff --git a/src/interfaces/ecpg/test/pg_regress_ecpg.c b/src/interfaces/ecpg/test/pg_regress_ecpg.c index 31dd507ea0..e3e0479ce3 100644 --- a/src/interfaces/ecpg/test/pg_regress_ecpg.c +++ b/src/interfaces/ecpg/test/pg_regress_ecpg.c @@ -23,13 +23,14 @@ #include "lib/stringinfo.h" +/* + * Create a filtered copy of sourcefile, removing any path + * appearing in #line directives; for example, replace + * #line x "./../bla/foo.h" with #line x "foo.h" + */ static void -ecpg_filter(const char *sourcefile, const char *outfile) +ecpg_filter_source(const char *sourcefile, const char *outfile) { - /* - * Create a filtered copy of sourcefile, replacing #line x - * "./../bla/foo.h" with #line x "foo.h" - */ FILE *s, *t; StringInfoData linebuf; @@ -76,6 +77,66 @@ ecpg_filter(const char *sourcefile, const char *outfile) fclose(t); } +/* + * Remove the details of "could not connect to ...:" error messages + * in a test result file, since the target socket path and/or port + * can vary. Rewrite the result file in-place. + * + * At some point it might be interesting to unify this with + * ecpg_filter_source, but building a general pattern matcher + * is no fun, nor does it seem desirable to introduce a + * dependency on an external one. + */ +static void +ecpg_filter_stderr(const char *resultfile, const char *tmpfile) +{ + FILE *s, + *t; + StringInfoData linebuf; + + s = fopen(resultfile, "r"); + if (!s) + { + fprintf(stderr, "Could not open file %s for reading\n", resultfile); + exit(2); + } + t = fopen(tmpfile, "w"); + if (!t) + { + fprintf(stderr, "Could not open file %s for writing\n", tmpfile); + exit(2); + } + + initStringInfo(&linebuf); + + while (pg_get_line_buf(s, &linebuf)) + { + char *p1 = strstr(linebuf.data, "could not connect to "); + + if (p1) + { + char *p2 = strchr(p1, ':'); + + if (p2) + { + memmove(p1 + 17, p2, strlen(p2) + 1); + /* we don't bother to fix up linebuf.len */ + } + } + fputs(linebuf.data, t); + } + + pfree(linebuf.data); + fclose(s); + fclose(t); + if (rename(tmpfile, resultfile) != 0) + { + fprintf(stderr, "Could not overwrite file %s with %s\n", + resultfile, tmpfile); + exit(2); + } +} + /* * start an ecpg test process for specified file (including redirection), * and return process ID @@ -139,7 +200,7 @@ ecpg_start_test(const char *testname, add_stringlist_item(expectfiles, expectfile_source); add_stringlist_item(tags, "source"); - ecpg_filter(insource, outfile_source); + ecpg_filter_source(insource, outfile_source); snprintf(cmd, sizeof(cmd), "\"%s\" >\"%s\" 2>\"%s\"", @@ -167,6 +228,21 @@ ecpg_start_test(const char *testname, return pid; } +static void +ecpg_postprocess_result(const char *filename) +{ + int nlen = strlen(filename); + + /* Only stderr files require filtering, at the moment */ + if (nlen > 7 && strcmp(filename + nlen - 7, ".stderr") == 0) + { + char *tmpfile = psprintf("%s.tmp", filename); + + ecpg_filter_stderr(filename, tmpfile); + pfree(tmpfile); + } +} + static void ecpg_init(int argc, char *argv[]) { @@ -176,5 +252,8 @@ ecpg_init(int argc, char *argv[]) int main(int argc, char *argv[]) { - return regression_main(argc, argv, ecpg_init, ecpg_start_test); + return regression_main(argc, argv, + ecpg_init, + ecpg_start_test, + ecpg_postprocess_result); } diff --git a/src/test/isolation/isolation_main.c b/src/test/isolation/isolation_main.c index 50893aa3ab..eff9c4f2df 100644 --- a/src/test/isolation/isolation_main.c +++ b/src/test/isolation/isolation_main.c @@ -145,5 +145,8 @@ isolation_init(int argc, char **argv) int main(int argc, char *argv[]) { - return regression_main(argc, argv, isolation_init, isolation_start_test); + return regression_main(argc, argv, + isolation_init, + isolation_start_test, + NULL /* no postfunc needed */ ); } diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c index 5cfb4c4a49..b284cc88c4 100644 --- a/src/test/regress/pg_regress.c +++ b/src/test/regress/pg_regress.c @@ -731,7 +731,7 @@ static void initialize_environment(void) { /* - * Set default application_name. (The test_function may choose to + * Set default application_name. (The test_start_function may choose to * override this, but if it doesn't, we have something useful in place.) */ setenv("PGAPPNAME", "pg_regress", 1); @@ -1616,7 +1616,8 @@ log_child_failure(int exitstatus) * Run all the tests specified in one schedule file */ static void -run_schedule(const char *schedule, test_function tfunc) +run_schedule(const char *schedule, test_start_function startfunc, + postprocess_result_function postfunc) { #define MAX_PARALLEL_TESTS 100 char *tests[MAX_PARALLEL_TESTS]; @@ -1730,7 +1731,7 @@ run_schedule(const char *schedule, test_function tfunc) if (num_tests == 1) { status(_("test %-28s ... "), tests[0]); - pids[0] = (tfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]); + pids[0] = (startfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]); INSTR_TIME_SET_CURRENT(starttimes[0]); wait_for_tests(pids, statuses, stoptimes, NULL, 1); /* status line is finished below */ @@ -1756,7 +1757,7 @@ run_schedule(const char *schedule, test_function tfunc) tests + oldest, i - oldest); oldest = i; } - pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]); + pids[i] = (startfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]); INSTR_TIME_SET_CURRENT(starttimes[i]); } wait_for_tests(pids + oldest, statuses + oldest, @@ -1769,7 +1770,7 @@ run_schedule(const char *schedule, test_function tfunc) status(_("parallel group (%d tests): "), num_tests); for (i = 0; i < num_tests; i++) { - pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]); + pids[i] = (startfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]); INSTR_TIME_SET_CURRENT(starttimes[i]); } wait_for_tests(pids, statuses, stoptimes, tests, num_tests); @@ -1801,6 +1802,8 @@ run_schedule(const char *schedule, test_function tfunc) { bool newdiff; + if (postfunc) + (*postfunc) (rl->str); newdiff = results_differ(tests[i], rl->str, el->str); if (newdiff && tl) { @@ -1867,7 +1870,8 @@ run_schedule(const char *schedule, test_function tfunc) * Run a single test */ static void -run_single_test(const char *test, test_function tfunc) +run_single_test(const char *test, test_start_function startfunc, + postprocess_result_function postfunc) { PID_TYPE pid; instr_time starttime; @@ -1882,7 +1886,7 @@ run_single_test(const char *test, test_function tfunc) bool differ = false; status(_("test %-28s ... "), test); - pid = (tfunc) (test, &resultfiles, &expectfiles, &tags); + pid = (startfunc) (test, &resultfiles, &expectfiles, &tags); INSTR_TIME_SET_CURRENT(starttime); wait_for_tests(&pid, &exit_status, &stoptime, NULL, 1); @@ -1900,6 +1904,8 @@ run_single_test(const char *test, test_function tfunc) { bool newdiff; + if (postfunc) + (*postfunc) (rl->str); newdiff = results_differ(test, rl->str, el->str); if (newdiff && tl) { @@ -2083,7 +2089,10 @@ help(void) } int -regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc) +regression_main(int argc, char *argv[], + init_function ifunc, + test_start_function startfunc, + postprocess_result_function postfunc) { static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, @@ -2554,12 +2563,12 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc for (sl = schedulelist; sl != NULL; sl = sl->next) { - run_schedule(sl->str, tfunc); + run_schedule(sl->str, startfunc, postfunc); } for (sl = extra_tests; sl != NULL; sl = sl->next) { - run_single_test(sl->str, tfunc); + run_single_test(sl->str, startfunc, postfunc); } /* diff --git a/src/test/regress/pg_regress.h b/src/test/regress/pg_regress.h index d04f7721aa..c6d015c840 100644 --- a/src/test/regress/pg_regress.h +++ b/src/test/regress/pg_regress.h @@ -27,12 +27,23 @@ typedef struct _stringlist struct _stringlist *next; } _stringlist; -typedef PID_TYPE(*test_function) (const char *, - _stringlist **, - _stringlist **, - _stringlist **); +/* + * Callback function signatures for test programs that use regression_main() + */ + +/* Initialize at program start */ typedef void (*init_function) (int argc, char **argv); +/* Launch one test case */ +typedef PID_TYPE(*test_start_function) (const char *testname, + _stringlist **resultfiles, + _stringlist **expectfiles, + _stringlist **tags); + +/* Postprocess one result file (optional) */ +typedef void (*postprocess_result_function) (const char *filename); + + extern char *bindir; extern char *libdir; extern char *datadir; @@ -48,7 +59,10 @@ extern const char *basic_diff_opts; extern const char *pretty_diff_opts; int regression_main(int argc, char *argv[], - init_function ifunc, test_function tfunc); + init_function ifunc, + test_start_function startfunc, + postprocess_result_function postfunc); + void add_stringlist_item(_stringlist **listhead, const char *str); PID_TYPE spawn_process(const char *cmdline); void replace_string(struct StringInfoData *string, diff --git a/src/test/regress/pg_regress_main.c b/src/test/regress/pg_regress_main.c index a218bae247..8dc4941c24 100644 --- a/src/test/regress/pg_regress_main.c +++ b/src/test/regress/pg_regress_main.c @@ -119,5 +119,8 @@ psql_init(int argc, char **argv) int main(int argc, char *argv[]) { - return regression_main(argc, argv, psql_init, psql_start_test); + return regression_main(argc, argv, + psql_init, + psql_start_test, + NULL /* no postfunc needed */ ); }
В списке pgsql-hackers по дате отправления: