* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.334 2009/03/11 23:19:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.338 2009/05/14 20:31:09 heikki Exp $
*
*-------------------------------------------------------------------------
*/
/* options taken from recovery.conf */
static char *recoveryRestoreCommand = NULL;
+static char *recoveryEndCommand = NULL;
static bool recoveryTarget = false;
static bool recoveryTargetExact = false;
static bool recoveryTargetInclusive = true;
static void XLogFileClose(void);
static bool RestoreArchivedFile(char *path, const char *xlogfname,
const char *recovername, off_t expectedSize);
+static void ExecuteRecoveryEndCommand(void);
static void PreallocXlogFiles(XLogRecPtr endptr);
static void RemoveOldXlogFiles(uint32 log, uint32 seg, XLogRecPtr endptr);
static void ValidateXLOGDirectoryStructure(void);
}
/*
+ * Attempt to execute the recovery_end_command.
+ */
+static void
+ExecuteRecoveryEndCommand(void)
+{
+ char xlogRecoveryEndCmd[MAXPGPATH];
+ char lastRestartPointFname[MAXPGPATH];
+ char *dp;
+ char *endp;
+ const char *sp;
+ int rc;
+ bool signaled;
+ uint32 restartLog;
+ uint32 restartSeg;
+
+ Assert(recoveryEndCommand);
+
+ /*
+ * Calculate the archive file cutoff point for use during log shipping
+ * replication. All files earlier than this point can be deleted
+ * from the archive, though there is no requirement to do so.
+ *
+ * We initialise this with the filename of an InvalidXLogRecPtr, which
+ * will prevent the deletion of any WAL files from the archive
+ * because of the alphabetic sorting property of WAL filenames.
+ *
+ * Once we have successfully located the redo pointer of the checkpoint
+ * from which we start recovery we never request a file prior to the redo
+ * pointer of the last restartpoint. When redo begins we know that we
+ * have successfully located it, so there is no need for additional
+ * status flags to signify the point when we can begin deleting WAL files
+ * from the archive.
+ */
+ if (InRedo)
+ {
+ XLByteToSeg(ControlFile->checkPointCopy.redo,
+ restartLog, restartSeg);
+ XLogFileName(lastRestartPointFname,
+ ControlFile->checkPointCopy.ThisTimeLineID,
+ restartLog, restartSeg);
+ }
+ else
+ XLogFileName(lastRestartPointFname, 0, 0, 0);
+
+ /*
+ * construct the command to be executed
+ */
+ dp = xlogRecoveryEndCmd;
+ endp = xlogRecoveryEndCmd + MAXPGPATH - 1;
+ *endp = '\0';
+
+ for (sp = recoveryEndCommand; *sp; sp++)
+ {
+ if (*sp == '%')
+ {
+ switch (sp[1])
+ {
+ case 'r':
+ /* %r: filename of last restartpoint */
+ sp++;
+ StrNCpy(dp, lastRestartPointFname, endp - dp);
+ dp += strlen(dp);
+ break;
+ case '%':
+ /* convert %% to a single % */
+ sp++;
+ if (dp < endp)
+ *dp++ = *sp;
+ break;
+ default:
+ /* otherwise treat the % as not special */
+ if (dp < endp)
+ *dp++ = *sp;
+ break;
+ }
+ }
+ else
+ {
+ if (dp < endp)
+ *dp++ = *sp;
+ }
+ }
+ *dp = '\0';
+
+ ereport(DEBUG3,
+ (errmsg_internal("executing recovery end command \"%s\"",
+ xlogRecoveryEndCmd)));
+
+ /*
+ * Copy xlog from archival storage to XLOGDIR
+ */
+ rc = system(xlogRecoveryEndCmd);
+ if (rc != 0)
+ {
+ /*
+ * If the failure was due to any sort of signal, it's best to punt and
+ * abort recovery. See also detailed comments on signals in
+ * RestoreArchivedFile().
+ */
+ signaled = WIFSIGNALED(rc) || WEXITSTATUS(rc) > 125;
+
+ ereport(signaled ? FATAL : WARNING,
+ (errmsg("recovery_end_command \"%s\": return code %d",
+ xlogRecoveryEndCmd, rc)));
+ }
+}
+
+/*
* Preallocate log files beyond the specified log endpoint.
*
* XXX this is currently extremely conservative, since it forces only one
(errmsg("restore_command = '%s'",
recoveryRestoreCommand)));
}
+ else if (strcmp(tok1, "recovery_end_command") == 0)
+ {
+ recoveryEndCommand = pstrdup(tok2);
+ ereport(LOG,
+ (errmsg("recovery_end_command = '%s'",
+ recoveryEndCommand)));
+ }
else if (strcmp(tok1, "recovery_target_timeline") == 0)
{
rtliGiven = true;
* If we are establishing a new timeline, we have to copy data from
* the last WAL segment of the old timeline to create a starting WAL
* segment for the new timeline.
+ *
+ * Notify the archiver that the last WAL segment of the old timeline
+ * is ready to copy to archival storage. Otherwise, it is not archived
+ * for a while.
*/
if (endTLI != ThisTimeLineID)
+ {
XLogFileCopy(endLogId, endLogSeg,
endTLI, endLogId, endLogSeg);
+
+ if (XLogArchivingActive())
+ {
+ XLogFileName(xlogpath, endTLI, endLogId, endLogSeg);
+ XLogArchiveNotify(xlogpath);
+ }
+ }
}
/*
* allows some extra error checking in xlog_redo.
*/
CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE);
+
+ if (recoveryEndCommand)
+ ExecuteRecoveryEndCommand();
}
/*
pg_start_backup(PG_FUNCTION_ARGS)
{
text *backupid = PG_GETARG_TEXT_P(0);
+ bool fast = PG_GETARG_BOOL(1);
char *backupidstr;
XLogRecPtr checkpointloc;
XLogRecPtr startpoint;
XLogCtl->Insert.forcePageWrites = true;
LWLockRelease(WALInsertLock);
+ /*
+ * Force an XLOG file switch before the checkpoint, to ensure that the WAL
+ * segment the checkpoint is written to doesn't contain pages with old
+ * timeline IDs. That would otherwise happen if you called
+ * pg_start_backup() right after restoring from a PITR archive: the first
+ * WAL segment containing the startup checkpoint has pages in the
+ * beginning with the old timeline ID. That can cause trouble at recovery:
+ * we won't have a history file covering the old timeline if pg_xlog
+ * directory was not included in the base backup and the WAL archive was
+ * cleared too before starting the backup.
+ */
+ RequestXLogSwitch();
+
/* Ensure we release forcePageWrites if fail below */
PG_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) 0);
{
* have different checkpoint positions and hence different history
* file names, even if nothing happened in between.
*
- * We don't use CHECKPOINT_IMMEDIATE, hence this can take awhile.
+ * We use CHECKPOINT_IMMEDIATE only if requested by user (via
+ * passing fast = true). Otherwise this can take awhile.
*/
- RequestCheckpoint(CHECKPOINT_FORCE | CHECKPOINT_WAIT);
+ RequestCheckpoint(CHECKPOINT_FORCE | CHECKPOINT_WAIT |
+ (fast ? CHECKPOINT_IMMEDIATE : 0));
/*
* Now we need to fetch the checkpoint record location, and also its