]> granicus.if.org Git - postgresql/commitdiff
Update libpq to make new features of FE/BE protocol available to
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 21 Jun 2003 21:51:35 +0000 (21:51 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 21 Jun 2003 21:51:35 +0000 (21:51 +0000)
client applications.  Some editorial work on libpq.sgml, too.

doc/src/sgml/libpq.sgml
doc/src/sgml/lobj.sgml
src/interfaces/libpq/blibpqdll.def
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/fe-exec.c
src/interfaces/libpq/fe-misc.c
src/interfaces/libpq/fe-protocol2.c
src/interfaces/libpq/fe-protocol3.c
src/interfaces/libpq/libpq-fe.h
src/interfaces/libpq/libpq-int.h
src/interfaces/libpq/libpqdll.def

index 88217d4506313635465203dc1b9d22615da3a495..65201ccf55a2522a6dba913de753537771e657d7 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.124 2003/06/13 23:10:07 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.125 2003/06/21 21:51:30 tgl Exp $
 -->
 
  <chapter id="libpq">
@@ -24,7 +24,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.124 2003/06/13 23:10:07 momj
   </para>
 
   <para>
-   Three short programs are included at the end of this chapter (<xref linkend="libpq-example">) to show how
+   Some short programs are included at the end of this chapter (<xref linkend="libpq-example">) to show how
    to write programs that use <application>libpq</application>.  There are also several
    complete examples of <application>libpq</application> applications in the
    directory <filename>src/test/examples</filename> in the source code distribution.
@@ -37,7 +37,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.124 2003/06/13 23:10:07 momj
   </para>
 
  <sect1 id="libpq-connect">
-  <title>Database Connection Functions</title>
+  <title>Database Connection Control Functions</title>
 
   <para>
    The following functions deal with making a connection to a
@@ -68,8 +68,8 @@ PGconn *PQconnectdb(const char *conninfo);
    This function opens a new database connection using the parameters taken
    from the string <literal>conninfo</literal>.  Unlike <function>PQsetdbLogin</> below,
    the parameter set can be extended without changing the function signature,
-   so use either of this function or the nonblocking analogues <function>PQconnectStart</>
-   and <function>PQconnectPoll</function> is preferred for new application programming.
+   so use of this function (or its nonblocking analogues <function>PQconnectStart</>
+   and <function>PQconnectPoll</function>) is preferred for new application programming.
    </para>
 
    <para>
@@ -86,7 +86,7 @@ PGconn *PQconnectdb(const char *conninfo);
    </para>
 
    <para>
-   The currently recognized parameter key words are:
+   The currently recognized parameter keywords are:
 
    <variablelist>
     <varlistentry>
@@ -107,11 +107,11 @@ PGconn *PQconnectdb(const char *conninfo);
      <term><literal>hostaddr</literal></term>
      <listitem>
      <para>
-      IP address of host to connect to.  This should be in the
+      Numeric IP address of host to connect to.  This should be in the
       standard IPv4 address format, e.g., <literal>172.28.40.9</>.  If
-      your machine supports IPv6, you can also use those addresses. If
-      a nonzero-length string is specified, TCP/IP communication is
-      used.
+      your machine supports IPv6, you can also use those addresses.
+      TCP/IP communication is
+      always used when a nonempty string is specified for this parameter.
      </para>
      <para>
       Using <literal>hostaddr</> instead of <literal>host</> allows the
@@ -119,9 +119,10 @@ PGconn *PQconnectdb(const char *conninfo);
       applications with time constraints. However, Kerberos authentication
       requires the host name. The following therefore applies: If
       <literal>host</> is specified without <literal>hostaddr</>, a host name
-      lookup is forced. If <literal>hostaddr</> is specified without
+      lookup occurs. If <literal>hostaddr</> is specified without
       <literal>host</>, the value for <literal>hostaddr</> gives the remote
-      address; if Kerberos is used, this causes a reverse name query. If both
+      address. When Kerberos is used, a reverse name query occurs to obtain
+      the host name for Kerberos. If both
       <literal>host</> and <literal>hostaddr</> are specified, the value for
       <literal>hostaddr</> gives the remote address; the value for
       <literal>host</> is ignored, unless Kerberos is used, in which case that
@@ -153,7 +154,7 @@ PGconn *PQconnectdb(const char *conninfo);
      <term><literal>dbname</literal></term>
      <listitem>
      <para>
-      The database name.
+      The database name.  Defaults to be the same as the user name.
      </para>
      </listitem>
     </varlistentry>
@@ -162,7 +163,7 @@ PGconn *PQconnectdb(const char *conninfo);
      <term><literal>user</literal></term> 
      <listitem>
      <para>
-      User name to connect as.
+      <productname>PostgreSQL</productname> user name to connect as.
      </para>
      </listitem>
     </varlistentry>
@@ -182,7 +183,7 @@ PGconn *PQconnectdb(const char *conninfo);
      <para>
       Maximum wait for connection, in seconds (write as a decimal integer
       string). Zero or not specified means wait indefinitely.  It is not
-      recommended to set the timeout to less than 2 seconds.
+      recommended to use a timeout of less than 2 seconds.
      </para>
      </listitem>
     </varlistentry>
@@ -237,7 +238,7 @@ PGconn *PQconnectdb(const char *conninfo);
    If  any  parameter is unspecified, then the corresponding
    environment variable (see <xref linkend="libpq-envars">)
    is checked. If the  environment  variable is not set either,
-   then hardwired defaults are used.
+   then built-in defaults are used.
    </para>
   </listitem>
  </varlistentry>
@@ -260,8 +261,9 @@ PGconn *PQsetdbLogin(const char *pghost,
 
 <para>
    This is the predecessor of <function>PQconnectdb</function> with a fixed
-   number of parameters.  It has the same functionality except that the
-   missing parameters cannot be specified in the call.
+   set of parameters.  It has the same functionality except that the
+   missing parameters will always take on default values.  Write NULL or an
+   empty string for any one of the fixed parameters that is to be defaulted.
    </para>
   </listitem>
  </varlistentry>
@@ -306,6 +308,10 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn);
    These two functions 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.
+   The point of this approach is that the waits for I/O to complete can occur
+   in the application's main loop, rather than down inside
+   <function>PQconnectdb()</>, and so the application can manage this
+   operation in parallel with other activities.
   </para>
   <para>
    The database connection is made using the parameters taken from the string
@@ -313,7 +319,7 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn);
    the same format as described above for <function>PQconnectdb</function>.
   </para>
   <para>
-   Neither <function>PQconnectStart</function> nor <function>PQconnectPoll</function> will block, as long as a number of
+   Neither <function>PQconnectStart</function> nor <function>PQconnectPoll</function> will block, so long as a number of
    restrictions are met:
    <itemizedlist>
     <listitem>
@@ -326,14 +332,14 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn);
 
     <listitem>
      <para>
-      If you call <function>PQtrace</function>, ensure that the stream object into which you trace
-      will not block.
+      If you call <function>PQtrace</function>, 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
+      You ensure that the socket is in the appropriate state
       before calling <function>PQconnectPoll</function>, as described below.
      </para>
     </listitem>
@@ -349,28 +355,31 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn);
    <symbol>CONNECTION_BAD</symbol>, <function>PQconnectStart</function> has failed.
   </para>
   <para>
-   If <function>PQconnectStart</> succeeds, the next stage is to poll <application>libpq</> so that it may
-   proceed with the connection sequence.  Loop thus: If <function>PQconnectPoll(conn)</function> last returned
-   <symbol>PGRES_POLLING_READING</symbol>, perform a <function>select()</> for reading on the socket determined using <function>PQsocket(conn)</function>. If
-   it last returned <symbol>PGRES_POLLING_WRITING</symbol>, perform a <function>select()</> for writing on
-   that same socket. If you have yet to call <function>PQconnectPoll</function>, i.e., after the call
-   to <function>PQconnectStart</function>, behave as if it last returned <symbol>PGRES_POLLING_WRITING</symbol>.  If
-   <function>select()</> shows that the socket is ready, consider it <quote>active</quote>. If it has
-   been decided that this connection is <quote>active</quote>, call <function>PQconnectPoll(conn)</function>
-   again. If this call returns <symbol>PGRES_POLLING_FAILED</symbol>, the connection procedure
-   has failed.  If this call returns <symbol>PGRES_POLLING_OK</symbol>, the connection has been
-   successfully made.
-  </para>
-
-  <para>
-    Note that the use of <function>select()</function> to ensure that the socket is ready is merely
-    a (likely) example; those with other facilities available, such as a
-    <function>poll()</function> call, may of course use that instead.
+   If <function>PQconnectStart</> succeeds, the next stage is to poll
+   <application>libpq</> so that it may proceed with the connection sequence.
+   Use <function>PQsocket(conn)</function> to obtain the descriptor of the
+   socket underlying the database connection.
+   Loop thus: If <function>PQconnectPoll(conn)</function> last returned
+   <symbol>PGRES_POLLING_READING</symbol>, wait until the socket is ready to
+   read (as indicated by <function>select()</>, <function>poll()</>, or
+   similar system function).
+   Then call <function>PQconnectPoll(conn)</function> again.
+   Conversely, if <function>PQconnectPoll(conn)</function> last returned
+   <symbol>PGRES_POLLING_WRITING</symbol>, wait until the socket is ready
+   to write, then call <function>PQconnectPoll(conn)</function> again.
+   If you have yet to call
+   <function>PQconnectPoll</function>, i.e., just after the call to
+   <function>PQconnectStart</function>, behave as if it last returned
+   <symbol>PGRES_POLLING_WRITING</symbol>.  Continue this loop until
+   <function>PQconnectPoll(conn)</function> returns
+   <symbol>PGRES_POLLING_FAILED</symbol>, indicating the connection procedure
+   has failed, or <symbol>PGRES_POLLING_OK</symbol>, indicating the connection
+   has been successfully made.
   </para>
 
   <para>
     At any time during connection, the status of the connection may be
-    checked, by calling <function>PQstatus</>. If this gives <symbol>CONNECTION_BAD</>, then the
+    checked by calling <function>PQstatus</>. If this gives <symbol>CONNECTION_BAD</>, then the
     connection procedure has failed; if it gives <function>CONNECTION_OK</>, then the
     connection is ready.  Both of these states are equally detectable
     from the return value of <function>PQconnectPoll</>, described above. Other states may also occur
@@ -410,17 +419,25 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn);
       <term><symbol>CONNECTION_AUTH_OK</symbol></term>
       <listitem>
        <para>
-        Received authentication; waiting for connection start-up to continue.
+        Received authentication; waiting for backend start-up to finish.
        </para>
       </listitem>
      </varlistentry>
 
      <varlistentry>
-      <term><symbol>CONNECTION_SETENV</symbol></term>
+      <term><symbol>CONNECTION_SSL_STARTUP</symbol></term>
+      <listitem>
+       <para>
+        Negotiating SSL encryption.
+       </para>
+      </listitem>
+     </varlistentry>
 
+     <varlistentry>
+      <term><symbol>CONNECTION_SETENV</symbol></term>
       <listitem>
        <para>
-        Negotiating environment (part of the connection start-up).
+        Negotiating environment-driven parameter settings.
        </para>
       </listitem>
      </varlistentry>
@@ -429,7 +446,7 @@ PostgresPollingStatusType PQconnectPoll(PGconn *conn);
     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:
+    documented values. An application might do something like this:
 <programlisting>
 switch(PQstatus(conn))
 {
@@ -450,20 +467,19 @@ switch(PQstatus(conn))
   </para>
 
   <para>
-   Note that if <function>PQconnectStart</function> returns a non-null pointer, you must call
-   <function>PQfinish</function> 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 <function>PQconnectStart</function> or <function>PQconnectPoll</function> failed.
-  </para>
-
-  <para>
-   <function>PQconnectPoll</function> will currently block if
-   <application>libpq</> is compiled with SSL support. This restriction may be removed in the future.
+   The <literal>connect_timeout</literal> connection parameter is ignored
+   when using <function>PQconnectPoll</function>; it is the application's
+   responsibility to decide whether an excessive amount of time has elapsed.
+   Otherwise, <function>PQconnectStart</function> followed by a
+   <function>PQconnectPoll</function> loop is equivalent to
+   <function>PQconnectdb</function>.
   </para>
 
   <para>
-   Finally, these functions leave the connection in a nonblocking state as if 
-   <function>PQsetnonblocking</function> had been called.
+   Note that if <function>PQconnectStart</function> returns a non-null pointer, you must call
+   <function>PQfinish</function> when you are finished with it, in order to dispose of
+   the structure and any associated memory blocks. This must be done even if
+   the connection attempt fails or is abandoned.
   </para>
   </listitem>
  </varlistentry>
@@ -498,7 +514,8 @@ typedef struct
    be used to determine all possible <function>PQconnectdb</function> options and their
    current default values.  The return value points to an array of
    <structname>PQconninfoOption</structname> structures, which ends with an entry having a null
-   key-word pointer.  Note that the current default values (<structfield>val</structfield> fields)
+   <structfield>keyword</> pointer.  Note that the current default values
+   (<structfield>val</structfield> fields)
    will depend on environment variables and other context.
    Callers must treat the connection options data as read-only.
    </para>
@@ -527,7 +544,8 @@ void PQfinish(PGconn *conn);
    Note that even if the server connection attempt fails (as
    indicated by <function>PQstatus</function>), the application should call <function>PQfinish</function>
    to free the memory used by the <structname>PGconn</structname> object.
-   The <structname>PGconn</> pointer should not be used after <function>PQfinish</function> has been called.
+   The <structname>PGconn</> pointer must not be used again after
+   <function>PQfinish</function> has been called.
    </para>
   </listitem>
  </varlistentry>
@@ -584,12 +602,23 @@ PostgresPollingStatusType PQresetPoll(PGconn *conn);
 
  </variablelist>
 </para>
+</sect1>
+
+<sect1 id="libpq-status">
+<title>Connection Status Functions</title>
 
+  <para>
+   These functions may be used to interrogate the status
+   of an existing database connection object.
+  </para>
+
+<tip>
 <para>
 <indexterm><primary>libpq-fe.h</></>
 <indexterm><primary>libpq-int.h</></>
 <application>libpq</application> application programmers should be careful to
-maintain the <structname>PGconn</structname> abstraction.  Use the accessor functions below to get
+maintain the <structname>PGconn</structname> abstraction.  Use the accessor
+functions described below to get
 at the contents of <structname>PGconn</structname>.  Avoid directly referencing the fields of the
 <structname>PGconn</> structure because they are subject to change in the future.
 (Beginning in <productname>PostgreSQL</productname> release 6.4, the
@@ -597,6 +626,12 @@ definition of the <type>struct</type> behind <structname>PGconn</> is not even p
 If you have old code that accesses <structname>PGconn</structname> fields directly, you can keep using it
 by including <filename>libpq-int.h</filename> too, but you are encouraged to fix the code
 soon.)
+</para>
+</tip>
+
+<para>
+The following functions return parameter values established at connection.
+These values are fixed for the life of the <structname>PGconn</> object.
 
 <variablelist>
 <varlistentry>
@@ -608,12 +643,6 @@ soon.)
 char *PQdb(const PGconn *conn);
 </synopsis>
 </para>
-
-<para>
-<function>PQdb</> and the next several functions return the values established
-at connection.  These values are fixed for the life of the <structname>PGconn</>
-object.
-</para>
 </listitem>
 </varlistentry>
 
@@ -691,7 +720,14 @@ char *PQoptions(const PGconn *conn);
 </para>
 </listitem>
 </varlistentry>
+</variablelist>
+</para>
+
+<para>
+The following functions return status data that can change as operations
+are executed on the <structname>PGconn</> object.
 
+<variablelist>
 <varlistentry>
 <term><function>PQstatus</function></term>
 <listitem>
@@ -728,6 +764,91 @@ ConnStatusType PQstatus(const PGconn *conn);
      </listitem>
     </varlistentry>
 
+<varlistentry>
+<term><function>PQtransactionStatus</function></term>
+<listitem>
+<para>
+         Returns the current in-transaction status of the server.
+<synopsis>
+PGTransactionStatusType PQtransactionStatus(const PGconn *conn);
+</synopsis>
+
+The status can be <literal>PQTRANS_IDLE</literal> (currently idle),
+<literal>PQTRANS_ACTIVE</literal> (a command is in progress),
+<literal>PQTRANS_INTRANS</literal> (idle, in a valid transaction block),
+or <literal>PQTRANS_INERROR</literal> (idle, in a failed transaction block).
+<literal>PQTRANS_UNKNOWN</literal> is reported if the connection is bad.
+<literal>PQTRANS_ACTIVE</literal> is reported only when a query
+has been sent to the server and not yet completed.
+</para>
+<caution>
+<para>
+<function>PQtransactionStatus</> will give incorrect results when using
+a <productname>PostgreSQL</> 7.3 server that has <literal>AUTOCOMMIT</>
+set to <literal>OFF</>.  The server-side autocommit feature has been
+deprecated and does not exist in later server versions.
+</para>
+</caution>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><function>PQparameterStatus</function></term>
+<listitem>
+<para>
+         Looks up a current parameter setting of the server.
+<synopsis>
+const char *PQparameterStatus(const PGconn *conn, const char *paramName);
+</synopsis>
+
+Certain parameter values are reported by the server automatically at
+connection startup or whenever their values change.
+<function>PQparameterStatus</> can be used to interrogate these settings.
+It returns the current value of a parameter if known, or NULL if the parameter
+is not known.
+</para>
+
+<para>
+Parameters reported as of the current release include
+<literal>server_version</> (cannot change after startup);
+<literal>server_encoding</> (also not presently changeable after start);
+<literal>client_encoding</>, and
+<literal>DateStyle</>.
+</para>
+
+<para>
+Pre-3.0-protocol servers do not report parameter settings,
+but <application>libpq</> includes logic to obtain values for 
+<literal>server_version</>, <literal>server_encoding</>, and
+<literal>client_encoding</>.  Applications are encouraged to use
+<function>PQparameterStatus</> rather than ad-hoc code to determine these
+values.  (Beware however that on a pre-3.0 connection, changing
+<literal>client_encoding</> via <command>SET</> after connection startup
+will not be reflected by <function>PQparameterStatus</>.)
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><function>PQprotocolVersion</function></term>
+<listitem>
+<para>
+         Interrogates the frontend/backend protocol being used.
+<synopsis>
+int PQprotocolVersion(const PGconn *conn);
+</synopsis>
+Applications may wish to use this to determine whether certain features
+are supported.
+Currently, the possible values are 2 (2.0 protocol), 3 (3.0 protocol),
+or zero (connection bad).  This will not change after connection
+startup is complete, but it could theoretically change during a reset.
+The 3.0 protocol will normally be used when communicating with
+<productname>PostgreSQL</> 7.4 or later servers; pre-7.4 servers support
+only protocol 2.0.  (Protocol 1.0 is obsolete and not supported by libpq.)
+</para>
+</listitem>
+</varlistentry>
+
     <varlistentry>
      <term><function>PQerrorMessage</function></term>
      <listitem>
@@ -757,7 +878,8 @@ char *PQerrorMessage(const PGconn* conn);
        Obtains the file descriptor number of the connection socket to
        the server.  A valid descriptor will be greater than or equal
        to 0; a result of -1 indicates that no server connection is
-       currently open.
+       currently open.  (This will not change during normal operation,
+       but could change during connection setup or reset.)
 <synopsis>
 int PQsocket(const PGconn *conn);
 </synopsis>
@@ -812,9 +934,10 @@ SSL *PQgetssl(const PGconn *conn);
      </listitem>
     </varlistentry>
 
-    </variablelist>
-   </para>
-  </sect1>
+</variablelist>
+</para>
+
+</sect1>
 
 <sect1 id="libpq-exec">
 <title>Command Execution Functions</title>
@@ -828,6 +951,7 @@ SQL queries and commands.
 <sect2 id="libpq-exec-main">
   <title>Main Functions</title>
 
+<para>
 <variablelist>
 <varlistentry>
 <term><function>PQexec</function></term>
@@ -836,8 +960,7 @@ SQL queries and commands.
           Submits a command to the server
           and waits for the result.
 <synopsis>
-PGresult *PQexec(PGconn *conn,
-                 const char *command);
+PGresult *PQexec(PGconn *conn, const char *command);
 </synopsis>
 </para>
 
