Merge branch 'recoveryinfra' into dev_hot_standby
authorSimon Riggs <sriggs@ebony.2ndQuadrant>
Wed, 25 Feb 2009 13:04:37 +0000 (13:04 +0000)
committerSimon Riggs <sriggs@ebony.2ndQuadrant>
Wed, 25 Feb 2009 13:04:37 +0000 (13:04 +0000)
Conflicts:

src/backend/access/transam/xlog.c
src/backend/postmaster/postmaster.c
src/include/access/xlog.h

Further changes to replace IsRecoveryProcessingMode() with RecoveryInProgress().

17 files changed:
1  2 
doc/src/sgml/config.sgml
src/backend/access/heap/pruneheap.c
src/backend/access/transam/multixact.c
src/backend/access/transam/xact.c
src/backend/access/transam/xlog.c
src/backend/commands/discard.c
src/backend/postmaster/postmaster.c
src/backend/storage/ipc/procarray.c
src/backend/storage/lmgr/lock.c
src/backend/storage/lmgr/proc.c
src/backend/tcop/postgres.c
src/backend/tcop/utility.c
src/backend/utils/adt/txid.c
src/backend/utils/init/postinit.c
src/backend/utils/time/tqual.c
src/include/access/xlog.h
src/include/storage/proc.h

