Add PQexecPrepared() and PQsendQueryPrepared() functions, to allow
authorTom Lane <[email protected]>
Wed, 13 Aug 2003 16:29:03 +0000 (16:29 +0000)
committerTom Lane <[email protected]>
Wed, 13 Aug 2003 16:29:03 +0000 (16:29 +0000)
libpq users to perform Bind/Execute of previously prepared statements.
Per yesterday's discussion, this offers enough performance improvement
to justify bending the 'no new features during beta' rule.

doc/src/sgml/libpq.sgml
src/interfaces/libpq/blibpqdll.def
src/interfaces/libpq/fe-exec.c
src/interfaces/libpq/libpq-fe.h
src/interfaces/libpq/libpqdll.def

index dc395d46c35d7f1afa7fe0414f814f9459ef43c5..8284d4b5f8d9aba2bdbb4d971948f94a611d124e 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.130 2003/08/01 03:10:04 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v 1.131 2003/08/13 16:29:03 tgl Exp $
 -->
 
  <chapter id="libpq">
@@ -1090,6 +1090,53 @@ 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>
+<variablelist>
+<varlistentry>
+<term><function>PQexecPrepared</function></term>
+<listitem>
+<para>
+          Sends a request to execute a prepared statement with given
+     parameters, and waits for the result.
+<synopsis>
+PGresult *PQexecPrepared(PGconn *conn,
+                         const char *stmtName,
+                         int nParams,
+                         const char * const *paramValues,
+                         const int *paramLengths,
+                         const int *paramFormats,
+                         int resultFormat);
+</synopsis>
+</para>
+
+<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
+connections; it will fail when using protocol 2.0.
+</para>
+
+<para>
+The parameters are identical to <function>PQexecParams</>, except that the
+name of a prepared statement is given instead of a query string, and the
+<parameter>paramTypes[]</> parameter is not present (it is not needed since
+the prepared statement's parameter types were determined when it was created).
+</para>
+</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>
 The <structname>PGresult</structname> structure encapsulates the result
 returned by the server.
@@ -1775,7 +1822,7 @@ SQL commands are fed to your database.
 <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</>.
+its sibling routines.
 
 <synopsis>
 size_t PQescapeString (char *to, const char *from, size_t length);
@@ -1961,9 +2008,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>.
-There is also <function>PQsendQueryParams</function>, which can be
-used with <function>PQgetResult</function> to duplicate the functionality
-of <function>PQexecParams</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>
 <varlistentry>
@@ -2014,13 +2063,41 @@ int PQsendQueryParams(PGconn *conn,
 </listitem>
 </varlistentry>
 
+<varlistentry>
+<term><function>PQsendQueryPrepared</function></term>
+<listitem>
+<para>
+          Sends a request to execute a prepared statement with given
+     parameters, without waiting for the result(s).
+<synopsis>
+int PQsendQueryPrepared(PGconn *conn,
+                        const char *stmtName,
+                        int nParams,
+                        const char * const *paramValues,
+                        const int *paramLengths,
+                        const int *paramFormats,
+                        int resultFormat);
+</synopsis>
+
+   This is similar to <function>PQsendQueryParams</function>, but the
+   command to be executed is specified by naming a previously-prepared
+   statement, instead of giving a query string.
+   The function's parameters are handled identically to
+   <function>PQexecPrepared</function>.  Like
+   <function>PQexecPrepared</function>, it will not work on 2.0-protocol
+   connections.
+</para>
+</listitem>
+</varlistentry>
+
 <varlistentry>
 <term><function>PQgetResult</function></term>
 <listitem>
 <para>
           Waits for the next result from a prior
-     <function>PQsendQuery</function> or
-     <function>PQsendQueryParams</function>,
+     <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.
 <synopsis>
index ff85e9cdfc827cffe9ffcf863ba6aa864d81ae2c..eb78e770f8aec358f471a06a5a66c284f0c29261 100644 (file)
@@ -111,6 +111,8 @@ EXPORTS
     _PQftable                @ 107
     _PQftablecol             @ 108
     _PQfformat               @ 109
+    _PQexecPrepared          @ 110
+    _PQsendQueryPrepared     @ 111
 
 ; Aliases for MS compatible names
     PQconnectdb             = _PQconnectdb            
@@ -222,3 +224,5 @@ EXPORTS
     PQftable                = _PQftable
     PQftablecol             = _PQftablecol
     PQfformat               = _PQfformat
+    PQexecPrepared          = _PQexecPrepared
+    PQsendQueryPrepared     = _PQsendQueryPrepared
index 350561d72671d0df32a28db4a8dd8320202d4917..0e2cd6a8a7638af7a908700a00ff86868c5484df 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.143 2003/08/04 02:40:16 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.144 2003/08/13 16:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,6 +44,15 @@ char    *const pgresStatus[] = {
 
 
 static bool PQsendQueryStart(PGconn *conn);
+static int PQsendQueryGuts(PGconn *conn,
+                          const char *command,
+                          const char *stmtName,
+                          int nParams,
+                          const Oid *paramTypes,
+                          const char *const * paramValues,
+                          const int *paramLengths,
+                          const int *paramFormats,
+                          int resultFormat);
 static void parseInput(PGconn *conn);
 static bool PQexecStart(PGconn *conn);
 static PGresult *PQexecFinish(PGconn *conn);
@@ -668,58 +677,160 @@ PQsendQueryParams(PGconn *conn,
                  const int *paramFormats,
                  int resultFormat)
 {
-   int         i;
+   if (!PQsendQueryStart(conn))
+       return 0;
+
+   if (!command)
+   {
+       printfPQExpBuffer(&conn->errorMessage,
+                   libpq_gettext("command string is a null pointer\n"));
+       return 0;
+   }
+
+   return PQsendQueryGuts(conn,
+                          command,
+                          "",  /* use unnamed statement */
+                          nParams,
+                          paramTypes,
+                          paramValues,
+                          paramLengths,
+                          paramFormats,
+                          resultFormat);
+}
 
+/*
+ * PQsendQueryPrepared
+ *     Like PQsendQuery, but execute a previously prepared statement,
+ *     using 3.0 protocol so we can pass parameters
+ */
+int
+PQsendQueryPrepared(PGconn *conn,
+                   const char *stmtName,
+                   int nParams,
+                   const char *const * paramValues,
+                   const int *paramLengths,
+                   const int *paramFormats,
+                   int resultFormat)
+{
    if (!PQsendQueryStart(conn))
        return 0;
 
-   /* This isn't gonna work on a 2.0 server */
-   if (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
+   if (!stmtName)
    {
        printfPQExpBuffer(&conn->errorMessage,
-            libpq_gettext("function requires at least 3.0 protocol\n"));
+                         libpq_gettext("statement name is a null pointer\n"));
        return 0;
    }
 
-   if (!command)
+   return PQsendQueryGuts(conn,
+                          NULL, /* no command to parse */
+                          stmtName,
+                          nParams,
+                          NULL, /* no param types */
+                          paramValues,
+                          paramLengths,
+                          paramFormats,
+                          resultFormat);
+}
+
+/*
+ * Common startup code for PQsendQuery and sibling routines
+ */
+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("command string is a null pointer\n"));
+                         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;
+}
+
+/*
+ * PQsendQueryGuts
+ *     Common code for 3.0-protocol query sending
+ *     PQsendQueryStart should be done already
+ *
+ * command may be NULL to indicate we use an already-prepared statement
+ */
+static int
+PQsendQueryGuts(PGconn *conn,
+               const char *command,
+               const char *stmtName,
+               int nParams,
+               const Oid *paramTypes,
+               const char *const * paramValues,
+               const int *paramLengths,
+               const int *paramFormats,
+               int resultFormat)
+{
+   int         i;
+
+   /* 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;
    }
 
    /*
-    * We will send Parse, Bind, Describe Portal, Execute, Sync, using
-    * unnamed statement and portal.
+    * We will send Parse (if needed), Bind, Describe Portal, Execute, Sync,
+    * using specified statement name and the unnamed 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 (command)
    {
-       if (pqPutInt(nParams, 2, conn) < 0)
+       /* construct the Parse message */
+       if (pqPutMsgStart('P', false, conn) < 0 ||
+           pqPuts(stmtName, conn) < 0 ||
+           pqPuts(command, conn) < 0)
            goto sendFailed;
-       for (i = 0; i < nParams; i++)
+       if (nParams > 0 && paramTypes)
        {
-           if (pqPutInt(paramTypes[i], 4, conn) < 0)
+           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)
+       else
+       {
+           if (pqPutInt(0, 2, conn) < 0)
+               goto sendFailed;
+       }
+       if (pqPutMsgEnd(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)
+       pqPuts(stmtName, conn) < 0)
        goto sendFailed;
    if (nParams > 0 && paramFormats)
    {
@@ -807,41 +918,6 @@ sendFailed:
    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.
  *
@@ -1071,7 +1147,30 @@ PQexecParams(PGconn *conn,
 }
 
 /*
- * Common code for PQexec and PQexecParams: prepare to send command
+ * PQexecPrepared
+ *     Like PQexec, but execute a previously prepared statement,
+ *     using 3.0 protocol so we can pass parameters
+ */
+PGresult *
+PQexecPrepared(PGconn *conn,
+              const char *stmtName,
+              int nParams,
+              const char *const * paramValues,
+              const int *paramLengths,
+              const int *paramFormats,
+              int resultFormat)
+{
+   if (!PQexecStart(conn))
+       return NULL;
+   if (!PQsendQueryPrepared(conn, stmtName,
+                            nParams, paramValues, paramLengths,
+                            paramFormats, resultFormat))
+       return NULL;
+   return PQexecFinish(conn);
+}
+
+/*
+ * Common code for PQexec and sibling routines: prepare to send command
  */
 static bool
 PQexecStart(PGconn *conn)
@@ -1139,7 +1238,7 @@ PQexecStart(PGconn *conn)
 }
 
 /*
- * Common code for PQexec and PQexecParams: wait for command result
+ * Common code for PQexec and sibling routines: wait for command result
  */
 static PGresult *
 PQexecFinish(PGconn *conn)
index eb64a5ac585946638beba3ce44cfefc6b0f7cc90..6843bb7e981b79a1a9692ab76828b89547bda5cf 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: libpq-fe.h,v 1.97 2003/08/08 21:42:55 momjian Exp $
+ * $Id: libpq-fe.h,v 1.98 2003/08/13 16:29:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -286,6 +286,13 @@ extern PGresult *PQexecParams(PGconn *conn,
             const int *paramLengths,
             const int *paramFormats,
             int resultFormat);
+extern PGresult *PQexecPrepared(PGconn *conn,
+            const char *stmtName,
+            int nParams,
+            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);
@@ -297,6 +304,13 @@ extern int PQsendQueryParams(PGconn *conn,
                  const int *paramLengths,
                  const int *paramFormats,
                  int resultFormat);
+extern int PQsendQueryPrepared(PGconn *conn,
+                 const char *stmtName,
+                 int nParams,
+                 const char *const * paramValues,
+                 const int *paramLengths,
+                 const int *paramFormats,
+                 int resultFormat);
 extern PGresult *PQgetResult(PGconn *conn);
 
 /* Routines for managing an asynchronous query */
index 8ff902f5218da1a98aae6524ad03af4fb1c8c21e..4973ddd713866449e17b96bca2e77fbc0bf8facc 100644 (file)
@@ -111,3 +111,5 @@ EXPORTS
     PQftable                @ 107
     PQftablecol             @ 108
     PQfformat               @ 109
+    PQexecPrepared          @ 110
+    PQsendQueryPrepared     @ 111