Add some sanity checks.
if ($test_mode == "local")
{
# Do rewind using a local pgdata as source
+ # Stop the master and be ready to perform the rewind
+ system_or_bail("pg_ctl -w -D $test_standby_datadir stop -m fast >>$log_path 2>&1");
my $result =
run(['./pg_rewind',
"--source-pgdata=$test_standby_datadir",
void
traverse_datadir(const char *datadir, process_file_callback_t callback)
{
- /* should this copy config files or not? */
recurse_dir(datadir, NULL, callback);
}
/*
* Copy a file from source to target, between 'begin' and 'end' offsets.
+ *
+ * If 'trunc' is true, any existing file with the same name is truncated.
*/
static void
copy_file_range(const char *path, off_t begin, off_t end, bool trunc)
pg_fatal("error closing file \"%s\": %s\n", srcpath, strerror(errno));
}
-/*
- * Checks if two file descriptors point to the same file. This is used as
- * a sanity check, to make sure the user doesn't try to copy a data directory
- * over itself.
- */
-void
-check_samefile(int fd1, int fd2)
-{
- struct stat statbuf1,
- statbuf2;
-
- if (fstat(fd1, &statbuf1) < 0)
- pg_fatal("fstat failed: %s\n", strerror(errno));
-
- if (fstat(fd2, &statbuf2) < 0)
- pg_fatal("fstat failed: %s\n", strerror(errno));
-
- if (statbuf1.st_dev == statbuf2.st_dev &&
- statbuf1.st_ino == statbuf2.st_ino)
- {
- pg_fatal("old and new data directory are the same\n");
- }
-}
-
/*
* Copy all relation data files from datadir_source to datadir_target, which
* are marked in the given data page map.
datapagemap_iterator_t *
datapagemap_iterate(datapagemap_t *map)
{
- datapagemap_iterator_t *iter = pg_malloc(sizeof(datapagemap_iterator_t));
+ datapagemap_iterator_t *iter;
+
+ iter = pg_malloc(sizeof(datapagemap_iterator_t));
iter->map = map;
iter->nextblkno = 0;
+
return iter;
}
void
datapagemap_print(datapagemap_t *map)
{
- datapagemap_iterator_t *iter = datapagemap_iterate(map);
+ datapagemap_iterator_t *iter;
BlockNumber blocknum;
+ iter = datapagemap_iterate(map);
while (datapagemap_next(iter, &blocknum))
- {
printf(" blk %u\n", blocknum);
- }
+
free(iter);
}
extern void truncate_target_file(const char *path, off_t newsize);
extern void create_target(file_entry_t *t);
extern void remove_target(file_entry_t *t);
-extern void check_samefile(int fd1, int fd2);
#endif /* FETCH_H */
static int final_filemap_cmp(const void *a, const void *b);
static void filemap_list_to_array(void);
-
-/*****
- * Public functions
- */
-
/*
* Create a new file map.
*/
filemap_t *
filemap_create(void)
{
- filemap_t *map = pg_malloc(sizeof(filemap_t));
+ filemap_t *map;
+
+ map = pg_malloc(sizeof(filemap_t));
map->first = map->last = NULL;
map->nlist = 0;
map->array = NULL;
/*
* Callback for processing remote file list.
+ *
+ * This is called once for every file in the source server. We decide what
+ * action needs to be taken for the file, depending on whether the file
+ * exists in the target and whether the size matches.
*/
void
process_remote_file(const char *path, file_type_t type, size_t newsize,
* regular file
*/
if (type != FILE_TYPE_REGULAR && isRelDataFile(path))
- pg_fatal("data file in source \"%s\" is a directory\n", path);
+ pg_fatal("data file in source \"%s\" is not a regular file\n", path);
snprintf(localpath, sizeof(localpath), "%s/%s", datadir_target, path);
/* Does the corresponding local file exist? */
if (lstat(localpath, &statbuf) < 0)
{
- /* does not exist */
if (errno != ENOENT)
pg_fatal("could not stat file \"%s\": %s",
localpath, strerror(errno));
/*
* Callback for processing local file list.
*
- * All remote files must be processed before calling this. This only marks
- * local files that don't exist in the remote system for deletion.
+ * All remote files must be already processed before calling this. This only
+ * marks local files that didn't exist in the remote system for deletion.
*/
void
process_local_file(const char *path, file_type_t type, size_t oldsize,
{
file_entry_t *fa = *((file_entry_t **) a);
file_entry_t *fb = *((file_entry_t **) b);
+
return strcmp(fa->path, fb->path);
}
static void receiveFileChunks(const char *sql);
static void execute_pagemap(datapagemap_t *pagemap, const char *path);
+static char *run_simple_query(const char *sql);
void
libpqConnect(const char *connstr)
{
+ char *str;
+
conn = PQconnectdb(connstr);
if (PQstatus(conn) == CONNECTION_BAD)
pg_fatal("could not connect to remote server: %s\n",
PQerrorMessage(conn));
pg_log(PG_VERBOSE, "connected to remote server\n");
+
+ /*
+ * Check that the server is not in hot standby mode. There is no
+ * fundamental reason that couldn't be made to work, but it doesn't
+ * currently because we use a temporary table. Better to check for it
+ * explicitly than error out, for a better error message.
+ */
+ str = run_simple_query("SELECT pg_is_in_recovery()");
+ if (strcmp(str, "f") != 0)
+ pg_fatal("source server must not be in recovery mode\n");
+ pg_free(str);
+
+ /*
+ * Also check that full_page-writes are enabled. We can get torn pages
+ * if a page is modified while we read it with pg_read_binary_file(), and
+ * we rely on full page images to fix them.
+ */
+ str = run_simple_query("SHOW full_page_writes");
+ if (strcmp(str, "on") != 0)
+ pg_fatal("full_page_writes must be enabled in the source server\n");
+ pg_free(str);
}
/*
* Runs a query that returns a single value.
*/
static char *
-libpqRunSimpleQuery(const char *sql)
+run_simple_query(const char *sql)
{
PGresult *res;
char *result;
uint32 lo;
char *val;
- val = libpqRunSimpleQuery("SELECT pg_current_xlog_insert_location()");
+ val = run_simple_query("SELECT pg_current_xlog_insert_location()");
if (sscanf(val, "%X/%X", &hi, &lo) != 2)
pg_fatal("unexpected result \"%s\" while fetching current XLOG insert location\n", val);
if (ControlFile_target.data_checksum_version != PG_DATA_CHECKSUM_VERSION &&
!ControlFile_target.wal_log_hints)
{
- pg_fatal("target master need to use either data checksums or \"wal_log_hints = on\"\n");
+ pg_fatal("target server need to use either data checksums or \"wal_log_hints = on\"\n");
}
/*
* long as it isn't running at the moment.
*/
if (ControlFile_target.state != DB_SHUTDOWNED)
- pg_fatal("target master must be shut down cleanly\n");
+ pg_fatal("target server must be shut down cleanly\n");
+
+ /*
+ * When the source is a data directory, also require that the source server
+ * is shut down. There isn't any very strong reason for this limitation
+ */
+ if (datadir_source && ControlFile_source.state != DB_SHUTDOWNED)
+ pg_fatal("source data directory must be shut down cleanly\n");
}
/*