@@ -185,15 +185,17 @@ static bool recoveryTargetInclusive = true;
185185static bool recoveryPauseAtTarget = true;
186186static TransactionId recoveryTargetXid ;
187187static TimestampTz recoveryTargetTime ;
188+ static char * recoveryTargetName ;
188189
189190/* options taken from recovery.conf for XLOG streaming */
190191static bool StandbyMode = false;
191192static char * PrimaryConnInfo = NULL ;
192193static char * TriggerFile = NULL ;
193194
194- /* if recoveryStopsHere returns true, it saves actual stop xid/time here */
195+ /* if recoveryStopsHere returns true, it saves actual stop xid/time/name here */
195196static TransactionId recoveryStopXid ;
196197static TimestampTz recoveryStopTime ;
198+ static char recoveryStopName [MAXFNAMELEN ];
197199static bool recoveryStopAfter ;
198200
199201/*
@@ -551,6 +553,13 @@ typedef struct xl_parameter_change
551553 int wal_level ;
552554} xl_parameter_change ;
553555
556+ /* logs restore point */
557+ typedef struct xl_restore_point
558+ {
559+ TimestampTz rp_time ;
560+ char rp_name [MAXFNAMELEN ];
561+ } xl_restore_point ;
562+
554563/*
555564 * Flags set by interrupt handlers for later service in the redo loop.
556565 */
@@ -4391,6 +4400,13 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
43914400 xlogfname ,
43924401 recoveryStopAfter ? "after" : "before" ,
43934402 timestamptz_to_str (recoveryStopTime ));
4403+ else if (recoveryTarget == RECOVERY_TARGET_NAME )
4404+ snprintf (buffer , sizeof (buffer ),
4405+ "%s%u\t%s\tat restore point \"%s\"\n" ,
4406+ (srcfd < 0 ) ? "" : "\n" ,
4407+ parentTLI ,
4408+ xlogfname ,
4409+ recoveryStopName );
43944410 else
43954411 snprintf (buffer , sizeof (buffer ),
43964412 "%s%u\t%s\tno recovery target specified\n" ,
@@ -5178,10 +5194,11 @@ readRecoveryCommandFile(void)
51785194 else if (strcmp (item -> name , "recovery_target_time" ) == 0 )
51795195 {
51805196 /*
5181- * if recovery_target_xid specified, then this overrides
5182- * recovery_target_time
5197+ * if recovery_target_xid or recovery_target_name specified, then
5198+ * this overrides recovery_target_time
51835199 */
5184- if (recoveryTarget == RECOVERY_TARGET_XID )
5200+ if (recoveryTarget == RECOVERY_TARGET_XID ||
5201+ recoveryTarget == RECOVERY_TARGET_NAME )
51855202 continue ;
51865203 recoveryTarget = RECOVERY_TARGET_TIME ;
51875204
@@ -5197,6 +5214,26 @@ readRecoveryCommandFile(void)
51975214 (errmsg ("recovery_target_time = '%s'" ,
51985215 timestamptz_to_str (recoveryTargetTime ))));
51995216 }
5217+ else if (strcmp (item -> name , "recovery_target_name" ) == 0 )
5218+ {
5219+ /*
5220+ * if recovery_target_xid specified, then this overrides
5221+ * recovery_target_name
5222+ */
5223+ if (recoveryTarget == RECOVERY_TARGET_XID )
5224+ continue ;
5225+ recoveryTarget = RECOVERY_TARGET_NAME ;
5226+
5227+ recoveryTargetName = pstrdup (item -> value );
5228+ if (strlen (recoveryTargetName ) >= MAXFNAMELEN )
5229+ ereport (FATAL ,
5230+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
5231+ errmsg ("recovery_target_name is too long" )));
5232+
5233+ ereport (DEBUG2 ,
5234+ (errmsg ("recovery_target_name = '%s'" ,
5235+ recoveryTargetName )));
5236+ }
52005237 else if (strcmp (item -> name , "recovery_target_inclusive" ) == 0 )
52015238 {
52025239 /*
@@ -5411,8 +5448,8 @@ exitArchiveRecovery(TimeLineID endTLI, uint32 endLogId, uint32 endLogSeg)
54115448 * Returns TRUE if we are stopping, FALSE otherwise. On TRUE return,
54125449 * *includeThis is set TRUE if we should apply this record before stopping.
54135450 *
5414- * We also track the timestamp of the latest applied COMMIT/ABORT record
5415- * in XLogCtl->recoveryLastXTime, for logging purposes.
5451+ * We also track the timestamp of the latest applied COMMIT/ABORT/RESTORE POINT
5452+ * record in XLogCtl->recoveryLastXTime, for logging purposes.
54165453 * Also, some information is saved in recoveryStopXid et al for use in
54175454 * annotating the new timeline's history file.
54185455 */
@@ -5422,9 +5459,10 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
54225459 bool stopsHere ;
54235460 uint8 record_info ;
54245461 TimestampTz recordXtime ;
5462+ char recordRPName [MAXFNAMELEN ];
54255463
5426- /* We only consider stopping at COMMIT or ABORT records */
5427- if (record -> xl_rmid != RM_XACT_ID )
5464+ /* We only consider stopping at COMMIT, ABORT or RESTORE POINT records */
5465+ if (record -> xl_rmid != RM_XACT_ID && record -> xl_rmid != RM_XLOG_ID )
54285466 return false;
54295467 record_info = record -> xl_info & ~XLR_INFO_MASK ;
54305468 if (record_info == XLOG_XACT_COMMIT )
@@ -5441,6 +5479,14 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
54415479 recordXactAbortData = (xl_xact_abort * ) XLogRecGetData (record );
54425480 recordXtime = recordXactAbortData -> xact_time ;
54435481 }
5482+ else if (record_info == XLOG_RESTORE_POINT )
5483+ {
5484+ xl_restore_point * recordRestorePointData ;
5485+
5486+ recordRestorePointData = (xl_restore_point * ) XLogRecGetData (record );
5487+ recordXtime = recordRestorePointData -> rp_time ;
5488+ strncpy (recordRPName , recordRestorePointData -> rp_name , MAXFNAMELEN );
5489+ }
54445490 else
54455491 return false;
54465492
@@ -5466,6 +5512,20 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
54665512 if (stopsHere )
54675513 * includeThis = recoveryTargetInclusive ;
54685514 }
5515+ else if (recoveryTarget == RECOVERY_TARGET_NAME )
5516+ {
5517+ /*
5518+ * there can be many restore points that share the same name, so we stop
5519+ * at the first one
5520+ */
5521+ stopsHere = (strcmp (recordRPName , recoveryTargetName ) == 0 );
5522+
5523+ /*
5524+ * ignore recoveryTargetInclusive because this is not a transaction
5525+ * record
5526+ */
5527+ * includeThis = false;
5528+ }
54695529 else
54705530 {
54715531 /*
@@ -5500,7 +5560,7 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
55005560 recoveryStopXid ,
55015561 timestamptz_to_str (recoveryStopTime ))));
55025562 }
5503- else
5563+ else if ( record_info == XLOG_XACT_ABORT )
55045564 {
55055565 if (recoveryStopAfter )
55065566 ereport (LOG ,
@@ -5513,6 +5573,15 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
55135573 recoveryStopXid ,
55145574 timestamptz_to_str (recoveryStopTime ))));
55155575 }
5576+ else
5577+ {
5578+ strncpy (recoveryStopName , recordRPName , MAXFNAMELEN );
5579+
5580+ ereport (LOG ,
5581+ (errmsg ("recovery stopping at restore point \"%s\", time %s" ,
5582+ recoveryStopName ,
5583+ timestamptz_to_str (recoveryStopTime ))));
5584+ }
55165585
55175586 if (recoveryStopAfter )
55185587 SetLatestXTime (recordXtime );
@@ -5900,6 +5969,10 @@ StartupXLOG(void)
59005969 ereport (LOG ,
59015970 (errmsg ("starting point-in-time recovery to %s" ,
59025971 timestamptz_to_str (recoveryTargetTime ))));
5972+ else if (recoveryTarget == RECOVERY_TARGET_NAME )
5973+ ereport (LOG ,
5974+ (errmsg ("starting point-in-time recovery to \"%s\"" ,
5975+ recoveryTargetName )));
59035976 else
59045977 ereport (LOG ,
59055978 (errmsg ("starting archive recovery" )));
@@ -7989,6 +8062,29 @@ RequestXLogSwitch(void)
79898062 return RecPtr ;
79908063}
79918064
8065+ /*
8066+ * Write a RESTORE POINT record
8067+ */
8068+ XLogRecPtr
8069+ XLogRestorePoint (const char * rpName )
8070+ {
8071+ XLogRecPtr RecPtr ;
8072+ XLogRecData rdata ;
8073+ xl_restore_point xlrec ;
8074+
8075+ xlrec .rp_time = GetCurrentTimestamp ();
8076+ strncpy (xlrec .rp_name , rpName , MAXFNAMELEN );
8077+
8078+ rdata .buffer = InvalidBuffer ;
8079+ rdata .data = (char * ) & xlrec ;
8080+ rdata .len = sizeof (xl_restore_point );
8081+ rdata .next = NULL ;
8082+
8083+ RecPtr = XLogInsert (RM_XLOG_ID , XLOG_RESTORE_POINT , & rdata );
8084+
8085+ return RecPtr ;
8086+ }
8087+
79928088/*
79938089 * Check if any of the GUC parameters that are critical for hot standby
79948090 * have changed, and update the value in pg_control file if necessary.
@@ -8181,6 +8277,10 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
81818277 {
81828278 /* nothing to do here */
81838279 }
8280+ else if (info == XLOG_RESTORE_POINT )
8281+ {
8282+ /* nothing to do here */
8283+ }
81848284 else if (info == XLOG_BACKUP_END )
81858285 {
81868286 XLogRecPtr startpoint ;
@@ -8283,6 +8383,13 @@ xlog_desc(StringInfo buf, uint8 xl_info, char *rec)
82838383 {
82848384 appendStringInfo (buf , "xlog switch" );
82858385 }
8386+ else if (info == XLOG_RESTORE_POINT )
8387+ {
8388+ xl_restore_point * xlrec = (xl_restore_point * ) rec ;
8389+
8390+ appendStringInfo (buf , "restore point: %s" , xlrec -> rp_name );
8391+
8392+ }
82868393 else if (info == XLOG_BACKUP_END )
82878394 {
82888395 XLogRecPtr startpoint ;
@@ -9080,6 +9187,51 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
90809187 PG_RETURN_TEXT_P (cstring_to_text (location ));
90819188}
90829189
9190+ /*
9191+ * pg_create_restore_point: a named point for restore
9192+ */
9193+ Datum
9194+ pg_create_restore_point (PG_FUNCTION_ARGS )
9195+ {
9196+ text * restore_name = PG_GETARG_TEXT_P (0 );
9197+ char * restore_name_str ;
9198+ XLogRecPtr restorepoint ;
9199+ char location [MAXFNAMELEN ];
9200+
9201+ if (!superuser ())
9202+ ereport (ERROR ,
9203+ (errcode (ERRCODE_INSUFFICIENT_PRIVILEGE ),
9204+ (errmsg ("must be superuser to create a restore point" ))));
9205+
9206+ if (RecoveryInProgress ())
9207+ ereport (ERROR ,
9208+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
9209+ (errmsg ("recovery is in progress" ),
9210+ errhint ("WAL control functions cannot be executed during recovery." ))));
9211+
9212+ if (!XLogIsNeeded ())
9213+ ereport (ERROR ,
9214+ (errcode (ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE ),
9215+ errmsg ("WAL level not sufficient for creating a restore point" ),
9216+ errhint ("wal_level must be set to \"archive\" or \"hot_standby\" at server start." )));
9217+
9218+ restore_name_str = text_to_cstring (restore_name );
9219+
9220+ if (strlen (restore_name_str ) >= MAXFNAMELEN )
9221+ ereport (ERROR ,
9222+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
9223+ errmsg ("value too long for restore point" )));
9224+
9225+ restorepoint = XLogRestorePoint (restore_name_str );
9226+
9227+ /*
9228+ * As a convenience, return the WAL location of the restore point record
9229+ */
9230+ snprintf (location , sizeof (location ), "%X/%X" ,
9231+ restorepoint .xlogid , restorepoint .xrecoff );
9232+ PG_RETURN_TEXT_P (cstring_to_text (location ));
9233+ }
9234+
90839235/*
90849236 * Report the current WAL write location (same format as pg_start_backup etc)
90859237 *
0 commit comments