v10-0002-Allow-SIGINT-to-cancel-psql-database-reconnectio.patch
text/x-patch
Filename: v10-0002-Allow-SIGINT-to-cancel-psql-database-reconnectio.patch
Type: text/x-patch
Part: 1
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v10-0002
Subject: Allow SIGINT to cancel psql database reconnections
| File | + | − |
|---|---|---|
| src/bin/psql/command.c | 71 | 1 |
From dc1eb3866975680b55451b79b40f109e3104eee8 Mon Sep 17 00:00:00 2001
From: Tristan Partin <tristan@neon.tech>
Date: Tue, 30 Jan 2024 15:53:10 -0600
Subject: [PATCH v10 2/2] Allow SIGINT to cancel psql database reconnections
After installing the SIGINT handler in psql, SIGINT can no longer cancel
database reconnections. For instance, if the user starts a reconnection
and then needs to do some form of interaction (ie psql is polling),
there is no way to cancel the reconnection process currently.
Use PQconnectStartParams() in order to insert a cancel_pressed check
into the polling loop.
---
src/bin/psql/command.c | 72 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 71 insertions(+), 1 deletion(-)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 9b0fa041f73..1e00b0d4869 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -159,6 +159,7 @@ static void discard_query_text(PsqlScanState scan_state, ConditionalStack cstack
static bool copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf);
static bool do_connect(enum trivalue reuse_previous_specification,
char *dbname, char *user, char *host, char *port);
+static void wait_until_connected(PGconn *conn);
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
int lineno, bool discard_on_quit, bool *edited);
static bool do_shell(const char *command);
@@ -3595,11 +3596,12 @@ do_connect(enum trivalue reuse_previous_specification,
values[paramnum] = NULL;
/* Note we do not want libpq to re-expand the dbname parameter */
- n_conn = PQconnectdbParams(keywords, values, false);
+ n_conn = PQconnectStartParams(keywords, values, false);
pg_free(keywords);
pg_free(values);
+ wait_until_connected(n_conn);
if (PQstatus(n_conn) == CONNECTION_OK)
break;
@@ -3748,6 +3750,74 @@ do_connect(enum trivalue reuse_previous_specification,
return true;
}
+/*
+ * Processes the connection sequence described by PQconnectStartParams(). Don't
+ * worry about reporting errors in this function. Our caller will check the
+ * connection's status, and report appropriately.
+ */
+static void
+wait_until_connected(PGconn *conn)
+{
+ bool forRead = false;
+
+ while (true)
+ {
+ int rc;
+ int sock;
+ time_t end_time;
+
+ /*
+ * On every iteration of the connection sequence, let's check if the
+ * user has requested a cancellation.
+ */
+ if (cancel_pressed)
+ break;
+
+ /*
+ * Do not assume that the socket remains the same across
+ * PQconnectPoll() calls.
+ */
+ sock = PQsocket(conn);
+ if (sock == -1)
+ break;
+
+ /*
+ * If the user sends SIGINT between the cancel_pressed check, and
+ * polling of the socket, it will not be recognized. Instead, we will
+ * just wait until the next step in the connection sequence or forever,
+ * which might require users to send SIGTERM or SIGQUIT.
+ *
+ * Some solutions would include the "self-pipe trick," using
+ * pselect(2) and ppoll(2), or using a timeout.
+ *
+ * The self-pipe trick requires a bit of code to setup. pselect(2) and
+ * ppoll(2) are not on all the platforms we support. The simplest
+ * solution happens to just be adding a timeout, so let's wait for 1
+ * second and check cancel_pressed again.
+ */
+ end_time = time(NULL) + 1;
+ rc = PQsocketPoll(sock, forRead, !forRead, end_time);
+ if (rc == -1)
+ return;
+
+ switch (PQconnectPoll(conn))
+ {
+ case PGRES_POLLING_OK:
+ case PGRES_POLLING_FAILED:
+ return;
+ case PGRES_POLLING_READING:
+ forRead = true;
+ continue;
+ case PGRES_POLLING_WRITING:
+ forRead = false;
+ continue;
+ case PGRES_POLLING_ACTIVE:
+ pg_unreachable();
+ }
+ }
+
+ pg_unreachable();
+}
void
connection_warnings(bool in_startup)
--
Tristan Partin
Neon (https://neon.tech)