@@ -854,21 +977,95 @@ PGresult *PQexec(PGconn *conn,
 </varlistentry>
 </variablelist>
 
+It is allowed to include multiple SQL commands (separated by semicolons) in
+the command string.  Multiple queries sent in a single <function>PQexec</>
+call are processed in a single transaction, unless there are explicit
+BEGIN/COMMIT commands included in the query string to divide it into multiple
+transactions.  Note however that the returned <structname>PGresult</structname>
+structure describes only the result of the last command executed from the
+string.  Should one of the commands fail, processing of the string stops with
+it and the returned <structname>PGresult</structname> describes the error
+condition.
+</para>
+
+<para>
+<variablelist>
+<varlistentry>
+<term><function>PQexecParams</function></term>
+<listitem>
+<para>
+          Submits a command to the server and waits for the result,
+         with the ability to pass parameters separately from the SQL
+         command text.
+<synopsis>
+PGresult *PQexecParams(PGconn *conn,
+                       const char *command,
+                       int nParams,
+                       const Oid *paramTypes,
+                       const char * const *paramValues,
+                       const int *paramLengths,
+                       const int *paramFormats,
+                       int resultFormat);
+</synopsis>
+</para>
+
+<para>
+<function>PQexecParams</> is like <function>PQexec</>, but offers additional
+functionality: parameter values can be specified separately from the command
+string proper, and query results can be requested in either text or binary
+format.  <function>PQexecParams</> is supported only in protocol 3.0 and later
+connections; it will fail when using protocol 2.0.
+</para>
+
+<para>
+If parameters are used, they are referred to in the command string
+as <literal>$1</>, <literal>$2</>, etc.
+<parameter>nParams</> is the number of parameters supplied; it is the length
+of the arrays <parameter>paramTypes[]</>, <parameter>paramValues[]</>,
+<parameter>paramLengths[]</>, and <parameter>paramFormats[]</>.  (The
+array pointers may be NULL when <parameter>nParams</> is zero.)
+<parameter>paramTypes[]</> specifies, by OID, the datatypes to be assigned to
+the parameter symbols.  If <parameter>paramTypes</> is NULL, or any particular
+element in the array is zero, the backend assigns a datatype to the parameter
+symbol in the same way it would do for an untyped literal string.
+<parameter>paramValues[]</> specifies the actual values of the parameters.
+A NULL pointer in this array means the corresponding parameter is NULL;
+otherwise the pointer points to a zero-terminated text string (for text
+format) or binary data in the format expected by the backend (for binary
+format).
+<parameter>paramLengths[]</> specifies the actual data lengths of
+binary-format parameters.  It is ignored for NULL parameters and text-format
+parameters.  The array pointer may be NULL when there are no binary
+parameters.
+<parameter>paramFormats[]</> specifies whether parameters are text (put a zero
+in the array) or binary (put a one in the array).  If the array pointer is
+NULL then all parameters are presumed to be text.
+<parameter>resultFormat</> is zero to obtain results in text format, or one to
+obtain results in binary format.  (There is not currently a provision to
+obtain different result columns in different formats, although that is
+possible in the underlying protocol.)
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+The primary advantage of <function>PQexecParams</> over <function>PQexec</>
+is that parameter values may be separated from the command string, thus
+avoiding the need for tedious and error-prone quoting and escaping.
+
+Unlike <function>PQexec</>, <function>PQexecParams</> allows at most one SQL
+command in the given string.  (There can be semicolons in it, but not more
+than one nonempty command.)  This is a limitation of the underlying protocol,
+but has some usefulness as an extra defense against SQL-injection attacks.
+</para>
+
 <para>
-The <function>PGresult</function> structure encapsulates the result
+The <structname>PGresult</structname> structure encapsulates the result
 returned by the server.
 <application>libpq</application> application programmers should be careful to
 maintain the <structname>PGresult</structname> abstraction.  Use the accessor functions below to get
 at the contents of <structname>PGresult</structname>.  Avoid directly referencing the fields of the
 <structname>PGresult</structname> structure because they are subject to change in the future.
-</para>
-
-<para>
-Multiple queries sent in a single
-function call are processed in a single transaction, unless there are explicit
-BEGIN/COMMIT commands included in the query string to divide it into multiple
-transactions.
-</para>
 
 <variablelist>
 <varlistentry>
@@ -902,7 +1099,8 @@ ExecStatusType PQresultStatus(const PGresult *res);
  <varlistentry>
   <term><literal>PGRES_TUPLES_OK</literal></term>
   <listitem>
-   <para>The query successfully executed.</para>
+   <para>Successful completion of a command returning data (such as
+   a <command>SELECT</> or <command>SHOW</>).</para>
   </listitem>
  </varlistentry>
 
@@ -930,7 +1128,7 @@ ExecStatusType PQresultStatus(const PGresult *res);
  <varlistentry>
   <term><literal>PGRES_NONFATAL_ERROR</literal></term>
   <listitem>
-   <para>A nonfatal error occurred.</para>
+   <para>A nonfatal error (a notice or warning) occurred.</para>
   </listitem>
  </varlistentry>
 
@@ -948,8 +1146,15 @@ the query.  Note that a <command>SELECT</command> command that happens
 to retrieve zero rows still shows <literal>PGRES_TUPLES_OK</literal>.
 <literal>PGRES_COMMAND_OK</literal> is for commands that can never
 return rows (<command>INSERT</command>, <command>UPDATE</command>,
-etc.). A response of <literal>PGRES_EMPTY_QUERY</literal> often
-exposes a bug in the client software.
+etc.). A response of <literal>PGRES_EMPTY_QUERY</literal> may indicate
+a bug in the client software.
+</para>
+
+<para>
+A result of status <symbol>PGRES_NONFATAL_ERROR</symbol> will never be
+returned directly by <function>PQexec</function> or other query
+execution functions; results of this kind are instead passed to the notice
+processor (see <xref linkend="libpq-notice-processing">).
 </para>
 </listitem>
 </varlistentry>
@@ -976,6 +1181,7 @@ if there was no error.
 <synopsis>
 char *PQresultErrorMessage(const PGresult *res);
 </synopsis>
+If there was an error, the returned string will include a trailing newline.
 </para>
 
 <para>
@@ -991,6 +1197,39 @@ when you want to know the status from the latest operation on the connection.
 </listitem>
 </varlistentry>
 
+<varlistentry>
+<term><function>PQresultErrorField</function></term>
+<listitem>
+<para>
+Returns an individual field of an error report.
+<synopsis>
+char *PQresultErrorField(const PGresult *res, int fieldcode);
+</synopsis>
+<parameter>fieldcode</> is an error field identifier defined by the
+<productname>PostgreSQL</> protocol (see <xref
+linkend="protocol-error-fields">), for example <literal>'C'</> for
+the SQLSTATE error code.  NULL is returned if the
+<structname>PGresult</structname> is not an error or warning result,
+or does not include the specified field.  Field values will normally
+not include a trailing newline.
+</para>
+
+<para>
+Errors generated internally by libpq will have severity and primary message,
+but typically no other fields.  Errors returned by a pre-3.0-protocol server
+will include severity and primary message, and sometimes a detail message,
+but no other fields.
+</para>
+
+<para>
+Note that error fields are only available from
+<structname>PGresult</structname> objects, not
+<structname>PGconn</structname> objects; there is no
+<function>PQerrorField</function> function.
+</para>
+</listitem>
+</varlistentry>
+
 <varlistentry>
 <term><function>PQclear</function></term>
 <listitem>
@@ -1008,7 +1247,7 @@ void PQclear(PQresult *res);
           need it; it does not go away when you issue a new command,
           nor even if you close the connection.  To get rid of it,
           you must call <function>PQclear</function>.  Failure to do this will
-          result in memory leaks in your client application.
+          result in memory leaks in your application.
 </para>
 </listitem>
 </varlistentry>
@@ -1035,161 +1274,20 @@ as with a <structname>PGresult</structname> returned by <application>libpq</appl
 </listitem>
 </varlistentry>
 </variablelist>
-</sect2>
-
-<sect2 id="libpq-exec-escape-string">
-  <title>Escaping Strings for Inclusion in SQL Commands</title>
-
-   <indexterm zone="libpq-exec-escape-string"><primary>escaping strings</></>
-
-<para>
-<function>PQescapeString</function> escapes a string for use within an SQL commmand.
-<synopsis>
-size_t PQescapeString (char *to, const char *from, size_t length);
-</synopsis>
-</para>
-
-<para>
-If you want to use strings that have been received
-from a source that is not trustworthy (for example, because a random user
-entered them), you should not directly include them in SQL
-commands for security reasons.  Instead, you have to escape certain
-characters that are otherwise interpreted specially by the SQL parser.
-<function>PQescapeString</> performs this operation.
-</para>
-<para>
-The parameter <parameter>from</> points to the first character of the string that
-is to be escaped, and the <parameter>length</> parameter counts the
-number of characters in this string.  (A terminating zero byte is
-neither necessary nor counted.)  <parameter>to</> shall point to a
-buffer that is able to hold at least one more character than twice
-the value of <parameter>length</>, otherwise the behavior is
-undefined.  A call to <function>PQescapeString</> writes an escaped
-version of the <parameter>from</> string to the <parameter>to</>
-buffer, replacing special characters so that they cannot cause any
-harm, and adding a terminating zero byte.  The single quotes that
-must surround <productname>PostgreSQL</> string literals are not part of the result
-string.
-</para>
-<para>
-<function>PQescapeString</> returns the number of characters written
-to <parameter>to</>, not including the terminating zero byte.
-Behavior is undefined when the <parameter>to</> and <parameter>from</>
-strings overlap.
 </para>
 </sect2>
 
