summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/libpq.sgml135
-rw-r--r--src/interfaces/libpq/exports.txt3
-rw-r--r--src/interfaces/libpq/fe-exec.c120
-rw-r--r--src/interfaces/libpq/fe-protocol3.c13
-rw-r--r--src/interfaces/libpq/libpq-fe.h8
-rw-r--r--src/interfaces/libpq/libpq-int.h13
6 files changed, 265 insertions, 27 deletions
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 97a6ab5e712..d0c59424fc9 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -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>
@@ -1147,6 +1148,81 @@ but has some usefulness as an extra defense against SQL-injection attacks.
<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,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>
@@ -2326,6 +2401,33 @@ int PQsendQueryParams(PGconn *conn,
</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,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.
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index d6532155a38..e14a8bb58a9 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -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
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 5e19d78b37a..fff9746e33b 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -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
@@ -718,6 +718,94 @@ PQsendQueryParams(PGconn *conn,
}
/*
+ * 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,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)
{
@@ -1169,6 +1256,29 @@ PQexecParams(PGconn *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,
* using protocol 3.0 so we can pass parameters
@@ -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)
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index cec7a672058..af88ddc8f43 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -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)
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 55e288b417a..1fba91bde4e 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -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,
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index e0992c4103a..d1fab1395b2 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -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 */