]> granicus.if.org Git - postgresql/commitdiff
This patch (against the current CVS sources) adds to libpq the functions
authorBruce Momjian <bruce@momjian.us>
Tue, 30 Nov 1999 03:08:19 +0000 (03:08 +0000)
committerBruce Momjian <bruce@momjian.us>
Tue, 30 Nov 1999 03:08:19 +0000 (03:08 +0000)
PQconnectStart
PQconnectPoll
PQresetStart
PQresetPoll
PQsetenvStart
PQsetenvPoll
PQsetenvAbort

and brings into the published interface

PQsetenv.

The first four are asynchronous analogues of PQconnectdb and PQreset -
they allow an application to connect to the DB without blocking on
remote I/O.

The PQsetenv functions perform an environment negotiation with the
server.

Internal to libpq, pqReadReady and pqWriteReady have been made available
across the library (they were previously static functions inside
fe-misc.c).  A lot of internal rearrangement has been necessary to
support these changes.

The API documentation has been updated also.

Caveats:

  o  The Windows code does not default to using non-blocking sockets,
since I have no documentation: Define WIN32_NON_BLOCKING_CONNECTIONS to
do that.

  o  The SSL code still blocks.

Ewan Mellor.

doc/src/sgml/libpq.sgml
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/fe-misc.c
src/interfaces/libpq/libpq-fe.h
src/interfaces/libpq/libpq-int.h

index 8f926922241e84c98fabc07053f6a51f91b48848..560feffadc988300919c1e05af992660301380ea 100644 (file)
@@ -57,14 +57,13 @@ header file <FileName>libpq-fe.h</FileName> and must link with the
 <synopsis>
 PGconn *PQconnectdb(const char *conninfo)
 </synopsis>
-   This routine opens a new database connection using the parameters
-   taken from the string <literal>conninfo</literal>.  Unlike PQsetdbLogin()
-   below, the parameter set
-   can be extended without changing the function signature, so use
-   of this routine is prefered for application programming.  The passed
-   string can be empty to use all default
-   parameters, or it can contain one or more parameter settings
-   separated by whitespace. 
+   This routine opens a new database connection using the parameters taken
+   from the string <literal>conninfo</literal>.  Unlike PQsetdbLogin() below,
+   the parameter set can be extended without changing the function signature,
+   so use either of this routine or the non-blocking analogues PQconnectStart
+   / PQconnectPoll is prefered for application programming.  The passed string
+   can be empty to use all default parameters, or it can contain one or more
+   parameter settings separated by whitespace.
    </Para>
    <Para>
    Each parameter setting is in the form <literal>keyword = value</literal>.
@@ -80,8 +79,38 @@ PGconn *PQconnectdb(const char *conninfo)
      <term><literal>host</literal></term>
      <ListItem>
      <Para>
-      Host to connect to. If a non-zero-length string is specified, TCP/IP communication is used.
-      Without a host name, libpq will connect using a local Unix domain socket.
+      Name of host to connect to. If a non-zero-length string is specified, TCP/IP
+      communication is used.  Using this parameter causes a hostname look-up.
+      See hostaddr.
+     </Para>
+     </ListItem>
+    </VarListEntry>
+
+    <VarListEntry>
+     <term><literal>hostaddr</literal></term>
+     <ListItem>
+     <Para>
+      IP address of host to connect to. This should be in standard
+      numbers-and-dots form, as used by the BSD functions inet_aton et al. If
+      a non-zero-length string is specified, TCP/IP communication is used.
+     </Para>
+     <Para>
+      Using hostaddr instead of host allows the application to avoid a host
+      name look-up, which may be important in applications with time
+      constraints. However, Kerberos authentication requires the host
+      name. The following therefore applies. If host is specified without
+      hostaddr, a hostname look-up is forced. If hostaddr is specified without
+      host, the value for hostaddr gives the remote address; if Kerberos is
+      used, this causes a reverse name query. If both host and hostaddr are
+      specified, the value for hostaddr gives the remote address; the value
+      for host is ignored, unless Kerberos is used, in which case that value
+      is used for Kerberos authentication. Note that authentication is likely
+      to fail if libpq is passed a host name which is not the name of the
+      machine at hostaddr.
+     </Para>
+     <Para>
+      Without both a host name and host address, libpq will connect using a
+      local Unix domain socket.
      </Para>
      </ListItem>
     </VarListEntry>
@@ -149,6 +178,9 @@ PGconn *PQconnectdb(const char *conninfo)
    The return value is a pointer to an abstract struct
    representing the connection to the backend.
    </Para>
+   <Para>
+    This function is not thread-safe.
+   </Para>
   </ListItem>
 
   <ListItem>