-
- <sect2 id="libpq-exec-escape-bytea">
-  <title>Escaping Binary Strings for Inclusion in SQL Commands</title>
-  <indexterm zone="libpq-exec-escape-bytea">
-   <primary>escaping binary strings</primary>
-  </indexterm>
-
-  <variablelist>
-  <varlistentry>
-  <term><function>PQescapeBytea</function></term>
-  <listitem>
-  <para>
-   Escapes binary data for use within an SQL command with the type <type>bytea</type>.
-<synopsis>
-unsigned char *PQescapeBytea(const unsigned char *from,
-                             size_t from_length,
-                             size_t *to_length);
-</synopsis>
-</para>
-
-<para>
-   Certain byte values <emphasis>must</emphasis> be escaped (but all
-   byte values <emphasis>may</emphasis> be escaped) when used as part
-   of a <type>bytea</type> literal in an <acronym>SQL</acronym>
-   statement. In general, to escape a byte, it is converted into the
-   three digit octal number equal to the octet value, and preceded by
-   two backslashes. The single quote (<literal>'</>) and backslash
-   (<literal>\</>) characters have special alternative escape
-   sequences. See <xref linkend="datatype-binary"> for more
-   information. <function>PQescapeBytea</function> performs this
-   operation, escaping only the minimally required bytes.
-  </para>
-
-  <para>
-   The <parameter>from</parameter> parameter points to the first
-   byte of the string that is to be escaped, and the
-   <parameter>from_length</parameter> parameter reflects the number of
-   bytes in this binary string.  (A terminating zero byte is
-   neither necessary nor counted.)  The <parameter>to_length</parameter>
-   parameter points to a variable that will hold the resultant
-   escaped string length. The result string length includes the terminating
-   zero byte of the result.
-  </para>
-
-  <para>
-   <function>PQescapeBytea</> returns an escaped version of the
-   <parameter>from</parameter> parameter binary string in memory
-   allocated with <function>malloc()</>, and must be freed using
-   <function>PQfreemem()</>.
-   The return string has all special characters replaced
-   so that they can be properly processed by the PostgreSQL string literal
-   parser, and the <type>bytea</type> input function. A terminating zero
-   byte is also added.  The single quotes that must surround
-   PostgreSQL string literals are not part of the result string.
-  </para>
-  </listitem>
-  </varlistentry>
-
-  <varlistentry>
-  <term><function>PQunescapeBytea</function></term>
-  <listitem>
-  <para>
-   Converts an escaped string representation of binary data into binary
-   data --- the reverse of <function>PQescapeBytea</function>.
-<synopsis>
-unsigned char *PQunescapeBytea(const unsigned char *from, size_t *to_length);
-</synopsis>
-</para>
+<sect2 id="libpq-exec-select-info">
+  <title>Retrieving Query Result Information</title>
 
 <para>
-   The <parameter>from</parameter> parameter points to an escaped string
-   such as might be returned by <function>PQgetvalue</function> when applied to a
-   <type>bytea</type> column. <function>PQunescapeBytea</function> converts
-   this string representation into its binary representation.
-   It returns a pointer to a buffer allocated with
-   <function>malloc()</function>, or null on error, and puts the size of
-   the buffer in <parameter>to_length</parameter>. The memory must be
-   freed using <function>PQfreemem()</>.
-
-  </para>
-  </listitem>
-  </varlistentry>
-
-  <varlistentry>
-  <term><function>PQfreemem</function></term>
-  <listitem>
-  <para>
-   Frees memory allocated by <application>libpq</>
-<synopsis>
-void PQfreemem(void *ptr);
-</synopsis>
+These functions are used to extract information from a
+<structname>PGresult</structname> object that represents a successful
+query result (that is, one that has status
+<literal>PGRES_TUPLES_OK</literal>).  For objects with other status
+values they will act as though the result has zero rows and zero columns.
 </para>
 
-<para>
-   Frees memory allocated by <application>libpq</>, particularly
-   <function>PQescapeBytea</function>,
-   <function>PQunescapeBytea</function>,
-   and <function>PQnotifies</function>.
-   It is needed by Win32, which can not free memory across
-   DLL's, unless multithreaded DLL's (/MD in VC6) are used.
-  </para>
-  </listitem>
-  </varlistentry>
-  </variablelist>
-   
- </sect2>
-
-
-<sect2 id="libpq-exec-select-info">
-  <title>Retrieving Query Result Information</title>
-
 <variablelist>
 <varlistentry>
 <term><function>PQntuples</function></term>
@@ -1228,6 +1326,10 @@ char *PQfname(const PGresult *res,
               int column_number);
 </synopsis>
 </para>
+
+<para>
+NULL is returned if the column number is out of range.
+</para>
 </listitem>
 </varlistentry>
 
@@ -1250,13 +1352,83 @@ int PQfnumber(const PGresult *res,
 </varlistentry>
 
 <varlistentry>
-<term><function>PQftype</function></term>
+<term><function>PQftable</function></term>
 <listitem>
 <para>
-            Returns the column data type associated with the
-          given  column number.  The  integer  returned is the
-          internal OID number of the type.  Column numbers start
-          at 0.
+ Returns the OID of the table from which the given column was fetched.
+ Column numbers start at 0.
+<synopsis>
+Oid PQftable(const PGresult *res,
+             int column_number);
+</synopsis>
+</para>
+
+<para>
+<literal>InvalidOid</> is returned if the column number is out of range,
+or if the specified column is not a simple reference to a table column,
+or when using pre-3.0 protocol.
+You can query the system table <literal>pg_class</literal> to determine
+exactly which table is referenced.
+</para>
+
+<para>
+          The type <type>Oid</type> and the constant
+          <literal>InvalidOid</literal> will be defined when you include
+          the <application>libpq</application> header file. They will
+          both be some integer type.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><function>PQftablecol</function></term>
+<listitem>
+<para>
+ Returns the column number (within its table) of the column making up
+ the specified query result column.
+ Result column numbers start at 0.
+<synopsis>
+int PQftablecol(const PGresult *res,
+                int column_number);
+</synopsis>
+</para>
+
+<para>
+Zero is returned if the column number is out of range,
+or if the specified column is not a simple reference to a table column,
+or when using pre-3.0 protocol.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><function>PQfformat</function></term>
+<listitem>
+<para>
+ Returns the format code indicating the format of the given column.
+ Column numbers start at 0.
+<synopsis>
+int PQfformat(const PGresult *res,
+              int column_number);
+</synopsis>
+</para>
+
+<para>
+Format code zero indicates textual data representation, while format
+code one indicates binary representation.  (Other codes are reserved
+for future definition.)
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><function>PQftype</function></term>
+<listitem>
+<para>
+          Returns the data type associated with the
+          given  column number.  The  integer  returned is the
+          internal OID number of the type.  Column numbers start
+          at 0.
 <synopsis>
 Oid PQftype(const PGresult *res,
             int column_number);
@@ -1265,7 +1437,7 @@ Oid PQftype(const PGresult *res,
 
 <para>
 You can query the system table <literal>pg_type</literal> to obtain
-the name and properties of the various data types. The <acronym>OID</acronym>s
+the names and properties of the various data types. The <acronym>OID</acronym>s
 of the built-in data types are defined in the file <filename>src/include/catalog/pg_type.h</filename>
 in the source tree.
 </para>
@@ -1276,7 +1448,7 @@ in the source tree.
 <term><function>PQfmod</function></term>
 <listitem>
 <para>
-          Returns  the type-specific modification data of the column
+          Returns  the type modifier of the column
           associated with the given column number.
           Column numbers start at 0.
 <synopsis>
@@ -1284,6 +1456,13 @@ int PQfmod(const PGresult *res,
            int column_number);
 </synopsis>
 </para>
+
+<para>
+The interpretation of modifier values is type-specific; they typically
+indicate precision or size limits.  The value -1 is used to indicate
+<quote>no information available</>.  Most data types do not use modifiers,
+in which case the value is always -1.
+</para>
 </listitem>
 </varlistentry>
 
@@ -1301,9 +1480,10 @@ int PQfsize(const PGresult *res,
 </para>
 
 <para>
-       <function>PQfsize</> returns the space allocated for this column in a database
-       row, in other words the size of the server's binary representation
-       of the data type.  -1 is returned if the column has a variable size.
+<function>PQfsize</> returns the space allocated for this column in a database
+row, in other words the size of the server's internal representation
+of the data type.  (Accordingly, it is not really very useful to clients.)
+A negative value indicates the data type is variable-length.
 </para>
 </listitem>
 </varlistentry>
@@ -1312,33 +1492,31 @@ int PQfsize(const PGresult *res,
 <term><function>PQbinaryTuples</function></term>
 <listitem>
 <para>
-          Returns 1 if the <structname>PGresult</> contains binary row data
-         and 0 if it contains text data.
+Returns 1 if the <structname>PGresult</> contains binary data
+and 0 if it contains text data.
 <synopsis>
 int PQbinaryTuples(const PGresult *res);
 </synopsis>
 </para>
 
 <para>
-Currently, binary row data can only be returned by a query that
-extracts data from a binary cursor.
+This function is deprecated (except for its use in connection with
+<command>COPY</>), because it is possible for a single
+<structname>PGresult</>
+to contain text data in some columns and binary data in others.
+<function>PQfformat()</> is preferred.  <function>PQbinaryTuples</>
+returns 1 only if all columns of the result are binary (format 1).
 </para>
 </listitem>
 </varlistentry>
-</variablelist>
-</sect2>
-
-<sect2 id="libpq-exec-select-values">
-  <title>Retrieving Query Result Values</title>
 
-<variablelist>
 <varlistentry>
 <term><function>PQgetvalue</function></term>
 <listitem>
 <para>
-            Returns a single column value of one row
+            Returns a single field value of one row
            of a <structname>PGresult</structname>.
-           Row and colums indices start at 0.
+            Row and column numbers start at 0.
 <synopsis>
 char* PQgetvalue(const PGresult *res,
                  int row_number,
@@ -1347,15 +1525,18 @@ char* PQgetvalue(const PGresult *res,
 </para>
 
 <para>
-For most queries, the value returned by <function>PQgetvalue</function>
+For data in text format, the value returned by <function>PQgetvalue</function>
 is a null-terminated character string  representation
-of the column value.  But if <function>PQbinaryTuples</function> returns 1,
-the  value  returned  by <function>PQgetvalue</function>  is  the  binary
-representation of the
-type in the internal format of the backend server
-(but not including the size word, if the column is variable-length).
-It  is then the programmer's responsibility to cast and
-convert the data to the correct C type.
+of the field value.  For data in binary format, the value is in the binary
+representation determined by the datatype's <function>typsend</> and
+<function>typreceive</> functions.  (The value is actually followed by
+a zero byte in this case too, but that is not ordinarily useful, since
+the value is likely to contain embedded nulls.)
+</para>
+
+<para>
+An empty string is returned if the field value is NULL.  See
+<function>PQgetisnull</> to distinguish NULLs from empty-string values.
 </para>
 
 <para>
@@ -1373,7 +1554,7 @@ be used past the lifetime of the  <structname>PGresult</structname>  structure i
 <term><function>PQgetisnull</function></term>
 <listitem>
 <para>
-           Tests a column for a null value.
+           Tests a field for a null value.
            Row and column numbers start at 0.
 <synopsis>
 int PQgetisnull(const PGresult *res,
@@ -1383,10 +1564,9 @@ int PQgetisnull(const PGresult *res,
 </para>
 
 <para>
-            This function returns  1 if the column is null and 0 if
-            it contains a non-null value.  (Note that <function>PQgetvalue</function>
-            will return an empty string, not a null pointer, for a null
-            column.)
+This function returns  1 if the field is null and 0 if
+it contains a non-null value.  (Note that <function>PQgetvalue</function>
+will return an empty string, not a null pointer, for a null field.)
 </para>
 </listitem>
 </varlistentry>
@@ -1395,7 +1575,7 @@ int PQgetisnull(const PGresult *res,
 <term><function>PQgetlength</function></term>
 <listitem>
 <para>
-          Returns   the   length  of  a  column value in bytes.
+          Returns the actual length of a field value in bytes.
           Row and column numbers start at 0.
 <synopsis>
 int PQgetlength(const PGresult *res,
@@ -1406,8 +1586,10 @@ int PQgetlength(const PGresult *res,
 
 <para>
 This is the actual data length for the particular data value, that is, the
-size of the object pointed to by <function>PQgetvalue</function>.  Note that for character-represented
-values, this size has little to do with the binary size reported by <function>PQfsize</function>.
+size of the object pointed to by <function>PQgetvalue</function>.  For text
+data format this is the same as <function>strlen()</>.  For binary format
+this is essential information.  Note that one should <emphasis>not</> rely
+on <function>PQfsize</function> to obtain the actual data length.
 </para>
 </listitem>
 </varlistentry>
@@ -1440,8 +1622,8 @@ typedef struct {
 
 <para>
 This function was formerly used by <application>psql</application>
-to print query results, but this is no longer the case and this
-function is no longer actively supported.
+to print query results, but this is no longer the case.  Note that it
+assumes all the data is in text format.
 </para>
 </listitem>
 </varlistentry>
@@ -1451,17 +1633,27 @@ function is no longer actively supported.
 <sect2 id="libpq-exec-nonselect">
   <title>Retrieving Result Information for Other Commands</title>
 
+<para>
+These functions are used to extract information from
+<structname>PGresult</structname> objects that are not <command>SELECT</>
+results.
+</para>
+
 <variablelist>
 <varlistentry>
 <term><function>PQcmdStatus</function></term>
 <listitem>
 <para>
-          Returns the command status string from the SQL command that
+          Returns the command status tag from the SQL command that
          generated the <structname>PGresult</structname>.
 <synopsis>
 char * PQcmdStatus(PGresult *res);
 </synopsis>
 </para>
+<para>
+Commonly this is just the name of the command, but it may include additional
+data such as the number of rows processed.
+</para>
 </listitem>
 </varlistentry>
 
@@ -1477,7 +1669,9 @@ char * PQcmdTuples(PGresult *res);
 
 <para>
           If the <acronym>SQL</acronym> command that generated the
-         <structname>PGresult</structname> was <command>INSERT</>, <command>UPDATE</>, or <command>DELETE</command>, this returns a
+         <structname>PGresult</structname> was <command>INSERT</>,
+         <command>UPDATE</>, <command>DELETE</command>, <command>MOVE</>,
+         or <command>FETCH</>, this returns a
          string containing the number of rows affected.  If the
           command was anything else, it returns the empty string.
 </para>
@@ -1496,13 +1690,6 @@ char * PQcmdTuples(PGresult *res);
 Oid PQoidValue(const PGresult *res);
 </synopsis>
 </para>
-
-<para>
-          The type <type>Oid</type> and the constant
-          <literal>InvalidOid</literal> will be defined if you include
-          the <application>libpq</application> header file. They will
-          both be some integer type.
-</para>
 </listitem>
 </varlistentry>
 
@@ -1523,14 +1710,184 @@ char * PQoidStatus(const PGresult *res);
 </para>
 
 <para>
-This function is deprecated in favor of <function>PQoidValue</function>
-and is not thread-safe.
+This function is deprecated in favor of <function>PQoidValue</function>.
+It is not thread-safe.
 </para>
 </listitem>
 </varlistentry>
 </variablelist>
 
 </sect2>
+
+<sect2 id="libpq-exec-escape-string">
+  <title>Escaping Strings for Inclusion in SQL Commands</title>
+
+   <indexterm zone="libpq-exec-escape-string"><primary>escaping strings</></>
+
+<para>
+<function>PQescapeString</function> escapes a string for use within an SQL
+commmand.  This is useful when inserting data values as literal constants
+in SQL commands.  Certain characters (such as quotes and backslashes) must
+be escaped to prevent them from being interpreted specially by the SQL parser.
+<function>PQescapeString</> performs this operation.
+</para>
+
+<tip>
+<para>
+It is especially important to do proper escaping when handling strings that
+were received from an untrustworthy source.  Otherwise there is a security
+risk: you are vulnerable to <quote>SQL injection</> attacks wherein unwanted
+SQL commands are fed to your database.
+</para>
+</tip>
+
+<para>
+Note that it is not necessary nor correct to do escaping when a data
+value is passed as a separate parameter in <function>PQexecParams</> or
+<function>PQsendQueryParams</>.
+
+<synopsis>
+size_t PQescapeString (char *to, const char *from, size_t length);
+</synopsis>
+</para>
+
+<para>
+The parameter <parameter>from</> points to the first character of the string
+that 
+is to be escaped, and the <parameter>length</> parameter gives the
+number of characters in this string.  (A terminating zero byte is
+neither necessary nor counted.)  <parameter>to</> shall point to a
+buffer that is able to hold at least one more character than twice
+the value of <parameter>length</>, otherwise the behavior is
+undefined.  A call to <function>PQescapeString</> writes an escaped
+version of the <parameter>from</> string to the <parameter>to</>
+buffer, replacing special characters so that they cannot cause any
+harm, and adding a terminating zero byte.  The single quotes that
+must surround <productname>PostgreSQL</> string literals are not
+included in the result string; they should be provided in the SQL
+command that the result is inserted into.
+</para>
+<para>
+<function>PQescapeString</> returns the number of characters written
+to <parameter>to</>, not including the terminating zero byte.
+</para>
+<para>
+Behavior is undefined if the <parameter>to</> and <parameter>from</>
+strings overlap.
+</para>
+</sect2>
+
+
+ <sect2 id="libpq-exec-escape-bytea">
+  <title>Escaping Binary Strings for Inclusion in SQL Commands</title>
+  <indexterm zone="libpq-exec-escape-bytea">
+   <primary>escaping binary strings</primary>
+  </indexterm>
+
+  <variablelist>
+  <varlistentry>
+  <term><function>PQescapeBytea</function></term>
+  <listitem>
+  <para>
+   Escapes binary data for use within an SQL command with the type
+   <type>bytea</type>.  As with <function>PQescapeString</function>,
+   this is only used when inserting data directly into an SQL command string.
+<synopsis>
+unsigned char *PQescapeBytea(const unsigned char *from,
+                             size_t from_length,
+                             size_t *to_length);
+</synopsis>
+</para>
+
+<para>
+   Certain byte values <emphasis>must</emphasis> be escaped (but all
+   byte values <emphasis>may</emphasis> be escaped) when used as part
+   of a <type>bytea</type> literal in an <acronym>SQL</acronym>
+   statement. In general, to escape a byte, it is converted into the
+   three digit octal number equal to the octet value, and preceded by
+   two backslashes. The single quote (<literal>'</>) and backslash
+   (<literal>\</>) characters have special alternative escape
+   sequences. See <xref linkend="datatype-binary"> for more
+   information. <function>PQescapeBytea</function> performs this
+   operation, escaping only the minimally required bytes.
+  </para>
+
+  <para>
+   The <parameter>from</parameter> parameter points to the first
+   byte of the string that is to be escaped, and the
+   <parameter>from_length</parameter> parameter gives the number of
+   bytes in this binary string.  (A terminating zero byte is
+   neither necessary nor counted.)  The <parameter>to_length</parameter>
+   parameter points to a variable that will hold the resultant
+   escaped string length. The result string length includes the terminating
+   zero byte of the result.
+  </para>
+
+  <para>
+   <function>PQescapeBytea</> returns an escaped version of the
+   <parameter>from</parameter> parameter binary string in memory
+   allocated with <function>malloc()</>.  This memory must be freed
+   using <function>PQfreemem()</> when the result is no longer needed.
+   The return string has all special characters replaced
+   so that they can be properly processed by the PostgreSQL string literal
+   parser, and the <type>bytea</type> input function. A terminating zero
+   byte is also added.  The single quotes that must surround
+   PostgreSQL string literals are not part of the result string.
+  </para>
+  </listitem>
+  </varlistentry>
+
+  <varlistentry>
+  <term><function>PQunescapeBytea</function></term>
+  <listitem>
+  <para>
+   Converts an escaped string representation of binary data into binary
+   data --- the reverse of <function>PQescapeBytea</function>.
+   This is needed when retrieving <type>bytea</type> data in text format,
+   but not when retrieving it in binary format.
+
+<synopsis>
+unsigned char *PQunescapeBytea(const unsigned char *from, size_t *to_length);
+</synopsis>
+</para>
+
+<para>
+   The <parameter>from</parameter> parameter points to an escaped string
+   such as might be returned by <function>PQgetvalue</function> when applied to a
+   <type>bytea</type> column. <function>PQunescapeBytea</function> converts
+   this string representation into its binary representation.
+   It returns a pointer to a buffer allocated with
+   <function>malloc()</function>, or null on error, and puts the size of
+   the buffer in <parameter>to_length</parameter>. The result must be
+   freed using <function>PQfreemem()</> when it is no longer needed.
+  </para>
+  </listitem>
+  </varlistentry>
+
+  <varlistentry>
+  <term><function>PQfreemem</function></term>
+  <listitem>
+  <para>
+   Frees memory allocated by <application>libpq</>.
+<synopsis>
+void PQfreemem(void *ptr);
+</synopsis>
+</para>
+
+<para>
+   Frees memory allocated by <application>libpq</>, particularly
+   <function>PQescapeBytea</function>,
+   <function>PQunescapeBytea</function>,
+   and <function>PQnotifies</function>.
+   It is needed by Win32, which can not free memory across
+   DLL's, unless multithreaded DLL's (/MD in VC6) are used.
+   On other platforms it is the same as <function>free()</>.
+  </para>
+  </listitem>
+  </varlistentry>
+  </variablelist>
+   
+ </sect2>
 </sect1>
 
 <sect1 id="libpq-async">
@@ -1573,70 +1930,11 @@ discarded by <function>PQexec</function>.
 Applications that do not like these limitations can instead use the
 underlying functions that <function>PQexec</function> is built from:
 <function>PQsendQuery</function> and <function>PQgetResult</function>.
-</para>
-<para>
-Older programs that used this functionality as well as 
-<function>PQputline</function> and <function>PQputnbytes</function>
-could block waiting to send data to the server. To
-address that issue, the function <function>PQsetnonblocking</function>
-was added.
-Old applications can neglect to use <function>PQsetnonblocking</function>
-and get the old potentially blocking behavior.  Newer programs can use 
-<function>PQsetnonblocking</function> to achieve a completely nonblocking
-connection to the server.
+There is also <function>PQsendQueryParams</function>, which can be
+used with <function>PQgetResult</function> to duplicate the functionality
+of <function>PQexecParams</function>.
 
 <variablelist>
-<varlistentry>
- <term><function>PQsetnonblocking</function></term>
- <listitem>
-   <para>
-    Sets the nonblocking status of the connection.
-<synopsis>
-int PQsetnonblocking(PGconn *conn, int arg);
-</synopsis>
-</para>
-
-<para>
-    Sets the state of the connection to nonblocking if <parameter>arg</parameter> is 1 and
-    blocking if <parameter>arg</parameter> is 0.  Returns 0 if OK, -1 if error.
-   </para>
-   <para>
-    In the nonblocking state, calls to
-    <function>PQputline</function>, <function>PQputnbytes</function>,
-    <function>PQsendQuery</function>, and <function>PQendcopy</function>
-    will not block but instead return an error if they need to be called
-    again.
-   </para>
-   <para>
-    When a database connection has been set to nonblocking mode and
-    <function>PQexec</function> is called, it will temporarily set the state
-    of the connection to blocking until the <function>PQexec</function> call
-    completes. 
-   </para>
-   <para>
-    More of <application>libpq</application> is expected to be made safe for 
-    the nonblocking mode in the future.
-  </para>
- </listitem>
-</varlistentry>
-
-<varlistentry>
-<term><function>PQisnonblocking</function></term>
-<listitem>
-<para>
-       Returns the blocking status of the database connection.
-<synopsis>
-int PQisnonblocking(const PGconn *conn);
-</synopsis>
-</para>
-
-<para>
-       Returns 1 if the connection is set to nonblocking mode and
-       0 if blocking.
-</para>
-</listitem>
-</varlistentry>
-
 <varlistentry>
 <term><function>PQsendQuery</function></term>
 <listitem>
@@ -1646,12 +1944,9 @@ int PQisnonblocking(const PGconn *conn);
          successfully dispatched and 0 if not (in which case, use
          <function>PQerrorMessage</> to get more information about the failure).
 <synopsis>
-int PQsendQuery(PGconn *conn,
-                const char *command);
+int PQsendQuery(PGconn *conn, const char *command);
 </synopsis>
-</para>
 
-<para>
          After successfully calling <function>PQsendQuery</function>, call
           <function>PQgetResult</function> one or more
          times to obtain the results.  <function>PQsendQuery</function> may not be called
@@ -1661,12 +1956,41 @@ int PQsendQuery(PGconn *conn,
 </listitem>
 </varlistentry>
 
+<varlistentry>
+<term><function>PQsendQueryParams</function></term>
+<listitem>
+<para>
+          Submits a command and separate parameters to the server without
+         waiting for the result(s).
+<synopsis>
+int PQsendQueryParams(PGconn *conn,
+                      const char *command,
+                      int nParams,
+                      const Oid *paramTypes,
+                      const char * const *paramValues,
+                      const int *paramLengths,
+                      const int *paramFormats,
+                      int resultFormat);
+</synopsis>
+
+       This is equivalent to <function>PQsendQuery</function> except that
+       query parameters can be specified separately from the query string.
+       The function's parameters are handled identically to
+       <function>PQexecParams</function>.  Like
+       <function>PQexecParams</function>, it will not work on 2.0-protocol
+       connections, and it allows only one command in the query string.
+</para>
+</listitem>
+</varlistentry>
+
 <varlistentry>
 <term><function>PQgetResult</function></term>
 <listitem>
 <para>
-          Waits for the next result from a prior <function>PQsendQuery</function>,
-         and return it.  A null pointer is returned when the command is complete
+          Waits for the next result from a prior
+         <function>PQsendQuery</function> or
+         <function>PQsendQueryParams</function>,
+         and returns it.  A null pointer is returned when the command is complete
          and there will be no more results.
 <synopsis>
 PGresult *PQgetResult(PGconn *conn);
@@ -1697,8 +2021,8 @@ overlapped processing, by the way: the client can be handling the
 results of one command while the server is still working on later
 queries in the same command string.)  However, calling <function>PQgetResult</function> will
 still cause the client to block until the server completes the
-next <acronym>SQL</acronym> command.  This can be avoided by proper use of three more
-functions:
+next <acronym>SQL</acronym> command.  This can be avoided by proper use of two
+more functions:
 
 <variablelist>
 <varlistentry>
@@ -1714,7 +2038,8 @@ int PQconsumeInput(PGconn *conn);
 <para>
 <function>PQconsumeInput</function> normally returns 1 indicating <quote>no error</quote>,
 but returns 0 if there was some kind of trouble (in which case
-<function>PQerrorMessage</function> can be used).  Note that the result does not say
+<function>PQerrorMessage</function> can be consulted).  Note that the result
+does not say 
 whether any input data was actually collected. After calling
 <function>PQconsumeInput</function>, the application may check
 <function>PQisBusy</function> and/or <function>PQnotifies</function> to see if
@@ -1750,35 +2075,13 @@ state will never end.
 </para>
 </listitem>
 </varlistentry>
-
-<varlistentry>
-<term><function>PQflush</function></term>
-<listitem>
-<para>
-Attempts to flush any data queued to the server.
-Returns 0 if successful (or if the send queue is empty), -1 if it failed for
-some reason, or 1 if it was unable to send all the data in the send queue yet
-(this case can only occur if the connection is nonblocking).
-<synopsis>
-int PQflush(PGconn *conn);
-</synopsis>
-</para>
-
-<para>
-<function>PQflush</function> needs to be called on a nonblocking connection 
-before calling <function>select()</function> to determine if a response has
-arrived.  If 0 is returned it ensures that there is no data queued to the 
-server that has not actually been sent.  Only applications that have used
-<function>PQsetnonblocking</function> have a need for this.
-</para>
-</listitem>
-</varlistentry>
 </variablelist>
 </para>
 
 <para>
 A typical application using these functions will have a main loop that uses
-<function>select()</function> to wait for all the conditions that it must
+<function>select()</function> or <function>poll()</> to wait for all the
+conditions that it must
 respond to.  One of the conditions will be input available from the server,
 which in terms of <function>select()</function> means readable data on the file
 descriptor identified by <function>PQsocket</function>.
@@ -1789,13 +2092,6 @@ if <function>PQisBusy</function> returns false (0).  It can also call
 <function>PQnotifies</function> to detect <command>NOTIFY</> messages (see <xref linkend="libpq-notify">).
 </para>
 
-<para>
-Nonblocking connections (that have used <function>PQsetnonblocking</function>)
-should not use <function>select()</function> until <function>PQflush</function>
-has returned 0 indicating that there is no buffered data waiting to be sent
-to the server.
-</para>
-
 <para>
 A client that uses <function>PQsendQuery</function>/<function>PQgetResult</function>
 can also attempt to cancel a command that is still being processed by the server.
@@ -1843,6 +2139,89 @@ interactive cancellation of commands that it issues through <function>PQexec</fu
 </variablelist>
 </para>
 
+<para>
+By using the functions described above, it is possible to avoid blocking
+while waiting for input from the database server.  However, it is still
+possible that the application will block waiting to send output to the
+server.  This is relatively uncommon but can happen if very long SQL commands
+or data values are sent.  (It is much more probable if the application
+sends data via COPY IN, however.)  To prevent this possibility and achieve
+completely nonblocking database operation, the following additional
+functions may be used.
+
+<variablelist>
+<varlistentry>
+ <term><function>PQsetnonblocking</function></term>
+ <listitem>
+   <para>
+    Sets the nonblocking status of the connection.
+<synopsis>
+int PQsetnonblocking(PGconn *conn, int arg);
+</synopsis>
+</para>
+
+<para>
+    Sets the state of the connection to nonblocking if
+    <parameter>arg</parameter> is 1, or
+    blocking if <parameter>arg</parameter> is 0.  Returns 0 if OK, -1 if error.
+   </para>
+   <para>
+    In the nonblocking state, calls to
+    <function>PQsendQuery</function>,
+    <function>PQputline</function>, <function>PQputnbytes</function>,
+    and <function>PQendcopy</function>
+    will not block but instead return an error if they need to be called
+    again.
+   </para>
+   <para>
+    Note that <function>PQexec</function> does not honor nonblocking mode;
+    if it is called, it will act in blocking fashion anyway.
+   </para>
+ </listitem>
+</varlistentry>
+
+<varlistentry>
+<term><function>PQisnonblocking</function></term>
+<listitem>
+<para>
+       Returns the blocking status of the database connection.
+<synopsis>
+int PQisnonblocking(const PGconn *conn);
+</synopsis>
+</para>
+
+<para>
+       Returns 1 if the connection is set to nonblocking mode and
+       0 if blocking.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><function>PQflush</function></term>
+<listitem>
+<para>
+Attempts to flush any queued output data to the server.
+Returns 0 if successful (or if the send queue is empty), -1 if it failed for
+some reason, or 1 if it was unable to send all the data in the send queue yet
+(this case can only occur if the connection is nonblocking).
+<synopsis>
+int PQflush(PGconn *conn);
+</synopsis>
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+</para>
+
+<para>
+After sending any command or data on a nonblocking connection, call
+<function>PQflush</function>.  If it returns 1, wait for the socket to be
+write-ready and call it again; repeat until it returns 0.  Once
+<function>PQflush</function> returns 0, wait for the socket to be read-ready
+and then read the response as described above.
+</para>
+
 </sect1>
 
 <sect1 id="libpq-fastpath">
@@ -1850,9 +2229,17 @@ interactive cancellation of commands that it issues through <function>PQexec</fu
 
 <para>
 <productname>PostgreSQL</productname> provides a fast-path interface to send
-function calls to the server.  This is a trapdoor into system internals and
-can be a potential security hole.  Most users will not need this feature.
+simple function calls to the server.
+</para>
+
+<tip>
+<para>
+This interface is somewhat obsolete, as one may achieve similar performance
+and greater functionality by setting up a prepared statement to define the
+function call.  Then, executing the statement with binary transmission of
+parameters and results substitutes for a fast-path function call.
 </para>
+</tip>
 
 <para>
 The function <function>PQfn</function> requests execution of a server
@@ -1879,25 +2266,42 @@ typedef struct {
 
 <para>
      The <parameter>fnid</> argument is the OID of the function to be
-     executed.
+     executed.  <parameter>args</> and <parameter>nargs</> define the
+     parameters to be passed to the function; they must match the declared
+     function argument list.  When the <parameter>isint</> field of a
+     parameter
+     struct is true,
+     the <parameter>u.integer</> value is sent to the server as an integer
+     of the indicated length (this must be 1, 2, or 4 bytes); proper
+     byte-swapping occurs.  When <parameter>isint</> is false, the
+     indicated number of bytes at <parameter>*u.ptr</> are sent with no
+     processing; the data must be in the format expected by the server for
+     binary transmission of the function's argument datatype.
      <parameter>result_buf</parameter> is the buffer in which
      to place the return value.  The caller must  have  allocated
      sufficient space to store the return value.  (There is no check!)
      The actual result length will be returned in the integer pointed
-     to  by  <parameter>result_len</parameter>.   If a 4-byte integer result is expected, set
-     <parameter>result_is_int</parameter> to 1, otherwise set it to 0.  (Setting <parameter>result_is_int</parameter> to 1
-     tells <application>libpq</> to byte-swap the value if necessary, so that it is
+     to  by  <parameter>result_len</parameter>.
+     If a 1, 2, or 4-byte integer result is expected, set
+     <parameter>result_is_int</parameter> to 1, otherwise set it to 0.
+     Setting <parameter>result_is_int</parameter> to 1
+     causes <application>libpq</> to byte-swap the value if necessary, so that
+     it is
      delivered as a proper <type>int</type> value for the client machine.  When
-     <parameter>result_is_int</> is 0, the byte string sent by the server is returned
-     unmodified.)
-     <parameter>args</> and <parameter>nargs</> specify the arguments to be passed to the function.
+     <parameter>result_is_int</> is 0, the binary-format byte string sent by
+     the server is returned unmodified.
+</para>
+
+<para>
+<function>PQfn</function> always returns a valid <structname>PGresult</structname> pointer. The result status
+should be checked before the result is used.   The
+caller is responsible for  freeing  the  <structname>PGresult</structname>  with
+<function>PQclear</function> when it is no longer needed.
 </para>
 
 <para>
-     <function>PQfn</function> always returns a valid <structname>PGresult</structname> pointer. The result status
-     should be checked before the result is used.   The
-     caller is responsible for  freeing  the  <structname>PGresult</structname>  with
-     <function>PQclear</function> when it is no longer needed.
+Note that it is not possible to handle NULL arguments, NULL results, nor
+set-valued results when using this interface.
 </para>
 
 </sect1>
@@ -1909,7 +2313,7 @@ typedef struct {
 
 <para>
 <productname>PostgreSQL</productname> offers asynchronous notification via the
-<command>LISTEN</command> and <command>NOTIFY</command> commands.  A server-side session registers its interest in a particular
+<command>LISTEN</command> and <command>NOTIFY</command> commands.  A client session registers its interest in a particular
 notification condition with the <command>LISTEN</command> command (and can stop listening
 with the <command>UNLISTEN</command> command).  All sessions listening on a
 particular condition will be notified asynchronously when a <command>NOTIFY</command> command with that
@@ -1922,7 +2326,7 @@ not necessary for there to be any associated table.
 
 <para>
 <application>libpq</application> applications submit <command>LISTEN</command> and <command>UNLISTEN</command>
-commands as ordinary SQL command.  The arrival of <command>NOTIFY</command>
+commands as ordinary SQL commands.  The arrival of <command>NOTIFY</command>
 messages can subsequently be detected by calling <function>PQnotifies</function>.
 </para>
 
@@ -1937,18 +2341,30 @@ The function <function>PQnotifies</function>
 PGnotify* PQnotifies(PGconn *conn);
 
 typedef struct pgNotify {
-    char *relname;              /* notification name */
+    char *relname;              /* notification condition name */
     int  be_pid;                /* process ID of server process */
+    char *extra;                /* notification parameter */
 } PGnotify;
 </synopsis>
-After processing a <structname>PGnotify</structname> object returned by <function>PQnotifies</function>,
-be sure to free it with <function>PQfreemem()</function>.
+After processing a <structname>PGnotify</structname> object returned by
+<function>PQnotifies</function>, be sure to free it with
+<function>PQfreemem</function>.  It is sufficient to free the
+<structname>PGnotify</structname> pointer; the
+<structfield>relname</structfield> and <structfield>extra</structfield> fields
+do not represent separate allocations.
+</para>
+
+<note>
+<para>
+ At present the <structfield>extra</structfield> field is unused and will
+ always point to an empty string.
 </para>
+</note>
 
 <note>
 <para>
  In <productname>PostgreSQL</productname> 6.4 and later,
- the <literal>be_pid</literal> is that of the notifying backend process,
+ the <structfield>be_pid</structfield> is that of the notifying backend process,
  whereas in earlier versions it was always the <acronym>PID</acronym> of your own backend process.
 </para>
 </note>
@@ -1996,19 +2412,263 @@ if any notifications came in during the processing of the command.
 </indexterm>
 
 <para>
- The <command>COPY</command> command in <productname>PostgreSQL</productname> has options to  read  from
- or  write  to  the  network  connection  used by <application>libpq</application>.
- Therefore, functions are necessary to access this  network
- connection directly so applications may take advantage of this capability.
+ The <command>COPY</command> command in <productname>PostgreSQL</productname>
+ has options to read from or write to the network connection used by
+ <application>libpq</application>.  The functions described in this section
+ allow applications to take advantage of this capability by supplying or
+ consuming copied data.
+</para>
+
+<para>
+ The overall process is that the application first issues the SQL
+ <command>COPY</command> command via <function>PQexec</function> or one
+ of the equivalent functions.  The response to this (if there is no error
+ in the command) will be a <structname>PGresult</> object bearing a status
+ code of <literal>PGRES_COPY_OUT</literal> or <literal>PGRES_COPY_IN</literal>
+ (depending on the specified copy direction).  The application should then
+ use the functions of this section to receive or transmit data rows.
+ When the data transfer is complete, another <structname>PGresult</> object
+ is returned to indicate success or failure of the transfer.  Its status
+ will be <literal>PGRES_COMMAND_OK</literal> for success or
+ <literal>PGRES_FATAL_ERROR</literal> if some problem was encountered.
+ At this point further SQL commands may be issued via
+ <function>PQexec</function>.  (It is not possible to execute other SQL
+ commands using the same connection while the <command>COPY</command>
+ operation is in progress.)
 </para>
 
 <para>
- These functions should be executed only after obtaining a result
- status of <literal>PGRES_COPY_OUT</literal> or
+ If a <command>COPY</command> command is issued via
+ <function>PQexec</function> in a string that could contain additional
+ commands, the application must continue fetching results via
+ <function>PQgetResult</> after completing the <command>COPY</command>
+ sequence.  Only when <function>PQgetResult</> returns NULL is it certain
+ that the <function>PQexec</function> command string is done and it is
+ safe to issue more commands.
+</para>
+
+<para>
+ The functions of this section should be executed only after obtaining a
+ result status of <literal>PGRES_COPY_OUT</literal> or
  <literal>PGRES_COPY_IN</literal> from <function>PQexec</function> or
  <function>PQgetResult</function>.
 </para>
 
+<para>
+ A <structname>PGresult</> object bearing one of these status values
+ carries some additional data about the <command>COPY</command> operation that
+ is starting.  This additional data is available using functions that are
+ also used in connection with query results:
+
+<variablelist>
+<varlistentry>
+<term><function>PQnfields</function></term>
+<listitem>
+<para>
+          Returns the number of columns (fields) to be copied.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><function>PQbinaryTuples</function></term>
+<listitem>
+<para>
+                0 indicates the overall copy format is textual (rows
+               separated by newlines, columns separated by separator
+               characters, etc).
+               1 indicates the overall copy format is binary.
+               See <xref linkend="sql-copy" endterm="sql-copy-title">
+               for more information.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><function>PQfformat</function></term>
+<listitem>
+<para>
+          Returns the format code (0 for text, 1 for binary) associated
+         with each column of the copy operation.  The per-column format
+         codes will always be zero when the overall copy format is textual,
+         but the binary format can support both text and binary columns.
+         (However, as of the current implementation of <command>COPY</>,
+         only binary columns appear in a binary copy; so the per-column
+         formats always match the overall format at present.)
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+</para>
+
+<note>
+<para>
+These additional data values are only available when using protocol 3.0.
+When using protocol 2.0, all these functions will return 0.
+</para>
+</note>
+
+<sect2 id="libpq-copy-send">
+  <title>Functions for Sending <command>COPY</command> Data</title>
+
+<para>
+ These functions are used to send data during <literal>COPY FROM STDIN</>.
+ They will fail if called when the connection is not in <literal>COPY_IN</>
+ state.
+</para>
+
+<variablelist>
+<varlistentry>
+<term><function>PQputCopyData</function></term>
+<listitem>
+<para>
+ Sends data to the server during <literal>COPY_IN</> state.
+<synopsis>
+int PQputCopyData(PGconn *conn,
+                  const char *buffer,
+                  int nbytes);
+</synopsis>
+</para>
+
+<para>
+Transmits the COPY data in the specified <parameter>buffer</>, of length
+<parameter>nbytes</>, to the server.  The result is 1 if the data was sent,
+zero if it was not sent because the attempt would block (this case is only
+possible if the connection is in nonblock mode), or -1 if an error occurred.
+(Use <function>PQerrorMessage</function> to retrieve details if the return
+value is -1.  If the value is zero, wait for write-ready and try again.)
+</para>
+
+<para>
+The application may divide the COPY datastream into bufferloads of any
+convenient size.  Bufferload boundaries have no semantic significance when
+sending.  The contents of the datastream must match the data format expected
+by the <command>COPY</> command; see
+<xref linkend="sql-copy" endterm="sql-copy-title"> for details.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><function>PQputCopyEnd</function></term>
+<listitem>
+<para>
+ Sends end-of-data indication to the server during <literal>COPY_IN</> state.
+<synopsis>
+int PQputCopyEnd(PGconn *conn,
+                 const char *errormsg);
+</synopsis>
+</para>
+
+<para>
+Ends the <literal>COPY_IN</> operation successfully if <parameter>errormsg</>
+is NULL.  If <parameter>errormsg</> is not NULL then the <command>COPY</>
+is forced to fail, with the string pointed to by <parameter>errormsg</>
+used as the error message.  (One should not assume that this exact error
+message will come back from the server, however, as the server might have
+already failed the <command>COPY</> for its own reasons.  Also note that the
+option to force failure does not work when using pre-3.0-protocol connections.)
+</para>
+
+<para>
+The result is 1 if the termination data was sent,
+zero if it was not sent because the attempt would block (this case is only
+possible if the connection is in nonblock mode), or -1 if an error occurred.
+(Use <function>PQerrorMessage</function> to retrieve details if the return
+value is -1.  If the value is zero, wait for write-ready and try again.)
+</para>
+
+<para>
+After successfully calling <function>PQputCopyEnd</>, call
+<function>PQgetResult</> to obtain the final result status of the
+<command>COPY</> command.  One may wait for
+this result to be available in the usual way.  Then return to normal
+operation.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect2>
+
+<sect2 id="libpq-copy-receive">
+  <title>Functions for Receiving <command>COPY</command> Data</title>
+
+<para>
+ These functions are used to receive data during <literal>COPY TO STDOUT</>.
+ They will fail if called when the connection is not in <literal>COPY_OUT</>
+ state.
+</para>
+
+<variablelist>
+<varlistentry>
+<term><function>PQgetCopyData</function></term>
+<listitem>
+<para>
+ Receives data from the server during <literal>COPY_OUT</> state.
+<synopsis>
+int PQgetCopyData(PGconn *conn,
+                  char **buffer,
+                  int async);
+</synopsis>
+</para>
+
+<para>
+Attempts to obtain another row of data from the server during a COPY.
+Data is always returned one data row at a time; if only a partial row
+is available, it is not returned.  Successful return of a data row
+involves allocating a chunk of memory to hold the data.  The
+<parameter>buffer</> parameter must be non-NULL.  <parameter>*buffer</>
+is set to point to the allocated memory, or to NULL in cases where no
+buffer is returned.  A non-NULL result buffer must be freed using
+<function>PQfreemem</> when no longer needed.
+</para>
+
+<para>
+When a row is successfully returned, the return value is the number of
+data bytes in the row (this will always be greater than zero).  The
+returned string is always null-terminated, though this is probably only
+useful for textual COPY.  A result of zero indicates that the COPY is
+still in progress, but no row is yet available (this is only possible
+when <parameter>async</> is true).  A
+result of -1 indicates that the COPY is done.
+A result of -2 indicates that an error occurred (consult
+<function>PQerrorMessage</> for the reason).
+</para>
+
+<para>
+When <parameter>async</> is true (not zero), <function>PQgetCopyData</>
+will not block waiting for input; it will return zero if the COPY is still
+in progress but no complete row is available.  (In this case wait for
+read-ready before trying again; it does not matter whether you call
+<function>PQconsumeInput</>.)  When <parameter>async</> is
+false (zero), <function>PQgetCopyData</> will block until data is available
+or the operation completes.
+</para>
+
+<para>
+After <function>PQgetCopyData</> returns -1, call
+<function>PQgetResult</> to obtain the final result status of the
+<command>COPY</> command.  One may wait for
+this result to be available in the usual way.  Then return to normal
+operation.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect2>
+
+<sect2 id="libpq-copy-deprecated">
+  <title>Obsolete Functions for <command>COPY</command></title>
+
+<para>
+ These functions represent older methods of handling <command>COPY</>.
+ Although they still work, they are deprecated due to poor error handling,
+ inconvenient methods of detecting end-of-data, and lack of support for binary
+ or nonblocking transfers.
+</para>
+
 <variablelist>
 <varlistentry>
 <term><function>PQgetline</function></term>
@@ -2042,9 +2702,6 @@ receive lines that are more than <parameter>length</>-1  characters  long,
 care is needed to be sure it recognizes the <literal>\.</literal> line correctly
 (and does not, for example, mistake the end of a long data line
 for a terminator line).
-The code in the file
-<filename>src/bin/psql/copy.c</filename>
-contains example functions that correctly handle the <command>COPY</command> protocol.
 </para>
 </listitem>
 </varlistentry>
@@ -2123,13 +2780,13 @@ call; it is okay to send a partial line or multiple lines per call.
 
 <note>
 <para>
-Before <productname>PostgreSQL</productname> 7.4, it was necessary for the
-application to explicitly send the two characters <literal>\.</literal> as a
-final line to indicate to the server that it had finished sending COPY data.
-While this still works, it is deprecated and the special meaning of
-<literal>\.</literal> can be expected to be removed in a future release.
-It is sufficient to call <function>PQendcopy</function> after having sent the
-actual data.
+Before <productname>PostgreSQL</productname> protocol 3.0, it was necessary
+for the application to explicitly send the two characters
+<literal>\.</literal> as a final line to indicate to the server that it had
+finished sending COPY data.  While this still works, it is deprecated and the
+special meaning of <literal>\.</literal> can be expected to be removed in a
+future release.  It is sufficient to call <function>PQendcopy</function> after
+having sent the actual data.
 </para>
 </note>
 </listitem>
@@ -2202,33 +2859,52 @@ This will work correctly only if the <command>COPY</command> is the only
 </varlistentry>
 </variablelist>
 
-<para>
-An example:
-
-<programlisting>
-PQexec(conn, "CREATE TABLE foo (a integer, b varchar(16), d double precision);");
-PQexec(conn, "COPY foo FROM STDIN;");
-PQputline(conn, "3\thello world\t4.5\n");
-PQputline(conn, "4\tgoodbye world\t7.11\n");
-...
-PQendcopy(conn);
-</programlisting>
-</para>
+</sect2>
 
 </sect1>
 
-<sect1 id="libpq-trace">
-<title>Tracing Functions</title>
+<sect1 id="libpq-control">
+<title>Control Functions</title>
+
+<para>
+These functions control miscellaneous details of
+<application>libpq</>'s behavior.
+</para>
 
 <variablelist>
+<varlistentry>
+<term><function>PQsetErrorVerbosity</function></term>
+<listitem>
+<para>
+Determines the verbosity of messages returned by
+<function>PQerrorMessage</> and <function>PQresultErrorMessage</>.
+<synopsis>
+typedef enum {
+    PQERRORS_TERSE, PQERRORS_DEFAULT, PQERRORS_VERBOSE
+} PGVerbosity;
+
+PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity);
+</synopsis>
+<function>PQsetErrorVerbosity</> sets the verbosity mode, returning the
+connection's previous setting.
+In TERSE mode, returned messages include severity, primary text, and position
+only; this will normally fit on a single line.  The DEFAULT mode produces
+messages that include the above plus any detail, hint, or context fields
+(these may span multiple lines).  The VERBOSE mode includes all available
+fields.  Changing the verbosity does not affect the messages available from
+already-existing <structname>PGresult</> objects, only subsequently-created
+ones.
+</para>
+</listitem>
+</varlistentry>
+
 <varlistentry>
 <term><function>PQtrace</function></term>
 <listitem>
 <para>
           Enables  tracing of the client/server communication to a debugging file stream.
 <synopsis>
-void PQtrace(PGconn *conn
-             FILE *stream);
+void PQtrace(PGconn *conn, FILE *stream);
 </synopsis>
 </para>
 </listitem>
@@ -2249,14 +2925,43 @@ void PQuntrace(PGconn *conn);
 
 </sect1>
 
-<sect1 id="libpq-control">
+<sect1 id="libpq-notice-processing">
 <title>Notice Processing</title>
 
 <para>
-The function <function>PQsetNoticeProcessor</function>
+Notice and warning messages generated by the server are not returned by the
+query execution functions, since they do not imply failure of the query.
+Instead they are passed to a notice handling function, and execution continues
+normally after the handler returns.  The default notice handling function
+prints the message on <filename>stderr</filename>, but the application can
+override this behavior by supplying its own handling function.
+</para>
+
+<para>
+For historical reasons, there are two levels of notice handling, called the
+notice receiver and notice processor.  The default behavior is for the notice
+receiver to format the notice and pass a string to the notice processor
+for printing.  However, an application that chooses to provide its own notice
+receiver will typically ignore the notice processor layer and just do all the
+work in the notice receiver.
+</para>
+
+<para>
+The function <function>PQsetNoticeReceiver</function>
+<indexterm><primary>notice receiver</></>
+sets or examines the current notice receiver for a connection object.
+Similarly, <function>PQsetNoticeProcessor</function>
 <indexterm><primary>notice processor</></>
-controls the  reporting of notice and warning messages generated by the server.
+sets or examines the current notice processor.
+
 <synopsis>
+typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res);
+
+PQnoticeReceiver
+PQsetNoticeReceiver(PGconn *conn,
+                    PQnoticeReceiver proc,
+                    void *arg);
+
 typedef void (*PQnoticeProcessor) (void *arg, const char *message);
 
 PQnoticeProcessor
@@ -2264,19 +2969,41 @@ PQsetNoticeProcessor(PGconn *conn,
                      PQnoticeProcessor proc,
                      void *arg);
 </synopsis>
+
+Each of these functions returns the previous notice receiver or processor
+function pointer, and sets the new value.
+If you supply a null function pointer, no action is taken,
+but the current pointer is returned.
+</para>
+
+<para>
+When a notice or warning message is received from the server, or generated
+internally by <application>libpq</application>, the notice receiver function
+is called.  It is passed the message in the form of a
+<symbol>PGRES_NONFATAL_ERROR</symbol> <structname>PGresult</structname>.
+(This allows the receiver to extract individual fields using
+<function>PQresultErrorField</>, or the complete preformatted message using
+<function>PQresultErrorMessage</>.)
+The same void pointer passed to 
+<function>PQsetNoticeReceiver</function> is also passed.
+(This pointer can be used to access application-specific state if needed.)
+</para>
+
+<para>
+The default notice receiver simply extracts the message (using
+<function>PQresultErrorMessage</>) and passes it to the notice processor.
 </para>
 
 <para>
-By default, <application>libpq</application> prints notice messages
-from the server, as well as a few error messages that it generates by
-itself, on <filename>stderr</filename>.
-This behavior can be overridden by supplying a callback function that
-does something else with the messages, a so-called notice processor.
-The callback function is passed
-the text of the message (which includes a trailing newline), plus
+The notice processor is responsible for handling a notice or warning message
+given in text form.  It is passed the string text of the message
+(including a trailing newline), plus
 a void pointer that is the same one passed to
 <function>PQsetNoticeProcessor</function>.
 (This pointer can be used to access application-specific state if needed.)
+</para>
+
+<para>
 The default notice processor is simply
 <programlisting>
 static void
@@ -2285,22 +3012,14 @@ defaultNoticeProcessor(void * arg, const char * message)
     fprintf(stderr, "%s", message);
 }
 </programlisting>
-To use a special notice processor, call
-<function>PQsetNoticeProcessor</function> just after
-creation of a new <structname>PGconn</> object.
-</para>
-
-<para>
-The return value is the pointer to the previous notice processor.
-If you supply a null callback function pointer, no action is taken,
-but the current pointer is returned.
 </para>
 
 <para>
-Once you have set a notice processor, you should expect that that function
-could be called as long as either the <structname>PGconn</> object or <structname>PGresult</> objects
-made from it exist.  At creation of a <structname>PGresult</>, the <structname>PGconn</>'s current
-notice processor pointer is copied into the <structname>PGresult</> for possible use by
+Once you have set a notice receiver or processor, you should expect that that
+function could be called as long as either the <structname>PGconn</> object or
+<structname>PGresult</> objects made from it exist.  At creation of a
+<structname>PGresult</>, the <structname>PGconn</>'s current notice handling
+pointers are copied into the <structname>PGresult</> for possible use by
 functions like <function>PQgetvalue</function>.
 </para>
 
@@ -2449,7 +3168,10 @@ It is not recommended to set the timeout to less than 2 seconds.
 
 <para>
 The following environment variables can be used to specify default
-behavior for every <productname>PostgreSQL</productname> session.
+behavior for each <productname>PostgreSQL</productname> session.
+(See also the <command>ALTER USER</> and <command>ALTER DATABASE</>
+commands for ways to set default behavior on a per-user or per-database
+basis.)
 
 <itemizedlist>
 <listitem>
@@ -2545,8 +3267,8 @@ If the permissions are less strict than this, the file will be ignored.
 
 <para>
 <application>libpq</application> is thread-safe if the library is
-compiled using the <literal>--with-threads</>
-<filename>configure</filename> command-line option.  (You might need to
+compiled using <filename>configure</filename>'s <literal>--with-threads</>
+command-line option.  (In addition, you might need to
 use other threading command-line options to compile your client code.)
 </para>
 
index 35c909931fa623f93305de6788154756e15ff768..019c715c25fa75f7bc38a74443b8eaedfc9d32e0 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/lobj.sgml,v 1.28 2003/03/13 01:30:28 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/lobj.sgml,v 1.29 2003/06/21 21:51:33 tgl Exp $
 -->
 
  <chapter id="largeObjects">
@@ -181,7 +181,8 @@ int lo_open(PGconn *conn, Oid lobjId, int mode);
      <function>lo_open</function> returns a large object descriptor
      for later use in <function>lo_read</function>, <function>lo_write</function>,
      <function>lo_lseek</function>, <function>lo_tell</function>, and
-     <function>lo_close</function>.
+     <function>lo_close</function>.  The descriptor is only valid for
+     the duration of the current transaction.
 </para>
 </sect2>
 
@@ -256,6 +257,11 @@ int lo_close(PGconn *conn, int fd);
      <function>lo_open</function>.  On success, <function>lo_close</function>
       returns zero.  On error, the return value is negative.
 </para>
+
+<para>
+     Any large  object  descriptors that remain open at the end of a
+     transaction will be closed automatically.
+</para>
 </sect2>
 
    <sect2>
@@ -296,6 +302,14 @@ SELECT lo_export(image.raster, '/tmp/motd') FROM image
     WHERE name = 'beautiful image';
 </programlisting>
 </para>
+
+<para>
+These functions read and write files in the server's filesystem, using the
+permissions of the database's owning user.  Therefore, their use is restricted
+to superusers.  (In contrast, the client-side import and export functions
+read and write files in the client's filesystem, using the permissions of
+the client program.  Their use is not restricted.)
+</para>
 </sect1>
 
 <sect1 id="lo-examplesect">
index 8445a29b1b40b317cfed691a8fd44ad26a9aa1e0..ff85e9cdfc827cffe9ffcf863ba6aa864d81ae2c 100644 (file)
@@ -97,6 +97,20 @@ EXPORTS
     _pg_utf_mblen            @ 93
     _PQunescapeBytea         @ 94
     _PQfreemem               @ 95
+    _PQtransactionStatus     @ 96
+    _PQparameterStatus       @ 97
+    _PQprotocolVersion       @ 98
+    _PQsetErrorVerbosity     @ 99
+    _PQsetNoticeReceiver     @ 100
+    _PQexecParams            @ 101
+    _PQsendQueryParams       @ 102
+    _PQputCopyData           @ 103
+    _PQputCopyEnd            @ 104
+    _PQgetCopyData           @ 105
+    _PQresultErrorField      @ 106
+    _PQftable                @ 107
+    _PQftablecol             @ 108
+    _PQfformat               @ 109
 
 ; Aliases for MS compatible names
     PQconnectdb             = _PQconnectdb            
@@ -194,4 +208,17 @@ EXPORTS
     pg_utf_mblen            = _pg_utf_mblen           
     PQunescapeBytea         = _PQunescapeBytea        
     PQfreemem               = _PQfreemem              
-
+    PQtransactionStatus     = _PQtransactionStatus
+    PQparameterStatus       = _PQparameterStatus
+    PQprotocolVersion       = _PQprotocolVersion
+    PQsetErrorVerbosity     = _PQsetErrorVerbosity
+    PQsetNoticeReceiver     = _PQsetNoticeReceiver
+    PQexecParams            = _PQexecParams
+    PQsendQueryParams       = _PQsendQueryParams
+    PQputCopyData           = _PQputCopyData
+    PQputCopyEnd            = _PQputCopyEnd
+    PQgetCopyData           = _PQgetCopyData
+    PQresultErrorField      = _PQresultErrorField
+    PQftable                = _PQftable
+    PQftablecol             = _PQftablecol
+    PQfformat               = _PQfformat
index a58caa3f3aaf7b1f249291abdbb23d3b87230617..7008d99601dff64c53555c2eefa1a9185205f185 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.249 2003/06/20 04:09:12 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.250 2003/06/21 21:51:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -176,6 +176,7 @@ static PQconninfoOption *conninfo_parse(const char *conninfo,
                           PQExpBuffer errorMessage);
 static char *conninfo_getval(PQconninfoOption *connOptions,
                                const char *keyword);
+static void defaultNoticeReceiver(void *arg, const PGresult *res);
 static void defaultNoticeProcessor(void *arg, const char *message);
 static int parseServiceInfo(PQconninfoOption *options,
                                 PQExpBuffer errorMessage);
@@ -1804,11 +1805,14 @@ makeEmptyPGconn(void)
        /* Zero all pointers and booleans */
        MemSet((char *) conn, 0, sizeof(PGconn));
 
-       conn->noticeHook = defaultNoticeProcessor;
+       conn->noticeHooks.noticeRec = defaultNoticeReceiver;
+       conn->noticeHooks.noticeProc = defaultNoticeProcessor;
        conn->status = CONNECTION_BAD;
        conn->asyncStatus = PGASYNC_IDLE;
+       conn->xactStatus = PQTRANS_IDLE;
        conn->setenv_state = SETENV_STATE_IDLE;
        conn->client_encoding = PG_SQL_ASCII;
+       conn->verbosity = PQERRORS_DEFAULT;
        conn->notifyList = DLNewList();
        conn->sock = -1;
 #ifdef USE_SSL
@@ -1850,7 +1854,6 @@ makeEmptyPGconn(void)
 /*
  * freePGconn
  *      - free the PGconn data structure
- *
  */
 static void
 freePGconn(PGconn *conn)
@@ -1899,9 +1902,9 @@ freePGconn(PGconn *conn)
 }
 
 /*
  closePGconn
       - properly close a connection to the backend
-*/
* closePGconn
*      - properly close a connection to the backend
+ */
 static void
 closePGconn(PGconn *conn)
 {
@@ -2662,6 +2665,41 @@ PQstatus(const PGconn *conn)
        return conn->status;
 }
 
+PGTransactionStatusType
+PQtransactionStatus(const PGconn *conn)
+{
+       if (!conn || conn->status != CONNECTION_OK)
+               return PQTRANS_UNKNOWN;
+       if (conn->asyncStatus != PGASYNC_IDLE)
+               return PQTRANS_ACTIVE;
+       return conn->xactStatus;
+}
+
+const char *
+PQparameterStatus(const PGconn *conn, const char *paramName)
+{
+       const pgParameterStatus *pstatus;
+
+       if (!conn || !paramName)
+               return NULL;
+       for (pstatus = conn->pstatus; pstatus != NULL; pstatus = pstatus->next)
+       {
+               if (strcmp(pstatus->name, paramName) == 0)
+                       return pstatus->value;
+       }
+       return NULL;
+}
+
+int
+PQprotocolVersion(const PGconn *conn)
+{
+       if (!conn)
+               return 0;
+       if (conn->status == CONNECTION_BAD)
+               return 0;
+       return PG_PROTOCOL_MAJOR(conn->pversion);
+}
+
 char *
 PQerrorMessage(const PGconn *conn)
 {
@@ -2731,11 +2769,22 @@ PQsetClientEncoding(PGconn *conn, const char *encoding)
        return (status);
 }
 
+PGVerbosity
+PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity)
+{
+       PGVerbosity             old;
+
+       if (!conn)
+               return PQERRORS_DEFAULT;
+       old = conn->verbosity;
+       conn->verbosity = verbosity;
+       return old;
+}
+
 void
 PQtrace(PGconn *conn, FILE *debug_port)
 {
-       if (conn == NULL ||
-               conn->status == CONNECTION_BAD)
+       if (conn == NULL)
                return;
        PQuntrace(conn);
        conn->Pfdebug = debug_port;
@@ -2744,7 +2793,6 @@ PQtrace(PGconn *conn, FILE *debug_port)
 void
 PQuntrace(PGconn *conn)
 {
-       /* note: better allow untrace even when connection bad */
        if (conn == NULL)
                return;
        if (conn->Pfdebug)
@@ -2754,6 +2802,23 @@ PQuntrace(PGconn *conn)
        }
 }
 
+PQnoticeReceiver
+PQsetNoticeReceiver(PGconn *conn, PQnoticeReceiver proc, void *arg)
+{
+       PQnoticeReceiver old;
+
+       if (conn == NULL)
+               return NULL;
+
+       old = conn->noticeHooks.noticeRec;
+       if (proc)
+       {
+               conn->noticeHooks.noticeRec = proc;
+               conn->noticeHooks.noticeRecArg = arg;
+       }
+       return old;
+}
+
 PQnoticeProcessor
 PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg)
 {
@@ -2762,22 +2827,35 @@ PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg)
        if (conn == NULL)
                return NULL;
 
-       old = conn->noticeHook;
+       old = conn->noticeHooks.noticeProc;
        if (proc)
        {
-               conn->noticeHook = proc;
-               conn->noticeArg = arg;
+               conn->noticeHooks.noticeProc = proc;
+               conn->noticeHooks.noticeProcArg = arg;
        }
        return old;
 }
 
 /*
- * The default notice/error message processor just prints the
+ * The default notice message receiver just gets the standard notice text
+ * and sends it to the notice processor.  This two-level setup exists
+ * mostly for backwards compatibility; perhaps we should deprecate use of
+ * PQsetNoticeProcessor?
+ */
+static void
+defaultNoticeReceiver(void *arg, const PGresult *res)
+{
+       (void) arg;                                     /* not used */
+       (*res->noticeHooks.noticeProc) (res->noticeHooks.noticeProcArg,
+                                                                       PQresultErrorMessage(res));
+}
+
+/*
+ * The default notice message processor just prints the
  * message on stderr.  Applications can override this if they
  * want the messages to go elsewhere (a window, for example).
  * Note that simply discarding notices is probably a bad idea.
  */
-
 static void
 defaultNoticeProcessor(void *arg, const char *message)
 {
index 45db359bde18ed557029c5dd27d78504c4b7f04e..4c96bbd386881f625fb9b5b8f809890d8d52bc0f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.138 2003/06/12 01:17:19 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.139 2003/06/21 21:51:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -43,7 +43,10 @@ char    *const pgresStatus[] = {
 
 
 
+static bool PQsendQueryStart(PGconn *conn);
 static void parseInput(PGconn *conn);
+static bool PQexecStart(PGconn *conn);
+static PGresult *PQexecFinish(PGconn *conn);
 
 
 /* ----------------
@@ -137,16 +140,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
        result->cmdStatus[0] = '\0';
        result->binary = 0;
        result->errMsg = NULL;
-       result->errSeverity = NULL;
-       result->errCode = NULL;
-       result->errPrimary = NULL;
-       result->errDetail = NULL;
-       result->errHint = NULL;
-       result->errPosition = NULL;
-       result->errContext = NULL;
-       result->errFilename = NULL;
-       result->errLineno = NULL;
-       result->errFuncname = NULL;
+       result->errFields = NULL;
        result->null_field[0] = '\0';
        result->curBlock = NULL;
        result->curOffset = 0;
@@ -155,8 +149,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
        if (conn)
        {
                /* copy connection data we might need for operations on PGresult */
-               result->noticeHook = conn->noticeHook;
-               result->noticeArg = conn->noticeArg;
+               result->noticeHooks = conn->noticeHooks;
                result->client_encoding = conn->client_encoding;
 
                /* consider copying conn's errorMessage */
@@ -177,9 +170,11 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
        else
        {
                /* defaults... */
-               result->noticeHook = NULL;
-               result->noticeArg = NULL;
-               result->client_encoding = 0;    /* should be SQL_ASCII */
+               result->noticeHooks.noticeRec = NULL;
+               result->noticeHooks.noticeRecArg = NULL;
+               result->noticeHooks.noticeProc = NULL;
+               result->noticeHooks.noticeProcArg = NULL;
+               result->client_encoding = PG_SQL_ASCII;
        }
 
        return result;
@@ -444,6 +439,41 @@ pqPrepareAsyncResult(PGconn *conn)
        return res;
 }
 
+/*
+ * pqInternalNotice - helper routine for internally-generated notices
+ *
+ * The supplied text is taken as primary message (ie., it should not include
+ * a trailing newline, and should not be more than one line).
+ */
+void
+pqInternalNotice(const PGNoticeHooks *hooks, const char *msgtext)
+{
+       PGresult   *res;
+
+       if (hooks->noticeRec == NULL)
+               return;                                 /* nobody home? */
+
+       /* Make a PGresult to pass to the notice receiver */
+       res = PQmakeEmptyPGresult(NULL, PGRES_NONFATAL_ERROR);
+       res->noticeHooks = *hooks;
+       /*
+        * Set up fields of notice.
+        */
+       pqSaveMessageField(res, 'M', msgtext);
+       pqSaveMessageField(res, 'S', libpq_gettext("NOTICE"));
+       /* XXX should provide a SQLSTATE too? */
+       /*
+        * Result text is always just the primary message + newline.
+        */
+       res->errMsg = (char *) pqResultAlloc(res, strlen(msgtext) + 2, FALSE);
+       sprintf(res->errMsg, "%s\n", msgtext);
+       /*
+        * Pass to receiver, then free it.
+        */
+       (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
+       PQclear(res);
+}
+
 /*
  * pqAddTuple
  *       add a row pointer to the PGresult structure, growing it if necessary
@@ -484,6 +514,25 @@ pqAddTuple(PGresult *res, PGresAttValue *tup)
        return TRUE;
 }
 
+/*
+ * pqSaveMessageField - save one field of an error or notice message
+ */
+void
+pqSaveMessageField(PGresult *res, char code, const char *value)
+{
+       PGMessageField *pfield;
+
+       pfield = (PGMessageField *)
+               pqResultAlloc(res,
+                                         sizeof(PGMessageField) + strlen(value),
+                                         TRUE);
+       if (!pfield)
+               return;                                 /* out of memory? */
+       pfield->code = code;
+       strcpy(pfield->contents, value);
+       pfield->next = res->errFields;
+       res->errFields = pfield;
+}
 
 /*
  * pqSaveParameterStatus - remember parameter status sent by backend
@@ -543,26 +592,6 @@ pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
                StrNCpy(conn->sversion, value, sizeof(conn->sversion));
 }
 
-/*
- * pqGetParameterStatus - fetch parameter value, if available
- *
- * Returns NULL if info not available
- *
- * XXX this probably should be exported for client use
- */
-const char *
-pqGetParameterStatus(PGconn *conn, const char *name)
-{
-       pgParameterStatus *pstatus;
-
-       for (pstatus = conn->pstatus; pstatus != NULL; pstatus = pstatus->next)
-       {
-               if (strcmp(pstatus->name, name) == 0)
-                       return pstatus->value;
-       }
-       return NULL;
-}
-
 
 /*
  * PQsendQuery
@@ -574,12 +603,9 @@ pqGetParameterStatus(PGconn *conn, const char *name)
 int
 PQsendQuery(PGconn *conn, const char *query)
 {
-       if (!conn)
+       if (!PQsendQueryStart(conn))
                return 0;
 
-       /* clear the error string */
-       resetPQExpBuffer(&conn->errorMessage);
-
        if (!query)
        {
                printfPQExpBuffer(&conn->errorMessage,
@@ -587,25 +613,6 @@ PQsendQuery(PGconn *conn, const char *query)
                return 0;
        }
 
-       /* Don't try to send if we know there's no live connection. */
-       if (conn->status != CONNECTION_OK)
-       {
-               printfPQExpBuffer(&conn->errorMessage,
-                                                 libpq_gettext("no connection to the server\n"));
-               return 0;
-       }
-       /* Can't send while already busy, either. */
-       if (conn->asyncStatus != PGASYNC_IDLE)
-       {
-               printfPQExpBuffer(&conn->errorMessage,
-                         libpq_gettext("another command is already in progress\n"));
-               return 0;
-       }
-
-       /* initialize async result-accumulation state */
-       conn->result = NULL;
-       conn->curTuple = NULL;
-
        /* construct the outgoing Query message */
        if (pqPutMsgStart('Q', false, conn) < 0 ||
                pqPuts(query, conn) < 0 ||
@@ -617,7 +624,7 @@ PQsendQuery(PGconn *conn, const char *query)
 
        /*
         * Give the data a push.  In nonblock mode, don't complain if we're
-        * unable to send it all; PQconsumeInput() will do any additional flushing
+        * unable to send it all; PQgetResult() will do any additional flushing
         * needed.
         */
        if (pqFlush(conn) < 0)
@@ -631,6 +638,194 @@ PQsendQuery(PGconn *conn, const char *query)
        return 1;
 }
 
+/*
+ * PQsendQueryParams
+ *             Like PQsendQuery, but use 3.0 protocol so we can pass parameters
+ */
+int
+PQsendQueryParams(PGconn *conn,
+                                 const char *command,
+                                 int nParams,
+                                 const Oid *paramTypes,
+                                 const char * const *paramValues,
+                                 const int *paramLengths,
+                                 const int *paramFormats,
+                                 int resultFormat)
+{
+       int                     i;
+
+       if (!PQsendQueryStart(conn))
+               return 0;
+
+       /* This isn't gonna work on a 2.0 server */
+       if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                               libpq_gettext("function requires at least 3.0 protocol\n"));
+               return 0;
+       }
+
+       if (!command)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                       libpq_gettext("command string is a null pointer\n"));
+               return 0;
+       }
+
+       /*
+        * We will send Parse, Bind, Describe Portal, Execute, Sync, using
+        * unnamed statement and portal.
+        */
+
+       /* construct the Parse message */
+       if (pqPutMsgStart('P', false, conn) < 0 ||
+               pqPuts("", conn) < 0 ||
+               pqPuts(command, conn) < 0)
+               goto sendFailed;
+       if (nParams > 0 && paramTypes)
+       {
+               if (pqPutInt(nParams, 2, conn) < 0)
+                       goto sendFailed;
+               for (i = 0; i < nParams; i++)
+               {
+                       if (pqPutInt(paramTypes[i], 4, conn) < 0)
+                               goto sendFailed;
+               }
+       }
+       else
+       {
+               if (pqPutInt(0, 2, conn) < 0)
+                       goto sendFailed;
+       }
+       if (pqPutMsgEnd(conn) < 0)
+               goto sendFailed;
+
+       /* construct the Bind message */
+       if (pqPutMsgStart('B', false, conn) < 0 ||
+               pqPuts("", conn) < 0 ||
+               pqPuts("", conn) < 0)
+               goto sendFailed;
+       if (nParams > 0 && paramFormats)
+       {
+               if (pqPutInt(nParams, 2, conn) < 0)
+                       goto sendFailed;
+               for (i = 0; i < nParams; i++)
+               {
+                       if (pqPutInt(paramFormats[i], 2, conn) < 0)
+                               goto sendFailed;
+               }
+       }
+       else
+       {
+               if (pqPutInt(0, 2, conn) < 0)
+                       goto sendFailed;
+       }
+       if (pqPutInt(nParams, 2, conn) < 0)
+               goto sendFailed;
+       for (i = 0; i < nParams; i++)
+       {
+               if (paramValues && paramValues[i])
+               {
+                       int             nbytes;
+
+                       if (paramFormats && paramFormats[i] != 0)
+                       {
+                               /* binary parameter */
+                               nbytes = paramLengths[i];
+                       }
+                       else
+                       {
+                               /* text parameter, do not use paramLengths */
+                               nbytes = strlen(paramValues[i]);
+                       }
+                       if (pqPutInt(nbytes, 4, conn) < 0 ||
+                               pqPutnchar(paramValues[i], nbytes, conn) < 0)
+                               goto sendFailed;
+               }
+               else
+               {
+                       /* take the param as NULL */
+                       if (pqPutInt(-1, 4, conn) < 0)
+                               goto sendFailed;
+               }
+       }
+       if (pqPutInt(1, 2, conn) < 0 ||
+               pqPutInt(resultFormat, 2, conn))
+               goto sendFailed;
+       if (pqPutMsgEnd(conn) < 0)
+               goto sendFailed;
+
+       /* construct the Describe Portal message */
+       if (pqPutMsgStart('D', false, conn) < 0 ||
+               pqPutc('P', conn) < 0 ||
+               pqPuts("", conn) < 0 ||
+               pqPutMsgEnd(conn) < 0)
+               goto sendFailed;
+
+       /* construct the Execute message */
+       if (pqPutMsgStart('E', false, conn) < 0 ||
+               pqPuts("", conn) < 0 ||
+               pqPutInt(0, 4, conn) < 0 ||
+               pqPutMsgEnd(conn) < 0)
+               goto sendFailed;
+
+       /* construct the Sync message */
+       if (pqPutMsgStart('S', false, conn) < 0 ||
+               pqPutMsgEnd(conn) < 0)
+               goto sendFailed;
+
+       /*
+        * Give the data a push.  In nonblock mode, don't complain if we're
+        * unable to send it all; PQgetResult() will do any additional flushing
+        * needed.
+        */
+       if (pqFlush(conn) < 0)
+               goto sendFailed;
+
+       /* OK, it's launched! */
+       conn->asyncStatus = PGASYNC_BUSY;
+       return 1;
+
+sendFailed:
+       pqHandleSendFailure(conn);
+       return 0;
+}
+
+/*
+ * Common startup code for PQsendQuery and PQsendQueryParams
+ */
+static bool
+PQsendQueryStart(PGconn *conn)
+{
+       if (!conn)
+               return false;
+
+       /* clear the error string */
+       resetPQExpBuffer(&conn->errorMessage);
+
+       /* Don't try to send if we know there's no live connection. */
+       if (conn->status != CONNECTION_OK)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 libpq_gettext("no connection to the server\n"));
+               return false;
+       }
+       /* Can't send while already busy, either. */
+       if (conn->asyncStatus != PGASYNC_IDLE)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                         libpq_gettext("another command is already in progress\n"));
+               return false;
+       }
+
+       /* initialize async result-accumulation state */
+       conn->result = NULL;
+       conn->curTuple = NULL;
+
+       /* ready to send command message */
+       return true;
+}
+
 /*
  * pqHandleSendFailure: try to clean up after failure to send command.
  *
@@ -746,8 +941,24 @@ PQgetResult(PGconn *conn)
        /* If not ready to return something, block until we are. */
        while (conn->asyncStatus == PGASYNC_BUSY)
        {
+               int             flushResult;
+
+               /*
+                * If data remains unsent, send it.  Else we might be waiting
+                * for the result of a command the backend hasn't even got yet.
+                */
+               while ((flushResult = pqFlush(conn)) > 0)
+               {
+                       if (pqWait(FALSE, TRUE, conn))
+                       {
+                               flushResult = -1;
+                               break;
+                       }
+               }
+
                /* Wait for some more data, and load it. */
-               if (pqWait(TRUE, FALSE, conn) ||
+               if (flushResult ||
+                       pqWait(TRUE, FALSE, conn) ||
                        pqReadData(conn) < 0)
                {
                        /*
@@ -758,6 +969,7 @@ PQgetResult(PGconn *conn)
                        conn->asyncStatus = PGASYNC_IDLE;
                        return pqPrepareAsyncResult(conn);
                }
+
                /* Parse it. */
                parseInput(conn);
        }
@@ -774,10 +986,16 @@ PQgetResult(PGconn *conn)
                        conn->asyncStatus = PGASYNC_BUSY;
                        break;
                case PGASYNC_COPY_IN:
-                       res = PQmakeEmptyPGresult(conn, PGRES_COPY_IN);
+                       if (conn->result && conn->result->resultStatus == PGRES_COPY_IN)
+                               res = pqPrepareAsyncResult(conn);
+                       else
+                               res = PQmakeEmptyPGresult(conn, PGRES_COPY_IN);
                        break;
                case PGASYNC_COPY_OUT:
-                       res = PQmakeEmptyPGresult(conn, PGRES_COPY_OUT);
+                       if (conn->result && conn->result->resultStatus == PGRES_COPY_OUT)
+                               res = pqPrepareAsyncResult(conn);
+                       else
+                               res = PQmakeEmptyPGresult(conn, PGRES_COPY_OUT);
                        break;
                default:
                        printfPQExpBuffer(&conn->errorMessage,
@@ -806,18 +1024,46 @@ PQgetResult(PGconn *conn)
 PGresult *
 PQexec(PGconn *conn, const char *query)
 {
-       PGresult   *result;
-       PGresult   *lastResult;
-       bool            savedblocking;
+       if (!PQexecStart(conn))
+               return NULL;
+       if (!PQsendQuery(conn, query))
+               return NULL;
+       return PQexecFinish(conn);
+}
 
-       /*
-        * we assume anyone calling PQexec wants blocking behaviour, we force
-        * the blocking status of the connection to blocking for the duration
-        * of this function and restore it on return
-        */
-       savedblocking = pqIsnonblocking(conn);
-       if (PQsetnonblocking(conn, FALSE) == -1)
+/*
+ * PQexecParams
+ *             Like PQexec, but use 3.0 protocol so we can pass parameters
+ */
+PGresult *
+PQexecParams(PGconn *conn,
+                        const char *command,
+                        int nParams,
+                        const Oid *paramTypes,
+                        const char * const *paramValues,
+                        const int *paramLengths,
+                        const int *paramFormats,
+                        int resultFormat)
+{
+       if (!PQexecStart(conn))
+               return NULL;
+       if (!PQsendQueryParams(conn, command,
+                                                  nParams, paramTypes, paramValues, paramLengths,
+                                                  paramFormats, resultFormat))
                return NULL;
+       return PQexecFinish(conn);
+}
+
+/*
+ * Common code for PQexec and PQexecParams: prepare to send command
+ */
+static bool
+PQexecStart(PGconn *conn)
+{
+       PGresult   *result;
+
+       if (!conn)
+               return false;
 
        /*
         * Silently discard any prior query result that application didn't
@@ -832,15 +1078,23 @@ PQexec(PGconn *conn, const char *query)
                        PQclear(result);
                        printfPQExpBuffer(&conn->errorMessage,
                                 libpq_gettext("COPY state must be terminated first\n"));
-                       /* restore blocking status */
-                       goto errout;
+                       return false;
                }
                PQclear(result);
        }
 
-       /* OK to send the message */
-       if (!PQsendQuery(conn, query))
-               goto errout;                    /* restore blocking status */
+       /* OK to send a command */
+       return true;
+}
+
+/*
+ * Common code for PQexec and PQexecParams: wait for command result
+ */
+static PGresult *
+PQexecFinish(PGconn *conn)
+{
+       PGresult   *result;
+       PGresult   *lastResult;
 
        /*
         * For backwards compatibility, return the last result if there are
@@ -848,7 +1102,7 @@ PQexec(PGconn *conn, const char *query)
         * error result.
         *
         * We have to stop if we see copy in/out, however. We will resume parsing
-        * when application calls PQendcopy.
+        * after application performs the data transfer.
         */
        lastResult = NULL;
        while ((result = PQgetResult(conn)) != NULL)
@@ -874,14 +1128,7 @@ PQexec(PGconn *conn, const char *query)
                        break;
        }
 
-       if (PQsetnonblocking(conn, savedblocking) == -1)
-               return NULL;
        return lastResult;
-
-errout:
-       if (PQsetnonblocking(conn, savedblocking) == -1)
-               return NULL;
-       return NULL;
 }
 
 /*
@@ -894,7 +1141,6 @@ errout:
  *
  * the CALLER is responsible for FREE'ing the structure returned
  */
-
 PGnotify *
 PQnotifies(PGconn *conn)
 {
@@ -916,6 +1162,156 @@ PQnotifies(PGconn *conn)
        return event;
 }
 
+/*
+ * PQputCopyData - send some data to the backend during COPY IN
+ *
+ * Returns 1 if successful, 0 if data could not be sent (only possible
+ * in nonblock mode), or -1 if an error occurs.
+ */
+int
+PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
+{
+       if (!conn)
+               return -1;
+       if (conn->asyncStatus != PGASYNC_COPY_IN)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 libpq_gettext("no COPY in progress\n"));
+               return -1;
+       }
+       if (nbytes > 0)
+       {
+               /*
+                * Try to flush any previously sent data in preference to growing
+                * the output buffer.  If we can't enlarge the buffer enough to hold
+                * the data, return 0 in the nonblock case, else hard error.
+                * (For simplicity, always assume 5 bytes of overhead even in
+                * protocol 2.0 case.)
+                */
+               if ((conn->outBufSize - conn->outCount - 5) < nbytes)
+               {
+                       if (pqFlush(conn) < 0)
+                               return -1;
+                       if (pqCheckOutBufferSpace(conn->outCount + 5 + nbytes, conn))
+                               return pqIsnonblocking(conn) ? 0 : -1;
+               }
+               /* Send the data (too simple to delegate to fe-protocol files) */
+               if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+               {
+                       if (pqPutMsgStart('d', false, conn) < 0 ||
+                               pqPutnchar(buffer, nbytes, conn) < 0 ||
+                               pqPutMsgEnd(conn) < 0)
+                               return -1;
+               }
+               else
+               {
+                       if (pqPutMsgStart(0, false, conn) < 0 ||
+                               pqPutnchar(buffer, nbytes, conn) < 0 ||
+                               pqPutMsgEnd(conn) < 0)
+                               return -1;
+               }
+       }
+       return 1;
+}
+
+/*
+ * PQputCopyEnd - send EOF indication to the backend during COPY IN
+ *
+ * After calling this, use PQgetResult() to check command completion status.
+ *
+ * Returns 1 if successful, 0 if data could not be sent (only possible
+ * in nonblock mode), or -1 if an error occurs.
+ */
+int
+PQputCopyEnd(PGconn *conn, const char *errormsg)
+{
+       if (!conn)
+               return -1;
+       if (conn->asyncStatus != PGASYNC_COPY_IN)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 libpq_gettext("no COPY in progress\n"));
+               return -1;
+       }
+       /*
+        * Send the COPY END indicator.  This is simple enough that we don't
+        * bother delegating it to the fe-protocol files.
+        */
+       if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+       {
+               if (errormsg)
+               {
+                       /* Send COPY FAIL */
+                       if (pqPutMsgStart('f', false, conn) < 0 ||
+                               pqPuts(errormsg, conn) < 0 ||
+                               pqPutMsgEnd(conn) < 0)
+                               return -1;
+               }
+               else
+               {
+                       /* Send COPY DONE */
+                       if (pqPutMsgStart('c', false, conn) < 0 ||
+                               pqPutMsgEnd(conn) < 0)
+                               return -1;
+               }
+       }
+       else
+       {
+               if (errormsg)
+               {
+                       /* Ooops, no way to do this in 2.0 */
+                       printfPQExpBuffer(&conn->errorMessage,
+                               libpq_gettext("function requires at least 3.0 protocol\n"));
+                       return -1;
+               }
+               else
+               {
+                       /* Send old-style end-of-data marker */
+                       if (pqPutMsgStart(0, false, conn) < 0 ||
+                               pqPuts("\\.\n", conn) < 0 ||
+                               pqPutMsgEnd(conn) < 0)
+                               return -1;
+               }
+       }
+
+       /* Return to active duty */
+       conn->asyncStatus = PGASYNC_BUSY;
+       resetPQExpBuffer(&conn->errorMessage);
+
+       /* Try to flush data */
+       if (pqFlush(conn) < 0)
+               return -1;
+
+       return 1;
+}
+
+/*
+ * PQgetCopyData - read a row of data from the backend during COPY OUT
+ *
+ * If successful, sets *buffer to point to a malloc'd row of data, and
+ * returns row length (always > 0) as result.
+ * Returns 0 if no row available yet (only possible if async is true),
+ * -1 if end of copy (consult PQgetResult), or -2 if error (consult
+ * PQerrorMessage).
+ */
+int
+PQgetCopyData(PGconn *conn, char **buffer, int async)
+{
+       *buffer = NULL;                         /* for all failure cases */
+       if (!conn)
+               return -2;
+       if (conn->asyncStatus != PGASYNC_COPY_OUT)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 libpq_gettext("no COPY in progress\n"));
+               return -2;
+       }
+       if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+               return pqGetCopyData3(conn, buffer, async);
+       else
+               return pqGetCopyData2(conn, buffer, async);
+}
+
 /*
  * PQgetline - gets a newline-terminated string from the backend.
  *
@@ -1002,11 +1398,12 @@ PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
 }
 
 /*
- * PQputline -- sends a string to the backend.
+ * PQputline -- sends a string to the backend during COPY IN.
  * Returns 0 if OK, EOF if not.
  *
- * This exists to support "COPY <rel> from stdin".  The backend will ignore
- * the string if not doing COPY.
+ * This is deprecated primarily because the return convention doesn't allow
+ * caller to tell the difference between a hard error and a nonblock-mode
+ * send failure.
  */
 int
 PQputline(PGconn *conn, const char *s)
