@@ -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
551553int 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,
43914400xlogfname ,
43924401recoveryStopAfter ?"after" :"before" ,
43934402timestamptz_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 );
43944410else
43954411snprintf (buffer ,sizeof (buffer ),
43964412"%s%u\t%s\tno recovery target specified\n" ,
@@ -5178,10 +5194,11 @@ readRecoveryCommandFile(void)
51785194else 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_xidor 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 )
51855202continue ;
51865203recoveryTarget = RECOVERY_TARGET_TIME ;
51875204
@@ -5197,6 +5214,26 @@ readRecoveryCommandFile(void)
51975214(errmsg ("recovery_target_time = '%s'" ,
51985215timestamptz_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+ }
52005237else 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)
54225459bool stopsHere ;
54235460uint8 record_info ;
54245461TimestampTz recordXtime ;
5462+ char recordRPName [MAXFNAMELEN ];
54255463
5426- /* We only consider stopping at COMMIT orABORT records */
5427- if (record -> xl_rmid != RM_XACT_ID )
5464+ /* We only consider stopping at COMMIT, ABORT orRESTORE POINT records */
5465+ if (record -> xl_rmid != RM_XACT_ID && record -> xl_rmid != RM_XLOG_ID )
54285466return false;
54295467record_info = record -> xl_info & ~XLR_INFO_MASK ;
54305468if (record_info == XLOG_XACT_COMMIT )
@@ -5441,6 +5479,14 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
54415479recordXactAbortData = (xl_xact_abort * )XLogRecGetData (record );
54425480recordXtime = 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+ }
54445490else
54455491return false;
54465492
@@ -5466,6 +5512,20 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
54665512if (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+ }
54695529else
54705530{
54715531/*
@@ -5500,7 +5560,7 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
55005560recoveryStopXid ,
55015561timestamptz_to_str (recoveryStopTime ))));
55025562}
5503- else
5563+ else if ( record_info == XLOG_XACT_ABORT )
55045564{
55055565if (recoveryStopAfter )
55065566ereport (LOG ,
@@ -5513,6 +5573,15 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
55135573recoveryStopXid ,
55145574timestamptz_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
55175586if (recoveryStopAfter )
55185587SetLatestXTime (recordXtime );
@@ -5900,6 +5969,10 @@ StartupXLOG(void)
59005969ereport (LOG ,
59015970(errmsg ("starting point-in-time recovery to %s" ,
59025971timestamptz_to_str (recoveryTargetTime ))));
5972+ else if (recoveryTarget == RECOVERY_TARGET_NAME )
5973+ ereport (LOG ,
5974+ (errmsg ("starting point-in-time recovery to \"%s\"" ,
5975+ recoveryTargetName )));
59035976else
59045977ereport (LOG ,
59055978(errmsg ("starting archive recovery" )));
@@ -7989,6 +8062,29 @@ RequestXLogSwitch(void)
79898062return 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+ }
81848284else if (info == XLOG_BACKUP_END )
81858285{
81868286XLogRecPtr startpoint ;
@@ -8283,6 +8383,13 @@ xlog_desc(StringInfo buf, uint8 xl_info, char *rec)
82838383{
82848384appendStringInfo (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+ }
82868393else if (info == XLOG_BACKUP_END )
82878394{
82888395XLogRecPtr startpoint ;
@@ -9080,6 +9187,51 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
90809187PG_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 *