@@ -185,15 +185,17 @@ static bool recoveryTargetInclusive = true;
185
185
static bool recoveryPauseAtTarget = true;
186
186
static TransactionId recoveryTargetXid ;
187
187
static TimestampTz recoveryTargetTime ;
188
+ static char * recoveryTargetName ;
188
189
189
190
/* options taken from recovery.conf for XLOG streaming */
190
191
static bool StandbyMode = false;
191
192
static char * PrimaryConnInfo = NULL ;
192
193
static char * TriggerFile = NULL ;
193
194
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 */
195
196
static TransactionId recoveryStopXid ;
196
197
static TimestampTz recoveryStopTime ;
198
+ static char recoveryStopName [MAXFNAMELEN ];
197
199
static bool recoveryStopAfter ;
198
200
199
201
/*
@@ -551,6 +553,13 @@ typedef struct xl_parameter_change
551
553
int wal_level ;
552
554
} xl_parameter_change ;
553
555
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
+
554
563
/*
555
564
* Flags set by interrupt handlers for later service in the redo loop.
556
565
*/
@@ -4391,6 +4400,13 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
4391
4400
xlogfname ,
4392
4401
recoveryStopAfter ? "after" : "before" ,
4393
4402
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 );
4394
4410
else
4395
4411
snprintf (buffer , sizeof (buffer ),
4396
4412
"%s%u\t%s\tno recovery target specified\n" ,
@@ -5178,10 +5194,11 @@ readRecoveryCommandFile(void)
5178
5194
else if (strcmp (item -> name , "recovery_target_time" ) == 0 )
5179
5195
{
5180
5196
/*
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
5183
5199
*/
5184
- if (recoveryTarget == RECOVERY_TARGET_XID )
5200
+ if (recoveryTarget == RECOVERY_TARGET_XID ||
5201
+ recoveryTarget == RECOVERY_TARGET_NAME )
5185
5202
continue ;
5186
5203
recoveryTarget = RECOVERY_TARGET_TIME ;
5187
5204
@@ -5197,6 +5214,26 @@ readRecoveryCommandFile(void)
5197
5214
(errmsg ("recovery_target_time = '%s'" ,
5198
5215
timestamptz_to_str (recoveryTargetTime ))));
5199
5216
}
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
+ }
5200
5237
else if (strcmp (item -> name , "recovery_target_inclusive" ) == 0 )
5201
5238
{
5202
5239
/*
@@ -5411,8 +5448,8 @@ exitArchiveRecovery(TimeLineID endTLI, uint32 endLogId, uint32 endLogSeg)
5411
5448
* Returns TRUE if we are stopping, FALSE otherwise. On TRUE return,
5412
5449
* *includeThis is set TRUE if we should apply this record before stopping.
5413
5450
*
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.
5416
5453
* Also, some information is saved in recoveryStopXid et al for use in
5417
5454
* annotating the new timeline's history file.
5418
5455
*/
@@ -5422,9 +5459,10 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
5422
5459
bool stopsHere ;
5423
5460
uint8 record_info ;
5424
5461
TimestampTz recordXtime ;
5462
+ char recordRPName [MAXFNAMELEN ];
5425
5463
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 )
5428
5466
return false;
5429
5467
record_info = record -> xl_info & ~XLR_INFO_MASK ;
5430
5468
if (record_info == XLOG_XACT_COMMIT )
@@ -5441,6 +5479,14 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
5441
5479
recordXactAbortData = (xl_xact_abort * ) XLogRecGetData (record );
5442
5480
recordXtime = recordXactAbortData -> xact_time ;
5443
5481
}
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
+ }
5444
5490
else
5445
5491
return false;
5446
5492
@@ -5466,6 +5512,20 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
5466
5512
if (stopsHere )
5467
5513
* includeThis = recoveryTargetInclusive ;
5468
5514
}
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
+ }
5469
5529
else
5470
5530
{
5471
5531
/*
@@ -5500,7 +5560,7 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
5500
5560
recoveryStopXid ,
5501
5561
timestamptz_to_str (recoveryStopTime ))));
5502
5562
}
5503
- else
5563
+ else if ( record_info == XLOG_XACT_ABORT )
5504
5564
{
5505
5565
if (recoveryStopAfter )
5506
5566
ereport (LOG ,
@@ -5513,6 +5573,15 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis)
5513
5573
recoveryStopXid ,
5514
5574
timestamptz_to_str (recoveryStopTime ))));
5515
5575
}
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
+ }
5516
5585
5517
5586
if (recoveryStopAfter )
5518
5587
SetLatestXTime (recordXtime );
@@ -5900,6 +5969,10 @@ StartupXLOG(void)
5900
5969
ereport (LOG ,
5901
5970
(errmsg ("starting point-in-time recovery to %s" ,
5902
5971
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 )));
5903
5976
else
5904
5977
ereport (LOG ,
5905
5978
(errmsg ("starting archive recovery" )));
@@ -7989,6 +8062,29 @@ RequestXLogSwitch(void)
7989
8062
return RecPtr ;
7990
8063
}
7991
8064
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
+
7992
8088
/*
7993
8089
* Check if any of the GUC parameters that are critical for hot standby
7994
8090
* have changed, and update the value in pg_control file if necessary.
@@ -8181,6 +8277,10 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
8181
8277
{
8182
8278
/* nothing to do here */
8183
8279
}
8280
+ else if (info == XLOG_RESTORE_POINT )
8281
+ {
8282
+ /* nothing to do here */
8283
+ }
8184
8284
else if (info == XLOG_BACKUP_END )
8185
8285
{
8186
8286
XLogRecPtr startpoint ;
@@ -8283,6 +8383,13 @@ xlog_desc(StringInfo buf, uint8 xl_info, char *rec)
8283
8383
{
8284
8384
appendStringInfo (buf , "xlog switch" );
8285
8385
}
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
+ }
8286
8393
else if (info == XLOG_BACKUP_END )
8287
8394
{
8288
8395
XLogRecPtr startpoint ;
@@ -9080,6 +9187,51 @@ pg_switch_xlog(PG_FUNCTION_ARGS)
9080
9187
PG_RETURN_TEXT_P (cstring_to_text (location ));
9081
9188
}
9082
9189
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
+
9083
9235
/*
9084
9236
* Report the current WAL write location (same format as pg_start_backup etc)
9085
9237
*
0 commit comments