@@ -1021,27 +1418,10 @@ PQputline(PGconn *conn, const char *s)
 int
 PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
 {
-       if (!conn || conn->sock < 0)
+       if (PQputCopyData(conn, buffer, nbytes) > 0)
+               return 0;
+       else
                return EOF;
-       if (nbytes > 0)
-       {
-               /* This is too simple to bother with separate subroutines */
-               if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
-               {
-                       if (pqPutMsgStart('d', false, conn) < 0 ||
-                               pqPutnchar(buffer, nbytes, conn) < 0 ||
-                               pqPutMsgEnd(conn) < 0)
-                               return EOF;
-               }
-               else
-               {
-                       if (pqPutMsgStart(0, false, conn) < 0 ||
-                               pqPutnchar(buffer, nbytes, conn) < 0 ||
-                               pqPutMsgEnd(conn) < 0)
-                               return EOF;
-               }
-       }
-       return 0;
 }
 
 /*
@@ -1049,6 +1429,11 @@ PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
  *             After completing the data transfer portion of a copy in/out,
  *             the application must call this routine to finish the command protocol.
  *
+ * When using 3.0 protocol this is deprecated; it's cleaner to use PQgetResult
+ * to get the transfer status.  Note however that when using 2.0 protocol,
+ * recovering from a copy failure often requires a PQreset.  PQendcopy will
+ * take care of that, PQgetResult won't.
+ *
  * RETURNS:
  *             0 on success
  *             1 on failure
@@ -1133,7 +1518,7 @@ ExecStatusType
 PQresultStatus(const PGresult *res)
 {
        if (!res)
-               return PGRES_NONFATAL_ERROR;
+               return PGRES_FATAL_ERROR;
        return res->resultStatus;
 }
 
@@ -1153,6 +1538,21 @@ PQresultErrorMessage(const PGresult *res)
        return res->errMsg;
 }
 
+char *
+PQresultErrorField(const PGresult *res, int fieldcode)
+{
+       PGMessageField *pfield;
+
+       if (!res)
+               return NULL;
+       for (pfield = res->errFields; pfield != NULL; pfield = pfield->next)
+       {
+               if (pfield->code == fieldcode)
+                       return pfield->contents;
+       }
+       return NULL;
+}
+
 int
 PQntuples(const PGresult *res)
 {
@@ -1191,13 +1591,10 @@ check_field_number(const PGresult *res, int field_num)
                return FALSE;                   /* no way to display error message... */
        if (field_num < 0 || field_num >= res->numAttributes)
        {
-               if (res->noticeHook)
-               {
-                       snprintf(noticeBuf, sizeof(noticeBuf),
-                          libpq_gettext("column number %d is out of range 0..%d\n"),
-                                        field_num, res->numAttributes - 1);
-                       PGDONOTICE(res, noticeBuf);
-               }
+               snprintf(noticeBuf, sizeof(noticeBuf),
+                                libpq_gettext("column number %d is out of range 0..%d"),
+                                field_num, res->numAttributes - 1);
+               PGDONOTICE(res, noticeBuf);
                return FALSE;
        }
        return TRUE;