@@ -167,6 +199,9 @@ PGconn *PQsetdbLogin(const char *pghost,
    This is the predecessor of <function>PQconnectdb</function> with a fixed number
    of parameters but the same functionality.   
    </Para>
+   <Para>
+    This function is not thread-safe.
+   </Para>
   </ListItem>
 
   <ListItem>
@@ -185,6 +220,173 @@ PGconn *PQsetdb(char *pghost,
    </Para>
   </ListItem>
 
+ <ListItem>
+  <Para>
+   <Function>PQconnectStart</Function>
+   <Function>PQconnectPoll</Function>
+   Make a connection to the database server in a non-blocking manner.
+<synopsis>
+PGconn *PQconnectStart(const char *conninfo)
+</synopsis>
+<synopsis>
+PostgresPollingStatusType *PQconnectPoll(PQconn *conn)
+</synopsis>
+   These two routines are used to open a connection to a database server such
+   that your application's thread of execution is not blocked on remote I/O
+   whilst doing so.
+  </Para>
+  <Para>
+   The database connection is made using the parameters taken from the string
+   <literal>conninfo</literal>, passed to PQconnectStart. This string is in
+   the same format as described above for PQconnectdb.
+  </Para>
+  <Para>
+   Neither PQconnectStart nor PQconnectPoll will block, as long as a number of
+   restrictions are met:
+   <ItemizedList>
+    <ListItem>
+     <Para>
+      The hostaddr and host parameters are used appropriately to ensure that
+      name and reverse name queries are not made. See the documentation of
+      these parameters under PQconnectdb above for details.
+     </Para>
+    </ListItem>
+
+    <ListItem>
+     <Para>
+      If you call PQtrace, ensure that the stream object into which you trace
+      will not block.
+     </Para>
+    </ListItem>
+
+    <ListItem>
+     <Para>
+      You ensure for yourself that the socket is in the appropriate state
+      before calling PQconnectPoll, as described below.
+     </Para>
+    </ListItem>
+   </ItemizedList>
+  </Para>
+
+  <Para>
+   To begin, call conn=PQconnectStart("&lt;connection_info_string&gt;"). If
+   conn is NULL, then libpq has been unable to allocate a new PGconn
+   structure. Otherwise, a valid PGconn pointer is returned (though not yet
+   representing a valid connection to the database). On return from
+   PQconnectStart, call status=PQstatus(conn). If status equals
+   CONNECTION_BAD, PQconnectStart has failed.
+  </Para>
+  <Para>
+   If PQconnectStart succeeds, the next stage is to poll libpq so that it may
+   proceed with the connection sequence.  Loop thus: Consider a connection
+   'inactive' by default. If PQconnectPoll last returned PGRES_POLLING_ACTIVE,
+   consider it 'active' instead. If PQconnectPoll(conn) last returned
+   PGRES_POLLING_READING, perform a select for reading on PQsocket(conn). If
+   it last returned PGRES_POLLING_WRITING, perform a select for writing on
+   PQsocket(conn). If you have yet to call PQconnectPoll, i.e. after the call
+   to PQconnectStart, behave as if it last returned PGRES_POLLING_WRITING.  If
+   the select shows that the socket is ready, consider it 'active'. If it has
+   been decided that this connection is 'active', call PQconnectPoll(conn)
+   again. If this call returns PGRES_POLLING_FAILED, the connection procedure
+   has failed.  If this call returns PGRES_POLLING_OK, the connection has been
+   successfully made.
+  </Para>
+  <Para>
+    Note that the use of select() to ensure that the socket is ready is merely
+    a (likely) example; those with other facilities available, such as a
+    poll() call, may of course use that instead.
+  </Para>
+  <Para>
+    At any time during connection, the status of the connection may be
+    checked, by calling PQstatus. If this is CONNECTION_BAD, then the
+    connection procedure has failed; if this is CONNECTION_OK, then the
+    connection is ready.  Either of these states should be equally detectable
+    from the return value of PQconnectPoll, as above. Other states may be
+    shown during (and only during) an asynchronous connection procedure. These
+    indicate the current stage of the connection procedure, and may be useful
+    to provide feedback to the user for example. These statuses may include:
+    <ItemizedList>
+     <ListItem>
+      <Para>
+      CONNECTION_STARTED: Waiting for connection to be made.
+      </Para>
+     </ListItem>
+     <ListItem>
+      <Para>
+      CONNECTION_MADE: Connection OK; waiting to send.
+      </Para>
+     </ListItem>
+     <ListItem>
+      <Para>
+      CONNECTION_AWAITING_RESPONSE: Waiting for a response from the backend.
+      </Para>
+     </ListItem>
+     <ListItem>
+      <Para>
+      CONNECTION_AUTH_RESPONSE: Got an authentication response; about to deal
+      with it.
+      </Para>
+     </ListItem>
+     <ListItem>
+      <Para>
+      CONNECTION_ERROR_RESPONSE: Got an error response; about to deal with it.
+      </Para>
+     </ListItem>
+     <ListItem>
+      <Para>
+      CONNECTION_AUTH_OK: Received authentication; waiting for ReadyForQuery etc.
+      </Para>
+     </ListItem>
+     <ListItem>
+      <Para>
+      CONNECTION_SETENV: Negotiating environment.
+      </Para>
+     </ListItem>
+    </ItemizedList>
+
+    Note that, although these constants will remain (in order to maintain
+    compatibility) an application should never rely upon these appearing in a
+    particular order, or at all, or on the status always being one of these
+    documented values. An application may do something like this:
+<ProgramListing>
+    switch(PQstatus(conn))
+    {
+        case CONNECTION_STARTED:
+            feedback = "Connecting...";
+           break;
+
+        case CONNECTION_MADE:
+            feedback = "Connected to server...";
+            break;
+.
+.
+.
+        default:
+           feedback = "Connecting...";
+    }
+</ProgramListing>
+  </Para>
+  <Para>
+   Note that if PQconnectStart returns a non-NULL pointer, you must call
+   PQfinish upon that, when you are finished with it, in order to dispose of
+   the structure and any associated memory blocks. This must be done even if a
+   call to PQconnectStart or PQconnectPoll failed.
+  </Para>
+  <Para>
+   PQconnectPoll will currently block if libpq is compiled with USE_SSL
+   defined. This restriction may be removed in the future.
+  </Para>
+  <Para>
+   PQconnectPoll will currently block under Windows, unless libpq is compiled
+   with WIN32_NON_BLOCKING_CONNECTIONS defined. This code has not yet been
+   tested under Windows, and so it is currently off by default. This may be
+   changed in the future.
+  </Para>
+  <Para>
+   These functions are not thread-safe.
+  </Para>
+ </ListItem>
+
   <ListItem>
    <Para>
    <Function>PQconndefaults</Function> Returns the default connection options.
@@ -215,6 +417,9 @@ struct PQconninfoOption
    will depend on environment variables and other context.
    Callers must treat the connection options data as read-only.
    </Para>
+   <Para>
+    This function is not thread-safe.
+   </Para>
   </ListItem>
 
   <ListItem>
@@ -247,6 +452,31 @@ void PQreset(PGconn *conn)
    </Para>
   </ListItem>
 
+  <ListItem>
+   <Para>
+   <Function>PQresetStart</Function>
+   <Function>PQresetPoll</Function>
+   Reset the communication  port  with  the  backend, in a non-blocking manner.
+<synopsis>
+int PQresetStart(PGconn *conn);
+</synopsis>
+<synopsis>
+PostgresPollingStatusType PQresetPoll(PGconn *conn);
+</synopsis>
+    These functions will close the connection to the backend and attempt to
+    reestablish a new connection to the same postmaster, using all the same
+    parameters previously used. This may be useful for error recovery if a
+    working connection is lost. They differ from PQreset (above) in that they
+    act in a non-blocking manner. These functions suffer from the same
+    restrictions as PQconnectStart and PQconnectPoll.
+   </Para>
+   <Para>
+    Call PQresetStart. If it returns 0, the reset has failed. If it returns 1,
+    poll the reset using PQresetPoll in exactly the same way as you would
+    create the connection using PQconnectPoll.
+   </Para>
+  </ListItem>
+
  </ItemizedList>
 </Para>
 
@@ -338,19 +568,25 @@ const char *PQoptions(const PGconn *conn)
 <Para>
 <Function>PQstatus</Function>
          Returns the status of the connection. 
-         The status can be <literal>CONNECTION_OK</literal> or <literal>CONNECTION_BAD</literal>.
 <synopsis>
 ConnStatusType PQstatus(const PGconn *conn)
 </synopsis>
 </Para>
 
 <Para>
-A failed connection attempt is signaled by status <literal>CONNECTION_BAD</literal>.
-Ordinarily, an OK status will remain so until <function>PQfinish</function>, but a
+The status can be one of a number of values. However, only two of these are
+seen outside of an asynchronous connection procedure -
+<literal>CONNECTION_OK</literal> or <literal>CONNECTION_BAD</literal>. A good
+connection to the database has the status CONNECTION_OK. A failed connection
+attempt is signaled by status <literal>CONNECTION_BAD</literal>.  Ordinarily,
+an OK status will remain so until <function>PQfinish</function>, but a
 communications failure might result in the status changing to
-<literal>CONNECTION_BAD</literal> prematurely.  In that case the application could
-try to recover by calling <function>PQreset</function>.
+<literal>CONNECTION_BAD</literal> prematurely.  In that case the application
+could try to recover by calling <function>PQreset</function>.
 </Para>
+<Para>
+See the entry for PQconnectStart and PQconnectPoll with regards to other status codes
+that might be seen.
 </ListItem>
 
 <ListItem>
@@ -385,6 +621,60 @@ server host, not the local host!
 </Para>
 </ListItem>
 
+ <ListItem>
+  <Para>
+   <Function>PQsetenvStart</Function>
+   <Function>PQsetenvPoll</Function>
+   <Function>PQsetenvAbort</Function>
+   Perform an environment negotiation.
+<synopsis>
+PGsetenvHandle *PQsetenvStart(PGconn *conn)
+</synopsis>
+<synopsis>
+PostgresPollingStatusType *PQsetenvPoll(PGsetenvHandle handle)
+</synopsis>
+<synopsis>
+void PQsetenvAbort(PGsetenvHandle handle)
+</synopsis>
+   These two routines can be used to re-perform the environment negotiation
+   that occurs during the opening of a connection to a database server. I have
+   no idea why this might be useful (XXX anyone?) but it might prove useful
+   for users to be able to reconfigure their character encodings on-the-fly,
+   for example.
+  </Para>
+  <Para>
+   These functions will not block, subject to the restrictions applied to
+   PQconnectStart and PQconnectPoll.
+  </Para>
+  <Para>
+   To begin, call handle=PQsetenvStart(conn), where conn is an open connection
+   to the database server. If handle is NULL, then libpq has been unable to
+   allocate a new PGsetenvHandle structure. Otherwise, a valid handle is
+   returned. This handle is intended to be opaque - you may only use it to
+   call other functions in libpq (PQsetenvPoll, for example).
+  </Para>
+  <Para>
+    Poll the procedure using PQsetenvPoll, in exactly the same way as you would
+    create a connection using PQconnectPoll.
+  </Para>
+  <Para>
+    The procedure may be aborted at any time by calling PQsetenvAbort(handle).
+  </Para>
+  <Para>
+   These functions are not thread-safe.
+  </Para>
+ </ListItem>
+
+ <ListItem>
+  <Para>
+   <Function>PQsetenv</Function>
+   Perform an environment negotiation.
+<synopsis>
+int PQsetenv(PGconn *conn)
+</synopsis>
+   This function performs the same duties as PQsetenvStart and PQsetenvPoll, but
+   blocks to do so. It returns 1 on success and 0 on failure.
+  </Para>
 </ItemizedList>
 </Para>
 </Sect1>
index 7e2fb4437975807ccfbc8a5d8321c75a613d1b77..85764b1e9d405d0cbfcea057ddadbc39199163bb 100644 (file)
@@ -7,11 +7,13 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.106 1999/11/11 00:10:13 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.107 1999/11/30 03:08:18 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 
+#include <sys/types.h>
+#include <sys/socket.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <ctype.h>
@@ -27,6 +29,7 @@
 #include <unistd.h>
 #include <netdb.h>
 #include <netinet/tcp.h>
+#include <arpa/inet.h>
 #endif
 
 #ifndef HAVE_STRDUP
 #include "mb/pg_wchar.h"
 #endif
 
+/* ----------
+ *     pg_setenv_state
+ * A struct used when polling a setenv request. This is referred to externally
+ * using a PGsetenvHandle.
+ * ----------
+ */
+struct pg_setenv_state
+{
+       enum
+       {
+               SETENV_STATE_OPTION_SEND,    /* About to send an Environment Option */
+               SETENV_STATE_OPTION_WAIT,    /* Waiting for above send to complete  */
+#ifdef MULTIBYTE
+               SETENV_STATE_ENCODINGS_SEND, /* About to send an "encodings" query  */
+               SETENV_STATE_ENCODINGS_WAIT, /* Waiting for query to complete       */
+#endif
+               SETENV_STATE_OK,
+               SETENV_STATE_FAILED
+       } state;
+       PGconn *conn;
+       PGresult *res;
+       struct EnvironmentOptions *eo;
+} ;
+
+static int connectDBStart(PGconn *conn);
+static int connectDBComplete(PGconn *conn);
+
 #ifdef USE_SSL
 static SSL_CTX *SSL_context = NULL;
 #endif
 
-static ConnStatusType connectDB(PGconn *conn);
 static PGconn *makeEmptyPGconn(void);
 static void freePGconn(PGconn *conn);
 static void closePGconn(PGconn *conn);
@@ -53,9 +82,6 @@ static char *conninfo_getval(char *keyword);
 static void conninfo_free(void);
 static void defaultNoticeProcessor(void *arg, const char *message);
 
-/* XXX Why is this not static? */
-void           PQsetenv(PGconn *conn);
-
 #define NOTIFYLIST_INITIAL_SIZE 10
 #define NOTIFYLIST_GROWBY 10
 
@@ -98,6 +124,9 @@ static PQconninfoOption PQconninfoOptions[] = {
        {"host", "PGHOST", NULL, NULL,
        "Database-Host", "", 40},
 
+       {"hostaddr", "PGHOSTADDR", NULL, NULL,
+        "Database-Host-IPv4-Address", "", 15}, /* Room for abc.def.ghi.jkl */
+
        {"port", "PGPORT", DEF_PGPORT, NULL,
        "Database-Port", "", 6},
 
@@ -145,27 +174,86 @@ static struct EnvironmentOptions
        }
 };
 
+
+/* ----------------
+ *      Connecting to a Database
+ *
+ * There are now four different ways a user of this API can connect to the
+ * database.  Two are not recommended for use in new code, because of their
+ * lack of extensibility with respect to the passing of options to the
+ * backend.  These are PQsetdb and PQsetdbLogin (the former now being a macro
+ * to the latter).
+ *
+ * If it is desired to connect in a synchronous (blocking) manner, use the
+ * function PQconnectdb.
+ *
+ * To connect in an asychronous (non-blocking) manner, use the functions
+ * PQconnectStart, and PQconnectPoll.
+ *
+ * Internally, the static functions connectDBStart, connectDBComplete
+ * are part of the connection procedure.
+ * 
+ * ----------------
+ */
+
 /* ----------------
  *             PQconnectdb
  *
  * establishes a connection to a postgres backend through the postmaster
  * using connection information in a string.
  *
- * The conninfo string is a list of
+ * The conninfo string is a white-separated list of
  *
  *        option = value
  *
- * definitions. Value might be a single value containing no whitespaces
- * or a single quoted string. If a single quote should appear everywhere
- * in the value, it must be escaped with a backslash like \'
+ * definitions. Value might be a single value containing no whitespaces or
+ * a single quoted string. If a single quote should appear anywhere in
+ * the value, it must be escaped with a backslash like \'
  *
- * Returns a PGconn* which is needed for all subsequent libpq calls
- * if the status field of the connection returned is CONNECTION_BAD,
- * then some fields may be null'ed out instead of having valid values
- * ----------------
- */
+ * Returns a PGconn* which is needed for all subsequent libpq calls, or NULL
+ * if a memory allocation failed.
+ * If the status field of the connection returned is CONNECTION_BAD,
+ * then some fields may be null'ed out instead of having valid values.
+ *
+ * You should call PQfinish (if conn is not NULL) regardless of whether this
+ * call succeeded.
+ *
+ * ---------------- */
+
 PGconn *
 PQconnectdb(const char *conninfo)
+{
+       PGconn *conn = PQconnectStart(conninfo);
+       
+       (void)(!conn || (conn->status == CONNECTION_BAD) ||
+                  !connectDBComplete(conn));
+
+       return conn;
+}
+
+/* ----------------
+ *             PQconnectStart
+ *
+ * Begins the establishment of a connection to a postgres backend through the
+ * postmaster using connection information in a string.
+ *
+ * See comment for PQconnectdb for the definition of the string format.
+ *
+ * Returns a PGconn*.  If NULL is returned, a malloc error has occurred, and
+ * you should not attempt to proceed with this connection.  If the status
+ * field of the connection returned is CONNECTION_BAD, an error has
+ * occurred. In this case you should call PQfinish on the result, (perhaps
+ * inspecting the error message first).  Other fields of the structure may not
+ * be valid if that occurs.  If the status field is not CONNECTION_BAD, then
+ * this stage has succeeded - call PQconnectPoll, using select(2) to see when
+ * this is necessary.
+ *
+ * See PQconnectPoll for more info.
+ *
+ * ---------------- */
+
+PGconn *
+PQconnectStart(const char *conninfo)
 {
        PGconn     *conn;
        char       *tmp;
@@ -174,6 +262,7 @@ PQconnectdb(const char *conninfo)
         * Allocate memory for the conn structure
         * ----------
         */
+       
        conn = makeEmptyPGconn();
        if (conn == NULL)
                return (PGconn *) NULL;
@@ -188,6 +277,8 @@ PQconnectdb(const char *conninfo)
                conninfo_free();
                return conn;
        }
+       tmp = conninfo_getval("hostaddr");
+       conn->pghostaddr = tmp ? strdup(tmp) : NULL;
        tmp = conninfo_getval("host");
        conn->pghost = tmp ? strdup(tmp) : NULL;
        tmp = conninfo_getval("port");
@@ -213,7 +304,11 @@ PQconnectdb(const char *conninfo)
         * Connect to the database
         * ----------
         */
-       conn->status = connectDB(conn);
+       if (!connectDBStart(conn))
+       {
+         /* Just in case we failed to set it in connectDBStart */
+               conn->status = CONNECTION_BAD;
+       }
 
        return conn;
 }
