mirror of
https://github.com/postgres/postgres.git
synced 2025-08-27 07:42:10 +03:00
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.)
This commit is contained in:
@@ -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,45 +1548,12 @@ 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;
|
||||
conn->status = CONNECTION_OK;
|
||||
return PGRES_POLLING_OK;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
/* Unreachable */
|
||||
|
||||
default:
|
||||
printfPQExpBuffer(&conn->errorMessage,
|
||||
libpq_gettext(
|
||||
@@ -1618,239 +1579,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
|
||||
|
@@ -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 */
|
||||
|
||||
/*
|
||||
@@ -1671,6 +1689,35 @@ fail:
|
||||
return EOF;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
@@ -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,
|
||||
|
@@ -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 */
|
||||
|
Reference in New Issue
Block a user