summaryrefslogtreecommitdiff
path: root/src/interfaces/libpq
diff options
context:
space:
mode:
authorTom Lane2003-04-25 19:45:10 +0000
committerTom Lane2003-04-25 19:45:10 +0000
commit9cbaf7217747d6b5c88ba9b500a37b8372f185c9 (patch)
tree18569f5cba46f81d9097c9ea21c29f4a0e055936 /src/interfaces/libpq
parenta2190c9eb6afc65fddb80667d48024428368a9b6 (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.c289
-rw-r--r--src/interfaces/libpq/fe-exec.c53
-rw-r--r--src/interfaces/libpq/libpq-int.h15
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 */