diff options
Diffstat (limited to 'src/backend/tcop/postgres.c')
-rw-r--r-- | src/backend/tcop/postgres.c | 166 |
1 files changed, 112 insertions, 54 deletions
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 8f743536cff..b82c3b333bd 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -331,6 +331,8 @@ SocketBackend(StringInfo inBuf) /* * Get message type code from the frontend. */ + HOLD_CANCEL_INTERRUPTS(); + pq_startmsgread(); qtype = pq_getbyte(); if (qtype == EOF) /* frontend disconnected */ @@ -379,7 +381,7 @@ SocketBackend(StringInfo inBuf) { /* * Can't send DEBUG log messages to client at this - * point.Since we're disconnecting right away, we + * point. Since we're disconnecting right away, we * don't need to restore whereToSendOutput. */ whereToSendOutput = DestNone; @@ -393,8 +395,30 @@ SocketBackend(StringInfo inBuf) break; case 'F': /* fastpath function call */ - /* we let fastpath.c cope with old-style input of this */ doing_extended_query_message = false; + if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) + { + if (GetOldFunctionMessage(inBuf)) + { + if (IsTransactionState()) + ereport(COMMERROR, + (errcode(ERRCODE_CONNECTION_FAILURE), + errmsg("unexpected EOF on client connection with an open transaction"))); + else + { + /* + * Can't send DEBUG log messages to client at this + * point. Since we're disconnecting right away, we + * don't need to restore whereToSendOutput. + */ + whereToSendOutput = DestNone; + ereport(DEBUG1, + (errcode(ERRCODE_CONNECTION_DOES_NOT_EXIST), + errmsg("unexpected EOF on client connection"))); + } + return EOF; + } + } break; case 'X': /* terminate */ @@ -462,6 +486,9 @@ SocketBackend(StringInfo inBuf) if (pq_getmessage(inBuf, 0)) return EOF; /* suitable message already logged */ } + else + pq_endmsgread(); + RESUME_CANCEL_INTERRUPTS(); return qtype; } @@ -506,7 +533,7 @@ prepare_for_client_read(void) EnableNotifyInterrupt(); EnableCatchupInterrupt(); - /* Allow cancel/die interrupts to be processed while waiting */ + /* Allow die interrupts to be processed while waiting */ ImmediateInterruptOK = true; /* And don't forget to detect one that already arrived */ @@ -2589,21 +2616,11 @@ die(SIGNAL_ARGS) ProcDiePending = true; /* - * If it's safe to interrupt, and we're waiting for input or a lock, - * service the interrupt immediately + * If we're waiting for input or a lock so that it's safe to + * interrupt, service the interrupt immediately */ - if (ImmediateInterruptOK && InterruptHoldoffCount == 0 && - CritSectionCount == 0) - { - /* bump holdoff count to make ProcessInterrupts() a no-op */ - /* until we are done getting ready for it */ - InterruptHoldoffCount++; - LockErrorCleanup(); /* prevent CheckDeadLock from running */ - DisableNotifyInterrupt(); - DisableCatchupInterrupt(); - InterruptHoldoffCount--; + if (ImmediateInterruptOK) ProcessInterrupts(); - } } /* If we're still here, waken anything waiting on the process latch */ @@ -2630,21 +2647,11 @@ StatementCancelHandler(SIGNAL_ARGS) QueryCancelPending = true; /* - * If it's safe to interrupt, and we're waiting for input or a lock, - * service the interrupt immediately + * If we're waiting for input or a lock so that it's safe to + * interrupt, service the interrupt immediately */ - if (ImmediateInterruptOK && InterruptHoldoffCount == 0 && - CritSectionCount == 0) - { - /* bump holdoff count to make ProcessInterrupts() a no-op */ - /* until we are done getting ready for it */ - InterruptHoldoffCount++; - LockErrorCleanup(); /* prevent CheckDeadLock from running */ - DisableNotifyInterrupt(); - DisableCatchupInterrupt(); - InterruptHoldoffCount--; + if (ImmediateInterruptOK) ProcessInterrupts(); - } } /* If we're still here, waken anything waiting on the process latch */ @@ -2787,21 +2794,11 @@ RecoveryConflictInterrupt(ProcSignalReason reason) RecoveryConflictRetryable = false; /* - * If it's safe to interrupt, and we're waiting for input or a lock, - * service the interrupt immediately + * If we're waiting for input or a lock so that it's safe to + * interrupt, service the interrupt immediately. */ - if (ImmediateInterruptOK && InterruptHoldoffCount == 0 && - CritSectionCount == 0) - { - /* bump holdoff count to make ProcessInterrupts() a no-op */ - /* until we are done getting ready for it */ - InterruptHoldoffCount++; - LockErrorCleanup(); /* prevent CheckDeadLock from running */ - DisableNotifyInterrupt(); - DisableCatchupInterrupt(); - InterruptHoldoffCount--; + if (ImmediateInterruptOK) ProcessInterrupts(); - } } /* @@ -2826,15 +2823,17 @@ RecoveryConflictInterrupt(ProcSignalReason reason) void ProcessInterrupts(void) { - /* OK to accept interrupt now? */ + /* OK to accept any interrupts now? */ if (InterruptHoldoffCount != 0 || CritSectionCount != 0) return; InterruptPending = false; + if (ProcDiePending) { ProcDiePending = false; QueryCancelPending = false; /* ProcDie trumps QueryCancel */ ImmediateInterruptOK = false; /* not idle anymore */ + LockErrorCleanup(); DisableNotifyInterrupt(); DisableCatchupInterrupt(); /* As in quickdie, don't risk sending to client during auth */ @@ -2871,6 +2870,7 @@ ProcessInterrupts(void) { QueryCancelPending = false; /* lost connection trumps QueryCancel */ ImmediateInterruptOK = false; /* not idle anymore */ + LockErrorCleanup(); DisableNotifyInterrupt(); DisableCatchupInterrupt(); /* don't send to client, we already know the connection to be dead. */ @@ -2879,12 +2879,53 @@ ProcessInterrupts(void) (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("connection to client lost"))); } + + /* + * If a recovery conflict happens while we are waiting for input from the + * client, the client is presumably just sitting idle in a transaction, + * preventing recovery from making progress. Terminate the connection to + * dislodge it. + */ + if (RecoveryConflictPending && DoingCommandRead) + { + QueryCancelPending = false; /* this trumps QueryCancel */ + ImmediateInterruptOK = false; /* not idle anymore */ + RecoveryConflictPending = false; + LockErrorCleanup(); + DisableNotifyInterrupt(); + DisableCatchupInterrupt(); + pgstat_report_recovery_conflict(RecoveryConflictReason); + ereport(FATAL, + (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), + errmsg("terminating connection due to conflict with recovery"), + errdetail_recovery_conflict(), + errhint("In a moment you should be able to reconnect to the" + " database and repeat your command."))); + } + if (QueryCancelPending) { + /* + * Don't allow query cancel interrupts while reading input from the + * client, because we might lose sync in the FE/BE protocol. (Die + * interrupts are OK, because we won't read any further messages from + * the client in that case.) + */ + if (QueryCancelHoldoffCount != 0) + { + /* + * Re-arm InterruptPending so that we process the cancel request + * as soon as we're done reading the message. + */ + InterruptPending = true; + return; + } + QueryCancelPending = false; if (ClientAuthInProgress) { ImmediateInterruptOK = false; /* not idle anymore */ + LockErrorCleanup(); DisableNotifyInterrupt(); DisableCatchupInterrupt(); /* As in quickdie, don't risk sending to client during auth */ @@ -2903,6 +2944,7 @@ ProcessInterrupts(void) { ImmediateInterruptOK = false; /* not idle anymore */ (void) get_timeout_indicator(STATEMENT_TIMEOUT, true); + LockErrorCleanup(); DisableNotifyInterrupt(); DisableCatchupInterrupt(); ereport(ERROR, @@ -2912,6 +2954,7 @@ ProcessInterrupts(void) if (get_timeout_indicator(STATEMENT_TIMEOUT, true)) { ImmediateInterruptOK = false; /* not idle anymore */ + LockErrorCleanup(); DisableNotifyInterrupt(); DisableCatchupInterrupt(); ereport(ERROR, @@ -2921,6 +2964,7 @@ ProcessInterrupts(void) if (IsAutoVacuumWorkerProcess()) { ImmediateInterruptOK = false; /* not idle anymore */ + LockErrorCleanup(); DisableNotifyInterrupt(); DisableCatchupInterrupt(); ereport(ERROR, @@ -2931,21 +2975,14 @@ ProcessInterrupts(void) { ImmediateInterruptOK = false; /* not idle anymore */ RecoveryConflictPending = false; + LockErrorCleanup(); DisableNotifyInterrupt(); DisableCatchupInterrupt(); pgstat_report_recovery_conflict(RecoveryConflictReason); - if (DoingCommandRead) - ereport(FATAL, - (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), - errmsg("terminating connection due to conflict with recovery"), - errdetail_recovery_conflict(), - errhint("In a moment you should be able to reconnect to the" - " database and repeat your command."))); - else - ereport(ERROR, - (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), + ereport(ERROR, + (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("canceling statement due to conflict with recovery"), - errdetail_recovery_conflict())); + errdetail_recovery_conflict())); } /* @@ -2956,6 +2993,7 @@ ProcessInterrupts(void) if (!DoingCommandRead) { ImmediateInterruptOK = false; /* not idle anymore */ + LockErrorCleanup(); DisableNotifyInterrupt(); DisableCatchupInterrupt(); ereport(ERROR, @@ -3862,6 +3900,19 @@ PostgresMain(int argc, char *argv[], /* We don't have a transaction command open anymore */ xact_started = false; + /* + * If an error occurred while we were reading a message from the + * client, we have potentially lost track of where the previous + * message ends and the next one begins. Even though we have + * otherwise recovered from the error, we cannot safely read any more + * messages from the client, so there isn't much we can do with the + * connection anymore. + */ + if (pq_is_reading_msg()) + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("terminating connection because protocol sync was lost"))); + /* Now we can allow interrupts again */ RESUME_INTERRUPTS(); } @@ -3946,7 +3997,14 @@ PostgresMain(int argc, char *argv[], /* * (4) disable async signal conditions again. + * + * Query cancel is supposed to be a no-op when there is no query in + * progress, so if a query cancel arrived while we were idle, just + * reset QueryCancelPending. ProcessInterrupts() has that effect when + * it's called when DoingCommandRead is set, so check for interrupts + * before resetting DoingCommandRead. */ + CHECK_FOR_INTERRUPTS(); DoingCommandRead = false; /* |