@@ -1213,32 +1610,26 @@ check_tuple_field_number(const PGresult *res,
                return FALSE;                   /* no way to display error message... */
        if (tup_num < 0 || tup_num >= res->ntups)
        {
-               if (res->noticeHook)
-               {
-                       snprintf(noticeBuf, sizeof(noticeBuf),
-                                 libpq_gettext("row number %d is out of range 0..%d\n"),
-                                        tup_num, res->ntups - 1);
-                       PGDONOTICE(res, noticeBuf);
-               }
+               snprintf(noticeBuf, sizeof(noticeBuf),
+                                libpq_gettext("row number %d is out of range 0..%d"),
+                                tup_num, res->ntups - 1);
+               PGDONOTICE(res, noticeBuf);
                return FALSE;
        }
        if (field_num < 0 || field_num >= res->numAttributes)
        {
-               if (res->noticeHook)
-               {
-                       snprintf(noticeBuf, sizeof(noticeBuf),
-                          libpq_gettext("column number %d is out of range 0..%d\n"),
-                                        field_num, res->numAttributes - 1);
-                       PGDONOTICE(res, noticeBuf);
-               }
+               snprintf(noticeBuf, sizeof(noticeBuf),
+                                libpq_gettext("column number %d is out of range 0..%d"),
+                                field_num, res->numAttributes - 1);
+               PGDONOTICE(res, noticeBuf);
                return FALSE;
        }
        return TRUE;
 }
 
 /*
  returns NULL if the field_num is invalid
-*/
* returns NULL if the field_num is invalid
+ */
 char *
 PQfname(const PGresult *res, int field_num)
 {
@@ -1251,8 +1642,8 @@ PQfname(const PGresult *res, int field_num)
 }
 
 /*
  returns -1 on a bad field name
-*/
* returns -1 on a bad field name
+ */
 int
 PQfnumber(const PGresult *res, const char *field_name)
 {
@@ -1290,6 +1681,39 @@ PQfnumber(const PGresult *res, const char *field_name)
        return -1;
 }
 
