First phase of FE/BE protocol modifications: new StartupPacket layout
authorTom Lane
Thu, 17 Apr 2003 22:26:02 +0000 (22:26 +0000)
committerTom Lane
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 @@
 
 
  
@@ -193,7 +193,7 @@ PGconn *PQconnectdb(const char *conninfo);
      tty
      
      
-      A file or TTY for optional debug output from the server.
+      Ignored (formerly, this specified where to send server debug output).
      
      
     
@@ -669,6 +669,9 @@ char *PQport(const PGconn *conn);
 
 
          Returns the debug TTY of the connection.
+    (This is obsolete, since the server no longer pays attention
+    to the TTY setting, but the function remains
+    for backwards compatibility.)
 
 char *PQtty(const PGconn *conn);
 
@@ -2365,12 +2368,6 @@ the PostgreSQL server.
 
 
 
-PGTTY sets the file or TTY on which  debugging  
-messages from the server are displayed.
-
-
-
-
 PGREQUIRESSL sets whether or not the connection must be
 made over SSL. If set to
 1libpq
@@ -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 
-
 #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 
 #ifdef WIN32
 #include 
 /* 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  argument
- *                is NULL or a null string.
+ *                is NULL or a null string.  (No longer used by backend.)
  *
  *   PGOPTIONS    identifies connection options if  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";