]> granicus.if.org Git - postgresql/commitdiff
First phase of FE/BE protocol modifications: new StartupPacket layout
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 17 Apr 2003 22:26:02 +0000 (22:26 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 17 Apr 2003 22:26:02 +0000 (22:26 +0000)
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.

16 files changed:
doc/src/sgml/libpq.sgml
src/backend/libpq/auth.c
src/backend/libpq/crypt.c
src/backend/libpq/hba.c
src/backend/postmaster/postmaster.c
src/backend/tcop/postgres.c
src/include/libpq/auth.h
src/include/libpq/hba.h
src/include/libpq/libpq-be.h
src/include/libpq/password.h [deleted file]
src/include/libpq/pqcomm.h
src/interfaces/libpq/fe-auth.c
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/libpq-int.h
src/test/regress/expected/interval.out
src/test/regress/sql/interval.sql

index 4d9c249c83945b2273e325bd2c6c64790efa739b..a1f8d6b5f6b768cde94c3251dcb433eaaf1f680e 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.117 2003/03/25 16:15:37 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.118 2003/04/17 22:26:00 tgl Exp $
 -->
 
  <chapter id="libpq">
@@ -193,7 +193,7 @@ PGconn *PQconnectdb(const char *conninfo);
      <term><literal>tty</literal></term>
      <listitem>
      <para>
-      A file or <acronym>TTY</acronym> for optional debug output from the server.
+      Ignored (formerly, this specified where to send server debug output).
      </para>
      </listitem>
     </varlistentry>
@@ -669,6 +669,9 @@ char *PQport(const PGconn *conn);
 <listitem>
 <para>
          Returns the debug <acronym>TTY</acronym> of the connection.
+        (This is obsolete, since the server no longer pays attention
+        to the <acronym>TTY</acronym> setting, but the function remains
+        for backwards compatibility.)
 <synopsis>
 char *PQtty(const PGconn *conn);
 </synopsis>
@@ -2365,12 +2368,6 @@ the <productname>PostgreSQL</productname> server.
 </listitem>
 <listitem>
 <para>
-<envar>PGTTY</envar> sets the file or <acronym>TTY</> on which  debugging  
-messages from the server are displayed.
-</para>
-</listitem>
-<listitem>
-<para>
 <envar>PGREQUIRESSL</envar> sets whether or not the connection must be
 made over <acronym>SSL</acronym>. If set to
 <quote>1</quote>, <application>libpq</>
@@ -2678,7 +2675,7 @@ main()
     pgport = NULL;              /* port of the backend server */
     pgoptions = NULL;           /* special options to start up the backend
                                  * server */
-    pgtty = NULL;               /* debugging tty for the backend server */
+    pgtty = NULL;               /* unused */
     dbName = "template1";
 
     /* make a connection to the database */
@@ -2826,7 +2823,7 @@ main()
     pgport = NULL;              /* port of the backend server */
     pgoptions = NULL;           /* special options to start up the backend
                                  * server */
-    pgtty = NULL;               /* debugging tty for the backend server */
+    pgtty = NULL;               /* unused */
     dbName = getenv("USER");    /* change this to the name of your test
                                  * database */
 
@@ -2950,7 +2947,7 @@ main()
     pgport = NULL;              /* port of the backend server */
     pgoptions = NULL;           /* special options to start up the backend
                                  * server */
-    pgtty = NULL;               /* debugging tty for the backend server */
+    pgtty = NULL;               /* unused */
 
     dbName = getenv("USER");    /* change this to the name of your test
                                  * database */
index e6edef6b6763dbf13c76e189fd652f25e94ca67b..5396cc47c18435b27eb275a9c7567daa0f238701 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.97 2003/02/14 14:05:00 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.98 2003/04/17 22:26:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,7 +29,6 @@
 #include "libpq/crypt.h"
 #include "libpq/hba.h"
 #include "libpq/libpq.h"
-#include "libpq/password.h"
 #include "libpq/pqcomm.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
@@ -378,7 +377,7 @@ auth_failed(Port *port, int status)
        }
 
        elog(FATAL, "%s authentication failed for user \"%s\"",
-                authmethod, port->user);
+                authmethod, port->user_name);
        /* doesn't return */
 }
 
@@ -427,7 +426,7 @@ ClientAuthentication(Port *port)
 
                                elog(FATAL,
                                        "No pg_hba.conf entry for host %s, user %s, database %s",
-                                       hostinfo, port->user, port->database);
+                                       hostinfo, port->user_name, port->database_name);
                                break;
                        }
 
@@ -638,10 +637,12 @@ CheckPAMAuth(Port *port, char *user, char *password)
                                                                                                                 * not allocated */
 
        /* Optionally, one can set the service name in pg_hba.conf */
-       if (port->auth_arg[0] == '\0')
-               retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@", &pam_passw_conv, &pamh);
+       if (port->auth_arg && port->auth_arg[0] != '\0')
+               retval = pam_start(port->auth_arg, "pgsql@",
+                                                  &pam_passw_conv, &pamh);
        else