Simple merge
index 8c8bbd83557700fdae0b8599b64ba51c558af0ca,2691666e39ced2c6a84e61837a91d7898c5d4e9a..00eb502a4ab212e46476f897bb8735b62f99496a
@@@ -85,14 -84,6 +85,14 @@@ heap_page_prune_opt(Relation relation, 
        if (!PageIsPrunable(page, OldestXmin))
                return;
  
-       if (IsRecoveryProcessingMode())
 +      /*
 +       * We can't write WAL in recovery mode, so there's no point trying to
 +       * clean the page. The master will likely issue a cleaning WAL record
 +       * soon anyway, so this is no particular loss.
 +       */
++      if (RecoveryInProgress())
 +              return;
 +
        /*
         * We prune when a previous UPDATE failed to find enough space on the page
         * for a new tuple version, or when free space falls below the relation's
index f33c7fa91d030d5e4cffcb75840d99a61e84b710,a36d049ca7f1a8994a99e8a8c386f464e536ba67..d02442249d638bec2bc6db05aabbc3d13e275523
@@@ -1544,9 -1544,8 +1544,9 @@@ CheckPointMultiXact(void
         * isn't valid (because StartupMultiXact hasn't been called yet) and so
         * SimpleLruTruncate would get confused.  It seems best not to risk
         * removing any data during recovery anyway, so don't truncate.
 +       * We are executing in the bgwriter, so we must access shared status.
         */
-       if (!IsRecoveryProcessingMode())
+       if (!RecoveryInProgress())
                TruncateMultiXact();
  
        TRACE_POSTGRESQL_MULTIXACT_CHECKPOINT_DONE(true);
index 12fec8e47758c9be0cd3a8c733a57e6201385dae,3654206c6bbd69dd4296ad5afd9b5b276c41f0b1..2fea2460b39f612ef8ebf97299d80b4bec8f0a91
@@@ -1627,7 -1528,6 +1627,7 @@@ StartTransaction(void
        s->childXids = NULL;
        s->nChildXids = 0;
        s->maxChildXids = 0;
-       s->startedInRecovery = IsRecoveryProcessingMode();
++      s->startedInRecovery = RecoveryInProgress();
        GetUserIdAndContext(&s->prevUser, &s->prevSecDefCxt);
        /* SecurityDefinerContext should never be set outside a transaction */
        Assert(!s->prevSecDefCxt);
index 9c58231e9470d61c74114e9037d5161614da8182,bc8372ce3a1522f6db1dd4efc945e7b231bcc2a7..bd07ad2b357642c856693aa20aece9c4232a6146
@@@ -138,19 -134,14 +139,19 @@@ TimeLineID      ThisTimeLineID = 0
  bool          InRecovery = false;
  
  /* Are we recovering using offline XLOG archives? */
 -static bool InArchiveRecovery = false;
 +bool          InArchiveRecovery = false;
 +
 +static        XLogRecPtr      LastRec;
  
  /*
-  * Local copy of shared RecoveryProcessingMode variable. True actually
-  * means "not known, need to check the shared state"
+  * Local copy of SharedRecoveryInProgress variable. True actually means "not
+  * known, need to check the shared state"
   */
- static bool LocalRecoveryProcessingMode = true;
+ static bool LocalRecoveryInProgress = true;
  
 +/* is the database proven consistent yet? */
 +bool  reachedSafeStartPoint = false;
 +
  /* Was the last xlog file restored from archive, or local? */
  static bool restoredFromArchive = false;
  
@@@ -4963,278 -4896,66 +4964,278 @@@ exitArchiveRecovery(TimeLineID endTLI, 
  static bool
  recoveryStopsHere(XLogRecord *record, bool *includeThis)
  {
 -      bool            stopsHere;
 -      uint8           record_info;
 -      TimestampTz recordXtime;
 -
 +      bool            stopsHere = false;
 +      bool            pauseHere = false;
 +      static bool     paused = false;
 +      uint8           record_info = 0;        /* valid iff (is_xact_completion_record) */
 +      TimestampTz recordXtime = 0;
 +      bool        is_xact_completion_record = false;
 +  
        /* We only consider stopping at COMMIT or ABORT records */
 -      if (record->xl_rmid != RM_XACT_ID)
 -              return false;
 -      record_info = record->xl_info & ~XLR_INFO_MASK;
 -      if (record_info == XLOG_XACT_COMMIT)
 +      if (record->xl_rmid == RM_XACT_ID)
        {
 -              xl_xact_commit *recordXactCommitData;
 +              record_info = record->xl_info & ~XLR_INFO_MASK;
 +              if (record_info == XLOG_XACT_COMMIT)
 +              {
 +                      xl_xact_commit *recordXactCommitData;
  
 -              recordXactCommitData = (xl_xact_commit *) XLogRecGetData(record);
 -              recordXtime = recordXactCommitData->xact_time;
 -      }
 -      else if (record_info == XLOG_XACT_ABORT)
 -      {
 -              xl_xact_abort *recordXactAbortData;
 +                      recordXactCommitData = (xl_xact_commit *) XLogRecGetData(record);
 +                      recordXtime = recordXactCommitData->xact_time;
 +                      is_xact_completion_record = true;
 +              }
 +              else if (record_info == XLOG_XACT_ABORT)
 +              {
 +                      xl_xact_abort *recordXactAbortData;
  
 -              recordXactAbortData = (xl_xact_abort *) XLogRecGetData(record);
 -              recordXtime = recordXactAbortData->xact_time;
 -      }
 -      else
 -              return false;
 +                      recordXactAbortData = (xl_xact_abort *) XLogRecGetData(record);
 +                      recordXtime = recordXactAbortData->xact_time;
 +                      is_xact_completion_record = true;
 +              }
  
 -      /* Do we have a PITR target at all? */
 -      if (!recoveryTarget)
 -      {
 -              recoveryLastXTime = recordXtime;
 -              return false;
 +              /* Remember the most recent COMMIT/ABORT time for logging purposes */
 +              if (is_xact_completion_record)
 +              {
 +                      recoveryLastXTime = recordXtime;
 +                      recoveryLastXid = record->xl_xid;
 +              }
        }
  
 -      if (recoveryTargetExact)
 +      do
        {
 +              int     prevRecoveryTargetMode = recoveryTargetMode;    
 +
 +              CHECK_FOR_INTERRUPTS();
 +
                /*
 -               * there can be only one transaction end record with this exact
 -               * transactionid
 -               *
 -               * when testing for an xid, we MUST test for equality only, since
 -               * transactions are numbered in the order they start, not the order
 -               * they complete. A higher numbered xid will complete before you about
 -               * 50% of the time...
 +               * Check if we were requested to exit without finishing
 +               * recovery.
                 */
 -              stopsHere = (record->xl_xid == recoveryTargetXid);
 -              if (stopsHere)
 -                      *includeThis = recoveryTargetInclusive;
 -      }
 -      else
 -      {
 +              if (shutdown_requested)
-                       proc_exit(0);
++                      proc_exit(1);
 +
                /*
 -               * there can be many transactions that share the same commit time, so
 -               * we stop after the last one, if we are inclusive, or stop at the
 -               * first one if we are exclusive
 +               * Let's see if user has updated our recoveryTargetMode.
                 */
 -              if (recoveryTargetInclusive)
 -                      stopsHere = (recordXtime > recoveryTargetTime);
 -              else
 -                      stopsHere = (recordXtime >= recoveryTargetTime);
 -              if (stopsHere)
 -                      *includeThis = false;
 +              {
 +                      /* use volatile pointer to prevent code rearrangement */
 +                      volatile XLogCtlData *xlogctl = XLogCtl;
 +
 +                      SpinLockAcquire(&xlogctl->info_lck);
 +                      recoveryTargetMode = xlogctl->recoveryTargetMode;
 +                      if (recoveryTargetMode != RECOVERY_TARGET_NONE)
 +                      {
 +                              recoveryTargetXid = xlogctl->recoveryTargetXid;
 +                              recoveryTargetTime = xlogctl->recoveryTargetTime;
 +
 +                              /* Don't reset counter while we're advancing */
 +                              if (recoveryTargetAdvance <= 0)
 +                              {
 +                                      recoveryTargetAdvance = xlogctl->recoveryTargetAdvance;
 +                                      xlogctl->recoveryTargetAdvance = 0;
 +                              }
 +                      }
 +                      if (is_xact_completion_record)
 +                      {
 +                              xlogctl->recoveryLastXTime = recordXtime;
 +                              xlogctl->recoveryLastXid = record->xl_xid;
 +                      }
 +                      xlogctl->recoveryLastRecPtr = LastRec;
 +                      SpinLockRelease(&xlogctl->info_lck);
 +              }
 +
 +              /* Decide how to act on any pause target */
 +              switch (recoveryTargetMode) 
 +              {
 +                      case RECOVERY_TARGET_PAUSE_LSN:
 +                                      return false;
 +
 +                      case RECOVERY_TARGET_NONE:
 +                                      /* 
 +                                       * If we aren't paused and we're not looking to stop,
 +                                       * just exit out quickly and get on with recovery.
 +                                       */
 +                                      if (paused)
 +                                      {
 +                                              ereport(LOG, 
 +                                                              (errmsg("recovery restarting after pause")));
 +                                              set_ps_display("recovery continues", false);
 +                                              paused = false;
 +                                      }
 +                                      return false;
 +
 +                      case RECOVERY_TARGET_PAUSE_ALL:
 +                                      pauseHere = true;
 +                                      break;
 +
 +                      case RECOVERY_TARGET_ADVANCE:
 +                                      if (paused)
 +                                      {
 +                                              if (recoveryTargetAdvance-- > 0)
 +                                              {
 +                                                      elog(LOG, "recovery advancing 1 record");
 +                                                      return false;
 +                                              }
 +                                              else
 +                                                      break;
 +                                      }
 +
 +                                      if (recoveryTargetAdvance-- <= 0)
 +                                              pauseHere = true;
 +                                      break;
 +
 +                      case RECOVERY_TARGET_STOP_IMMEDIATE:
 +                      case RECOVERY_TARGET_STOP_XID:
 +                      case RECOVERY_TARGET_STOP_TIME:
 +                                      paused = false;
 +                                      break;
 +
 +                      /*
 +                       * If we're paused, and mode has changed reset to allow new settings
 +                       * to apply and maybe allow us to continue.
 +                       */
 +                      if (paused && prevRecoveryTargetMode != recoveryTargetMode)
 +                              paused = false;
 +
 +                      case RECOVERY_TARGET_PAUSE_XID:
 +                                      /*
 +                                       * there can be only one transaction end record with this exact
 +                                       * transactionid
 +                                       *
 +                                       * when testing for an xid, we MUST test for equality only, since
 +                                       * transactions are numbered in the order they start, not the order
 +                                       * they complete. A higher numbered xid will complete before you about
 +                                       * 50% of the time...
 +                                       */
 +                                      if (is_xact_completion_record)
 +                                              pauseHere = (record->xl_xid == recoveryTargetXid);
 +                                      break;
 +
 +                      case RECOVERY_TARGET_PAUSE_TIME:
 +                                      /*
 +                                       * there can be many transactions that share the same commit time, so
 +                                       * we pause after the last one, if we are inclusive, or pause at the
 +                                       * first one if we are exclusive
 +                                       */
 +                                      if (is_xact_completion_record)
 +                                      {
 +                                              if (recoveryTargetInclusive)
 +                                                      pauseHere = (recoveryLastXTime > recoveryTargetTime);
 +                                              else
 +                                                      pauseHere = (recoveryLastXTime >= recoveryTargetTime);
 +                                      }
 +                                      break;
 +
 +                      default:
 +                                      ereport(WARNING,
 +                                                      (errmsg("unknown recovery mode %d, continuing recovery", 
 +                                                                                      recoveryTargetMode)));
 +                                      return false;
 +              }
 +
 +              /*
 +               * If we just entered pause, issue log messages
 +               */
 +              if (pauseHere && !paused)
 +              {
 +                      if (is_xact_completion_record)
 +                      {
 +                              if (record_info == XLOG_XACT_COMMIT)
 +                                      ereport(LOG,
 +                                              (errmsg("recovery pausing before commit of transaction %u, log time %s",
 +                                                                      record->xl_xid,
 +                                                                      timestamptz_to_str(recoveryLastXTime))));
 +                              else
 +                                      ereport(LOG,
 +                                              (errmsg("recovery pausing before abort of transaction %u, log time %s",
 +                                                                      record->xl_xid,
 +                                                                      timestamptz_to_str(recoveryLastXTime))));
 +                      }
 +                      else
 +                              ereport(LOG,
 +                                              (errmsg("recovery pausing; last recovered transaction %u, "
 +                                                              "last recovered xact timestamp %s",
 +                                                                      recoveryLastXid,
 +                                                                      timestamptz_to_str(recoveryLastXTime))));
 +
 +                      set_ps_display("recovery paused", false);
 +
 +                      paused = true;
 +              }
 +
 +              /*
 +               * Pause for a while before rechecking mode at top of loop.
 +               */
 +              if (paused)
 +              {
 +                      recoveryTargetAdvance = 0;
 +
 +                      /*
 +                       * Update the recoveryTargetMode
 +                       */
 +                      {
 +                              /* use volatile pointer to prevent code rearrangement */
 +                              volatile XLogCtlData *xlogctl = XLogCtl;
 +
 +                              SpinLockAcquire(&xlogctl->info_lck);
 +                              xlogctl->recoveryTargetMode = RECOVERY_TARGET_PAUSE_ALL;
 +                              xlogctl->recoveryTargetAdvance = 0;
 +                              SpinLockRelease(&xlogctl->info_lck);
 +                      }
 +
 +                      pg_usleep(200000L);
 +              }
 +              
 +              /*
 +               * We leave the loop at the bottom only if our recovery mode is
 +               * set (or has been recently reset) to one of the stop options.
 +               */
 +      } while (paused);
 +
 +      /* 
 +       * Decide how to act if stop target mode set. We run this separately from 
 +       * pause to allow user to reset their stop target while paused.
 +       */
 +      switch (recoveryTargetMode) 
 +      {
 +              case RECOVERY_TARGET_STOP_IMMEDIATE:
 +                              ereport(LOG,
 +                                              (errmsg("recovery stopping immediately due to user request")));
 +                              return true;
 +
 +              case RECOVERY_TARGET_STOP_XID:
 +                              /*
 +                               * there can be only one transaction end record with this exact
 +                               * transactionid
 +                               *
 +                               * when testing for an xid, we MUST test for equality only, since
 +                               * transactions are numbered in the order they start, not the order
 +                               * they complete. A higher numbered xid will complete before you about
 +                               * 50% of the time...
 +                               */
 +                              if (is_xact_completion_record)
 +                              {
 +                                      stopsHere = (record->xl_xid == recoveryTargetXid);
 +                                      if (stopsHere)
 +                                              *includeThis = recoveryTargetInclusive;
 +                              }
 +                              break;
 +
 +              case RECOVERY_TARGET_STOP_TIME:
 +                              /*
 +                               * there can be many transactions that share the same commit time, so
 +                               * we stop after the last one, if we are inclusive, or stop at the
 +                               * first one if we are exclusive
 +                               */
 +                              if (is_xact_completion_record)
 +                              {
 +                                      if (recoveryTargetInclusive)
 +                                              stopsHere = (recoveryLastXTime > recoveryTargetTime);
 +                                      else
 +                                              stopsHere = (recoveryLastXTime >= recoveryTargetTime);
 +                                      if (stopsHere)
 +                                              *includeThis = false;
 +                              }
 +                              break;
        }
  
        if (stopsHere)
                                                                recoveryStopXid,
                                                                timestamptz_to_str(recoveryStopTime))));
                }
 +      }
  
 -              if (recoveryStopAfter)
 -                      recoveryLastXTime = recordXtime;
 +      return stopsHere;
 +}
 +
 +static void
 +recoveryPausesAfterLSN(void)
 +{
 +      while (recoveryTargetMode == RECOVERY_TARGET_PAUSE_LSN &&
 +                      XLByteLE(recoveryTargetLSN, LastRec))
 +      {
 +              {
 +                      /* use volatile pointer to prevent code rearrangement */
 +                      volatile XLogCtlData *xlogctl = XLogCtl;
 +
 +                      SpinLockAcquire(&xlogctl->info_lck);
 +                      recoveryTargetMode = xlogctl->recoveryTargetMode;
 +                      recoveryTargetLSN = xlogctl->recoveryTargetLSN;
 +                      SpinLockRelease(&xlogctl->info_lck);
 +              }
 +
 +              pg_usleep(25000L);
 +      }
 +}
 +
 +/*
 + * Utility function used by various user functions to set the recovery
 + * target mode. This allows user control over the progress of recovery.
 + */
 +static void
 +SetRecoveryTargetMode(int mode, TransactionId xid, TimestampTz ts, 
 +                                              XLogRecPtr lsn, int advance)
 +{
-       if (!IsRecoveryProcessingMode())
++      if (!RecoveryInProgress())
 +              ereport(ERROR,
 +                              (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 +                               errmsg("recovery is not in progress"),
 +                               errhint("WAL control functions can only be executed during recovery.")));
 +
 +      if (!InRecovery && !superuser())
 +              ereport(ERROR,
 +                              (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 +                               errmsg("must be superuser to control recovery")));
 +
 +
 +      {
 +              /* use volatile pointer to prevent code rearrangement */
 +              volatile XLogCtlData *xlogctl = XLogCtl;
 +
 +              SpinLockAcquire(&xlogctl->info_lck);
 +              xlogctl->recoveryTargetMode = mode;
 +
 +              if (mode == RECOVERY_TARGET_STOP_XID || 
 +                      mode == RECOVERY_TARGET_PAUSE_XID)
 +                      xlogctl->recoveryTargetXid = xid;
 +              else if (mode == RECOVERY_TARGET_STOP_TIME || 
 +                                mode == RECOVERY_TARGET_PAUSE_TIME)
 +                      xlogctl->recoveryTargetTime = ts;
 +              else if (mode == RECOVERY_TARGET_ADVANCE)
 +                      xlogctl->recoveryTargetAdvance = advance;
 +              else if (mode == RECOVERY_TARGET_PAUSE_LSN)
 +                      xlogctl->recoveryTargetLSN = lsn;
 +
 +              SpinLockRelease(&xlogctl->info_lck);
        }
-       PG_RETURN_BOOL(IsRecoveryProcessingMode());
 +}
 +
 +/*
 + * Forces recovery mode to reset to unfrozen.
 + * Returns void.
 + */
 +Datum
 +pg_recovery_continue(PG_FUNCTION_ARGS)
 +{
 +      SetRecoveryTargetMode(RECOVERY_TARGET_NONE, 
 +                                                      InvalidTransactionId, 0, InvalidXLogRecPtr, 0);
 +
 +      PG_RETURN_VOID();
 +}
 +
 +/*
 + * Pause recovery immediately. Stays paused until asked to play again.
 + * Returns void.
 + */
 +Datum
 +pg_recovery_pause(PG_FUNCTION_ARGS)
 +{
 +      SetRecoveryTargetMode(RECOVERY_TARGET_PAUSE_ALL, 
 +                                                      InvalidTransactionId, 0, InvalidXLogRecPtr, 0);
 +
 +      PG_RETURN_VOID();
 +}
 +
 +/*
 + * Pause recovery at stated xid, if ever seen. Once paused, stays paused
 + * until asked to play again.
 + */
 +Datum
 +pg_recovery_pause_xid(PG_FUNCTION_ARGS)
 +{
 +      int                       xidi = PG_GETARG_INT32(0);
 +      TransactionId xid = (TransactionId) xidi;
 +
 +      if (xid < 3)
 +              elog(ERROR, "cannot specify special values for transaction id");
 +
 +      SetRecoveryTargetMode(RECOVERY_TARGET_PAUSE_XID, 
 +                                                      xid, 0, InvalidXLogRecPtr, 0);
 +
 +      PG_RETURN_VOID();
 +}
 +
 +/*
 + * Pause recovery at stated timestamp, if ever reached. Once paused, stays paused
 + * until asked to play again.
 + */
 +Datum
 +pg_recovery_pause_time(PG_FUNCTION_ARGS)
 +{
 +      TimestampTz ts = PG_GETARG_TIMESTAMPTZ(0);
 +
 +      SetRecoveryTargetMode(RECOVERY_TARGET_PAUSE_TIME, 
 +                                                      InvalidTransactionId, ts, InvalidXLogRecPtr, 0);
 +
 +      PG_RETURN_VOID();
 +}
 +
 +/*
 + * Pause recovery after stated LSN, if ever reached. Once paused, stays paused
 + * until asked to play again.
 + */
 +Datum
 +pg_recovery_pause_lsn(PG_FUNCTION_ARGS)
 +{
 +      XLogRecPtr lsn;
 +
 +      lsn.xlogid = PG_GETARG_INT32(0);
 +      lsn.xrecoff = PG_GETARG_INT32(1);
 +
 +      SetRecoveryTargetMode(RECOVERY_TARGET_PAUSE_LSN, 
 +                                                      InvalidTransactionId, 0, lsn, 0);
 +
 +      PG_RETURN_VOID();
 +}
 +
 +/*
 + * If paused, advance N records.
 + */
 +Datum
 +pg_recovery_advance(PG_FUNCTION_ARGS)
 +{
 +      int adv = PG_GETARG_INT32(0);
 +
 +      if (adv < 1)
 +              elog(ERROR, "recovery advance must be greater than or equal to 1");
 +
 +      SetRecoveryTargetMode(RECOVERY_TARGET_ADVANCE, 
 +                                                      InvalidTransactionId, 0, InvalidXLogRecPtr, adv);
 +
 +      PG_RETURN_VOID();
 +}
 +
 +/*
 + * Forces recovery to stop now if paused, or at end of next record if playing.
 + */
 +Datum
 +pg_recovery_stop(PG_FUNCTION_ARGS)
 +{
 +      SetRecoveryTargetMode(RECOVERY_TARGET_STOP_IMMEDIATE, 
 +                                                      InvalidTransactionId, 0, InvalidXLogRecPtr, 0);
 +
 +      PG_RETURN_VOID();
 +}
 +
 +Datum
 +pg_current_recovery_target(PG_FUNCTION_ARGS)
 +{
 +      StringInfoData buf;
 +
 +      initStringInfo(&buf);
 +
 +      {
 +              /* use volatile pointer to prevent code rearrangement */
 +              volatile XLogCtlData *xlogctl = XLogCtl;
 +
 +              SpinLockAcquire(&xlogctl->info_lck);
 +
 +              recoveryTargetMode = xlogctl->recoveryTargetMode;
 +              if (recoveryTargetMode != RECOVERY_TARGET_NONE)
 +              {
 +                      recoveryTargetXid = xlogctl->recoveryTargetXid;
 +                      recoveryTargetTime = xlogctl->recoveryTargetTime;
 +                      recoveryTargetAdvance = xlogctl->recoveryTargetAdvance;
 +              }
 +
 +              SpinLockRelease(&xlogctl->info_lck);
 +      }
 +
 +      switch (recoveryTargetMode)
 +      {
 +              case RECOVERY_TARGET_NONE:
 +                              appendStringInfo(&buf, "No recovery target has been set");
 +                              break;
 +              case RECOVERY_TARGET_PAUSE_ALL:
 +                              appendStringInfo(&buf, "Recovery paused");
 +                              break;
 +              case RECOVERY_TARGET_PAUSE_XID:
 +                              appendStringInfo(&buf, "Recovery will pause after commit of transaction %u", recoveryTargetXid);
 +                              break;
 +              case RECOVERY_TARGET_PAUSE_TIME:
 +                              appendStringInfo(&buf, "Recovery will pause after transaction completion timestamp %s", 
 +                                                                              timestamptz_to_str(recoveryTargetTime));
 +                              break;
 +              case RECOVERY_TARGET_PAUSE_LSN:
 +                              appendStringInfo(&buf, "Recovery will pause after applying record at xlog location %X/%X", 
 +                                                                              recoveryTargetLSN.xlogid, 
 +                                                                              recoveryTargetLSN.xrecoff);
 +                              break;
 +              case RECOVERY_TARGET_ADVANCE:
 +                              appendStringInfo(&buf, "Recovery will advance");
 +                              break;
 +              case RECOVERY_TARGET_STOP_IMMEDIATE:
 +                              appendStringInfo(&buf, "No recovery target has been set");
 +                              break;
 +              case RECOVERY_TARGET_STOP_XID:
 +                              appendStringInfo(&buf, "Recovery will stop after commit of transaction %u", recoveryTargetXid);
 +                              break;
 +              case RECOVERY_TARGET_STOP_TIME:
 +                              appendStringInfo(&buf, "Recovery will stop after transaction completion timestamp %s",
 +                                                                              timestamptz_to_str(recoveryTargetTime));
 +                              break;
 +      }
 +
 +      PG_RETURN_TEXT_P(cstring_to_text(buf.data));
 +}
 +
 +/*
 + * Returns bool with current recovery mode, a global state.
 + */
 +Datum
 +pg_is_in_recovery(PG_FUNCTION_ARGS)
 +{
++      PG_RETURN_BOOL(RecoveryInProgress());
 +}
 +
 +/*
 + * Returns timestamp of last completed transaction
 + */
 +Datum
 +pg_last_recovered_xact_timestamp(PG_FUNCTION_ARGS)
 +{
 +      {
 +              /* use volatile pointer to prevent code rearrangement */
 +              volatile XLogCtlData *xlogctl = XLogCtl;
 +
 +              SpinLockAcquire(&xlogctl->info_lck);
 +
 +              recoveryLastXTime = xlogctl->recoveryLastXTime;
 +
 +              SpinLockRelease(&xlogctl->info_lck);
 +      }
 +
 +      PG_RETURN_TIMESTAMPTZ(recoveryLastXTime);
 +}
 +
 +/*
 + * Returns xid of last completed transaction
 + */
 +Datum
 +pg_last_recovered_xid(PG_FUNCTION_ARGS)
 +{
 +      {
 +              /* use volatile pointer to prevent code rearrangement */
 +              volatile XLogCtlData *xlogctl = XLogCtl;
 +
 +              SpinLockAcquire(&xlogctl->info_lck);
 +
 +              recoveryLastXid = xlogctl->recoveryLastXid;
 +
 +              SpinLockRelease(&xlogctl->info_lck);
 +      }
 +
 +      PG_RETURN_INT32(recoveryLastXid);
 +}
 +
 +/*
 + * Returns xlog location of last recovered WAL record.
 + */
 +Datum
 +pg_last_recovered_xlog_location(PG_FUNCTION_ARGS)
 +{
 +      char            location[MAXFNAMELEN];
 +
 +      {
 +              /* use volatile pointer to prevent code rearrangement */
 +              volatile XLogCtlData *xlogctl = XLogCtl;
 +
 +              SpinLockAcquire(&xlogctl->info_lck);
 +
 +              LastRec = xlogctl->recoveryLastRecPtr;
 +
 +              SpinLockRelease(&xlogctl->info_lck);
 +      }
 +
 +      snprintf(location, sizeof(location), "%X/%X",
 +                       LastRec.xlogid, LastRec.xrecoff);
 +      PG_RETURN_TEXT_P(cstring_to_text(location));
 +}
 +
 +/*
 + * Returns delay in milliseconds, or -1 if delay too large
 + */
 +int
 +GetLatestReplicationDelay(void)
 +{
 +      long            delay_secs;
 +      int                     delay_usecs;
 +      int                     delay;
 +      TimestampTz currTz = GetCurrentTimestamp();
 +
 +      TimestampDifference(recoveryLastXTime, currTz,
 +                                              &delay_secs, &delay_usecs);
 +
 +      /*
 +       * If delay is very large we probably aren't looking at
 +       * a replication situation at all, just a recover from backup.
 +       * So return a special value instead.
 +       */
 +      if (delay_secs > (long)(INT_MAX / 1000))
 +              delay = -1;
        else
 -              recoveryLastXTime = recordXtime;
 +              delay = (int)(delay_secs * 1000) + (delay_usecs / 1000);
  
 -      return stopsHere;
 +      return delay;
  }
  
  /*
@@@ -5978,7 -5361,14 +5979,6 @@@ StartupXLOG(void
                                        pfree(buf.data);
                                }
  #endif
 -
 -                              /*
 -                               * Check if we were requested to exit without finishing
 -                               * recovery.
 -                               */
 -                              if (shutdown_requested)
 -                                      proc_exit(1);
