Here's the patch actually applied.  I cleaned up the state problem,
fixed a couple minor omissions in the code, and did some work on the
documentation; also updated exports.txt which is now the source for
the *.def files.

                        regards, tom lane

*** doc/src/sgml/libpq.sgml.orig        Fri Oct  1 13:34:17 2004
--- doc/src/sgml/libpq.sgml     Mon Oct 18 17:50:04 2004
***************
*** 1055,1062 ****
            out-of-memory conditions or serious errors such as inability
            to send the command to the server.
            If a null pointer is returned, it
!           should be treated like a <symbol>PGRES_FATAL_ERROR</symbol> result.  Use
!           <function>PQerrorMessage</function> to get more information about the 
error.
  </para>
  </listitem>
  </varlistentry>
--- 1055,1063 ----
            out-of-memory conditions or serious errors such as inability
            to send the command to the server.
            If a null pointer is returned, it
!           should be treated like a <symbol>PGRES_FATAL_ERROR</symbol> result.
!           Use <function>PQerrorMessage</function> to get more information
!           about such errors.
  </para>
  </listitem>
  </varlistentry>
***************
*** 1147,1152 ****
--- 1148,1228 ----
  <para>
  <variablelist>
  <varlistentry>
+ <term><function>PQprepare</function><indexterm><primary>PQprepare</></></term>
+ <listitem>
+ <para>
+           Submits a request to create a prepared statement with the
+           given parameters, and waits for completion.
+ <synopsis>
+ PGresult *PQprepare(PGconn *conn,
+                     const char *stmtName,
+                     const char *query,
+                     int nParams,
+                     const Oid *paramTypes);
+ </synopsis>
+ </para>
+ 
+ <para>
+ <function>PQprepare</> creates a prepared statement for later execution with
+ <function>PQexecPrepared</>.
+ This feature allows commands
+ that will be used repeatedly to be parsed and planned just once, rather
+ than each time they are executed.
+ <function>PQprepare</> is supported only in protocol 3.0 and later
+ connections; it will fail when using protocol 2.0.
+ </para>
+ 
+ <para>
+ The function creates a prepared statement named <parameter>stmtName</>
+ from the <parameter>query</> string, which must contain a single SQL command.
+ <parameter>stmtName</> may be <literal>""</> to create an unnamed statement,
+ in which case any pre-existing unnamed statement is automatically replaced;
+ otherwise it is an error if the statement name is already defined in the
+ current session.
+ If any parameters are used, they are referred
+ to in the query as <literal>$1</>, <literal>$2</>, etc.
+ <parameter>nParams</> is the number of parameters for which types are
+ pre-specified in the array <parameter>paramTypes[]</>.  (The array pointer
+ may be <symbol>NULL</symbol> when <parameter>nParams</> is zero.)
+ <parameter>paramTypes[]</> specifies, by OID, the data types to be assigned to
+ the parameter symbols.  If <parameter>paramTypes</> is <symbol>NULL</symbol>,
+ or any particular element in the array is zero, the server assigns a data type
+ to the parameter symbol in the same way it would do for an untyped literal
+ string.  Also, the query may use parameter symbols with numbers higher than
+ <parameter>nParams</>; data types will be inferred for these symbols as
+ well.
+ </para>
+ 
+ <para>
+ As with <function>PQexec</>, the result is normally a
+ <structname>PGresult</structname> object whose contents indicate server-side
+ success or failure.  A null result indicates out-of-memory or inability to
+ send the command at all.
+ Use <function>PQerrorMessage</function> to get more information
+ about such errors.
+ </para>
+ 
+ <para>
+ At present, there is no way to determine the actual datatype inferred for
+ any parameters whose types are not specified in <parameter>paramTypes[]</>.
+ This is a <application>libpq</> omission that will probably be rectified
+ in a future release.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ 
+ Prepared statements for use with <function>PQexecPrepared</> can also be
+ created by executing SQL <command>PREPARE</> statements.  (But
+ <function>PQprepare</> is more flexible since it does not require
+ parameter types to be pre-specified.)  Also, although there is no
+ <application>libpq</> function for deleting a prepared statement,
+ the SQL <command>DEALLOCATE</> statement can be used for that purpose.
+ </para>
+ 
+ <para>
+ <variablelist>
+ <varlistentry>
  
<term><function>PQexecPrepared</function><indexterm><primary>PQexecPrepared</></></term>
  <listitem>
  <para>
***************
*** 1166,1172 ****
  <para>
  <function>PQexecPrepared</> is like <function>PQexecParams</>, but the
  command to be executed is specified by naming a previously-prepared
