1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-28 18:48:04 +03:00

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
This commit is contained in:
Michael Paquier
2023-07-04 14:48:10 +09:00
parent 03f80daac8
commit 28b5726561
8 changed files with 337 additions and 32 deletions

View File

@@ -187,3 +187,7 @@ PQsetTraceFlags 184
PQmblenBounded 185
PQsendFlushRequest 186
PQconnectionUsedGSSAPI 187
PQclosePrepared 188
PQclosePortal 189
PQsendClosePrepared 190
PQsendClosePortal 191

View File

@@ -77,8 +77,8 @@ static void parseInput(PGconn *conn);
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);
@@ -2422,7 +2422,7 @@ PQdescribePrepared(PGconn *conn, const char *stmt)
{
if (!PQexecStart(conn))
return NULL;
if (!PQsendDescribe(conn, 'S', stmt))
if (!PQsendTypedCommand(conn, 'D', 'S', stmt))
return NULL;
return PQexecFinish(conn);
}
@@ -2441,7 +2441,7 @@ PQdescribePortal(PGconn *conn, const char *portal)
{
if (!PQexecStart(conn))
return NULL;
if (!PQsendDescribe(conn, 'P', portal))
if (!PQsendTypedCommand(conn, 'D', 'P', portal))
return NULL;
return PQexecFinish(conn);
}
@@ -2456,7 +2456,7 @@ PQdescribePortal(PGconn *conn, const char *portal)
int
PQsendDescribePrepared(PGconn *conn, const char *stmt)
{
return PQsendDescribe(conn, 'S', stmt);
return PQsendTypedCommand(conn, 'D', 'S', stmt);
}
/*
@@ -2469,26 +2469,96 @@ PQsendDescribePrepared(PGconn *conn, const char *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;
@@ -2497,10 +2567,10 @@ PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
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;
@@ -2512,8 +2582,20 @@ PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
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

View File

@@ -278,8 +278,25 @@ pqParseInput3(PGconn *conn)
}
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))

View File

@@ -548,6 +548,12 @@ extern PGresult *PQdescribePortal(PGconn *conn, const char *portal);
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);

View File

@@ -324,7 +324,7 @@ typedef enum
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;
/*