@@ -384,10 +479,14 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions, cons
        }
 
        if (error)
+       {
                conn->status = CONNECTION_BAD;
+       }
        else
-               conn->status = connectDB(conn);
-
+       {
+               (void)(!connectDBStart(conn) || !connectDBComplete(conn));
+       }
+               
        return conn;
 }
 
@@ -480,7 +579,9 @@ update_db_info(PGconn *conn)
                                if (strcmp(old + offset, "localhost") != 0)
                                {
                                        printfPQExpBuffer(&conn->errorMessage,
-                                                                         "connectDB() -- non-tcp access only possible on localhost\n");
+                                                                         "connectDBStart() -- "
+                                                                         "non-tcp access only possible on "
+                                                                         "localhost\n");
                                        return 1;
                                }
                        }
@@ -494,24 +595,84 @@ update_db_info(PGconn *conn)
        return 0;
 }
 
-/*
- * connectDB -
- * make a connection to the backend so it is ready to receive queries.
- * return CONNECTION_OK if successful, CONNECTION_BAD if not.
- *
+
+/* ----------
+ * connectMakeNonblocking -
+ * Make a connection non-blocking.
+ * Returns 1 if successful, 0 if not.
+ * ----------
  */
-static ConnStatusType
-connectDB(PGconn *conn)
+static int
+connectMakeNonblocking(PGconn *conn)
+{
+#ifndef WIN32
+       if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) < 0)
+#else
+       if (ioctlsocket(conn->sock, FIONBIO, &on) != 0)
+#endif
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 "connectMakeNonblocking -- fcntl() failed: errno=%d\n%s\n",
+                                                 errno, strerror(errno));
+               return 0;
+       }
+
+       return 1;
+}
+
+/* ----------
+ * connectNoDelay -
+ * Sets the TCP_NODELAY socket option.
+ * Returns 1 if successful, 0 if not.
+ * ----------
+ */
+static int
+connectNoDelay(PGconn *conn)
+{
+       struct protoent *pe;
+       int                     on = 1;
+
+       pe = getprotobyname("TCP");
+       if (pe == NULL)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 "connectNoDelay() -- "
+                                                 "getprotobyname failed: errno=%d\n%s\n",
+                                                 errno, strerror(errno));
+               return 0;
+       }
+       if (setsockopt(conn->sock, pe->p_proto, TCP_NODELAY,
+#ifdef WIN32
+                                  (char *)
+#endif
+                                  &on,
+                                  sizeof(on)) < 0)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 "connectNoDelay() -- setsockopt failed: errno=%d\n%s\n",
+                                                 errno, strerror(errno));
+#ifdef WIN32
+               printf("Winsock error: %i\n", WSAGetLastError());
+#endif
+               return 0;
+       }
+
+       return 1;
+}
+
+
+/* ----------
+ * connectDBStart -
+ * Start to make a connection to the backend so it is ready to receive
+ * queries.
+ * Returns 1 if successful, 0 if not.
+ * ----------
+ */
+static int
+connectDBStart(PGconn *conn)
 {
-       PGresult   *res;
-       struct hostent *hp;
-       StartupPacket sp;
-       AuthRequest areq;
-       SOCKET_SIZE_TYPE laddrlen;
        int                     portno,
                                family;
-       char            beresp;
-       int                     on = 1;
 #ifdef USE_SSL
        StartupPacket           np; /* Used to negotiate SSL connection */
        char                    SSLok;
@@ -519,92 +680,126 @@ connectDB(PGconn *conn)
        int                     tried_ssl = 0;      /* Set if SSL negotiation was tried */
 #endif
 
+       if (!conn)
+               return 0;
        /*
         * parse dbName to get all additional info in it, if any
         */
        if (update_db_info(conn) != 0)
                goto connect_errReturn;
 
+       /* Ensure our buffers are empty */
+       conn->inStart = conn->inCursor = conn->inEnd = 0;
+       conn->outCount = 0;
+
        /*
-        * Initialize the startup packet.
+        * Set up the connection to postmaster/backend.
+        * Note that this supports IPv4 and UDP only.
         */
 
-       MemSet((char *) &sp, 0, sizeof(StartupPacket));
-
-       sp.protoVersion = (ProtocolVersion) htonl(PG_PROTOCOL_LIBPQ);
+       MemSet((char *) &conn->raddr, 0, sizeof(conn->raddr));
 
-       strncpy(sp.user, conn->pguser, SM_USER);
-       strncpy(sp.database, conn->dbName, SM_DATABASE);
-       strncpy(sp.tty, conn->pgtty, SM_TTY);
+       if (conn->pghostaddr != NULL)
+       {
+               /* Using pghostaddr avoids a hostname lookup */
+               /* Note that this supports IPv4 only */
+               struct in_addr addr;
 
-       if (conn->pgoptions)
-               strncpy(sp.options, conn->pgoptions, SM_OPTIONS);
+               if(!inet_aton(conn->pghostaddr, &addr))
+               {
+                       printfPQExpBuffer(&conn->errorMessage,
+                                                         "connectDBStart() -- "
+                                                         "invalid host address: %s\n", conn->pghostaddr);
+                       goto connect_errReturn;
+               }
 
-       /*
-        * Open a connection to postmaster/backend.
-        */
+               family = AF_INET;
 
-       if (conn->pghost != NULL)
+               memmove((char *) &(conn->raddr.in.sin_addr),
+                               (char *) &addr, sizeof(addr));
+       }
+       else if (conn->pghost != NULL)
        {
+               /* Using pghost, so we have to look-up the hostname */
+               struct hostent *hp;
+
                hp = gethostbyname(conn->pghost);
                if ((hp == NULL) || (hp->h_addrtype != AF_INET))
                {
                        printfPQExpBuffer(&conn->errorMessage,
-                                                         "connectDB() --  unknown hostname: %s\n",
+                                                         "connectDBStart() --  unknown hostname: %s\n",
                                                          conn->pghost);
                        goto connect_errReturn;
                }
                family = AF_INET;
+
+               memmove((char *) &(conn->raddr.in.sin_addr),
+                               (char *) hp->h_addr,
+                               hp->h_length);
        }
        else
        {
-               hp = NULL;
+               /* pghostaddr and pghost are NULL, so use UDP */
                family = AF_UNIX;
        }
 
-       MemSet((char *) &conn->raddr, 0, sizeof(conn->raddr));
+       /* Set family */
        conn->raddr.sa.sa_family = family;
 
+       /* Set port number */
        portno = atoi(conn->pgport);
        if (family == AF_INET)
        {
-               memmove((char *) &(conn->raddr.in.sin_addr),
-                               (char *) hp->h_addr,
-                               hp->h_length);
                conn->raddr.in.sin_port = htons((unsigned short) (portno));
                conn->raddr_len = sizeof(struct sockaddr_in);
        }
 #if !defined(WIN32) && !defined(__CYGWIN32__)
-       else
-               conn->raddr_len = UNIXSOCK_PATH(conn->raddr.un, portno);
+               else
+                       conn->raddr_len = UNIXSOCK_PATH(conn->raddr.un, portno);
 #endif
 
 
-       /* Connect to the server  */
+       /* Open a socket */
        if ((conn->sock = socket(family, SOCK_STREAM, 0)) < 0)
        {
                printfPQExpBuffer(&conn->errorMessage,
-                                                 "connectDB() -- socket() failed: errno=%d\n%s\n",
+                                                 "connectDBStart() -- "
+                                                 "socket() failed: errno=%d\n%s\n",
                                                  errno, strerror(errno));
                goto connect_errReturn;
        }
-       if (connect(conn->sock, &conn->raddr.sa, conn->raddr_len) < 0)
+
+       /* ----------
+        * Set the right options. Normally, we need nonblocking I/O, and we don't
+        * want delay of outgoing data for AF_INET sockets.  If we are using SSL,
+        * then we need the blocking I/O (XXX Can this be fixed?).
+        * ---------- */
+
+       if (family == AF_INET)
        {
-               printfPQExpBuffer(&conn->errorMessage,
-                                                 "connectDB() -- connect() failed: %s\n"
-                                                 "Is the postmaster running%s at '%s' and accepting connections on %s '%s'?\n",
-                                                 strerror(errno),
-                                                 (family == AF_INET) ? " (with -i)" : "",
-                                                 conn->pghost ? conn->pghost : "localhost",
-                                                 (family == AF_INET) ? "TCP/IP port" : "Unix socket",
-                                                 conn->pgport);
-               goto connect_errReturn;
+               if (!connectNoDelay(conn))
+                       goto connect_errReturn;
        }
 
+       /* ----------
+        * Since I have no idea whether this is a valid thing to do under Windows
+        * before a connection is made, and since I have no way of testing it, I
+        * leave the code looking as below.  When someone decides that they want
+        * non-blocking connections under Windows, they can define
+        * WIN32_NON_BLOCKING_CONNECTIONS before compilation.  If it works, then
+        * this code can be cleaned up.
+        *
+        *   Ewan Mellor <eem21@cam.ac.uk>.
+        * ---------- */
+#if (!defined(WIN32) || defined(WIN32_NON_BLOCKING_CONNECTIONS)) && !defined(USE_SSL)
+       if (!connectMakeNonblocking(conn))
+               goto connect_errReturn;
+#endif 
+
+#ifdef USE_SSL
        /* This needs to be done before we set into nonblocking, since SSL negotiation
         * does not like that mode */
 
-#ifdef USE_SSL
        /* Attempt to negotiate SSL usage */
        if (allow_ssl_try) {
          tried_ssl = 1;
@@ -653,7 +848,7 @@ connectDB(PGconn *conn)
              fprintf(conn->Pfdebug, "Backend reports error, attempting fallback to pre-6.6.\n");
            close(conn->sock);
            allow_ssl_try = 0;
-           return connectDB(conn);
+           return connectDBStart(conn);
          }
          else if (SSLok != 'N') {
            strcpy(conn->errorMessage,
@@ -665,242 +860,812 @@ connectDB(PGconn *conn)
          allow_ssl_try = 1; /* We'll allow an attempt to use SSL next time */
 #endif
 
-       /*
-        * Set the right options. We need nonblocking I/O, and we don't want
-        * delay of outgoing data.
+       /* ----------
+     * Start / make connection.  We are hopefully in non-blocking mode
+        * now, but it is possible that:
+        *   1. Older systems will still block on connect, despite the
+        *      non-blocking flag. (Anyone know if this is true?)
+        *   2. We are running under Windows, and aren't even trying
+        *      to be non-blocking (see above).
+        *   3. We are using SSL.
+        * Thus, we have make arrangements for all eventualities.
+        * ----------
         */
-
-#ifndef WIN32
-       if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) < 0)
-#else
-       if (ioctlsocket(conn->sock, FIONBIO, &on) != 0)
-#endif
-       {
-               printfPQExpBuffer(&conn->errorMessage,
-                                                 "connectDB() -- fcntl() failed: errno=%d\n%s\n",
-                                                 errno, strerror(errno));
-               goto connect_errReturn;
-       }
-
-       if (family == AF_INET)
+       if (connect(conn->sock, &conn->raddr.sa, conn->raddr_len) < 0)
        {
-               struct protoent *pe;
-
-               pe = getprotobyname("TCP");
-               if (pe == NULL)
+               if (errno == EINPROGRESS)
                {
-                       printfPQExpBuffer(&conn->errorMessage,
-                                                         "connectDB(): getprotobyname failed\n");
-                       goto connect_errReturn;
+                       /* This is fine - we're in non-blocking mode, and the
+                        * connection is in progress. */
+                       conn->status = CONNECTION_STARTED;
                }
-               if (setsockopt(conn->sock, pe->p_proto, TCP_NODELAY,
-#ifdef WIN32
-                                          (char *)
-#endif
-                                          &on,
-                                          sizeof(on)) < 0)
+               else
                {
+                       /* Something's gone wrong */
                        printfPQExpBuffer(&conn->errorMessage,
-                                                         "connectDB() -- setsockopt failed: errno=%d\n%s\n",
-                                                         errno, strerror(errno));
-#ifdef WIN32
-                       printf("Winsock error: %i\n", WSAGetLastError());
-#endif
+                                                         "connectDBStart() -- connect() failed: %s\n"
+                                                         "Is the postmaster running%s at '%s' "
+                                                         "and accepting connections on %s '%s'?\n",
+                                                         strerror(errno),
+                                                         (family == AF_INET) ? " (with -i)" : "",
+                                                         conn->pghost ? conn->pghost : "localhost",
+                                                         (family == AF_INET) ?
+                                                         "TCP/IP port" : "Unix socket",
+                                                         conn->pgport);
                        goto connect_errReturn;
                }
        }
