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:
parent
ae6c0e0c55
commit
78eed96f63
@ -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
|
||||
|
2
Makefile
2
Makefile
@ -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
|
||||
|
@ -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
|
||||
|
92
backup.c
92
backup.c
@ -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(¤t))
|
||||
return NULL;
|
||||
if (!HAVE_DATABASE(¤t)) {
|
||||
/* check if arclog backup. if arclog backup and no suitable full backup, */
|
||||
/* take full backup instead. */
|
||||
if (HAVE_ARCLOG(¤t)) {
|
||||
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, ¤t);
|
||||
|
||||
/* 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);
|
||||
|
18
catalog.c
18
catalog.c
@ -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;
|
||||
|
2
clean.c
2
clean.c
@ -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
18
data.c
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
2
db.c
@ -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
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
38
delete.c
38
delete.c
@ -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
11
dir.c
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
2
file.c
@ -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
27
init.c
@ -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)
|
||||
|
2
parray.c
2
parray.c
@ -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
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
2
parray.h
2
parray.h
@ -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
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
2
pg_ctl.c
2
pg_ctl.c
@ -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
630
pg_rman.c
@ -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" , ¤t.with_serverlog , SOURCE_ENV },
|
||||
{ 'b', 'Z', "compress-data" , ¤t.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(¤t);
|
||||
|
||||
/* 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" , ¤t.with_serverlog , SOURCE_ENV },
|
||||
{ 'b', 'Z', "compress-data" , ¤t.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(¤t);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
34
pg_rman.h
34
pg_rman.h
@ -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);
|
||||
|
1030
pgsql_src/pg_crc.c
1030
pgsql_src/pg_crc.c
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
2
queue.c
2
queue.c
@ -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
112
restore.c
@ -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
7
show.c
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 $?
|
||||
|
160
sql/option.sh
160
sql/option.sh
@ -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
|
||||
|
@ -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
2
util.c
@ -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
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
2
utils.c
2
utils.c
@ -2,7 +2,7 @@
|
||||
*
|
||||
* utils.c:
|
||||
*
|
||||
* Copyright (c) 2009-2010, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
* Copyright (c) 2009-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
123
validate.c
123
validate.c
@ -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 *
|
||||
|
2
verify.c
2
verify.c
@ -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
251
xlog.c
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user