Here is a mega-patch that merges in your four previous patches. Is this
ready to be applied? Making libpq changes during beta is risky so we
had better be careful.
I have also attached your pgtest.c file.
---------------------------------------------------------------------------
Abhijit Menon-Sen wrote:
> At 2004-10-16 18:41:05 -0400, [EMAIL PROTECTED] wrote:
> >
> > I think the cleanest solution is probably to add a state flag indicating
> > whether ParseComplete should generate a PGresult or not.
>
> Like the appended (incremental) patch?
>
> (I didn't think this would work, because I thought libpq would allow you
> to send multiple queries before trying to read results. But you said it
> didn't support that, so...)
>
> -- ams
>
> --- libpq-int.h.1~ 2004-10-18 17:42:13.175759981 +0530
> +++ libpq-int.h 2004-10-18 17:43:40.105986570 +0530
> @@ -269,6 +269,8 @@
> * nonblock sending
> semantics */
> bool ext_query; /* was our last query sent with
> extended
> * query protocol? */
> + bool sent_prepare; /* was our last Parse message sent with
> + * PQprepare? */
> char copy_is_binary; /* 1 = copy binary, 0 = copy text */
> int copy_already_done; /* # bytes already
> returned in
> *
> COPY OUT */
>
> --- fe-exec.c.2~ 2004-10-18 17:47:55.540189274 +0530
> +++ fe-exec.c 2004-10-18 17:48:30.119038902 +0530
> @@ -686,6 +686,7 @@
> goto sendFailed;
>
> conn->ext_query = true;
> + conn->sent_prepare = true;
> if (pqFlush(conn) < 0)
> goto sendFailed;
> conn->asyncStatus = PGASYNC_BUSY;
>
> --- fe-protocol3.c.2~ 2004-10-18 17:44:06.616198123 +0530
> +++ fe-protocol3.c 2004-10-18 17:46:34.431656569 +0530
> @@ -220,10 +220,13 @@
> conn->asyncStatus = PGASYNC_READY;
> break;
> case '1': /* Parse Complete */
> - if (conn->result == NULL)
> - conn->result =
> PQmakeEmptyPGresult(conn,
> -
> PGRES_COMMAND_OK);
> - conn->asyncStatus = PGASYNC_READY;
> + if (conn->sent_prepare) {
> + if (!conn->result)
> + conn->result =
> PQmakeEmptyPGresult(conn,
> +
> PGRES_COMMAND_OK);
> + conn->asyncStatus = PGASYNC_READY;
> + conn->sent_prepare = false;
> + }
> break;
> case '2': /* Bind Complete */
> case '3': /* Close Complete */
>
> --- libpq-fe.h.2~ 2004-10-18 17:55:40.632064120 +0530
> +++ libpq-fe.h 2004-10-18 17:56:26.501634328 +0530
> @@ -312,9 +312,9 @@
> int resultFormat);
>
> /* Interface for multiple-result or asynchronous queries */
> -extern PGresult *PQsendPrepare(PGconn *conn, const char *stmtName,
> - const char *query, int
> nParams,
> - const Oid *paramTypes);
> +extern int PQsendPrepare(PGconn *conn, const char *stmtName,
> + const char *query, int nParams,
> + const Oid *paramTypes);
> extern int PQsendQuery(PGconn *conn, const char *query);
> extern int PQsendQueryParams(PGconn *conn,
> const char *command,
>
> ---------------------------(end of broadcast)---------------------------
> TIP 5: Have you checked our extensive FAQ?
>
> http://www.postgresql.org/docs/faqs/FAQ.html
>
--
Bruce Momjian | http://candle.pha.pa.us
[EMAIL PROTECTED] | (610) 359-1001
+ If your life is a hard drive, | 13 Roberts Road
+ Christ can be your backup. | Newtown Square, Pennsylvania 19073
Index: doc/src/sgml/libpq.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/libpq.sgml,v
retrieving revision 1.165
diff -c -c -r1.165 libpq.sgml
*** doc/src/sgml/libpq.sgml 1 Oct 2004 17:34:17 -0000 1.165
--- doc/src/sgml/libpq.sgml 18 Oct 2004 18:13:09 -0000
***************
*** 1183,1194 ****
</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>
--- 1183,1238 ----
</varlistentry>
</variablelist>
! Prepared statements for use with <function>PQexecPrepared</> can be
! created by executing an SQL <command>PREPARE</> statement (which is
! sent with <function>PQexec</>, or one of the other query-submission
! functions), or with <function>PQprepare</>. The latter does not
! require parameter types to be pre-specified.
! </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 execution with
! <function>PQexecPrepared</>. Unlike <command>PREPARE</>, it allows the
! client to prepare a statement without pre-specifying the types of each
! parameter. This function 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</>
! (which may be <literal>""</>, to refer to the unnamed statement) from
! the <parameter>query</>. 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[]</>. It may be zero,
! or up to the number of parameters used in the query. Each entry in the
! <parameter>paramTypes[]</> array should contain the OID of the type of
! the corresponding parameter. If <parameter>nParams</> is 0, the server
! assigns a data type to each parameter, as it would for untyped literal
! strings. Likewise, if any element in the type array is zero, its type
! is inferred.
! </para>
! </listitem>
! </varlistentry>
! </variablelist>
</para>
<para>
***************
*** 2353,2358 ****
--- 2397,2423 ----
</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</>, and
+ its parameters are handled identically. It will not work on 2.0
+ protocol connections.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><function>PQgetResult</function><indexterm><primary>PQgetResult</></></term>
<listitem>
<para>
Index: src/interfaces/libpq/fe-exec.c
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v
retrieving revision 1.163
diff -c -c -r1.163 fe-exec.c
*** src/interfaces/libpq/fe-exec.c 16 Oct 2004 22:52:53 -0000 1.163
--- src/interfaces/libpq/fe-exec.c 18 Oct 2004 18:13:23 -0000
***************
*** 635,640 ****
--- 635,704 ----
/*
+ * 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 (PG_PROTOCOL_MAJOR(conn->pversion) < 3)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("function requires at
least protocol version 3.0\n"));
+ return 0;
+ }
+
+ if (!PQsendQueryStart(conn))
+ return 0;
+
+ 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;
+
+ if (pqPutMsgStart('S', false, conn) < 0 ||
+ pqPutMsgEnd(conn) < 0)
+ goto sendFailed;
+
+ conn->ext_query = true;
+ conn->sent_prepare = true;
+ if (pqFlush(conn) < 0)
+ goto sendFailed;
+ conn->asyncStatus = PGASYNC_BUSY;
+ return 1;
+
+ sendFailed:
+ pqHandleSendFailure(conn);
+ return 0;
+ }
+
+
+ /*
* PQsendQuery
* Submit a query, but don't wait for it to finish
*
***************
*** 1145,1150 ****
--- 1209,1236 ----
return PQexecFinish(conn);
}
+
+ /*
+ * PQprepare
+ * Creates a prepared statement by issuing a v3.0 parse message.
+ *
+ * Returns NULL if the query failed, and a new PGresult otherwise. The
+ * user is responsible for calling PQclient() on the result.
+ */
+
+ 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);
+ }
+
+
/*
* PQexecParams
* Like PQexec, but use protocol 3.0 so we can pass parameters
Index: src/interfaces/libpq/fe-protocol3.c
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v
retrieving revision 1.18
diff -c -c -r1.18 fe-protocol3.c
*** src/interfaces/libpq/fe-protocol3.c 16 Oct 2004 22:52:54 -0000 1.18
--- src/interfaces/libpq/fe-protocol3.c 18 Oct 2004 18:13:26 -0000
***************
*** 220,225 ****
--- 220,233 ----
conn->asyncStatus = PGASYNC_READY;
break;
case '1': /* Parse Complete */
+ if (conn->sent_prepare) {
+ if (!conn->result)
+ conn->result =
PQmakeEmptyPGresult(conn,
+
PGRES_COMMAND_OK);
+ conn->asyncStatus = PGASYNC_READY;
+ conn->sent_prepare = false;
+ }
+ break;
case '2': /* Bind Complete */
case '3': /* Close Complete */
/* Nothing to do for these message types */
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.111
diff -c -c -r1.111 libpq-fe.h
*** src/interfaces/libpq/libpq-fe.h 16 Oct 2004 22:52:55 -0000 1.111
--- src/interfaces/libpq/libpq-fe.h 18 Oct 2004 18:13:28 -0000
***************
*** 296,301 ****
--- 296,304 ----
/* Simple synchronous query */
extern PGresult *PQexec(PGconn *conn, const char *query);
+ extern PGresult *PQprepare(PGconn *conn, const char *stmtName,
+ const char *query, int nParams,
+ const Oid *paramTypes);
extern PGresult *PQexecParams(PGconn *conn,
const char *command,
int nParams,
***************
*** 313,318 ****
--- 316,324 ----
int resultFormat);
/* Interface for multiple-result or asynchronous queries */
+ extern int PQsendPrepare(PGconn *conn, const char *stmtName,
+ const char *query, int nParams,
+ const Oid *paramTypes);
extern int PQsendQuery(PGconn *conn, const char *query);
extern int PQsendQueryParams(PGconn *conn,
const char *command,
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.94
diff -c -c -r1.94 libpq-int.h
*** src/interfaces/libpq/libpq-int.h 16 Oct 2004 22:52:55 -0000 1.94
--- src/interfaces/libpq/libpq-int.h 18 Oct 2004 18:13:29 -0000
***************
*** 268,273 ****
--- 268,275 ----
* nonblock sending
semantics */
bool ext_query; /* was our last query sent with
extended
* query protocol? */
+ bool sent_prepare; /* was our last Parse message sent with
+ * PQprepare? */
char copy_is_binary; /* 1 = copy binary, 0 = copy text */
int copy_already_done; /* # bytes already
returned in
*
COPY OUT */
/*
* Test program for PQprepare/PQdescribe.
* Abhijit Menon-Sen <[EMAIL PROTECTED]>
*
* create table pqtest (a int, b text, c text);
* insert into pqtest (a,b,c) values (1,'foo','bar');
* insert into pqtest (a,b,c) values (1,'foo','baz');
* insert into pqtest (a,b,c) values (2,'foo','barf');
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libpq-fe.h"
int main(int argc, char *argv[])
{
int i, a, b, c;
PGconn *conn;
PGresult *res;
char *query = "select * from pqtest where a=$1 and b=$2";
const char *values[2];
conn = PQconnectdb("");
if (PQstatus(conn) != CONNECTION_OK) {
fprintf(stderr, "Couldn't connect to the database: %s",
PQerrorMessage(conn));
PQfinish(conn);
exit(-1);
}
res = PQprepare(conn, "prep", query, 0, NULL);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "PREPARE failed: %s", PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);
exit(-1);
}
PQclear(res);
values[0] = "1";
values[1] = "foo";
res = PQexecPrepared(conn, "prep", 2, values, NULL, NULL, 0);
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
fprintf(stderr, "EXECUTE failed: %s", PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);
exit(-1);
}
a = PQfnumber(res, "a");
b = PQfnumber(res, "b");
c = PQfnumber(res, "c");
printf("Results:\n");
for(i = 0; i < PQntuples(res); i++) {
char *av = PQgetvalue(res, i, a);
char *bv = PQgetvalue(res, i, b);
char *cv = PQgetvalue(res, i, c);
printf(" (a=%s)(b=%s)(c=%s)\n", av, bv, cv);
}
printf("%d rows\n", i);
PQclear(res);
PQfinish(conn);
return 0;
}
---------------------------(end of broadcast)---------------------------
TIP 1: subscribe and unsubscribe commands go to [EMAIL PROTECTED]