-
-       /* Fill in the client address */
-       laddrlen = sizeof(conn->laddr);
-       if (getsockname(conn->sock, &conn->laddr.sa, &laddrlen) < 0)
+       else
        {
-               printfPQExpBuffer(&conn->errorMessage,
-                                                 "connectDB() -- getsockname() failed: errno=%d\n%s\n",
-                                                 errno, strerror(errno));
-               goto connect_errReturn;
+               /* We're connected already */
+               conn->status = CONNECTION_MADE;
        }
 
-       /* Ensure our buffers are empty */
-       conn->inStart = conn->inCursor = conn->inEnd = 0;
-       conn->outCount = 0;
+       /* This makes the connection non-blocking, for all those cases which forced us
+          not to do it above. */
+#if (defined(WIN32) && !defined(WIN32_NON_BLOCKING_CONNECTIONS)) || defined(USE_SSL)
+       if (!connectMakeNonblocking(conn))
+               goto connect_errReturn;
+#endif 
 
-       /* Send the startup packet. */
+       return 1;
 
-       if (pqPacketSend(conn, (char *) &sp, sizeof(StartupPacket)) != STATUS_OK)
+connect_errReturn:
+       if (conn->sock >= 0)
        {
-               printfPQExpBuffer(&conn->errorMessage,
-                                                 "connectDB() --  couldn't send startup packet: errno=%d\n%s\n",
-                                                 errno, strerror(errno));
-               goto connect_errReturn;
+#ifdef WIN32
+               closesocket(conn->sock);
+#else
+               close(conn->sock);
+#endif
+               conn->sock = -1;
        }
+       conn->status = CONNECTION_BAD;
 
-       /*
-        * Perform the authentication exchange: wait for backend messages and
-        * respond as necessary. We fall out of this loop when done talking to
-        * the postmaster.
-        */
+       return 0;
+}
 