+Oid
+PQftable(const PGresult *res, int field_num)
+{
+       if (!check_field_number(res, field_num))
+               return InvalidOid;
+       if (res->attDescs)
+               return res->attDescs[field_num].tableid;
+       else
+               return InvalidOid;
+}
+
+int
+PQftablecol(const PGresult *res, int field_num)
+{
+       if (!check_field_number(res, field_num))
+               return 0;
+       if (res->attDescs)
+               return res->attDescs[field_num].columnid;
+       else
+               return 0;
+}
+
+int
+PQfformat(const PGresult *res, int field_num)
+{
+       if (!check_field_number(res, field_num))
+               return 0;
+       if (res->attDescs)
+               return res->attDescs[field_num].format;
+       else
+               return 0;
+}
+
 Oid
 PQftype(const PGresult *res, int field_num)
 {
@@ -1332,10 +1756,10 @@ PQcmdStatus(PGresult *res)
 }
 
 /*
  PQoidStatus -
      if the last command was an INSERT, return the oid string
      if not, return ""
-*/
* PQoidStatus -
*     if the last command was an INSERT, return the oid string
*     if not, return ""
+ */
 char *
 PQoidStatus(const PGresult *res)
 {
@@ -1360,10 +1784,10 @@ PQoidStatus(const PGresult *res)
 }
 
 /*
-  PQoidValue -
      a perhaps preferable form of the above which just returns
      an Oid type
-*/
* PQoidValue -
*     a perhaps preferable form of the above which just returns
*     an Oid type
+ */
 Oid
 PQoidValue(const PGresult *res)
 {
@@ -1388,13 +1812,13 @@ PQoidValue(const PGresult *res)
 
 
 /*
  PQcmdTuples -
      If the last command was an INSERT/UPDATE/DELETE/MOVE/FETCH, return a
      string containing the number of inserted/affected tuples. If not,
      return "".
-
      XXX: this should probably return an int
-*/
* PQcmdTuples -
*     If the last command was an INSERT/UPDATE/DELETE/MOVE/FETCH, return a
*     string containing the number of inserted/affected tuples. If not,
*     return "".
+ *
*     XXX: this should probably return an int
+ */
 char *
 PQcmdTuples(PGresult *res)
 {
@@ -1426,13 +1850,10 @@ PQcmdTuples(PGresult *res)
 
        if (*p == 0)
        {
-               if (res->noticeHook)
-               {
-                       snprintf(noticeBuf, sizeof(noticeBuf),
-                                        libpq_gettext("could not interpret result from server: %s\n"),
-                                        res->cmdStatus);
-                       PGDONOTICE(res, noticeBuf);
-               }
+               snprintf(noticeBuf, sizeof(noticeBuf),
+                                libpq_gettext("could not interpret result from server: %s"),
+                                res->cmdStatus);
+               PGDONOTICE(res, noticeBuf);
                return "";
        }
 
@@ -1440,15 +1861,9 @@ PQcmdTuples(PGresult *res)
 }
 
 /*
-   PQgetvalue:
-       return the value of field 'field_num' of row 'tup_num'
-
-       If res is binary, then the value returned is NOT a null-terminated
-       ASCII string, but the binary representation in the server's native
-       format.
-
-       if res is not binary, a null-terminated ASCII string is returned.
-*/
+ * PQgetvalue:
+ *     return the value of field 'field_num' of row 'tup_num'
+ */
 char *
 PQgetvalue(const PGresult *res, int tup_num, int field_num)
 {
@@ -1458,11 +1873,8 @@ PQgetvalue(const PGresult *res, int tup_num, int field_num)
 }
 
 /* PQgetlength:
-        returns the length of a field value in bytes.  If res is binary,
-        i.e. a result of a binary portal, then the length returned does
-        NOT include the size field of the varlena.  (The data returned
-        by PQgetvalue doesn't either.)
-*/
+ *     returns the actual length of a field value in bytes.
+ */
 int
 PQgetlength(const PGresult *res, int tup_num, int field_num)
 {
@@ -1475,8 +1887,8 @@ PQgetlength(const PGresult *res, int tup_num, int field_num)
 }
 
 /* PQgetisnull:
       returns the null status of a field value.
-*/
*     returns the null status of a field value.
+ */
 int
 PQgetisnull(const PGresult *res, int tup_num, int field_num)
 {
@@ -1489,16 +1901,17 @@ PQgetisnull(const PGresult *res, int tup_num, int field_num)
 }
 
 /* PQsetnonblocking:
-        sets the PGconn's database connection non-blocking if the arg is TRUE
-        or makes it non-blocking if the arg is FALSE, this will not protect
-        you from PQexec(), you'll only be safe when using the non-blocking
-        API
-        Needs to be called only on a connected database connection.
-*/
-
+ *     sets the PGconn's database connection non-blocking if the arg is TRUE
+ *     or makes it non-blocking if the arg is FALSE, this will not protect
+ *     you from PQexec(), you'll only be safe when using the non-blocking API.
+ *     Needs to be called only on a connected database connection.
+ */
 int
 PQsetnonblocking(PGconn *conn, int arg)
 {
+       if (!conn || conn->status == CONNECTION_BAD)
+               return -1;
+
        arg = (arg == TRUE) ? 1 : 0;
        /* early out if the socket is already in the state requested */
        if (arg == conn->nonblocking)
@@ -1520,9 +1933,10 @@ PQsetnonblocking(PGconn *conn, int arg)
        return (0);
 }
 
-/* return the blocking status of the database connection, TRUE == nonblocking,
-        FALSE == blocking
-*/
+/*
+ * return the blocking status of the database connection
+ *             TRUE == nonblocking, FALSE == blocking
+ */
 int
 PQisnonblocking(const PGconn *conn)
 {
index 3856145a4ef1c42d48a99a801b7ccdb7a8d2b51c..f10c3112d7e81419426047da57e17a2a15f5378b 100644 (file)
@@ -23,7 +23,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.96 2003/06/14 17:49:54 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.97 2003/06/21 21:51:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -197,7 +197,7 @@ pqPutnchar(const char *s, size_t len, PGconn *conn)
 }
 
 /*
- * pgGetInt
+ * pqGetInt
  *     read a 2 or 4 byte integer and convert from network byte order
  *     to local byte order
  */
@@ -226,7 +226,7 @@ pqGetInt(int *result, size_t bytes, PGconn *conn)
                        break;
                default:
                        snprintf(noticeBuf, sizeof(noticeBuf),
-                                        libpq_gettext("integer of size %lu not supported by pqGetInt\n"),
+                                        libpq_gettext("integer of size %lu not supported by pqGetInt"),
                                         (unsigned long) bytes);
                        PGDONOTICE(conn, noticeBuf);
                        return EOF;
@@ -239,7 +239,7 @@ pqGetInt(int *result, size_t bytes, PGconn *conn)
 }
 
 /*
- * pgPutInt
+ * pqPutInt
  * write an integer of 2 or 4 bytes, converting from host byte order
  * to network byte order.
  */
@@ -264,7 +264,7 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
                        break;
                default:
                        snprintf(noticeBuf, sizeof(noticeBuf),
-                                        libpq_gettext("integer of size %lu not supported by pqPutInt\n"),
+                                        libpq_gettext("integer of size %lu not supported by pqPutInt"),
                                         (unsigned long) bytes);
                        PGDONOTICE(conn, noticeBuf);
                        return EOF;
@@ -282,7 +282,7 @@ pqPutInt(int value, size_t bytes, PGconn *conn)
  *
  * Returns 0 on success, EOF if failed to enlarge buffer
  */