--
                                /*
                                 * Have we reached our safe starting point? If so, we can
                                 * tell postmaster that the database is consistent now.
@@@ -6310,13 -5686,12 +6310,13 @@@ RecoveryInProgress(void
  
                /*
                 * Initialize TimeLineID and RedoRecPtr the first time we see that
 -               * recovery is finished.
 +               * recovery is finished. InitPostgres() relies upon this behaviour
 +               * to ensure that InitXLOGAccess() is called at backend startup.
                 */
-               if (!LocalRecoveryProcessingMode)
+               if (!LocalRecoveryInProgress)
                        InitXLOGAccess();
  
-               return LocalRecoveryProcessingMode;
+               return LocalRecoveryInProgress;
        }
  }
  
@@@ -7005,19 -6380,8 +7005,19 @@@ CreateCheckPoint(int flags
                                  CheckpointStats.ckpt_segs_recycled);
  
        LWLockRelease(CheckpointLock);
 -}
  
-       if (!shutdown && !IsRecoveryProcessingMode())
 +      /*
 +       * Take a snapshot of running transactions and write this to WAL.
 +       * This allows us to reconstruct the state of running transactions 
 +       * during archive recovery, if required.
 +       * 
 +       * If we are shutting down, or Startup process is completing crash
 +       * recovery we don't need to write running xact data.
 +       */