-       for (;;)
-       {
-               /* Wait for some data to arrive (or for the channel to close) */
-               if (pqWait(TRUE, FALSE, conn))
-                       goto connect_errReturn;
-               /* Load data, or detect EOF */
-               if (pqReadData(conn) < 0)
-                       goto connect_errReturn;
 
-               /*
-                * Scan the message. If we run out of data, loop around to try
-                * again.
-                */
-               conn->inCursor = conn->inStart;
+/* ----------------
+ *             connectDBComplete
+ *
+ * Block and complete a connection.
+ *
+ * Returns 1 on success, 0 on failure.
+ * ----------------
+ */
+static int
+connectDBComplete(PGconn *conn)
+{
+       PostgresPollingStatusType flag;
+       int r = 0, w = 1;
 
-               if (pqGetc(&beresp, conn))
-                       continue;                       /* no data yet */
+       do
+       {
+               if(pqWait(r, w, conn))
+               {
+                       conn->status = CONNECTION_BAD;
+                       return 0;
+               }
 
-               /* Handle errors. */
-               if (beresp == 'E')
+       again:
+               switch(flag = PQconnectPoll(conn))
                {
-                       if (pqGets(&conn->errorMessage, conn))
-                               continue;
-                       goto connect_errReturn;
+                       case PGRES_POLLING_ACTIVE:
+                               goto again;
+
+                       case PGRES_POLLING_OK:
+                               break;
+                               
+                       case PGRES_POLLING_READING:
+                               r = 1;
+                               w = 0;
+                               break;
+
+                       case PGRES_POLLING_WRITING:
+                               r = 0;
+                               w = 1;
+                               break;
+
+                       default:
+                               /* Just in case we failed to set it in PQconnectPoll */
+                               conn->status = CONNECTION_BAD;
+                               return 0;
                }
+       } while (flag != PGRES_POLLING_OK);
+       
+       return 1;
+}
+
+
+/* ----------------
+ *             PQconnectPoll
+ *
+ * Poll an asynchronous connection.
+ *
+ * Returns a PostgresPollingStatusType.
+ * Before calling this function, use select(2) to determine when data arrive.
+ * 
+ * You must call PQfinish whether or not this fails.
+ *
+ * This function and PQconnectStart are intended to allow connections to be
+ * made without blocking the execution of your program on remote I/O. However,
+ * there are a number of caveats:
+ *
+ *   o  If you call PQtrace, ensure that the stream object into which you trace
+        will not block.
+ *   o  If you do not supply an IP address for the remote host (i.e. you 
+ *      supply a host name instead) then this function will block on
+ *      gethostbyname.  You will be fine if using UDP (i.e. by supplying
+ *      neither a host name nor a host address).
+ *   o  If your backend wants to use Kerberos authentication then you must
+ *      supply both a host name and a host address, otherwise this function
+ *      may block on gethostname.
+ *   o  This function will block if compiled with USE_SSL.
+ *
+ * ---------------- */
+PostgresPollingStatusType
+PQconnectPoll(PGconn *conn)
+{
+       PGresult   *res;
 
-               /* Otherwise it should be an authentication request. */
-               if (beresp != 'R')
+       if (conn == NULL)
+               return PGRES_POLLING_FAILED;
+
+       /* Get the new data */
+       switch (conn->status)
+       {
+               /* We really shouldn't have been polled in these two cases, but
+                  we can handle it. */
+               case CONNECTION_BAD:
+                       return PGRES_POLLING_FAILED;
+               case CONNECTION_OK:
+                       return PGRES_POLLING_OK;
+
+               /* These are reading states */
+               case CONNECTION_AWAITING_RESPONSE:
+               case CONNECTION_AUTH_RESPONSE:
+               case CONNECTION_ERROR_RESPONSE:
+               case CONNECTION_AUTH_OK:
                {
-                       printfPQExpBuffer(&conn->errorMessage,
-                                                         "connectDB() -- expected authentication request\n");
-                       goto connect_errReturn;
+                       /* Load waiting data */
+                       int n = pqReadData(conn);
+                               
+                       if (n < 0)
+                               goto error_return;
+                       if (n == 0)
+                               return PGRES_POLLING_READING;
+
+                       break;
                }
 
-               /* Get the type of request. */
-               if (pqGetInt((int *) &areq, 4, conn))
-                       continue;
+               /* These are writing states, so we just proceed. */
+               case CONNECTION_STARTED:
+               case CONNECTION_MADE:
+                  break;
 
-               /* Get the password salt if there is one. */
-               if (areq == AUTH_REQ_CRYPT)
+               case CONNECTION_SETENV:
+                       /* We allow PQsetenvPoll to decide whether to proceed */
+                       break;
+
+               default:
+                       printfPQExpBuffer(&conn->errorMessage,
+                                                         "PQconnectPoll() -- unknown connection state - "
+                                                         "probably indicative of memory corruption!\n");
+                       goto error_return;
+       }
+
+
+ keep_going: /* We will come back to here until there is nothing left to
+                               parse. */
+       switch(conn->status)
+       {
+               case CONNECTION_STARTED:
                {
-                       if (pqGetnchar(conn->salt, sizeof(conn->salt), conn))
-                               continue;
+                       SOCKET_SIZE_TYPE laddrlen;
+                       int optval;
+                       socklen_t optlen = sizeof(int);
+
+                       /* Write ready, since we've made it here, so the connection
+                        * has been made. */
+
+                       /* Now check (using getsockopt) that there is not an error
+                          state waiting for us on the socket. */
+
+                       if (getsockopt(conn->sock, SOL_SOCKET, SO_ERROR,
+                                                  &optval, &optlen) == -1)
+                       {
+                               printfPQExpBuffer(&conn->errorMessage,
+                                                                 "PQconnectPoll() -- getsockopt() failed: "
+                                                                 "errno=%d\n%s\n",
+                                                                 errno, strerror(errno));
+                               goto error_return;
+                       }
+                       else if (optval != 0)
+                       {
+                               printfPQExpBuffer(&conn->errorMessage,
+                                                                 "PQconnectPoll() -- "
+                                                                 "socket has error condition %d: %s.\n",
+                                                                 optval, strerror(optval));
+                               goto error_return;
+                       }
+
+                       /* Fill in the client address */
+                       laddrlen = sizeof(conn->laddr);
+                       if (getsockname(conn->sock, &conn->laddr.sa, &laddrlen) < 0)
+                       {
+                               printfPQExpBuffer(&conn->errorMessage,
+                                                                 "PQconnectPoll() -- getsockname() failed: "
+                                                                 "errno=%d\n%s\n",
+                                                                 errno, strerror(errno));
+                               goto error_return;
+                       }
+
+                       conn->status = CONNECTION_MADE;
+                       return PGRES_POLLING_WRITING;
                }
 
-               /* OK, we successfully read the message; mark data consumed */
-               conn->inStart = conn->inCursor;
+               case CONNECTION_MADE:
+               {
+                       StartupPacket sp;
+                       
+                       /*
+                        * Initialize the startup packet.
+                        */
 
-               /* Respond to the request if necessary. */
-               /* fe-auth.c has not been fixed to support PQExpBuffers, so: */
-               if (fe_sendauth(areq, conn, conn->pghost, conn->pgpass,
-                                               conn->errorMessage.data) != STATUS_OK)
+                       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);
+
+                       /* Send the startup packet. */
+
+                       if (pqPacketSend(conn, (char *) &sp,
+                                                        sizeof(StartupPacket)) != STATUS_OK)
+                       {
+                               printfPQExpBuffer(&conn->errorMessage,
+                                                                 "PQconnectPoll() --  "
+                                                                 "couldn't send startup packet: "
+                                                                 "errno=%d\n%s\n",
+                                                                 errno, strerror(errno));
+                               goto error_return;
+                       }
+
+                       conn->status = CONNECTION_AWAITING_RESPONSE;
+                       return PGRES_POLLING_READING;
+               }
+
+
+               /*
+                * Handle the authentication exchange: wait for backend messages
+                * and respond as necessary.
+                */
+               case CONNECTION_AWAITING_RESPONSE:
+               {
+                       char            beresp;
+
+                       /* Scan the message */
+                       conn->inCursor = conn->inStart;
+
+                       if (pqGetc(&beresp, conn))
+                       {
+                               /* We'll come back when there are more data */
+                               return PGRES_POLLING_READING;
+                       }
+
+                       /* Handle errors. */
+                       if (beresp == 'E')
+                       {
+                               conn->status = CONNECTION_ERROR_RESPONSE;
+                               goto keep_going;
+                       }
+
+                       /* Otherwise it should be an authentication request. */
+                       if (beresp != 'R')
+                       {
+                               printfPQExpBuffer(&conn->errorMessage,
+                                                          "PQconnectPoll() -- expected authentication "
+                                                          "request\n");
+                               goto error_return;
+                       }
+
+                       /* Got an authentication request, so that's OK */
+                       conn->status = CONNECTION_AUTH_RESPONSE;
+                       goto keep_going;
+               }
+
+               case CONNECTION_AUTH_RESPONSE:
                {
+                       AuthRequest areq;
+                       
+                       /* Get the type of request. */
+                       if (pqGetInt((int *) &areq, 4, conn))
+                       {
+                               /* We'll come back when there are more data */
+                               return PGRES_POLLING_READING;
+                       }
+
+                       /* Get the password salt if there is one. */
+                       if (areq == AUTH_REQ_CRYPT)
+                       {
+                               if (pqGetnchar(conn->salt, sizeof(conn->salt), conn))
+                               {
+                                       /* We'll come back when there are more data */
+                                       return PGRES_POLLING_READING;
+                               }
+                       }
+
+                       /* OK, we successfully read the message; mark data consumed */
+                       conn->inStart = conn->inCursor;
+
+                       /* Respond to the request if necessary. */
+                       /* Note that conn->pghost must be non-NULL if we are going
+                        * avoid the Kerberos code doing a hostname look-up. */
+                       /* XXX fe-auth.c has not been fixed to support PQExpBuffers, so: */
+                       if (fe_sendauth(areq, conn, conn->pghost, conn->pgpass,
+                                                       conn->errorMessage.data) != STATUS_OK)
+                       {
+                               conn->errorMessage.len = strlen(conn->errorMessage.data);
+                               goto error_return;
+                       }
                        conn->errorMessage.len = strlen(conn->errorMessage.data);
-                       goto connect_errReturn;
+
+                       /* This function has a section near the end that looks like it
+                        * should block.  I think that it will be OK though, since the
+                        * socket is non-blocking, and thus the data should get out
+                        * as quickly as possible.                                    */
+                       if (pqFlush(conn))
+                               goto error_return;
+
+                       if (areq == AUTH_REQ_OK)
+                       {
+                               /* We are done with authentication exchange */
+                               conn->status = CONNECTION_AUTH_OK;
+                               /* Set asyncStatus so that PQsetResult will think that what
+                                * comes back next is the result of a query.  See below.  */
+                               conn->asyncStatus = PGASYNC_BUSY;
+                               goto keep_going;
+                       }
+
+                       conn->status = CONNECTION_AWAITING_RESPONSE;
+                       return PGRES_POLLING_READING;
                }
 
-               if (pqFlush(conn))
-                       goto connect_errReturn;
+               case CONNECTION_ERROR_RESPONSE:
+                       if (pqGets(&conn->errorMessage, conn))
+                       {
+                               /* We'll come back when there are more data */
+                               return PGRES_POLLING_READING;
+                       }
+                       goto error_return;
 
-               /* Are we done? */
-               if (areq == AUTH_REQ_OK)
-                       break;
-       }
+               case CONNECTION_AUTH_OK:
+               {
+                       /* ----------
+                        * Now we expect to hear from the backend. A ReadyForQuery
+                        * message indicates that startup is successful, but we might
+                        * also get an Error message indicating failure. (Notice
+                        * messages indicating nonfatal warnings are also allowed by
+                        * the protocol, as is a BackendKeyData message.) Easiest way
+                        * to handle this is to let PQgetResult() read the messages. We
+                        * just have to fake it out about the state of the connection.
+                        *----------
+                        */
 
-       /*
-        * Now we expect to hear from the backend. A ReadyForQuery message
-        * indicates that startup is successful, but we might also get an
-        * Error message indicating failure. (Notice messages indicating
-        * nonfatal warnings are also allowed by the protocol, as is a
-        * BackendKeyData message.) Easiest way to handle this is to let
-        * PQgetResult() read the messages. We just have to fake it out about
-        * the state of the connection.
-        */
+                       if (!PQconsumeInput(conn))
+                               goto error_return;
 
-       conn->status = CONNECTION_OK;
-       conn->asyncStatus = PGASYNC_BUSY;
-       res = PQgetResult(conn);
-       /* NULL return indicating we have gone to IDLE state is expected */
-       if (res)
-       {
-               if (res->resultStatus != PGRES_FATAL_ERROR)
+                       if(PQisBusy(conn))
+                               return PGRES_POLLING_READING;
+                       
+                       res = PQgetResult(conn);
+                       /* NULL return indicating we have gone to
+                          IDLE state is expected */
+                       if (res)
+                       {
+                               if (res->resultStatus != PGRES_FATAL_ERROR)
+                                       printfPQExpBuffer(&conn->errorMessage,
+                                                                         "PQconnectPoll() -- unexpected message "
+                                                                         "during startup\n");
+                               PQclear(res);
+                               goto error_return;
+                       }
+
+                       /*
+                        * Post-connection housekeeping. Send environment variables
+                        * to server.
+                        */
+
+                       if ((conn->setenv_handle = PQsetenvStart(conn)) == NULL)
+                               goto error_return;
+
+                       conn->status = CONNECTION_SETENV;
+
+                       goto keep_going;
+               }
+
+               case CONNECTION_SETENV:
+                       /* We pretend that the connection is OK for the duration of
+                          theses queries. */
+                       conn->status = CONNECTION_OK;
+
+                       switch(PQsetenvPoll(conn->setenv_handle))
+                       {
+                               case PGRES_POLLING_OK: /* Success */
+                                       conn->status = CONNECTION_OK;
+                                       return PGRES_POLLING_OK;
+
+                               case PGRES_POLLING_READING: /* Still going */
+                                       conn->status = CONNECTION_SETENV;
+                                       return PGRES_POLLING_READING;
+
+                               case PGRES_POLLING_WRITING: /* Still going */
+                                       conn->status = CONNECTION_SETENV;
+                                       return PGRES_POLLING_WRITING;
+
+                               default:
+                                       conn->status = CONNECTION_SETENV;
+                                       goto error_return;
+                       }
+                       /* Unreachable */
+
+               default:
                        printfPQExpBuffer(&conn->errorMessage,
-                                                         "connectDB() -- unexpected message during startup\n");
-               PQclear(res);
-               goto connect_errReturn;
+                                                         "PQconnectPoll() -- unknown connection state - "
+                                                         "probably indicative of memory corruption!\n",
+                                                         sizeof(conn->errorMessage));
+                       goto error_return;
        }
 
