Re: [HACKERS] Final libpq patch?

From: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
To: Abhijit Menon-Sen <ams(at)oryx(dot)com>
Cc: Bruce Momjian <pgman(at)candle(dot)pha(dot)pa(dot)us>, pgsql-patches(at)postgreSQL(dot)org
Subject: Re: [HACKERS] Final libpq patch?
Date: 2004-10-18 22:03:50
Message-ID: 8745.1098137030@sss.pgh.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers pgsql-patches

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 */

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Bruce Momjian 2004-10-18 22:12:29 Re: libpq and prepared statements progress for 8.0
Previous Message Jan Wieck 2004-10-18 21:19:17 Autotuning of shared buffer size (was: Re: Getting rid of AtEOXact Buffers (was Re: [Testperf-general] Re: [PERFORM] First set of OSDL Shared Memscalability results, some wierdness ...))

Browse pgsql-patches by date

  From Date Subject
Next Message Bruce Momjian 2004-10-18 22:12:29 Re: libpq and prepared statements progress for 8.0
Previous Message Tom Lane 2004-10-18 19:40:36 Re: Nearing final release?