You've already forked pg_probackup
							
							
				mirror of
				https://github.com/postgrespro/pg_probackup.git
				synced 2025-10-31 00:17:52 +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:
		| @@ -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); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user