-       /*
-        * Post-connection housekeeping. Send environment variables to server
+       /* Unreachable */
+
+error_return:
+       /* ----------
+        * We used to close the socket at this point, but that makes it awkward
+        * for those above us if they wish to remove this socket from their
+        * own records (an fd_set for example).  We'll just have this socket
+        * closed when PQfinish is called (which is compulsory even after an
+        * error, since the connection structure must be freed).
+        * ----------
         */
+       return PGRES_POLLING_FAILED;
+}
 
-       PQsetenv(conn);
 
-       return CONNECTION_OK;
+/* ----------------
+ *             PQsetenvStart
+ *
+ * Starts the process of passing the values of a standard set of environment
+ * variables to the backend.
+ *
+ * ---------------- */
+PGsetenvHandle
+PQsetenvStart(PGconn *conn)
+{
+       struct pg_setenv_state *handle;
 
-connect_errReturn:
-       if (conn->sock >= 0)
+       if ((handle = malloc(sizeof(struct pg_setenv_state))) == NULL)
        {
-#ifdef WIN32
-               closesocket(conn->sock);
-#else
-               close(conn->sock);
-#endif
-               conn->sock = -1;
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 "PQsetenvStart() -- malloc error - %s\n",
+                                                 strerror(errno));
+               return NULL;
        }
-       return CONNECTION_BAD;
 
+       handle->conn = conn;
+       handle->res = NULL;
+       handle->eo = EnvironmentOptions;
+
+#ifdef MULTIBYTE
+       handle->state = SETENV_STATE_ENCODINGS_SEND;
+#else
+       handle->state = SETENV_STATE_OPTION_SEND;
+#endif
+       
+       return handle; /* Note that a struct pg_setenv_state * is the same as a
+                                         PGsetenvHandle */
 }
 
-void
-PQsetenv(PGconn *conn)
+/* ----------------
+ *             PQsetenvPoll
+ *
+ * Polls the process of passing the values of a standard set of environment
+ * variables to the backend.
+ *
+ * ---------------- */
+PostgresPollingStatusType
+PQsetenvPoll(PGsetenvHandle handle)
 {
-       struct EnvironmentOptions *eo;
-       char            setQuery[100];  /* note length limits in sprintf's below */
-       const char *val;
-       PGresult   *res;
 #ifdef MULTIBYTE
-       char       *envname = "PGCLIENTENCODING";
+       const char envname[] = "PGCLIENTENCODING";
+#endif
 
-       /* Set env. variable PGCLIENTENCODING if it's not set already */
-       val = getenv(envname);
-       if (!val || *val == '\0')
+       if (!handle || handle->state == SETENV_STATE_FAILED)
+               return PGRES_POLLING_FAILED;
+
+       /* Check whether there are any data for us */
+       switch (handle->state)
        {
-               const char *encoding = NULL;
-
-               /* query server encoding */
-               res = PQexec(conn, "select getdatabaseencoding()");
-               if (res && PQresultStatus(res) == PGRES_TUPLES_OK)
-                       encoding = PQgetvalue(res, 0, 0);
-               if (!encoding)                  /* this should not happen */
-                       encoding = pg_encoding_to_char(MULTIBYTE);
-               if (encoding)
+               /* These are reading states */
+#ifdef MULTIBYTE
+               case SETENV_STATE_ENCODINGS_WAIT:
+#endif
+               case SETENV_STATE_OPTION_WAIT:
                {
-                       /* set client encoding via environment variable */
-                       char       *envbuf;
+                       /* Load waiting data */
+                       int n = pqReadData(handle->conn);
+                               
+                       if (n < 0)
+                               goto error_return;
+                       if (n == 0)
+                               return PGRES_POLLING_READING;
 
-                       envbuf = (char *) malloc(strlen(envname) + strlen(encoding) + 2);
-                       sprintf(envbuf, "%s=%s", envname, encoding);
-                       putenv(envbuf);
+                       break;
                }
-               PQclear(res);
-       }
+
+               /* These are writing states, so we just proceed. */
+#ifdef MULTIBYTE
+               case SETENV_STATE_ENCODINGS_SEND:
 #endif
+               case SETENV_STATE_OPTION_SEND:
+                       break;
+                  
+               default:
+                       printfPQExpBuffer(&handle->conn->errorMessage,
+                                                         "PQsetenvPoll() -- unknown state - "
+                                                         "probably indicative of memory corruption!\n");
+                       goto error_return;
+       }
 
