aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/transam/recovery.conf.sample7
-rw-r--r--src/backend/access/transam/xlog.c170
-rw-r--r--src/include/access/xlog.h4
-rw-r--r--src/include/access/xlog_internal.h1
-rw-r--r--src/include/catalog/pg_control.h1
-rw-r--r--src/include/catalog/pg_proc.h1
6 files changed, 172 insertions, 12 deletions
diff --git a/src/backend/access/transam/recovery.conf.sample b/src/backend/access/transam/recovery.conf.sample
index f8f6f9b1083..0243b51fdf9 100644
--- a/src/backend/access/transam/recovery.conf.sample
+++ b/src/backend/access/transam/recovery.conf.sample
@@ -66,11 +66,14 @@
# If you want to stop rollforward at a specific point, you
# must set a recovery target.
#
-# You may set a recovery target either by transactionId, or
-# by timestamp. Recovery may either include or exclude the
+# You may set a recovery target either by transactionId, by name,
+# or by timestamp. Recovery may either include or exclude the
# transaction(s) with the recovery target value (ie, stop either
# just after or just before the given target, respectively).
#
+#
+#recovery_target_name = '' # e.g. 'daily backup 2011-01-26'
+#
#recovery_target_time = '' # e.g. '2004-07-14 22:39:00 EST'
#
#recovery_target_xid = ''
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
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 122e96b5d11..e7adead9a28 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -184,7 +184,8 @@ typedef enum
{
RECOVERY_TARGET_UNSET,
RECOVERY_TARGET_XID,
- RECOVERY_TARGET_TIME
+ RECOVERY_TARGET_TIME,
+ RECOVERY_TARGET_NAME
} RecoveryTargetType;
extern XLogRecPtr XactLastRecEnd;
@@ -302,6 +303,7 @@ extern void InitXLOGAccess(void);
extern void CreateCheckPoint(int flags);
extern bool CreateRestartPoint(int flags);
extern void XLogPutNextOid(Oid nextOid);
+extern XLogRecPtr XLogRestorePoint(const char *rpName);
extern XLogRecPtr GetRedoRecPtr(void);
extern XLogRecPtr GetInsertRecPtr(void);
extern XLogRecPtr GetFlushRecPtr(void);
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index 6390113de37..eeccdce31d0 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -267,6 +267,7 @@ extern XLogRecPtr RequestXLogSwitch(void);
extern Datum pg_start_backup(PG_FUNCTION_ARGS);
extern Datum pg_stop_backup(PG_FUNCTION_ARGS);
extern Datum pg_switch_xlog(PG_FUNCTION_ARGS);
+extern Datum pg_create_restore_point(PG_FUNCTION_ARGS);
extern Datum pg_current_xlog_location(PG_FUNCTION_ARGS);
extern Datum pg_current_xlog_insert_location(PG_FUNCTION_ARGS);
extern Datum pg_last_xlog_receive_location(PG_FUNCTION_ARGS);
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index ddf139857d0..fb458acf831 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -59,6 +59,7 @@ typedef struct CheckPoint
#define XLOG_SWITCH 0x40
#define XLOG_BACKUP_END 0x50
#define XLOG_PARAMETER_CHANGE 0x60
+#define XLOG_RESTORE_POINT 0x70
/*
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 734f43a1e45..30ff1b5bb9f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3397,6 +3397,7 @@ DATA(insert OID = 2173 ( pg_stop_backup PGNSP PGUID 12 1 0 0 f f f t f v 0 0 2
DESCR("finish taking an online backup");
DATA(insert OID = 2848 ( pg_switch_xlog PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_switch_xlog _null_ _null_ _null_ ));
DESCR("switch to new xlog file");
+DATA(insert OID = 3098 ( pg_create_restore_point PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_create_restore_point _null_ _null_ _null_ ));
DATA(insert OID = 2849 ( pg_current_xlog_location PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_current_xlog_location _null_ _null_ _null_ ));
DESCR("current xlog write location");
DATA(insert OID = 2852 ( pg_current_xlog_insert_location PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_current_xlog_insert_location _null_ _null_ _null_ ));