]> granicus.if.org Git - postgresql/commitdiff
Add PQprepare/PQsendPrepared functions to libpq to support preparing
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 18 Oct 2004 22:00:42 +0000 (22:00 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 18 Oct 2004 22:00:42 +0000 (22:00 +0000)
statements without necessarily specifying the datatypes of their parameters.
Abhijit Menon-Sen with some help from Tom Lane.

doc/src/sgml/libpq.sgml
src/interfaces/libpq/exports.txt
src/interfaces/libpq/fe-exec.c
src/interfaces/libpq/fe-protocol3.c
src/interfaces/libpq/libpq-fe.h
src/interfaces/libpq/libpq-int.h

index 97a6ab5e7124d1db9c5a8c94a6a8e31c987edc4a..d0c59424fc9e8231fe616c6c788b1b983f9a66ee 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.165 2004/10/01 17:34:17 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.166 2004/10/18 22:00:41 tgl Exp $
 -->
 
  <chapter id="libpq">
@@ -1055,8 +1055,9 @@ PGresult *PQexec(PGconn *conn, const char *command);
           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.
+          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>
@@ -1144,6 +1145,81 @@ 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>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>
@@ -1166,7 +1242,8 @@ PGresult *PQexecPrepared(PGconn *conn,
 <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
+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,13 +1259,6 @@ the prepared statement's parameter types were determined when it was created).
 </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>
@@ -2270,10 +2340,15 @@ 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 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>
+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>
@@ -2325,6 +2400,33 @@ int PQsendQueryParams(PGconn *conn,
 </listitem>
 </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>
@@ -2358,7 +2460,8 @@ int PQsendQueryPrepared(PGconn *conn,
 <para>
           Waits for the next result from a prior
           <function>PQsendQuery</function>,
-          <function>PQsendQueryParams</function>, or
+          <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.
index d6532155a386411bbdb30d214c9017df7444c0c0..e14a8bb58a9aa218dd8838945bd3d8b9ce5180bc 100644 (file)
@@ -1,3 +1,4 @@
+# $PostgreSQL: pgsql/src/interfaces/libpq/exports.txt,v 1.2 2004/10/18 22:00:42 tgl Exp $
 # Functions to be exported by libpq DLLs
 PQconnectdb               1
 PQsetdbLogin              2
@@ -116,3 +117,5 @@ PQgetssl                  114
 pg_char_to_encoding       115
 pg_valid_server_encoding  116
 pqsignal                  117
+PQprepare                 118
+PQsendPrepare             119
index 5e19d78b37adc48fe5d122ebd1bb9326219e3c86..fff9746e33ba28aac62b673b18a58fe4720d7dfa 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.163 2004/10/16 22:52:53 tgl Exp $
+ *       $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.164 2004/10/18 22:00:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -664,7 +664,7 @@ PQsendQuery(PGconn *conn, const char *query)
        }
 
        /* remember we are using simple query protocol */
-       conn->ext_query = false;
+       conn->queryclass = PGQUERY_SIMPLE;
 
        /*
         * Give the data a push.  In nonblock mode, don't complain if we're
@@ -717,6 +717,94 @@ PQsendQueryParams(PGconn *conn,
                                                   resultFormat);
 }
 
+/*
+ * 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,
@@ -921,7 +1009,7 @@ PQsendQueryGuts(PGconn *conn,
                goto sendFailed;
 
        /* remember we are using extended query protocol */
-       conn->ext_query = true;
+       conn->queryclass = PGQUERY_EXTENDED;
 
        /*
         * Give the data a push.  In nonblock mode, don't complain if we're
@@ -1134,7 +1222,6 @@ PQgetResult(PGconn *conn)
  * The user is responsible for freeing the PGresult via PQclear()
  * when done with it.
  */
-
 PGresult *
 PQexec(PGconn *conn, const char *query)
 {
@@ -1168,6 +1255,29 @@ PQexecParams(PGconn *conn,
        return PQexecFinish(conn);
 }
 
+/*
+ * 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,
@@ -1451,7 +1561,7 @@ PQputCopyEnd(PGconn *conn, const char *errormsg)
                 * If we sent the COPY command in extended-query mode, we must
                 * issue a Sync as well.
                 */
-               if (conn->ext_query)
+               if (conn->queryclass != PGQUERY_SIMPLE)
                {
                        if (pqPutMsgStart('S', false, conn) < 0 ||
                                pqPutMsgEnd(conn) < 0)
index cec7a6720585a01e97b49f29365864bf00491511..af88ddc8f436702eeae7c55fc5b472137816409d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.18 2004/10/16 22:52:54 tgl Exp $
+ *       $PostgreSQL: pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.19 2004/10/18 22:00:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -220,6 +220,15 @@ pqParseInput3(PGconn *conn)
                                        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,7 +1127,7 @@ pqEndcopy3(PGconn *conn)
                 * If we sent the COPY command in extended-query mode, we must
                 * issue a Sync as well.
                 */
-               if (conn->ext_query)
+               if (conn->queryclass != PGQUERY_SIMPLE)
                {
                        if (pqPutMsgStart('S', false, conn) < 0 ||
                                pqPutMsgEnd(conn) < 0)
index 55e288b417ab86280f2ac326cac4ebe1903ad64e..1fba91bde4ef4dba65961b2334e81b3d90099a1d 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.111 2004/10/16 22:52:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.112 2004/10/18 22:00:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -304,6 +304,9 @@ extern PGresult *PQexecParams(PGconn *conn,
                         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,6 +325,9 @@ extern int PQsendQueryParams(PGconn *conn,
                                  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,
index e0992c4103ad6dc51f62e9ee423065eb52682797..d1fab1395b24645390777da38ee021a208b25ca4 100644 (file)
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.94 2004/10/16 22:52:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.95 2004/10/18 22:00:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -185,6 +185,14 @@ typedef enum
        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,10 +272,9 @@ struct pg_conn
        PGAsyncStatusType asyncStatus;
        PGTransactionStatusType xactStatus;
        /* note: xactStatus never changes to ACTIVE */
+       PGQueryClass queryclass;
        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 */