diff options
| author | Michael Paquier | 2023-07-04 05:48:10 +0000 |
|---|---|---|
| committer | Michael Paquier | 2023-07-04 05:48:10 +0000 |
| commit | 28b5726561841556dc3e00ffe26b01a8107ee654 (patch) | |
| tree | 79a4dad5f2ebae14b3ed650b367a79d7a1f23964 /src/interfaces/libpq | |
| parent | 03f80daac8cff257294108caf908fe0397c40e1a (diff) | |
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
Diffstat (limited to 'src/interfaces/libpq')
| -rw-r--r-- | src/interfaces/libpq/exports.txt | 4 | ||||
| -rw-r--r-- | src/interfaces/libpq/fe-exec.c | 124 | ||||
| -rw-r--r-- | src/interfaces/libpq/fe-protocol3.c | 19 | ||||
| -rw-r--r-- | src/interfaces/libpq/libpq-fe.h | 6 | ||||
| -rw-r--r-- | src/interfaces/libpq/libpq-int.h | 2 |
5 files changed, 132 insertions, 23 deletions
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt index 7ded77aff37..850734ac96c 100644 --- a/src/interfaces/libpq/exports.txt +++ b/src/interfaces/libpq/exports.txt @@ -187,3 +187,7 @@ PQsetTraceFlags 184 PQmblenBounded 185 PQsendFlushRequest 186 PQconnectionUsedGSSAPI 187 +PQclosePrepared 188 +PQclosePortal 189 +PQsendClosePrepared 190 +PQsendClosePortal 191 diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 14d706efd57..01f8efabbef 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -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 diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index 32b66d561cb..7bc6355d17f 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -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)) diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h index 7476dbe0e90..97762d56f5d 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -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); diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index b70d4aee6a1..9d63472c35b 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -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; /* |
