Skip to content

Commit 9a7daf0

Browse files
committed
Merge branch 'pgpro-1643-squashed'
2 parents 144b852 + 586e336 commit 9a7daf0

File tree

12 files changed

+265
-16
lines changed

12 files changed

+265
-16
lines changed

src/help.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ help_pg_probackup(void)
116116

117117
printf(_("\n %s restore -B backup-dir --instance=instance_name\n"), PROGRAM_NAME);
118118
printf(_(" [-D pgdata-dir] [-i backup-id] [--progress]\n"));
119-
printf(_(" [--time=time|--xid=xid [--inclusive=boolean]]\n"));
119+
printf(_(" [--time=time|--xid=xid|--lsn=lsn [--inclusive=boolean]]\n"));
120120
printf(_(" [--timeline=timeline] [-T OLDDIR=NEWDIR]\n"));
121121
printf(_(" [--immediate] [--recovery-target-name=target-name]\n"));
122122
printf(_(" [--recovery-target-action=pause|promote|shutdown]\n"));
@@ -125,7 +125,7 @@ help_pg_probackup(void)
125125

126126
printf(_("\n %s validate -B backup-dir [--instance=instance_name]\n"), PROGRAM_NAME);
127127
printf(_(" [-i backup-id] [--progress]\n"));
128-
printf(_(" [--time=time|--xid=xid [--inclusive=boolean]]\n"));
128+
printf(_(" [--time=time|--xid=xid|--lsn=lsn [--inclusive=boolean]]\n"));
129129
printf(_(" [--recovery-target-name=target-name]\n"));
130130
printf(_(" [--timeline=timeline]\n"));
131131

@@ -265,7 +265,7 @@ help_restore(void)
265265
{
266266
printf(_("%s restore -B backup-dir --instance=instance_name\n"), PROGRAM_NAME);
267267
printf(_(" [-D pgdata-dir] [-i backup-id] [--progress]\n"));
268-
printf(_(" [--time=time|--xid=xid [--inclusive=boolean]]\n"));
268+
printf(_(" [--time=time|--xid=xid|--lsn=lsn [--inclusive=boolean]]\n"));
269269
printf(_(" [--timeline=timeline] [-T OLDDIR=NEWDIR]\n"));
270270
printf(_(" [--immediate] [--recovery-target-name=target-name]\n"));
271271
printf(_(" [--recovery-target-action=pause|promote|shutdown]\n"));
@@ -280,6 +280,7 @@ help_restore(void)
280280
printf(_(" --progress show progress\n"));
281281
printf(_(" --time=time time stamp up to which recovery will proceed\n"));
282282
printf(_(" --xid=xid transaction ID up to which recovery will proceed\n"));
283+
printf(_(" --lsn=lsn LSN of the write-ahead log location up to which recovery will proceed\n"));
283284
printf(_(" --inclusive=boolean whether we stop just after the recovery target\n"));
284285
printf(_(" --timeline=timeline recovering into a particular timeline\n"));
285286
printf(_(" -T, --tablespace-mapping=OLDDIR=NEWDIR\n"));
@@ -323,7 +324,7 @@ help_validate(void)
323324
{
324325
printf(_("%s validate -B backup-dir [--instance=instance_name]\n"), PROGRAM_NAME);
325326
printf(_(" [-i backup-id] [--progress]\n"));
326-
printf(_(" [--time=time|--xid=xid [--inclusive=boolean]]\n"));
327+
printf(_(" [--time=time|--xid=xid|--lsn=lsn [--inclusive=boolean]]\n"));
327328
printf(_(" [--timeline=timeline]\n\n"));
328329

329330
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
@@ -333,6 +334,7 @@ help_validate(void)
333334
printf(_(" --progress show progress\n"));
334335
printf(_(" --time=time time stamp up to which recovery will proceed\n"));
335336
printf(_(" --xid=xid transaction ID up to which recovery will proceed\n"));
337+
printf(_(" --lsn=lsn LSN of the write-ahead log location up to which recovery will proceed\n"));
336338
printf(_(" --inclusive=boolean whether we stop just after the recovery target\n"));
337339
printf(_(" --timeline=timeline recovering into a particular timeline\n"));
338340
printf(_(" --recovery-target-name=target-name\n"));

src/parsexlog.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,7 @@ validate_wal(pgBackup *backup,
421421
const char *archivedir,
422422
time_t target_time,
423423
TransactionId target_xid,
424+
XLogRecPtr target_lsn,
424425
TimeLineID tli)
425426
{
426427
XLogRecPtr startpoint = backup->start_lsn;
@@ -472,7 +473,7 @@ validate_wal(pgBackup *backup,
472473
* If recovery target is provided check that we can restore backup to a
473474
* recovery target time or xid.
474475
*/
475-
if (!TransactionIdIsValid(target_xid) && target_time == 0)
476+
if (!TransactionIdIsValid(target_xid) && target_time == 0 && !XRecOffIsValid(target_lsn))
476477
{
477478
/* Recovery target is not given so exit */
478479
elog(INFO, "Backup %s WAL segments are valid", backup_id);
@@ -498,7 +499,8 @@ validate_wal(pgBackup *backup,
498499
last_xid = backup->recovery_xid;
499500

500501
if ((TransactionIdIsValid(target_xid) && target_xid == last_xid)
501-
|| (target_time != 0 && backup->recovery_time >= target_time))
502+
|| (target_time != 0 && backup->recovery_time >= target_time)
503+
|| (XRecOffIsValid(target_lsn) && backup->stop_lsn >= target_lsn))
502504
all_wal = true;
503505

504506
startpoint = backup->stop_lsn;
@@ -570,6 +572,9 @@ validate_wal(pgBackup *backup,
570572
else if (target_time != 0)
571573
elog(ERROR, "not enough WAL records to time %s",
572574
target_timestamp);
575+
else if (XRecOffIsValid(target_lsn))
576+
elog(ERROR, "not enough WAL records to lsn %X/%X",
577+
(uint32) (target_lsn >> 32), (uint32) (target_lsn));
573578
}
574579

575580
/* clean */

src/pg_probackup.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ uint32 replica_timeout = 300; /* default is 300 seconds */
6161
/* restore options */
6262
static char *target_time;
6363
static char *target_xid;
64+
static char *target_lsn;
6465
static char *target_inclusive;
6566
static TimeLineID target_tli;
6667
static bool target_immediate;
@@ -150,6 +151,7 @@ static pgut_option options[] =
150151
{ 's', 26, "recovery-target-action", &target_action, SOURCE_CMDLINE },
151152
{ 'b', 'R', "restore-as-replica", &restore_as_replica, SOURCE_CMDLINE },
152153
{ 'b', 27, "no-validate", &restore_no_validate, SOURCE_CMDLINE },
154+
{ 's', 28, "lsn", &target_lsn, SOURCE_CMDLINE },
153155
/* delete options */
154156
{ 'b', 130, "wal", &delete_wal, SOURCE_CMDLINE },
155157
{ 'b', 131, "expired", &delete_expired, SOURCE_CMDLINE },
@@ -436,7 +438,7 @@ main(int argc, char *argv[])
436438
{
437439
/* parse all recovery target options into recovery_target_options structure */
438440
recovery_target_options = parseRecoveryTargetOptions(target_time, target_xid,
439-
target_inclusive, target_tli, target_immediate,
441+
target_inclusive, target_tli, target_lsn, target_immediate,
440442
target_name, target_action, restore_no_validate);
441443
}
442444

src/pg_probackup.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@ typedef struct pgRecoveryTarget
259259
TransactionId recovery_target_xid;
260260
/* add one more field in order to avoid deparsing recovery_target_xid back */
261261
const char *target_xid_string;
262+
bool lsn_specified;
263+
XLogRecPtr recovery_target_lsn;
264+
/* add one more field in order to avoid deparsing recovery_target_lsn back */
265+
const char *target_lsn_string;
262266
TimeLineID recovery_target_tli;
263267
bool recovery_target_inclusive;
264268
bool inclusive_specified;
@@ -400,8 +404,9 @@ extern bool satisfy_recovery_target(const pgBackup *backup,
400404
extern parray * readTimeLineHistory_probackup(TimeLineID targetTLI);
401405
extern pgRecoveryTarget *parseRecoveryTargetOptions(
402406
const char *target_time, const char *target_xid,
403-
const char *target_inclusive, TimeLineID target_tli, bool target_immediate,
404-
const char *target_name, const char *target_action, bool restore_no_validate);
407+
const char *target_inclusive, TimeLineID target_tli, const char* target_lsn,
408+
bool target_immediate, const char *target_name,
409+
const char *target_action, bool restore_no_validate);
405410

406411
extern void opt_tablespace_map(pgut_option *opt, const char *arg);
407412

@@ -514,6 +519,7 @@ extern void validate_wal(pgBackup *backup,
514519
const char *archivedir,
515520
time_t target_time,
516521
TransactionId target_xid,
522+
XLogRecPtr target_lsn,
517523
TimeLineID tli);
518524
extern bool read_recovery_info(const char *archivedir, TimeLineID tli,
519525
XLogRecPtr start_lsn, XLogRecPtr stop_lsn,
@@ -536,6 +542,7 @@ extern long unsigned int base36dec(const char *text);
536542
extern uint64 get_system_identifier(char *pgdata);
537543
extern uint64 get_remote_system_identifier(PGconn *conn);
538544
extern pg_time_t timestamptz_to_time_t(TimestampTz t);
545+
extern int parse_server_version(char *server_version_str);
539546
extern void pgBackup_init(pgBackup *backup);
540547

541548
/* in status.c */

src/restore.c

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ do_restore_or_validate(time_t target_backup_id, pgRecoveryTarget *rt,
276276
* because it's needed to form the name of xlog file.
277277
*/
278278
validate_wal(dest_backup, arclog_path, rt->recovery_target_time,
279-
rt->recovery_target_xid, base_full_backup->tli);
279+
rt->recovery_target_xid, rt->recovery_target_lsn,
280+
base_full_backup->tli);
280281

281282
/* Set every incremental backup between corrupted backup and nearest FULL backup as orphans */
282283
if (corrupted_backup)
@@ -335,6 +336,11 @@ do_restore_or_validate(time_t target_backup_id, pgRecoveryTarget *rt,
335336
for (i = base_full_backup_index; i >= dest_backup_index; i--)
336337
{
337338
pgBackup *backup = (pgBackup *) parray_get(backups, i);
339+
340+
if (rt->lsn_specified && parse_server_version(backup->server_version) < 100000)
341+
elog(ERROR, "Backup %s was created for version %s which doesn't support recovery_target_lsn",
342+
base36enc(dest_backup->start_time), dest_backup->server_version);
343+
338344
restore_backup(backup);
339345
}
340346

@@ -838,6 +844,9 @@ create_recovery_conf(time_t backup_id,
838844
if (rt->xid_specified)
839845
fprintf(fp, "recovery_target_xid = '%s'\n", rt->target_xid_string);
840846

847+
if (rt->recovery_target_lsn)
848+
fprintf(fp, "recovery_target_lsn = '%s'\n", rt->target_lsn_string);
849+
841850
if (rt->recovery_target_immediate)
842851
fprintf(fp, "recovery_target = 'immediate'\n");
843852

@@ -982,6 +991,9 @@ satisfy_recovery_target(const pgBackup *backup, const pgRecoveryTarget *rt)
982991
if (rt->time_specified)
983992
return backup->recovery_time <= rt->recovery_target_time;
984993

994+
if (rt->lsn_specified)
995+
return backup->stop_lsn <= rt->recovery_target_lsn;
996+
985997
return true;
986998
}
987999

@@ -1010,6 +1022,7 @@ parseRecoveryTargetOptions(const char *target_time,
10101022
const char *target_xid,
10111023
const char *target_inclusive,
10121024
TimeLineID target_tli,
1025+
const char *target_lsn,
10131026
bool target_immediate,
10141027
const char *target_name,
10151028
const char *target_action,
@@ -1018,6 +1031,7 @@ parseRecoveryTargetOptions(const char *target_time,
10181031
time_t dummy_time;
10191032
TransactionId dummy_xid;
10201033
bool dummy_bool;
1034+
XLogRecPtr dummy_lsn;
10211035
/*
10221036
* count the number of the mutually exclusive options which may specify
10231037
* recovery target. If final value > 1, throw an error.
@@ -1029,10 +1043,13 @@ parseRecoveryTargetOptions(const char *target_time,
10291043
rt->time_specified = false;
10301044
rt->xid_specified = false;
10311045
rt->inclusive_specified = false;
1046+
rt->lsn_specified = false;
10321047
rt->recovery_target_time = 0;
10331048
rt->recovery_target_xid = 0;
1049+
rt->recovery_target_lsn = InvalidXLogRecPtr;
10341050
rt->target_time_string = NULL;
10351051
rt->target_xid_string = NULL;
1052+
rt->target_lsn_string = NULL;
10361053
rt->recovery_target_inclusive = false;
10371054
rt->recovery_target_tli = 0;
10381055
rt->recovery_target_immediate = false;
@@ -1069,6 +1086,17 @@ parseRecoveryTargetOptions(const char *target_time,
10691086
elog(ERROR, "Invalid value of --xid option %s", target_xid);
10701087
}
10711088

1089+
if (target_lsn)
1090+
{
1091+
recovery_target_specified++;
1092+
rt->lsn_specified = true;
1093+
rt->target_lsn_string = target_lsn;
1094+
if (parse_lsn(target_lsn, &dummy_lsn))
1095+
rt->recovery_target_lsn = dummy_lsn;
1096+
else
1097+
elog(ERROR, "Invalid value of --lsn option %s", target_lsn);
1098+
}
1099+
10721100
if (target_inclusive)
10731101
{
10741102
rt->inclusive_specified = true;
@@ -1113,10 +1141,10 @@ parseRecoveryTargetOptions(const char *target_time,
11131141

11141142
/* More than one mutually exclusive option was defined. */
11151143
if (recovery_target_specified > 1)
1116-
elog(ERROR, "At most one of --immediate, --target-name, --time, or --xid can be used");
1144+
elog(ERROR, "At most one of --immediate, --target-name, --time, --xid, or --lsn can be used");
11171145

11181146
/* If none of the options is defined, '--inclusive' option is meaningless */
1119-
if (!(rt->xid_specified || rt->time_specified) && rt->recovery_target_inclusive)
1147+
if (!(rt->xid_specified || rt->time_specified || rt->lsn_specified) && rt->recovery_target_inclusive)
11201148
elog(ERROR, "--inclusive option applies when either --time or --xid is specified");
11211149

11221150
return rt;

src/util.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,35 @@ timestamptz_to_time_t(TimestampTz t)
235235
return result;
236236
}
237237

238+
/* Parse string representation of the server version */
239+
int
240+
parse_server_version(char *server_version_str)
241+
{
242+
int nfields;
243+
int result = 0;
244+
int major_version = 0;
245+
int minor_version = 0;
246+
247+
nfields = sscanf(server_version_str, "%d.%d", &major_version, &minor_version);
248+
if (nfields == 2)
249+
{
250+
/* Server version lower than 10 */
251+
if (major_version > 10)
252+
elog(ERROR, "Server version format doesn't match major version %d", major_version);
253+
result = major_version * 10000 + minor_version * 100;
254+
}
255+
else if (nfields == 1)
256+
{
257+
if (major_version < 10)
258+
elog(ERROR, "Server version format doesn't match major version %d", major_version);
259+
result = major_version * 10000;
260+
}
261+
else
262+
elog(ERROR, "Unknown server version format");
263+
264+
return result;
265+
}
266+
238267
const char *
239268
status2str(BackupStatus status)
240269
{

src/utils/pgut.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#define MAX_TZDISP_HOUR 15 /* maximum allowed hour part */
3232
#define SECS_PER_MINUTE 60
3333
#define MINS_PER_HOUR 60
34+
#define MAXPG_LSNCOMPONENT 8
3435

3536
const char *PROGRAM_NAME = NULL;
3637

@@ -983,6 +984,32 @@ parse_int(const char *value, int *result, int flags, const char **hintmsg)
983984
return true;
984985
}
985986

987+
bool
988+
parse_lsn(const char *value, XLogRecPtr *result)
989+
{
990+
uint32 xlogid;
991+
uint32 xrecoff;
992+
int len1;
993+
int len2;
994+
995+
len1 = strspn(value, "0123456789abcdefABCDEF");
996+
if (len1 < 1 || len1 > MAXPG_LSNCOMPONENT || value[len1] != '/')
997+
elog(ERROR, "invalid LSN \"%s\"", value);
998+
len2 = strspn(value + len1 + 1, "0123456789abcdefABCDEF");
999+
if (len2 < 1 || len2 > MAXPG_LSNCOMPONENT || value[len1 + 1 + len2] != '\0')
1000+
elog(ERROR, "invalid LSN \"%s\"", value);
1001+
1002+
if (sscanf(value, "%X/%X", &xlogid, &xrecoff) == 2)
1003+
*result = (XLogRecPtr) ((uint64) xlogid << 32) | xrecoff;
1004+
else
1005+
{
1006+
elog(ERROR, "invalid LSN \"%s\"", value);
1007+
return false;
1008+
}
1009+
1010+
return true;
1011+
}
1012+
9861013
static char *
9871014
longopts_to_optstring(const struct option opts[], const size_t len)
9881015
{

src/utils/pgut.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <assert.h>
1818
#include <sys/time.h>
1919

20+
#include "access/xlogdefs.h"
2021
#include "logger.h"
2122

2223
#if !defined(C_H) && !defined(__cplusplus)
@@ -207,6 +208,7 @@ extern bool parse_uint64(const char *value, uint64 *result, int flags);
207208
extern bool parse_time(const char *value, time_t *result, bool utc_default);
208209
extern bool parse_int(const char *value, int *result, int flags,
209210
const char **hintmsg);
211+
extern bool parse_lsn(const char *value, XLogRecPtr *result);
210212

211213
extern void convert_from_base_unit(int64 base_value, int base_unit,
212214
int64 *value, const char **unit);

src/validate.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ do_validate_instance(void)
328328
base36enc(current_backup->start_time));
329329
/* Validate corresponding WAL files */
330330
validate_wal(current_backup, arclog_path, 0,
331-
0, base_full_backup->tli);
331+
0, 0, base_full_backup->tli);
332332
}
333333
/* Mark every incremental backup between corrupted backup and nearest FULL backup as orphans */
334334
if (current_backup->status == BACKUP_STATUS_CORRUPT)

tests/cfs_restore.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
pg_probackup restore -B backupdir --instance instance_name
66
[-D datadir]
7-
[ -i backup_id | [{--time=time | --xid=xid } [--inclusive=boolean]]][--timeline=timeline] [-T OLDDIR=NEWDIR]
7+
[ -i backup_id | [{--time=time | --xid=xid | --lsn=lsn } [--inclusive=boolean]]][--timeline=timeline] [-T OLDDIR=NEWDIR]
88
[-j num_threads] [--progress] [-q] [-v]
99
1010
"""

0 commit comments

Comments
 (0)