diff options
| author | Tom Lane | 2003-04-25 19:45:10 +0000 |
|---|---|---|
| committer | Tom Lane | 2003-04-25 19:45:10 +0000 |
| commit | 9cbaf7217747d6b5c88ba9b500a37b8372f185c9 (patch) | |
| tree | 18569f5cba46f81d9097c9ea21c29f4a0e055936 /src/interfaces/libpq | |
| parent | a2190c9eb6afc65fddb80667d48024428368a9b6 (diff) | |
In the continuing saga of FE/BE protocol revisions, add reporting of
initial values and runtime changes in selected parameters. This gets
rid of the need for an initial 'select pg_client_encoding()' query in
libpq, bringing us back to one message transmitted in each direction
for a standard connection startup. To allow server version to be sent
using the same GUC mechanism that handles other parameters, invent the
concept of a never-settable GUC parameter: you can 'show server_version'
but it's not settable by any GUC input source. Create 'lc_collate' and
'lc_ctype' never-settable parameters so that people can find out these
settings without need for pg_controldata. (These side ideas were all
discussed some time ago in pgsql-hackers, but not yet implemented.)
Diffstat (limited to 'src/interfaces/libpq')
| -rw-r--r-- | src/interfaces/libpq/fe-connect.c | 289 | ||||
| -rw-r--r-- | src/interfaces/libpq/fe-exec.c | 53 | ||||
| -rw-r--r-- | src/interfaces/libpq/libpq-int.h | 15 |
3 files changed, 62 insertions, 295 deletions
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index a31f34d7a98..e2df4de859a 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.236 2003/04/25 01:24:00 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.237 2003/04/25 19:45:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -174,8 +174,6 @@ static const struct EnvironmentOptions static int connectDBStart(PGconn *conn); static int connectDBComplete(PGconn *conn); -static bool PQsetenvStart(PGconn *conn); -static PostgresPollingStatusType PQsetenvPoll(PGconn *conn); static PGconn *makeEmptyPGconn(void); static void freePGconn(PGconn *conn); static void closePGconn(PGconn *conn); @@ -1207,10 +1205,6 @@ PQconnectPoll(PGconn *conn) case CONNECTION_MADE: break; - case CONNECTION_SETENV: - /* We allow PQsetenvPoll to decide whether to proceed */ - break; - default: printfPQExpBuffer(&conn->errorMessage, libpq_gettext( @@ -1517,10 +1511,10 @@ keep_going: /* We will come back to here until there * message indicates that startup is successful, but we * might also get an Error message indicating failure. * (Notice messages indicating nonfatal warnings are also - * allowed by the protocol, as is a BackendKeyData - * message.) Easiest way to handle this is to let - * PQgetResult() read the messages. We just have to fake - * it out about the state of the connection, by setting + * allowed by the protocol, as are ParameterStatus and + * BackendKeyData messages.) Easiest way to handle this is + * to let PQgetResult() read the messages. We just have to + * fake it out about the state of the connection, by setting * asyncStatus = PGASYNC_BUSY (done above). */ @@ -1554,44 +1548,11 @@ keep_going: /* We will come back to here until there } /* - * Post-connection housekeeping. Prepare to send - * environment variables to server. + * We are open for business! */ - if (!PQsetenvStart(conn)) - goto error_return; - - conn->status = CONNECTION_SETENV; - - goto keep_going; - } - - case CONNECTION_SETENV: - - /* - * We pretend that the connection is OK for the duration of - * these queries. - */ - conn->status = CONNECTION_OK; - - switch (PQsetenvPoll(conn)) - { - case PGRES_POLLING_OK: /* Success */ - conn->status = CONNECTION_OK; - return PGRES_POLLING_OK; - - case PGRES_POLLING_READING: /* Still going */ - conn->status = CONNECTION_SETENV; - return PGRES_POLLING_READING; - - case PGRES_POLLING_WRITING: /* Still going */ - conn->status = CONNECTION_SETENV; - return PGRES_POLLING_WRITING; - - default: - conn->status = CONNECTION_SETENV; - goto error_return; + conn->status = CONNECTION_OK; + return PGRES_POLLING_OK; } - /* Unreachable */ default: printfPQExpBuffer(&conn->errorMessage, @@ -1619,239 +1580,6 @@ error_return: /* - * PQsetenvStart - * - * Starts the process of passing the values of a standard set of environment - * variables to the backend. - */ -static bool -PQsetenvStart(PGconn *conn) -{ - if (conn == NULL || - conn->status == CONNECTION_BAD || - conn->setenv_state != SETENV_STATE_IDLE) - return false; - - conn->setenv_state = SETENV_STATE_ENCODINGS_SEND; - - return true; -} - -/* - * PQsetenvPoll - * - * Polls the process of passing the values of a standard set of environment - * variables to the backend. - */ -static PostgresPollingStatusType -PQsetenvPoll(PGconn *conn) -{ - PGresult *res; - - if (conn == NULL || conn->status == CONNECTION_BAD) - return PGRES_POLLING_FAILED; - - /* Check whether there are any data for us */ - switch (conn->setenv_state) - { - /* These are reading states */ - case SETENV_STATE_ENCODINGS_WAIT: - { - /* Load waiting data */ - int n = pqReadData(conn); - - if (n < 0) - goto error_return; - if (n == 0) - return PGRES_POLLING_READING; - - break; - } - - /* These are writing states, so we just proceed. */ - case SETENV_STATE_ENCODINGS_SEND: - break; - - /* Should we raise an error if called when not active? */ - case SETENV_STATE_IDLE: - return PGRES_POLLING_OK; - - default: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext( - "invalid setenv state %c, " - "probably indicative of memory corruption\n" - ), - conn->setenv_state); - goto error_return; - } - - /* We will loop here until there is nothing left to do in this call. */ - for (;;) - { - switch (conn->setenv_state) - { - case SETENV_STATE_ENCODINGS_SEND: - { - const char *env = getenv("PGCLIENTENCODING"); - - if (!env || *env == '\0') - { - /* - * PGCLIENTENCODING is not specified, so query - * server for it. We must use begin/commit in - * case autocommit is off by default. - */ - if (!PQsendQuery(conn, "begin; select pg_client_encoding(); commit")) - goto error_return; - - conn->setenv_state = SETENV_STATE_ENCODINGS_WAIT; - return PGRES_POLLING_READING; - } - else - { - /* otherwise set client encoding in pg_conn struct */ - int encoding = pg_char_to_encoding(env); - - if (encoding < 0) - { - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("invalid encoding name in PGCLIENTENCODING: %s\n"), - env); - goto error_return; - } - conn->client_encoding = encoding; - - /* Move on to setting the environment options */ - conn->setenv_state = SETENV_STATE_IDLE; - } - break; - } - - case SETENV_STATE_ENCODINGS_WAIT: - { - if (PQisBusy(conn)) - return PGRES_POLLING_READING; - - res = PQgetResult(conn); - - if (res) - { - if (PQresultStatus(res) == PGRES_TUPLES_OK) - { - /* set client encoding in pg_conn struct */ - char *encoding; - - encoding = PQgetvalue(res, 0, 0); - if (!encoding) /* this should not happen */ - conn->client_encoding = PG_SQL_ASCII; - else - conn->client_encoding = pg_char_to_encoding(encoding); - } - else if (PQresultStatus(res) != PGRES_COMMAND_OK) - { - PQclear(res); - goto error_return; - } - PQclear(res); - /* Keep reading until PQgetResult returns NULL */ - } - else - { - /* - * NULL result indicates that the query is - * finished - */ - conn->setenv_state = SETENV_STATE_IDLE; - } - break; - } - - case SETENV_STATE_IDLE: - return PGRES_POLLING_OK; - - default: - printfPQExpBuffer(&conn->errorMessage, - libpq_gettext("invalid state %c, " - "probably indicative of memory corruption\n"), - conn->setenv_state); - goto error_return; - } - } - - /* Unreachable */ - -error_return: - conn->setenv_state = SETENV_STATE_IDLE; - return PGRES_POLLING_FAILED; -} - - -#ifdef NOT_USED - -/* - * PQsetenv - * - * Passes the values of a standard set of environment variables to the - * backend. - * - * Returns true on success, false on failure. - * - * This function used to be exported for no particularly good reason. - * Since it's no longer used by libpq itself, let's try #ifdef'ing it out - * and see if anyone complains. - */ -static bool -PQsetenv(PGconn *conn) -{ - PostgresPollingStatusType flag = PGRES_POLLING_WRITING; - - if (!PQsetenvStart(conn)) - return false; - - for (;;) - { - /* - * Wait, if necessary. Note that the initial state (just after - * PQsetenvStart) is to wait for the socket to select for writing. - */ - switch (flag) - { - case PGRES_POLLING_OK: - return true; /* success! */ - - case PGRES_POLLING_READING: - if (pqWait(1, 0, conn)) - { - conn->status = CONNECTION_BAD; - return false; - } - break; - - case PGRES_POLLING_WRITING: - if (pqWait(0, 1, conn)) - { - conn->status = CONNECTION_BAD; - return false; - } - break; - - default: - /* Just in case we failed to set it in PQsetenvPoll */ - conn->status = CONNECTION_BAD; - return false; - } - - /* - * Now try to advance the state machine. - */ - flag = PQsetenvPoll(conn); - } -} -#endif /* NOT_USED */ - - -/* * makeEmptyPGconn * - create a PGconn data structure with (as yet) no interesting data */ @@ -1869,7 +1597,6 @@ makeEmptyPGconn(void) conn->noticeHook = defaultNoticeProcessor; conn->status = CONNECTION_BAD; conn->asyncStatus = PGASYNC_IDLE; - conn->setenv_state = SETENV_STATE_IDLE; conn->notifyList = DLNewList(); conn->sock = -1; #ifdef USE_SSL diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 84520987812..53c4e288622 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.132 2003/04/25 01:24:00 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.133 2003/04/25 19:45:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,6 +21,8 @@ #include "libpq-fe.h" #include "libpq-int.h" +#include "mb/pg_wchar.h" + #ifdef WIN32 #include "win32.h" #else @@ -54,6 +56,7 @@ static void handleSendFailure(PGconn *conn); static void handleSyncLoss(PGconn *conn, char id, int msgLength); static int getRowDescriptions(PGconn *conn); static int getAnotherTuple(PGconn *conn, int binary); +static int getParameterStatus(PGconn *conn); static int getNotify(PGconn *conn); /* --------------- @@ -950,6 +953,11 @@ parseInput(PGconn *conn) * * However, if the state is IDLE then we got trouble; we need to deal * with the unexpected message somehow. + * + * ParameterStatus ('S') messages are a special case: in IDLE state + * we must process 'em (this case could happen if a new value was + * adopted from config file due to SIGHUP), but otherwise we hold + * off until BUSY state. */ if (id == 'A') { @@ -970,6 +978,7 @@ parseInput(PGconn *conn) /* * Unexpected message in IDLE state; need to recover somehow. * ERROR messages are displayed using the notice processor; + * ParameterStatus is handled normally; * anything else is just dropped on the floor after displaying * a suitable warning notice. (An ERROR is very possibly the * backend telling us why it is about to close the connection, @@ -980,6 +989,11 @@ parseInput(PGconn *conn) if (pqGetErrorNotice(conn, false /* treat as notice */)) return; } + else if (id == 'S') + { + if (getParameterStatus(conn)) + return; + } else { snprintf(noticeWorkspace, sizeof(noticeWorkspace), @@ -1021,6 +1035,10 @@ parseInput(PGconn *conn) PGRES_EMPTY_QUERY); conn->asyncStatus = PGASYNC_READY; break; + case 'S': /* parameter status */ + if (getParameterStatus(conn)) + return; + break; case 'K': /* secret key data from the backend */ /* @@ -1672,6 +1690,35 @@ fail: } /* + * Attempt to read a ParameterStatus message. + * This is possible in several places, so we break it out as a subroutine. + * Entry: 'S' message type and length have already been consumed. + * Exit: returns 0 if successfully consumed message. + * returns EOF if not enough data. + */ +static int +getParameterStatus(PGconn *conn) +{ + /* Get the parameter name */ + if (pqGets(&conn->workBuffer, conn)) + return EOF; + /* Is it one we care about? */ + if (strcmp(conn->workBuffer.data, "client_encoding") == 0) + { + if (pqGets(&conn->workBuffer, conn)) + return EOF; + conn->client_encoding = pg_char_to_encoding(conn->workBuffer.data); + } + else + { + /* Uninteresting parameter, ignore it */ + if (pqGets(&conn->workBuffer, conn)) + return EOF; + } + return 0; +} + +/* * Attempt to read a Notify response message. * This is possible in several places, so we break it out as a subroutine. * Entry: 'A' message type and length have already been consumed. @@ -2249,6 +2296,10 @@ PQfn(PGconn *conn, if (conn->result) return prepareAsyncResult(conn); return PQmakeEmptyPGresult(conn, status); + case 'S': /* parameter status */ + if (getParameterStatus(conn)) + continue; + break; default: /* The backend violates the protocol. */ printfPQExpBuffer(&conn->errorMessage, diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 4688fbf8f5a..12670f3a0a4 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-int.h,v 1.64 2003/04/24 21:16:44 tgl Exp $ + * $Id: libpq-int.h,v 1.65 2003/04/25 19:45:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -56,7 +56,7 @@ typedef int ssize_t; /* ssize_t doesn't exist in VC (atleast * pqcomm.h describe what the backend knows, not what libpq knows. */ -#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,103) /* XXX temporary value */ +#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,104) /* XXX temporary value */ /* * POSTGRES backend dependent Constants. @@ -194,14 +194,6 @@ typedef enum PGASYNC_COPY_OUT /* Copy Out data transfer in progress */ } PGAsyncStatusType; -/* PGSetenvStatusType defines the state of the PQSetenv state machine */ -typedef enum -{ - SETENV_STATE_ENCODINGS_SEND, /* About to send an "encodings" query */ - SETENV_STATE_ENCODINGS_WAIT, /* Waiting for query to complete */ - SETENV_STATE_IDLE -} PGSetenvStatusType; - /* large-object-access data ... allocated only if large-object code is used. */ typedef struct pgLobjfuncs { @@ -293,9 +285,6 @@ struct pg_conn PGresult *result; /* result being constructed */ PGresAttValue *curTuple; /* tuple currently being read */ - /* Status for sending environment info. Used during PQSetenv only. */ - PGSetenvStatusType setenv_state; - #ifdef USE_SSL bool allow_ssl_try; /* Allowed to try SSL negotiation */ bool require_ssl; /* Require SSL to make connection */ |