-               retval = pam_start(port->auth_arg, "pgsql@", &pam_passw_conv, &pamh);
+               retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@",
+                                                  &pam_passw_conv, &pamh);
 
        if (retval != PAM_SUCCESS)
        {
@@ -741,7 +742,7 @@ recv_and_check_password_packet(Port *port)
        /* Do not echo password to logs, for security. */
        elog(DEBUG5, "received password packet");
 
-       result = md5_crypt_verify(port, port->user, buf.data);
+       result = md5_crypt_verify(port, port->user_name, buf.data);
 
        pfree(buf.data);
        return result;
index ac11ce98ef83c708fa74846c51519787417c7863..728d5eb0492fbee1765ab16a28a5f62a48f00c04 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/libpq/crypt.c,v 1.51 2002/12/05 18:52:42 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/crypt.c,v 1.52 2003/04/17 22:26:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -87,15 +87,19 @@ md5_crypt_verify(const Port *port, const char *user, char *client_pass)
                                /* pg_shadow plain, double-encrypt */
                                char       *crypt_pwd2 = palloc(MD5_PASSWD_LEN + 1);
 
-                               if (!EncryptMD5(shadow_pass, port->user, strlen(port->user),
+                               if (!EncryptMD5(shadow_pass,
+                                                               port->user_name,
+                                                               strlen(port->user_name),
                                                                crypt_pwd2))
                                {
                                        pfree(crypt_pwd);
                                        pfree(crypt_pwd2);
                                        return STATUS_ERROR;
                                }
-                               if (!EncryptMD5(crypt_pwd2 + strlen("md5"), port->md5Salt,
-                                                               sizeof(port->md5Salt), crypt_pwd))
+                               if (!EncryptMD5(crypt_pwd2 + strlen("md5"),
+                                                               port->md5Salt,
+                                                               sizeof(port->md5Salt),
+                                                               crypt_pwd))
                                {
                                        pfree(crypt_pwd);
                                        pfree(crypt_pwd2);
@@ -117,7 +121,9 @@ md5_crypt_verify(const Port *port, const char *user, char *client_pass)
                        {
                                /* Encrypt user-supplied password to match MD5 in pg_shadow */
                                crypt_client_pass = palloc(MD5_PASSWD_LEN + 1);
-                               if (!EncryptMD5(client_pass, port->user, strlen(port->user),
+                               if (!EncryptMD5(client_pass,
+                                                               port->user_name,
+                                                               strlen(port->user_name),
                                                                crypt_client_pass))
                                {
                                        pfree(crypt_client_pass);
index 2988ade443004e8f0736a620b6ad8d58a854af5e..f607167b6e82faf54cfe9c09066b294a3d8968d5 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.98 2003/04/13 04:07:17 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/libpq/hba.c,v 1.99 2003/04/17 22:26:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -471,15 +471,17 @@ check_db(char *dbname, char *user, char *param_str)
 
 /*
  *     Scan the rest of a host record (after the mask field)
- *     and return the interpretation of it as *userauth_p, auth_arg, and
+ *     and return the interpretation of it as *userauth_p, *auth_arg_p, and
  *     *error_p.  line points to the next token of the line.
  */
 static void
-parse_hba_auth(List *line, UserAuth *userauth_p, char *auth_arg,
+parse_hba_auth(List *line, UserAuth *userauth_p, char **auth_arg_p,
                           bool *error_p)
 {
        char       *token;
 
+       *auth_arg_p = NULL;
+
        if (!line)
                *error_p = true;
        else
@@ -514,11 +516,10 @@ parse_hba_auth(List *line, UserAuth *userauth_p, char *auth_arg,
        if (!*error_p)
        {
                /* Get the authentication argument token, if any */
-               if (!line)
-                       auth_arg[0] = '\0';
-               else
+               if (line)
                {
-                       StrNCpy(auth_arg, lfirst(line), MAX_AUTH_ARG - 1);
+                       token = lfirst(line);
+                       *auth_arg_p = pstrdup(token);
                        /* If there is more on the line, it is an error */
                        if (lnext(line))
                                *error_p = true;
@@ -570,7 +571,7 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
                        goto hba_syntax;
 
                /* Read the rest of the line. */
-               parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
+               parse_hba_auth(line, &port->auth_method, &port->auth_arg, error_p);
                if (*error_p)
                        goto hba_syntax;
 
@@ -642,7 +643,7 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
                line = lnext(line);
                if (!line)
                        goto hba_syntax;
-               parse_hba_auth(line, &port->auth_method, port->auth_arg, error_p);
+               parse_hba_auth(line, &port->auth_method, &port->auth_arg, error_p);
                if (*error_p)
                        goto hba_syntax;
 
@@ -654,9 +655,9 @@ parse_hba(List *line, hbaPort *port, bool *found_p, bool *error_p)
        else
                goto hba_syntax;
 
-       if (!check_db(port->database, port->user, db))
+       if (!check_db(port->database_name, port->user_name, db))
                return;
-       if (!check_user(port->user, user))
+       if (!check_user(port->user_name, user))
                return;
 
        /* Success */
@@ -946,7 +947,7 @@ check_ident_usermap(const char *usermap_name,
        bool            found_entry = false,
                                error = false;
 
-       if (usermap_name[0] == '\0')
+       if (usermap_name == NULL || usermap_name[0] == '\0')
        {
                elog(LOG, "check_ident_usermap: hba configuration file does not "
                   "have the usermap field filled in in the entry that pertains "
@@ -1387,7 +1388,7 @@ authident(hbaPort *port)
                        return STATUS_ERROR;
        }
 
-       if (check_ident_usermap(port->auth_arg, port->user, ident_user))
+       if (check_ident_usermap(port->auth_arg, port->user_name, ident_user))
                return STATUS_OK;
        else
                return STATUS_ERROR;
index 0221c64773d189385de92f644b58a579e642be92..e9df82a147668344b34fa11b2c32b30b7230baf8 100644 (file)
@@ -37,7 +37,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.310 2003/04/06 22:45:22 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.311 2003/04/17 22:26:01 tgl Exp $
  *
  * NOTES
  *
 #include "utils/memutils.h"
 #include "utils/ps_status.h"
 #include "bootstrap/bootstrap.h"
-
 #include "pgstat.h"
 
+
 #define INVALID_SOCK   (-1)
-#define ARGV_SIZE      64
 
 #ifdef HAVE_SIGPROCMASK
 sigset_t       UnBlockSig,
@@ -1114,10 +1113,11 @@ initMasks(fd_set *rmask, fd_set *wmask)
 static int
 ProcessStartupPacket(Port *port, bool SSLdone)
 {
-       StartupPacket *packet;
        enum CAC_state cac;
        int32           len;
        void       *buf;
+       ProtocolVersion proto;
+       MemoryContext oldcontext;
 
        if (pq_getbytes((char *) &len, 4) == EOF)
        {
@@ -1128,11 +1128,20 @@ ProcessStartupPacket(Port *port, bool SSLdone)
        len = ntohl(len);
        len -= 4;
 
-       if (len < sizeof(ProtocolVersion) || len > sizeof(StartupPacket))
+       if (len < (int32) sizeof(ProtocolVersion) ||
+               len > MAX_STARTUP_PACKET_LENGTH)
                elog(FATAL, "invalid length of startup packet");
 
-       /* Ensure we see zeroes for any bytes not sent */
-       buf = palloc0(sizeof(StartupPacket));
+       /*
+        * Allocate at least the size of an old-style startup packet, plus one
+        * extra byte, and make sure all are zeroes.  This ensures we will have
+        * null termination of all strings, in both fixed- and variable-length
+        * packet layouts.
+        */
+       if (len <= (int32) sizeof(StartupPacket))
+               buf = palloc0(sizeof(StartupPacket) + 1);
+       else
+               buf = palloc0(len + 1);
 
        if (pq_getbytes(buf, len) == EOF)
        {
@@ -1140,21 +1149,19 @@ ProcessStartupPacket(Port *port, bool SSLdone)
                return STATUS_ERROR;
        }
 
-       packet = buf;
-
        /*
         * The first field is either a protocol version number or a special
         * request code.
         */
-       port->proto = ntohl(packet->protoVersion);
+       port->proto = proto = ntohl(*((ProtocolVersion *) buf));
 
-       if (port->proto == CANCEL_REQUEST_CODE)
+       if (proto == CANCEL_REQUEST_CODE)
        {
-               processCancelRequest(port, packet);
+               processCancelRequest(port, buf);
                return 127;                             /* XXX */
        }
 
-       if (port->proto == NEGOTIATE_SSL_CODE && !SSLdone)
+       if (proto == NEGOTIATE_SSL_CODE && !SSLdone)
        {
                char            SSLok;
 
@@ -1187,39 +1194,113 @@ ProcessStartupPacket(Port *port, bool SSLdone)
 
        /* Check we can handle the protocol the frontend is using. */
 
-       if (PG_PROTOCOL_MAJOR(port->proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
-               PG_PROTOCOL_MAJOR(port->proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) ||
-               (PG_PROTOCOL_MAJOR(port->proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) &&
-                PG_PROTOCOL_MINOR(port->proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST)))
-               elog(FATAL, "unsupported frontend protocol");
+       if (PG_PROTOCOL_MAJOR(proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
+               PG_PROTOCOL_MAJOR(proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) ||
+               (PG_PROTOCOL_MAJOR(proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) &&
+                PG_PROTOCOL_MINOR(proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST)))
+               elog(FATAL, "unsupported frontend protocol %u.%u: server supports %u.0 to %u.%u",
+                        PG_PROTOCOL_MAJOR(proto), PG_PROTOCOL_MINOR(proto),
+                        PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST),
+                        PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST),
+                        PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST));
 
        /*
-        * Get the parameters from the startup packet as C strings.  The
-        * packet destination was cleared first so a short packet has zeros
-        * silently added.
+        * XXX temporary for 3.0 protocol development: we are using the minor
+        * number as a test-version number.  Insist it match exactly so people
+        * don't get burnt by using yesterday's libpq with today's server.
+        * XXX this must go away before release!!!
         */
-       StrNCpy(port->database, packet->database, sizeof(port->database));
-       StrNCpy(port->user, packet->user, sizeof(port->user));
-       StrNCpy(port->options, packet->options, sizeof(port->options));
-       StrNCpy(port->tty, packet->tty, sizeof(port->tty));
-
-       /* The database defaults to the user name. */
-       if (port->database[0] == '\0')
-               StrNCpy(port->database, packet->user, sizeof(port->database));
+       if (PG_PROTOCOL_MAJOR(proto) == 3 &&
+               PG_PROTOCOL_MINOR(proto) != PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST))
+               elog(FATAL, "Your development libpq is out of sync with the server");
 
        /*
-        * Truncate given database and user names to length of a Postgres
-        * name.  This avoids lookup failures when overlength names are given.
+        * Now fetch parameters out of startup packet and save them into the
+        * Port structure.  All data structures attached to the Port struct
+        * must be allocated in TopMemoryContext so that they won't disappear
+        * when we pass them to PostgresMain (see DoBackend).  We need not worry
+        * about leaking this storage on failure, since we aren't in the postmaster
+        * process anymore.
         */
-       if ((int) sizeof(port->database) >= NAMEDATALEN)
-               port->database[NAMEDATALEN - 1] = '\0';
-       if ((int) sizeof(port->user) >= NAMEDATALEN)
-               port->user[NAMEDATALEN - 1] = '\0';
+       oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+
+       if (PG_PROTOCOL_MAJOR(proto) >= 3)
+       {
+               int32   offset = sizeof(ProtocolVersion);
+
+               /*
+                * Scan packet body for name/option pairs.  We can assume any
+                * string beginning within the packet body is null-terminated,
+                * thanks to zeroing extra byte above.
+                */
+               port->guc_options = NIL;
+
+               while (offset < len)
+               {
+                       char   *nameptr = ((char *) buf) + offset;
+                       int32   valoffset;
+                       char   *valptr;
+
+                       if (*nameptr == '\0')
+                               break;                  /* found packet terminator */
+                       valoffset = offset + strlen(nameptr) + 1;
+                       if (valoffset >= len)
+                               break;                  /* missing value, will complain below */
+                       valptr = ((char *) buf) + valoffset;
+
+                       if (strcmp(nameptr, "database") == 0)
+                               port->database_name = pstrdup(valptr);
+                       else if (strcmp(nameptr, "user") == 0)
+                               port->user_name = pstrdup(valptr);
+                       else if (strcmp(nameptr, "options") == 0)
+                               port->cmdline_options = pstrdup(valptr);
+                       else
+                       {
+                               /* Assume it's a generic GUC option */
+                               port->guc_options = lappend(port->guc_options,
+                                                                                       pstrdup(nameptr));
+                               port->guc_options = lappend(port->guc_options,
+                                                                                       pstrdup(valptr));
+                       }
+                       offset = valoffset + strlen(valptr) + 1;
+               }
+               /*
+                * If we didn't find a packet terminator exactly at the end of the
+                * given packet length, complain.
+                */
+               if (offset != len-1)
+                       elog(FATAL, "invalid startup packet layout: expected terminator as last byte");
+       }
+       else
+       {
+               /*
+                * Get the parameters from the old-style, fixed-width-fields startup
+                * packet as C strings.  The packet destination was cleared first so a
+                * short packet has zeros silently added.  We have to be prepared to
+                * truncate the pstrdup result for oversize fields, though.
+                */
+               StartupPacket *packet = (StartupPacket *) buf;
+
+               port->database_name = pstrdup(packet->database);
+               if (strlen(port->database_name) > sizeof(packet->database))
+                       port->database_name[sizeof(packet->database)] = '\0';
+               port->user_name = pstrdup(packet->user);
+               if (strlen(port->user_name) > sizeof(packet->user))
+                       port->user_name[sizeof(packet->user)] = '\0';
+               port->cmdline_options = pstrdup(packet->options);
+               if (strlen(port->cmdline_options) > sizeof(packet->options))
+                       port->cmdline_options[sizeof(packet->options)] = '\0';
+               port->guc_options = NIL;
+       }
 
        /* Check a user name was given. */
-       if (port->user[0] == '\0')
+       if (port->user_name == NULL || port->user_name[0] == '\0')
                elog(FATAL, "no PostgreSQL user name specified in startup packet");
 
+       /* The database defaults to the user name. */
+       if (port->database_name == NULL || port->database_name[0] == '\0')
+               port->database_name = pstrdup(port->user_name);
+
        if (Db_user_namespace)
        {
                /*
@@ -1228,19 +1309,35 @@ ProcessStartupPacket(Port *port, bool SSLdone)
                 * string or they may fake as a local user of another database
                 * attaching to this database.
                 */
-               if (strchr(port->user, '@') == port->user + strlen(port->user) - 1)
-                       *strchr(port->user, '@') = '\0';
+               if (strchr(port->user_name, '@') ==
+                       port->user_name + strlen(port->user_name) - 1)
+                       *strchr(port->user_name, '@') = '\0';
                else
                {
                        /* Append '@' and dbname */
-                       char            hold_user[SM_DATABASE_USER + 1];
+                       char       *db_user;
 
-                       snprintf(hold_user, SM_DATABASE_USER + 1, "%s@%s", port->user,
-                                        port->database);
-                       strcpy(port->user, hold_user);
+                       db_user = palloc(strlen(port->user_name) +
+                                                        strlen(port->database_name) + 2);
+                       sprintf(db_user, "%s@%s", port->user_name, port->database_name);
+                       port->user_name = db_user;
                }
        }
 
+       /*
+        * Truncate given database and user names to length of a Postgres
+        * name.  This avoids lookup failures when overlength names are given.
+        */
+       if (strlen(port->database_name) >= NAMEDATALEN)
+               port->database_name[NAMEDATALEN - 1] = '\0';
+       if (strlen(port->user_name) >= NAMEDATALEN)
+               port->user_name[NAMEDATALEN - 1] = '\0';
+
+       /*
+        * Done putting stuff in TopMemoryContext.
+        */
+       MemoryContextSwitchTo(oldcontext);
+
        /*
         * If we're going to reject the connection due to database state, say
         * so now instead of wasting cycles on an authentication exchange.
@@ -2076,13 +2173,11 @@ static int
 DoBackend(Port *port)
 {
        char       *remote_host;
-       char       *av[ARGV_SIZE * 2];
-       int                     ac = 0;
-       char            debugbuf[ARGV_SIZE];
-       char            protobuf[ARGV_SIZE];
-       char            dbbuf[ARGV_SIZE];
-       char            optbuf[ARGV_SIZE];
-       char            ttybuf[ARGV_SIZE];
+       char      **av;
+       int                     maxac;
+       int                     ac;
+       char            debugbuf[32];
+       char            protobuf[32];
        int                     i;
        int                     status;
        struct timeval now;
@@ -2225,7 +2320,7 @@ DoBackend(Port *port)
         * title for ps.  It's good to do this as early as possible in
         * startup.
         */
-       init_ps_display(port->user, port->database, remote_host);
+       init_ps_display(port->user_name, port->database_name, remote_host);
        set_ps_display("authentication");
 
        /*
@@ -2243,7 +2338,7 @@ DoBackend(Port *port)
 
        if (Log_connections)
                elog(LOG, "connection authorized: user=%s database=%s",
-                        port->user, port->database);
+                        port->user_name, port->database_name);
 
        /*
         * Don't want backend to be able to see the postmaster random number
@@ -2260,8 +2355,20 @@ DoBackend(Port *port)
         * The layout of the command line is
         *              postgres [secure switches] -p databasename [insecure switches]
         * where the switches after -p come from the client request.
+        *
+        * The maximum possible number of commandline arguments that could come
+        * from ExtraOptions or port->cmdline_options is (strlen + 1) / 2; see
+        * split_opts().
         * ----------------
         */
+       maxac = 10;                                     /* for fixed args supplied below */
+       maxac += (strlen(ExtraOptions) + 1) / 2;
+       if (port->cmdline_options)
+               maxac += (strlen(port->cmdline_options) + 1) / 2;
+
+       av = (char **) MemoryContextAlloc(TopMemoryContext,
+                                                                         maxac * sizeof(char *));
+       ac = 0;
 
        av[ac++] = "postgres";
 
@@ -2270,7 +2377,7 @@ DoBackend(Port *port)
         */
        if (debug_flag > 0)
        {
-               sprintf(debugbuf, "-d%d", debug_flag);
+               snprintf(debugbuf, sizeof(debugbuf), "-d%d", debug_flag);
                av[ac++] = debugbuf;
        }
 
@@ -2283,7 +2390,7 @@ DoBackend(Port *port)
        split_opts(av, &ac, ExtraOptions);
 
        /* Tell the backend what protocol the frontend is using. */
-       sprintf(protobuf, "-v%u", port->proto);
+       snprintf(protobuf, sizeof(protobuf), "-v%u", port->proto);
        av[ac++] = protobuf;
 
        /*
@@ -2291,38 +2398,25 @@ DoBackend(Port *port)
         * database to use.  -p marks the end of secure switches.
         */
        av[ac++] = "-p";
-
-       StrNCpy(dbbuf, port->database, ARGV_SIZE);
-       av[ac++] = dbbuf;
+       av[ac++] = port->database_name;
 
        /*
         * Pass the (insecure) option switches from the connection request.
+        * (It's OK to mangle port->cmdline_options now.)
         */
-       StrNCpy(optbuf, port->options, ARGV_SIZE);
-       split_opts(av, &ac, optbuf);
-
-       /*
-        * Pass the (insecure) debug output file request.
-        *
-        * NOTE: currently, this is useless code, since the backend will not
-        * honor an insecure -o switch.  I left it here since the backend
-        * could be modified to allow insecure -o, given adequate checking
-        * that the specified filename is something safe to write on.
-        */
-       if (port->tty[0])
-       {
-               StrNCpy(ttybuf, port->tty, ARGV_SIZE);
-               av[ac++] = "-o";
-               av[ac++] = ttybuf;
-       }
+       if (port->cmdline_options)
+               split_opts(av, &ac, port->cmdline_options);
 
        av[ac] = (char *) NULL;
 
+       Assert(ac < maxac);
+
        /*
         * Release postmaster's working memory context so that backend can
         * recycle the space.  Note this does not trash *MyProcPort, because
         * ConnCreate() allocated that space with malloc() ... else we'd need
-        * to copy the Port data here.
+        * to copy the Port data here.  Also, subsidiary data such as the
+        * username isn't lost either; see ProcessStartupPacket().
         */
        MemoryContextSwitchTo(TopMemoryContext);
        MemoryContextDelete(PostmasterContext);
@@ -2339,7 +2433,7 @@ DoBackend(Port *port)
        ClientAuthInProgress = false;           /* client_min_messages is active
                                                                                 * now */
 
-       return (PostgresMain(ac, av, port->user));
+       return (PostgresMain(ac, av, port->user_name));
 }
 
 /*
@@ -2578,11 +2672,10 @@ SSDataBase(int xlop)
        if ((pid = fork()) == 0)        /* child */
        {
                const char *statmsg;
-               char       *av[ARGV_SIZE * 2];
+               char       *av[10];
                int                     ac = 0;
-               char            nbbuf[ARGV_SIZE];
-               char            dbbuf[ARGV_SIZE];
-               char            xlbuf[ARGV_SIZE];
+               char            nbbuf[32];
+               char            xlbuf[32];
 
 #ifdef LINUX_PROFILE
                setitimer(ITIMER_PROF, &prof_itimer, NULL);
@@ -2626,19 +2719,19 @@ SSDataBase(int xlop)
                /* Set up command-line arguments for subprocess */
                av[ac++] = "postgres";
 
-               sprintf(nbbuf, "-B%d", NBuffers);
+               snprintf(nbbuf, sizeof(nbbuf), "-B%d", NBuffers);
                av[ac++] = nbbuf;
 
-               sprintf(xlbuf, "-x%d", xlop);
+               snprintf(xlbuf, sizeof(xlbuf), "-x%d", xlop);
                av[ac++] = xlbuf;
 
                av[ac++] = "-p";
-
-               StrNCpy(dbbuf, "template1", ARGV_SIZE);
-               av[ac++] = dbbuf;
+               av[ac++] = "template1";
 
                av[ac] = (char *) NULL;
 
+               Assert(ac < lengthof(av));
+
                BootstrapMain(ac, av);
                ExitPostmaster(0);
        }
index bbfa4695a3b534078f8db537e3ff0fedcbb0cf54..1048d2fa1c6816386299b49df1ddb69ba56593d6 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.320 2003/03/24 18:33:52 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.321 2003/04/17 22:26:01 tgl Exp $
  *
  * NOTES
  *       this is the "main" module of the postgres backend and
@@ -1611,6 +1611,26 @@ PostgresMain(int argc, char *argv[], const char *username)
        if (debug_flag >= 5)
                SetConfigOption("debug_print_rewritten", "true", ctx, gucsource);
 
+       /*
+        * Process any additional GUC variable settings passed in startup packet.
+        */
+       if (MyProcPort != NULL)
+       {
+               List   *gucopts = MyProcPort->guc_options;
+
+               while (gucopts)
+               {
+                       char       *name,
+                                          *value;
+
+                       name = lfirst(gucopts);
+                       gucopts = lnext(gucopts);
+                       value = lfirst(gucopts);
+                       gucopts = lnext(gucopts);
+                       SetConfigOption(name, value, PGC_BACKEND, PGC_S_CLIENT);
+               }
+       }
+
        /*
         * Post-processing for command line options.
         */
@@ -1795,7 +1815,7 @@ PostgresMain(int argc, char *argv[], const char *username)
        if (!IsUnderPostmaster)
        {
                puts("\nPOSTGRES backend interactive interface ");
-               puts("$Revision: 1.320 $ $Date: 2003/03/24 18:33:52 $\n");
+               puts("$Revision: 1.321 $ $Date: 2003/04/17 22:26:01 $\n");
        }
 
        /*
index 4418adb5c9bfe0b32feababa252c88d3f8dda039..f99e6d03739e3a4726f3031a9d61c91347828d67 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: auth.h,v 1.21 2002/06/20 20:29:49 momjian Exp $
+ * $Id: auth.h,v 1.22 2003/04/17 22:26:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,7 +21,7 @@
  *----------------------------------------------------------------
  */
 
-void           ClientAuthentication(Port *port);
+extern void ClientAuthentication(Port *port);
 
 #define PG_KRB4_VERSION "PGVER4.1"             /* at most KRB_SENDAUTH_VLEN chars */
 #define PG_KRB5_VERSION "PGVER5.1"
index b9daf985f5c47996c0083818dd4d0af39c7201e5..9a7e355ff3f6ce61b66c198777de6e2fb5e37c65 100644 (file)
@@ -4,7 +4,7 @@
  *       Interface to hba.c
  *
  *
- * $Id: hba.h,v 1.32 2002/04/04 04:25:54 momjian Exp $
+ * $Id: hba.h,v 1.33 2003/04/17 22:26:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,8 +26,6 @@
 #define IDENT_PORT 113
  /* Standard TCP port number for Ident service.  Assigned by IANA */
 
-#define MAX_AUTH_ARG   80              /* Max size of an authentication arg */
-
 typedef enum UserAuth
 {
        uaReject,
index e9d906d06a835a1ee544d569704a9f5d47694230..19ac0402d38f3328192fa96e3e5718b8c24405bb 100644 (file)
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq-be.h,v 1.34 2002/08/29 03:22:01 tgl Exp $
+ * $Id: libpq-be.h,v 1.35 2003/04/17 22:26:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef LIBPQ_BE_H
 #define LIBPQ_BE_H
 
-#include <sys/types.h>
-
 #include "libpq/hba.h"
 #include "libpq/pqcomm.h"
 
 /*
  * This is used by the postmaster in its communication with frontends. It
  * contains all state information needed during this communication before the
- * backend is run.
+ * backend is run.  The Port structure is kept in malloc'd memory and is
+ * still available when a backend is running (see MyProcPort).  The data
+ * it points to must also be malloc'd, or else palloc'd in TopMemoryContext,
+ * so that it survives into PostgresMain execution!
  */
 
 typedef struct Port
 {
        int                     sock;                   /* File descriptor */
+       ProtocolVersion proto;          /* FE/BE protocol version */
        SockAddr        laddr;                  /* local addr (postmaster) */
        SockAddr        raddr;                  /* remote addr (client) */
-       char            md5Salt[4];             /* Password salt */
-       char            cryptSalt[2];   /* Password salt */
 
        /*
-        * Information that needs to be held during the fe/be authentication
-        * handshake.
+        * Information that needs to be saved from the startup packet and passed
+        * into backend execution.  "char *" fields are NULL if not set.
+        * guc_options points to a List of alternating option names and values.
         */
+       char       *database_name;
+       char       *user_name;
+       char       *cmdline_options;
+       List       *guc_options;
 
-       ProtocolVersion proto;
-       char            database[SM_DATABASE + 1];
-       char            user[SM_DATABASE_USER + 1];
-       char            options[SM_OPTIONS + 1];
-       char            tty[SM_TTY + 1];
-       char            auth_arg[MAX_AUTH_ARG];
+       /*
+        * Information that needs to be held during the authentication cycle.
+        */
        UserAuth        auth_method;
+       char       *auth_arg;
+       char            md5Salt[4];             /* Password salt */
+       char            cryptSalt[2];   /* Password salt */
 
        /*
         * SSL structures
diff --git a/src/include/libpq/password.h b/src/include/libpq/password.h
deleted file mode 100644 (file)
index c704ede..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef PASSWORD_H
-#define PASSWORD_H
-
-int                    verify_password(const Port *port, const char *user, const char *password);
-
-#endif
index 7c476699f0648be2884d05425758792105a7d91b..fabfb0cb2534d311788df194dab7a0f998d72dd9 100644 (file)
@@ -9,14 +9,13 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pqcomm.h,v 1.75 2003/01/06 09:58:36 petere Exp $
+ * $Id: pqcomm.h,v 1.76 2003/04/17 22:26:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef PQCOMM_H
 #define PQCOMM_H
 
-#include <sys/types.h>
 #ifdef WIN32
 #include <winsock.h>
 /* workaround for clashing defines of "ERROR" */
@@ -93,7 +92,7 @@ typedef union SockAddr
  * functionality).
  *
  * If a backend supports version m.n of the protocol it must actually support
- * versions m.0..n].  Backend support for version m-1 can be dropped after a
+ * versions m.[0..n].  Backend support for version m-1 can be dropped after a
  * `reasonable' length of time.
  *
  * A frontend isn't required to support anything other than the current
@@ -107,27 +106,26 @@ typedef union SockAddr
 /* The earliest and latest frontend/backend protocol version supported. */
 
 #define PG_PROTOCOL_EARLIEST   PG_PROTOCOL(1,0)
-#define PG_PROTOCOL_LATEST     PG_PROTOCOL(2,0)
+#define PG_PROTOCOL_LATEST             PG_PROTOCOL(3,100) /* XXX temporary value */
 
-/*
- * All packets sent to the postmaster start with the length.  This is omitted
- * from the different packet definitions specified below.
- */
+typedef uint32 ProtocolVersion; /* FE/BE protocol version number */
 
-typedef uint32 PacketLen;
+typedef ProtocolVersion MsgType;
 
 
 /*
- * Startup message parameters sizes.  These must not be changed without changing
- * the protocol version.  These are all strings that are '\0' terminated only if
- * there is room.
+ * Packet lengths are 4 bytes in network byte order.
+ *
+ * The initial length is omitted from the packet layouts appearing below.
  */
 
+typedef uint32 PacketLen;
+
+
 /*
- * FIXME: remove the fixed size limitations on the database name, user
- * name, and options fields and use a variable length field instead. The
- * actual limits on database & user name will then be NAMEDATALEN, which
- * can be changed without changing the FE/BE protocol. -neilc,2002/08/27
+ * Old-style startup packet layout with fixed-width fields.  This is used in
+ * protocol 1.0 and 2.0, but not in later versions.  Note that the fields
+ * in this layout are '\0' terminated only if there is room.
  */
 
 #define SM_DATABASE            64
@@ -138,11 +136,6 @@ typedef uint32 PacketLen;
 #define SM_UNUSED              64
 #define SM_TTY                 64
 
-typedef uint32 ProtocolVersion; /* Fe/Be protocol version number */
-
-typedef ProtocolVersion MsgType;
-
-
 typedef struct StartupPacket
 {
        ProtocolVersion protoVersion;           /* Protocol version */
@@ -156,7 +149,16 @@ typedef struct StartupPacket
 
 extern bool Db_user_namespace;
 
-/* These are the authentication requests sent by the backend. */
+/*
+ * In protocol 3.0 and later, the startup packet length is not fixed, but
+ * we set an arbitrary limit on it anyway.  This is just to prevent simple
+ * denial-of-service attacks via sending enough data to run the server
+ * out of memory.
+ */
+#define MAX_STARTUP_PACKET_LENGTH 10000
+
+
+/* These are the authentication request codes sent by the backend. */
 
 #define AUTH_REQ_OK                    0       /* User is authenticated  */
 #define AUTH_REQ_KRB4          1       /* Kerberos V4 */
@@ -169,12 +171,12 @@ extern bool Db_user_namespace;
 typedef uint32 AuthRequest;
 
 
-/* A client can also send a cancel-current-operation request to the postmaster.
+/*
+ * A client can also send a cancel-current-operation request to the postmaster.
  * This is uglier than sending it directly to the client's backend, but it
  * avoids depending on out-of-band communication facilities.
- */
-
-/* The cancel request code must not match any protocol version number
+ *
+ * The cancel request code must not match any protocol version number
  * we're ever likely to use.  This random choice should do.
  */
 #define CANCEL_REQUEST_CODE PG_PROTOCOL(1234,5678)
index 4ac99fd76ab9b641c7752a197fdabeb8212ddb5e..fca2d2e3035d8d665348cce360d37e0f8f9eb8de 100644 (file)
@@ -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;
index f2e509e6246eff3ad5f85e17904e16b3f47e68be..9f5c8714a68caa9b90bb9f2181b31f1377da5a12 100644 (file)
@@ -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;
index cde2b2d2b05f2ff0ee61537a5b6d9a7c6073c5ec..43c3bd11c56864e088f237ca3671301fe71da499 100644 (file)
@@ -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 === */
 
index ed16ada224f82f12f55ad60b24cf06f75f4e308c..32120be5412dfecd4a80272a7e2c3d885e7f57a9 100644 (file)
@@ -1,7 +1,7 @@
 --
 -- INTERVAL
 --
-SET DATESTYLE = DEFAULT;
+SET DATESTYLE = 'ISO';
 -- check acceptance of "time zone style"
 SELECT INTERVAL '01:00' AS "One hour";
  One hour 
index aa14cd3b094ae792c81b9ad34697b7a68281aaa5..b6a6eb20ef30b1986beb00bf60c29f944ab0c670 100644 (file)
@@ -2,7 +2,7 @@
 -- INTERVAL
 --
 
-SET DATESTYLE = DEFAULT;
+SET DATESTYLE = 'ISO';
 
 -- check acceptance of "time zone style"
 SELECT INTERVAL '01:00' AS "One hour";