Skip to content

Commit cafe105

Browse files
committed
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. Tristan Partin, reviewed by Gurjeet Singh, Heikki Linnakangas, Jelte Fennema-Nio, and me. Discussion: http://postgr.es/m/D08WWCPVHKHN.3QELIKZJ2D9RZ@neon.tech
1 parent f5e4ded commit cafe105

File tree

1 file changed

+71
-1
lines changed

1 file changed

+71
-1
lines changed

src/bin/psql/command.c

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ static void discard_query_text(PsqlScanState scan_state, ConditionalStack cstack
159159
static bool copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf);
160160
static bool do_connect(enum trivalue reuse_previous_specification,
161161
char *dbname, char *user, char *host, char *port);
162+
static void wait_until_connected(PGconn *conn);
162163
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
163164
int lineno, bool discard_on_quit, bool *edited);
164165
static bool do_shell(const char *command);
@@ -3595,11 +3596,12 @@ do_connect(enum trivalue reuse_previous_specification,
35953596
values[paramnum] = NULL;
35963597

35973598
/* Note we do not want libpq to re-expand the dbname parameter */
3598-
n_conn = PQconnectdbParams(keywords, values, false);
3599+
n_conn = PQconnectStartParams(keywords, values, false);
35993600

36003601
pg_free(keywords);
36013602
pg_free(values);
36023603

3604+
wait_until_connected(n_conn);
36033605
if (PQstatus(n_conn) == CONNECTION_OK)
36043606
break;
36053607

@@ -3748,6 +3750,74 @@ do_connect(enum trivalue reuse_previous_specification,
37483750
return true;
37493751
}
37503752

3753+
/*
3754+
* Processes the connection sequence described by PQconnectStartParams(). Don't
3755+
* worry about reporting errors in this function. Our caller will check the
3756+
* connection's status, and report appropriately.
3757+
*/
3758+
static void
3759+
wait_until_connected(PGconn *conn)
3760+
{
3761+
bool forRead = false;
3762+
3763+
while (true)
3764+
{
3765+
int rc;
3766+
int sock;
3767+
time_t end_time;
3768+
3769+
/*
3770+
* On every iteration of the connection sequence, let's check if the
3771+
* user has requested a cancellation.
3772+
*/
3773+
if (cancel_pressed)
3774+
break;
3775+
3776+
/*
3777+
* Do not assume that the socket remains the same across
3778+
* PQconnectPoll() calls.
3779+
*/
3780+
sock = PQsocket(conn);
3781+
if (sock == -1)
3782+
break;
3783+
3784+
/*
3785+
* If the user sends SIGINT between the cancel_pressed check, and
3786+
* polling of the socket, it will not be recognized. Instead, we will
3787+
* just wait until the next step in the connection sequence or forever,
3788+
* which might require users to send SIGTERM or SIGQUIT.
3789+
*
3790+
* Some solutions would include the "self-pipe trick," using
3791+
* pselect(2) and ppoll(2), or using a timeout.
3792+
*
3793+
* The self-pipe trick requires a bit of code to setup. pselect(2) and
3794+
* ppoll(2) are not on all the platforms we support. The simplest
3795+
* solution happens to just be adding a timeout, so let's wait for 1
3796+
* second and check cancel_pressed again.
3797+
*/
3798+
end_time = time(NULL) + 1;
3799+
rc = PQsocketPoll(sock, forRead, !forRead, end_time);
3800+
if (rc == -1)
3801+
return;
3802+
3803+
switch (PQconnectPoll(conn))
3804+
{
3805+
case PGRES_POLLING_OK:
3806+
case PGRES_POLLING_FAILED:
3807+
return;
3808+
case PGRES_POLLING_READING:
3809+
forRead = true;
3810+
continue;
3811+
case PGRES_POLLING_WRITING:
3812+
forRead = false;
3813+
continue;
3814+
case PGRES_POLLING_ACTIVE:
3815+
pg_unreachable();
3816+
}
3817+
}
3818+
3819+
pg_unreachable();
3820+
}
37513821

37523822
void
37533823
connection_warnings(bool in_startup)

0 commit comments

Comments
 (0)