! statement, instead of giving a query string.  This feature allows commands
  that will be used repeatedly to be parsed and planned just once, rather
  than each time they are executed.
  <function>PQexecPrepared</> is supported only in protocol 3.0 and later
--- 1242,1249 ----
  <para>
  <function>PQexecPrepared</> is like <function>PQexecParams</>, but the
  command to be executed is specified by naming a previously-prepared
! statement, instead of giving a query string.
! This feature allows commands
  that will be used repeatedly to be parsed and planned just once, rather
  than each time they are executed.
  <function>PQexecPrepared</> is supported only in protocol 3.0 and later
***************
*** 1182,1194 ****
  </listitem>
  </varlistentry>
  </variablelist>
- 
- Presently, prepared statements for use with <function>PQexecPrepared</>
- must be set up by executing an SQL <command>PREPARE</> command,
- which is typically sent with <function>PQexec</> (though any of
- <application>libpq</>'s query-submission functions may be used).
- A lower-level interface for preparing statements may be offered in a
- future release.
  </para>
  
  <para>
--- 1259,1264 ----
***************
*** 2270,2279 ****
  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>.
! There are also <function>PQsendQueryParams</function> and
! <function>PQsendQueryPrepared</function>, which can be used with
! <function>PQgetResult</function> to duplicate the functionality of
! <function>PQexecParams</function> and <function>PQexecPrepared</function>
  respectively.
  
  <variablelist>
--- 2340,2354 ----
  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>.
! There are also
! <function>PQsendQueryParams</function>,
! <function>PQsendPrepare</function>, and
! <function>PQsendQueryPrepared</function>,
! which can be used with <function>PQgetResult</function> to duplicate the
! functionality of
! <function>PQexecParams</function>,
! <function>PQprepare</function>, and
! <function>PQexecPrepared</function>
  respectively.
  
  <variablelist>
***************
*** 2326,2331 ****
--- 2401,2433 ----
  </varlistentry>
  
  <varlistentry>
+ <term><function>PQsendPrepare</><indexterm><primary>PQsendPrepare</></></term>
+ <listitem>
+ <para>
+         Sends a request to create a prepared statement with the given
+         parameters, without waiting for completion.
+ <synopsis>
+ int PQsendPrepare(PGconn *conn,
+                   const char *stmtName,
+                   const char *query,
+                   int nParams,
+                   const Oid *paramTypes);
+ </synopsis>
+ 
+         This is an asynchronous version of <function>PQprepare</>: it
+         returns 1 if it was able to dispatch the request, and 0 if not.
+         After a successful call, call <function>PQgetResult</function>
+         to determine whether the server successfully created the prepared
+         statement.
+         The function's parameters are handled identically to
+         <function>PQprepare</function>.  Like
+         <function>PQprepare</function>, it will not work on 2.0-protocol
+         connections.
+ </para>
+ </listitem>
+ </varlistentry>
+ 
+ <varlistentry>
  
<term><function>PQsendQueryPrepared</function><indexterm><primary>PQsendQueryPrepared</></></term>
  <listitem>
  <para>
***************
*** 2358,2364 ****
  <para>
            Waits for the next result from a prior
            <function>PQsendQuery</function>,
!           <function>PQsendQueryParams</function>, or
            <function>PQsendQueryPrepared</function> call,
            and returns it.  A null pointer is returned when the command is complete
            and there will be no more results.
--- 2460,2467 ----
  <para>
            Waits for the next result from a prior
            <function>PQsendQuery</function>,
!           <function>PQsendQueryParams</function>,
!           <function>PQsendPrepare</function>, or
            <function>PQsendQueryPrepared</function> call,
            and returns it.  A null pointer is returned when the command is complete
            and there will be no more results.
*** src/interfaces/libpq/exports.txt.orig       Sat Oct 16 16:03:45 2004
--- src/interfaces/libpq/exports.txt    Mon Oct 18 17:36:43 2004
***************
*** 1,3 ****
--- 1,4 ----
+ # $PostgreSQL$
  # Functions to be exported by libpq DLLs
  PQconnectdb               1
  PQsetdbLogin              2
***************
*** 116,118 ****
--- 117,121 ----
  pg_char_to_encoding       115
  pg_valid_server_encoding  116
  pqsignal                  117
+ PQprepare                 118
+ PQsendPrepare             119
*** src/interfaces/libpq/fe-exec.c.orig Sat Oct 16 18:52:53 2004
--- src/interfaces/libpq/fe-exec.c      Mon Oct 18 17:35:24 2004
***************
*** 664,670 ****
        }
  
        /* remember we are using simple query protocol */
