Commit 82170c74 authored by Tom Lane's avatar Tom Lane

Fix (some of the) breakage introduced into query-cancel processing by HS.

It is absolutely not okay to throw an ereport(ERROR) in any random place in
the code just because DoingCommandRead is set; interrupting, say, OpenSSL
in the midst of its activities is guaranteed to result in heartache.

Instead of that, undo the original optimizations that threw away
QueryCancelPending anytime we were starting or finishing a command read, and
instead discard the cancel request within ProcessInterrupts if we find that
there is no HS reason for forcing a cancel and we are DoingCommandRead.

In passing, may I once again condemn the practice of changing the code
and not fixing the adjacent comment that you just turned into a lie?
parent 6fb79119
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.580 2010/01/02 16:57:52 momjian Exp $ * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.581 2010/01/07 16:29:58 tgl Exp $
* *
* NOTES * NOTES
* this is the "main" module of the postgres backend and * this is the "main" module of the postgres backend and
...@@ -476,11 +476,10 @@ prepare_for_client_read(void) ...@@ -476,11 +476,10 @@ prepare_for_client_read(void)
EnableNotifyInterrupt(); EnableNotifyInterrupt();
EnableCatchupInterrupt(); EnableCatchupInterrupt();
/* Allow "die" interrupt to be processed while waiting */ /* Allow cancel/die interrupts to be processed while waiting */
ImmediateInterruptOK = true; ImmediateInterruptOK = true;
/* And don't forget to detect one that already arrived */ /* And don't forget to detect one that already arrived */
QueryCancelPending = false;
CHECK_FOR_INTERRUPTS(); CHECK_FOR_INTERRUPTS();
} }
} }
...@@ -494,7 +493,6 @@ client_read_ended(void) ...@@ -494,7 +493,6 @@ client_read_ended(void)
if (DoingCommandRead) if (DoingCommandRead)
{ {
ImmediateInterruptOK = false; ImmediateInterruptOK = false;
QueryCancelPending = false; /* forget any CANCEL signal */
DisableNotifyInterrupt(); DisableNotifyInterrupt();
DisableCatchupInterrupt(); DisableCatchupInterrupt();
...@@ -2640,12 +2638,11 @@ StatementCancelHandler(SIGNAL_ARGS) ...@@ -2640,12 +2638,11 @@ StatementCancelHandler(SIGNAL_ARGS)
QueryCancelPending = true; QueryCancelPending = true;
/* /*
* If it's safe to interrupt, and we're waiting for a lock, service * If it's safe to interrupt, and we're waiting for input or a lock,
* the interrupt immediately. No point in interrupting if we're * service the interrupt immediately
* waiting for input, however.
*/ */
if (InterruptHoldoffCount == 0 && CritSectionCount == 0 && if (ImmediateInterruptOK && InterruptHoldoffCount == 0 &&
(DoingCommandRead || ImmediateInterruptOK)) CritSectionCount == 0)
{ {
/* bump holdoff count to make ProcessInterrupts() a no-op */ /* bump holdoff count to make ProcessInterrupts() a no-op */
/* until we are done getting ready for it */ /* until we are done getting ready for it */
...@@ -2717,25 +2714,36 @@ ProcessInterrupts(void) ...@@ -2717,25 +2714,36 @@ ProcessInterrupts(void)
if (QueryCancelPending) if (QueryCancelPending)
{ {
QueryCancelPending = false; QueryCancelPending = false;
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
DisableCatchupInterrupt();
/* As in quickdie, don't risk sending to client during auth */
if (ClientAuthInProgress && whereToSendOutput == DestRemote)
whereToSendOutput = DestNone;
if (ClientAuthInProgress) if (ClientAuthInProgress)
{
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
DisableCatchupInterrupt();
/* As in quickdie, don't risk sending to client during auth */
if (whereToSendOutput == DestRemote)
whereToSendOutput = DestNone;
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED), (errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling authentication due to timeout"))); errmsg("canceling authentication due to timeout")));
else if (cancel_from_timeout) }
if (cancel_from_timeout)
{
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
DisableCatchupInterrupt();
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED), (errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling statement due to statement timeout"))); errmsg("canceling statement due to statement timeout")));
else if (IsAutoVacuumWorkerProcess()) }
if (IsAutoVacuumWorkerProcess())
{
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
DisableCatchupInterrupt();
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED), (errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling autovacuum task"))); errmsg("canceling autovacuum task")));
else }
{ {
int cancelMode = MyProc->recoveryConflictMode; int cancelMode = MyProc->recoveryConflictMode;
...@@ -2756,34 +2764,50 @@ ProcessInterrupts(void) ...@@ -2756,34 +2764,50 @@ ProcessInterrupts(void)
switch (cancelMode) switch (cancelMode)
{ {
case CONFLICT_MODE_FATAL: case CONFLICT_MODE_FATAL:
Assert(RecoveryInProgress()); ImmediateInterruptOK = false; /* not idle anymore */
ereport(FATAL, DisableNotifyInterrupt();
DisableCatchupInterrupt();
Assert(RecoveryInProgress());
ereport(FATAL,
(errcode(ERRCODE_QUERY_CANCELED), (errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling session due to conflict with recovery"))); errmsg("canceling session due to conflict with recovery")));
case CONFLICT_MODE_ERROR: case CONFLICT_MODE_ERROR:
/* /*
* We are aborting because we need to release * We are aborting because we need to release
* locks. So we need to abort out of all * locks. So we need to abort out of all
* subtransactions to make sure we release * subtransactions to make sure we release
* all locks at whatever their level. * all locks at whatever their level.
* *
* XXX Should we try to examine the * XXX Should we try to examine the
* transaction tree and cancel just enough * transaction tree and cancel just enough
* subxacts to remove locks? Doubt it. * subxacts to remove locks? Doubt it.
*/ */
Assert(RecoveryInProgress()); ImmediateInterruptOK = false; /* not idle anymore */
AbortOutOfAnyTransaction(); DisableNotifyInterrupt();
ereport(ERROR, DisableCatchupInterrupt();
Assert(RecoveryInProgress());
AbortOutOfAnyTransaction();
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED), (errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling statement due to conflict with recovery"))); errmsg("canceling statement due to conflict with recovery")));
return;
default: default:
/* No conflict pending, so fall through */ /* No conflict pending, so fall through */
break; break;
} }
}
/*
* If we are reading a command from the client, just ignore the
* cancel request --- sending an extra error message won't
* accomplish anything. Otherwise, go ahead and throw the error.
*/
if (!DoingCommandRead)
{
ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt();
DisableCatchupInterrupt();
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED), (errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling statement due to user request"))); errmsg("canceling statement due to user request")));
...@@ -3626,7 +3650,6 @@ PostgresMain(int argc, char *argv[], const char *username) ...@@ -3626,7 +3650,6 @@ PostgresMain(int argc, char *argv[], const char *username)
* conditional since we don't want, say, reads on behalf of COPY FROM * conditional since we don't want, say, reads on behalf of COPY FROM
* STDIN doing the same thing.) * STDIN doing the same thing.)
*/ */
QueryCancelPending = false; /* forget any earlier CANCEL signal */
DoingCommandRead = true; DoingCommandRead = true;
/* /*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment