diff options
author | Simon Riggs | 2011-02-08 19:39:08 +0000 |
---|---|---|
committer | Simon Riggs | 2011-02-08 19:39:08 +0000 |
commit | c016ce728139be95bb0dc7c4e5640507334c2339 (patch) | |
tree | 43b1708d3a4f637ddd5dcaa0474e8f9bedc9508e /src/backend/access/transam/xlog.c | |
parent | 8c6e3adbf792c2bba448e88cbf2c8e03fb802e73 (diff) |
Named restore points in recovery. Users can record named points, then
new recovery.conf parameter recovery_target_name allows PITR to
specify named points as recovery targets.
Jaime Casanova, reviewed by Euler Taveira de Oliveira, plus minor edits
Diffstat (limited to 'src/backend/access/transam/xlog.c')
-rw-r--r-- | src/backend/access/transam/xlog.c | 170 |
1 files changed, 161 insertions, 9 deletions
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 5ba3e26f81e..72a1a158c07 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -185,15 +185,17 @@ static bool recoveryTargetInclusive = true; static bool recoveryPauseAtTarget = true; static TransactionId recoveryTargetXid; static TimestampTz recoveryTargetTime; +static char *recoveryTargetName; /* options taken from recovery.conf for XLOG streaming */ static bool StandbyMode = false; static char *PrimaryConnInfo = NULL; static char *TriggerFile = NULL; -/* if recoveryStopsHere returns true, it saves actual stop xid/time here */ +/* if recoveryStopsHere returns true, it saves actual stop xid/time/name here */ static TransactionId recoveryStopXid; static TimestampTz recoveryStopTime; +static char recoveryStopName[MAXFNAMELEN]; static bool recoveryStopAfter; /* @@ -551,6 +553,13 @@ typedef struct xl_parameter_change int wal_level; } xl_parameter_change; +/* logs restore point */ +typedef struct xl_restore_point +{ + TimestampTz rp_time; + char rp_name[MAXFNAMELEN]; +} xl_restore_point; + /* * Flags set by interrupt handlers for later service in the redo loop. */ @@ -4391,6 +4400,13 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI, xlogfname, recoveryStopAfter ? "after" : "before", timestamptz_to_str(recoveryStopTime)); + else if (recoveryTarget == RECOVERY_TARGET_NAME) + snprintf(buffer, sizeof(buffer), + "%s%u\t%s\tat restore point \"%s\"\n", + (srcfd < 0) ? "" : "\n", + parentTLI, + xlogfname, + recoveryStopName); else snprintf(buffer, sizeof(buffer), "%s%u\t%s\tno recovery target specified\n", @@ -5178,10 +5194,11 @@ readRecoveryCommandFile(void) else if (strcmp(item->name, "recovery_target_time") == 0) { /* - * if recovery_target_xid specified, then this overrides - * recovery_target_time + * if recovery_target_xid or recovery_target_name specified, then + * this overrides recovery_target_time */ - if (recoveryTarget == RECOVERY_TARGET_XID) + if (recoveryTarget == RECOVERY_TARGET_XID || + recoveryTarget == RECOVERY_TARGET_NAME) continue; recoveryTarget = RECOVERY_TARGET_TIME; @@ -5197,6 +5214,26 @@ readRecoveryCommandFile(void) (errmsg("recovery_target_time = '%s'", timestamptz_to_str(recoveryTargetTime)))); } + else if (strcmp(item->name, "recovery_target_name") == 0) + { + /* + * if recovery_target_xid specified, then this overrides + * recovery_target_name + */ + if (recoveryTarget == RECOVERY_TARGET_XID) + continue; + recoveryTarget = RECOVERY_TARGET_NAME; + + recoveryTargetName = pstrdup(item->value); + if (strlen(recoveryTargetName) >= MAXFNAMELEN) + ereport(FATAL, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("recovery_target_name is too long"))); + + ereport(DEBUG2, + (errmsg("recovery_target_name = '%s'", + recoveryTargetName))); + } else if (strcmp(item->name, "recovery_target_inclusive") == 0) { /* @@ -5411,8 +5448,8 @@ exitArchiveRecovery(TimeLineID endTLI, uint32 endLogId, uint32 endLogSeg) * Returns TRUE if we are stopping, FALSE otherwise. On TRUE return, * *includeThis is set TRUE if we should apply this record before stopping. * - * We also track the timestamp of the latest applied COMMIT/ABORT record - * in XLogCtl->recoveryLastXTime, for logging purposes. + * We also track the timestamp of the latest applied COMMIT/ABORT/RESTORE POINT + * record in XLogCtl->recoveryLastXTime, for logging purposes. * Also, some information is saved in recoveryStopXid et al for use in * annotating the new timeline's history file. */ @@ -5422,9 +5459,10 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) bool stopsHere; uint8 record_info; TimestampTz recordXtime; + char recordRPName[MAXFNAMELEN]; - /* We only consider stopping at COMMIT or ABORT records */ - if (record->xl_rmid != RM_XACT_ID) + /* We only consider stopping at COMMIT, ABORT or RESTORE POINT records */ + if (record->xl_rmid != RM_XACT_ID && record->xl_rmid != RM_XLOG_ID) return false; record_info = record->xl_info & ~XLR_INFO_MASK; if (record_info == XLOG_XACT_COMMIT) @@ -5441,6 +5479,14 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) recordXactAbortData = (xl_xact_abort *) XLogRecGetData(record); recordXtime = recordXactAbortData->xact_time; } + else if (record_info == XLOG_RESTORE_POINT) + { + xl_restore_point *recordRestorePointData; + + recordRestorePointData = (xl_restore_point *) XLogRecGetData(record); + recordXtime = recordRestorePointData->rp_time; + strncpy(recordRPName, recordRestorePointData->rp_name, MAXFNAMELEN); + } else return false; @@ -5466,6 +5512,20 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) if (stopsHere) *includeThis = recoveryTargetInclusive; } + else if (recoveryTarget == RECOVERY_TARGET_NAME) + { + /* + * there can be many restore points that share the same name, so we stop + * at the first one + */ + stopsHere = (strcmp(recordRPName, recoveryTargetName) == 0); + + /* + * ignore recoveryTargetInclusive because this is not a transaction + * record + */ + *includeThis = false; + } else { /* @@ -5500,7 +5560,7 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) recoveryStopXid, timestamptz_to_str(recoveryStopTime)))); } - else + else if (record_info == XLOG_XACT_ABORT) { if (recoveryStopAfter) ereport(LOG, @@ -5513,6 +5573,15 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) recoveryStopXid, timestamptz_to_str(recoveryStopTime)))); } + else + { + strncpy(recoveryStopName, recordRPName, MAXFNAMELEN); + + ereport(LOG, + (errmsg("recovery stopping at restore point \"%s\", time %s", + recoveryStopName, + timestamptz_to_str(recoveryStopTime)))); + } if (recoveryStopAfter) SetLatestXTime(recordXtime); @@ -5900,6 +5969,10 @@ StartupXLOG(void) ereport(LOG, (errmsg("starting point-in-time recovery to %s", timestamptz_to_str(recoveryTargetTime)))); + else if (recoveryTarget == RECOVERY_TARGET_NAME) + ereport(LOG, + (errmsg("starting point-in-time recovery to \"%s\"", + recoveryTargetName))); else ereport(LOG, (errmsg("starting archive recovery"))); @@ -7990,6 +8063,29 @@ RequestXLogSwitch(void) } /* + * Write a RESTORE POINT record + */ +XLogRecPtr +XLogRestorePoint(const char *rpName) +{ + XLogRecPtr RecPtr; + XLogRecData rdata; + xl_restore_point xlrec; + + xlrec.rp_time = GetCurrentTimestamp(); + strncpy(xlrec.rp_name, rpName, MAXFNAMELEN); + + rdata.buffer = InvalidBuffer; + rdata.data = (char *) &xlrec; + rdata.len = sizeof(xl_restore_point); + rdata.next = NULL; + + RecPtr = XLogInsert(RM_XLOG_ID, XLOG_RESTORE_POINT, &rdata); + + return RecPtr; +} + +/* * Check if any of the GUC parameters that are critical for hot standby * have changed, and update the value in pg_control file if necessary. */ @@ -8181,6 +8277,10 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record) { /* nothing to do here */ } + else if (info == XLOG_RESTORE_POINT) + { + /* nothing to do here */ + } else if (info == XLOG_BACKUP_END) { XLogRecPtr startpoint; @@ -8283,6 +8383,13 @@ xlog_desc(StringInfo buf, uint8 xl_info, char *rec) { appendStringInfo(buf, "xlog switch"); } + else if (info == XLOG_RESTORE_POINT) + { + xl_restore_point *xlrec = (xl_restore_point *) rec; + + appendStringInfo(buf, "restore point: %s", xlrec->rp_name); + + } else if (info == XLOG_BACKUP_END) { XLogRecPtr startpoint; @@ -9081,6 +9188,51 @@ pg_switch_xlog(PG_FUNCTION_ARGS) } /* + * pg_create_restore_point: a named point for restore + */ +Datum +pg_create_restore_point(PG_FUNCTION_ARGS) +{ + text *restore_name = PG_GETARG_TEXT_P(0); + char *restore_name_str; + XLogRecPtr restorepoint; + char location[MAXFNAMELEN]; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to create a restore point")))); + + 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 (!XLogIsNeeded()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("WAL level not sufficient for creating a restore point"), + errhint("wal_level must be set to \"archive\" or \"hot_standby\" at server start."))); + + restore_name_str = text_to_cstring(restore_name); + + if (strlen(restore_name_str) >= MAXFNAMELEN) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("value too long for restore point"))); + + restorepoint = XLogRestorePoint(restore_name_str); + + /* + * As a convenience, return the WAL location of the restore point record + */ + snprintf(location, sizeof(location), "%X/%X", + restorepoint.xlogid, restorepoint.xrecoff); + PG_RETURN_TEXT_P(cstring_to_text(location)); +} + +/* * Report the current WAL write location (same format as pg_start_backup etc) * * This is useful for determining how much of WAL is visible to an external |