-static int
+int
 pqCheckOutBufferSpace(int bytes_needed, PGconn *conn)
 {
        int                     newsize = conn->outBufSize;
@@ -748,7 +748,7 @@ pqSendSome(PGconn *conn, int len)
                if (sent < 0)
                {
                        /*
-                        * Anything except EAGAIN or EWOULDBLOCK is trouble. If it's
+                        * Anything except EAGAIN/EWOULDBLOCK/EINTR is trouble. If it's
                         * EPIPE or ECONNRESET, assume we've lost the backend
                         * connection permanently.
                         */
@@ -804,25 +804,17 @@ pqSendSome(PGconn *conn, int len)
 
                if (len > 0)
                {
-                       /* We didn't send it all, wait till we can send more */
-
                        /*
-                        * if the socket is in non-blocking mode we may need to abort
-                        * here and return 1 to indicate that data is still pending.
+                        * We didn't send it all, wait till we can send more.
+                        *
+                        * If the connection is in non-blocking mode we don't wait,
+                        * but return 1 to indicate that data is still pending.
                         */
-#ifdef USE_SSL
-                       /* can't do anything for our SSL users yet */
-                       if (conn->ssl == NULL)
+                       if (pqIsnonblocking(conn))
                        {
-#endif
-                               if (pqIsnonblocking(conn))
-                               {
-                                       result = 1;
-                                       break;
-                               }
-#ifdef USE_SSL
+                               result = 1;
+                               break;
                        }
-#endif
 
                        if (pqWait(FALSE, TRUE, conn))
                        {
index 2a7b6b43bf5d57aea3d357bcbc58c90e3f690ad2..6b909334079d0ab60c994d95df6c004fa47d0986 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol2.c,v 1.1 2003/06/08 17:43:00 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol2.c,v 1.2 2003/06/21 21:51:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,6 +38,7 @@
 static int     getRowDescriptions(PGconn *conn);
 static int     getAnotherTuple(PGconn *conn, bool binary);
 static int     pqGetErrorNotice2(PGconn *conn, bool isError);
+static void checkXactStatus(PGconn *conn, const char *cmdTag);
 static int     getNotify(PGconn *conn);
 
 
@@ -312,7 +313,7 @@ pqSetenvPoll(PGconn *conn)
                                                                                                          val);
                                                        else
                                                        {
-                                                               val = pqGetParameterStatus(conn, "server_encoding");
+                                                               val = PQparameterStatus(conn, "server_encoding");
                                                                if (val && *val)
                                                                        pqSaveParameterStatus(conn, "client_encoding",
                                                                                                                  val);
@@ -424,7 +425,7 @@ pqParseInput2(PGconn *conn)
                        else
                        {
                                snprintf(noticeWorkspace, sizeof(noticeWorkspace),
-                                                libpq_gettext("message type 0x%02x arrived from server while idle\n"),
+                                                libpq_gettext("message type 0x%02x arrived from server while idle"),
                                                 id);
                                PGDONOTICE(conn, noticeWorkspace);
                                /* Discard the unexpected message; good idea?? */
@@ -447,6 +448,7 @@ pqParseInput2(PGconn *conn)
                                                                                                           PGRES_COMMAND_OK);
                                        strncpy(conn->result->cmdStatus, conn->workBuffer.data,
                                                        CMDSTATUS_LEN);
+                                       checkXactStatus(conn, conn->workBuffer.data);
                                        conn->asyncStatus = PGASYNC_READY;
                                        break;
                                case 'E':               /* error return */
@@ -464,7 +466,7 @@ pqParseInput2(PGconn *conn)
                                        if (id != '\0')
                                        {
                                                snprintf(noticeWorkspace, sizeof(noticeWorkspace),
-                                                                libpq_gettext("unexpected character %c following empty query response (\"I\" message)\n"),
+                                                                libpq_gettext("unexpected character %c following empty query response (\"I\" message)"),
                                                                 id);
                                                PGDONOTICE(conn, noticeWorkspace);
                                        }
@@ -521,7 +523,7 @@ pqParseInput2(PGconn *conn)
                                        else
                                        {
                                                snprintf(noticeWorkspace, sizeof(noticeWorkspace),
-                                                                libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)\n"));
+                                                                libpq_gettext("server sent data (\"D\" message) without prior row description (\"T\" message)"));
                                                PGDONOTICE(conn, noticeWorkspace);
                                                /* Discard the unexpected message; good idea?? */
                                                conn->inStart = conn->inEnd;
@@ -538,7 +540,7 @@ pqParseInput2(PGconn *conn)
                                        else
                                        {
                                                snprintf(noticeWorkspace, sizeof(noticeWorkspace),
-                                                                libpq_gettext("server sent binary data (\"B\" message) without prior row description (\"T\" message)\n"));
+                                                                libpq_gettext("server sent binary data (\"B\" message) without prior row description (\"T\" message)"));
                                                PGDONOTICE(conn, noticeWorkspace);
                                                /* Discard the unexpected message; good idea?? */
                                                conn->inStart = conn->inEnd;
@@ -628,6 +630,9 @@ getRowDescriptions(PGconn *conn)
 
                result->attDescs[i].name = pqResultStrdup(result,
                                                                                                  conn->workBuffer.data);
+               result->attDescs[i].tableid = 0;
+               result->attDescs[i].columnid = 0;
+               result->attDescs[i].format = 0;
                result->attDescs[i].typid = typid;
                result->attDescs[i].typlen = typlen;
                result->attDescs[i].atttypmod = atttypmod;
@@ -674,6 +679,15 @@ getAnotherTuple(PGconn *conn, bool binary)
                if (conn->curTuple == NULL)
                        goto outOfMemory;
                MemSet((char *) conn->curTuple, 0, nfields * sizeof(PGresAttValue));
+               /*
+                * If it's binary, fix the column format indicators.  We assume
+                * the backend will consistently send either B or D, not a mix.
+                */
+               if (binary)
+               {
+                       for (i = 0; i < nfields; i++)
+                               result->attDescs[i].format = 1;
+               }
        }
        tup = conn->curTuple;
 
@@ -778,6 +792,8 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
 {
        PGresult   *res;
        PQExpBufferData workBuf;
+       char       *startp;
+       char       *splitp;
 
        /*
         * Since the message might be pretty long, we create a temporary
@@ -800,19 +816,63 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
        res->resultStatus = isError ? PGRES_FATAL_ERROR : PGRES_NONFATAL_ERROR;
        res->errMsg = pqResultStrdup(res, workBuf.data);
 
+       /*
+        * Break the message into fields.  We can't do very much here, but we
+        * can split the severity code off, and remove trailing newlines.  Also,
+        * we use the heuristic that the primary message extends only to the
+        * first newline --- anything after that is detail message.  (In some
+        * cases it'd be better classed as hint, but we can hardly be expected
+        * to guess that here.)
+        */
+       while (workBuf.len > 0 && workBuf.data[workBuf.len-1] == '\n')
+               workBuf.data[--workBuf.len] = '\0';
+       splitp = strstr(workBuf.data, ":  ");
+       if (splitp)
+       {
+               /* what comes before the colon is severity */
+               *splitp = '\0';
+               pqSaveMessageField(res, 'S', workBuf.data);
+               startp = splitp + 3;
+       }
+       else
+       {
+               /* can't find a colon?  oh well... */
+               startp = workBuf.data;
+       }
+       splitp = strchr(startp, '\n');
+       if (splitp)
+       {
+               /* what comes before the newline is primary message */
+               *splitp++ = '\0';
+               pqSaveMessageField(res, 'M', startp);
+               /* the rest is detail; strip any leading whitespace */
+               while (*splitp && isspace((unsigned char) *splitp))
+                       splitp++;
+               pqSaveMessageField(res, 'D', splitp);
+       }
+       else
+       {
+               /* single-line message, so all primary */
+               pqSaveMessageField(res, 'M', startp);
+       }
+
        /*
         * Either save error as current async result, or just emit the notice.
+        * Also, if it's an error and we were in a transaction block, assume
+        * the server has now gone to error-in-transaction state.
         */
        if (isError)
        {
                pqClearAsyncResult(conn);
                conn->result = res;
                resetPQExpBuffer(&conn->errorMessage);
-               appendPQExpBufferStr(&conn->errorMessage, workBuf.data);
+               appendPQExpBufferStr(&conn->errorMessage, res->errMsg);
+               if (conn->xactStatus == PQTRANS_INTRANS)
+                       conn->xactStatus = PQTRANS_INERROR;
        }
        else
        {
-               PGDONOTICE(conn, workBuf.data);
+               (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
                PQclear(res);
        }
 
@@ -820,6 +880,37 @@ pqGetErrorNotice2(PGconn *conn, bool isError)
        return 0;
 }
 
+/*
+ * checkXactStatus - attempt to track transaction-block status of server
+ *
+ * This is called each time we receive a command-complete message.  By
+ * watching for messages from BEGIN/COMMIT/ROLLBACK commands, we can do
+ * a passable job of tracking the server's xact status.  BUT: this does
+ * not work at all on 7.3 servers with AUTOCOMMIT OFF.  (Man, was that
+ * feature ever a mistake.)  Caveat user.
+ *
+ * The tags known here are all those used as far back as 7.0; is it worth
+ * adding those from even-older servers?
+ */
+static void
+checkXactStatus(PGconn *conn, const char *cmdTag)
+{
+       if (strcmp(cmdTag, "BEGIN") == 0)
+               conn->xactStatus = PQTRANS_INTRANS;
+       else if (strcmp(cmdTag, "COMMIT") == 0)
+               conn->xactStatus = PQTRANS_IDLE;
+       else if (strcmp(cmdTag, "ROLLBACK") == 0)
+               conn->xactStatus = PQTRANS_IDLE;
+       else if (strcmp(cmdTag, "START TRANSACTION") == 0) /* 7.3 only */
+               conn->xactStatus = PQTRANS_INTRANS;
+       /*
+        * Normally we get into INERROR state by detecting an Error message.
+        * However, if we see one of these tags then we know for sure the
+        * server is in abort state ...
+        */
+       else if (strcmp(cmdTag, "*ABORT STATE*") == 0) /* pre-7.3 only */
+               conn->xactStatus = PQTRANS_INERROR;
+}
 
 /*
  * Attempt to read a Notify response message.
@@ -832,6 +923,7 @@ static int
 getNotify(PGconn *conn)
 {
        int                     be_pid;
+       int                     nmlen;
        PGnotify   *newNotify;
 
        if (pqGetInt(&be_pid, 4, conn))
@@ -844,12 +936,14 @@ getNotify(PGconn *conn)
         * can all be freed at once.  We don't use NAMEDATALEN because we
         * don't want to tie this interface to a specific server name length.
         */
-       newNotify = (PGnotify *) malloc(sizeof(PGnotify) +
-                                                                       strlen(conn->workBuffer.data) +1);
+       nmlen = strlen(conn->workBuffer.data);
+       newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + 1);
        if (newNotify)
        {
                newNotify->relname = (char *) newNotify + sizeof(PGnotify);
                strcpy(newNotify->relname, conn->workBuffer.data);
+               /* fake up an empty-string extra field */
+               newNotify->extra = newNotify->relname + nmlen;
                newNotify->be_pid = be_pid;
                DLAddTail(conn->notifyList, DLNewElem(newNotify));
        }
@@ -858,6 +952,84 @@ getNotify(PGconn *conn)
 }
 
 
+/*
+ * PQgetCopyData - read a row of data from the backend during COPY OUT
+ *
+ * If successful, sets *buffer to point to a malloc'd row of data, and
+ * returns row length (always > 0) as result.
+ * Returns 0 if no row available yet (only possible if async is true),
+ * -1 if end of copy (consult PQgetResult), or -2 if error (consult
+ * PQerrorMessage).
+ */
+int
+pqGetCopyData2(PGconn *conn, char **buffer, int async)
+{
+       bool            found;
+       int                     msgLength;
+
+       for (;;)
+       {
+               /*
+                * Do we have a complete line of data?
+                */
+               conn->inCursor = conn->inStart;
+               found = false;
+               while (conn->inCursor < conn->inEnd)
+               {
+                       char            c = conn->inBuffer[conn->inCursor++];
+
+                       if (c == '\n')
+                       {
+                               found = true;
+                               break;
+                       }
+               }
+               if (!found)
+                       goto nodata;
+               msgLength = conn->inCursor - conn->inStart;
+
+               /*
+                * If it's the end-of-data marker, consume it, exit COPY_OUT mode,
+                * and let caller read status with PQgetResult().
+                */
+               if (msgLength == 3 &&
+                       strncmp(&conn->inBuffer[conn->inStart], "\\.\n", 3) == 0)
+               {
+                       conn->inStart = conn->inCursor;
+                       conn->asyncStatus = PGASYNC_BUSY;
+                       return -1;
+               }
+
+               /*
+                * Pass the line back to the caller.
+                */
+               *buffer = (char *) malloc(msgLength + 1);
+               if (*buffer == NULL)
+               {
+                       printfPQExpBuffer(&conn->errorMessage,
+                                                         libpq_gettext("out of memory\n"));
+                       return -2;
+               }
+               memcpy(*buffer, &conn->inBuffer[conn->inStart], msgLength);
+               (*buffer)[msgLength] = '\0'; /* Add terminating null */
+
+               /* Mark message consumed */
+               conn->inStart = conn->inCursor;
+
+               return msgLength;
+
+       nodata:
+               /* Don't block if async read requested */
+               if (async)
+                       return 0;
+               /* Need to load more data */
+               if (pqWait(TRUE, FALSE, conn) ||
+                       pqReadData(conn) < 0)
+                       return -2;
+       }
+}
+
+
 /*
  * PQgetline - gets a newline-terminated string from the backend.
  *
@@ -1020,7 +1192,7 @@ pqEndcopy2(PGconn *conn)
        if (conn->errorMessage.len > 0)
                PGDONOTICE(conn, conn->errorMessage.data);
 
-       PGDONOTICE(conn, libpq_gettext("lost synchronization with server, resetting connection\n"));
+       PGDONOTICE(conn, libpq_gettext("lost synchronization with server, resetting connection"));
 
        /*
         * Users doing non-blocking connections need to handle the reset
index 2fbfa01566e21d8370b81d5cb8eb74c5fedca7de..05543f8e76dd04d88d395e9a05e3b879c72197e6 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.1 2003/06/08 17:43:00 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.2 2003/06/21 21:51:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,6 +40,8 @@ static int    getRowDescriptions(PGconn *conn);
 static int     getAnotherTuple(PGconn *conn, int msgLength);
 static int     getParameterStatus(PGconn *conn);
 static int     getNotify(PGconn *conn);
+static int     getCopyStart(PGconn *conn, ExecStatusType copytype);
+static int     getReadyForQuery(PGconn *conn);
 static int     build_startup_packet(const PGconn *conn, char *packet,
                                                                 const PQEnvironmentOption *options);
 
@@ -171,7 +173,7 @@ pqParseInput3(PGconn *conn)
                        else
                        {
                                snprintf(noticeWorkspace, sizeof(noticeWorkspace),
-                                                libpq_gettext("message type 0x%02x arrived from server while idle\n"),
+                                                libpq_gettext("message type 0x%02x arrived from server while idle"),
                                                 id);
                                PGDONOTICE(conn, noticeWorkspace);
                                /* Discard the unexpected message */
@@ -201,7 +203,7 @@ pqParseInput3(PGconn *conn)
                                        conn->asyncStatus = PGASYNC_READY;
                                        break;
                                case 'Z':               /* backend is ready for new query */
-                                       if (pqGetc(&conn->xact_status, conn))
+                                       if (getReadyForQuery(conn))
                                                return;
                                        conn->asyncStatus = PGASYNC_IDLE;
                                        break;
@@ -211,6 +213,11 @@ pqParseInput3(PGconn *conn)
                                                                                                          PGRES_EMPTY_QUERY);
                                        conn->asyncStatus = PGASYNC_READY;
                                        break;
+                               case '1':               /* Parse Complete */
+                               case '2':               /* Bind Complete */
+                               case '3':               /* Close Complete */
+                                       /* Nothing to do for these message types */
+                                       break;
                                case 'S':               /* parameter status */
                                        if (getParameterStatus(conn))
                                                return;
@@ -276,17 +283,13 @@ pqParseInput3(PGconn *conn)
                                        }
                                        break;
                                case 'G':               /* Start Copy In */
-                                       if (pqGetc(&conn->copy_is_binary, conn))
+                                       if (getCopyStart(conn, PGRES_COPY_IN))
                                                return;
-                                       /* XXX we currently ignore the rest of the message */
-                                       conn->inCursor = conn->inStart + 5 + msgLength;
                                        conn->asyncStatus = PGASYNC_COPY_IN;
                                        break;
                                case 'H':               /* Start Copy Out */
-                                       if (pqGetc(&conn->copy_is_binary, conn))
+                                       if (getCopyStart(conn, PGRES_COPY_OUT))
                                                return;
-                                       /* XXX we currently ignore the rest of the message */
-                                       conn->inCursor = conn->inStart + 5 + msgLength;
                                        conn->asyncStatus = PGASYNC_COPY_OUT;
                                        conn->copy_already_done = 0;
                                        break;
@@ -398,6 +401,9 @@ getRowDescriptions(PGconn *conn)
                MemSet((char *) result->attDescs, 0, nfields * sizeof(PGresAttDesc));
        }
 
+       /* result->binary is true only if ALL columns are binary */
+       result->binary = (nfields > 0) ? 1 : 0;
+
        /* get type info */
        for (i = 0; i < nfields; i++)
        {
@@ -430,10 +436,15 @@ getRowDescriptions(PGconn *conn)
 
                result->attDescs[i].name = pqResultStrdup(result,
                                                                                                  conn->workBuffer.data);
+               result->attDescs[i].tableid = tableid;
+               result->attDescs[i].columnid = columnid;
+               result->attDescs[i].format = format;
                result->attDescs[i].typid = typid;
                result->attDescs[i].typlen = typlen;
                result->attDescs[i].atttypmod = atttypmod;
-               /* XXX todo: save tableid/columnid, format too */
+
+               if (format != 1)
+                       result->binary = 0;
        }
 
        /* Success! */
@@ -503,7 +514,9 @@ getAnotherTuple(PGconn *conn, int msgLength)
                        vlen = 0;
                if (tup[i].value == NULL)
                {
-                       tup[i].value = (char *) pqResultAlloc(result, vlen + 1, false);
+                       bool    isbinary = (result->attDescs[i].format != 0);
+
+                       tup[i].value = (char *) pqResultAlloc(result, vlen + 1, isbinary);
                        if (tup[i].value == NULL)
                                goto outOfMemory;
                }
@@ -553,6 +566,7 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
        PGresult   *res;
        PQExpBufferData workBuf;
        char            id;
+       const char *val;
 
        /*
         * Make a PGresult to hold the accumulated fields.  We temporarily
@@ -580,68 +594,63 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
                        break;                          /* terminator found */
                if (pqGets(&workBuf, conn))
                        goto fail;
-               switch (id)
-               {
-                       case 'S':
-                               res->errSeverity = pqResultStrdup(res, workBuf.data);
-                               break;
-                       case 'C':
-                               res->errCode = pqResultStrdup(res, workBuf.data);
-                               break;
-                       case 'M':
-                               res->errPrimary = pqResultStrdup(res, workBuf.data);
-                               break;
-                       case 'D':
-                               res->errDetail = pqResultStrdup(res, workBuf.data);
-                               break;
-                       case 'H':
-                               res->errHint = pqResultStrdup(res, workBuf.data);
-                               break;
-                       case 'P':
-                               res->errPosition = pqResultStrdup(res, workBuf.data);
-                               break;
-                       case 'W':
-                               res->errContext = pqResultStrdup(res, workBuf.data);
-                               break;
-                       case 'F':
-                               res->errFilename = pqResultStrdup(res, workBuf.data);
-                               break;
-                       case 'L':
-                               res->errLineno = pqResultStrdup(res, workBuf.data);
-                               break;
-                       case 'R':
-                               res->errFuncname = pqResultStrdup(res, workBuf.data);
-                               break;
-                       default:
-                               /* silently ignore any other field type */
-                               break;
-               }
+               pqSaveMessageField(res, id, workBuf.data);
        }
 
        /*
         * Now build the "overall" error message for PQresultErrorMessage.
-        *
-        * XXX this should be configurable somehow.
         */
        resetPQExpBuffer(&workBuf);
-       if (res->errSeverity)
-               appendPQExpBuffer(&workBuf, "%s:  ", res->errSeverity);
-       if (res->errPrimary)
-               appendPQExpBufferStr(&workBuf, res->errPrimary);
-       /* translator: %s represents a digit string */
-       if (res->errPosition)
-               appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"),
-                                                 res->errPosition);
+       val = PQresultErrorField(res, 'S');     /* Severity */
+       if (val)
+               appendPQExpBuffer(&workBuf, "%s:  ", val);
+       if (conn->verbosity == PQERRORS_VERBOSE)
+       {
+               val = PQresultErrorField(res, 'C');     /* SQLSTATE Code */
+               if (val)
+                       appendPQExpBuffer(&workBuf, "%s: ", val);
+       }
+       val = PQresultErrorField(res, 'M');     /* Primary message */
+       if (val)
+               appendPQExpBufferStr(&workBuf, val);
+       val = PQresultErrorField(res, 'P');     /* Position */
+       if (val)
+       {
+               /* translator: %s represents a digit string */
+               appendPQExpBuffer(&workBuf, libpq_gettext(" at character %s"), val);
+       }
        appendPQExpBufferChar(&workBuf, '\n');
-       if (res->errDetail)
-               appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL:  %s\n"),
-                                                 res->errDetail);
-       if (res->errHint)
-               appendPQExpBuffer(&workBuf, libpq_gettext("HINT:  %s\n"),
-                                                 res->errHint);
-       if (res->errContext)
-               appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT:  %s\n"),
-                                                 res->errContext);
+       if (conn->verbosity != PQERRORS_TERSE)
+       {
+               val = PQresultErrorField(res, 'D');     /* Detail */
+               if (val)
+                       appendPQExpBuffer(&workBuf, libpq_gettext("DETAIL:  %s\n"), val);
+               val = PQresultErrorField(res, 'H');     /* Hint */
+               if (val)
+                       appendPQExpBuffer(&workBuf, libpq_gettext("HINT:  %s\n"), val);
+               val = PQresultErrorField(res, 'W');     /* Where */
+               if (val)
+                       appendPQExpBuffer(&workBuf, libpq_gettext("CONTEXT:  %s\n"), val);
+       }
+       if (conn->verbosity == PQERRORS_VERBOSE)
+       {
+               const char *valf;
+               const char *vall;
+
+               valf = PQresultErrorField(res, 'F');    /* File */
+               vall = PQresultErrorField(res, 'L');    /* Line */
+               val = PQresultErrorField(res, 'R');     /* Routine */
+               if (val || valf || vall)
+               {
+                       appendPQExpBufferStr(&workBuf, libpq_gettext("LOCATION:  "));
+                       if (val)
+                               appendPQExpBuffer(&workBuf, libpq_gettext("%s, "), val);
+                       if (valf && vall)       /* unlikely we'd have just one */
+                               appendPQExpBuffer(&workBuf, libpq_gettext("%s:%s"),
+                                                                 valf, vall);
+                       appendPQExpBufferChar(&workBuf, '\n');
+               }
+       }
 
        /*
         * Either save error as current async result, or just emit the notice.
@@ -656,7 +665,9 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
        }
        else
        {
-               PGDONOTICE(conn, workBuf.data);
+               /* We can cheat a little here and not copy the message. */
+               res->errMsg = workBuf.data;
+               (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
                PQclear(res);
        }
 
@@ -710,35 +721,216 @@ static int
 getNotify(PGconn *conn)
 {
        int                     be_pid;
+       char       *svname;
+       int                     nmlen;
+       int                     extralen;
        PGnotify   *newNotify;
 
        if (pqGetInt(&be_pid, 4, conn))
                return EOF;
        if (pqGets(&conn->workBuffer, conn))
                return EOF;
+       /* must save name while getting extra string */
+       svname = strdup(conn->workBuffer.data);
+       if (!svname)
+               return EOF;
+       if (pqGets(&conn->workBuffer, conn))
+       {
+               free(svname);
+               return EOF;
+       }
 
        /*
-        * Store the relation name right after the PQnotify structure so it
+        * Store the strings right after the PQnotify structure so it
         * can all be freed at once.  We don't use NAMEDATALEN because we
         * don't want to tie this interface to a specific server name length.
         */
-       newNotify = (PGnotify *) malloc(sizeof(PGnotify) +
-                                                                       strlen(conn->workBuffer.data) +1);
+       nmlen = strlen(svname);
+       extralen = strlen(conn->workBuffer.data);
+       newNotify = (PGnotify *) malloc(sizeof(PGnotify) + nmlen + extralen + 2);
        if (newNotify)
        {
                newNotify->relname = (char *) newNotify + sizeof(PGnotify);
-               strcpy(newNotify->relname, conn->workBuffer.data);
+               strcpy(newNotify->relname, svname);
+               newNotify->extra = newNotify->relname + nmlen + 1;
+               strcpy(newNotify->extra, conn->workBuffer.data);
                newNotify->be_pid = be_pid;
                DLAddTail(conn->notifyList, DLNewElem(newNotify));
        }
 
-       /* Swallow extra string (not presently used) */
-       if (pqGets(&conn->workBuffer, conn))
+       free(svname);
+       return 0;
+}
+
+/*
+ * getCopyStart - process CopyInResponse or CopyOutResponse message
+ *
+ * parseInput already read the message type and length.
+ */
+static int
+getCopyStart(PGconn *conn, ExecStatusType copytype)
+{
+       PGresult   *result;
+       int                     nfields;
+       int                     i;
+
+       result = PQmakeEmptyPGresult(conn, copytype);
+
+       if (pqGetc(&conn->copy_is_binary, conn))
+       {
+               PQclear(result);
                return EOF;
+       }
+       result->binary = conn->copy_is_binary;
+       /* the next two bytes are the number of fields  */
+       if (pqGetInt(&(result->numAttributes), 2, conn))
+       {
+               PQclear(result);
+               return EOF;
+       }
+       nfields = result->numAttributes;
+
+       /* allocate space for the attribute descriptors */
+       if (nfields > 0)
+       {
+               result->attDescs = (PGresAttDesc *)
+                       pqResultAlloc(result, nfields * sizeof(PGresAttDesc), TRUE);
+               MemSet((char *) result->attDescs, 0, nfields * sizeof(PGresAttDesc));
+       }
+
+       for (i = 0; i < nfields; i++)
+       {
+               int                     format;
 
+               if (pqGetInt(&format, 2, conn))
+               {
+                       PQclear(result);
+                       return EOF;
+               }
+
+               /*
+                * Since pqGetInt treats 2-byte integers as unsigned, we need to
+                * coerce these results to signed form.
+                */
+               format = (int) ((int16) format);
+
+               result->attDescs[i].format = format;
+       }
+
+       /* Success! */
+       conn->result = result;
        return 0;
 }
 
