mirror of
https://github.com/postgres/postgres.git
synced 2025-06-30 21:42:05 +03:00
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
This commit is contained in:
@ -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 copy_previous_query(PQExpBuffer query_buf, PQExpBuffer previous_buf);
|
||||||
static bool do_connect(enum trivalue reuse_previous_specification,
|
static bool do_connect(enum trivalue reuse_previous_specification,
|
||||||
char *dbname, char *user, char *host, char *port);
|
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,
|
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
|
||||||
int lineno, bool discard_on_quit, bool *edited);
|
int lineno, bool discard_on_quit, bool *edited);
|
||||||
static bool do_shell(const char *command);
|
static bool do_shell(const char *command);
|
||||||
@ -3595,11 +3596,12 @@ do_connect(enum trivalue reuse_previous_specification,
|
|||||||
values[paramnum] = NULL;
|
values[paramnum] = NULL;
|
||||||
|
|
||||||
/* Note we do not want libpq to re-expand the dbname parameter */
|
/* 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(keywords);
|
||||||
pg_free(values);
|
pg_free(values);
|
||||||
|
|
||||||
|
wait_until_connected(n_conn);
|
||||||
if (PQstatus(n_conn) == CONNECTION_OK)
|
if (PQstatus(n_conn) == CONNECTION_OK)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -3748,6 +3750,74 @@ do_connect(enum trivalue reuse_previous_specification,
|
|||||||
return true;
|
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
|
void
|
||||||
connection_warnings(bool in_startup)
|
connection_warnings(bool in_startup)
|
||||||
|
Reference in New Issue
Block a user