!       conn->ext_query = false;
  
        /*
         * Give the data a push.  In nonblock mode, don't complain if we're
--- 664,670 ----
        }
  
        /* remember we are using simple query protocol */
!       conn->queryclass = PGQUERY_SIMPLE;
  
        /*
         * Give the data a push.  In nonblock mode, don't complain if we're
***************
*** 718,723 ****
--- 718,811 ----
  }
  
  /*
+  * PQsendPrepare
+  *   Submit a Parse message, but don't wait for it to finish
+  *
+  * Returns: 1 if successfully submitted
+  *          0 if error (conn->errorMessage is set)
+  */
+ int
+ PQsendPrepare(PGconn *conn,
+                         const char *stmtName, const char *query,
+                         int nParams, const Oid *paramTypes)
+ {
+       if (!PQsendQueryStart(conn))
+               return 0;
+ 
+       if (!stmtName)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                       libpq_gettext("statement name is a null 
pointer\n"));
+               return 0;
+       }
+ 
+       if (!query)
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                       libpq_gettext("command string is a null 
pointer\n"));
+               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 protocol version 3.0\n"));
+               return 0;
+       }
+ 
+       /* construct the Parse message */
+       if (pqPutMsgStart('P', false, conn) < 0 ||
+               pqPuts(stmtName, conn) < 0 ||
+               pqPuts(query, conn) < 0)
+               goto sendFailed;
+ 
+       if (nParams > 0 && paramTypes)
+       {
+               int i;
+ 
+               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 Sync message */
+       if (pqPutMsgStart('S', false, conn) < 0 ||
+               pqPutMsgEnd(conn) < 0)
+               goto sendFailed;
+ 
+       /* remember we are doing just a Parse */
+       conn->queryclass = PGQUERY_PREPARE;
+ 
+       /*
+        * 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;
+ }
+ 
+ /*
   * PQsendQueryPrepared
   *            Like PQsendQuery, but execute a previously prepared statement,
   *            using protocol 3.0 so we can pass parameters
***************
*** 921,927 ****
                goto sendFailed;
  
        /* remember we are using extended query protocol */
!       conn->ext_query = true;
  
        /*
         * Give the data a push.  In nonblock mode, don't complain if we're
--- 1009,1015 ----
                goto sendFailed;
  
        /* remember we are using extended query protocol */
!       conn->queryclass = PGQUERY_EXTENDED;
  
        /*
         * Give the data a push.  In nonblock mode, don't complain if we're
***************
*** 1134,1140 ****
   * The user is responsible for freeing the PGresult via PQclear()
   * when done with it.
   */
- 
  PGresult *
  PQexec(PGconn *conn, const char *query)
  {
--- 1222,1227 ----
***************
*** 1169,1174 ****
--- 1256,1284 ----
  }
  
  /*
+  * PQprepare
+  *    Creates a prepared statement by issuing a v3.0 parse message.
+  *
+  * If the query was not even sent, return NULL; conn->errorMessage is set to
+  * a relevant message.
+  * If the query was sent, a new PGresult is returned (which could indicate
+  * either success or failure).
+  * The user is responsible for freeing the PGresult via PQclear()
+  * when done with it.
+  */
+ PGresult *
+ PQprepare(PGconn *conn,
+                 const char *stmtName, const char *query,
+                 int nParams, const Oid *paramTypes)
+ {
+       if (!PQexecStart(conn))
+               return NULL;
+       if (!PQsendPrepare(conn, stmtName, query, nParams, paramTypes))
+               return NULL;
+       return PQexecFinish(conn);
+ }
+ 
+ /*
   * PQexecPrepared
   *            Like PQexec, but execute a previously prepared statement,
   *            using protocol 3.0 so we can pass parameters
***************
*** 1451,1457 ****
                 * If we sent the COPY command in extended-query mode, we must
                 * issue a Sync as well.
                 */
!               if (conn->ext_query)
                {
                        if (pqPutMsgStart('S', false, conn) < 0 ||
                                pqPutMsgEnd(conn) < 0)
--- 1561,1567 ----
                 * If we sent the COPY command in extended-query mode, we must
                 * issue a Sync as well.
                 */
!               if (conn->queryclass != PGQUERY_SIMPLE)
                {
                        if (pqPutMsgStart('S', false, conn) < 0 ||
                                pqPutMsgEnd(conn) < 0)
*** src/interfaces/libpq/fe-protocol3.c.orig    Sat Oct 16 18:52:54 2004
--- src/interfaces/libpq/fe-protocol3.c Mon Oct 18 17:35:24 2004
***************
*** 220,225 ****
--- 220,234 ----
                                        conn->asyncStatus = PGASYNC_READY;
                                        break;
                                case '1':               /* Parse Complete */
+                                       /* If we're doing PQprepare, we're done; else 
ignore */
+                                       if (conn->queryclass == PGQUERY_PREPARE)
+                                       {
+                                               if (conn->result == NULL)
+                                                       conn->result = 
PQmakeEmptyPGresult(conn,
+                                                                                      
                                    PGRES_COMMAND_OK);
+                                               conn->asyncStatus = PGASYNC_READY;
+                                       }
+                                       break;
                                case '2':               /* Bind Complete */
                                case '3':               /* Close Complete */
                                        /* Nothing to do for these message types */
***************
*** 1118,1124 ****
                 * If we sent the COPY command in extended-query mode, we must
                 * issue a Sync as well.
                 */
!               if (conn->ext_query)
                {
                        if (pqPutMsgStart('S', false, conn) < 0 ||
                                pqPutMsgEnd(conn) < 0)
--- 1127,1133 ----
                 * If we sent the COPY command in extended-query mode, we must
                 * issue a Sync as well.
                 */
!               if (conn->queryclass != PGQUERY_SIMPLE)
                {
                        if (pqPutMsgStart('S', false, conn) < 0 ||
                                pqPutMsgEnd(conn) < 0)
*** src/interfaces/libpq/libpq-fe.h.orig        Sat Oct 16 18:52:55 2004
--- src/interfaces/libpq/libpq-fe.h     Mon Oct 18 17:35:24 2004
***************
*** 304,309 ****
--- 304,312 ----
                         const int *paramLengths,
                         const int *paramFormats,
                         int resultFormat);
+ extern PGresult *PQprepare(PGconn *conn, const char *stmtName,
+                                                  const char *query, int nParams,
+                                                  const Oid *paramTypes);
  extern PGresult *PQexecPrepared(PGconn *conn,
                           const char *stmtName,
                           int nParams,
***************
*** 322,327 ****
--- 325,333 ----
                                  const int *paramLengths,
                                  const int *paramFormats,
                                  int resultFormat);
+ extern int PQsendPrepare(PGconn *conn, const char *stmtName,
+                                                const char *query, int nParams,
+                                                const Oid *paramTypes);
  extern int PQsendQueryPrepared(PGconn *conn,
                                        const char *stmtName,
                                        int nParams,
*** src/interfaces/libpq/libpq-int.h.orig       Sat Oct 16 18:52:55 2004
--- src/interfaces/libpq/libpq-int.h    Mon Oct 18 17:35:25 2004
***************
*** 185,190 ****
--- 185,198 ----
        PGASYNC_COPY_OUT                        /* Copy Out data transfer in progress 
*/
  } PGAsyncStatusType;
  
+ /* PGQueryClass tracks which query protocol we are now executing */
+ typedef enum
+ {
+       PGQUERY_SIMPLE,                         /* simple Query protocol (PQexec) */
+       PGQUERY_EXTENDED,                       /* full Extended protocol 
(PQexecParams) */
+       PGQUERY_PREPARE                         /* Parse only (PQprepare) */
+ } PGQueryClass;
+ 
  /* PGSetenvStatusType defines the state of the PQSetenv state machine */
  /* (this is used only for 2.0-protocol connections) */
  typedef enum
***************
*** 264,273 ****
        PGAsyncStatusType asyncStatus;
        PGTransactionStatusType xactStatus;
        /* note: xactStatus never changes to ACTIVE */
        bool            nonblocking;    /* whether this connection is using
                                                                 * nonblock sending 
semantics */
-       bool            ext_query;              /* was our last query sent with 
extended
-                                                                * query protocol? */
        char            copy_is_binary; /* 1 = copy binary, 0 = copy text */
        int                     copy_already_done;              /* # bytes already 
returned in
                                                                                 * 
COPY OUT */
--- 272,280 ----
        PGAsyncStatusType asyncStatus;
        PGTransactionStatusType xactStatus;
        /* note: xactStatus never changes to ACTIVE */
+       PGQueryClass queryclass;
        bool            nonblocking;    /* whether this connection is using
                                                                 * nonblock sending 
semantics */
        char            copy_is_binary; /* 1 = copy binary, 0 = copy text */
        int                     copy_already_done;              /* # bytes already 
returned in
                                                                                 * 
COPY OUT */

---------------------------(end of broadcast)---------------------------
TIP 3: if posting/reading through Usenet, please send an appropriate
      subscribe-nomail command to [EMAIL PROTECTED] so that your
      message can get through to the mailing list cleanly

Reply via email to