Misc cleanup.
authorHeikki Linnakangas <[email protected]>
Fri, 9 Jan 2015 13:09:50 +0000 (15:09 +0200)
committerHeikki Linnakangas <[email protected]>
Fri, 16 Jan 2015 11:37:07 +0000 (13:37 +0200)
Add some sanity checks.

contrib/pg_rewind/RewindTest.pm
contrib/pg_rewind/copy_fetch.c
contrib/pg_rewind/datapagemap.c
contrib/pg_rewind/fetch.h
contrib/pg_rewind/filemap.c
contrib/pg_rewind/libpq_fetch.c
contrib/pg_rewind/pg_rewind.c

index 2578464b972c3c1c97c453553bb9550a6126673b..9f393d7085fc08502d781cca66d32b845f31a31c 100644 (file)
@@ -179,6 +179,8 @@ recovery_target_timeline='latest'
        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",
index 392aef973ef81186a054d33057f4589f2ade36d1..d3226a4d48240e1df12b698af5b6c9f1bb37d61c 100644 (file)
@@ -48,7 +48,6 @@ static void remove_target_symlink(const char *path);
 void
 traverse_datadir(const char *datadir, process_file_callback_t callback)
 {
-       /* should this copy config files or not? */
        recurse_dir(datadir, NULL, callback);
 }
 
@@ -229,6 +228,8 @@ write_file_range(char *buf, off_t begin, size_t size)
 
 /*
  * 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)
@@ -275,30 +276,6 @@ 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.
index 6d76b3ad7d463e980bfd93520c2790b49b28202b..f89a3545a369402c226afb4ea5aa75ba88ac24d1 100644 (file)
@@ -76,9 +76,12 @@ datapagemap_add(datapagemap_t *map, BlockNumber blkno)
 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;
 }
 
@@ -115,12 +118,12 @@ datapagemap_next(datapagemap_iterator_t *iter, BlockNumber *blkno)
 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);
 }
index 9ad0c5889b7790c5e5767c16f1602de010dd31b9..101dc73a74aa53334771bdc3c50556f0e257c18f 100644 (file)
@@ -51,7 +51,6 @@ extern void traverse_datadir(const char *datadir, process_file_callback_t callba
 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 */
index b79e8d6644f386632595d7c50231d928c39fcea4..90245a33627aeff4f029b7db492954b0b3fe76a3 100644 (file)
@@ -32,18 +32,15 @@ static int path_cmp(const void *a, const void *b);
 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;
@@ -57,6 +54,10 @@ filemap_create(void)
 
 /*
  * 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,
@@ -94,14 +95,13 @@ 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));
@@ -234,8 +234,8 @@ process_remote_file(const char *path, file_type_t type, size_t newsize,
 /*
  * 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,
@@ -578,6 +578,7 @@ path_cmp(const void *a, const void *b)
 {
        file_entry_t *fa = *((file_entry_t **) a);
        file_entry_t *fb = *((file_entry_t **) b);
+
        return strcmp(fa->path, fb->path);
 }
 
index bf91306e43af1745a96091ffaae76efcf199a61e..d509e53d0ea569c4410947fa4431194e7fcba1a4 100644 (file)
@@ -38,23 +38,47 @@ static PGconn *conn = NULL;
 
 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;
@@ -87,7 +111,7 @@ libpqGetCurrentXlogInsertLocation(void)
        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);
index ff7ea2133fe3d9fc125bacf4b77cdc21fcd72563..9078cf09ba16f3e843daad6827665ca41ffc1dc3 100644 (file)
@@ -325,7 +325,7 @@ sanityChecks(void)
        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");
        }
 
        /*
@@ -335,7 +335,14 @@ sanityChecks(void)
         * 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");
 }
 
 /*