Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 28b5726

Browse files
committed
libpq: Add support for Close on portals and statements
The following routines are added to libpq: PGresult *PQclosePrepared(PGconn *conn, const char *stmt); PGresult *PQclosePortal(PGconn *conn, const char *portal); int PQsendClosePrepared(PGconn *conn, const char *stmt); int PQsendClosePortal(PGconn *conn, const char *portal); The "send" routines are non-blocking versions of the two others. Close messages are part of the protocol but they did not have a libpq implementation. And, having these routines is for instance useful with connection poolers as these can detect more easily Close messages than DEALLOCATE queries. The implementation takes advantage of what the Describe routines rely on for portals and statements. Some regression tests are added in libpq_pipeline, for the four new routines, by closing portals and statements created already by the tests. Author: Jelte Fennema Reviewed-by: Jian He, Michael Paquier Discussion: https://postgr.es/m/CAGECzQTb4xFAopAVokudB+L62Kt44mNAL4Z9zZ7UTrs1TRFvWA@mail.gmail.com
1 parent 03f80da commit 28b5726

File tree

8 files changed

+337
-32
lines changed

8 files changed

+337
-32
lines changed

doc/src/sgml/libpq.sgml

Lines changed: 116 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3250,10 +3250,7 @@ PGresult *PQprepare(PGconn *conn,
32503250

32513251
Prepared statements for use with <xref linkend="libpq-PQexecPrepared"/> can also
32523252
be created by executing SQL <xref linkend="sql-prepare"/>
3253-
statements. Also, although there is no <application>libpq</application>
3254-
function for deleting a prepared statement, the SQL <xref
3255-
linkend="sql-deallocate"/> statement
3256-
can be used for that purpose.
3253+
statements.
32573254
</para>
32583255

32593256
<para>
@@ -3360,6 +3357,66 @@ PGresult *PQdescribePortal(PGconn *conn, const char *portalName);
33603357
</para>
33613358
</listitem>
33623359
</varlistentry>
3360+
3361+
<varlistentry id="libpq-PQclosePrepared">
3362+
<term><function>PQclosePrepared</function><indexterm><primary>PQclosePrepared</primary></indexterm></term>
3363+
3364+
<listitem>
3365+
<para>
3366+
Submits a request to close the specified prepared statement, and waits
3367+
for completion.
3368+
<synopsis>
3369+
PGresult *PQclosePrepared(PGconn *conn, const char *stmtName);
3370+
</synopsis>
3371+
</para>
3372+
3373+
<para>
3374+
<xref linkend="libpq-PQclosePrepared"/> allows an application to close
3375+
a previously prepared statement. Closing a statement releases all
3376+
of its associated resources on the server and allows its name to be
3377+
reused.
3378+
</para>
3379+
3380+
<para>
3381+
<parameter>stmtName</parameter> can be <literal>""</literal> or
3382+
<symbol>NULL</symbol> to reference the unnamed statement. It is fine
3383+
if no statement exists with this name, in that case the operation is a
3384+
no-op. On success, a <structname>PGresult</structname> with
3385+
status <literal>PGRES_COMMAND_OK</literal> is returned.
3386+
</para>
3387+
</listitem>
3388+
</varlistentry>
3389+
3390+
<varlistentry id="libpq-PQclosePortal">
3391+
<term><function>PQclosePortal</function><indexterm><primary>PQclosePortal</primary></indexterm></term>
3392+
3393+
<listitem>
3394+
<para>
3395+
Submits a request to close the specified portal, and waits for
3396+
completion.
3397+
<synopsis>
3398+
PGresult *PQclosePortal(PGconn *conn, const char *portalName);
3399+
</synopsis>
3400+
</para>
3401+
3402+
<para>
3403+
<xref linkend="libpq-PQclosePortal"/> allows an application to trigger
3404+
a close of a previously created portal. Closing a portal releases all
3405+
of its associated resources on the server and allows its name to be
3406+
reused. (<application>libpq</application> does not provide any
3407+
direct access to portals, but you can use this function to close a
3408+
cursor created with a <command>DECLARE CURSOR</command> SQL command.)
3409+
</para>
3410+
3411+
<para>
3412+
<parameter>portalName</parameter> can be <literal>""</literal> or
3413+
<symbol>NULL</symbol> to reference the unnamed portal. It is fine
3414+
if no portal exists with this name, in that case the operation is a
3415+
no-op. On success, a <structname>PGresult</structname> with status
3416+
<literal>PGRES_COMMAND_OK</literal> is returned.
3417+
</para>
3418+
</listitem>
3419+
</varlistentry>
33633420
</variablelist>
33643421
</para>
33653422

@@ -4851,15 +4908,19 @@ unsigned char *PQunescapeBytea(const unsigned char *from, size_t *to_length);
48514908
<xref linkend="libpq-PQsendQueryParams"/>,
48524909
<xref linkend="libpq-PQsendPrepare"/>,
48534910
<xref linkend="libpq-PQsendQueryPrepared"/>,
4854-
<xref linkend="libpq-PQsendDescribePrepared"/>, and
4911+
<xref linkend="libpq-PQsendDescribePrepared"/>,
48554912
<xref linkend="libpq-PQsendDescribePortal"/>,
4913+
<xref linkend="libpq-PQsendClosePrepared"/>, and
4914+
<xref linkend="libpq-PQsendClosePortal"/>,
48564915
which can be used with <xref linkend="libpq-PQgetResult"/> to duplicate
48574916
the functionality of
48584917
<xref linkend="libpq-PQexecParams"/>,
48594918
<xref linkend="libpq-PQprepare"/>,
48604919
<xref linkend="libpq-PQexecPrepared"/>,
4861-
<xref linkend="libpq-PQdescribePrepared"/>, and
4920+
<xref linkend="libpq-PQdescribePrepared"/>,
48624921
<xref linkend="libpq-PQdescribePortal"/>
4922+
<xref linkend="libpq-PQclosePrepared"/>, and
4923+
<xref linkend="libpq-PQclosePortal"/>
48634924
respectively.
48644925

48654926
<variablelist>
@@ -5008,6 +5069,46 @@ int PQsendDescribePortal(PGconn *conn, const char *portalName);
50085069
</listitem>
50095070
</varlistentry>
50105071

5072+
<varlistentry id="libpq-PQsendClosePrepared">
5073+
<term><function>PQsendClosePrepared</function><indexterm><primary>PQsendClosePrepared</primary></indexterm></term>
5074+
5075+
<listitem>
5076+
<para>
5077+
Submits a request to close the specified prepared statement, without
5078+
waiting for completion.
5079+
<synopsis>
5080+
int PQsendClosePrepared(PGconn *conn, const char *stmtName);
5081+
</synopsis>
5082+
5083+
This is an asynchronous version of <xref linkend="libpq-PQclosePrepared"/>:
5084+
it returns 1 if it was able to dispatch the request, and 0 if not.
5085+
After a successful call, call <xref linkend="libpq-PQgetResult"/> to
5086+
obtain the results. The function's parameters are handled
5087+
identically to <xref linkend="libpq-PQclosePrepared"/>.
5088+
</para>
5089+
</listitem>
5090+
</varlistentry>
5091+
5092+
<varlistentry id="libpq-PQsendClosePortal">
5093+
<term><function>PQsendClosePortal</function><indexterm><primary>PQsendClosePortal</primary></indexterm></term>
5094+
5095+
<listitem>
5096+
<para>
5097+
Submits a request to close specified portal, without waiting for
5098+
completion.
5099+
<synopsis>
5100+
int PQsendClosePortal(PGconn *conn, const char *portalName);
5101+
</synopsis>
5102+
5103+
This is an asynchronous version of <xref linkend="libpq-PQclosePortal"/>:
5104+
it returns 1 if it was able to dispatch the request, and 0 if not.
5105+
After a successful call, call <xref linkend="libpq-PQgetResult"/> to
5106+
obtain the results. The function's parameters are handled
5107+
identically to <xref linkend="libpq-PQclosePortal"/>.
5108+
</para>
5109+
</listitem>
5110+
</varlistentry>
5111+
50115112
<varlistentry id="libpq-PQgetResult">
50125113
<term><function>PQgetResult</function><indexterm><primary>PQgetResult</primary></indexterm></term>
50135114

@@ -5019,7 +5120,9 @@ int PQsendDescribePortal(PGconn *conn, const char *portalName);
50195120
<xref linkend="libpq-PQsendPrepare"/>,
50205121
<xref linkend="libpq-PQsendQueryPrepared"/>,
50215122
<xref linkend="libpq-PQsendDescribePrepared"/>,
5022-
<xref linkend="libpq-PQsendDescribePortal"/>, or
5123+
<xref linkend="libpq-PQsendDescribePortal"/>,
5124+
<xref linkend="libpq-PQsendClosePrepared"/>,
5125+
<xref linkend="libpq-PQsendClosePortal"/>, or
50235126
<xref linkend="libpq-PQpipelineSync"/>
50245127
call, and returns it.
50255128
A null pointer is returned when the command is complete and there
@@ -5350,6 +5453,8 @@ int PQflush(PGconn *conn);
53505453
<function>PQexecPrepared</function>,
53515454
<function>PQdescribePrepared</function>,
53525455
<function>PQdescribePortal</function>,
5456+
<function>PQclosePrepared</function>,
5457+
<function>PQclosePortal</function>,
53535458
is an error condition.
53545459
<function>PQsendQuery</function> is
53555460
also disallowed, because it uses the simple query protocol.
@@ -5389,8 +5494,10 @@ int PQflush(PGconn *conn);
53895494
establish a synchronization point in the pipeline,
53905495
or when <xref linkend="libpq-PQflush"/> is called.
53915496
The functions <xref linkend="libpq-PQsendPrepare"/>,
5392-
<xref linkend="libpq-PQsendDescribePrepared"/>, and
5393-
<xref linkend="libpq-PQsendDescribePortal"/> also work in pipeline mode.
5497+
<xref linkend="libpq-PQsendDescribePrepared"/>,
5498+
<xref linkend="libpq-PQsendDescribePortal"/>,
5499+
<xref linkend="libpq-PQsendClosePrepared"/>, and
5500+
<xref linkend="libpq-PQsendClosePortal"/> also work in pipeline mode.
53945501
Result processing is described below.
53955502
</para>
53965503

src/interfaces/libpq/exports.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,7 @@ PQsetTraceFlags 184
187187
PQmblenBounded 185
188188
PQsendFlushRequest 186
189189
PQconnectionUsedGSSAPI 187
190+
PQclosePrepared 188
191+
PQclosePortal 189
192+
PQsendClosePrepared 190
193+
PQsendClosePortal 191

src/interfaces/libpq/fe-exec.c

Lines changed: 103 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ static void parseInput(PGconn *conn);
7777
static PGresult *getCopyResult(PGconn *conn, ExecStatusType copytype);
7878
static bool PQexecStart(PGconn *conn);
7979
static PGresult *PQexecFinish(PGconn *conn);
80-
static int PQsendDescribe(PGconn *conn, char desc_type,
81-
const char *desc_target);
80+
static int PQsendTypedCommand(PGconn *conn, char command, char type,
81+
const char *target);
8282
static int check_field_number(const PGresult *res, int field_num);
8383
static void pqPipelineProcessQueue(PGconn *conn);
8484
static int pqPipelineFlush(PGconn *conn);
@@ -2422,7 +2422,7 @@ PQdescribePrepared(PGconn *conn, const char *stmt)
24222422
{
24232423
if (!PQexecStart(conn))
24242424
return NULL;
2425-
if (!PQsendDescribe(conn, 'S', stmt))
2425+
if (!PQsendTypedCommand(conn, 'D', 'S', stmt))
24262426
return NULL;
24272427
return PQexecFinish(conn);
24282428
}
@@ -2441,7 +2441,7 @@ PQdescribePortal(PGconn *conn, const char *portal)
24412441
{
24422442
if (!PQexecStart(conn))
24432443
return NULL;
2444-
if (!PQsendDescribe(conn, 'P', portal))
2444+
if (!PQsendTypedCommand(conn, 'D', 'P', portal))
24452445
return NULL;
24462446
return PQexecFinish(conn);
24472447
}
@@ -2456,7 +2456,7 @@ PQdescribePortal(PGconn *conn, const char *portal)
24562456
int
24572457
PQsendDescribePrepared(PGconn *conn, const char *stmt)
24582458
{
2459-
return PQsendDescribe(conn, 'S', stmt);
2459+
return PQsendTypedCommand(conn, 'D', 'S', stmt);
24602460
}
24612461

24622462
/*
@@ -2469,26 +2469,96 @@ PQsendDescribePrepared(PGconn *conn, const char *stmt)
24692469
int
24702470
PQsendDescribePortal(PGconn *conn, const char *portal)
24712471
{
2472-
return PQsendDescribe(conn, 'P', portal);
2472+
return PQsendTypedCommand(conn, 'D', 'P', portal);
24732473
}
24742474

24752475
/*
2476-
* PQsendDescribe
2477-
* Common code to send a Describe command
2476+
* PQclosePrepared
2477+
* Close a previously prepared statement
2478+
*
2479+
* If the query was not even sent, return NULL; conn->errorMessage is set to
2480+
* a relevant message.
2481+
* If the query was sent, a new PGresult is returned (which could indicate
2482+
* either success or failure). On success, the PGresult contains status
2483+
* PGRES_COMMAND_OK. The user is responsible for freeing the PGresult via
2484+
* PQclear() when done with it.
2485+
*/
2486+
PGresult *
2487+
PQclosePrepared(PGconn *conn, const char *stmt)
2488+
{
2489+
if (!PQexecStart(conn))
2490+
return NULL;
2491+
if (!PQsendTypedCommand(conn, 'C', 'S', stmt))
2492+
return NULL;
2493+
return PQexecFinish(conn);
2494+
}
2495+
2496+
/*
2497+
* PQclosePortal
2498+
* Close a previously created portal
2499+
*
2500+
* This is exactly like PQclosePrepared, but for portals. Note that at the
2501+
* moment, libpq doesn't really expose portals to the client; but this can be
2502+
* used with a portal created by a SQL DECLARE CURSOR command.
2503+
*/
2504+
PGresult *
2505+
PQclosePortal(PGconn *conn, const char *portal)
2506+
{
2507+
if (!PQexecStart(conn))
2508+
return NULL;
2509+
if (!PQsendTypedCommand(conn, 'C', 'P', portal))
2510+
return NULL;
2511+
return PQexecFinish(conn);
2512+
}
2513+
2514+
/*
2515+
* PQsendClosePrepared
2516+
* Submit a Close Statement command, but don't wait for it to finish
2517+
*
2518+
* Returns: 1 if successfully submitted
2519+
* 0 if error (conn->errorMessage is set)
2520+
*/
2521+
int
2522+
PQsendClosePrepared(PGconn *conn, const char *stmt)
2523+
{
2524+
return PQsendTypedCommand(conn, 'C', 'S', stmt);
2525+
}
2526+
2527+
/*
2528+
* PQsendClosePortal
2529+
* Submit a Close Portal command, but don't wait for it to finish
2530+
*
2531+
* Returns: 1 if successfully submitted
2532+
* 0 if error (conn->errorMessage is set)
2533+
*/
2534+
int
2535+
PQsendClosePortal(PGconn *conn, const char *portal)
2536+
{
2537+
return PQsendTypedCommand(conn, 'C', 'P', portal);
2538+
}
2539+
2540+
/*
2541+
* PQsendTypedCommand
2542+
* Common code to send a Describe or Close command
2543+
*
2544+
* Available options for "command" are
2545+
* 'C' for Close; or
2546+
* 'D' for Describe.
2547+
*
2548+
* Available options for "type" are
2549+
* 'S' to run a command on a prepared statement; or
2550+
* 'P' to run a command on a portal.
24782551
*
2479-
* Available options for desc_type are
2480-
* 'S' to describe a prepared statement; or
2481-
* 'P' to describe a portal.
24822552
* Returns 1 on success and 0 on failure.
24832553
*/
24842554
static int
2485-
PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
2555+
PQsendTypedCommand(PGconn *conn, char command, char type, const char *target)
24862556
{
24872557
PGcmdQueueEntry *entry = NULL;
24882558

2489-
/* Treat null desc_target as empty string */
2490-
if (!desc_target)
2491-
desc_target = "";
2559+
/* Treat null target as empty string */
2560+
if (!target)
2561+
target = "";
24922562

24932563
if (!PQsendQueryStart(conn, true))
24942564
return 0;
@@ -2497,10 +2567,10 @@ PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
24972567
if (entry == NULL)
24982568
return 0; /* error msg already set */
24992569

2500-
/* construct the Describe message */
2501-
if (pqPutMsgStart('D', conn) < 0 ||
2502-
pqPutc(desc_type, conn) < 0 ||
2503-
pqPuts(desc_target, conn) < 0 ||
2570+
/* construct the Close message */
2571+
if (pqPutMsgStart(command, conn) < 0 ||
2572+
pqPutc(type, conn) < 0 ||
2573+
pqPuts(target, conn) < 0 ||
25042574
pqPutMsgEnd(conn) < 0)
25052575
goto sendFailed;
25062576

@@ -2512,8 +2582,20 @@ PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
25122582
goto sendFailed;
25132583
}
25142584

2515-
/* remember we are doing a Describe */
2516-
entry->queryclass = PGQUERY_DESCRIBE;
2585+
/* remember if we are doing a Close or a Describe */
2586+
if (command == 'C')
2587+
{
2588+
entry->queryclass = PGQUERY_CLOSE;
2589+
}
2590+
else if (command == 'D')
2591+
{
2592+
entry->queryclass = PGQUERY_DESCRIBE;
2593+
}
2594+
else
2595+
{
2596+
libpq_append_conn_error(conn, "unknown command type provided");
2597+
goto sendFailed;
2598+
}
25172599

25182600
/*
25192601
* Give the data a push (in pipeline mode, only if we're past the size

src/interfaces/libpq/fe-protocol3.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,8 +278,25 @@ pqParseInput3(PGconn *conn)
278278
}
279279
break;
280280
case '2': /* Bind Complete */
281+
/* Nothing to do for this message type */
282+
break;
281283
case '3': /* Close Complete */
282-
/* Nothing to do for these message types */
284+
/* If we're doing PQsendClose, we're done; else ignore */
285+
if (conn->cmd_queue_head &&
286+
conn->cmd_queue_head->queryclass == PGQUERY_CLOSE)
287+
{
288+
if (!pgHavePendingResult(conn))
289+
{
290+
conn->result = PQmakeEmptyPGresult(conn,
291+
PGRES_COMMAND_OK);
292+
if (!conn->result)
293+
{
294+
libpq_append_conn_error(conn, "out of memory");
295+
pqSaveErrorResult(conn);
296+
}
297+
}
298+
conn->asyncStatus = PGASYNC_READY;
299+
}
283300
break;
284301
case 'S': /* parameter status */
285302
if (getParameterStatus(conn))

0 commit comments

Comments
 (0)