-       for (eo = EnvironmentOptions; eo->envName; eo++)
+
+ keep_going: /* We will come back to here until there is nothing left to
+                               parse. */
+       switch(handle->state)
        {
-               if ((val = getenv(eo->envName)))
+
+#ifdef MULTIBYTE
+               case SETENV_STATE_ENCODINGS_SEND:
                {
-                       if (strcasecmp(val, "default") == 0)
-                               sprintf(setQuery, "SET %s = %.60s", eo->pgName, val);
-                       else
-                               sprintf(setQuery, "SET %s = '%.60s'", eo->pgName, val);
+                       const char *env;
+                       
+                       /* query server encoding */
+                       env = getenv(envname);
+                       if (!env || *env == '\0')
+                       {
+                               if (!PQsendQuery(handle->conn,
+                                                                "select getdatabaseencoding()"))
+                                       goto error_return;
+
+                               handle->state = SETENV_STATE_ENCODINGS_WAIT;
+                               return PGRES_POLLING_READING;
+                       }
+               }
+
+               case SETENV_STATE_ENCODINGS_WAIT:
+               {
+                       const char *encoding = 0;
+
+                       if (!PQconsumeInput(handle->conn))
+                               goto error_return;
+
+                       if (PQisBusy(handle->conn))
+                               return PGRES_POLLING_READING;
+                       
+                       handle->res = PQgetResult(handle->conn);
+
+                       if (handle->res)
+                       {
+                               if (PQresultStatus(handle->res) != PGRES_TUPLES_OK)
+                               {
+                                       PQclear(handle->res);
+                                       goto error_return;
+                               }
+
+                               encoding = PQgetvalue(handle->res, 0, 0);
+                               if (!encoding)                  /* this should not happen */
+                                       encoding = pg_encoding_to_char(MULTIBYTE);
+
+                               if (encoding)
+                               {
+                                       /* set client encoding via environment variable */
+                                       char       *envbuf;
+
+                                       envbuf = (char *) malloc(strlen(envname) + strlen(encoding) + 2);
+                                       sprintf(envbuf, "%s=%s", envname, encoding);
+                                       putenv(envbuf);
+                               }
+                               PQclear(handle->res);
+                               /* We have to keep going in order to clear up the query */
+                               goto keep_going;
+                       }
+
+                       /* NULL result indicates that the query is finished */
+
+                       /* Move on to setting the environment options */
+                       handle->state = SETENV_STATE_OPTION_SEND;
+                       goto keep_going;
+               }
+#endif
+
+               case SETENV_STATE_OPTION_SEND:
+               {
+                       /* Send an Environment Option */
+                       char            setQuery[100];  /* note length limits in sprintf's below */
+
+                       if (handle->eo->envName)
+                       {
+                               const char *val;
+
+                               if ((val = getenv(handle->eo->envName)))
+                               {
+                                       if (strcasecmp(val, "default") == 0)
+                                               sprintf(setQuery, "SET %s = %.60s",
+                                                               handle->eo->pgName, val);
+                                       else
+                                               sprintf(setQuery, "SET %s = '%.60s'",
+                                                               handle->eo->pgName, val);
 #ifdef CONNECTDEBUG
-                       printf("Use environment variable %s to send %s\n", eo->envName, setQuery);
+                                       printf("Use environment variable %s to send %s\n",
+                                                  handle->eo->envName, setQuery);
 #endif
-                       res = PQexec(conn, setQuery);
-                       PQclear(res);           /* Don't care? */
+                                       if (!PQsendQuery(handle->conn, setQuery))
+                                               goto error_return;
+
+                                       handle->state = SETENV_STATE_OPTION_WAIT;
+                               }
+                               else
+                               {
+                                       handle->eo++;
+                               }
+                       }
+                       else
+                       {
+                               /* No option to send, so we are done. */
+                               handle->state = SETENV_STATE_OK;
+                       }
+
+                       goto keep_going;
                }
+
+               case SETENV_STATE_OPTION_WAIT:
+               {
+                       if (!PQconsumeInput(handle->conn))
+                               goto error_return;
+
+                       if (PQisBusy(handle->conn))
+                               return PGRES_POLLING_READING;
+                       
+                       handle->res = PQgetResult(handle->conn);
+
+                       if (handle->res)
+                       {
+                               if (PQresultStatus(handle->res) != PGRES_COMMAND_OK)
+                               {
+                                       PQclear(handle->res);
+                                       goto error_return;
+                               }
+                               /* Don't need the result */
+                               PQclear(handle->res);
+                               /* We have to keep going in order to clear up the query */
+                               goto keep_going;
+                       }
+
+                       /* NULL result indicates that the query is finished */
+
+                       /* Send the next option */
+                       handle->eo++;
+                       handle->state = SETENV_STATE_OPTION_SEND;
+                       goto keep_going;
+               }
+
+               case SETENV_STATE_OK:
+                       /* Tidy up */
+                       free(handle);
+                       return PGRES_POLLING_OK;
+
+               default:
+                       printfPQExpBuffer(&handle->conn->errorMessage,
+                                                         "PQsetenvPoll() -- unknown state - "
+                                                         "probably indicative of memory corruption!\n");
+                       goto error_return;
+       }
+
+       /* Unreachable */
+
+ error_return:
+       handle->state = SETENV_STATE_FAILED; /* This may protect us even if we
+                                                                                 * are called after the handle
+                                                                                 * has been freed.             */
+       free(handle);
+       return PGRES_POLLING_FAILED;
+}
+
+
+/* ----------------
+ *             PQsetenvAbort
+ *
+ * Aborts the process of passing the values of a standard set of environment
+ * variables to the backend.
+ *
+ * ---------------- */
+void
+PQsetenvAbort(PGsetenvHandle handle)
+{
+       /* We should not have been called in the FAILED state, but we can cope by
+        * not freeing the handle (it has probably been freed by now anyway). */
+       if (handle->state != SETENV_STATE_FAILED)
+       {
+               handle->state = SETENV_STATE_FAILED;
+               free(handle);
        }
-}      /* PQsetenv() */
+}
+
+
+/* ----------------
+ *             PQsetenv
+ *
+ * Passes the values of a standard set of environment variables to the
+ * backend.
+ *
+ * Returns 1 on success, 0 on failure.
+ *
+ * This function used to return void.  I don't think that there should be
+ * compatibility problems caused by giving it a return value, especially as
+ * this function has not been documented previously.
+ *
+ * ---------------- */
+int
+PQsetenv(PGconn *conn)
+{
+       PostgresPollingStatusType flag;
+       PGsetenvHandle handle;
+       int r = 0, w = 1;
+       
+       if((handle = PQsetenvStart(conn)) == NULL)
+               return 0;
+
+       do
+       {
+               if(pqWait(r, w, conn))
+               {
+                       /* XXX This is not a good sign - perhaps we should mark the
+                          connection as bad here... */
+                       return 0;
+               }
+
+       again:
+               switch(flag = PQsetenvPoll(handle))
+               {
+                       case PGRES_POLLING_ACTIVE:
+                               goto again;
+
+                       case PGRES_POLLING_OK:
+                               break;
+                               
+                       case PGRES_POLLING_READING:
+                               r = 1;
+                               w = 0;
+                               break;
+                               
+                       case PGRES_POLLING_WRITING:
+                               r = 0;
+                               w = 1;
+                               break;
+                               
+                       default: /* Failed */
+                               return 0;
+               }
+       } while (flag != PGRES_POLLING_OK);
+  
+       return 1;
+}
+
 
 /*
  * makeEmptyPGconn
@@ -973,6 +1738,8 @@ freePGconn(PGconn *conn)
 #endif
        if (conn->pghost)
                free(conn->pghost);
+       if (conn->pghostaddr)
+               free(conn->pghostaddr);
        if (conn->pgport)
                free(conn->pgport);
        if (conn->pgtty)
@@ -1006,6 +1773,12 @@ freePGconn(PGconn *conn)
 static void
 closePGconn(PGconn *conn)
 {
+       if (conn->status == CONNECTION_SETENV)
+       {
+               /* We have to abort the setenv process as well */
+               PQsetenvAbort(conn->setenv_handle);
+       }
+
        if (conn->sock >= 0)
        {
 
@@ -1067,8 +1840,47 @@ PQreset(PGconn *conn)
        if (conn)
        {
                closePGconn(conn);
-               conn->status = connectDB(conn);
+
+               (void)(!connectDBStart(conn) || !connectDBComplete(conn));
        }
+
+       return;
+}
+
+
+/* PQresetStart :
+   resets the connection to the backend
+   closes the existing connection and makes a new one
+   Returns 1 on success, 0 on failure.
+*/
+int
+PQresetStart(PGconn *conn)
+{
+       if (conn)
+       {
+               closePGconn(conn);
+
+               return connectDBStart(conn);
+       }
+
+       return 1;
+}
+
+
+/* PQresetPoll :
+   resets the connection to the backend
+   closes the existing connection and makes a new one
+*/
+
+PostgresPollingStatusType
+PQresetPoll(PGconn *conn)
+{
+       if (conn)
+       {
+               return PQconnectPoll(conn);
+       }
+
+       return PGRES_POLLING_FAILED;
 }
 
 
index d29277bf2004276bfaab4b9515848d347b81ed00..89425e0034c40c1d3de798a72b8260fb89c4a6aa 100644 (file)
@@ -24,7 +24,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.32 1999/11/11 00:10:14 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.33 1999/11/30 03:08:19 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -269,29 +269,69 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
 
 /* --------------------------------------------------------------------- */
 /* pqReadReady: is select() saying the file is ready to read?
+ * Returns -1 on failure, 0 if not ready, 1 if ready.
  */
