summaryrefslogtreecommitdiff
path: root/src/interfaces
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2003-04-17 22:26:02 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2003-04-17 22:26:02 +0000
commitcb7fb3ca958ec8bd5a14e740c067f1d096af3454 (patch)
tree3494f623627ebebb9590c0ab993297a719bfe7f2 /src/interfaces
parent76fd678c06b826ae50aac5c4afb2e01e69d2b405 (diff)
downloadpostgresql-cb7fb3ca958ec8bd5a14e740c067f1d096af3454.tar.gz
First phase of FE/BE protocol modifications: new StartupPacket layout
with variable-width fields. No more truncation of long user names. Also, libpq can now send its environment-variable-driven SET commands as part of the startup packet, saving round trips to server.
Diffstat (limited to 'src/interfaces')
-rw-r--r--src/interfaces/libpq/fe-auth.c4
-rw-r--r--src/interfaces/libpq/fe-connect.c253
-rw-r--r--src/interfaces/libpq/libpq-int.h10
3 files changed, 143 insertions, 124 deletions
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 4ac99fd76a..fca2d2e303 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -10,7 +10,7 @@
* exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes).
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.74 2003/03/10 22:28:21 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.75 2003/04/17 22:26:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -559,7 +559,7 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
default:
return STATUS_ERROR;
}
- ret = pqPacketSend(conn, crypt_pwd, strlen(crypt_pwd) + 1);
+ ret = pqPacketSend(conn, 0, crypt_pwd, strlen(crypt_pwd) + 1);
if (areq == AUTH_REQ_MD5)
free(crypt_pwd);
return ret;
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index f2e509e624..9f5c8714a6 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.231 2003/04/04 20:42:13 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.232 2003/04/17 22:26:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -64,9 +64,6 @@ inet_aton(const char *cp, struct in_addr * inp)
#endif
-#define NOTIFYLIST_INITIAL_SIZE 10
-#define NOTIFYLIST_GROWBY 10
-
#define PGPASSFILE ".pgpass"
/* ----------
@@ -128,6 +125,10 @@ static const PQconninfoOption PQconninfoOptions[] = {
{"port", "PGPORT", DEF_PGPORT_STR, NULL,
"Database-Port", "", 6},
+ /*
+ * "tty" is no longer used either, but keep it present for backwards
+ * compatibility.
+ */
{"tty", "PGTTY", DefaultTty, NULL,
"Backend-Debug-TTY", "D", 40},
@@ -182,12 +183,13 @@ static PQconninfoOption *conninfo_parse(const char *conninfo,
PQExpBuffer errorMessage);
static char *conninfo_getval(PQconninfoOption *connOptions,
const char *keyword);
+static int build_startup_packet(const PGconn *conn, char *packet);
static void defaultNoticeProcessor(void *arg, const char *message);
static int parseServiceInfo(PQconninfoOption *options,
PQExpBuffer errorMessage);
-char *pwdfMatchesString(char *buf, char *token);
-char *PasswordFromFile(char *hostname, char *port, char *dbname,
- char *username);
+static char *pwdfMatchesString(char *buf, char *token);
+static char *PasswordFromFile(char *hostname, char *port, char *dbname,
+ char *username);
/*
* Connecting to a Database
@@ -396,7 +398,7 @@ PQconndefaults(void)
* is NULL or a null string.
*
* PGTTY identifies tty to which to send messages if <pgtty> argument
- * is NULL or a null string.
+ * is NULL or a null string. (No longer used by backend.)
*
* PGOPTIONS identifies connection options if <pgoptions> argument is
* NULL or a null string.
@@ -792,10 +794,6 @@ connectDBStart(PGconn *conn)
{
int portnum;
char portstr[64];
-#ifdef USE_SSL
- StartupPacket np; /* Used to negotiate SSL connection */
- char SSLok;
-#endif
struct addrinfo *addrs = NULL;
struct addrinfo *addr_cur = NULL;
struct addrinfo hint;
@@ -980,9 +978,11 @@ retry1:
/* Attempt to negotiate SSL usage */
if (conn->allow_ssl_try)
{
- memset((char *) &np, 0, sizeof(np));
- np.protoVersion = htonl(NEGOTIATE_SSL_CODE);
- if (pqPacketSend(conn, (char *) &np, sizeof(StartupPacket)) != STATUS_OK)
+ ProtocolVersion pv;
+ char SSLok;
+
+ pv = htonl(NEGOTIATE_SSL_CODE);
+ if (pqPacketSend(conn, 0, &pv, sizeof(ProtocolVersion)) != STATUS_OK)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not send SSL negotiation packet: %s\n"),
@@ -1284,22 +1284,21 @@ keep_going: /* We will come back to here until there
case CONNECTION_MADE:
{
- StartupPacket sp;
+ char *startpacket;
+ int packetlen;
/*
- * Initialize the startup packet.
+ * Build the startup packet.
*/
-
- MemSet((char *) &sp, 0, sizeof(StartupPacket));
-
- sp.protoVersion = (ProtocolVersion) htonl(PG_PROTOCOL_LIBPQ);
-
- strncpy(sp.user, conn->pguser, SM_USER);
- strncpy(sp.database, conn->dbName, SM_DATABASE);
- strncpy(sp.tty, conn->pgtty, SM_TTY);
-
- if (conn->pgoptions)
- strncpy(sp.options, conn->pgoptions, SM_OPTIONS);
+ packetlen = build_startup_packet(conn, NULL);
+ startpacket = (char *) malloc(packetlen);
+ if (!startpacket)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("out of memory\n"));
+ goto error_return;
+ }
+ packetlen = build_startup_packet(conn, startpacket);
/*
* Send the startup packet.
@@ -1307,16 +1306,17 @@ keep_going: /* We will come back to here until there
* Theoretically, this could block, but it really shouldn't
* since we only got here if the socket is write-ready.
*/
-
- if (pqPacketSend(conn, (char *) &sp,
- sizeof(StartupPacket)) != STATUS_OK)
+ if (pqPacketSend(conn, 0, startpacket, packetlen) != STATUS_OK)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not send startup packet: %s\n"),
SOCK_STRERROR(SOCK_ERRNO));
+ free(startpacket);
goto error_return;
}
+ free(startpacket);
+
conn->status = CONNECTION_AWAITING_RESPONSE;
return PGRES_POLLING_READING;
}
@@ -1576,7 +1576,6 @@ PQsetenvStart(PGconn *conn)
return false;
conn->setenv_state = SETENV_STATE_ENCODINGS_SEND;
- conn->next_eo = EnvironmentOptions;
return true;
}
@@ -1600,7 +1599,6 @@ PQsetenvPoll(PGconn *conn)
{
/* These are reading states */
case SETENV_STATE_ENCODINGS_WAIT:
- case SETENV_STATE_OPTION_WAIT:
{
/* Load waiting data */
int n = pqReadData(conn);
@@ -1615,7 +1613,6 @@ PQsetenvPoll(PGconn *conn)
/* These are writing states, so we just proceed. */
case SETENV_STATE_ENCODINGS_SEND:
- case SETENV_STATE_OPTION_SEND:
break;
/* Should we raise an error if called when not active? */
@@ -1669,7 +1666,7 @@ PQsetenvPoll(PGconn *conn)
conn->client_encoding = encoding;
/* Move on to setting the environment options */
- conn->setenv_state = SETENV_STATE_OPTION_SEND;
+ conn->setenv_state = SETENV_STATE_IDLE;
}
break;
}
@@ -1708,80 +1705,11 @@ PQsetenvPoll(PGconn *conn)
* NULL result indicates that the query is
* finished
*/
- /* Move on to setting the environment options */
- conn->setenv_state = SETENV_STATE_OPTION_SEND;
- }
- break;
- }
-
- case SETENV_STATE_OPTION_SEND:
- {
- /* Send an Environment Option */
- char setQuery[100]; /* note length limits in
- * sprintf's below */
-
- if (conn->next_eo->envName)
- {
- const char *val;
-
- if ((val = getenv(conn->next_eo->envName)))
- {
- if (strcasecmp(val, "default") == 0)
- sprintf(setQuery, "SET %s = %.60s",
- conn->next_eo->pgName, val);
- else
- sprintf(setQuery, "SET %s = '%.60s'",
- conn->next_eo->pgName, val);
-#ifdef CONNECTDEBUG
- printf("Use environment variable %s to send %s\n",
- conn->next_eo->envName, setQuery);
-#endif
- if (!PQsendQuery(conn, setQuery))
- goto error_return;
-
- conn->setenv_state = SETENV_STATE_OPTION_WAIT;
- }
- else
- conn->next_eo++;
- }
- else
- {
- /* No more options to send, so we are done. */
conn->setenv_state = SETENV_STATE_IDLE;
}
break;
}
- case SETENV_STATE_OPTION_WAIT:
- {
- if (PQisBusy(conn))
- return PGRES_POLLING_READING;
-
- res = PQgetResult(conn);
-
- if (res)
- {
- 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
- */
- /* Send the next option */
- conn->next_eo++;
- conn->setenv_state = SETENV_STATE_OPTION_SEND;
- }
- break;
- }
-
case SETENV_STATE_IDLE:
return PGRES_POLLING_OK;
@@ -2225,24 +2153,34 @@ cancel_errReturn:
/*
* pqPacketSend() -- send a single-packet message.
- * this is like PacketSend(), defined in backend/libpq/pqpacket.c
+ *
+ * pack_type: the single-byte message type code. (Pass zero for startup
+ * packets, which have no message type code.)
+ *
+ * buf, buf_len: contents of message. The given length includes only what
+ * is in buf; the message type and message length fields are added here.
*
* RETURNS: STATUS_ERROR if the write fails, STATUS_OK otherwise.
* SIDE_EFFECTS: may block.
*/
int
-pqPacketSend(PGconn *conn, const char *buf, size_t len)
+pqPacketSend(PGconn *conn, char pack_type,
+ const void *buf, size_t buf_len)
{
- /* Send the total packet size. */
-
- if (pqPutInt(4 + len, 4, conn))
+ /* Send the message type. */
+ if (pack_type != 0)
+ if (pqPutc(pack_type, conn))
+ return STATUS_ERROR;
+
+ /* Send the (self-inclusive) message length word. */
+ if (pqPutInt(buf_len + 4, 4, conn))
return STATUS_ERROR;
- /* Send the packet itself. */
-
- if (pqPutnchar(buf, len, conn))
+ /* Send the message body. */
+ if (pqPutnchar(buf, buf_len, conn))
return STATUS_ERROR;
+ /* Flush to ensure backend gets it. */
if (pqFlush(conn))
return STATUS_ERROR;
@@ -2661,6 +2599,87 @@ PQconninfoFree(PQconninfoOption *connOptions)
}
+/*
+ * Build a startup packet given a filled-in PGconn structure.
+ *
+ * We need to figure out how much space is needed, then fill it in.
+ * To avoid duplicate logic, this routine is called twice: the first time
+ * (with packet == NULL) just counts the space needed, the second time
+ * (with packet == allocated space) fills it in. Return value is the number
+ * of bytes used.
+ */
+static int
+build_startup_packet(const PGconn *conn, char *packet)
+{
+ int packet_len = 0;
+ const struct EnvironmentOptions *next_eo;
+
+ /* Protocol version comes first. */
+ if (packet)
+ {
+ ProtocolVersion pv = htonl(PG_PROTOCOL_LIBPQ);
+
+ memcpy(packet + packet_len, &pv, sizeof(ProtocolVersion));
+ }
+ packet_len += sizeof(ProtocolVersion);
+
+ /* Add user name, database name, options */
+ if (conn->pguser)
+ {
+ if (packet)
+ strcpy(packet + packet_len, "user");
+ packet_len += strlen("user") + 1;
+ if (packet)
+ strcpy(packet + packet_len, conn->pguser);
+ packet_len += strlen(conn->pguser) + 1;
+ }
+ if (conn->dbName)
+ {
+ if (packet)
+ strcpy(packet + packet_len, "database");
+ packet_len += strlen("database") + 1;
+ if (packet)
+ strcpy(packet + packet_len, conn->dbName);
+ packet_len += strlen(conn->dbName) + 1;
+ }
+ if (conn->pgoptions)
+ {
+ if (packet)
+ strcpy(packet + packet_len, "options");
+ packet_len += strlen("options") + 1;
+ if (packet)
+ strcpy(packet + packet_len, conn->pgoptions);
+ packet_len += strlen(conn->pgoptions) + 1;
+ }
+
+ /* Add any environment-driven GUC settings needed */
+ for (next_eo = EnvironmentOptions; next_eo->envName; next_eo++)
+ {
+ const char *val;
+
+ if ((val = getenv(next_eo->envName)) != NULL)
+ {
+ if (strcasecmp(val, "default") != 0)
+ {
+ if (packet)
+ strcpy(packet + packet_len, next_eo->pgName);
+ packet_len += strlen(next_eo->pgName) + 1;
+ if (packet)
+ strcpy(packet + packet_len, val);
+ packet_len += strlen(val) + 1;
+ }
+ }
+ }
+
+ /* Add trailing terminator */
+ if (packet)
+ packet[packet_len] = '\0';
+ packet_len++;
+
+ return packet_len;
+}
+
+
/* =========== accessor functions for PGconn ========= */
char *
PQdb(const PGconn *conn)
@@ -2850,9 +2869,11 @@ defaultNoticeProcessor(void *arg, const char *message)
fprintf(stderr, "%s", message);
}
-/* returns a pointer to the next token or NULL if the current
- * token doesn't match */
-char *
+/*
+ * returns a pointer to the next token or NULL if the current
+ * token doesn't match
+ */
+static char *
pwdfMatchesString(char *buf, char *token)
{
char *tbuf,
@@ -2889,7 +2910,7 @@ pwdfMatchesString(char *buf, char *token)
}
/* Get a password from the password file. Return value is malloc'd. */
-char *
+static char *
PasswordFromFile(char *hostname, char *port, char *dbname, char *username)
{
FILE *fp;
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index cde2b2d2b0..43c3bd11c5 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.60 2002/10/16 02:55:30 momjian Exp $
+ * $Id: libpq-int.h,v 1.61 2003/04/17 22:26:02 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(2,0)
+#define PG_PROTOCOL_LIBPQ PG_PROTOCOL(3,100) /* XXX temporary value */
/*
* POSTGRES backend dependent Constants.
@@ -181,8 +181,6 @@ typedef enum
/* 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 */
SETENV_STATE_ENCODINGS_SEND, /* About to send an "encodings" query */
SETENV_STATE_ENCODINGS_WAIT, /* Waiting for query to complete */
SETENV_STATE_IDLE
@@ -274,7 +272,6 @@ struct pg_conn
/* Status for sending environment info. Used during PQSetenv only. */
PGSetenvStatusType setenv_state;
- const struct EnvironmentOptions *next_eo;
#ifdef USE_SSL
bool allow_ssl_try; /* Allowed to try SSL negotiation */
@@ -312,7 +309,8 @@ extern char *const pgresStatus[];
/* === in fe-connect.c === */
-extern int pqPacketSend(PGconn *conn, const char *buf, size_t len);
+extern int pqPacketSend(PGconn *conn, char pack_type,
+ const void *buf, size_t buf_len);
/* === in fe-exec.c === */