Here's the patch actually applied. I cleaned up the state problem, fixed a couple minor omissions in the code, and did some work on the documentation; also updated exports.txt which is now the source for the *.def files.
regards, tom lane *** doc/src/sgml/libpq.sgml.orig Fri Oct 1 13:34:17 2004 --- doc/src/sgml/libpq.sgml Mon Oct 18 17:50:04 2004 *************** *** 1055,1062 **** 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. </para> </listitem> </varlistentry> --- 1055,1063 ---- 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 such errors. </para> </listitem> </varlistentry> *************** *** 1147,1152 **** --- 1148,1228 ---- <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,1172 **** <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 --- 1242,1249 ---- <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 *************** *** 1182,1194 **** </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> --- 1259,1264 ---- *************** *** 2270,2279 **** 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> respectively. <variablelist> --- 2340,2354 ---- 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>, ! <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,2331 **** --- 2401,2433 ---- </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,2364 **** <para> Waits for the next result from a prior <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. --- 2460,2467 ---- <para> Waits for the next result from a prior <function>PQsendQuery</function>, ! <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. *** src/interfaces/libpq/exports.txt.orig Sat Oct 16 16:03:45 2004 --- src/interfaces/libpq/exports.txt Mon Oct 18 17:36:43 2004 *************** *** 1,3 **** --- 1,4 ---- + # $PostgreSQL$ # Functions to be exported by libpq DLLs PQconnectdb 1 PQsetdbLogin 2 *************** *** 116,118 **** --- 117,121 ---- pg_char_to_encoding 115 pg_valid_server_encoding 116 pqsignal 117 + PQprepare 118 + PQsendPrepare 119 *** src/interfaces/libpq/fe-exec.c.orig Sat Oct 16 18:52:53 2004 --- src/interfaces/libpq/fe-exec.c Mon Oct 18 17:35:24 2004 *************** *** 664,670 **** } /* remember we are using simple query protocol */ ! conn->ext_query = false; /* * Give the data a push. In nonblock mode, don't complain if we're --- 664,670 ---- } /* remember we are using simple query protocol */ ! conn->queryclass = PGQUERY_SIMPLE; /* * Give the data a push. In nonblock mode, don't complain if we're *************** *** 718,723 **** --- 718,811 ---- } /* + * 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,927 **** goto sendFailed; /* remember we are using extended query protocol */ ! conn->ext_query = true; /* * Give the data a push. In nonblock mode, don't complain if we're --- 1009,1015 ---- goto sendFailed; /* remember we are using extended query protocol */ ! conn->queryclass = PGQUERY_EXTENDED; /* * Give the data a push. In nonblock mode, don't complain if we're *************** *** 1134,1140 **** * The user is responsible for freeing the PGresult via PQclear() * when done with it. */ - PGresult * PQexec(PGconn *conn, const char *query) { --- 1222,1227 ---- *************** *** 1169,1174 **** --- 1256,1284 ---- } /* + * 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,1457 **** * If we sent the COPY command in extended-query mode, we must * issue a Sync as well. */ ! if (conn->ext_query) { if (pqPutMsgStart('S', false, conn) < 0 || pqPutMsgEnd(conn) < 0) --- 1561,1567 ---- * If we sent the COPY command in extended-query mode, we must * issue a Sync as well. */ ! if (conn->queryclass != PGQUERY_SIMPLE) { if (pqPutMsgStart('S', false, conn) < 0 || pqPutMsgEnd(conn) < 0) *** src/interfaces/libpq/fe-protocol3.c.orig Sat Oct 16 18:52:54 2004 --- src/interfaces/libpq/fe-protocol3.c Mon Oct 18 17:35:24 2004 *************** *** 220,225 **** --- 220,234 ---- 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,1124 **** * If we sent the COPY command in extended-query mode, we must * issue a Sync as well. */ ! if (conn->ext_query) { if (pqPutMsgStart('S', false, conn) < 0 || pqPutMsgEnd(conn) < 0) --- 1127,1133 ---- * If we sent the COPY command in extended-query mode, we must * issue a Sync as well. */ ! if (conn->queryclass != PGQUERY_SIMPLE) { if (pqPutMsgStart('S', false, conn) < 0 || pqPutMsgEnd(conn) < 0) *** src/interfaces/libpq/libpq-fe.h.orig Sat Oct 16 18:52:55 2004 --- src/interfaces/libpq/libpq-fe.h Mon Oct 18 17:35:24 2004 *************** *** 304,309 **** --- 304,312 ---- 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,327 **** --- 325,333 ---- 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, *** src/interfaces/libpq/libpq-int.h.orig Sat Oct 16 18:52:55 2004 --- src/interfaces/libpq/libpq-int.h Mon Oct 18 17:35:25 2004 *************** *** 185,190 **** --- 185,198 ---- 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,273 **** PGAsyncStatusType asyncStatus; PGTransactionStatusType xactStatus; /* note: xactStatus never changes to ACTIVE */ 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 */ --- 272,280 ---- PGAsyncStatusType asyncStatus; PGTransactionStatusType xactStatus; /* note: xactStatus never changes to ACTIVE */ + PGQueryClass queryclass; bool nonblocking; /* whether this connection is using * nonblock sending semantics */ char copy_is_binary; /* 1 = copy binary, 0 = copy text */ int copy_already_done; /* # bytes already returned in * COPY OUT */ ---------------------------(end of broadcast)--------------------------- TIP 3: if posting/reading through Usenet, please send an appropriate subscribe-nomail command to [EMAIL PROTECTED] so that your message can get through to the mailing list cleanly