-static int
+int
 pqReadReady(PGconn *conn)
 {
        fd_set          input_mask;
        struct timeval timeout;
 
-       if (conn->sock < 0)
-               return 0;
+       if (!conn || conn->sock < 0)
+               return -1;
 
        FD_ZERO(&input_mask);
        FD_SET(conn->sock, &input_mask);
        timeout.tv_sec = 0;
        timeout.tv_usec = 0;
+ retry:
        if (select(conn->sock + 1, &input_mask, (fd_set *) NULL, (fd_set *) NULL,
                           &timeout) < 0)
        {
+               if (errno == EINTR)
+                       /* Interrupted system call - we'll just try again */
+                       goto retry;
+
                printfPQExpBuffer(&conn->errorMessage,
                                                  "pqReadReady() -- select() failed: errno=%d\n%s\n",
                                                  errno, strerror(errno));
-               return 0;
+               return -1;
+       }
+
+       return FD_ISSET(conn->sock, &input_mask) ? 1 : 0;
+}
+
+/* --------------------------------------------------------------------- */
+/* pqWriteReady: is select() saying the file is ready to write?
+ * Returns -1 on failure, 0 if not ready, 1 if ready.
+ */
+int
+pqWriteReady(PGconn *conn)
+{
+       fd_set          input_mask;
+       struct timeval timeout;
+
+       if (!conn || conn->sock < 0)
+               return -1;
+
+       FD_ZERO(&input_mask);
+       FD_SET(conn->sock, &input_mask);
+       timeout.tv_sec = 0;
+       timeout.tv_usec = 0;
+ retry:
+       if (select(conn->sock + 1, (fd_set *) NULL, &input_mask, (fd_set *) NULL,
+                          &timeout) < 0)
+       {
+               if (errno == EINTR)
+                       /* Interrupted system call - we'll just try again */
+                       goto retry;
+
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 "pqWriteReady() -- select() failed: errno=%d\n%s\n",
+                                                 errno, strerror(errno));
+               return -1;
        }
-       return FD_ISSET(conn->sock, &input_mask);
+       return FD_ISSET(conn->sock, &input_mask) ? 1 : 0;
 }
 
 /* --------------------------------------------------------------------- */
@@ -418,8 +458,17 @@ tryAgain:
         * be taken much, since in normal practice we should not be trying to
         * read data unless the file selected for reading already.
         */
-       if (!pqReadReady(conn))
-               return 0;                               /* definitely no data available */
+       switch (pqReadReady(conn))
+       {
+               case 0:
+                       /* definitely no data available */
+                       return 0;
+               case 1:
+                       /* ready for read */
+                       break;
+               default:
+                       goto definitelyFailed;
+       }
 
        /*
         * Still not sure that it's EOF, because some data could have just
@@ -570,6 +619,10 @@ pqFlush(PGconn *conn)
                if (len > 0)
                {
                        /* We didn't send it all, wait till we can send more */
+
+                       /* At first glance this looks as though it should block.  I think
+                        * that it will be OK though, as long as the socket is
+                        * non-blocking. */
                        if (pqWait(FALSE, TRUE, conn))
                                return EOF;
                }
@@ -599,9 +652,9 @@ pqWait(int forRead, int forWrite, PGconn *conn)
                return EOF;
        }
 
-       /* loop in case select returns EINTR */
-       for (;;)
+       if (forRead || forWrite)
        {
+       retry:
                FD_ZERO(&input_mask);
                FD_ZERO(&output_mask);
                if (forRead)
@@ -612,14 +665,12 @@ pqWait(int forRead, int forWrite, PGconn *conn)
                                   (struct timeval *) NULL) < 0)
                {
                        if (errno == EINTR)
-                               continue;
+                               goto retry;
                        printfPQExpBuffer(&conn->errorMessage,
                                                          "pqWait() -- select() failed: errno=%d\n%s\n",
                                                          errno, strerror(errno));
                        return EOF;
                }
-               /* On nonerror return, assume we're done */
-               break;
        }
 
        return 0;
index b1c97e046a60ec93439798182fd063bd3a4279d0..5a25c40121f199cc642f52da0d6361a700286bf5 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq-fe.h,v 1.52 1999/11/11 00:10:14 momjian Exp $
+ * $Id: libpq-fe.h,v 1.53 1999/11/30 03:08:19 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,10 +29,40 @@ extern              "C"
 
        typedef enum
        {
+               /* Although you may decide to change this list in some way,
+                  values which become unused should never be removed, nor
+           should constants be redefined - that would break 
+           compatibility with existing code.                           */
                CONNECTION_OK,
-               CONNECTION_BAD
+               CONNECTION_BAD,
+               /* Non-blocking mode only below here */
+               /* The existence of these should never be relied upon - they
+                  should only be used for user feedback or similar purposes.  */
+               CONNECTION_STARTED,     /* Waiting for connection to be made.  */
+               CONNECTION_MADE,        /* Connection OK; waiting to send.     */
+               CONNECTION_AWAITING_RESPONSE,   /* Waiting for a response
+                                                                                  from the backend.           */
+               CONNECTION_AUTH_RESPONSE,       /* Got an authentication
+                                                                                  response; about to deal
+                                                                                  with it.                    */
+               CONNECTION_ERROR_RESPONSE,      /* Got an error
+                                                                                  response; about to deal 
+                                                                                  with it.                    */
+               CONNECTION_AUTH_OK,             /* Received authentication;
+                                                                                  waiting for ReadyForQuery
+                                                                                  etc.                        */
+               CONNECTION_SETENV               /* Negotiating environment.    */
        } ConnStatusType;
 
+       typedef enum
+       {
+               PGRES_POLLING_FAILED = 0,
+               PGRES_POLLING_READING,     /* These two indicate that one may    */
+               PGRES_POLLING_WRITING,     /* use select before polling again.   */
+               PGRES_POLLING_OK,
+               PGRES_POLLING_ACTIVE       /* Can call poll function immediately.*/
+       } PostgresPollingStatusType;
+
        typedef enum
        {
                PGRES_EMPTY_QUERY = 0,
@@ -67,6 +97,12 @@ extern               "C"
  */
        typedef struct pg_result PGresult;
 
+/* PGsetenvHandle is an opaque handle which is returned by PQsetenvStart and
+ * which should be passed to PQsetenvPoll or PQsetenvAbort in order to refer
+ * to the particular process being performed.
+ */
+       typedef struct pg_setenv_state *PGsetenvHandle;
+
 /* PGnotify represents the occurrence of a NOTIFY message.
  * Ideally this would be an opaque typedef, but it's so simple that it's
  * unlikely to change.
@@ -152,11 +188,15 @@ extern            "C"
 /* === in fe-connect.c === */
 
        /* make a new client connection to the backend */
+       /* Asynchronous (non-blocking) */
+       extern PGconn *PQconnectStart(const char *conninfo);
+       extern PostgresPollingStatusType PQconnectPoll(PGconn *conn);
+       /* Synchronous (blocking) */
        extern PGconn *PQconnectdb(const char *conninfo);
        extern PGconn *PQsetdbLogin(const char *pghost, const char *pgport,
                                                                const char *pgoptions, const char *pgtty,
-                                                                                       const char *dbName,
-                                                                        const char *login, const char *pwd);
+                                                               const char *dbName,
+                                                               const char *login, const char *pwd);
 #define PQsetdb(M_PGHOST,M_PGPORT,M_PGOPT,M_PGTTY,M_DBNAME)  \
        PQsetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, NULL, NULL)
 
@@ -170,6 +210,10 @@ extern             "C"
         * close the current connection and restablish a new one with the same
         * parameters
         */
+       /* Asynchronous (non-blocking) */
+       extern int PQresetStart(PGconn *conn);
+       extern PostgresPollingStatusType PQresetPoll(PGconn *conn);
+       /* Synchronous (blocking) */
        extern void PQreset(PGconn *conn);
 
        /* issue a cancel request */
@@ -195,6 +239,15 @@ extern             "C"
        /* Override default notice processor */
        extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg);
 
+       /* Passing of environment variables */
+       /* Asynchronous (non-blocking) */
+       extern PGsetenvHandle PQsetenvStart(PGconn *conn);
+       extern PostgresPollingStatusType PQsetenvPoll(PGsetenvHandle handle);
+       extern void PQsetenvAbort(PGsetenvHandle handle);
+
+       /* Synchronous (blocking) */
+       extern int PQsetenv(PGconn *conn);
+
 /* === in fe-exec.c === */
 
        /* Simple synchronous query */
index 7323e9fe065db71d8f429b29c0706bbfdc444a78..64566d6c743f5273e24f8757c81e79d5a109e532 100644 (file)
@@ -11,7 +11,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq-int.h,v 1.13 1999/11/11 00:10:14 momjian Exp $
+ * $Id: libpq-int.h,v 1.14 1999/11/30 03:08:19 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -168,6 +168,10 @@ struct pg_conn
        /* Saved values of connection options */
        char       *pghost;                     /* the machine on which the server is
                                                                 * running */
+       char       *pghostaddr;         /* the IPv4 address of the machine on
+                                                                * which the server is running, in
+                                                                * IPv4 numbers-and-dots notation. Takes
+                                                                * precedence over above. */
        char       *pgport;                     /* the server's communication port */
        char       *pgtty;                      /* tty on which the backend messages is
                                                                 * displayed (NOT ACTUALLY USED???) */
@@ -220,6 +224,9 @@ struct pg_conn
        PGresult   *result;                     /* result being constructed */
        PGresAttValue *curTuple;        /* tuple currently being read */
 
+       /* Handle for setenv request.  Used during connection only. */
+       PGsetenvHandle setenv_handle;
+
 #ifdef USE_SSL
         SSL *ssl;
 #endif
@@ -268,6 +275,8 @@ extern int  pqPutInt(int value, size_t bytes, PGconn *conn);
 extern int     pqReadData(PGconn *conn);
 extern int     pqFlush(PGconn *conn);
 extern int     pqWait(int forRead, int forWrite, PGconn *conn);
+extern int     pqReadReady(PGconn *conn);
+extern int     pqWriteReady(PGconn *conn);
 
 /* bits in a byte */
 #define BYTELEN 8