+/*
+ * getReadyForQuery - process ReadyForQuery message
+ */
+static int
+getReadyForQuery(PGconn *conn)
+{
+       char            xact_status;
+
+       if (pqGetc(&xact_status, conn))
+               return EOF;
+       switch (xact_status)
+       {
+               case 'I':
+                       conn->xactStatus = PQTRANS_IDLE;
+                       break;
+               case 'T':
+                       conn->xactStatus = PQTRANS_INTRANS;
+                       break;
+               case 'E':
+                       conn->xactStatus = PQTRANS_INERROR;
+                       break;
+               default:
+                       conn->xactStatus = PQTRANS_UNKNOWN;
+                       break;
+       }
+
+       return 0;
+}
+
+/*
+ * PQgetCopyData - read a row of data from the backend during COPY OUT
+ *
+ * If successful, sets *buffer to point to a malloc'd row of data, and
+ * returns row length (always > 0) as result.
+ * Returns 0 if no row available yet (only possible if async is true),
+ * -1 if end of copy (consult PQgetResult), or -2 if error (consult
+ * PQerrorMessage).
+ */
+int
+pqGetCopyData3(PGconn *conn, char **buffer, int async)
+{
+       char            id;
+       int                     msgLength;
+       int                     avail;
+
+       for (;;)
+       {
+               /*
+                * Do we have the next input message?  To make life simpler for async
+                * callers, we keep returning 0 until the next message is fully
+                * available, even if it is not Copy Data.
+                */
+               conn->inCursor = conn->inStart;
+               if (pqGetc(&id, conn))
+                       goto nodata;
+               if (pqGetInt(&msgLength, 4, conn))
+                       goto nodata;
+               avail = conn->inEnd - conn->inCursor;
+               if (avail < msgLength - 4)
+                       goto nodata;
+
+               /*
+                * If it's anything except Copy Data, exit COPY_OUT mode and let
+                * caller read status with PQgetResult().  The normal case is that
+                * it's Copy Done, but we let parseInput read that.
+                */
+               if (id != 'd')
+               {
+                       conn->asyncStatus = PGASYNC_BUSY;
+                       return -1;
+               }
+
+               /*
+                * Drop zero-length messages (shouldn't happen anyway).  Otherwise
+                * pass the data back to the caller.
+                */
+               msgLength -= 4;
+               if (msgLength > 0)
+               {
+                       *buffer = (char *) malloc(msgLength + 1);
+                       if (*buffer == NULL)
+                       {
+                               printfPQExpBuffer(&conn->errorMessage,
+                                                                 libpq_gettext("out of memory\n"));
+                               return -2;
+                       }
+                       memcpy(*buffer, &conn->inBuffer[conn->inCursor], msgLength);
+                       (*buffer)[msgLength] = '\0'; /* Add terminating null */
+
+                       /* Mark message consumed */
+                       conn->inStart = conn->inCursor + msgLength;
+
+                       return msgLength;
+               }
+
+               /* Empty, so drop it and loop around for another */
+               conn->inStart = conn->inCursor;
+               continue;
+
+       nodata:
+               /* Don't block if async read requested */
+               if (async)
+                       return 0;
+               /* Need to load more data */
+               if (pqWait(TRUE, FALSE, conn) ||
+                       pqReadData(conn) < 0)
+                       return -2;
+       }
+}
 
 /*
  * PQgetline - gets a newline-terminated string from the backend.
@@ -1108,7 +1300,7 @@ pqFunctionCall3(PGconn *conn, Oid fnid,
                                        continue;
                                break;
                        case 'Z':                       /* backend is ready for new query */
-                               if (pqGetc(&conn->xact_status, conn))
+                               if (getReadyForQuery(conn))
                                        continue;
                                /* consume the message and exit */
                                conn->inStart += 5 + msgLength;
index da61b770906627f486e30288d0c12206d62cb33f..d8ff52d6b0e6a705302965de4e472034d5f808cd 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: libpq-fe.h,v 1.93 2003/06/08 17:43:00 tgl Exp $
+ * $Id: libpq-fe.h,v 1.94 2003/06/21 21:51:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -88,6 +88,22 @@ typedef enum
        PGRES_FATAL_ERROR                       /* query failed */
 } ExecStatusType;
 
+typedef enum
+{
+       PQTRANS_IDLE,                           /* connection idle */
+       PQTRANS_ACTIVE,                         /* command in progress */
+       PQTRANS_INTRANS,                        /* idle, within transaction block */
+       PQTRANS_INERROR,                        /* idle, within failed transaction */
+       PQTRANS_UNKNOWN                         /* cannot determine status */
+} PGTransactionStatusType;
+
+typedef enum
+{
+       PQERRORS_TERSE,                         /* single-line error messages */
+       PQERRORS_DEFAULT,                       /* recommended style */
+       PQERRORS_VERBOSE                        /* all the facts, ma'am */
+} PGVerbosity;
+
 /* PGconn encapsulates a connection to the backend.
  * The contents of this struct are not supposed to be known to applications.
  */
@@ -108,12 +124,13 @@ typedef struct pg_result PGresult;
  */
 typedef struct pgNotify
 {
-       char       *relname;            /* name of relation containing data */
-       int                     be_pid;                 /* process id of backend */
+       char       *relname;            /* notification condition name */
+       int                     be_pid;                 /* process ID of server process */
+       char       *extra;                      /* notification parameter */
 } PGnotify;
 
-/* PQnoticeProcessor is the function type for the notice-message callback.
- */
+/* Function types for notice-handling callbacks */
+typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res);
 typedef void (*PQnoticeProcessor) (void *arg, const char *message);
 
 /* Print options for PQprint() */
@@ -227,6 +244,10 @@ extern char *PQport(const PGconn *conn);
 extern char *PQtty(const PGconn *conn);
 extern char *PQoptions(const PGconn *conn);
 extern ConnStatusType PQstatus(const PGconn *conn);
+extern PGTransactionStatusType PQtransactionStatus(const PGconn *conn);
+extern const char *PQparameterStatus(const PGconn *conn,
+                                                                        const char *paramName);
+extern int     PQprotocolVersion(const PGconn *conn);
 extern char *PQerrorMessage(const PGconn *conn);
 extern int     PQsocket(const PGconn *conn);
 extern int     PQbackendPID(const PGconn *conn);
@@ -238,42 +259,58 @@ extern int        PQsetClientEncoding(PGconn *conn, const char *encoding);
 extern SSL *PQgetssl(PGconn *conn);
 #endif
 
+/* Set verbosity for PQerrorMessage and PQresultErrorMessage */
+extern PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity);
 
 /* Enable/disable tracing */
 extern void PQtrace(PGconn *conn, FILE *debug_port);
 extern void PQuntrace(PGconn *conn);
 
-/* Override default notice processor */
+/* Override default notice handling routines */
+extern PQnoticeReceiver PQsetNoticeReceiver(PGconn *conn,
+                                                                                       PQnoticeReceiver proc,
+                                                                                       void *arg);
 extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn,
                                         PQnoticeProcessor proc,
                                         void *arg);
 
 /* === in fe-exec.c === */
 
-/* Quoting strings before inclusion in queries. */
-extern size_t PQescapeString(char *to, const char *from, size_t length);
-extern unsigned char *PQescapeBytea(const unsigned char *bintext, size_t binlen,
-                         size_t *bytealen);
-extern unsigned char *PQunescapeBytea(const unsigned char *strtext,
-                               size_t *retbuflen);
-extern void PQfreemem(void *ptr);
-
-
 /* Simple synchronous query */
 extern PGresult *PQexec(PGconn *conn, const char *query);
-extern PGnotify *PQnotifies(PGconn *conn);
-/* Exists for backward compatibility.  bjm 2003-03-24 */
-#define PQfreeNotify(ptr) PQfreemem(ptr)
+extern PGresult *PQexecParams(PGconn *conn,
+                                                         const char *command,
+                                                         int nParams,
+                                                         const Oid *paramTypes,
+                                                         const char * const *paramValues,
+                                                         const int *paramLengths,
+                                                         const int *paramFormats,
+                                                         int resultFormat);
 
 /* Interface for multiple-result or asynchronous queries */
 extern int     PQsendQuery(PGconn *conn, const char *query);
+extern int     PQsendQueryParams(PGconn *conn,
+                                                         const char *command,
+                                                         int nParams,
+                                                         const Oid *paramTypes,
+                                                         const char * const *paramValues,
+                                                         const int *paramLengths,
+                                                         const int *paramFormats,
+                                                         int resultFormat);
 extern PGresult *PQgetResult(PGconn *conn);
 
 /* Routines for managing an asynchronous query */
 extern int     PQisBusy(PGconn *conn);
 extern int     PQconsumeInput(PGconn *conn);
 
+/* LISTEN/NOTIFY support */
+extern PGnotify *PQnotifies(PGconn *conn);
+
 /* Routines for copy in/out */
+extern int     PQputCopyData(PGconn *conn, const char *buffer, int nbytes);
+extern int     PQputCopyEnd(PGconn *conn, const char *errormsg);
+extern int     PQgetCopyData(PGconn *conn, char **buffer, int async);
+/* Deprecated routines for copy in/out */
 extern int     PQgetline(PGconn *conn, char *string, int length);
 extern int     PQputline(PGconn *conn, const char *string);
 extern int     PQgetlineAsync(PGconn *conn, char *buffer, int bufsize);
@@ -303,11 +340,15 @@ extern PGresult *PQfn(PGconn *conn,
 extern ExecStatusType PQresultStatus(const PGresult *res);
 extern char *PQresStatus(ExecStatusType status);
 extern char *PQresultErrorMessage(const PGresult *res);
+extern char *PQresultErrorField(const PGresult *res, int fieldcode);
 extern int     PQntuples(const PGresult *res);
 extern int     PQnfields(const PGresult *res);
 extern int     PQbinaryTuples(const PGresult *res);
 extern char *PQfname(const PGresult *res, int field_num);
 extern int     PQfnumber(const PGresult *res, const char *field_name);
+extern Oid     PQftable(const PGresult *res, int field_num);
+extern int     PQftablecol(const PGresult *res, int field_num);
+extern int     PQfformat(const PGresult *res, int field_num);
 extern Oid     PQftype(const PGresult *res, int field_num);
 extern int     PQfsize(const PGresult *res, int field_num);
 extern int     PQfmod(const PGresult *res, int field_num);
@@ -322,6 +363,12 @@ extern int PQgetisnull(const PGresult *res, int tup_num, int field_num);
 /* Delete a PGresult */
 extern void PQclear(PGresult *res);
 
+/* For freeing other alloc'd results, such as PGnotify structs */
+extern void PQfreemem(void *ptr);
+
+/* Exists for backward compatibility.  bjm 2003-03-24 */
+#define PQfreeNotify(ptr) PQfreemem(ptr)
+
 /*
  * Make an empty PGresult with given status (some apps find this
  * useful). If conn is not NULL and status indicates an error, the
@@ -329,26 +376,33 @@ extern void PQclear(PGresult *res);
  */
 extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
 
+
+/* Quoting strings before inclusion in queries. */
+extern size_t PQescapeString(char *to, const char *from, size_t length);
+extern unsigned char *PQescapeBytea(const unsigned char *bintext, size_t binlen,
+                         size_t *bytealen);
+extern unsigned char *PQunescapeBytea(const unsigned char *strtext,
+                               size_t *retbuflen);
+
+
+
 /* === in fe-print.c === */
 
-extern void
-PQprint(FILE *fout,                            /* output stream */
-               const PGresult *res,
-               const PQprintOpt *ps);  /* option structure */
+extern void PQprint(FILE *fout,                                /* output stream */
+                                       const PGresult *res,
+                                       const PQprintOpt *ps);  /* option structure */
 
 /*
  * really old printing routines
  */
-extern void
-PQdisplayTuples(const PGresult *res,
+extern void PQdisplayTuples(const PGresult *res,
                                FILE *fp,               /* where to send the output */
                                int fillAlign,  /* pad the fields with spaces */
                                const char *fieldSep,   /* field separator */
                                int printHeader,        /* display headers? */
                                int quiet);
 
-extern void
-PQprintTuples(const PGresult *res,
+extern void PQprintTuples(const PGresult *res,
                          FILE *fout,           /* output stream */
                          int printAttName, /* print attribute names */
                          int terseOutput,      /* delimiter bars */
index 019683ac2650cbe69d1ea3ce84d85e8ec105a173..f651320e1cbbe97c9a32d6d55c0dcb5d8b810577 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.74 2003/06/14 17:49:54 momjian Exp $
+ * $Id: libpq-int.h,v 1.75 2003/06/21 21:51:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,6 +20,9 @@
 #ifndef LIBPQ_INT_H
 #define LIBPQ_INT_H
 
+/* We assume libpq-fe.h has already been included. */
+#include "postgres_fe.h"
+
 #include <time.h>
 #include <sys/types.h>
 #ifndef WIN32
 
 
 #if defined(WIN32) && (!defined(ssize_t))
-typedef int ssize_t;                   /* ssize_t doesn't exist in VC (atleast
+typedef int ssize_t;                   /* ssize_t doesn't exist in VC (at least
                                                                 * not VC6) */
 #endif
 
-/* We assume libpq-fe.h has already been included. */
-#include "postgres_fe.h"
-
 /* include stuff common to fe and be */
 #include "getaddrinfo.h"
 #include "libpq/pqcomm.h"
@@ -78,7 +78,10 @@ union pgresult_data
 
 typedef struct pgresAttDesc
 {
-       char       *name;                       /* type name */
+       char       *name;                       /* column name */
+       Oid                     tableid;                /* source table, if known */
+       int                     columnid;               /* source column, if known */
+       int                     format;                 /* format code for value (text/binary) */
        Oid                     typid;                  /* type id */
        int                     typlen;                 /* type size */
        int                     atttypmod;              /* type-specific modifier info */
@@ -91,7 +94,7 @@ typedef struct pgresAttDesc
  *
  * The value pointer always points to a null-terminated area; we add a
  * null (zero) byte after whatever the backend sends us.  This is only
- * particularly useful for text tuples ... with a binary value, the
+ * particularly useful for text values ... with a binary value, the
  * value might have embedded nulls, so the application can't use C string
  * operators on it.  But we add a null anyway for consistency.
  * Note that the value itself does not contain a length word.
@@ -111,6 +114,23 @@ typedef struct pgresAttValue
                                                                 * byte */
 }      PGresAttValue;
 
+/* Typedef for message-field list entries */
+typedef struct pgMessageField
+{
+       struct pgMessageField *next;    /* list link */
+       char            code;                   /* field code */
+       char            contents[1];    /* field value (VARIABLE LENGTH) */
+} PGMessageField;
+
+/* Fields needed for notice handling */
+typedef struct
+{
+       PQnoticeReceiver noticeRec;             /* notice message receiver */
+       void       *noticeRecArg;
+       PQnoticeProcessor noticeProc;   /* notice message processor */
+       void       *noticeProcArg;
+} PGNoticeHooks;
+
 struct pg_result
 {
        int                     ntups;
@@ -118,10 +138,10 @@ struct pg_result
        PGresAttDesc *attDescs;
        PGresAttValue **tuples;         /* each PGresTuple is an array of
                                                                 * PGresAttValue's */
-       int                     tupArrSize;             /* size of tuples array allocated */
+       int                     tupArrSize;             /* allocated size of tuples array */
        ExecStatusType resultStatus;
        char            cmdStatus[CMDSTATUS_LEN];               /* cmd status from the
-                                                                                                * last query */
+                                                                                                * query */
        int                     binary;                 /* binary tuple values if binary == 1,
                                                                 * otherwise text */
 
@@ -129,35 +149,23 @@ struct pg_result
         * These fields are copied from the originating PGconn, so that
         * operations on the PGresult don't have to reference the PGconn.
         */
-       PQnoticeProcessor noticeHook;           /* notice/error message processor */
-       void       *noticeArg;
+       PGNoticeHooks noticeHooks;
        int                     client_encoding;        /* encoding id */
 
        /*
         * Error information (all NULL if not an error result).  errMsg is the
         * "overall" error message returned by PQresultErrorMessage.  If we
-        * got a field-ized error from the server then the additional fields
-        * may be set.
+        * have per-field info then it is stored in a linked list.
         */
        char       *errMsg;                     /* error message, or NULL if no error */
-
-       char       *errSeverity;        /* severity code */
-       char       *errCode;            /* SQLSTATE code */
-       char       *errPrimary;         /* primary message text */
-       char       *errDetail;          /* detail text */
-       char       *errHint;            /* hint text */
-       char       *errPosition;        /* cursor position */
-       char       *errContext;         /* location information */
-       char       *errFilename;        /* source-code file name */
-       char       *errLineno;          /* source-code line number */
-       char       *errFuncname;        /* source-code function name */
+       PGMessageField *errFields;      /* message broken into fields */
 
        /* All NULL attributes in the query result point to this null string */
        char            null_field[1];
 
        /*
-        * Space management information.  Note that attDescs and errMsg, if
-        * not null, point into allocated blocks.  But tuples points to a
+        * Space management information.  Note that attDescs and error stuff,
+        * if not null, point into allocated blocks.  But tuples points to a
         * separately malloc'd block, so that we can realloc it.
         */
        PGresult_data *curBlock;        /* most recently allocated block */
@@ -245,18 +253,18 @@ struct pg_conn
        /* Optional file to write trace info to */
        FILE       *Pfdebug;
 
-       /* Callback procedure for notice/error message processing */
-       PQnoticeProcessor noticeHook;
-       void       *noticeArg;
+       /* Callback procedures for notice message processing */
+       PGNoticeHooks noticeHooks;
 
        /* Status indicators */
        ConnStatusType status;
        PGAsyncStatusType asyncStatus;
-       char            xact_status;    /* status flag from latest ReadyForQuery */
-       char            copy_is_binary; /* 1 = copy binary, 0 = copy text */
-       int                     copy_already_done; /* # bytes already returned in COPY OUT */
+       PGTransactionStatusType xactStatus;
+       /* note: xactStatus never changes to ACTIVE */
        int                     nonblocking;    /* whether this connection is using a
                                                                 * blocking socket to the backend or not */
+       char            copy_is_binary; /* 1 = copy binary, 0 = copy text */
+       int                     copy_already_done; /* # bytes already returned in COPY OUT */
        Dllist     *notifyList;         /* Notify msgs not yet handed to
                                                                 * application */
 
@@ -281,6 +289,7 @@ struct pg_conn
        char            cryptSalt[2];   /* password salt received from backend */
        pgParameterStatus *pstatus;     /* ParameterStatus data */
        int                     client_encoding; /* encoding id */
+       PGVerbosity     verbosity;              /* error/notice message verbosity */
        PGlobjfuncs *lobjfuncs;         /* private state for large-object access
                                                                 * fns */
 
@@ -351,10 +360,12 @@ extern char *pqResultStrdup(PGresult *res, const char *str);
 extern void pqClearAsyncResult(PGconn *conn);
 extern void pqSaveErrorResult(PGconn *conn);
 extern PGresult *pqPrepareAsyncResult(PGconn *conn);
+extern void pqInternalNotice(const PGNoticeHooks *hooks, const char *msgtext);
 extern int     pqAddTuple(PGresult *res, PGresAttValue *tup);
+extern void pqSaveMessageField(PGresult *res, char code,
+                                                          const char *value);
 extern void pqSaveParameterStatus(PGconn *conn, const char *name,
                                                                  const char *value);
-extern const char *pqGetParameterStatus(PGconn *conn, const char *name);
 extern void pqHandleSendFailure(PGconn *conn);
 
 /* === in fe-protocol2.c === */
@@ -364,6 +375,7 @@ extern PostgresPollingStatusType pqSetenvPoll(PGconn *conn);
 extern char *pqBuildStartupPacket2(PGconn *conn, int *packetlen,
                                                                   const PQEnvironmentOption *options);
 extern void pqParseInput2(PGconn *conn);
+extern int     pqGetCopyData2(PGconn *conn, char **buffer, int async);
 extern int     pqGetline2(PGconn *conn, char *s, int maxlen);
 extern int     pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize);
 extern int     pqEndcopy2(PGconn *conn);
@@ -378,6 +390,7 @@ extern char *pqBuildStartupPacket3(PGconn *conn, int *packetlen,
                                                                   const PQEnvironmentOption *options);
 extern void pqParseInput3(PGconn *conn);
 extern int     pqGetErrorNotice3(PGconn *conn, bool isError);
+extern int     pqGetCopyData3(PGconn *conn, char **buffer, int async);
 extern int     pqGetline3(PGconn *conn, char *s, int maxlen);
 extern int     pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize);
 extern int     pqEndcopy3(PGconn *conn);
@@ -393,6 +406,7 @@ extern PGresult *pqFunctionCall3(PGconn *conn, Oid fnid,
   * for Get, EOF merely means the buffer is exhausted, not that there is
   * necessarily any error.
   */
+extern int     pqCheckOutBufferSpace(int bytes_needed, PGconn *conn);
 extern int     pqCheckInBufferSpace(int bytes_needed, PGconn *conn);
 extern int     pqGetc(char *result, PGconn *conn);
 extern int     pqPutc(char c, PGconn *conn);
@@ -423,10 +437,10 @@ extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
 
 /* Note: PGDONOTICE macro will work if applied to either PGconn or PGresult */
 #define PGDONOTICE(conn,message) \
-       ((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
+       pqInternalNotice(&(conn)->noticeHooks, (message))
 
 /*
- * this is so that we can check is a connection is non-blocking internally
+ * this is so that we can check if a connection is non-blocking internally
  * without the overhead of a function call
  */
 #define pqIsnonblocking(conn)  ((conn)->nonblocking)
index f8432fc713acee9080dbb0760bf75367ea1ccc72..8ff902f5218da1a98aae6524ad03af4fb1c8c21e 100644 (file)
@@ -97,4 +97,17 @@ EXPORTS
     pg_utf_mblen            @ 93
     PQunescapeBytea         @ 94
     PQfreemem               @ 95
-
+    PQtransactionStatus     @ 96
+    PQparameterStatus       @ 97
+    PQprotocolVersion       @ 98
+    PQsetErrorVerbosity     @ 99
+    PQsetNoticeReceiver     @ 100
+    PQexecParams            @ 101
+    PQsendQueryParams       @ 102
+    PQputCopyData           @ 103
+    PQputCopyEnd            @ 104
+    PQgetCopyData           @ 105
+    PQresultErrorField      @ 106
+    PQftable                @ 107
+    PQftablecol             @ 108
+    PQfformat               @ 109