summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2003-08-13 16:29:03 +0000
committerTom Lane2003-08-13 16:29:03 +0000
commit0be731ad4441bcb8d2aea809c08c37a4a5d831ce (patch)
treea999540619a361343a9ca8eb9e87799a9a6e13e7
parent5be44fa453df96aecfa5eb717553e8264700db18 (diff)
Add PQexecPrepared() and PQsendQueryPrepared() functions, to allow
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.
-rw-r--r--doc/src/sgml/libpq.sgml91
-rw-r--r--src/interfaces/libpq/blibpqdll.def4
-rw-r--r--src/interfaces/libpq/fe-exec.c223
-rw-r--r--src/interfaces/libpq/libpq-fe.h16
-rw-r--r--src/interfaces/libpq/libpqdll.def2
5 files changed, 266 insertions, 70 deletions
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index dc395d46c35..8284d4b5f8d 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -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">
@@ -1091,6 +1091,53 @@ 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.
<application>libpq</application> application programmers should be careful to
@@ -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>
@@ -2015,12 +2064,40 @@ int PQsendQueryParams(PGconn *conn,
</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>
diff --git a/src/interfaces/libpq/blibpqdll.def b/src/interfaces/libpq/blibpqdll.def
index ff85e9cdfc8..eb78e770f8a 100644
--- a/src/interfaces/libpq/blibpqdll.def
+++ b/src/interfaces/libpq/blibpqdll.def
@@ -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
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 350561d7267..0e2cd6a8a76 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.c
@@ -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)
{
@@ -808,41 +919,6 @@ sendFailed:
}
/*
- * 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.
*
* Primarily, what we want to accomplish here is to process an async
@@ -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)
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index eb64a5ac585..6843bb7e981 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -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 */
diff --git a/src/interfaces/libpq/libpqdll.def b/src/interfaces/libpq/libpqdll.def
index 8ff902f5218..4973ddd7138 100644
--- a/src/interfaces/libpq/libpqdll.def
+++ b/src/interfaces/libpq/libpqdll.def
@@ -111,3 +111,5 @@ EXPORTS
PQftable @ 107
PQftablecol @ 108
PQfformat @ 109
+ PQexecPrepared @ 110
+ PQsendQueryPrepared @ 111