Fold PQsetenv working state into PGconn, rather than trying to maintain

it in a separate object.  There's no value in keeping the state separate,
and it creates dangling-pointer problems.  Also, remove PQsetenv routines
from public API, until and unless they are redesigned to have a safer
interface.  Since they were never part of the documented API before 7.0,
it's unlikely that anyone is calling them.
This commit is contained in:
Tom Lane 2000-03-24 01:39:55 +00:00
parent 5b1f92eaa7
commit 0edcee3459
4 changed files with 90 additions and 207 deletions

View File

@ -629,63 +629,6 @@ int PQbackendPID(const PGconn *conn);
server host, not the local host! server host, not the local host!
</para> </para>
</listitem> </listitem>
<listitem>
<para>
<function>PQsetenvStart</function>
<function>PQsetenvPoll</function>
<function>PQsetenvAbort</function>
Perform an environment negotiation.
<synopsis>
PGsetenvHandle *PQsetenvStart(PGconn *conn)
</synopsis>
<synopsis>
PostgresPollingStatusType *PQsetenvPoll(PGsetenvHandle handle)
</synopsis>
<synopsis>
void PQsetenvAbort(PGsetenvHandle handle)
</synopsis>
These two routines can be used to re-perform the environment negotiation
that occurs during the opening of a connection to a database
server. I have
no idea why this might be useful (XXX anyone?) but it might prove useful
for users to be able to reconfigure their character encodings
on-the-fly, for example.
</para>
<para>
These functions will not block, subject to the restrictions applied to
PQconnectStart and PQconnectPoll.
</para>
<para>
To begin, call handle=PQsetenvStart(conn), where conn is an open connection
to the database server. If handle is NULL, then libpq has been unable to
allocate a new PGsetenvHandle structure. Otherwise, a valid handle is
returned. This handle is intended to be opaque - you may only use it to
call other functions in libpq (PQsetenvPoll, for example).
</para>
<para>
Poll the procedure using PQsetenvPoll, in exactly the same way as you would
create a connection using PQconnectPoll.
</para>
<para>
The procedure may be aborted at any time by calling PQsetenvAbort(handle).
</para>
</listitem>
<listitem>
<para>
<function>PQsetenv</function>
Perform an environment negotiation.
<synopsis>
int PQsetenv(PGconn *conn)
</synopsis>
This function performs the same duties as PQsetenvStart and
PQsetenvPoll, but
blocks to do so. It returns 1 on success and 0 on failure.
</para>
</listitem>
</itemizedlist> </itemizedlist>
</para> </para>
</sect1> </sect1>

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.124 2000/03/23 17:27:29 momjian Exp $ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.125 2000/03/24 01:39:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -55,29 +55,6 @@ static int inet_aton(const char *cp, struct in_addr *inp) {
} }
#endif #endif
/* ----------
* pg_setenv_state
* A struct used when polling a setenv request. This is referred to externally
* using a PGsetenvHandle.
* ----------
*/
struct pg_setenv_state
{
enum
{
SETENV_STATE_OPTION_SEND, /* About to send an Environment Option */
SETENV_STATE_OPTION_WAIT, /* Waiting for above send to complete */
#ifdef MULTIBYTE
SETENV_STATE_ENCODINGS_SEND, /* About to send an "encodings" query */
SETENV_STATE_ENCODINGS_WAIT, /* Waiting for query to complete */
#endif
SETENV_STATE_OK,
SETENV_STATE_FAILED
} state;
PGconn *conn;
PGresult *res;
const struct EnvironmentOptions *eo;
} ;
#ifdef USE_SSL #ifdef USE_SSL
static SSL_CTX *SSL_context = NULL; static SSL_CTX *SSL_context = NULL;
@ -181,6 +158,8 @@ static const struct EnvironmentOptions
static int connectDBStart(PGconn *conn); static int connectDBStart(PGconn *conn);
static int connectDBComplete(PGconn *conn); static int connectDBComplete(PGconn *conn);
static bool PQsetenvStart(PGconn *conn);
static PostgresPollingStatusType PQsetenvPoll(PGconn *conn);
static PGconn *makeEmptyPGconn(void); static PGconn *makeEmptyPGconn(void);
static void freePGconn(PGconn *conn); static void freePGconn(PGconn *conn);
static void closePGconn(PGconn *conn); static void closePGconn(PGconn *conn);
@ -1313,8 +1292,7 @@ PQconnectPoll(PGconn *conn)
* Post-connection housekeeping. Prepare to send environment * Post-connection housekeeping. Prepare to send environment
* variables to server. * variables to server.
*/ */
if (! PQsetenvStart(conn))
if ((conn->setenv_handle = PQsetenvStart(conn)) == NULL)
goto error_return; goto error_return;
conn->status = CONNECTION_SETENV; conn->status = CONNECTION_SETENV;
@ -1377,34 +1355,23 @@ error_return:
* *
* ---------------- * ----------------
*/ */
PGsetenvHandle static bool
PQsetenvStart(PGconn *conn) PQsetenvStart(PGconn *conn)
{ {
struct pg_setenv_state *handle; if (conn == NULL ||
conn->status == CONNECTION_BAD ||
if (conn == NULL || conn->status == CONNECTION_BAD) conn->setenv_state != SETENV_STATE_IDLE)
return NULL; return false;
if ((handle = malloc(sizeof(struct pg_setenv_state))) == NULL)
{
printfPQExpBuffer(&conn->errorMessage,
"PQsetenvStart() -- malloc error - %s\n",
strerror(errno));
return NULL;
}
handle->conn = conn;
handle->res = NULL;
handle->eo = EnvironmentOptions;
#ifdef MULTIBYTE #ifdef MULTIBYTE
handle->state = SETENV_STATE_ENCODINGS_SEND; conn->setenv_state = SETENV_STATE_ENCODINGS_SEND;
#else #else
handle->state = SETENV_STATE_OPTION_SEND; conn->setenv_state = SETENV_STATE_OPTION_SEND;
#endif #endif
return handle; /* Note that a struct pg_setenv_state * is the same as a conn->next_eo = EnvironmentOptions;
PGsetenvHandle */
return true;
} }
/* ---------------- /* ----------------
@ -1415,19 +1382,19 @@ PQsetenvStart(PGconn *conn)
* *
* ---------------- * ----------------
*/ */
PostgresPollingStatusType static PostgresPollingStatusType
PQsetenvPoll(PGconn *conn) PQsetenvPoll(PGconn *conn)
{ {
PGsetenvHandle handle = conn->setenv_handle; PGresult *res;
#ifdef MULTIBYTE #ifdef MULTIBYTE
static const char envname[] = "PGCLIENTENCODING"; static const char envname[] = "PGCLIENTENCODING";
#endif #endif
if (!handle || handle->state == SETENV_STATE_FAILED) if (conn == NULL || conn->status == CONNECTION_BAD)
return PGRES_POLLING_FAILED; return PGRES_POLLING_FAILED;
/* Check whether there are any data for us */ /* Check whether there are any data for us */
switch (handle->state) switch (conn->setenv_state)
{ {
/* These are reading states */ /* These are reading states */
#ifdef MULTIBYTE #ifdef MULTIBYTE
@ -1436,7 +1403,7 @@ PQsetenvPoll(PGconn *conn)
case SETENV_STATE_OPTION_WAIT: case SETENV_STATE_OPTION_WAIT:
{ {
/* Load waiting data */ /* Load waiting data */
int n = pqReadData(handle->conn); int n = pqReadData(conn);
if (n < 0) if (n < 0)
goto error_return; goto error_return;
@ -1452,9 +1419,13 @@ PQsetenvPoll(PGconn *conn)
#endif #endif
case SETENV_STATE_OPTION_SEND: case SETENV_STATE_OPTION_SEND:
break; break;
/* Should we raise an error if called when not active? */
case SETENV_STATE_IDLE:
return PGRES_POLLING_OK;
default: default:
printfPQExpBuffer(&handle->conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
"PQsetenvPoll() -- unknown state - " "PQsetenvPoll() -- unknown state - "
"probably indicative of memory corruption!\n"); "probably indicative of memory corruption!\n");
goto error_return; goto error_return;
@ -1463,7 +1434,7 @@ PQsetenvPoll(PGconn *conn)
keep_going: /* We will come back to here until there is nothing left to keep_going: /* We will come back to here until there is nothing left to
parse. */ parse. */
switch(handle->state) switch(conn->setenv_state)
{ {
#ifdef MULTIBYTE #ifdef MULTIBYTE
@ -1475,39 +1446,39 @@ PQsetenvPoll(PGconn *conn)
env = getenv(envname); env = getenv(envname);
if (!env || *env == '\0') if (!env || *env == '\0')
{ {
if (!PQsendQuery(handle->conn, if (!PQsendQuery(conn,
"select getdatabaseencoding()")) "select getdatabaseencoding()"))
goto error_return; goto error_return;
handle->state = SETENV_STATE_ENCODINGS_WAIT; conn->setenv_state = SETENV_STATE_ENCODINGS_WAIT;
return PGRES_POLLING_READING; return PGRES_POLLING_READING;
} }
} }
case SETENV_STATE_ENCODINGS_WAIT: case SETENV_STATE_ENCODINGS_WAIT:
{ {
if (PQisBusy(handle->conn)) if (PQisBusy(conn))
return PGRES_POLLING_READING; return PGRES_POLLING_READING;
handle->res = PQgetResult(handle->conn); res = PQgetResult(conn);
if (handle->res) if (res)
{ {
char *encoding; char *encoding;
if (PQresultStatus(handle->res) != PGRES_TUPLES_OK) if (PQresultStatus(res) != PGRES_TUPLES_OK)
{ {
PQclear(handle->res); PQclear(res);
goto error_return; goto error_return;
} }
encoding = PQgetvalue(handle->res, 0, 0); /* set client encoding in pg_conn struct */
encoding = PQgetvalue(res, 0, 0);
if (!encoding) /* this should not happen */ if (!encoding) /* this should not happen */
conn->client_encoding = SQL_ASCII; conn->client_encoding = SQL_ASCII;
else else
/* set client encoding to pg_conn struct */
conn->client_encoding = pg_char_to_encoding(encoding); conn->client_encoding = pg_char_to_encoding(encoding);
PQclear(handle->res); PQclear(res);
/* We have to keep going in order to clear up the query */ /* We have to keep going in order to clear up the query */
goto keep_going; goto keep_going;
} }
@ -1515,7 +1486,7 @@ PQsetenvPoll(PGconn *conn)
/* NULL result indicates that the query is finished */ /* NULL result indicates that the query is finished */
/* Move on to setting the environment options */ /* Move on to setting the environment options */
handle->state = SETENV_STATE_OPTION_SEND; conn->setenv_state = SETENV_STATE_OPTION_SEND;
goto keep_going; goto keep_going;
} }
#endif #endif
@ -1525,36 +1496,36 @@ PQsetenvPoll(PGconn *conn)
/* Send an Environment Option */ /* Send an Environment Option */
char setQuery[100]; /* note length limits in sprintf's below */ char setQuery[100]; /* note length limits in sprintf's below */
if (handle->eo->envName) if (conn->next_eo->envName)
{ {
const char *val; const char *val;
if ((val = getenv(handle->eo->envName))) if ((val = getenv(conn->next_eo->envName)))
{ {
if (strcasecmp(val, "default") == 0) if (strcasecmp(val, "default") == 0)
sprintf(setQuery, "SET %s = %.60s", sprintf(setQuery, "SET %s = %.60s",
handle->eo->pgName, val); conn->next_eo->pgName, val);
else else
sprintf(setQuery, "SET %s = '%.60s'", sprintf(setQuery, "SET %s = '%.60s'",
handle->eo->pgName, val); conn->next_eo->pgName, val);
#ifdef CONNECTDEBUG #ifdef CONNECTDEBUG
printf("Use environment variable %s to send %s\n", printf("Use environment variable %s to send %s\n",
handle->eo->envName, setQuery); conn->next_eo->envName, setQuery);
#endif #endif
if (!PQsendQuery(handle->conn, setQuery)) if (!PQsendQuery(conn, setQuery))
goto error_return; goto error_return;
handle->state = SETENV_STATE_OPTION_WAIT; conn->setenv_state = SETENV_STATE_OPTION_WAIT;
} }
else else
{ {
handle->eo++; conn->next_eo++;
} }
} }
else else
{ {
/* No option to send, so we are done. */ /* No more options to send, so we are done. */
handle->state = SETENV_STATE_OK; conn->setenv_state = SETENV_STATE_IDLE;
} }
goto keep_going; goto keep_going;
@ -1562,20 +1533,20 @@ PQsetenvPoll(PGconn *conn)
case SETENV_STATE_OPTION_WAIT: case SETENV_STATE_OPTION_WAIT:
{ {
if (PQisBusy(handle->conn)) if (PQisBusy(conn))
return PGRES_POLLING_READING; return PGRES_POLLING_READING;
handle->res = PQgetResult(handle->conn); res = PQgetResult(conn);
if (handle->res) if (res)
{ {
if (PQresultStatus(handle->res) != PGRES_COMMAND_OK) if (PQresultStatus(res) != PGRES_COMMAND_OK)
{ {
PQclear(handle->res); PQclear(res);
goto error_return; goto error_return;
} }
/* Don't need the result */ /* Don't need the result */
PQclear(handle->res); PQclear(res);
/* We have to keep going in order to clear up the query */ /* We have to keep going in order to clear up the query */
goto keep_going; goto keep_going;
} }
@ -1583,18 +1554,16 @@ PQsetenvPoll(PGconn *conn)
/* NULL result indicates that the query is finished */ /* NULL result indicates that the query is finished */
/* Send the next option */ /* Send the next option */
handle->eo++; conn->next_eo++;
handle->state = SETENV_STATE_OPTION_SEND; conn->setenv_state = SETENV_STATE_OPTION_SEND;
goto keep_going; goto keep_going;
} }
case SETENV_STATE_OK: case SETENV_STATE_IDLE:
/* Tidy up */
free(handle);
return PGRES_POLLING_OK; return PGRES_POLLING_OK;
default: default:
printfPQExpBuffer(&handle->conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
"PQsetenvPoll() -- unknown state - " "PQsetenvPoll() -- unknown state - "
"probably indicative of memory corruption!\n"); "probably indicative of memory corruption!\n");
goto error_return; goto error_return;
@ -1603,34 +1572,12 @@ PQsetenvPoll(PGconn *conn)
/* Unreachable */ /* Unreachable */
error_return: error_return:
handle->state = SETENV_STATE_FAILED; /* This may protect us even if we conn->setenv_state = SETENV_STATE_IDLE;
* are called after the handle
* has been freed. */
free(handle);
return PGRES_POLLING_FAILED; return PGRES_POLLING_FAILED;
} }
/* ---------------- #ifdef NOT_USED
* PQsetenvAbort
*
* Aborts the process of passing the values of a standard set of environment
* variables to the backend.
*
* ----------------
*/
void
PQsetenvAbort(PGsetenvHandle handle)
{
/* We should not have been called in the FAILED state, but we can cope by
* not freeing the handle (it has probably been freed by now anyway). */
if (handle->state != SETENV_STATE_FAILED)
{
handle->state = SETENV_STATE_FAILED;
free(handle);
}
}
/* ---------------- /* ----------------
* PQsetenv * PQsetenv
@ -1638,22 +1585,20 @@ PQsetenvAbort(PGsetenvHandle handle)
* Passes the values of a standard set of environment variables to the * Passes the values of a standard set of environment variables to the
* backend. * backend.
* *
* Returns 1 on success, 0 on failure. * Returns true on success, false on failure.
*
* This function used to return void. I don't think that there should be
* compatibility problems caused by giving it a return value, especially as
* this function has not been documented previously.
* *
* 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.
* ---------------- * ----------------
*/ */
int static bool
PQsetenv(PGconn *conn) PQsetenv(PGconn *conn)
{ {
PGsetenvHandle handle;
PostgresPollingStatusType flag = PGRES_POLLING_WRITING; PostgresPollingStatusType flag = PGRES_POLLING_WRITING;
if ((handle = PQsetenvStart(conn)) == NULL) if (! PQsetenvStart(conn))
return 0; return false;
for (;;) { for (;;) {
/* /*
@ -1666,13 +1611,13 @@ PQsetenv(PGconn *conn)
break; break;
case PGRES_POLLING_OK: case PGRES_POLLING_OK:
return 1; /* success! */ return true; /* success! */
case PGRES_POLLING_READING: case PGRES_POLLING_READING:
if (pqWait(1, 0, conn)) if (pqWait(1, 0, conn))
{ {
conn->status = CONNECTION_BAD; conn->status = CONNECTION_BAD;
return 0; return false;
} }
break; break;
@ -1680,14 +1625,14 @@ PQsetenv(PGconn *conn)
if (pqWait(0, 1, conn)) if (pqWait(0, 1, conn))
{ {
conn->status = CONNECTION_BAD; conn->status = CONNECTION_BAD;
return 0; return false;
} }
break; break;
default: default:
/* Just in case we failed to set it in PQsetenvPoll */ /* Just in case we failed to set it in PQsetenvPoll */
conn->status = CONNECTION_BAD; conn->status = CONNECTION_BAD;
return 0; return false;
} }
/* /*
* Now try to advance the state machine. * Now try to advance the state machine.
@ -1696,6 +1641,9 @@ PQsetenv(PGconn *conn)
} }
} }
#endif /* NOT_USED */
/* /*
* makeEmptyPGconn * makeEmptyPGconn
* - create a PGconn data structure with (as yet) no interesting data * - create a PGconn data structure with (as yet) no interesting data
@ -1714,6 +1662,7 @@ makeEmptyPGconn(void)
conn->noticeHook = defaultNoticeProcessor; conn->noticeHook = defaultNoticeProcessor;
conn->status = CONNECTION_BAD; conn->status = CONNECTION_BAD;
conn->asyncStatus = PGASYNC_IDLE; conn->asyncStatus = PGASYNC_IDLE;
conn->setenv_state = SETENV_STATE_IDLE;
conn->notifyList = DLNewList(); conn->notifyList = DLNewList();
conn->sock = -1; conn->sock = -1;
#ifdef USE_SSL #ifdef USE_SSL
@ -1808,12 +1757,6 @@ freePGconn(PGconn *conn)
static void static void
closePGconn(PGconn *conn) closePGconn(PGconn *conn)
{ {
if (conn->status == CONNECTION_SETENV)
{
/* We have to abort the setenv process as well */
PQsetenvAbort(conn->setenv_handle);
}
if (conn->sock >= 0) if (conn->sock >= 0)
{ {

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: libpq-fe.h,v 1.62 2000/03/23 15:00:11 momjian Exp $ * $Id: libpq-fe.h,v 1.63 2000/03/24 01:39:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -86,12 +86,6 @@ extern "C"
*/ */
typedef struct pg_result PGresult; typedef struct pg_result PGresult;
/* PGsetenvHandle is an opaque handle which is returned by PQsetenvStart and
* which should be passed to PQsetenvPoll or PQsetenvAbort in order to refer
* to the particular process being performed.
*/
typedef struct pg_setenv_state *PGsetenvHandle;
/* PGnotify represents the occurrence of a NOTIFY message. /* PGnotify represents the occurrence of a NOTIFY message.
* Ideally this would be an opaque typedef, but it's so simple that it's * Ideally this would be an opaque typedef, but it's so simple that it's
* unlikely to change. * unlikely to change.
@ -231,15 +225,6 @@ extern "C"
/* Override default notice processor */ /* Override default notice processor */
extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg); extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg);
/* Passing of environment variables */
/* Asynchronous (non-blocking) */
extern PGsetenvHandle PQsetenvStart(PGconn *conn);
extern PostgresPollingStatusType PQsetenvPoll(PGconn *conn);
extern void PQsetenvAbort(PGsetenvHandle handle);
/* Synchronous (blocking) */
extern int PQsetenv(PGconn *conn);
/* === in fe-exec.c === */ /* === in fe-exec.c === */
/* Simple synchronous query */ /* Simple synchronous query */

View File

@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: libpq-int.h,v 1.21 2000/03/14 23:59:23 tgl Exp $ * $Id: libpq-int.h,v 1.22 2000/03/24 01:39:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -164,6 +164,17 @@ typedef enum
PGASYNC_COPY_OUT /* Copy Out data transfer in progress */ PGASYNC_COPY_OUT /* Copy Out data transfer in progress */
} PGAsyncStatusType; } PGAsyncStatusType;
/* PGSetenvStatusType defines the state of the PQSetenv state machine */
typedef enum
{
SETENV_STATE_OPTION_SEND, /* About to send an Environment Option */
SETENV_STATE_OPTION_WAIT, /* Waiting for above send to complete */
/* these next two are only used in MULTIBYTE mode */
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. */ /* large-object-access data ... allocated only if large-object code is used. */
typedef struct pgLobjfuncs typedef struct pgLobjfuncs
{ {
@ -244,8 +255,9 @@ struct pg_conn
PGresult *result; /* result being constructed */ PGresult *result; /* result being constructed */
PGresAttValue *curTuple; /* tuple currently being read */ PGresAttValue *curTuple; /* tuple currently being read */
/* Handle for setenv request. Used during connection only. */ /* Status for sending environment info. Used during PQSetenv only. */
PGsetenvHandle setenv_handle; PGSetenvStatusType setenv_state;
const struct EnvironmentOptions *next_eo;
#ifdef USE_SSL #ifdef USE_SSL
bool allow_ssl_try; /* Allowed to try SSL negotiation */ bool allow_ssl_try; /* Allowed to try SSL negotiation */