Prepared statements for use with <xref linkend="libpq-PQexecPrepared"/> can also
be created by executing SQL <xref linkend="sql-prepare"/>
- statements. Also, although there is no <application>libpq</application>
- function for deleting a prepared statement, the SQL <xref
- linkend="sql-deallocate"/> statement
- can be used for that purpose.
+ statements.
</para>
<para>
</para>
</listitem>
</varlistentry>
+
+ <varlistentry id="libpq-PQclosePrepared">
+ <term><function>PQclosePrepared</function><indexterm><primary>PQclosePrepared</primary></indexterm></term>
+
+ <listitem>
+ <para>
+ Submits a request to close the specified prepared statement, and waits
+ for completion.
+<synopsis>
+PGresult *PQclosePrepared(PGconn *conn, const char *stmtName);
+</synopsis>
+ </para>
+
+ <para>
+ <xref linkend="libpq-PQclosePrepared"/> allows an application to close
+ a previously prepared statement. Closing a statement releases all
+ of its associated resources on the server and allows its name to be
+ reused.
+ </para>
+
+ <para>
+ <parameter>stmtName</parameter> can be <literal>""</literal> or
+ <symbol>NULL</symbol> to reference the unnamed statement. It is fine
+ if no statement exists with this name, in that case the operation is a
+ no-op. On success, a <structname>PGresult</structname> with
+ status <literal>PGRES_COMMAND_OK</literal> is returned.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-PQclosePortal">
+ <term><function>PQclosePortal</function><indexterm><primary>PQclosePortal</primary></indexterm></term>
+
+ <listitem>
+ <para>
+ Submits a request to close the specified portal, and waits for
+ completion.
+<synopsis>
+PGresult *PQclosePortal(PGconn *conn, const char *portalName);
+</synopsis>
+ </para>
+
+ <para>
+ <xref linkend="libpq-PQclosePortal"/> allows an application to trigger
+ a close of a previously created portal. Closing a portal releases all
+ of its associated resources on the server and allows its name to be
+ reused. (<application>libpq</application> does not provide any
+ direct access to portals, but you can use this function to close a
+ cursor created with a <command>DECLARE CURSOR</command> SQL command.)
+ </para>
+
+ <para>
+ <parameter>portalName</parameter> can be <literal>""</literal> or
+ <symbol>NULL</symbol> to reference the unnamed portal. It is fine
+ if no portal exists with this name, in that case the operation is a
+ no-op. On success, a <structname>PGresult</structname> with status
+ <literal>PGRES_COMMAND_OK</literal> is returned.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
<xref linkend="libpq-PQsendQueryParams"/>,
<xref linkend="libpq-PQsendPrepare"/>,
<xref linkend="libpq-PQsendQueryPrepared"/>,
- <xref linkend="libpq-PQsendDescribePrepared"/>, and
+ <xref linkend="libpq-PQsendDescribePrepared"/>,
<xref linkend="libpq-PQsendDescribePortal"/>,
+ <xref linkend="libpq-PQsendClosePrepared"/>, and
+ <xref linkend="libpq-PQsendClosePortal"/>,
which can be used with <xref linkend="libpq-PQgetResult"/> to duplicate
the functionality of
<xref linkend="libpq-PQexecParams"/>,
<xref linkend="libpq-PQprepare"/>,
<xref linkend="libpq-PQexecPrepared"/>,
- <xref linkend="libpq-PQdescribePrepared"/>, and
+ <xref linkend="libpq-PQdescribePrepared"/>,
<xref linkend="libpq-PQdescribePortal"/>
+ <xref linkend="libpq-PQclosePrepared"/>, and
+ <xref linkend="libpq-PQclosePortal"/>
respectively.
<variablelist>
</listitem>
</varlistentry>
+ <varlistentry id="libpq-PQsendClosePrepared">
+ <term><function>PQsendClosePrepared</function><indexterm><primary>PQsendClosePrepared</primary></indexterm></term>
+
+ <listitem>
+ <para>
+ Submits a request to close the specified prepared statement, without
+ waiting for completion.
+<synopsis>
+int PQsendClosePrepared(PGconn *conn, const char *stmtName);
+</synopsis>
+
+ This is an asynchronous version of <xref linkend="libpq-PQclosePrepared"/>:
+ it returns 1 if it was able to dispatch the request, and 0 if not.
+ After a successful call, call <xref linkend="libpq-PQgetResult"/> to
+ obtain the results. The function's parameters are handled
+ identically to <xref linkend="libpq-PQclosePrepared"/>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="libpq-PQsendClosePortal">
+ <term><function>PQsendClosePortal</function><indexterm><primary>PQsendClosePortal</primary></indexterm></term>
+
+ <listitem>
+ <para>
+ Submits a request to close specified portal, without waiting for
+ completion.
+<synopsis>
+int PQsendClosePortal(PGconn *conn, const char *portalName);
+</synopsis>
+
+ This is an asynchronous version of <xref linkend="libpq-PQclosePortal"/>:
+ it returns 1 if it was able to dispatch the request, and 0 if not.
+ After a successful call, call <xref linkend="libpq-PQgetResult"/> to
+ obtain the results. The function's parameters are handled
+ identically to <xref linkend="libpq-PQclosePortal"/>.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="libpq-PQgetResult">
<term><function>PQgetResult</function><indexterm><primary>PQgetResult</primary></indexterm></term>
<xref linkend="libpq-PQsendPrepare"/>,
<xref linkend="libpq-PQsendQueryPrepared"/>,
<xref linkend="libpq-PQsendDescribePrepared"/>,
- <xref linkend="libpq-PQsendDescribePortal"/>, or
+ <xref linkend="libpq-PQsendDescribePortal"/>,
+ <xref linkend="libpq-PQsendClosePrepared"/>,
+ <xref linkend="libpq-PQsendClosePortal"/>, or
<xref linkend="libpq-PQpipelineSync"/>
call, and returns it.
A null pointer is returned when the command is complete and there
<function>PQexecPrepared</function>,
<function>PQdescribePrepared</function>,
<function>PQdescribePortal</function>,
+ <function>PQclosePrepared</function>,
+ <function>PQclosePortal</function>,
is an error condition.
<function>PQsendQuery</function> is
also disallowed, because it uses the simple query protocol.
establish a synchronization point in the pipeline,
or when <xref linkend="libpq-PQflush"/> is called.
The functions <xref linkend="libpq-PQsendPrepare"/>,
- <xref linkend="libpq-PQsendDescribePrepared"/>, and
- <xref linkend="libpq-PQsendDescribePortal"/> also work in pipeline mode.
+ <xref linkend="libpq-PQsendDescribePrepared"/>,
+ <xref linkend="libpq-PQsendDescribePortal"/>,
+ <xref linkend="libpq-PQsendClosePrepared"/>, and
+ <xref linkend="libpq-PQsendClosePortal"/> also work in pipeline mode.
Result processing is described below.
</para>
PQmblenBounded 185
PQsendFlushRequest 186
PQconnectionUsedGSSAPI 187
+PQclosePrepared 188
+PQclosePortal 189
+PQsendClosePrepared 190
+PQsendClosePortal 191
static PGresult *getCopyResult(PGconn *conn, ExecStatusType copytype);
static bool PQexecStart(PGconn *conn);
static PGresult *PQexecFinish(PGconn *conn);
-static int PQsendDescribe(PGconn *conn, char desc_type,
- const char *desc_target);
+static int PQsendTypedCommand(PGconn *conn, char command, char type,
+ const char *target);
static int check_field_number(const PGresult *res, int field_num);
static void pqPipelineProcessQueue(PGconn *conn);
static int pqPipelineFlush(PGconn *conn);
{
if (!PQexecStart(conn))
return NULL;
- if (!PQsendDescribe(conn, 'S', stmt))
+ if (!PQsendTypedCommand(conn, 'D', 'S', stmt))
return NULL;
return PQexecFinish(conn);
}
{
if (!PQexecStart(conn))
return NULL;
- if (!PQsendDescribe(conn, 'P', portal))
+ if (!PQsendTypedCommand(conn, 'D', 'P', portal))
return NULL;
return PQexecFinish(conn);
}
int
PQsendDescribePrepared(PGconn *conn, const char *stmt)
{
- return PQsendDescribe(conn, 'S', stmt);
+ return PQsendTypedCommand(conn, 'D', 'S', stmt);
}
/*
int
PQsendDescribePortal(PGconn *conn, const char *portal)
{
- return PQsendDescribe(conn, 'P', portal);
+ return PQsendTypedCommand(conn, 'D', 'P', portal);
}
/*
- * PQsendDescribe
- * Common code to send a Describe command
+ * PQclosePrepared
+ * Close a previously prepared statement
+ *
+ * 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). On success, the PGresult contains status
+ * PGRES_COMMAND_OK. The user is responsible for freeing the PGresult via
+ * PQclear() when done with it.
+ */
+PGresult *
+PQclosePrepared(PGconn *conn, const char *stmt)
+{
+ if (!PQexecStart(conn))
+ return NULL;
+ if (!PQsendTypedCommand(conn, 'C', 'S', stmt))
+ return NULL;
+ return PQexecFinish(conn);
+}
+
+/*
+ * PQclosePortal
+ * Close a previously created portal
+ *
+ * This is exactly like PQclosePrepared, but for portals. Note that at the
+ * moment, libpq doesn't really expose portals to the client; but this can be
+ * used with a portal created by a SQL DECLARE CURSOR command.
+ */
+PGresult *
+PQclosePortal(PGconn *conn, const char *portal)
+{
+ if (!PQexecStart(conn))
+ return NULL;
+ if (!PQsendTypedCommand(conn, 'C', 'P', portal))
+ return NULL;
+ return PQexecFinish(conn);
+}
+
+/*
+ * PQsendClosePrepared
+ * Submit a Close Statement command, but don't wait for it to finish
+ *
+ * Returns: 1 if successfully submitted
+ * 0 if error (conn->errorMessage is set)
+ */
+int
+PQsendClosePrepared(PGconn *conn, const char *stmt)
+{
+ return PQsendTypedCommand(conn, 'C', 'S', stmt);
+}
+
+/*
+ * PQsendClosePortal
+ * Submit a Close Portal command, but don't wait for it to finish
+ *
+ * Returns: 1 if successfully submitted
+ * 0 if error (conn->errorMessage is set)
+ */
+int
+PQsendClosePortal(PGconn *conn, const char *portal)
+{
+ return PQsendTypedCommand(conn, 'C', 'P', portal);
+}
+
+/*
+ * PQsendTypedCommand
+ * Common code to send a Describe or Close command
+ *
+ * Available options for "command" are
+ * 'C' for Close; or
+ * 'D' for Describe.
+ *
+ * Available options for "type" are
+ * 'S' to run a command on a prepared statement; or
+ * 'P' to run a command on a portal.
*
- * Available options for desc_type are
- * 'S' to describe a prepared statement; or
- * 'P' to describe a portal.
* Returns 1 on success and 0 on failure.
*/
static int
-PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
+PQsendTypedCommand(PGconn *conn, char command, char type, const char *target)
{
PGcmdQueueEntry *entry = NULL;
- /* Treat null desc_target as empty string */
- if (!desc_target)
- desc_target = "";
+ /* Treat null target as empty string */
+ if (!target)
+ target = "";
if (!PQsendQueryStart(conn, true))
return 0;
if (entry == NULL)
return 0; /* error msg already set */
- /* construct the Describe message */
- if (pqPutMsgStart('D', conn) < 0 ||
- pqPutc(desc_type, conn) < 0 ||
- pqPuts(desc_target, conn) < 0 ||
+ /* construct the Close message */
+ if (pqPutMsgStart(command, conn) < 0 ||
+ pqPutc(type, conn) < 0 ||
+ pqPuts(target, conn) < 0 ||
pqPutMsgEnd(conn) < 0)
goto sendFailed;
goto sendFailed;
}
- /* remember we are doing a Describe */
- entry->queryclass = PGQUERY_DESCRIBE;
+ /* remember if we are doing a Close or a Describe */
+ if (command == 'C')
+ {
+ entry->queryclass = PGQUERY_CLOSE;
+ }
+ else if (command == 'D')
+ {
+ entry->queryclass = PGQUERY_DESCRIBE;
+ }
+ else
+ {
+ libpq_append_conn_error(conn, "unknown command type provided");
+ goto sendFailed;
+ }
/*
* Give the data a push (in pipeline mode, only if we're past the size
}
break;
case '2': /* Bind Complete */
+ /* Nothing to do for this message type */
+ break;
case '3': /* Close Complete */
- /* Nothing to do for these message types */
+ /* If we're doing PQsendClose, we're done; else ignore */
+ if (conn->cmd_queue_head &&
+ conn->cmd_queue_head->queryclass == PGQUERY_CLOSE)
+ {
+ if (!pgHavePendingResult(conn))
+ {
+ conn->result = PQmakeEmptyPGresult(conn,
+ PGRES_COMMAND_OK);
+ if (!conn->result)
+ {
+ libpq_append_conn_error(conn, "out of memory");
+ pqSaveErrorResult(conn);
+ }
+ }
+ conn->asyncStatus = PGASYNC_READY;
+ }
break;
case 'S': /* parameter status */
if (getParameterStatus(conn))
extern int PQsendDescribePrepared(PGconn *conn, const char *stmt);
extern int PQsendDescribePortal(PGconn *conn, const char *portal);
+/* Close prepared statements and portals */
+extern PGresult *PQclosePrepared(PGconn *conn, const char *stmt);
+extern PGresult *PQclosePortal(PGconn *conn, const char *portal);
+extern int PQsendClosePrepared(PGconn *conn, const char *stmt);
+extern int PQsendClosePortal(PGconn *conn, const char *portal);
+
/* Delete a PGresult */
extern void PQclear(PGresult *res);
PGQUERY_PREPARE, /* Parse only (PQprepare) */
PGQUERY_DESCRIBE, /* Describe Statement or Portal */
PGQUERY_SYNC, /* Sync (at end of a pipeline) */
- PGQUERY_CLOSE
+ PGQUERY_CLOSE /* Close Statement or Portal */
} PGQueryClass;
/*
if (PQresultStatus(res) != PGRES_PIPELINE_SYNC)
pg_fatal("expected PGRES_PIPELINE_SYNC, got %s", PQresStatus(PQresultStatus(res)));
+ fprintf(stderr, "closing statement..");
+ if (PQsendClosePrepared(conn, "select_one") != 1)
+ pg_fatal("PQsendClosePrepared failed: %s", PQerrorMessage(conn));
+ if (PQpipelineSync(conn) != 1)
+ pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
+
+ res = PQgetResult(conn);
+ if (res == NULL)
+ pg_fatal("expected non-NULL result");
+ if (PQresultStatus(res) != PGRES_COMMAND_OK)
+ pg_fatal("expected COMMAND_OK, got %s", PQresStatus(PQresultStatus(res)));
+ PQclear(res);
+ res = PQgetResult(conn);
+ if (res != NULL)
+ pg_fatal("expected NULL result");
+ res = PQgetResult(conn);
+ if (PQresultStatus(res) != PGRES_PIPELINE_SYNC)
+ pg_fatal("expected PGRES_PIPELINE_SYNC, got %s", PQresStatus(PQresultStatus(res)));
+
if (PQexitPipelineMode(conn) != 1)
pg_fatal("could not exit pipeline mode: %s", PQerrorMessage(conn));
+ /* Now that it's closed we should get an error when describing */
+ res = PQdescribePrepared(conn, "select_one");
+ if (PQresultStatus(res) != PGRES_FATAL_ERROR)
+ pg_fatal("expected FATAL_ERROR, got %s", PQresStatus(PQresultStatus(res)));
+
+ /*
+ * Also test the blocking close, this should not fail since closing a
+ * non-existent prepared statement is a no-op
+ */
+ res = PQclosePrepared(conn, "select_one");
+ if (PQresultStatus(res) != PGRES_COMMAND_OK)
+ pg_fatal("expected COMMAND_OK, got %s", PQresStatus(PQresultStatus(res)));
+
+ fprintf(stderr, "creating portal... ");
PQexec(conn, "BEGIN");
PQexec(conn, "DECLARE cursor_one CURSOR FOR SELECT 1");
PQenterPipelineMode(conn);
if (PQresultStatus(res) != PGRES_PIPELINE_SYNC)
pg_fatal("expected PGRES_PIPELINE_SYNC, got %s", PQresStatus(PQresultStatus(res)));
+ fprintf(stderr, "closing portal... ");
+ if (PQsendClosePortal(conn, "cursor_one") != 1)
+ pg_fatal("PQsendClosePortal failed: %s", PQerrorMessage(conn));
+ if (PQpipelineSync(conn) != 1)
+ pg_fatal("pipeline sync failed: %s", PQerrorMessage(conn));
+
+ res = PQgetResult(conn);
+ if (res == NULL)
+ pg_fatal("expected non-NULL result");
+ if (PQresultStatus(res) != PGRES_COMMAND_OK)
+ pg_fatal("expected COMMAND_OK, got %s", PQresStatus(PQresultStatus(res)));
+ PQclear(res);
+ res = PQgetResult(conn);
+ if (res != NULL)
+ pg_fatal("expected NULL result");
+ res = PQgetResult(conn);
+ if (PQresultStatus(res) != PGRES_PIPELINE_SYNC)
+ pg_fatal("expected PGRES_PIPELINE_SYNC, got %s", PQresStatus(PQresultStatus(res)));
+
if (PQexitPipelineMode(conn) != 1)
pg_fatal("could not exit pipeline mode: %s", PQerrorMessage(conn));
+ /* Now that it's closed we should get an error when describing */
+ res = PQdescribePortal(conn, "cursor_one");
+ if (PQresultStatus(res) != PGRES_FATAL_ERROR)
+ pg_fatal("expected FATAL_ERROR, got %s", PQresStatus(PQresultStatus(res)));
+
+ /*
+ * Also test the blocking close, this should not fail since closing a
+ * non-existent portal is a no-op
+ */
+ res = PQclosePortal(conn, "cursor_one");
+ if (PQresultStatus(res) != PGRES_COMMAND_OK)
+ pg_fatal("expected COMMAND_OK, got %s", PQresStatus(PQresultStatus(res)));
+
fprintf(stderr, "ok\n");
}
B 10 ParameterDescription 1 NNNN
B 113 RowDescription 4 "?column?" NNNN 0 NNNN 4 -1 0 "?column?" NNNN 0 NNNN 65535 -1 0 "numeric" NNNN 0 NNNN 65535 -1 0 "interval" NNNN 0 NNNN 16 -1 0
B 5 ReadyForQuery I
+F 16 Close S "select_one"
+F 4 Sync
+B 4 CloseComplete
+B 5 ReadyForQuery I
+F 16 Describe S "select_one"
+F 4 Sync
+B NN ErrorResponse S "ERROR" V "ERROR" C "26000" M "prepared statement "select_one" does not exist" F "SSSS" L "SSSS" R "SSSS" \x00
+B 5 ReadyForQuery I
+F 16 Close S "select_one"
+F 4 Sync
+B 4 CloseComplete
+B 5 ReadyForQuery I
F 10 Query "BEGIN"
B 10 CommandComplete "BEGIN"
B 5 ReadyForQuery T
F 4 Sync
B 33 RowDescription 1 "?column?" NNNN 0 NNNN 4 -1 0
B 5 ReadyForQuery T
+F 16 Close P "cursor_one"
+F 4 Sync
+B 4 CloseComplete
+B 5 ReadyForQuery T
+F 16 Describe P "cursor_one"
+F 4 Sync
+B NN ErrorResponse S "ERROR" V "ERROR" C "34000" M "portal "cursor_one" does not exist" F "SSSS" L "SSSS" R "SSSS" \x00
+B 5 ReadyForQuery E
+F 16 Close P "cursor_one"
+F 4 Sync
+B 4 CloseComplete
+B 5 ReadyForQuery E
F 4 Terminate