++      if (!shutdown && !RecoveryInProgress())
 +              LogCurrentRunningXacts();
 +}
 + 
  /*
   * Flush all data in shared memory to disk, and fsync
   *
@@@ -7570,12 -6919,6 +7570,12 @@@ pg_start_backup(PG_FUNCTION_ARGS
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 errmsg("must be superuser to run a backup")));
  
-       if (IsRecoveryProcessingMode())
++      if (RecoveryInProgress())
 +              ereport(ERROR,
 +                              (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 +                               errmsg("recovery is in progress"),
 +                               errhint("WAL control functions cannot be executed during recovery.")));
 +
        if (!XLogArchivingActive())
                ereport(ERROR,
                                (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@@ -7748,12 -7091,6 +7748,12 @@@ pg_stop_backup(PG_FUNCTION_ARGS
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                 (errmsg("must be superuser to run a backup"))));
  
-       if (IsRecoveryProcessingMode())
++      if (RecoveryInProgress())
 +              ereport(ERROR,
 +                              (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 +                               errmsg("recovery is in progress"),
 +                               errhint("WAL control functions cannot be executed during recovery.")));
 +
        if (!XLogArchivingActive())
                ereport(ERROR,
                                (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@@ -7915,12 -7252,6 +7915,12 @@@ pg_switch_xlog(PG_FUNCTION_ARGS
                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                         (errmsg("must be superuser to switch transaction log files"))));
  
-       if (IsRecoveryProcessingMode())
++      if (RecoveryInProgress())
 +              ereport(ERROR,
 +                              (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 +                               errmsg("recovery is in progress"),
 +                               errhint("WAL control functions cannot be executed during recovery.")));
 +
        switchpoint = RequestXLogSwitch();
  
        /*
@@@ -7943,12 -7274,6 +7943,12 @@@ pg_current_xlog_location(PG_FUNCTION_AR
  {
        char            location[MAXFNAMELEN];
  
-       if (IsRecoveryProcessingMode())
++      if (RecoveryInProgress())
 +              ereport(ERROR,
 +                              (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 +                               errmsg("recovery is in progress"),
 +                               errhint("WAL control functions cannot be executed during recovery.")));
 +
        /* Make sure we have an up-to-date local LogwrtResult */
        {
                /* use volatile pointer to prevent code rearrangement */
@@@ -7976,12 -7301,6 +7976,12 @@@ pg_current_xlog_insert_location(PG_FUNC
        XLogRecPtr      current_recptr;
        char            location[MAXFNAMELEN];
  
-       if (IsRecoveryProcessingMode())
++      if (RecoveryInProgress())
 +              ereport(ERROR,
 +                              (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 +                               errmsg("recovery is in progress"),
 +                               errhint("WAL control functions cannot be executed during recovery.")));
 +
        /*
         * Get the current end-of-WAL position ... shared lock is sufficient
         */
index 9623a6bd77a7125e7a7754b011abaa704ad51681,348e6e033f7ada92fd2506720a6fa09d5b10cb7f..613dbc12c38468c5fc8cb5d9f6fbb373c5812e30
@@@ -65,8 -65,7 +65,8 @@@ DiscardAll(bool isTopLevel
        ResetAllOptions();
        DropAllPreparedStatements();
        PortalHashTableDeleteAll();
-       if (!IsRecoveryProcessingMode())
 -      Async_UnlistenAll();
++      if (!RecoveryInProgress())
 +              Async_UnlistenAll();
        LockReleaseAll(USER_LOCKMETHOD, true);
        ResetPlanCache();
        ResetTempTableNamespace();
index cfd4eeddd2ac545e0cc1d06db28d660f32d138bd,bae413255a2322a48b615159de7d9f083bab4ca5..cff40cfb29fb4b980a7c55f92c70e05ec8393b85
@@@ -2016,11 -2010,11 +2009,12 @@@ pmdie(SIGNAL_ARGS
                        ereport(LOG,
                                        (errmsg("received smart shutdown request")));
  
-                       if (pmState == PM_RUN || pmState == PM_RECOVERY || pmState == PM_RECOVERY_CONSISTENT)
+                       if (pmState == PM_RUN || pmState == PM_RECOVERY ||
+                               pmState == PM_RECOVERY_CONSISTENT)
                        {
                                /* autovacuum workers are told to shut down immediately */
 -                              SignalAutovacWorkers(SIGTERM);
 +                              if (pmState == PM_RUN)
 +                                      SignalAutovacWorkers(SIGTERM);
                                /* and the autovac launcher too */
                                if (AutoVacPID != 0)
                                        signal_child(AutoVacPID, SIGTERM);
@@@ -2178,9 -2163,20 +2163,20 @@@ reaper(SIGNAL_ARGS
                                LogChildExit(LOG, _("startup process"),
                                                         pid, exitstatus);
                                ereport(LOG,
 -                              (errmsg("aborting startup due to startup process failure")));
 +                                              (errmsg("aborting startup due to startup process failure")));
                                ExitPostmaster(1);
                        }
+                       /*
+                        * Startup process exited in response to a shutdown request (or
+                        * it completed normally regardless of the shutdown request).
+                        */
+                       if (Shutdown > NoShutdown &&
+                               (EXIT_STATUS_0(exitstatus) || EXIT_STATUS_1(exitstatus)))
+                       {
+                               pmState = PM_WAIT_BACKENDS;
+                               /* PostmasterStateMachine logic does the rest */
+                               continue;
+                       }
                        /*
                         * Any unexpected exit (including FATAL exit) of the startup
                         * process is treated as a crash, except that we don't want
index 3aafe348cd17f8e61c2b51df3c34fd7d775534a3,06f8ad8f4a1a519b70e4138fae8264af62466810..04bf18ceec9ff00eaedd96a7f60dd1fb4dde0401
@@@ -262,15 -181,6 +262,15 @@@ ProcArrayRemove(PGPROC *proc, Transacti
  
        LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
  
-       if (IsRecoveryProcessingMode())
 +      /*
 +       * Remove any UnobservedXids remaining
 +       */
++      if (RecoveryInProgress())
 +      {
 +              for (index = 0; index < nsubxids; index++)
 +                      UnobservedTransactionsRemoveXid(subxids[index], false);
 +      }
 +
        if (TransactionIdIsValid(latestXid))
        {
                Assert(TransactionIdIsValid(proc->xid));
        else
        {
                /* Shouldn't be trying to remove a live transaction here */
-               Assert(IsRecoveryProcessingMode() || !TransactionIdIsValid(proc->xid));
 -              Assert(!TransactionIdIsValid(proc->xid));
++              Assert(RecoveryInProgress() || !TransactionIdIsValid(proc->xid));
        }
  
        for (index = 0; index < arrayP->numProcs; index++)
        /* Ooops */
        LWLockRelease(ProcArrayLock);
  
-       elog(IsRecoveryProcessingMode() ? ERROR : LOG, 
 -      elog(LOG, "failed to find proc %p in ProcArray", proc);
++      elog(RecoveryInProgress() ? ERROR : LOG, 
 +                      "failed to find proc %p in ProcArray", proc);
  }
  
 +/*
 + * Initialisation when we switch into PM_RECOVERY mode.
 + * Expected caller is InitRecoveryTransactionEnvironment()
 + */
 +void
 +ProcArrayInitRecoveryEnvironment(void)
 +{
 +      PublishStartupProcessInformation();
 +}
  
  /*
   * ProcArrayEndTransaction -- mark a transaction as no longer running
@@@ -1370,9 -589,6 +1370,9 @@@ GetOldestXmin(bool allDbs, bool ignoreV
        TransactionId result;
        int                     index;
  
-       Assert(allDbs || !IsRecoveryProcessingMode());
 +      /* Cannot look for individual databases during recovery */
++      Assert(allDbs || !RecoveryInProgress());
 +
        LWLockAcquire(ProcArrayLock, LW_SHARED);
  
        /*
@@@ -1481,16 -697,10 +1481,16 @@@ GetSnapshotData(Snapshot snapshot
        if (snapshot->xip == NULL)
        {
                /*
 -               * First call for this snapshot
 +               * First call for this snapshot. In recovery we need an additional
 +               * space allocation to allow for UnobservedXids, which never occur
 +               * in normal running.
                 */
-               if (IsRecoveryProcessingMode())
 -              snapshot->xip = (TransactionId *)
 -                      malloc(arrayP->maxProcs * sizeof(TransactionId));
++              if (RecoveryInProgress())
 +                      snapshot->xip = (TransactionId *)
 +                              malloc(3 * arrayP->maxProcs * sizeof(TransactionId));
 +              else
 +                      snapshot->xip = (TransactionId *)
 +                              malloc(arrayP->maxProcs * sizeof(TransactionId));
                if (snapshot->xip == NULL)
                        ereport(ERROR,
                                        (errcode(ERRCODE_OUT_OF_MEMORY),
         */
        LWLockAcquire(ProcArrayLock, LW_SHARED);
  
-       if (IsRecoveryProcessingMode() && !arrayP->allowStandbySnapshots)
++      if (RecoveryInProgress() && !arrayP->allowStandbySnapshots)
 +      {
 +              LWLockRelease(ProcArrayLock);
 +              ereport(ERROR,
 +                      (errcode(ERRCODE_QUERY_CANCELED),
 +                       errmsg("canceling statement because standby snapshots are currently disabled"),
 +                       errdetail("Valid MVCC snapshot cannot be taken at this time."),
 +                       errhint("Contact your administrator if this error recurs frequently")));
 +      }
 +
        /* xmax is always latestCompletedXid + 1 */
        xmax = ShmemVariableCache->latestCompletedXid;
        Assert(TransactionIdIsNormal(xmax));
                        if (proc->subxids.overflowed)
                                subcount = -1;  /* overflowed */
                        else
 -                      {
 -                              int                     nxids = proc->subxids.nxids;
 +                      {
 +                              int                     nxids = proc->subxids.nxids;
 +  
 +                              if (nxids > 0)
 +                              {
 +                                      memcpy(snapshot->subxip + subcount,
 +                                                 (void *) proc->subxids.xids,
 +                                                 nxids * sizeof(TransactionId));
 +                                      subcount += nxids;
 +                              }
 +                      }
 +              }
 +      }
  
 -                              if (nxids > 0)
 -                              {
 -                                      memcpy(snapshot->subxip + subcount,
 -                                                 (void *) proc->subxids.xids,
 -                                                 nxids * sizeof(TransactionId));
 -                                      subcount += nxids;
 -                              }
 -                      }
 -              }
 +      /*
 +       * Also check for unobserved xids. There is no need for us to specify
-        * that this is only if IsRecoveryProcessingMode(), since the list will
++       * that this is only if RecoveryInProgress(), since the list will
 +       * always be empty when normal processing begins and the test will be
 +       * optimised to nearly nothing very quickly.
 +       */
 +      for (index = 0; index < arrayP->numUnobservedXids; index++)
 +      {
 +              volatile TransactionId  *UnobservedXids;
 +              TransactionId   xid;
 +
 +              UnobservedXids = (TransactionId *) &(arrayP->procs[arrayP->maxProcs]);
 +
 +              /* Fetch xid just once - see GetNewTransactionId */
 +              xid = UnobservedXids[index];
 +
 +              /*
 +               * If there are no more visible xids, we're done. This works
 +               * because UnobservedXids is maintained in strict ascending order.
 +               */
 +              if (!TransactionIdIsNormal(xid) || TransactionIdPrecedes(xid, xmax))
 +                      break;
 +
 +              /*
 +               * Add unobserved xids onto the main xip array.
 +               */
 +              snapshot->xip[count++] = xid;
 +
 +              /*
 +               * Check to see if this changes xmin. It is possible that an unobserved
 +               * xid could be xmin if there is contention between long-lived 
 +               * transactions.
 +               */
 +              if (TransactionIdPrecedes(xid, xmin))
 +                      xmin = xid;
        }
  
        if (!TransactionIdIsValid(MyProc->xmin))
@@@ -2484,9 -1207,6 +2484,9 @@@ CountOtherDBBackends(Oid databaseId, in
        int                     autovac_pids[MAXAUTOVACPIDS];
        int                     tries;
  
-       Assert(!IsRecoveryProcessingMode());
 +      /* Gives wrong answer in recovery, so make sure we don't use it */
++      Assert(!RecoveryInProgress());
 +
        /* 50 tries with 100ms sleep between tries makes 5 sec total wait */
        for (tries = 0; tries < 50; tries++)
        {
index b9cd501f6c1e5491641b9a34c2b3cfb82a67f13b,7c8b1f5aace81a2a73604363bf7e9a8961c172c4..0154900e678c8757b10f43eee09babc3ecad6220
@@@ -492,15 -490,6 +492,15 @@@ LockAcquire(const LOCKTAG *locktag
        if (lockmode <= 0 || lockmode > lockMethodTable->numLockModes)
                elog(ERROR, "unrecognized lock mode: %d", lockmode);
  
-       if (IsRecoveryProcessingMode() && 
++      if (RecoveryInProgress() && 
 +              locktag->locktag_type == LOCKTAG_OBJECT &&
 +              lockmode > AccessShareLock)
 +              ereport(ERROR,
 +                              (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 +                               errmsg("cannot acquire lockmode %s on database objects while recovery is in progress", 
 +                                                                      lockMethodTable->lockModeNames[lockmode]),
 +                               errhint("Only AccessShareLock can be acquired on database objects during recovery.")));
 +
  #ifdef LOCK_DEBUG
        if (LOCK_DEBUG_ENABLED(locktag))
                elog(LOG, "LockAcquire: lock [%u,%u] %s",
  
        LWLockRelease(partitionLock);
  
-       if (!IsRecoveryProcessingMode() && lockmode >= AccessExclusiveLock && 
 +      /*
 +       * We made it all the way here. We've got the lock and we've got
 +       * it for the first time in this transaction. So now it's time
 +       * to send a WAL message so that standby servers can see this event,
 +       * if its an AccessExclusiveLock on a relation. 
 +       */
++      if (!RecoveryInProgress() && lockmode >= AccessExclusiveLock && 
 +              locktag->locktag_type == LOCKTAG_RELATION)
 +      {
 +              XLogRecData             rdata;
 +              xl_rel_lock             xlrec;
 +              TransactionId   xid;
 +
 +              /*
 +               * First thing we do is ensure that a TransactionId has been
 +               * assigned to this transaction. We don't actually need the xid
 +               * but if we don't do this then RecordTransactionCommit() and
 +               * RecordTransactionAbort() will optimise away the transaction
 +               * completion record which recovery relies upon to release locks.
 +               * It's a hack, but for a corner case not worth adding code for 
 +               * into the main commit path.
 +               */
 +              xid = GetTopTransactionId();
 +              Assert(TransactionIdIsValid(xid));
 +
 +              Assert(OidIsValid(locktag->locktag_field2));
 +
 +              START_CRIT_SECTION();
 +
 +              /* 
 +               * Decode the locktag back to the original values, to avoid
 +               * sending lots of empty bytes with every message.  See
 +               * lock.h to check how a locktag is defined  for LOCKTAG_RELATION
 +               */
 +              xlrec.xid = xid;
 +              xlrec.dbOid = locktag->locktag_field1;
 +              xlrec.relOid = locktag->locktag_field2;
 +
 +              rdata.data = (char *) (&xlrec);
 +              rdata.len = sizeof(xl_rel_lock);
 +              rdata.buffer = InvalidBuffer;
 +              rdata.next = NULL;
 +
 +              (void) XLogInsert(RM_RELATION_ID, XLOG_RELATION_LOCK, &rdata);
 +
 +              END_CRIT_SECTION();
 +      }
 +
        return LOCKACQUIRE_OK;
  }
  
index f1d67d928a981d3a85b7209c49b3b93227de6e83,9e871eff92a0e6d8d4418b9fe0fd61a6930d7ab6..e6e0acb6e7e3eb7b6de4b01fdf4eda3c3332bd96
@@@ -1426,31 -1271,7 +1426,31 @@@ ProcWaitForSignal(void
  void
  ProcSendSignal(int pid)
  {
 -      PGPROC     *proc = BackendPidGetProc(pid);
 +      PGPROC     *proc = NULL;
 +
-       if (IsRecoveryProcessingMode())
++      if (RecoveryInProgress())
 +      {
 +              /* use volatile pointer to prevent code rearrangement */
 +              volatile PROC_HDR *procglobal = ProcGlobal;
 +
 +              SpinLockAcquire(ProcStructLock);
 +
 +              /*
 +               * Check to see whether it is the Startup process we wish to signal.
 +               * This call is made by the buffer manager when it wishes to wake
 +               * up a process that has been waiting for a pin in so it can obtain a
 +               * cleanup lock using LockBufferForCleanup(). Startup is not a normal 
 +               * backend, so BackendPidGetProc() will not return any pid at all. 
 +               * So we remember the information for this special case.
 +               */
 +              if (pid == procglobal->startupProcPid)
 +                      proc = procglobal->startupProc;
 +
 +              SpinLockRelease(ProcStructLock);
 +      }
 +
 +      if (proc == NULL) 
 +              proc = BackendPidGetProc(pid);
  
        if (proc != NULL)
                PGSemaphoreUnlock(&proc->sem);
index 2480e712d9217fdc736eeb322cc64e5a3fecd022,3781b55be899c6d65c786ec0e727b14417a1ad4a..e0d0412bf4c7508a37e8416dbcf1f45f0ff8dfd5
@@@ -2660,46 -2660,7 +2660,46 @@@ ProcessInterrupts(void
                        ereport(ERROR,
                                        (errcode(ERRCODE_QUERY_CANCELED),
                                         errmsg("canceling autovacuum task")));
 -              else
 +              else 
 +              {
 +                      int cancelMode = MyProc->rconflicts.cancelMode;
 +
 +                      /*
 +                       * XXXHS: We don't yet have a clean way to cancel an
 +                       * idle-in-transaction session, so make it FATAL instead.
 +                       */
 +                      if (DoingCommandRead && IsTransactionBlock() && cancelMode == ERROR)
 +                              cancelMode = FATAL;
 +
 +                      switch (cancelMode)
 +                      {
 +                              case FATAL:
-                                               Assert(IsRecoveryProcessingMode());
++                                              Assert(RecoveryInProgress());
 +                                              ereport(FATAL,
 +                                                      (errcode(ERRCODE_QUERY_CANCELED),
 +                                                       errmsg("canceling session due to conflict with recovery")));
 +                              case ERROR:
 +                                              /*
 +                                               * We are aborting because we need to release
 +                                               * locks. So we need to abort out of all
 +                                               * subtransactions to make sure we release
 +                                               * all locks at whatever their level.
 +                                               *
 +                                               * XXXHS: Should we try to examine the
 +                                               * transaction tree and remove just enough
 +                                               * subxacts to remove locks? Doubt it.
 +                                               */
-                                               Assert(IsRecoveryProcessingMode());
++                                              Assert(RecoveryInProgress());
 +                                              AbortOutOfAnyTransaction();
 +                                              ereport(ERROR,
 +                                                      (errcode(ERRCODE_QUERY_CANCELED),
 +                                                       errmsg("canceling statement due to conflict with recovery")));
 +                                              return; 
 +                              default:
 +                                              /* No conflict pending, so fall through */
 +                                              break;
 +                      }
 +
                        ereport(ERROR,
                                        (errcode(ERRCODE_QUERY_CANCELED),
                                         errmsg("canceling statement due to user request")));
index 482f6d048cfa6967f02daadb12673cce0bf96ab7,80070e39b2d7d2f7c564d66eea5785f1df3216ba..65a9f2f6d81f5f46f510a97f81828320f9560cc6
@@@ -2527,12 -2504,3 +2527,12 @@@ GetCommandLogLevel(Node *parsetree
  
        return lev;
  }
-       if (IsRecoveryProcessingMode())
 +
 +void
 +PreventCommandDuringRecovery(void)
 +{
++      if (RecoveryInProgress())
 +              ereport(ERROR,
 +                      (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
 +                       errmsg("cannot be run until recovery completes")));
 +}
index 81814e6e2b77dffa2e60b13e737addc14c73dc44,7e51f9e2ad2fc375e3744203bac01fe44a3dbcae..0a4ee4b6c06b2dfbdf518e673fc6c8571cea1fea
@@@ -338,12 -338,6 +338,12 @@@ txid_current(PG_FUNCTION_ARGS
        txid            val;
        TxidEpoch       state;
  
-       if (IsRecoveryProcessingMode())
++      if (RecoveryInProgress())
 +              ereport(ERROR,
 +                              (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 +                               errmsg("cannot assign txid while recovery is in progress"),
 +                               errhint("only read only queries can execute during recovery")));
 +
        load_xid_epoch(&state);
  
        val = convert_xid(GetTopTransactionId(), &state);
index 18a2c7b51b3e336afb5d0aa82c7ffe125d9ba8e5,b35939556f17f32966f13628d636ee632ac10a6b..8bc5909d6742231998598a2b1198ac046faa7c6c
@@@ -452,11 -452,10 +452,11 @@@ InitPostgres(const char *in_dbname, Oi
  
        /*
         * Initialize local process's access to XLOG.  In bootstrap case we may
 -       * skip this since StartupXLOG() was run instead.
 +       * skip this since StartupXLOG() was run instead. InitXLOGAccess() will
 +       * be called here if we are not in recovery processing mode.
         */
        if (!bootstrap)
-               (void) IsRecoveryProcessingMode();
 -              InitXLOGAccess();
++              (void) RecoveryInProgress();
  
        /*
         * Initialize the relation cache and the system catalog caches.  Note that
         */
        if (!bootstrap)
        {
-               if (IsRecoveryProcessingMode())
++              if (RecoveryInProgress())
 +                      SetConfigOption("default_transaction_read_only", "true",
 +                              PGC_POSTMASTER, PGC_S_OVERRIDE);
                StartTransactionCommand();
                (void) GetTransactionSnapshot();
        }
         */
        if (!bootstrap)
                LockSharedObject(DatabaseRelationId, MyDatabaseId, 0,
-                               (IsRecoveryProcessingMode() ? AccessShareLock : RowExclusiveLock));
 -                                               RowExclusiveLock);
++                              (RecoveryInProgress() ? AccessShareLock : RowExclusiveLock));
  
        /*
         * Recheck the flat file copy of pg_database to make sure the target
index 07e20d532484d07a6279a360ae667df55b74920e,dbfbb023aea3449ce896b90e8d56a891095e1a49..e54f537479e94634b30897d840135ce1b9126064
@@@ -86,7 -86,7 +86,7 @@@ static inline voi
  SetHintBits(HeapTupleHeader tuple, Buffer buffer,
                        uint16 infomask, TransactionId xid)
  {
-       if (!IsRecoveryProcessingMode() && TransactionIdIsValid(xid))
 -      if (TransactionIdIsValid(xid))
++      if (!RecoveryInProgress() && TransactionIdIsValid(xid))
        {
                /* NB: xid must be known committed here! */
                XLogRecPtr      commitLSN = TransactionIdGetCommitLSN(xid);
index 44971d823d42e1397bf02efe31cd7359d62cc35c,f8720bbc2cdedfc090c13ed1644d85acdbed01cc..55198e97cc917ff6ac6928fa360301bbbfe3aaf3
@@@ -136,16 -133,7 +136,17 @@@ typedef struct XLogRecDat
  } XLogRecData;
  
  extern TimeLineID ThisTimeLineID;             /* current TLI */
 -extern bool InRecovery;
++
 +/* 
 + * Prior to 8.4, all activity during recovery were carried out by Startup
 + * process. This local variable continues to be used in many parts of the
 + * code to indicate actions taken by RecoveryManagers. Other processes who
 + * potentially perform work during recovery should check
 + * IsRecoveryProcessingMode(), see XLogCtl notes in xlog.c
 + */
 +extern bool InRecovery;       
 +extern bool InArchiveRecovery;
 +extern bool InHotStandby;
  extern XLogRecPtr XactLastRecEnd;
  
  /* these variables are GUC parameters related to XLOG */
@@@ -212,10 -199,7 +213,10 @@@ extern void RestoreBkpBlocks(XLogRecPt
  extern void xlog_redo(XLogRecPtr lsn, XLogRecord *record);
  extern void xlog_desc(StringInfo buf, uint8 xl_info, char *rec);
  
- extern bool IsRecoveryProcessingMode(void);
+ extern bool RecoveryInProgress(void);
 +extern int GetLatestReplicationDelay(void);
 +
 +extern void RestoreBkpBlocks(XLogRecPtr lsn, XLogRecord *record, bool cleanup);
  
  extern void UpdateControlFile(void);
  extern Size XLOGShmemSize(void);
Simple merge