Final libpq patch? - Mailing list pgsql-hackers
From | Bruce Momjian |
---|---|
Subject | Final libpq patch? |
Date | |
Msg-id | 200410181816.i9IIGnq16187@candle.pha.pa.us Whole thread Raw |
In response to | Re: Nearing final release? (Abhijit Menon-Sen <ams@oryx.com>) |
Responses |
Re: Final libpq patch?
|
List | pgsql-hackers |
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, tgl@sss.pgh.pa.us 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 pgman@candle.pha.pa.us | (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 <ams@oryx.com> * * 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; }
pgsql-hackers by date: