1
0
mirror of https://github.com/postgrespro/pg_probackup.git synced 2025-01-05 13:20:31 +02:00

revised for pg_rman1.2.2

git-svn-id: http://pg-rman.googlecode.com/svn/trunk@48 182aca00-e38e-11de-a668-6fd11605f5ce
This commit is contained in:
t.katsumata1122 2011-11-28 04:22:05 +00:00
parent ae6c0e0c55
commit 78eed96f63
42 changed files with 1940 additions and 1615 deletions

View File

@ -1,4 +1,4 @@
Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
Portions Copyright (c) 1994, The Regents of the University of California

View File

@ -24,7 +24,7 @@ OBJS = $(SRCS:.c=.o)
PG_CPPFLAGS = -I$(libpq_srcdir)
PG_LIBS = $(libpq_pgport)
REGRESS = option init show_validate backup_restore snapshot
REGRESS = option init show_validate backup_restore
ifdef USE_PGXS
PG_CONFIG = pg_config

View File

@ -9,7 +9,7 @@
## Set general information for pg_rman.
Summary: Backup and Recovery Tool for PostgreSQL
Name: pg_rman
Version: 1.2.1
Version: 1.2.0
Release: 1%{?dist}
License: BSD
Group: Applications/Databases
@ -58,7 +58,6 @@ rm -rf %{buildroot}
# History of pg_rman.
%changelog
* Mon Jun 20 2011 - Tomonari Katsumata <t.katsumata1122@gmail.com> 1.2.1-1
* Wed Nov 10 2010 - NTT OSS Center <tomonari.katsumata@oss.ntt.co.jp> 1.2.0-1
* Wed Dec 9 2009 - NTT OSS Center <itagaki.takahiro@oss.ntt.co.jp> 1.1.1-1
- Initial cut for 1.1.1

View File

@ -2,7 +2,7 @@
*
* backup.c: backup DB cluster, archived WAL, serverlog.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
@ -41,6 +41,7 @@ static void pg_start_backup(const char *label, bool smooth, pgBackup *backup);
static void pg_stop_backup(pgBackup *backup);
static void pg_switch_xlog(pgBackup *backup);
static void get_lsn(PGresult *res, TimeLineID *timeline, XLogRecPtr *lsn);
static void get_xid(PGresult *res, uint32 *xid);
static void delete_arclog_link(void);
static void delete_online_wal_backup(void);
@ -75,8 +76,26 @@ do_backup_database(parray *backup_list, bool smooth_checkpoint)
XLogRecPtr *lsn = NULL;
char prev_file_txt[MAXPGPATH]; /* path of the previous backup list file */
if (!HAVE_DATABASE(&current))
return NULL;
if (!HAVE_DATABASE(&current)) {
/* check if arclog backup. if arclog backup and no suitable full backup, */
/* take full backup instead. */
if (HAVE_ARCLOG(&current)) {
pgBackup *prev_backup;
/* find last completed database backup */
prev_backup = catalog_get_last_data_backup(backup_list);
if (prev_backup == NULL)
{
elog(ERROR_SYSTEM, _("There is indeed a full backup but it is not validated."
"So I can't take any arclog backup."
"Please validate it and retry."));
/// elog(INFO, _("no previous full backup, performing a full backup instead"));
/// current.backup_mode = BACKUP_MODE_FULL;
}
}
else
return NULL;
}
elog(INFO, _("database backup start"));
@ -89,6 +108,16 @@ do_backup_database(parray *backup_list, bool smooth_checkpoint)
strncat(label, " with pg_rman", lengthof(label));
pg_start_backup(label, smooth_checkpoint, &current);
/* If backup_label does not exist in $PGDATA, stop taking backup */
snprintf(path, lengthof(path), "%s/backup_label", pgdata);
make_native_path(path);
if (!fileExists(path)) {
if (verbose)
printf(_("backup_label does not exist, stop backup\n"));
pg_stop_backup(NULL);
elog(ERROR_SYSTEM, _("backup_label does not exist in PGDATA."));
}
/*
* list directories and symbolic links with the physical path to make
* mkdirs.sh
@ -127,10 +156,13 @@ do_backup_database(parray *backup_list, bool smooth_checkpoint)
/* find last completed database backup */
prev_backup = catalog_get_last_data_backup(backup_list);
if (prev_backup == NULL)
if (prev_backup == NULL || prev_backup->tli != current.tli)
{
elog(INFO, _("no previous full backup, do a full backup instead"));
current.backup_mode = BACKUP_MODE_FULL;
elog(ERROR_SYSTEM, _("There is indeed a full backup but it is not validated."
"So I can't take any incremental backup."
"Please validate it and retry."));
/// elog(INFO, _("no previous full backup, performing a full backup instead"));
/// current.backup_mode = BACKUP_MODE_FULL;
}
else
{
@ -440,7 +472,7 @@ do_backup_arclog(parray *backup_list)
*/
prev_backup = catalog_get_last_arclog_backup(backup_list);
if (verbose && prev_backup == NULL)
printf(_("no previous full backup, do a full backup instead\n"));
printf(_("no previous full backup, performing a full backup instead\n"));
if (prev_backup)
{
@ -564,7 +596,7 @@ do_backup_srvlog(parray *backup_list)
*/
prev_backup = catalog_get_last_srvlog_backup(backup_list);
if (verbose && prev_backup == NULL)
printf(_("no previous full backup, do a full backup instead\n"));
printf(_("no previous full backup, performing a full backup instead\n"));
if (prev_backup)
{
@ -695,6 +727,8 @@ do_backup(bool smooth_checkpoint,
current.write_bytes = 0; /* write_bytes is valid always */
current.block_size = BLCKSZ;
current.wal_block_size = XLOG_BLCKSZ;
current.recovery_xid = 0;
current.recovery_time = (time_t) 0;
/* create backup directory and backup.ini */
if (!check)
@ -708,6 +742,9 @@ do_backup(bool smooth_checkpoint,
/* get list of backups already taken */
backup_list = catalog_get_backup_list(NULL);
if(!backup_list){
elog(ERROR_SYSTEM, _("can't process any more."));
}
/* set the error processing function for the backup process */
pgut_atexit_push(backup_cleanup, NULL);
@ -893,6 +930,12 @@ wait_for_archive(pgBackup *backup, const char *sql)
elog(LOG, "%s() wait for %s", __FUNCTION__, ready_path);
PQclear(res);
res = execute(TXID_CURRENT_SQL, 0, NULL);
if(backup != NULL){
get_xid(res, &backup->recovery_xid);
backup->recovery_time = time(NULL);
}
disconnect();
/* wait until switched WAL is archived */
@ -960,6 +1003,26 @@ get_lsn(PGresult *res, TimeLineID *timeline, XLogRecPtr *lsn)
lsn->xrecoff += off_upper << 24;
}
/*
* Get XID from result of txid_current() after pg_stop_backup().
*/
static void
get_xid(PGresult *res, uint32 *xid)
{
if(res == NULL || PQntuples(res) != 1 || PQnfields(res) != 1)
elog(ERROR_PG_COMMAND,
_("result of txid_current() is invalid: %s"),
PQerrorMessage(connection));
if(sscanf(PQgetvalue(res, 0, 0), "%u", xid) != 1)
{
elog(ERROR_PG_COMMAND,
_("result of txid_current() is invalid: %s"),
PQerrorMessage(connection));
}
elog(LOG, "%s():%s", __FUNCTION__, PQgetvalue(res, 0, 0));
}
/*
* Return true if the path is a existing regular file.
*/
@ -1055,6 +1118,11 @@ backup_files(const char *from_root,
pgFile *file = (pgFile *) parray_get(files, i);
/* If current time is rewinded, abort this backup. */
if(tv.tv_sec < file->mtime){
elog(ERROR_SYSTEM, _("current time may be rewound. Please retry with full backup mode."));
}
/* check for interrupt */
if (interrupted)
elog(ERROR_INTERRUPTED, _("interrupted during backup"));
@ -1101,8 +1169,9 @@ backup_files(const char *from_root,
char dirpath[MAXPGPATH];
join_path_components(dirpath, to_root, JoinPathEnd(file->path, from_root));
if (!check)
if (!check){
dir_create_dir(dirpath, DIR_PERMISSION);
}
if (verbose)
printf(_("directory\n"));
}
@ -1143,7 +1212,7 @@ backup_files(const char *from_root,
prev_file = *p;
}
if (prev_file && prev_file->mtime >= file->mtime)
if (prev_file && prev_file->mtime == file->mtime)
{
/* record as skipped file in file_xxx.txt */
file->write_size = BYTES_INVALID;
@ -1158,7 +1227,8 @@ backup_files(const char *from_root,
* file should contain all modifications at the clock of mtime.
* timer resolution of ext3 file system is one second.
*/
if (tv.tv_sec <= file->mtime)
if (tv.tv_sec == file->mtime)
{
/* update time and recheck */
gettimeofday(&tv, NULL);

View File

@ -2,7 +2,7 @@
*
* catalog.c: backup catalog opration
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
@ -364,6 +364,12 @@ pgBackupWriteResultSection(FILE *out, pgBackup *backup)
time2iso(timestamp, lengthof(timestamp), backup->end_time);
fprintf(out, "END_TIME='%s'\n", timestamp);
}
fprintf(out, "RECOVERY_XID=%u\n", backup->recovery_xid);
if (backup->recovery_time > 0)
{
time2iso(timestamp, lengthof(timestamp), backup->recovery_time);
fprintf(out, "RECOVERY_TIME='%s'\n", timestamp);
}
if (backup->total_data_bytes != BYTES_INVALID)
fprintf(out, "TOTAL_DATA_BYTES=" INT64_FORMAT "\n",
@ -434,6 +440,8 @@ catalog_read_ini(const char *path)
{ 's', 0, "stop-lsn" , NULL, SOURCE_ENV },
{ 't', 0, "start-time" , NULL, SOURCE_ENV },
{ 't', 0, "end-time" , NULL, SOURCE_ENV },
{ 'u', 0, "recovery-xid" , NULL, SOURCE_ENV },
{ 't', 0, "recovery-time" , NULL, SOURCE_ENV },
{ 'I', 0, "total-data-bytes" , NULL, SOURCE_ENV },
{ 'I', 0, "read-data-bytes" , NULL, SOURCE_ENV },
{ 'I', 0, "read-arclog-bytes" , NULL, SOURCE_ENV },
@ -445,6 +453,10 @@ catalog_read_ini(const char *path)
{ 0 }
};
if (access(path, F_OK) != 0){
return NULL;
}
backup = pgut_new(pgBackup);
catalog_init_config(backup);
@ -457,6 +469,8 @@ catalog_read_ini(const char *path)
options[i++].var = &stop_lsn;
options[i++].var = &backup->start_time;
options[i++].var = &backup->end_time;
options[i++].var = &backup->recovery_xid;
options[i++].var = &backup->recovery_time;
options[i++].var = &backup->total_data_bytes;
options[i++].var = &backup->read_data_bytes;
options[i++].var = &backup->read_arclog_bytes;
@ -601,6 +615,8 @@ catalog_init_config(pgBackup *backup)
backup->stop_lsn.xrecoff = 0;
backup->start_time = (time_t) 0;
backup->end_time = (time_t) 0;
backup->recovery_xid = 0;
backup->recovery_time = (time_t) 0;
backup->total_data_bytes = BYTES_INVALID;
backup->read_data_bytes = BYTES_INVALID;
backup->read_arclog_bytes = BYTES_INVALID;

View File

@ -2,7 +2,7 @@
*
* clean.c: cleanup backup files.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/

18
data.c
View File

@ -2,7 +2,7 @@
*
* data.c: compress / uncompress data pages
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
@ -756,6 +756,14 @@ restore_data_file(const char *from_root,
elog(ERROR_SYSTEM, _("can't change mode of \"%s\": %s"), to_path,
strerror(errno_tmp));
}
//aaa if (chown(to_path, file->uid, file->gid) == -1)
//aaa {
//aaa int errno_tmp = errno;
//aaa fclose(in);
//aaa fclose(out);
//aaa elog(ERROR_SYSTEM, _("can't change owner of \"%s\": %s"), to_path,
//aaa strerror(errno_tmp));
//aaa }
fclose(in);
fclose(out);
@ -992,6 +1000,14 @@ copy_file(const char *from_root, const char *to_root, pgFile *file,
elog(ERROR_SYSTEM, _("can't change mode of \"%s\": %s"), to_path,
strerror(errno_tmp));
}
//aaa if (chown(to_path, file->uid, file->gid) == -1)
//aaa {
//aaa errno_tmp = errno;
//aaa fclose(in);
//aaa fclose(out);
//aaa elog(ERROR_SYSTEM, _("can't change owner of \"%s\": %s"), to_path,
//aaa strerror(errno_tmp));
//aaa }
fclose(in);
fclose(out);

View File

@ -1,18 +1,18 @@
# configuration
BACKUP_MODE=FULL
WITH_SERVERLOG=NO
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-05-31 17:05:53'
END_TIME='2009-05-31 17:09:13'
TOTAL_DATA_BYTES=1242102558
READ_DATA_BYTES=1024
READ_ARCLOG_BYTES=9223372036854775807
READ_SRVLOG_BYTES=-1
WRITE_BYTES=242102558
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=DONE
# configuration
BACKUP_MODE=FULL
WITH_SERVERLOG=NO
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-05-31 17:05:53'
END_TIME='2009-05-31 17:09:13'
TOTAL_DATA_BYTES=1242102558
READ_DATA_BYTES=1024
READ_ARCLOG_BYTES=9223372036854775807
READ_SRVLOG_BYTES=-1
WRITE_BYTES=242102558
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=DONE

View File

@ -1 +1 @@
PG_VERSION f 4 4277607361 0600 2009-08-06 18:40:18
PG_VERSION f 4 4277607361 0600 2009-08-06 18:40:18

View File

@ -1,18 +1,18 @@
# configuration
BACKUP_MODE=INCREMENTAL
WITH_SERVERLOG=NO
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-01 17:05:53'
END_TIME='2009-06-01 17:09:13'
TOTAL_DATA_BYTES=1242102558
READ_DATA_BYTES=9223372036854775807
READ_ARCLOG_BYTES=16777216
READ_SRVLOG_BYTES=-1
WRITE_BYTES=162372983
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=DONE
# configuration
BACKUP_MODE=INCREMENTAL
WITH_SERVERLOG=NO
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-01 17:05:53'
END_TIME='2009-06-01 17:09:13'
TOTAL_DATA_BYTES=1242102558
READ_DATA_BYTES=9223372036854775807
READ_ARCLOG_BYTES=16777216
READ_SRVLOG_BYTES=-1
WRITE_BYTES=162372983
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=DONE

View File

@ -1 +1 @@
PG_VERSION f 4 0 0600 2009-08-06 18:40:18
PG_VERSION f 4 0 0600 2009-08-06 18:40:18

View File

@ -1,18 +1,18 @@
# configuration
BACKUP_MODE=ARCHIVE
WITH_SERVERLOG=YES
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-02 17:05:03'
END_TIME='2009-06-02 17:05:03'
TOTAL_DATA_BYTES=-1
READ_DATA_BYTES=-1
READ_ARCLOG_BYTES=-1
READ_SRVLOG_BYTES=4335423
WRITE_BYTES=162372983
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=DELETED
# configuration
BACKUP_MODE=ARCHIVE
WITH_SERVERLOG=YES
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-02 17:05:03'
END_TIME='2009-06-02 17:05:03'
TOTAL_DATA_BYTES=-1
READ_DATA_BYTES=-1
READ_ARCLOG_BYTES=-1
READ_SRVLOG_BYTES=4335423
WRITE_BYTES=162372983
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=DELETED

View File

@ -1,18 +1,18 @@
# configuration
BACKUP_MODE=FULL
WITH_SERVERLOG=YES
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-03 17:05:53'
END_TIME='****-**-** **:**:**'
TOTAL_DATA_BYTES=-1
READ_DATA_BYTES=-1
READ_ARCLOG_BYTES=-1
READ_SRVLOG_BYTES=-1
WRITE_BYTES=-1
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=RUNNING
# configuration
BACKUP_MODE=FULL
WITH_SERVERLOG=YES
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-03 17:05:53'
END_TIME='2009-06-03 17:05:53'
TOTAL_DATA_BYTES=-1
READ_DATA_BYTES=-1
READ_ARCLOG_BYTES=-1
READ_SRVLOG_BYTES=-1
WRITE_BYTES=-1
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=RUNNING

2
db.c
View File

@ -2,7 +2,7 @@
*
* db.c: SQLite3 access module
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/

View File

@ -2,7 +2,7 @@
*
* delete.c: delete backup files.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
@ -10,14 +10,17 @@
#include "pg_rman.h"
static int pgBackupDeleteFiles(pgBackup *backup);
static bool checkIfDeletable(pgBackup *backup);
int
do_delete(pgBackupRange *range)
//do_delete(pgBackupRange *range)
do_delete(pgBackupRange *range, bool force)
{
int i;
int ret;
parray *backup_list;
bool do_delete;
bool force_delete;
/* DATE are always required */
if (!pgBackupRangeIsValid(range))
@ -29,20 +32,31 @@ do_delete(pgBackupRange *range)
elog(ERROR_SYSTEM, _("can't lock backup catalog."));
else if (ret == 1)
elog(ERROR_ALREADY_RUNNING,
_("another pg_rman is running, stop restore."));
_("another pg_rman is running, stop delete."));
/* get list of backups. */
backup_list = catalog_get_backup_list(NULL);
if(!backup_list){
elog(ERROR_SYSTEM, _("can't process any more."));
}
do_delete = false;
force_delete = false;
/* find delete target backup. */
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup = (pgBackup *)parray_get(backup_list, i);
if(force)
force_delete = checkIfDeletable(backup);
/* delete backup and update status to DELETED */
if (do_delete)
if (do_delete || force_delete)
{
/* check for interrupt */
if (interrupted)
elog(ERROR_INTERRUPTED, _("interrupted during delete backup"));
pgBackupDeleteFiles(backup);
continue;
}
@ -76,6 +90,7 @@ pgBackupDelete(int keep_generations, int keep_days)
int backup_num;
time_t days_threshold = current.start_time - (keep_days * 60 * 60 * 24);
if (verbose)
{
char generations_str[100];
@ -123,7 +138,8 @@ pgBackupDelete(int keep_generations, int keep_days)
backup_num++;
/* do not include the latest full backup in a count. */
if (backup_num - 1 <= keep_generations)
// if (backup_num - 1 <= keep_generations)
if (backup_num <= keep_generations)
{
elog(LOG, "%s() backup are only %d", __FUNCTION__, backup_num);
continue;
@ -233,3 +249,15 @@ pgBackupDeleteFiles(pgBackup *backup)
return 0;
}
bool
checkIfDeletable(pgBackup *backup)
{
/* find latest full backup. */
if (backup->status != BACKUP_STATUS_OK &&
backup->status != BACKUP_STATUS_DELETED &&
backup->status != BACKUP_STATUS_DONE)
return true;
return false;
}

11
dir.c
View File

@ -2,7 +2,7 @@
*
* dir.c: directory operation utility.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
@ -363,11 +363,13 @@ dir_print_mkdirs_sh(FILE *out, const parray *files, const char *root)
pgFile *file = (pgFile *) parray_get(files, i);
if (S_ISDIR(file->mode))
{
if (strstr(file->path, root) == file->path)
if (strstr(file->path, root) == file->path) {
fprintf(out, "mkdir -m 700 -p %s\n", file->path + strlen(root)
+ 1);
else
}
else {
fprintf(out, "mkdir -m 700 -p %s\n", file->path);
}
}
}
@ -539,8 +541,9 @@ dir_copy_files(const char *from_root, const char *to_root)
if (verbose && !check)
printf(_("create directory \"%s\"\n"),
file->path + strlen(from_root) + 1);
if (!check)
if (!check) {
dir_create_dir(to_path, DIR_PERMISSION);
}
continue;
}
else if(S_ISREG(file->mode))

View File

@ -15,13 +15,13 @@ diff files in BACKUP_PATH/backup/pg_xlog
1
diff files in BACKUP_PATH/backup/pg_xlog
# of files in BACKUP_PATH/backup/srvlog
2
1
full database backup after recovery
CHECKPOINT
# of files in BACKUP_PATH/backup/pg_xlog
0
# of files in BACKUP_PATH/backup/srvlog
2
1
# of symbolic links in ARCLOG_PATH
0
# of files in BACKUP_PATH/timeline_history

View File

@ -1,6 +1,5 @@
\! rm -rf results/init_test
\! pg_rman init -B results/init_test --quiet;echo $?
WARNING: ARCLOG_PATH is not set because archive_command is empty
\! pg_rman init -B ${PWD}/results/init_test --quiet;echo $?
0
\! find results/init_test | xargs ls -Fd | sort
results/init_test/
@ -9,6 +8,6 @@ results/init_test/backup/pg_xlog/
results/init_test/backup/srvlog/
results/init_test/pg_rman.ini
results/init_test/timeline_history/
\! pg_rman init -B results/init_test --quiet;echo $?
ERROR: backup catalog already exist.
\! pg_rman init -B ${PWD}/results/init_test --quiet;echo $?
ERROR: backup catalog already exist. and it's not empty.
2

View File

@ -54,7 +54,7 @@ Generic options:
Read the website for details. <http://code.google.com/p/pg-rman/>
Report bugs to <http://code.google.com/p/pg-rman/issues/list>.
pg_rman 1.1.2
pg_rman 1.2.2
ERROR: required parameter not specified: BACKUP_PATH (-B, --backup-path)
ERROR: required parameter not specified: BACKUP_MODE (-b, --backup-mode)
ERROR: required parameter not specified: ARCLOG_PATH (-A, --arclog-path)
@ -62,11 +62,11 @@ ERROR: required parameter not specified: ARCLOG_PATH (-A, --arclog-path)
ERROR: required parameter not specified: ARCLOG_PATH (-A, --arclog-path)
ERROR: invalid backup-mode "bad"
ERROR: required delete range option not specified: delete DATE
INFO: validate: 2009-05-31 17:05:53
INFO: validate: 2009-06-01 17:05:53
INFO: validate: 2009-05-31 17:05:53 backup and archive log files by CRC
INFO: validate: 2009-06-01 17:05:53 backup and archive log files by CRC
WARNING: CRC of backup file "PG_VERSION" must be 0 but FEF71BC1
WARNING: backup 2009-06-01 17:05:53 is corrupted
WARNING: syntax error in " = INFINITE".
WARNING: syntax error in " = INFINITE"
ERROR: required parameter not specified: BACKUP_MODE (-b, --backup-mode)
ERROR: invalid backup-mode ""
ERROR: required parameter not specified: ARCLOG_PATH (-A, --arclog-path)

View File

@ -1,35 +1,35 @@
-- test show command
\! rm -rf results/sample_backup
\! cp -rp data/sample_backup results/sample_backup
\! pg_rman show -B results/sample_backup
\! rm -rf ${PWD}/results/sample_backup
\! cp -rp data/sample_backup ${PWD}/results/sample_backup
\! pg_rman show -B ${PWD}/results/sample_backup
============================================================================
Start Time Total Data WAL Log Backup Status
============================================================================
2009-06-03 17:05:53 ---- ---- ---- ---- ---- ---- RUNNING
2009-06-03 17:05:53 0m ---- ---- ---- ---- ---- RUNNING
2009-06-01 17:05:53 3m ---- 9223PB 16MB ---- 162MB DONE
2009-05-31 17:05:53 3m 1242MB ---- 9223PB ---- 242MB DONE
\! pg_rman validate -B results/sample_backup 2009-05-31 17:05:53 --debug
INFO: validate: 2009-05-31 17:05:53
\! pg_rman validate -B ${PWD}/results/sample_backup 2009-05-31 17:05:53 --debug
INFO: validate: 2009-05-31 17:05:53 backup and archive log files by CRC
LOG: database files...
LOG: (1/1) PG_VERSION
LOG: archive WAL files...
LOG: backup 2009-05-31 17:05:53 is valid
\! pg_rman validate -B results/sample_backup 2009-06-01 17:05:53 --debug
INFO: validate: 2009-06-01 17:05:53
\! pg_rman validate -B ${PWD}/results/sample_backup 2009-06-01 17:05:53 --debug
INFO: validate: 2009-06-01 17:05:53 backup and archive log files by CRC
LOG: database files...
LOG: (1/1) PG_VERSION
WARNING: CRC of backup file "PG_VERSION" must be 0 but FEF71BC1
LOG: archive WAL files...
WARNING: backup 2009-06-01 17:05:53 is corrupted
\! pg_rman show -a -B results/sample_backup
\! pg_rman show -a -B ${PWD}/results/sample_backup
============================================================================
Start Time Total Data WAL Log Backup Status
============================================================================
2009-06-03 17:05:53 ---- ---- ---- ---- ---- ---- RUNNING
2009-06-03 17:05:53 0m ---- ---- ---- ---- ---- RUNNING
2009-06-02 17:05:03 0m ---- ---- ---- 4335kB 162MB DELETED
2009-06-01 17:05:53 3m ---- 9223PB 16MB ---- 162MB CORRUPT
2009-05-31 17:05:53 3m 1242MB ---- 9223PB ---- 242MB OK
\! pg_rman show 2009-06-01 17:05:53 -B results/sample_backup
\! pg_rman show 2009-06-01 17:05:53 -B ${PWD}/results/sample_backup
# configuration
BACKUP_MODE=INCREMENTAL
WITH_SERVERLOG=false
@ -40,10 +40,10 @@ START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-01 17:05:53'
END_TIME='2009-06-01 17:09:13'
RECOVERY_XID=0
TOTAL_DATA_BYTES=1242102558
READ_DATA_BYTES=9223372036854775807
READ_ARCLOG_BYTES=16777216
READ_SRVLOG_BYTES=-1
WRITE_BYTES=162372983
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192

2
file.c
View File

@ -2,7 +2,7 @@
*
* file.c:
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/

27
init.c
View File

@ -2,7 +2,7 @@
*
* init.c: manage backup catalog.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
@ -10,10 +10,19 @@
#include "pg_rman.h"
#include <unistd.h>
#include <dirent.h>
static void parse_postgresql_conf(const char *path, char **log_directory,
char **archive_command);
/*
* selects function for scandir.
*/
static int selects(const struct dirent *dir)
{
return dir->d_name[0] != '.';
}
/*
* Initialize backup catalog.
*/
@ -25,8 +34,14 @@ do_init(void)
char *archive_command = NULL;
FILE *fp;
if (access(backup_path, F_OK) == 0)
elog(ERROR, _("backup catalog already exist."));
struct dirent **dp;
int results;
if (access(backup_path, F_OK) == 0){
results = scandir(backup_path, &dp, selects, NULL);
if(results != 0){
elog(ERROR, _("backup catalog already exist. and it's not empty."));
}
}
/* create backup catalog root directory */
dir_create_dir(backup_path, DIR_PERMISSION);
@ -98,9 +113,11 @@ do_init(void)
elog(INFO, "ARCLOG_PATH is set to '%s'", arclog_path);
}
else if (archive_command && archive_command[0])
elog(WARNING, "ARCLOG_PATH is not set because failed to parse archive_command '%s'", archive_command);
elog(WARNING, "ARCLOG_PATH is not set because failed to parse archive_command '%s'."
"Please set ARCLOG_PATH in pg_rman.ini or environmental variable", archive_command);
else
elog(WARNING, "ARCLOG_PATH is not set because archive_command is empty");
elog(WARNING, "ARCLOG_PATH is not set because archive_command is empty."
"Please set ARCLOG_PATH in pg_rman.ini or environmental variable");
/* set SRVLOG_PATH refered with log_directory */
if (srvlog_path == NULL)

View File

@ -2,7 +2,7 @@
*
* parray.c: pointer array collection.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/

View File

@ -2,7 +2,7 @@
*
* parray.h: pointer array collection.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/

View File

@ -2,7 +2,7 @@
*
* pg_ctl.c: operations for control file
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/

630
pg_rman.c
View File

@ -1,303 +1,327 @@
/*-------------------------------------------------------------------------
*
* pg_rman.c: Backup/Recovery manager for PostgreSQL.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
const char *PROGRAM_VERSION = "1.2.1";
const char *PROGRAM_URL = "http://code.google.com/p/pg-rman/";
const char *PROGRAM_EMAIL = "http://code.google.com/p/pg-rman/issues/list";
/* path configuration */
char *backup_path;
char *pgdata;
char *arclog_path;
char *srvlog_path;
/* common configuration */
bool verbose = false;
bool check = false;
/* directory configuration */
pgBackup current;
/* backup configuration */
static bool smooth_checkpoint;
static int keep_arclog_files = KEEP_INFINITE;
static int keep_arclog_days = KEEP_INFINITE;
static int keep_srvlog_files = KEEP_INFINITE;
static int keep_srvlog_days = KEEP_INFINITE;
static int keep_data_generations = KEEP_INFINITE;
static int keep_data_days = KEEP_INFINITE;
/* restore configuration */
static char *target_time;
static char *target_xid;
static char *target_inclusive;
static TimeLineID target_tli;
/* show configuration */
static bool show_all = false;
static void opt_backup_mode(pgut_option *opt, const char *arg);
static void parse_range(pgBackupRange *range, const char *arg1, const char *arg2);
static pgut_option options[] =
{
/* directory options */
{ 's', 'D', "pgdata" , &pgdata , SOURCE_ENV },
{ 's', 'A', "arclog-path" , &arclog_path , SOURCE_ENV },
{ 's', 'B', "backup-path" , &backup_path , SOURCE_ENV },
{ 's', 'S', "srvlog-path" , &srvlog_path , SOURCE_ENV },
/* common options */
{ 'b', 'v', "verbose" , &verbose },
{ 'b', 'c', "check" , &check },
/* backup options */
{ 'f', 'b', "backup-mode" , opt_backup_mode , SOURCE_ENV },
{ 'b', 's', "with-serverlog" , &current.with_serverlog , SOURCE_ENV },
{ 'b', 'Z', "compress-data" , &current.compress_data , SOURCE_ENV },
{ 'b', 'C', "smooth-checkpoint" , &smooth_checkpoint , SOURCE_ENV },
/* options with only long name (keep-xxx) */
{ 'i', 1, "keep-data-generations" , &keep_data_generations, SOURCE_ENV },
{ 'i', 2, "keep-data-days" , &keep_data_days , SOURCE_ENV },
{ 'i', 3, "keep-arclog-files" , &keep_arclog_files , SOURCE_ENV },
{ 'i', 4, "keep-arclog-days" , &keep_arclog_days , SOURCE_ENV },
{ 'i', 5, "keep-srvlog-files" , &keep_srvlog_files , SOURCE_ENV },
{ 'i', 6, "keep-srvlog-days" , &keep_srvlog_days , SOURCE_ENV },
/* restore options */
{ 's', 7, "recovery-target-time" , &target_time , SOURCE_ENV },
{ 's', 8, "recovery-target-xid" , &target_xid , SOURCE_ENV },
{ 's', 9, "recovery-target-inclusive" , &target_inclusive , SOURCE_ENV },
{ 'u', 10, "recovery-target-timeline" , &target_tli , SOURCE_ENV },
/* catalog options */
{ 'b', 'a', "show-all" , &show_all },
{ 0 }
};
/*
* Entry point of pg_rman command.
*/
int
main(int argc, char *argv[])
{
const char *cmd = NULL;
const char *range1 = NULL;
const char *range2 = NULL;
bool show_timeline = false;
pgBackupRange range;
int i;
/* do not buffer progress messages */
setvbuf(stdout, 0, _IONBF, 0); /* TODO: remove this */
/* initialize configuration */
catalog_init_config(&current);
/* overwrite configuration with command line arguments */
i = pgut_getopt(argc, argv, options);
for (; i < argc; i++)
{
if (cmd == NULL)
cmd = argv[i];
else if (pg_strcasecmp(argv[i], "timeline") == 0 &&
pg_strcasecmp(cmd, "show") == 0)
show_timeline = true;
else if (range1 == NULL)
range1 = argv[i];
else if (range2 == NULL)
range2 = argv[i];
else
elog(ERROR_ARGS, "too many arguments");
}
/* command argument (backup/restore/show/...) is required. */
if (cmd == NULL)
{
help(false);
return HELP;
}
/* get object range argument if any */
if (range1 && range2)
parse_range(&range, range1, range2);
else if (range1)
parse_range(&range, range1, "");
else
range.begin = range.end = 0;
/* Read default configuration from file. */
if (backup_path)
{
char path[MAXPGPATH];
join_path_components(path, backup_path, PG_RMAN_INI_FILE);
pgut_readopt(path, options, ERROR_ARGS);
}
/* BACKUP_PATH is always required */
if (backup_path == NULL)
elog(ERROR_ARGS, "required parameter not specified: BACKUP_PATH (-B, --backup-path)");
/* path must be absolute */
if (pgdata != NULL && !is_absolute_path(pgdata))
elog(ERROR_ARGS, "-D, --pgdata must be an absolute path");
if (arclog_path != NULL && !is_absolute_path(arclog_path))
elog(ERROR_ARGS, "-A, --arclog-path must be an absolute path");
if (srvlog_path != NULL && !is_absolute_path(srvlog_path))
elog(ERROR_ARGS, "-S, --srvlog-path must be an absolute path");
/* setup exclusion list for file search */
for (i = 0; pgdata_exclude[i]; i++) /* find first empty slot */
;
if (arclog_path)
pgdata_exclude[i++] = arclog_path;
if (srvlog_path)
pgdata_exclude[i++] = srvlog_path;
/* do actual operation */
if (pg_strcasecmp(cmd, "init") == 0)
return do_init();
else if (pg_strcasecmp(cmd, "backup") == 0)
return do_backup(smooth_checkpoint,
keep_arclog_files, keep_arclog_days,
keep_srvlog_files, keep_srvlog_days,
keep_data_generations, keep_data_days);
else if (pg_strcasecmp(cmd, "restore") == 0)
return do_restore(target_time, target_xid, target_inclusive, target_tli);
else if (pg_strcasecmp(cmd, "show") == 0)
return do_show(&range, show_timeline, show_all);
else if (pg_strcasecmp(cmd, "validate") == 0)
return do_validate(&range);
else if (pg_strcasecmp(cmd, "delete") == 0)
return do_delete(&range);
else
elog(ERROR_ARGS, "invalid command \"%s\"", cmd);
return 0;
}
void
pgut_help(bool details)
{
printf(_("%s manage backup/recovery of PostgreSQL database.\n\n"), PROGRAM_NAME);
printf(_("Usage:\n"));
printf(_(" %s OPTION init\n"), PROGRAM_NAME);
printf(_(" %s OPTION backup\n"), PROGRAM_NAME);
printf(_(" %s OPTION restore\n"), PROGRAM_NAME);
printf(_(" %s OPTION show [DATE]\n"), PROGRAM_NAME);
printf(_(" %s OPTION show timeline [DATE]\n"), PROGRAM_NAME);
printf(_(" %s OPTION validate [DATE]\n"), PROGRAM_NAME);
printf(_(" %s OPTION delete DATE\n"), PROGRAM_NAME);
if (!details)
return;
printf(_("\nCommon Options:\n"));
printf(_(" -D, --pgdata=PATH location of the database storage area\n"));
printf(_(" -A, --arclog-path=PATH location of archive WAL storage area\n"));
printf(_(" -S, --srvlog-path=PATH location of server log storage area\n"));
printf(_(" -B, --backup-path=PATH location of the backup storage area\n"));
printf(_(" -c, --check show what would have been done\n"));
printf(_("\nBackup options:\n"));
printf(_(" -b, --backup-mode=MODE full, incremental, or archive\n"));
printf(_(" -s, --with-serverlog also backup server log files\n"));
printf(_(" -Z, --compress-data compress data backup with zlib\n"));
printf(_(" -C, --smooth-checkpoint do smooth checkpoint before backup\n"));
printf(_(" --keep-data-generations=N keep GENERATION of full data backup\n"));
printf(_(" --keep-data-days=DAY keep enough data backup to recover to DAY days age\n"));
printf(_(" --keep-arclog-files=NUM keep NUM of archived WAL\n"));
printf(_(" --keep-arclog-days=DAY keep archived WAL modified in DAY days\n"));
printf(_(" --keep-srvlog-files=NUM keep NUM of serverlogs\n"));
printf(_(" --keep-srvlog-days=DAY keep serverlog modified in DAY days\n"));
printf(_("\nRestore options:\n"));
printf(_(" --recovery-target-time time stamp up to which recovery will proceed\n"));
printf(_(" --recovery-target-xid transaction ID up to which recovery will proceed\n"));
printf(_(" --recovery-target-inclusive whether we stop just after the recovery target\n"));
printf(_(" --recovery-target-timeline recovering into a particular timeline\n"));
printf(_("\nCatalog options:\n"));
printf(_(" -a, --show-all show deleted backup too\n"));
}
/*
* Create range object from one or two arguments.
* All not-digit characters in the argument(s) are igonred.
* Both arg1 and arg2 must be valid pointer.
*/
static void
parse_range(pgBackupRange *range, const char *arg1, const char *arg2)
{
size_t len = strlen(arg1) + strlen(arg2) + 1;
char *tmp;
int num;
struct tm tm;
tmp = pgut_malloc(len);
tmp[0] = '\0';
if (arg1 != NULL)
remove_not_digit(tmp, len, arg1);
if (arg2 != NULL)
remove_not_digit(tmp + strlen(tmp), len - strlen(tmp), arg2);
memset(&tm, 0, sizeof(tm));
tm.tm_year = 0; /* tm_year is year - 1900 */
tm.tm_mon = 0; /* tm_mon is 0 - 11 */
tm.tm_mday = 1; /* tm_mday is 1 - 31 */
tm.tm_hour = 0;
tm.tm_min = 0;
tm.tm_sec = 0;
num = sscanf(tmp, "%04d %02d %02d %02d %02d %02d",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec);
if (num < 1)
elog(ERROR_ARGS, _("supplied id(%s) is invalid."), tmp);
free(tmp);
/* adjust year and month to convert to time_t */
tm.tm_year -= 1900;
if (num > 1)
tm.tm_mon -= 1;
tm.tm_isdst = -1;
range->begin = mktime(&tm);
switch (num)
{
case 1:
tm.tm_year++;
break;
case 2:
tm.tm_mon++;
break;
case 3:
tm.tm_mday++;
break;
case 4:
tm.tm_hour++;
break;
case 5:
tm.tm_min++;
break;
case 6:
tm.tm_sec++;
break;
}
range->end = mktime(&tm);
range->end--;
}
static void
opt_backup_mode(pgut_option *opt, const char *arg)
{
current.backup_mode = parse_backup_mode(arg, ERROR_ARGS);
}
/*-------------------------------------------------------------------------
*
* pg_rman.c: Backup/Recovery manager for PostgreSQL.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
const char *PROGRAM_VERSION = "1.2.2";
const char *PROGRAM_URL = "http://code.google.com/p/pg-rman/";
const char *PROGRAM_EMAIL = "http://code.google.com/p/pg-rman/issues/list";
/* path configuration */
char *backup_path;
char *pgdata;
char *arclog_path;
char *srvlog_path;
/* common configuration */
bool verbose = false;
bool check = false;
/* directory configuration */
pgBackup current;
/* backup configuration */
static bool smooth_checkpoint;
static int keep_arclog_files = KEEP_INFINITE;
static int keep_arclog_days = KEEP_INFINITE;
static int keep_srvlog_files = KEEP_INFINITE;
static int keep_srvlog_days = KEEP_INFINITE;
static int keep_data_generations = KEEP_INFINITE;
static int keep_data_days = KEEP_INFINITE;
/* restore configuration */
static char *target_time;
static char *target_xid;
static char *target_inclusive;
static TimeLineID target_tli;
/* delete configuration */
static bool force;
/* show configuration */
static bool show_all = false;
static void opt_backup_mode(pgut_option *opt, const char *arg);
static void parse_range(pgBackupRange *range, const char *arg1, const char *arg2);
static pgut_option options[] =
{
/* directory options */
{ 's', 'D', "pgdata" , &pgdata , SOURCE_ENV },
{ 's', 'A', "arclog-path" , &arclog_path , SOURCE_ENV },
{ 's', 'B', "backup-path" , &backup_path , SOURCE_ENV },
{ 's', 'S', "srvlog-path" , &srvlog_path , SOURCE_ENV },
/* common options */
{ 'b', 'v', "verbose" , &verbose },
{ 'b', 'c', "check" , &check },
/* backup options */
{ 'f', 'b', "backup-mode" , opt_backup_mode , SOURCE_ENV },
{ 'b', 's', "with-serverlog" , &current.with_serverlog , SOURCE_ENV },
{ 'b', 'Z', "compress-data" , &current.compress_data , SOURCE_ENV },
{ 'b', 'C', "smooth-checkpoint" , &smooth_checkpoint , SOURCE_ENV },
/* delete options */
{ 'b', 'f', "force" , &force , SOURCE_ENV },
/* options with only long name (keep-xxx) */
{ 'i', 1, "keep-data-generations" , &keep_data_generations, SOURCE_ENV },
{ 'i', 2, "keep-data-days" , &keep_data_days , SOURCE_ENV },
{ 'i', 3, "keep-arclog-files" , &keep_arclog_files , SOURCE_ENV },
{ 'i', 4, "keep-arclog-days" , &keep_arclog_days , SOURCE_ENV },
{ 'i', 5, "keep-srvlog-files" , &keep_srvlog_files , SOURCE_ENV },
{ 'i', 6, "keep-srvlog-days" , &keep_srvlog_days , SOURCE_ENV },
/* restore options */
{ 's', 7, "recovery-target-time" , &target_time , SOURCE_ENV },
{ 's', 8, "recovery-target-xid" , &target_xid , SOURCE_ENV },
{ 's', 9, "recovery-target-inclusive" , &target_inclusive , SOURCE_ENV },
{ 'u', 10, "recovery-target-timeline" , &target_tli , SOURCE_ENV },
/* catalog options */
{ 'b', 'a', "show-all" , &show_all },
{ 0 }
};
/*
* Entry point of pg_rman command.
*/
int
main(int argc, char *argv[])
{
const char *cmd = NULL;
const char *range1 = NULL;
const char *range2 = NULL;
bool show_timeline = false;
pgBackupRange range;
int i;
/* do not buffer progress messages */
setvbuf(stdout, 0, _IONBF, 0); /* TODO: remove this */
/* initialize configuration */
catalog_init_config(&current);
/* overwrite configuration with command line arguments */
i = pgut_getopt(argc, argv, options);
for (; i < argc; i++)
{
if (cmd == NULL)
cmd = argv[i];
else if (pg_strcasecmp(argv[i], "timeline") == 0 &&
pg_strcasecmp(cmd, "show") == 0)
show_timeline = true;
else if (range1 == NULL)
range1 = argv[i];
else if (range2 == NULL)
range2 = argv[i];
else
elog(ERROR_ARGS, "too many arguments");
}
/* command argument (backup/restore/show/...) is required. */
if (cmd == NULL)
{
help(false);
return HELP;
}
/* get object range argument if any */
if (range1 && range2)
parse_range(&range, range1, range2);
else if (range1)
parse_range(&range, range1, "");
else
range.begin = range.end = 0;
/* Read default configuration from file. */
if (backup_path)
{
char path[MAXPGPATH];
/* Check if backup_path is directory. */
struct stat stat_buf;
int rc = stat(backup_path, &stat_buf);
if(rc != -1 && !S_ISDIR(stat_buf.st_mode)){
/* If rc == -1, there is no file or directory. So it's OK. */
elog(ERROR_ARGS, "-B, --backup-path must be a path to directory");
}
join_path_components(path, backup_path, PG_RMAN_INI_FILE);
pgut_readopt(path, options, ERROR_ARGS);
}
/* BACKUP_PATH is always required */
if (backup_path == NULL)
elog(ERROR_ARGS, "required parameter not specified: BACKUP_PATH (-B, --backup-path)");
/* path must be absolute */
if (backup_path != NULL && !is_absolute_path(backup_path))
elog(ERROR_ARGS, "-B, --backup-path must be an absolute path");
if (pgdata != NULL && !is_absolute_path(pgdata))
elog(ERROR_ARGS, "-D, --pgdata must be an absolute path");
if (arclog_path != NULL && !is_absolute_path(arclog_path))
elog(ERROR_ARGS, "-A, --arclog-path must be an absolute path");
if (srvlog_path != NULL && !is_absolute_path(srvlog_path))
elog(ERROR_ARGS, "-S, --srvlog-path must be an absolute path");
/* setup exclusion list for file search */
for (i = 0; pgdata_exclude[i]; i++) /* find first empty slot */
;
if (arclog_path)
pgdata_exclude[i++] = arclog_path;
if (srvlog_path)
pgdata_exclude[i++] = srvlog_path;
/* do actual operation */
if (pg_strcasecmp(cmd, "init") == 0)
return do_init();
else if (pg_strcasecmp(cmd, "backup") == 0)
return do_backup(smooth_checkpoint,
keep_arclog_files, keep_arclog_days,
keep_srvlog_files, keep_srvlog_days,
keep_data_generations, keep_data_days);
else if (pg_strcasecmp(cmd, "restore") == 0){
return do_restore(target_time, target_xid, target_inclusive, target_tli);
}
else if (pg_strcasecmp(cmd, "show") == 0)
return do_show(&range, show_timeline, show_all);
else if (pg_strcasecmp(cmd, "validate") == 0)
return do_validate(&range);
else if (pg_strcasecmp(cmd, "delete") == 0)
// return do_delete(&range);
return do_delete(&range, force);
else
elog(ERROR_ARGS, "invalid command \"%s\"", cmd);
return 0;
}
void
pgut_help(bool details)
{
printf(_("%s manage backup/recovery of PostgreSQL database.\n\n"), PROGRAM_NAME);
printf(_("Usage:\n"));
printf(_(" %s OPTION init\n"), PROGRAM_NAME);
printf(_(" %s OPTION backup\n"), PROGRAM_NAME);
printf(_(" %s OPTION restore\n"), PROGRAM_NAME);
printf(_(" %s OPTION show [DATE]\n"), PROGRAM_NAME);
printf(_(" %s OPTION show timeline [DATE]\n"), PROGRAM_NAME);
printf(_(" %s OPTION validate [DATE]\n"), PROGRAM_NAME);
printf(_(" %s OPTION delete DATE\n"), PROGRAM_NAME);
if (!details)
return;
printf(_("\nCommon Options:\n"));
printf(_(" -D, --pgdata=PATH location of the database storage area\n"));
printf(_(" -A, --arclog-path=PATH location of archive WAL storage area\n"));
printf(_(" -S, --srvlog-path=PATH location of server log storage area\n"));
printf(_(" -B, --backup-path=PATH location of the backup storage area\n"));
printf(_(" -c, --check show what would have been done\n"));
printf(_("\nBackup options:\n"));
printf(_(" -b, --backup-mode=MODE full, incremental, or archive\n"));
printf(_(" -s, --with-serverlog also backup server log files\n"));
printf(_(" -Z, --compress-data compress data backup with zlib\n"));
printf(_(" -C, --smooth-checkpoint do smooth checkpoint before backup\n"));
printf(_(" --keep-data-generations=N keep GENERATION of full data backup\n"));
printf(_(" --keep-data-days=DAY keep enough data backup to recover to DAY days age\n"));
printf(_(" --keep-arclog-files=NUM keep NUM of archived WAL\n"));
printf(_(" --keep-arclog-days=DAY keep archived WAL modified in DAY days\n"));
printf(_(" --keep-srvlog-files=NUM keep NUM of serverlogs\n"));
printf(_(" --keep-srvlog-days=DAY keep serverlog modified in DAY days\n"));
printf(_("\nRestore options:\n"));
printf(_(" --recovery-target-time time stamp up to which recovery will proceed\n"));
printf(_(" --recovery-target-xid transaction ID up to which recovery will proceed\n"));
printf(_(" --recovery-target-inclusive whether we stop just after the recovery target\n"));
printf(_(" --recovery-target-timeline recovering into a particular timeline\n"));
printf(_("\nCatalog options:\n"));
printf(_(" -a, --show-all show deleted backup too\n"));
}
/*
* Create range object from one or two arguments.
* All not-digit characters in the argument(s) are igonred.
* Both arg1 and arg2 must be valid pointer.
*/
static void
parse_range(pgBackupRange *range, const char *arg1, const char *arg2)
{
size_t len = strlen(arg1) + strlen(arg2) + 1;
char *tmp;
int num;
struct tm tm;
tmp = pgut_malloc(len);
tmp[0] = '\0';
if (arg1 != NULL)
remove_not_digit(tmp, len, arg1);
if (arg2 != NULL)
remove_not_digit(tmp + strlen(tmp), len - strlen(tmp), arg2);
memset(&tm, 0, sizeof(tm));
tm.tm_year = 0; /* tm_year is year - 1900 */
tm.tm_mon = 0; /* tm_mon is 0 - 11 */
tm.tm_mday = 1; /* tm_mday is 1 - 31 */
tm.tm_hour = 0;
tm.tm_min = 0;
tm.tm_sec = 0;
num = sscanf(tmp, "%04d %02d %02d %02d %02d %02d",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec);
if (num < 1){
if (strcmp(tmp,"") != 0)
elog(ERROR_ARGS, _("supplied id(%s) is invalid."), tmp);
else
elog(ERROR_ARGS, _("argments are invalid. near \"%s\""), arg1);
}
free(tmp);
/* adjust year and month to convert to time_t */
tm.tm_year -= 1900;
if (num > 1)
tm.tm_mon -= 1;
tm.tm_isdst = -1;
if(!IsValidTime(tm)){
elog(ERROR_ARGS, _("supplied time(%s) is invalid."), arg1);
}
range->begin = mktime(&tm);
switch (num)
{
case 1:
tm.tm_year++;
break;
case 2:
tm.tm_mon++;
break;
case 3:
tm.tm_mday++;
break;
case 4:
tm.tm_hour++;
break;
case 5:
tm.tm_min++;
break;
case 6:
tm.tm_sec++;
break;
}
range->end = mktime(&tm);
range->end--;
}
static void
opt_backup_mode(pgut_option *opt, const char *arg)
{
current.backup_mode = parse_backup_mode(arg, ERROR_ARGS);
}

View File

@ -2,7 +2,7 @@
*
* pg_rman.h: Backup/Recovery manager for PostgreSQL.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
@ -23,6 +23,13 @@
#define XLOG_BLCKSZ BLCKSZ
#endif
#if PG_VERSION_NUM < 80300
#define TXID_CURRENT_SQL "SELECT transactionid FROM pg_locks WHERE locktype = 'transactionid' AND pid = pg_backend_pid();"
#include <sys/stat.h>
#else
#define TXID_CURRENT_SQL "SELECT txid_current();"
#endif
/* Directory/File names */
#define DATABASE_DIR "database"
#define ARCLOG_DIR "arclog"
@ -88,6 +95,14 @@ typedef struct pgBackupRange
#define pgBackupRangeIsSingle(range) \
(pgBackupRangeIsValid(range) && (range)->begin == ((range)->end))
#define IsValidTime(tm) \
((tm.tm_sec >= 0 && tm.tm_sec <= 60) && /* range check for tm_sec (0-60) */ \
(tm.tm_min >= 0 && tm.tm_min <= 59) && /* range check for tm_min (0-59) */ \
(tm.tm_hour >= 0 && tm.tm_hour <= 23) && /* range check for tm_hour(0-23) */ \
(tm.tm_mday >= 1 && tm.tm_mday <= 31) && /* range check for tm_mday(1-31) */ \
(tm.tm_mon >= 0 && tm.tm_mon <= 11) && /* range check for tm_mon (0-23) */ \
(tm.tm_year + 1900 >= 1900)) /* range check for tm_year(70-) */
/* Backup status */
/* XXX re-order ? */
typedef enum BackupStatus
@ -131,6 +146,8 @@ typedef struct pgBackup
XLogRecPtr stop_lsn;
time_t start_time;
time_t end_time;
time_t recovery_time;
uint32 recovery_xid;
/* Size (-1 means not-backup'ed) */
int64 total_data_bytes;
@ -142,6 +159,7 @@ typedef struct pgBackup
/* data/wal block size for compatibility check */
uint32 block_size;
uint32 wal_block_size;
} pgBackup;
/* special values of pgBackup */
@ -161,6 +179,15 @@ typedef struct pgTimeLine
XLogRecPtr end;
} pgTimeLine;
typedef struct pgRecoveryTarget
{
bool time_specified;
time_t recovery_target_time;
bool xid_specified;
unsigned int recovery_target_xid;
bool recovery_target_inclusive;
} pgRecoveryTarget;
typedef enum CompressionMode
{
NO_COMPRESSION,
@ -215,12 +242,13 @@ extern int do_init(void);
extern int do_show(pgBackupRange *range, bool show_timeline, bool show_all);
/* in delete.c */
extern int do_delete(pgBackupRange *range);
//extern int do_delete(pgBackupRange *range);
extern int do_delete(pgBackupRange *range, bool force);
extern void pgBackupDelete(int keep_generations, int keep_days);
/* in validate.c */
extern int do_validate(pgBackupRange *range);
extern void pgBackupValidate(pgBackup *backup, bool size_only);
extern void pgBackupValidate(pgBackup *backup, bool size_only, bool for_get_timeline, bool with_database);
/* in catalog.c */
extern pgBackup *catalog_get_backup(time_t timestamp);

File diff suppressed because it is too large Load Diff

View File

@ -1,105 +1,105 @@
/*-------------------------------------------------------------------------
*
* pg_ctl --- start/stops/restarts the PostgreSQL server
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.111 2009/06/11 14:49:07 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include "pg_rman.h"
/* PID can be negative for standalone backend */
typedef long pgpid_t;
static pgpid_t get_pgpid(void);
static bool postmaster_is_alive(pid_t pid);
static char pid_file[MAXPGPATH];
static pgpid_t
get_pgpid(void)
{
FILE *pidf;
long pid;
snprintf(pid_file, lengthof(pid_file), "%s/postmaster.pid", pgdata);
pidf = fopen(pid_file, "r");
if (pidf == NULL)
{
/* No pid file, not an error on startup */
if (errno == ENOENT)
return 0;
else
elog(ERROR_SYSTEM, _("could not open PID file \"%s\": %s\n"),
pid_file, strerror(errno));
}
if (fscanf(pidf, "%ld", &pid) != 1)
elog(ERROR_PID_BROKEN, _("invalid data in PID file \"%s\"\n"), pid_file);
fclose(pidf);
return (pgpid_t) pid;
}
/*
* utility routines
*/
static bool
postmaster_is_alive(pid_t pid)
{
/*
* Test to see if the process is still there. Note that we do not
* consider an EPERM failure to mean that the process is still there;
* EPERM must mean that the given PID belongs to some other userid, and
* considering the permissions on $PGDATA, that means it's not the
* postmaster we are after.
*
* Don't believe that our own PID or parent shell's PID is the postmaster,
* either. (Windows hasn't got getppid(), though.)
*/
if (pid == getpid())
return false;
#ifndef WIN32
if (pid == getppid())
return false;
#endif
if (kill(pid, 0) == 0)
return true;
return false;
}
/*
* original is do_status() in src/bin/pg_ctl/pg_ctl.c
* changes are:
* renamed from do_status() from do_status().
* return true if PG server is running.
* don't print any message.
* don't print postopts file.
* log with elog() in pgut library.
*/
bool
is_pg_running(void)
{
pgpid_t pid;
pid = get_pgpid();
if (pid == 0) /* 0 means no pid file */
return false;
if (pid < 0) /* standalone backend */
pid = -pid;
return postmaster_is_alive((pid_t) pid);
}
/*-------------------------------------------------------------------------
*
* pg_ctl --- start/stops/restarts the PostgreSQL server
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.111 2009/06/11 14:49:07 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include "pg_rman.h"
/* PID can be negative for standalone backend */
typedef long pgpid_t;
static pgpid_t get_pgpid(void);
static bool postmaster_is_alive(pid_t pid);
static char pid_file[MAXPGPATH];
static pgpid_t
get_pgpid(void)
{
FILE *pidf;
long pid;
snprintf(pid_file, lengthof(pid_file), "%s/postmaster.pid", pgdata);
pidf = fopen(pid_file, "r");
if (pidf == NULL)
{
/* No pid file, not an error on startup */
if (errno == ENOENT)
return 0;
else
elog(ERROR_SYSTEM, _("could not open PID file \"%s\": %s\n"),
pid_file, strerror(errno));
}
if (fscanf(pidf, "%ld", &pid) != 1)
elog(ERROR_PID_BROKEN, _("invalid data in PID file \"%s\"\n"), pid_file);
fclose(pidf);
return (pgpid_t) pid;
}
/*
* utility routines
*/
static bool
postmaster_is_alive(pid_t pid)
{
/*
* Test to see if the process is still there. Note that we do not
* consider an EPERM failure to mean that the process is still there;
* EPERM must mean that the given PID belongs to some other userid, and
* considering the permissions on $PGDATA, that means it's not the
* postmaster we are after.
*
* Don't believe that our own PID or parent shell's PID is the postmaster,
* either. (Windows hasn't got getppid(), though.)
*/
if (pid == getpid())
return false;
#ifndef WIN32
if (pid == getppid())
return false;
#endif
if (kill(pid, 0) == 0)
return true;
return false;
}
/*
* original is do_status() in src/bin/pg_ctl/pg_ctl.c
* changes are:
* renamed from do_status() from do_status().
* return true if PG server is running.
* don't print any message.
* don't print postopts file.
* log with elog() in pgut library.
*/
bool
is_pg_running(void)
{
pgpid_t pid;
pid = get_pgpid();
if (pid == 0) /* 0 means no pid file */
return false;
if (pid < 0) /* standalone backend */
pid = -pid;
return postmaster_is_alive((pid_t) pid);
}

View File

@ -899,7 +899,8 @@ pgut_connect(int elevel)
elog(ERROR_INTERRUPTED, "interrupted");
#ifndef PGUT_NO_PROMPT
if (prompt_password == DEFAULT)
// if (prompt_password == DEFAULT) // katsumata
if (prompt_password == YES)
prompt_for_password(username);
#endif

View File

@ -2,7 +2,7 @@
*
* queue.c: Job queue with thread pooling.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/

112
restore.c
View File

@ -2,7 +2,7 @@
*
* restore.c: restore DB cluster and archived WAL.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
@ -24,10 +24,14 @@ static void create_recovery_conf(const char *target_time,
const char *target_xid,
const char *target_inclusive,
TimeLineID target_tli);
static pgRecoveryTarget *checkIfCreateRecoveryConf(const char *target_time,
const char *target_xid,
const char *target_inclusive);
static parray * readTimeLineHistory(TimeLineID targetTLI);
static bool satisfy_timeline(const parray *timelines, const pgBackup *backup);
static bool satisfy_recovery_target(const pgBackup *backup, const pgRecoveryTarget *rt);
static TimeLineID get_current_timeline(void);
static TimeLineID get_fullbackup_timeline(parray *backups);
static TimeLineID get_fullbackup_timeline(parray *backups, const pgRecoveryTarget *rt);
static void print_backup_id(const pgBackup *backup);
static void search_next_wal(const char *path, uint32 *needId, uint32 *needSeg, parray *timelines);
@ -50,6 +54,7 @@ do_restore(const char *target_time,
char timeline_dir[MAXPGPATH];
uint32 needId = 0;
uint32 needSeg = 0;
pgRecoveryTarget *rt = NULL;
/* PGDATA and ARCLOG_PATH are always required */
if (pgdata == NULL)
@ -80,11 +85,19 @@ do_restore(const char *target_time,
if (is_pg_running())
elog(ERROR_PG_RUNNING, _("PostgreSQL server is running"));
rt = checkIfCreateRecoveryConf(target_time, target_xid, target_inclusive);
if(rt == NULL){
elog(ERROR_ARGS, _("can't create recovery.conf. specified args are invalid."));
}
/* get list of backups. (index == 0) is the last backup */
backups = catalog_get_backup_list(NULL);
if(!backups){
elog(ERROR_SYSTEM, _("can't process any more."));
}
cur_tli = get_current_timeline();
backup_tli = get_fullbackup_timeline(backups);
backup_tli = get_fullbackup_timeline(backups, rt);
/* determine target timeline */
if (target_tli == 0)
@ -154,7 +167,7 @@ do_restore(const char *target_time,
_("can't restore from compressed backup (compression not supported in this installation)"));
}
#endif
if (satisfy_timeline(timelines, base_backup))
if (satisfy_timeline(timelines, base_backup) && satisfy_recovery_target(base_backup, rt))
goto base_backup_found;
}
/* no full backup found, can't restore */
@ -168,6 +181,7 @@ base_backup_found:
/* restore base backup */
restore_database(base_backup);
last_restored_index = base_index;
/* restore following incremental backup */
@ -183,11 +197,11 @@ base_backup_found:
continue;
/* use database backup only */
if (backup->backup_mode < BACKUP_MODE_INCREMENTAL)
if (backup->backup_mode != BACKUP_MODE_INCREMENTAL)
continue;
/* is the backup is necessary for restore to target timeline ? */
if (!satisfy_timeline(timelines, backup))
if (!satisfy_timeline(timelines, backup) && !satisfy_recovery_target(backup, rt))
continue;
if (verbose)
@ -314,9 +328,9 @@ restore_database(pgBackup *backup)
/*
* Validate backup files with its size, because load of CRC calculation is
* not light.
* not right.
*/
pgBackupValidate(backup, true);
pgBackupValidate(backup, true, false, true);
/* make direcotries and symbolic links */
pgBackupGetPath(backup, path, lengthof(path), MKDIRS_SH_FILE);
@ -452,7 +466,7 @@ restore_database(pgBackup *backup)
parray_free(files);
if (verbose && !check)
printf(_("resotre backup completed\n"));
printf(_("restore backup completed\n"));
}
/*
@ -476,6 +490,12 @@ restore_archive_logs(pgBackup *backup)
printf(_("restoring WAL from backup %s.\n"), timestamp);
}
/*
* Validate backup files with its size, because load of CRC calculation is
* not light.
*/
pgBackupValidate(backup, true, false, false);
pgBackupGetPath(backup, list_path, lengthof(list_path), ARCLOG_FILE_LIST);
pgBackupGetPath(backup, base_path, lengthof(list_path), ARCLOG_DIR);
files = dir_read_file_list(base_path, list_path);
@ -806,6 +826,28 @@ readTimeLineHistory(TimeLineID targetTLI)
return result;
}
static bool
satisfy_recovery_target(const pgBackup *backup, const pgRecoveryTarget *rt)
{
if(rt->xid_specified){
// elog(INFO, "in satisfy_recovery_target:xid::%u:%u", backup->recovery_xid, rt->recovery_target_xid);
if(backup->recovery_xid <= rt->recovery_target_xid)
return true;
else
return false;
}
if(rt->time_specified){
// elog(INFO, "in satisfy_recovery_target:time_t::%ld:%ld", backup->recovery_time, rt->recovery_target_time);
if(backup->recovery_time <= rt->recovery_target_time)
return true;
else
return false;
}
else{
return true;
}
}
static bool
satisfy_timeline(const parray *timelines, const pgBackup *backup)
{
@ -878,7 +920,7 @@ get_current_timeline(void)
/* get TLI of the latest full backup */
static TimeLineID
get_fullbackup_timeline(parray *backups)
get_fullbackup_timeline(parray *backups, const pgRecoveryTarget *rt)
{
int i;
pgBackup *base_backup = NULL;
@ -892,10 +934,13 @@ get_fullbackup_timeline(parray *backups)
{
/*
* Validate backup files with its size, because load of CRC
* calculation is not light.
* calculation is not right.
*/
if (base_backup->status == BACKUP_STATUS_DONE)
pgBackupValidate(base_backup, true);
pgBackupValidate(base_backup, true, true, false);
if(!satisfy_recovery_target(base_backup, rt))
continue;
if (base_backup->status == BACKUP_STATUS_OK)
break;
@ -969,3 +1014,46 @@ search_next_wal(const char *path, uint32 *needId, uint32 *needSeg, parray *timel
NextLogSeg(*needId, *needSeg);
}
}
static pgRecoveryTarget *
checkIfCreateRecoveryConf(const char *target_time,
const char *target_xid,
const char *target_inclusive)
{
time_t dummy_time;
unsigned int dummy_xid;
bool dummy_bool;
pgRecoveryTarget *rt;
// init pgRecoveryTarget
rt = pgut_new(pgRecoveryTarget);
rt->time_specified = false;
rt->xid_specified = false;
rt->recovery_target_time = 0;
rt->recovery_target_xid = 0;
rt->recovery_target_inclusive = false;
if(target_time){
rt->time_specified = true;
if(parse_time(target_time, &dummy_time))
rt->recovery_target_time = dummy_time;
else
elog(ERROR_ARGS, _("can't create recovery.conf with %s"), target_time);
}
if(target_xid){
rt->xid_specified = true;
if(parse_uint32(target_xid, &dummy_xid))
rt->recovery_target_xid = dummy_xid;
else
elog(ERROR_ARGS, _("can't create recovery.conf with %s"), target_xid);
}
if(target_inclusive){
if(parse_bool(target_inclusive, &dummy_bool))
rt->recovery_target_inclusive = dummy_bool;
else
elog(ERROR_ARGS, _("can't create recovery.conf with %s"), target_inclusive);
}
return rt;
}

7
show.c
View File

@ -2,7 +2,7 @@
*
* show.c: show backup catalog.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
@ -45,8 +45,9 @@ do_show(pgBackupRange *range, bool show_timeline, bool show_all)
parray *backup_list;
backup_list = catalog_get_backup_list(range);
if (backup_list == NULL)
return 1;
if (backup_list == NULL){
elog(ERROR_SYSTEM, _("can't process any more."));
}
if (!show_timeline)
show_backup_list(stdout, backup_list, show_all);

View File

@ -1,274 +1,281 @@
#!/bin/sh
BASE_PATH=`pwd`
export PGDATA=$BASE_PATH/results/sample_database
export BACKUP_PATH=$BASE_PATH/results/sample_backup2
export ARCLOG_PATH=$BASE_PATH/results/arclog
export SRVLOG_PATH=$PGDATA/pg_log
export COMPRESS_DATA=YES
XLOG_PATH=$PGDATA/pg_xlog
TBLSPC_PATH=$BASE_PATH/results/tblspc
# Port used for test database cluster
TEST_PGPORT=54321
# configuration
SCALE=1
DURATION=10
ISOLATE_SRVLOG=0
ISOLATE_WAL=0
while [ $# -gt 0 ]; do
case $1 in
"-s")
ISOLATE_SRVLOG=1
shift
;;
"-w")
ISOLATE_WAL=1
shift
;;
"-d")
DURATION=`expr $2 + 0`
if [ $? -ne 0 ]; then
echo "invalid duration"
exit 1
fi
shift 2
;;
"-s")
SCALE=`expr $2 + 0`
if [ $? -ne 0 ]; then
echo "invalid scale"
exit 1
fi
shift 2
;;
*)
shift
;;
esac
done
# delete old database cluster
pg_ctl stop -m immediate > /dev/null 2>&1
rm -rf $PGDATA
rm -rf $BASE_PATH/results/pg_xlog
rm -rf $BASE_PATH/results/srvlog
rm -rf $ARCLOG_PATH
rm -rf $SRVLOG_PATH
rm -rf $TBLSPC_PATH
# create new backup catalog
rm -rf $BACKUP_PATH
pg_rman init -B $BACKUP_PATH --quiet
# create default configuration file
cat << EOF > $BACKUP_PATH/pg_rman.ini
# comment
BACKUP_MODE = F # comment
EOF
# create new database cluster
initdb --no-locale > $BASE_PATH/results/initdb.log 2>&1
cat << EOF >> $PGDATA/postgresql.conf
port = $TEST_PGPORT
logging_collector = on
archive_mode = on
archive_command = 'cp "%p" "$ARCLOG_PATH/%f"'
EOF
mkdir -p $ARCLOG_PATH
mkdir -p $TBLSPC_PATH
# determine serverlog directory
if [ "$ISOLATE_SRVLOG" -ne 0 ]; then
export SRVLOG_PATH=$BASE_PATH/results/srvlog
echo "log_directory = '$SRVLOG_PATH'" >> $PGDATA/postgresql.conf
mkdir -p $SRVLOG_PATH
else
export SRVLOG_PATH=$PGDATA/pg_log
echo "log_directory = 'pg_log'" >> $PGDATA/postgresql.conf
fi
# isolate online WAL
if [ "$ISOLATE_WAL" -ne 0 ]; then
XLOG_PATH=$BASE_PATH/results/pg_xlog
mv $PGDATA/pg_xlog $XLOG_PATH
ln -s $XLOG_PATH $PGDATA/pg_xlog
fi
# start PostgreSQL
pg_ctl start -w -t 3600 > /dev/null 2>&1
# create tablespace and database for pgbench
mkdir -p $TBLSPC_PATH/pgbench
psql -p $TEST_PGPORT postgres <<EOF
CREATE TABLESPACE pgbench LOCATION '$TBLSPC_PATH/pgbench';
CREATE DATABASE pgbench TABLESPACE = pgbench;
EOF
# data_delete
export KEEP_DATA_GENERATIONS=2
export KEEP_DATA_DAYS=0
for i in `seq 1 5`; do
pg_rman -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_0_$i 2>&1
done
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show_d_1 2>&1
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show_d_1
pgbench -p $TEST_PGPORT -i -s $SCALE pgbench > $BASE_PATH/results/pgbench.log 2>&1
echo "full database backup"
psql -p $TEST_PGPORT postgres -c "checkpoint"
pg_rman -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_1 2>&1
pgbench -p $TEST_PGPORT -T $DURATION -c 10 pgbench >> $BASE_PATH/results/pgbench.log 2>&1
echo "incremental database backup"
psql -p $TEST_PGPORT postgres -c "checkpoint"
pg_rman -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr1 2>&1
# validate all backup
pg_rman validate `date +%Y` --verbose > $BASE_PATH/results/log_validate1 2>&1
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show0 2>&1
pg_dumpall > $BASE_PATH/results/dump_before_rtx.sql
target_xid=`psql -p $TEST_PGPORT pgbench -tAq -c "INSERT INTO pgbench_history VALUES (1) RETURNING(xmin);"`
psql -p $TEST_PGPORT postgres -c "checkpoint"
pg_rman -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr2 2>&1
pgbench -p $TEST_PGPORT -T $DURATION -c 10 pgbench >> $BASE_PATH/results/pgbench.log 2>&1
echo "archived WAL and serverlog backup"
pg_rman -p $TEST_PGPORT backup -b a --verbose -d postgres > $BASE_PATH/results/log_arclog 2>&1
# stop PG during transaction and get commited info for verifing
echo "stop DB during running pgbench"
pgbench -p $TEST_PGPORT -T $DURATION -c 10 pgbench >> $BASE_PATH/results/pgbench.log 2>&1 &
sleep `expr $DURATION / 2`
pg_ctl stop -m immediate > /dev/null 2>&1
cp -rp $PGDATA $PGDATA.bak
pg_ctl start -w -t 3600 > /dev/null 2>&1
pg_dumpall > $BASE_PATH/results/dump_before.sql
# revert to crushed cluster
pg_ctl stop > /dev/null 2>&1
rm -rf $PGDATA
mv $PGDATA.bak $PGDATA
# validate all backup
pg_rman validate `date +%Y` --verbose > $BASE_PATH/results/log_validate2 2>&1
# restore check with pg_rman
pg_rman restore -! --verbose --check > $BASE_PATH/results/log_restore_check_1 2>&1
# restore with pg_rman
CUR_TLI=`pg_controldata | grep TimeLineID | awk '{print $4}'`
pg_rman restore -! --verbose > $BASE_PATH/results/log_restore1_1 2>&1
CUR_TLI_R=`grep "current timeline ID = " $BASE_PATH/results/log_restore1_1 | awk '{print $5}'`
TARGET_TLI=`grep "target timeline ID = " $BASE_PATH/results/log_restore1_1 | awk '{print $5}'`
if [ "$CUR_TLI" != "$CUR_TLI_R" -o "$CUR_TLI" != "$CUR_TLI_R" ]; then
echo "failed: bad timeline ID" CUR_TLI=$CUR_TLI CUR_TLI_R=$CUR_TLI_R
fi
# Backup of online-WAL and serverlog.
echo "diff files in BACKUP_PATH/backup/pg_xlog"
diff -r $PGDATA/pg_xlog $BACKUP_PATH/backup/pg_xlog
echo "# of files in BACKUP_PATH/backup/srvlog"
find $BACKUP_PATH/backup/srvlog -type f | wc -l
# recovery database
pg_ctl start -w -t 3600 > /dev/null 2>&1
# re-restore with pg_rman
pg_ctl stop -m immediate > /dev/null 2>&1
# restore check with pg_rman
pg_rman restore -! --verbose --check > $BASE_PATH/results/log_restore_check_2 2>&1
CUR_TLI=`pg_controldata | grep TimeLineID | awk '{print $4}'`
pg_rman restore -! --verbose > $BASE_PATH/results/log_restore1_2 2>&1
CUR_TLI_R=`grep "current timeline ID = " $BASE_PATH/results/log_restore1_2 | awk '{print $5}'`
TARGET_TLI=`grep "target timeline ID = " $BASE_PATH/results/log_restore1_2 | awk '{print $5}'`
if [ "$CUR_TLI" != "$CUR_TLI_R" -o "$CUR_TLI" != "$CUR_TLI_R" ]; then
echo "failed: bad timeline ID" CUR_TLI=$CUR_TLI CUR_TLI_R=$CUR_TLI_R
fi
# Backup of online-WAL and serverlog.
echo "diff files in BACKUP_PATH/backup/pg_xlog"
diff -r $PGDATA/pg_xlog $BACKUP_PATH/backup/pg_xlog
echo "# of files in BACKUP_PATH/backup/srvlog"
find $BACKUP_PATH/backup/srvlog -type f | wc -l
# re-recovery database
pg_ctl start -w -t 3600 > /dev/null 2>&1
# compare recovery results
pg_dumpall > $BASE_PATH/results/dump_after.sql
diff $BASE_PATH/results/dump_before.sql $BASE_PATH/results/dump_after.sql
# take a backup and delete backed up online files
# incrementa backup can't find last full backup because new timeline started.
echo "full database backup after recovery"
psql -p $TEST_PGPORT postgres -c "checkpoint"
pg_rman -p $TEST_PGPORT backup -b f --verbose -d postgres > $BASE_PATH/results/log_full2 2>&1
# Backup of online-WAL should been deleted, but serverlog remain.
echo "# of files in BACKUP_PATH/backup/pg_xlog"
find $BACKUP_PATH/backup/pg_xlog -type f | wc -l
echo "# of files in BACKUP_PATH/backup/srvlog"
find $BACKUP_PATH/backup/srvlog -type f | wc -l
# Symbolic links in $ARCLOG_PATH should be deleted.
echo "# of symbolic links in ARCLOG_PATH"
find $ARCLOG_PATH -type l | wc -l
# timeline history files are backed up.
echo "# of files in BACKUP_PATH/timeline_history"
find $BACKUP_PATH/timeline_history -type f | wc -l
# restore with pg_rman
pg_ctl stop -m immediate > /dev/null 2>&1
# restore check with pg_rman
pg_rman restore -! --verbose --check > $BASE_PATH/results/log_restore_check_3 2>&1
CUR_TLI=`pg_controldata | grep TimeLineID | awk '{print $4}'`
pg_rman restore -! --recovery-target-xid $target_xid --recovery-target-inclusive false --verbose > $BASE_PATH/results/log_restore2 2>&1
CUR_TLI_R=`grep "current timeline ID = " $BASE_PATH/results/log_restore2 | awk '{print $5}'`
TARGET_TLI=`grep "target timeline ID = " $BASE_PATH/results/log_restore2 | awk '{print $5}'`
if [ "$CUR_TLI" != "$CUR_TLI_R" -o "$CUR_TLI" != "$CUR_TLI_R" ]; then
echo "failed: bad timeline ID" CUR_TLI=$CUR_TLI CUR_TLI_R=$CUR_TLI_R
fi
echo "# of recovery target option in recovery.conf"
grep -c "recovery_target_" $PGDATA/recovery.conf
# recovery database
pg_ctl start -w -t 3600 > /dev/null 2>&1
pg_dumpall > $BASE_PATH/results/dump_after_rtx.sql
diff $BASE_PATH/results/dump_before_rtx.sql $BASE_PATH/results/dump_after_rtx.sql
# show timeline
pg_rman -p $TEST_PGPORT show timeline --verbose -a -d postgres > $BASE_PATH/results/log_show_timeline_1 2>&1
pg_rman -p $TEST_PGPORT show timeline `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show_timeline_2 2>&1
pg_rman -p $TEST_PGPORT show timeline `date +%Y` --verbose -d postgres > $BASE_PATH/results/log_show_timeline_3 2>&1
echo "# of deleted backups (show all)"
grep -c DELETED $BASE_PATH/results/log_show_timeline_2
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show_timeline_3
echo "delete backup"
pg_rman -p $TEST_PGPORT delete --debug -d postgres > $BASE_PATH/results/log_delete1 2>&1
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show1 2>&1
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show1
pg_rman -p $TEST_PGPORT delete `date "+%Y-%m-%d %T"` --debug -d postgres > $BASE_PATH/results/log_delete2 2>&1
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show2 2>&1
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show2
pg_rman -p $TEST_PGPORT show timeline `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show_timeline_4 2>&1
# cleanup
pg_ctl stop -m immediate > /dev/null 2>&1
#!/bin/sh
BASE_PATH=`pwd`
export PGDATA=$BASE_PATH/results/sample_database
export BACKUP_PATH=$BASE_PATH/results/sample_backup2
export ARCLOG_PATH=$BASE_PATH/results/arclog
export SRVLOG_PATH=$PGDATA/pg_log
export COMPRESS_DATA=YES
XLOG_PATH=$PGDATA/pg_xlog
TBLSPC_PATH=$BASE_PATH/results/tblspc
# Port used for test database cluster
TEST_PGPORT=54321
# configuration
SCALE=1
DURATION=10
ISOLATE_SRVLOG=0
ISOLATE_WAL=0
while [ $# -gt 0 ]; do
case $1 in
"-s")
ISOLATE_SRVLOG=1
shift
;;
"-w")
ISOLATE_WAL=1
shift
;;
"-d")
DURATION=`expr $2 + 0`
if [ $? -ne 0 ]; then
echo "invalid duration"
exit 1
fi
shift 2
;;
"-s")
SCALE=`expr $2 + 0`
if [ $? -ne 0 ]; then
echo "invalid scale"
exit 1
fi
shift 2
;;
*)
shift
;;
esac
done
# delete old database cluster
pg_ctl stop -m immediate > /dev/null 2>&1
rm -rf $PGDATA
rm -rf $BASE_PATH/results/pg_xlog
rm -rf $BASE_PATH/results/srvlog
rm -rf $ARCLOG_PATH
rm -rf $SRVLOG_PATH
rm -rf $TBLSPC_PATH
# create new backup catalog
rm -rf $BACKUP_PATH
pg_rman init -B $BACKUP_PATH --quiet
# create default configuration file
cat << EOF > $BACKUP_PATH/pg_rman.ini
# comment
BACKUP_MODE = F # comment
EOF
# create new database cluster
initdb --no-locale > $BASE_PATH/results/initdb.log 2>&1
cat << EOF >> $PGDATA/postgresql.conf
port = $TEST_PGPORT
logging_collector = on
wal_level = archive
archive_mode = on
archive_command = 'cp "%p" "$ARCLOG_PATH/%f"'
EOF
mkdir -p $ARCLOG_PATH
mkdir -p $TBLSPC_PATH
# determine serverlog directory
if [ "$ISOLATE_SRVLOG" -ne 0 ]; then
export SRVLOG_PATH=$BASE_PATH/results/srvlog
echo "log_directory = '$SRVLOG_PATH'" >> $PGDATA/postgresql.conf
mkdir -p $SRVLOG_PATH
else
export SRVLOG_PATH=$PGDATA/pg_log
echo "log_directory = 'pg_log'" >> $PGDATA/postgresql.conf
fi
# isolate online WAL
if [ "$ISOLATE_WAL" -ne 0 ]; then
XLOG_PATH=$BASE_PATH/results/pg_xlog
mv $PGDATA/pg_xlog $XLOG_PATH
ln -s $XLOG_PATH $PGDATA/pg_xlog
fi
# start PostgreSQL
pg_ctl start -w -t 3600 > /dev/null 2>&1
# create tablespace and database for pgbench
mkdir -p $TBLSPC_PATH/pgbench
psql -p $TEST_PGPORT postgres <<EOF
CREATE TABLESPACE pgbench LOCATION '$TBLSPC_PATH/pgbench';
CREATE DATABASE pgbench TABLESPACE = pgbench;
EOF
# data_delete
export KEEP_DATA_GENERATIONS=2
export KEEP_DATA_DAYS=0
for i in `seq 1 5`; do
# pg_rman -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_0_$i 2>&1
pg_rman -w -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_0_$i 2>&1
done
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show_d_1 2>&1
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show_d_1
pgbench -p $TEST_PGPORT -i -s $SCALE pgbench > $BASE_PATH/results/pgbench.log 2>&1
echo "full database backup"
psql -p $TEST_PGPORT postgres -c "checkpoint"
#pg_rman -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_1 2>&1
pg_rman -w -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_1 2>&1
pgbench -p $TEST_PGPORT -T $DURATION -c 10 pgbench >> $BASE_PATH/results/pgbench.log 2>&1
echo "incremental database backup"
psql -p $TEST_PGPORT postgres -c "checkpoint"
#pg_rman -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr1 2>&1
pg_rman -w -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr1 2>&1
# validate all backup
pg_rman validate `date +%Y` --verbose > $BASE_PATH/results/log_validate1 2>&1
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show0 2>&1
pg_dumpall > $BASE_PATH/results/dump_before_rtx.sql
target_xid=`psql -p $TEST_PGPORT pgbench -tAq -c "INSERT INTO pgbench_history VALUES (1) RETURNING(xmin);"`
psql -p $TEST_PGPORT postgres -c "checkpoint"
#pg_rman -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr2 2>&1
pg_rman -w -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr2 2>&1
pgbench -p $TEST_PGPORT -T $DURATION -c 10 pgbench >> $BASE_PATH/results/pgbench.log 2>&1
echo "archived WAL and serverlog backup"
#pg_rman -p $TEST_PGPORT backup -b a --verbose -d postgres > $BASE_PATH/results/log_arclog 2>&1
pg_rman -w -p $TEST_PGPORT backup -b a --verbose -d postgres > $BASE_PATH/results/log_arclog 2>&1
# stop PG during transaction and get commited info for verifing
echo "stop DB during running pgbench"
pgbench -p $TEST_PGPORT -T $DURATION -c 10 pgbench >> $BASE_PATH/results/pgbench.log 2>&1 &
sleep `expr $DURATION / 2`
pg_ctl stop -m immediate > /dev/null 2>&1
cp -rp $PGDATA $PGDATA.bak
pg_ctl start -w -t 3600 > /dev/null 2>&1
pg_dumpall > $BASE_PATH/results/dump_before.sql
# revert to crushed cluster
pg_ctl stop > /dev/null 2>&1
rm -rf $PGDATA
mv $PGDATA.bak $PGDATA
# validate all backup
pg_rman validate `date +%Y` --verbose > $BASE_PATH/results/log_validate2 2>&1
# restore check with pg_rman
pg_rman restore -! --verbose --check > $BASE_PATH/results/log_restore_check_1 2>&1
# restore with pg_rman
CUR_TLI=`pg_controldata | grep TimeLineID | awk '{print $4}'`
pg_rman restore -! --verbose > $BASE_PATH/results/log_restore1_1 2>&1
CUR_TLI_R=`grep "current timeline ID = " $BASE_PATH/results/log_restore1_1 | awk '{print $5}'`
TARGET_TLI=`grep "target timeline ID = " $BASE_PATH/results/log_restore1_1 | awk '{print $5}'`
if [ "$CUR_TLI" != "$CUR_TLI_R" -o "$CUR_TLI" != "$CUR_TLI_R" ]; then
echo "failed: bad timeline ID" CUR_TLI=$CUR_TLI CUR_TLI_R=$CUR_TLI_R
fi
# Backup of online-WAL and serverlog.
echo "diff files in BACKUP_PATH/backup/pg_xlog"
diff -r $PGDATA/pg_xlog $BACKUP_PATH/backup/pg_xlog
echo "# of files in BACKUP_PATH/backup/srvlog"
find $BACKUP_PATH/backup/srvlog -type f | wc -l
# recovery database
pg_ctl start -w -t 3600 > /dev/null 2>&1
# re-restore with pg_rman
pg_ctl stop -m immediate > /dev/null 2>&1
# restore check with pg_rman
pg_rman restore -! --verbose --check > $BASE_PATH/results/log_restore_check_2 2>&1
CUR_TLI=`pg_controldata | grep TimeLineID | awk '{print $4}'`
pg_rman restore -! --verbose > $BASE_PATH/results/log_restore1_2 2>&1
CUR_TLI_R=`grep "current timeline ID = " $BASE_PATH/results/log_restore1_2 | awk '{print $5}'`
TARGET_TLI=`grep "target timeline ID = " $BASE_PATH/results/log_restore1_2 | awk '{print $5}'`
if [ "$CUR_TLI" != "$CUR_TLI_R" -o "$CUR_TLI" != "$CUR_TLI_R" ]; then
echo "failed: bad timeline ID" CUR_TLI=$CUR_TLI CUR_TLI_R=$CUR_TLI_R
fi
# Backup of online-WAL and serverlog.
echo "diff files in BACKUP_PATH/backup/pg_xlog"
diff -r $PGDATA/pg_xlog $BACKUP_PATH/backup/pg_xlog
echo "# of files in BACKUP_PATH/backup/srvlog"
find $BACKUP_PATH/backup/srvlog -type f | wc -l
# re-recovery database
pg_ctl start -w -t 3600 > /dev/null 2>&1
# compare recovery results
pg_dumpall > $BASE_PATH/results/dump_after.sql
diff $BASE_PATH/results/dump_before.sql $BASE_PATH/results/dump_after.sql
# take a backup and delete backed up online files
# incrementa backup can't find last full backup because new timeline started.
echo "full database backup after recovery"
psql -p $TEST_PGPORT postgres -c "checkpoint"
#pg_rman -p $TEST_PGPORT backup -b f --verbose -d postgres > $BASE_PATH/results/log_full2 2>&1
pg_rman -w -p $TEST_PGPORT backup -b f --verbose -d postgres > $BASE_PATH/results/log_full2 2>&1
# Backup of online-WAL should been deleted, but serverlog remain.
echo "# of files in BACKUP_PATH/backup/pg_xlog"
find $BACKUP_PATH/backup/pg_xlog -type f | wc -l
echo "# of files in BACKUP_PATH/backup/srvlog"
find $BACKUP_PATH/backup/srvlog -type f | wc -l
# Symbolic links in $ARCLOG_PATH should be deleted.
echo "# of symbolic links in ARCLOG_PATH"
find $ARCLOG_PATH -type l | wc -l
# timeline history files are backed up.
echo "# of files in BACKUP_PATH/timeline_history"
find $BACKUP_PATH/timeline_history -type f | wc -l
# restore with pg_rman
pg_ctl stop -m immediate > /dev/null 2>&1
# restore check with pg_rman
pg_rman restore -! --verbose --check > $BASE_PATH/results/log_restore_check_3 2>&1
CUR_TLI=`pg_controldata | grep TimeLineID | awk '{print $4}'`
pg_rman restore -! --recovery-target-xid $target_xid --recovery-target-inclusive false --verbose > $BASE_PATH/results/log_restore2 2>&1
CUR_TLI_R=`grep "current timeline ID = " $BASE_PATH/results/log_restore2 | awk '{print $5}'`
TARGET_TLI=`grep "target timeline ID = " $BASE_PATH/results/log_restore2 | awk '{print $5}'`
if [ "$CUR_TLI" != "$CUR_TLI_R" -o "$CUR_TLI" != "$CUR_TLI_R" ]; then
echo "failed: bad timeline ID" CUR_TLI=$CUR_TLI CUR_TLI_R=$CUR_TLI_R
fi
echo "# of recovery target option in recovery.conf"
grep -c "recovery_target_" $PGDATA/recovery.conf
# recovery database
pg_ctl start -w -t 3600 > /dev/null 2>&1
pg_dumpall > $BASE_PATH/results/dump_after_rtx.sql
diff $BASE_PATH/results/dump_before_rtx.sql $BASE_PATH/results/dump_after_rtx.sql
# show timeline
pg_rman -p $TEST_PGPORT show timeline --verbose -a -d postgres > $BASE_PATH/results/log_show_timeline_1 2>&1
pg_rman -p $TEST_PGPORT show timeline `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show_timeline_2 2>&1
pg_rman -p $TEST_PGPORT show timeline `date +%Y` --verbose -d postgres > $BASE_PATH/results/log_show_timeline_3 2>&1
echo "# of deleted backups (show all)"
grep -c DELETED $BASE_PATH/results/log_show_timeline_2
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show_timeline_3
echo "delete backup"
pg_rman -p $TEST_PGPORT delete --debug -d postgres > $BASE_PATH/results/log_delete1 2>&1
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show1 2>&1
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show1
pg_rman -p $TEST_PGPORT delete `date "+%Y-%m-%d %T"` --debug -d postgres > $BASE_PATH/results/log_delete2 2>&1
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show2 2>&1
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show2
pg_rman -p $TEST_PGPORT show timeline `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show_timeline_4 2>&1
# cleanup
pg_ctl stop -m immediate > /dev/null 2>&1

View File

@ -1,4 +1,4 @@
\! rm -rf results/init_test
\! pg_rman init -B results/init_test --quiet;echo $?
\! pg_rman init -B ${PWD}/results/init_test --quiet;echo $?
\! find results/init_test | xargs ls -Fd | sort
\! pg_rman init -B results/init_test --quiet;echo $?
\! pg_rman init -B ${PWD}/results/init_test --quiet;echo $?

View File

@ -1,80 +1,80 @@
#!/bin/sh
#============================================================================
# This is a test script for option test of pg_rman.
#============================================================================
BASE_PATH=`pwd`
# Clear environment variables used by pg_rman except $PGDATA.
# List of environment variables is defined in catalog.c.
unset BACKUP_PATH
unset ARCLOG_PATH
unset SRVLOG_PATH
unset BACKUP_MODE
unset COMPRESS_DATA
unset KEEP_ARCLOG_DAYS
unset KEEP_DATA_GENERATIONS
unset KEEP_DATA_DAYS
unset KEEP_SRVLOG_FILES
unset KEEP_SRVLOG_DAYS
export PGDATA=$BASE_PATH/results/sample_database
# Note: not exported
BACKUP_PATH=$BASE_PATH/results/sample_backup2
# Setup backup catalog for backup test.
rm -rf $BACKUP_PATH
cp -rp data/sample_backup $BACKUP_PATH
# general option
pg_rman --help
pg_rman --version
# backup option
# required arguments check
pg_rman backup --verbose
pg_rman backup --verbose -B $BACKUP_PATH
pg_rman backup --verbose -B $BACKUP_PATH -b f
pg_rman backup --verbose -B $BACKUP_PATH -b i
pg_rman backup --verbose -B $BACKUP_PATH -b a
# bad arguments check
pg_rman backup --verbose -B $BACKUP_PATH -b bad
# delete or validate requires DATE
pg_rman delete -B $BACKUP_PATH
pg_rman validate -B $BACKUP_PATH
# invalid configuration file check
echo " = INFINITE" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE= " > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE = F#S" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE = F #comment A" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE=B" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "COMPRESS_DATA=FOO" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "KEEP_ARCLOG_FILES=YES" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "TIMELINEID=-1" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_TARGETS=F" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE='F''\'\\\F'" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
# configuration priorityfile check
echo "BACKUP_MODE=ENV_PATH" > $BACKUP_PATH/pg_rman.ini
mkdir $BACKUP_PATH/conf_path
echo "BACKUP_PATH=$BACKUP_PATH/conf_path" > $BACKUP_PATH/pg_rman.conf
echo "BACKUP_MODE=CONF_PATH" > $BACKUP_PATH/conf_path/pg_rman.ini
mkdir $BACKUP_PATH/comm_path
echo "BACKUP_MODE=COMM_PATH" > $BACKUP_PATH/comm_path/pg_rman.ini
export BACKUP_PATH=$BACKUP_PATH
pg_rman backup --verbose
#!/bin/sh
#============================================================================
# This is a test script for option test of pg_rman.
#============================================================================
BASE_PATH=`pwd`
# Clear environment variables used by pg_rman except $PGDATA.
# List of environment variables is defined in catalog.c.
unset BACKUP_PATH
unset ARCLOG_PATH
unset SRVLOG_PATH
unset BACKUP_MODE
unset COMPRESS_DATA
unset KEEP_ARCLOG_DAYS
unset KEEP_DATA_GENERATIONS
unset KEEP_DATA_DAYS
unset KEEP_SRVLOG_FILES
unset KEEP_SRVLOG_DAYS
export PGDATA=$BASE_PATH/results/sample_database
# Note: not exported
BACKUP_PATH=$BASE_PATH/results/sample_backup2
# Setup backup catalog for backup test.
rm -rf $BACKUP_PATH
cp -rp data/sample_backup $BACKUP_PATH
# general option
pg_rman --help
pg_rman --version
# backup option
# required arguments check
pg_rman backup --verbose
pg_rman backup --verbose -B $BACKUP_PATH
pg_rman backup --verbose -B $BACKUP_PATH -b f
pg_rman backup --verbose -B $BACKUP_PATH -b i
pg_rman backup --verbose -B $BACKUP_PATH -b a
# bad arguments check
pg_rman backup --verbose -B $BACKUP_PATH -b bad
# delete or validate requires DATE
pg_rman delete -B $BACKUP_PATH
pg_rman validate -B $BACKUP_PATH
# invalid configuration file check
echo " = INFINITE" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE= " > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE = F#S" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE = F #comment A" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE=B" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "COMPRESS_DATA=FOO" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "KEEP_ARCLOG_FILES=YES" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "TIMELINEID=-1" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_TARGETS=F" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE='F''\'\\\F'" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
# configuration priorityfile check
echo "BACKUP_MODE=ENV_PATH" > $BACKUP_PATH/pg_rman.ini
mkdir $BACKUP_PATH/conf_path
echo "BACKUP_PATH=$BACKUP_PATH/conf_path" > $BACKUP_PATH/pg_rman.conf
echo "BACKUP_MODE=CONF_PATH" > $BACKUP_PATH/conf_path/pg_rman.ini
mkdir $BACKUP_PATH/comm_path
echo "BACKUP_MODE=COMM_PATH" > $BACKUP_PATH/comm_path/pg_rman.ini
export BACKUP_PATH=$BACKUP_PATH
pg_rman backup --verbose

View File

@ -1,8 +1,8 @@
-- test show command
\! rm -rf results/sample_backup
\! cp -rp data/sample_backup results/sample_backup
\! pg_rman show -B results/sample_backup
\! pg_rman validate -B results/sample_backup 2009-05-31 17:05:53 --debug
\! pg_rman validate -B results/sample_backup 2009-06-01 17:05:53 --debug
\! pg_rman show -a -B results/sample_backup
\! pg_rman show 2009-06-01 17:05:53 -B results/sample_backup
\! rm -rf ${PWD}/results/sample_backup
\! cp -rp data/sample_backup ${PWD}/results/sample_backup
\! pg_rman show -B ${PWD}/results/sample_backup
\! pg_rman validate -B ${PWD}/results/sample_backup 2009-05-31 17:05:53 --debug
\! pg_rman validate -B ${PWD}/results/sample_backup 2009-06-01 17:05:53 --debug
\! pg_rman show -a -B ${PWD}/results/sample_backup
\! pg_rman show 2009-06-01 17:05:53 -B ${PWD}/results/sample_backup

2
util.c
View File

@ -2,7 +2,7 @@
*
* util.c: log messages to log file or stderr, and misc code.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/

View File

@ -2,7 +2,7 @@
*
* utils.c:
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/

View File

@ -2,7 +2,7 @@
*
* validate.c: validate backup files.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
@ -22,22 +22,36 @@ do_validate(pgBackupRange *range)
{
int i;
parray *backup_list;
int ret;
bool another_pg_rman = false;
catalog_lock();
ret = catalog_lock();
if (ret == 1)
another_pg_rman = true;
/* get backup list matches given range */
backup_list = catalog_get_backup_list(range);
if(!backup_list){
elog(ERROR_SYSTEM, _("can't process any more."));
}
parray_qsort(backup_list, pgBackupCompareId);
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup = (pgBackup *)parray_get(backup_list, i);
/* clean extra backups (switch STATUS to ERROR) */
if(!another_pg_rman &&
(backup->status == BACKUP_STATUS_RUNNING || backup->status == BACKUP_STATUS_DELETING)){
backup->status = BACKUP_STATUS_ERROR;
pgBackupWriteIni(backup);
}
/* Validate completed backups only. */
if (backup->status != BACKUP_STATUS_DONE)
continue;
/* validate with CRC value and update status to OK */
pgBackupValidate(backup, false);
pgBackupValidate(backup, false, false, (HAVE_DATABASE(backup)));
}
/* cleanup */
@ -53,7 +67,7 @@ do_validate(pgBackupRange *range)
* Validate each files in the backup with its size.
*/
void
pgBackupValidate(pgBackup *backup, bool size_only)
pgBackupValidate(pgBackup *backup, bool size_only, bool for_get_timeline, bool with_database)
{
char timestamp[100];
char base_path[MAXPGPATH];
@ -62,54 +76,65 @@ pgBackupValidate(pgBackup *backup, bool size_only)
bool corrupted = false;
time2iso(timestamp, lengthof(timestamp), backup->start_time);
elog(INFO, "validate: %s", timestamp);
if (HAVE_DATABASE(backup))
{
elog(LOG, "database files...");
pgBackupGetPath(backup, base_path, lengthof(base_path), DATABASE_DIR);
pgBackupGetPath(backup, path, lengthof(path),
DATABASE_FILE_LIST);
files = dir_read_file_list(base_path, path);
if (!pgBackupValidateFiles(files, base_path, size_only))
corrupted = true;
parray_walk(files, pgFileFree);
parray_free(files);
}
if (HAVE_ARCLOG(backup))
{
elog(LOG, "archive WAL files...");
pgBackupGetPath(backup, base_path, lengthof(base_path), ARCLOG_DIR);
pgBackupGetPath(backup, path, lengthof(path), ARCLOG_FILE_LIST);
files = dir_read_file_list(base_path, path);
if (!pgBackupValidateFiles(files, base_path, size_only))
corrupted = true;
parray_walk(files, pgFileFree);
parray_free(files);
}
if (backup->with_serverlog)
{
elog(LOG, "server log files...");
pgBackupGetPath(backup, base_path, lengthof(base_path), SRVLOG_DIR);
pgBackupGetPath(backup, path, lengthof(path), SRVLOG_FILE_LIST);
files = dir_read_file_list(base_path, path);
if (!pgBackupValidateFiles(files, base_path, size_only))
corrupted = true;
parray_walk(files, pgFileFree);
parray_free(files);
if(!for_get_timeline){
if (with_database)
elog(INFO, "validate: %s backup and archive log files by %s", timestamp, (size_only ? "SIZE" : "CRC"));
else{
if (backup->backup_mode == BACKUP_MODE_ARCHIVE)
elog(INFO, "validate: %s archive log files by %s", timestamp, (size_only ? "SIZE" : "CRC"));
else if (backup->with_serverlog)
elog(INFO, "validate: %s server log files by %s", timestamp, (size_only ? "SIZE" : "CRC"));
}
}
/* update status to OK */
if (corrupted)
backup->status = BACKUP_STATUS_CORRUPT;
else
backup->status = BACKUP_STATUS_OK;
pgBackupWriteIni(backup);
if(!check){
if (HAVE_DATABASE(backup))
{
elog(LOG, "database files...");
pgBackupGetPath(backup, base_path, lengthof(base_path), DATABASE_DIR);
pgBackupGetPath(backup, path, lengthof(path),
DATABASE_FILE_LIST);
files = dir_read_file_list(base_path, path);
if (!pgBackupValidateFiles(files, base_path, size_only))
corrupted = true;
parray_walk(files, pgFileFree);
parray_free(files);
}
if (HAVE_ARCLOG(backup))
{
elog(LOG, "archive WAL files...");
pgBackupGetPath(backup, base_path, lengthof(base_path), ARCLOG_DIR);
pgBackupGetPath(backup, path, lengthof(path), ARCLOG_FILE_LIST);
files = dir_read_file_list(base_path, path);
if (!pgBackupValidateFiles(files, base_path, size_only))
corrupted = true;
parray_walk(files, pgFileFree);
parray_free(files);
}
if (backup->with_serverlog)
{
elog(LOG, "server log files...");
pgBackupGetPath(backup, base_path, lengthof(base_path), SRVLOG_DIR);
pgBackupGetPath(backup, path, lengthof(path), SRVLOG_FILE_LIST);
files = dir_read_file_list(base_path, path);
if (!pgBackupValidateFiles(files, base_path, size_only))
corrupted = true;
parray_walk(files, pgFileFree);
parray_free(files);
}
if (corrupted)
elog(WARNING, "backup %s is corrupted", timestamp);
else
elog(LOG, "backup %s is valid", timestamp);
/* update status to OK */
if (corrupted)
backup->status = BACKUP_STATUS_CORRUPT;
else
backup->status = BACKUP_STATUS_OK;
pgBackupWriteIni(backup);
if (corrupted)
elog(WARNING, "backup %s is corrupted", timestamp);
else
elog(LOG, "backup %s is valid", timestamp);
}
}
static const char *

View File

@ -2,7 +2,7 @@
*
* verify.c: verify backup files.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/

251
xlog.c
View File

@ -1,124 +1,127 @@
/*-------------------------------------------------------------------------
*
* xlog.c: Parse WAL files.
*
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#if PG_VERSION_NUM >= 80400
typedef unsigned long Datum;
typedef struct MemoryContextData *MemoryContext;
#endif
#include "access/xlog_internal.h"
#define XLOG_PAGE_MAGIC_v80 0xD05C /* 8.0 */
#define XLOG_PAGE_MAGIC_v81 0xD05D /* 8.1 */
#define XLOG_PAGE_MAGIC_v82 0xD05E /* 8.2 */
#define XLOG_PAGE_MAGIC_v83 0xD062 /* 8.3 */
#define XLOG_PAGE_MAGIC_v84 0xD063 /* 8.4 */
#define XLOG_PAGE_MAGIC_v90 0xD064 /* 9.0 */
/*
* XLogLongPageHeaderData is modified in 8.3, but the layout is compatible
* except xlp_xlog_blcksz.
*/
typedef union XLogPage
{
XLogPageHeaderData header;
XLogLongPageHeaderData lheader;
char data[XLOG_BLCKSZ];
} XLogPage;
/*
* Return whether the file is a WAL segment or not.
* based on ValidXLOGHeader() in src/backend/access/transam/xlog.c.
*/
bool
xlog_is_complete_wal(const pgFile *file, int server_version)
{
FILE *fp;
XLogPage page;
uint16 xlog_page_magic;
fp = fopen(file->path, "r");
if (!fp)
return false;
if (fread(&page, 1, sizeof(page), fp) != XLOG_BLCKSZ)
{
fclose(fp);
return false;
}
fclose(fp);
/* xlog_page_magic from server version */
if (server_version < 80000)
return false; /* never happen */
else if (server_version < 80100)
xlog_page_magic = XLOG_PAGE_MAGIC_v80;
else if (server_version < 80200)
xlog_page_magic = XLOG_PAGE_MAGIC_v81;
else if (server_version < 80300)
xlog_page_magic = XLOG_PAGE_MAGIC_v82;
else if (server_version < 80400)
xlog_page_magic = XLOG_PAGE_MAGIC_v83;
else if (server_version < 90000)
xlog_page_magic = XLOG_PAGE_MAGIC_v84;
else if (server_version < 90100)
xlog_page_magic = XLOG_PAGE_MAGIC_v90;
else
return false; /* not supported */
/* check header */
if (page.header.xlp_magic != xlog_page_magic)
return false;
if ((page.header.xlp_info & ~XLP_ALL_FLAGS) != 0)
return false;
if ((page.header.xlp_info & XLP_LONG_HEADER) == 0)
return false;
if (page.lheader.xlp_seg_size != XLogSegSize)
return false;
if (server_version >= 80300 && page.lheader.xlp_xlog_blcksz != XLOG_BLCKSZ)
return false;
/*
* check size (actual file size, not backup file size)
* TODO: Support pre-compressed xlog. They might have different file sizes.
*/
if (file->size != XLogSegSize)
return false;
return true;
}
bool
xlog_logfname2lsn(const char *logfname, XLogRecPtr *lsn)
{
uint32 tli;
if (sscanf(logfname, "%08X%08X%08X",
&tli, &lsn->xlogid, &lsn->xrecoff) != 3)
return false;
lsn->xrecoff *= XLogSegSize;
return true;
}
/*
* based on XLogFileName() in xlog_internal.h
*/
void
xlog_fname(char *fname, size_t len, TimeLineID tli, XLogRecPtr *lsn)
{
snprintf(fname, len, "%08X%08X%08X", tli,
lsn->xlogid, lsn->xrecoff / XLogSegSize);
}
/*-------------------------------------------------------------------------
*
* xlog.c: Parse WAL files.
*
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#if PG_VERSION_NUM >= 80400
typedef unsigned long Datum;
typedef struct MemoryContextData *MemoryContext;
#endif
#include "access/xlog_internal.h"
#define XLOG_PAGE_MAGIC_v80 0xD05C /* 8.0 */
#define XLOG_PAGE_MAGIC_v81 0xD05D /* 8.1 */
#define XLOG_PAGE_MAGIC_v82 0xD05E /* 8.2 */
#define XLOG_PAGE_MAGIC_v83 0xD062 /* 8.3 */
#define XLOG_PAGE_MAGIC_v84 0xD063 /* 8.4 */
#define XLOG_PAGE_MAGIC_v90 0xD064 /* 9.0 */
#define XLOG_PAGE_MAGIC_v91 0xD066 /* 9.1 */
/*
* XLogLongPageHeaderData is modified in 8.3, but the layout is compatible
* except xlp_xlog_blcksz.
*/
typedef union XLogPage
{
XLogPageHeaderData header;
XLogLongPageHeaderData lheader;
char data[XLOG_BLCKSZ];
} XLogPage;
/*
* Return whether the file is a WAL segment or not.
* based on ValidXLOGHeader() in src/backend/access/transam/xlog.c.
*/
bool
xlog_is_complete_wal(const pgFile *file, int server_version)
{
FILE *fp;
XLogPage page;
uint16 xlog_page_magic;
fp = fopen(file->path, "r");
if (!fp)
return false;
if (fread(&page, 1, sizeof(page), fp) != XLOG_BLCKSZ)
{
fclose(fp);
return false;
}
fclose(fp);
/* xlog_page_magic from server version */
if (server_version < 80000)
return false; /* never happen */
else if (server_version < 80100)
xlog_page_magic = XLOG_PAGE_MAGIC_v80;
else if (server_version < 80200)
xlog_page_magic = XLOG_PAGE_MAGIC_v81;
else if (server_version < 80300)
xlog_page_magic = XLOG_PAGE_MAGIC_v82;
else if (server_version < 80400)
xlog_page_magic = XLOG_PAGE_MAGIC_v83;
else if (server_version < 90000)
xlog_page_magic = XLOG_PAGE_MAGIC_v84;
else if (server_version < 90100)
xlog_page_magic = XLOG_PAGE_MAGIC_v90;
else if (server_version < 90200)
xlog_page_magic = XLOG_PAGE_MAGIC_v91;
else
return false; /* not supported */
/* check header */
if (page.header.xlp_magic != xlog_page_magic)
return false;
if ((page.header.xlp_info & ~XLP_ALL_FLAGS) != 0)
return false;
if ((page.header.xlp_info & XLP_LONG_HEADER) == 0)
return false;
if (page.lheader.xlp_seg_size != XLogSegSize)
return false;
if (server_version >= 80300 && page.lheader.xlp_xlog_blcksz != XLOG_BLCKSZ)
return false;
/*
* check size (actual file size, not backup file size)
* TODO: Support pre-compressed xlog. They might have different file sizes.
*/
if (file->size != XLogSegSize)
return false;
return true;
}
bool
xlog_logfname2lsn(const char *logfname, XLogRecPtr *lsn)
{
uint32 tli;
if (sscanf(logfname, "%08X%08X%08X",
&tli, &lsn->xlogid, &lsn->xrecoff) != 3)
return false;
lsn->xrecoff *= XLogSegSize;
return true;
}
/*
* based on XLogFileName() in xlog_internal.h
*/
void
xlog_fname(char *fname, size_t len, TimeLineID tli, XLogRecPtr *lsn)
{
snprintf(fname, len, "%08X%08X%08X", tli,
lsn->xlogid, lsn->xrecoff / XLogSegSize);
}