summaryrefslogtreecommitdiffstats
path: root/src/bin/pg_rewind
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/bin/pg_rewind/.gitignore6
-rw-r--r--src/bin/pg_rewind/Makefile60
-rw-r--r--src/bin/pg_rewind/datapagemap.c127
-rw-r--r--src/bin/pg_rewind/datapagemap.h29
-rw-r--r--src/bin/pg_rewind/file_ops.c477
-rw-r--r--src/bin/pg_rewind/file_ops.h29
-rw-r--r--src/bin/pg_rewind/filemap.c832
-rw-r--r--src/bin/pg_rewind/filemap.h113
-rw-r--r--src/bin/pg_rewind/libpq_source.c643
-rw-r--r--src/bin/pg_rewind/local_source.c131
-rw-r--r--src/bin/pg_rewind/nls.mk8
-rw-r--r--src/bin/pg_rewind/parsexlog.c452
-rw-r--r--src/bin/pg_rewind/pg_rewind.c1142
-rw-r--r--src/bin/pg_rewind/pg_rewind.h59
-rw-r--r--src/bin/pg_rewind/po/cs.po1019
-rw-r--r--src/bin/pg_rewind/po/de.po964
-rw-r--r--src/bin/pg_rewind/po/el.po959
-rw-r--r--src/bin/pg_rewind/po/es.po967
-rw-r--r--src/bin/pg_rewind/po/fr.po1222
-rw-r--r--src/bin/pg_rewind/po/ja.po964
-rw-r--r--src/bin/pg_rewind/po/ru.po1146
-rw-r--r--src/bin/pg_rewind/po/sv.po964
-rw-r--r--src/bin/pg_rewind/po/uk.po943
-rw-r--r--src/bin/pg_rewind/po/zh_CN.po959
-rw-r--r--src/bin/pg_rewind/rewind_source.h73
-rw-r--r--src/bin/pg_rewind/t/001_basic.pl194
-rw-r--r--src/bin/pg_rewind/t/002_databases.pl77
-rw-r--r--src/bin/pg_rewind/t/003_extrafiles.pl106
-rw-r--r--src/bin/pg_rewind/t/004_pg_xlog_symlink.pl79
-rw-r--r--src/bin/pg_rewind/t/005_same_timeline.pl23
-rw-r--r--src/bin/pg_rewind/t/006_options.pl43
-rw-r--r--src/bin/pg_rewind/t/007_standby_source.pl181
-rw-r--r--src/bin/pg_rewind/t/008_min_recovery_point.pl177
-rw-r--r--src/bin/pg_rewind/t/RewindTest.pm396
-rw-r--r--src/bin/pg_rewind/timeline.c130
35 files changed, 15694 insertions, 0 deletions
diff --git a/src/bin/pg_rewind/.gitignore b/src/bin/pg_rewind/.gitignore
new file mode 100644
index 0000000..79ddca3
--- /dev/null
+++ b/src/bin/pg_rewind/.gitignore
@@ -0,0 +1,6 @@
+# Files generated during build
+/xlogreader.c
+/pg_rewind
+
+# Generated by test suite
+/tmp_check/
diff --git a/src/bin/pg_rewind/Makefile b/src/bin/pg_rewind/Makefile
new file mode 100644
index 0000000..5514b95
--- /dev/null
+++ b/src/bin/pg_rewind/Makefile
@@ -0,0 +1,60 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/bin/pg_rewind
+#
+# Portions Copyright (c) 2013-2021, PostgreSQL Global Development Group
+#
+# src/bin/pg_rewind/Makefile
+#
+#-------------------------------------------------------------------------
+
+PGFILEDESC = "pg_rewind - synchronize a data directory with another one forked from"
+PGAPPICON = win32
+
+subdir = src/bin/pg_rewind
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+override CPPFLAGS := -I$(libpq_srcdir) -DFRONTEND $(CPPFLAGS)
+LDFLAGS_INTERNAL += -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport)
+
+OBJS = \
+ $(WIN32RES) \
+ datapagemap.o \
+ file_ops.o \
+ filemap.o \
+ libpq_source.o \
+ local_source.o \
+ parsexlog.o \
+ pg_rewind.o \
+ timeline.o \
+ xlogreader.o
+
+EXTRA_CLEAN = xlogreader.c
+
+all: pg_rewind
+
+pg_rewind: $(OBJS) | submake-libpq submake-libpgport
+ $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
+
+xlogreader.c: % : $(top_srcdir)/src/backend/access/transam/%
+ rm -f $@ && $(LN_S) $< .
+
+install: all installdirs
+ $(INSTALL_PROGRAM) pg_rewind$(X) '$(DESTDIR)$(bindir)/pg_rewind$(X)'
+
+installdirs:
+ $(MKDIR_P) '$(DESTDIR)$(bindir)'
+
+uninstall:
+ rm -f '$(DESTDIR)$(bindir)/pg_rewind$(X)'
+
+clean distclean maintainer-clean:
+ rm -f pg_rewind$(X) $(OBJS) xlogreader.c
+ rm -rf tmp_check
+
+check:
+ $(prove_check)
+
+installcheck:
+ $(prove_installcheck)
diff --git a/src/bin/pg_rewind/datapagemap.c b/src/bin/pg_rewind/datapagemap.c
new file mode 100644
index 0000000..3f8952b
--- /dev/null
+++ b/src/bin/pg_rewind/datapagemap.c
@@ -0,0 +1,127 @@
+/*-------------------------------------------------------------------------
+ *
+ * datapagemap.c
+ * A data structure for keeping track of data pages that have changed.
+ *
+ * This is a fairly simple bitmap.
+ *
+ * Copyright (c) 2013-2021, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include "common/logging.h"
+#include "datapagemap.h"
+
+struct datapagemap_iterator
+{
+ datapagemap_t *map;
+ BlockNumber nextblkno;
+};
+
+/*****
+ * Public functions
+ */
+
+/*
+ * Add a block to the bitmap.
+ */
+void
+datapagemap_add(datapagemap_t *map, BlockNumber blkno)
+{
+ int offset;
+ int bitno;
+
+ offset = blkno / 8;
+ bitno = blkno % 8;
+
+ /* enlarge or create bitmap if needed */
+ if (map->bitmapsize <= offset)
+ {
+ int oldsize = map->bitmapsize;
+ int newsize;
+
+ /*
+ * The minimum to hold the new bit is offset + 1. But add some
+ * headroom, so that we don't need to repeatedly enlarge the bitmap in
+ * the common case that blocks are modified in order, from beginning
+ * of a relation to the end.
+ */
+ newsize = offset + 1;
+ newsize += 10;
+
+ map->bitmap = pg_realloc(map->bitmap, newsize);
+
+ /* zero out the newly allocated region */
+ memset(&map->bitmap[oldsize], 0, newsize - oldsize);
+
+ map->bitmapsize = newsize;
+ }
+
+ /* Set the bit */
+ map->bitmap[offset] |= (1 << bitno);
+}
+
+/*
+ * Start iterating through all entries in the page map.
+ *
+ * After datapagemap_iterate, call datapagemap_next to return the entries,
+ * until it returns false. After you're done, use pg_free() to destroy the
+ * iterator.
+ */
+datapagemap_iterator_t *
+datapagemap_iterate(datapagemap_t *map)
+{
+ datapagemap_iterator_t *iter;
+
+ iter = pg_malloc(sizeof(datapagemap_iterator_t));
+ iter->map = map;
+ iter->nextblkno = 0;
+
+ return iter;
+}
+
+bool
+datapagemap_next(datapagemap_iterator_t *iter, BlockNumber *blkno)
+{
+ datapagemap_t *map = iter->map;
+
+ for (;;)
+ {
+ BlockNumber blk = iter->nextblkno;
+ int nextoff = blk / 8;
+ int bitno = blk % 8;
+
+ if (nextoff >= map->bitmapsize)
+ break;
+
+ iter->nextblkno++;
+
+ if (map->bitmap[nextoff] & (1 << bitno))
+ {
+ *blkno = blk;
+ return true;
+ }
+ }
+
+ /* no more set bits in this bitmap. */
+ return false;
+}
+
+/*
+ * A debugging aid. Prints out the contents of the page map.
+ */
+void
+datapagemap_print(datapagemap_t *map)
+{
+ datapagemap_iterator_t *iter;
+ BlockNumber blocknum;
+
+ iter = datapagemap_iterate(map);
+ while (datapagemap_next(iter, &blocknum))
+ pg_log_debug("block %u", blocknum);
+
+ pg_free(iter);
+}
diff --git a/src/bin/pg_rewind/datapagemap.h b/src/bin/pg_rewind/datapagemap.h
new file mode 100644
index 0000000..76e9f20
--- /dev/null
+++ b/src/bin/pg_rewind/datapagemap.h
@@ -0,0 +1,29 @@
+/*-------------------------------------------------------------------------
+ *
+ * datapagemap.h
+ *
+ * Copyright (c) 2013-2021, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef DATAPAGEMAP_H
+#define DATAPAGEMAP_H
+
+#include "storage/block.h"
+#include "storage/relfilenode.h"
+
+struct datapagemap
+{
+ char *bitmap;
+ int bitmapsize;
+};
+
+typedef struct datapagemap datapagemap_t;
+typedef struct datapagemap_iterator datapagemap_iterator_t;
+
+extern void datapagemap_add(datapagemap_t *map, BlockNumber blkno);
+extern datapagemap_iterator_t *datapagemap_iterate(datapagemap_t *map);
+extern bool datapagemap_next(datapagemap_iterator_t *iter, BlockNumber *blkno);
+extern void datapagemap_print(datapagemap_t *map);
+
+#endif /* DATAPAGEMAP_H */
diff --git a/src/bin/pg_rewind/file_ops.c b/src/bin/pg_rewind/file_ops.c
new file mode 100644
index 0000000..c50f283
--- /dev/null
+++ b/src/bin/pg_rewind/file_ops.c
@@ -0,0 +1,477 @@
+/*-------------------------------------------------------------------------
+ *
+ * file_ops.c
+ * Helper functions for operating on files.
+ *
+ * Most of the functions in this file are helper functions for writing to
+ * the target data directory. The functions check the --dry-run flag, and
+ * do nothing if it's enabled. You should avoid accessing the target files
+ * directly but if you do, make sure you honor the --dry-run mode!
+ *
+ * Portions Copyright (c) 2013-2021, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "common/file_perm.h"
+#include "common/file_utils.h"
+#include "file_ops.h"
+#include "filemap.h"
+#include "pg_rewind.h"
+
+/*
+ * Currently open target file.
+ */
+static int dstfd = -1;
+static char dstpath[MAXPGPATH] = "";
+
+static void create_target_dir(const char *path);
+static void remove_target_dir(const char *path);
+static void create_target_symlink(const char *path, const char *link);
+static void remove_target_symlink(const char *path);
+
+static void recurse_dir(const char *datadir, const char *parentpath,
+ process_file_callback_t callback);
+
+/*
+ * Open a target file for writing. If 'trunc' is true and the file already
+ * exists, it will be truncated.
+ */
+void
+open_target_file(const char *path, bool trunc)
+{
+ int mode;
+
+ if (dry_run)
+ return;
+
+ if (dstfd != -1 && !trunc &&
+ strcmp(path, &dstpath[strlen(datadir_target) + 1]) == 0)
+ return; /* already open */
+
+ close_target_file();
+
+ snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
+
+ mode = O_WRONLY | O_CREAT | PG_BINARY;
+ if (trunc)
+ mode |= O_TRUNC;
+ dstfd = open(dstpath, mode, pg_file_create_mode);
+ if (dstfd < 0)
+ pg_fatal("could not open target file \"%s\": %m",
+ dstpath);
+}
+
+/*
+ * Close target file, if it's open.
+ */
+void
+close_target_file(void)
+{
+ if (dstfd == -1)
+ return;
+
+ if (close(dstfd) != 0)
+ pg_fatal("could not close target file \"%s\": %m",
+ dstpath);
+
+ dstfd = -1;
+}
+
+void
+write_target_range(char *buf, off_t begin, size_t size)
+{
+ size_t writeleft;
+ char *p;
+
+ /* update progress report */
+ fetch_done += size;
+ progress_report(false);
+
+ if (dry_run)
+ return;
+
+ if (lseek(dstfd, begin, SEEK_SET) == -1)
+ pg_fatal("could not seek in target file \"%s\": %m",
+ dstpath);
+
+ writeleft = size;
+ p = buf;
+ while (writeleft > 0)
+ {
+ ssize_t writelen;
+
+ errno = 0;
+ writelen = write(dstfd, p, writeleft);
+ if (writelen < 0)
+ {
+ /* if write didn't set errno, assume problem is no disk space */
+ if (errno == 0)
+ errno = ENOSPC;
+ pg_fatal("could not write file \"%s\": %m",
+ dstpath);
+ }
+
+ p += writelen;
+ writeleft -= writelen;
+ }
+
+ /* keep the file open, in case we need to copy more blocks in it */
+}
+
+
+void
+remove_target(file_entry_t *entry)
+{
+ Assert(entry->action == FILE_ACTION_REMOVE);
+ Assert(entry->target_exists);
+
+ switch (entry->target_type)
+ {
+ case FILE_TYPE_DIRECTORY:
+ remove_target_dir(entry->path);
+ break;
+
+ case FILE_TYPE_REGULAR:
+ remove_target_file(entry->path, false);
+ break;
+
+ case FILE_TYPE_SYMLINK:
+ remove_target_symlink(entry->path);
+ break;
+
+ case FILE_TYPE_UNDEFINED:
+ pg_fatal("undefined file type for \"%s\"", entry->path);
+ break;
+ }
+}
+
+void
+create_target(file_entry_t *entry)
+{
+ Assert(entry->action == FILE_ACTION_CREATE);
+ Assert(!entry->target_exists);
+
+ switch (entry->source_type)
+ {
+ case FILE_TYPE_DIRECTORY:
+ create_target_dir(entry->path);
+ break;
+
+ case FILE_TYPE_SYMLINK:
+ create_target_symlink(entry->path, entry->source_link_target);
+ break;
+
+ case FILE_TYPE_REGULAR:
+ /* can't happen. Regular files are created with open_target_file. */
+ pg_fatal("invalid action (CREATE) for regular file");
+ break;
+
+ case FILE_TYPE_UNDEFINED:
+ pg_fatal("undefined file type for \"%s\"", entry->path);
+ break;
+ }
+}
+
+/*
+ * Remove a file from target data directory. If missing_ok is true, it
+ * is fine for the target file to not exist.
+ */
+void
+remove_target_file(const char *path, bool missing_ok)
+{
+ char dstpath[MAXPGPATH];
+
+ if (dry_run)
+ return;
+
+ snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
+ if (unlink(dstpath) != 0)
+ {
+ if (errno == ENOENT && missing_ok)
+ return;
+
+ pg_fatal("could not remove file \"%s\": %m",
+ dstpath);
+ }
+}
+
+void
+truncate_target_file(const char *path, off_t newsize)
+{
+ char dstpath[MAXPGPATH];
+ int fd;
+
+ if (dry_run)
+ return;
+
+ snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
+
+ fd = open(dstpath, O_WRONLY, pg_file_create_mode);
+ if (fd < 0)
+ pg_fatal("could not open file \"%s\" for truncation: %m",
+ dstpath);
+
+ if (ftruncate(fd, newsize) != 0)
+ pg_fatal("could not truncate file \"%s\" to %u: %m",
+ dstpath, (unsigned int) newsize);
+
+ close(fd);
+}
+
+static void
+create_target_dir(const char *path)
+{
+ char dstpath[MAXPGPATH];
+
+ if (dry_run)
+ return;
+
+ snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
+ if (mkdir(dstpath, pg_dir_create_mode) != 0)
+ pg_fatal("could not create directory \"%s\": %m",
+ dstpath);
+}
+
+static void
+remove_target_dir(const char *path)
+{
+ char dstpath[MAXPGPATH];
+
+ if (dry_run)
+ return;
+
+ snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
+ if (rmdir(dstpath) != 0)
+ pg_fatal("could not remove directory \"%s\": %m",
+ dstpath);
+}
+
+static void
+create_target_symlink(const char *path, const char *link)
+{
+ char dstpath[MAXPGPATH];
+
+ if (dry_run)
+ return;
+
+ snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
+ if (symlink(link, dstpath) != 0)
+ pg_fatal("could not create symbolic link at \"%s\": %m",
+ dstpath);
+}
+
+static void
+remove_target_symlink(const char *path)
+{
+ char dstpath[MAXPGPATH];
+
+ if (dry_run)
+ return;
+
+ snprintf(dstpath, sizeof(dstpath), "%s/%s", datadir_target, path);
+ if (unlink(dstpath) != 0)
+ pg_fatal("could not remove symbolic link \"%s\": %m",
+ dstpath);
+}
+
+/*
+ * Sync target data directory to ensure that modifications are safely on disk.
+ *
+ * We do this once, for the whole data directory, for performance reasons. At
+ * the end of pg_rewind's run, the kernel is likely to already have flushed
+ * most dirty buffers to disk. Additionally fsync_pgdata uses a two-pass
+ * approach (only initiating writeback in the first pass), which often reduces
+ * the overall amount of IO noticeably.
+ */
+void
+sync_target_dir(void)
+{
+ if (!do_sync || dry_run)
+ return;
+
+ fsync_pgdata(datadir_target, PG_VERSION_NUM);
+}
+
+
+/*
+ * Read a file into memory. The file to be read is <datadir>/<path>.
+ * The file contents are returned in a malloc'd buffer, and *filesize
+ * is set to the length of the file.
+ *
+ * The returned buffer is always zero-terminated; the size of the returned
+ * buffer is actually *filesize + 1. That's handy when reading a text file.
+ * This function can be used to read binary files as well, you can just
+ * ignore the zero-terminator in that case.
+ */
+char *
+slurpFile(const char *datadir, const char *path, size_t *filesize)
+{
+ int fd;
+ char *buffer;
+ struct stat statbuf;
+ char fullpath[MAXPGPATH];
+ int len;
+ int r;
+
+ snprintf(fullpath, sizeof(fullpath), "%s/%s", datadir, path);
+
+ if ((fd = open(fullpath, O_RDONLY | PG_BINARY, 0)) == -1)
+ pg_fatal("could not open file \"%s\" for reading: %m",
+ fullpath);
+
+ if (fstat(fd, &statbuf) < 0)
+ pg_fatal("could not open file \"%s\" for reading: %m",
+ fullpath);
+
+ len = statbuf.st_size;
+
+ buffer = pg_malloc(len + 1);
+
+ r = read(fd, buffer, len);
+ if (r != len)
+ {
+ if (r < 0)
+ pg_fatal("could not read file \"%s\": %m",
+ fullpath);
+ else
+ pg_fatal("could not read file \"%s\": read %d of %zu",
+ fullpath, r, (Size) len);
+ }
+ close(fd);
+
+ /* Zero-terminate the buffer. */
+ buffer[len] = '\0';
+
+ if (filesize)
+ *filesize = len;
+ return buffer;
+}
+
+/*
+ * Traverse through all files in a data directory, calling 'callback'
+ * for each file.
+ */
+void
+traverse_datadir(const char *datadir, process_file_callback_t callback)
+{
+ recurse_dir(datadir, NULL, callback);
+}
+
+/*
+ * recursive part of traverse_datadir
+ *
+ * parentpath is the current subdirectory's path relative to datadir,
+ * or NULL at the top level.
+ */
+static void
+recurse_dir(const char *datadir, const char *parentpath,
+ process_file_callback_t callback)
+{
+ DIR *xldir;
+ struct dirent *xlde;
+ char fullparentpath[MAXPGPATH];
+
+ if (parentpath)
+ snprintf(fullparentpath, MAXPGPATH, "%s/%s", datadir, parentpath);
+ else
+ snprintf(fullparentpath, MAXPGPATH, "%s", datadir);
+
+ xldir = opendir(fullparentpath);
+ if (xldir == NULL)
+ pg_fatal("could not open directory \"%s\": %m",
+ fullparentpath);
+
+ while (errno = 0, (xlde = readdir(xldir)) != NULL)
+ {
+ struct stat fst;
+ char fullpath[MAXPGPATH * 2];
+ char path[MAXPGPATH * 2];
+
+ if (strcmp(xlde->d_name, ".") == 0 ||
+ strcmp(xlde->d_name, "..") == 0)
+ continue;
+
+ snprintf(fullpath, sizeof(fullpath), "%s/%s", fullparentpath, xlde->d_name);
+
+ if (lstat(fullpath, &fst) < 0)
+ {
+ if (errno == ENOENT)
+ {
+ /*
+ * File doesn't exist anymore. This is ok, if the new primary
+ * is running and the file was just removed. If it was a data
+ * file, there should be a WAL record of the removal. If it
+ * was something else, it couldn't have been anyway.
+ *
+ * TODO: But complain if we're processing the target dir!
+ */
+ }
+ else
+ pg_fatal("could not stat file \"%s\": %m",
+ fullpath);
+ }
+
+ if (parentpath)
+ snprintf(path, sizeof(path), "%s/%s", parentpath, xlde->d_name);
+ else
+ snprintf(path, sizeof(path), "%s", xlde->d_name);
+
+ if (S_ISREG(fst.st_mode))
+ callback(path, FILE_TYPE_REGULAR, fst.st_size, NULL);
+ else if (S_ISDIR(fst.st_mode))
+ {
+ callback(path, FILE_TYPE_DIRECTORY, 0, NULL);
+ /* recurse to handle subdirectories */
+ recurse_dir(datadir, path, callback);
+ }
+#ifndef WIN32
+ else if (S_ISLNK(fst.st_mode))
+#else
+ else if (pgwin32_is_junction(fullpath))
+#endif
+ {
+#if defined(HAVE_READLINK) || defined(WIN32)
+ char link_target[MAXPGPATH];
+ int len;
+
+ len = readlink(fullpath, link_target, sizeof(link_target));
+ if (len < 0)
+ pg_fatal("could not read symbolic link \"%s\": %m",
+ fullpath);
+ if (len >= sizeof(link_target))
+ pg_fatal("symbolic link \"%s\" target is too long",
+ fullpath);
+ link_target[len] = '\0';
+
+ callback(path, FILE_TYPE_SYMLINK, 0, link_target);
+
+ /*
+ * If it's a symlink within pg_tblspc, we need to recurse into it,
+ * to process all the tablespaces. We also follow a symlink if
+ * it's for pg_wal. Symlinks elsewhere are ignored.
+ */
+ if ((parentpath && strcmp(parentpath, "pg_tblspc") == 0) ||
+ strcmp(path, "pg_wal") == 0)
+ recurse_dir(datadir, path, callback);
+#else
+ pg_fatal("\"%s\" is a symbolic link, but symbolic links are not supported on this platform",
+ fullpath);
+#endif /* HAVE_READLINK */
+ }
+ }
+
+ if (errno)
+ pg_fatal("could not read directory \"%s\": %m",
+ fullparentpath);
+
+ if (closedir(xldir))
+ pg_fatal("could not close directory \"%s\": %m",
+ fullparentpath);
+}
diff --git a/src/bin/pg_rewind/file_ops.h b/src/bin/pg_rewind/file_ops.h
new file mode 100644
index 0000000..611981f
--- /dev/null
+++ b/src/bin/pg_rewind/file_ops.h
@@ -0,0 +1,29 @@
+/*-------------------------------------------------------------------------
+ *
+ * file_ops.h
+ * Helper functions for operating on files
+ *
+ * Copyright (c) 2013-2021, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FILE_OPS_H
+#define FILE_OPS_H
+
+#include "filemap.h"
+
+extern void open_target_file(const char *path, bool trunc);
+extern void write_target_range(char *buf, off_t begin, size_t size);
+extern void close_target_file(void);
+extern void remove_target_file(const char *path, bool missing_ok);
+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 sync_target_dir(void);
+
+extern char *slurpFile(const char *datadir, const char *path, size_t *filesize);
+
+typedef void (*process_file_callback_t) (const char *path, file_type_t type, size_t size, const char *link_target);
+extern void traverse_datadir(const char *datadir, process_file_callback_t callback);
+
+#endif /* FILE_OPS_H */
diff --git a/src/bin/pg_rewind/filemap.c b/src/bin/pg_rewind/filemap.c
new file mode 100644
index 0000000..2618b4c
--- /dev/null
+++ b/src/bin/pg_rewind/filemap.c
@@ -0,0 +1,832 @@
+/*-------------------------------------------------------------------------
+ *
+ * filemap.c
+ * A data structure for keeping track of files that have changed.
+ *
+ * This source file contains the logic to decide what to do with different
+ * kinds of files, and the data structure to support it. Before modifying
+ * anything, pg_rewind collects information about all the files and their
+ * attributes in the target and source data directories. It also scans the
+ * WAL log in the target, and collects information about data blocks that
+ * were changed. All this information is stored in a hash table, using the
+ * file path relative to the root of the data directory as the key.
+ *
+ * After collecting all the information required, the decide_file_actions()
+ * function scans the hash table and decides what action needs to be taken
+ * for each file. Finally, it sorts the array to the final order that the
+ * actions should be executed in.
+ *
+ * Copyright (c) 2013-2021, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "catalog/pg_tablespace_d.h"
+#include "common/hashfn.h"
+#include "common/string.h"
+#include "datapagemap.h"
+#include "filemap.h"
+#include "pg_rewind.h"
+#include "storage/fd.h"
+
+/*
+ * Define a hash table which we can use to store information about the files
+ * appearing in source and target systems.
+ */
+static uint32 hash_string_pointer(const char *s);
+#define SH_PREFIX filehash
+#define SH_ELEMENT_TYPE file_entry_t
+#define SH_KEY_TYPE const char *
+#define SH_KEY path
+#define SH_HASH_KEY(tb, key) hash_string_pointer(key)
+#define SH_EQUAL(tb, a, b) (strcmp(a, b) == 0)
+#define SH_SCOPE static inline
+#define SH_RAW_ALLOCATOR pg_malloc0
+#define SH_DECLARE
+#define SH_DEFINE
+#include "lib/simplehash.h"
+
+#define FILEHASH_INITIAL_SIZE 1000
+
+static filehash_hash *filehash;
+
+static bool isRelDataFile(const char *path);
+static char *datasegpath(RelFileNode rnode, ForkNumber forknum,
+ BlockNumber segno);
+
+static file_entry_t *insert_filehash_entry(const char *path);
+static file_entry_t *lookup_filehash_entry(const char *path);
+static int final_filemap_cmp(const void *a, const void *b);
+static bool check_file_excluded(const char *path, bool is_source);
+
+/*
+ * Definition of one element part of an exclusion list, used to exclude
+ * contents when rewinding. "name" is the name of the file or path to
+ * check for exclusion. If "match_prefix" is true, any items matching
+ * the name as prefix are excluded.
+ */
+struct exclude_list_item
+{
+ const char *name;
+ bool match_prefix;
+};
+
+/*
+ * The contents of these directories are removed or recreated during server
+ * start so they are not included in data processed by pg_rewind.
+ *
+ * Note: those lists should be kept in sync with what basebackup.c provides.
+ * Some of the values, contrary to what basebackup.c uses, are hardcoded as
+ * they are defined in backend-only headers. So this list is maintained
+ * with a best effort in mind.
+ */
+static const char *excludeDirContents[] =
+{
+ /*
+ * Skip temporary statistics files. PG_STAT_TMP_DIR must be skipped even
+ * when stats_temp_directory is set because PGSS_TEXT_FILE is always
+ * created there.
+ */
+ "pg_stat_tmp", /* defined as PG_STAT_TMP_DIR */
+
+ /*
+ * It is generally not useful to backup the contents of this directory
+ * even if the intention is to restore to another primary. See backup.sgml
+ * for a more detailed description.
+ */
+ "pg_replslot",
+
+ /* Contents removed on startup, see dsm_cleanup_for_mmap(). */
+ "pg_dynshmem", /* defined as PG_DYNSHMEM_DIR */
+
+ /* Contents removed on startup, see AsyncShmemInit(). */
+ "pg_notify",
+
+ /*
+ * Old contents are loaded for possible debugging but are not required for
+ * normal operation, see SerialInit().
+ */
+ "pg_serial",
+
+ /* Contents removed on startup, see DeleteAllExportedSnapshotFiles(). */
+ "pg_snapshots",
+
+ /* Contents zeroed on startup, see StartupSUBTRANS(). */
+ "pg_subtrans",
+
+ /* end of list */
+ NULL
+};
+
+/*
+ * List of files excluded from filemap processing. Files are excluded
+ * if their prefix match.
+ */
+static const struct exclude_list_item excludeFiles[] =
+{
+ /* Skip auto conf temporary file. */
+ {"postgresql.auto.conf.tmp", false}, /* defined as PG_AUTOCONF_FILENAME */
+
+ /* Skip current log file temporary file */
+ {"current_logfiles.tmp", false}, /* defined as
+ * LOG_METAINFO_DATAFILE_TMP */
+
+ /* Skip relation cache because it is rebuilt on startup */
+ {"pg_internal.init", true}, /* defined as RELCACHE_INIT_FILENAME */
+
+ /*
+ * If there's a backup_label or tablespace_map file, it belongs to a
+ * backup started by the user with pg_start_backup(). It is *not* correct
+ * for this backup. Our backup_label is written later on separately.
+ */
+ {"backup_label", false}, /* defined as BACKUP_LABEL_FILE */
+ {"tablespace_map", false}, /* defined as TABLESPACE_MAP */
+
+ /*
+ * If there's a backup_manifest, it belongs to a backup that was used to
+ * start this server. It is *not* correct for this backup. Our
+ * backup_manifest is injected into the backup separately if users want
+ * it.
+ */
+ {"backup_manifest", false},
+
+ {"postmaster.pid", false},
+ {"postmaster.opts", false},
+
+ /* end of list */
+ {NULL, false}
+};
+
+/*
+ * Initialize the hash table for the file map.
+ */
+void
+filehash_init(void)
+{
+ filehash = filehash_create(FILEHASH_INITIAL_SIZE, NULL);
+}
+
+/* Look up entry for 'path', creating a new one if it doesn't exist */
+static file_entry_t *
+insert_filehash_entry(const char *path)
+{
+ file_entry_t *entry;
+ bool found;
+
+ entry = filehash_insert(filehash, path, &found);
+ if (!found)
+ {
+ entry->path = pg_strdup(path);
+ entry->isrelfile = isRelDataFile(path);
+
+ entry->target_exists = false;
+ entry->target_type = FILE_TYPE_UNDEFINED;
+ entry->target_size = 0;
+ entry->target_link_target = NULL;
+ entry->target_pages_to_overwrite.bitmap = NULL;
+ entry->target_pages_to_overwrite.bitmapsize = 0;
+
+ entry->source_exists = false;
+ entry->source_type = FILE_TYPE_UNDEFINED;
+ entry->source_size = 0;
+ entry->source_link_target = NULL;
+
+ entry->action = FILE_ACTION_UNDECIDED;
+ }
+
+ return entry;
+}
+
+static file_entry_t *
+lookup_filehash_entry(const char *path)
+{
+ return filehash_lookup(filehash, path);
+}
+
+/*
+ * Callback for processing source file list.
+ *
+ * This is called once for every file in the source server. We record the
+ * type and size of the file, so that decide_file_action() can later decide what
+ * to do with it.
+ */
+void
+process_source_file(const char *path, file_type_t type, size_t size,
+ const char *link_target)
+{
+ file_entry_t *entry;
+
+ /*
+ * Pretend that pg_wal is a directory, even if it's really a symlink. We
+ * don't want to mess with the symlink itself, nor complain if it's a
+ * symlink in source but not in target or vice versa.
+ */
+ if (strcmp(path, "pg_wal") == 0 && type == FILE_TYPE_SYMLINK)
+ type = FILE_TYPE_DIRECTORY;
+
+ /*
+ * sanity check: a filename that looks like a data file better be a
+ * regular file
+ */
+ if (type != FILE_TYPE_REGULAR && isRelDataFile(path))
+ pg_fatal("data file \"%s\" in source is not a regular file", path);
+
+ /* Remember this source file */
+ entry = insert_filehash_entry(path);
+ if (entry->source_exists)
+ pg_fatal("duplicate source file \"%s\"", path);
+ entry->source_exists = true;
+ entry->source_type = type;
+ entry->source_size = size;
+ entry->source_link_target = link_target ? pg_strdup(link_target) : NULL;
+}
+
+/*
+ * Callback for processing target file list.
+ *
+ * Record the type and size of the file, like process_source_file() does.
+ */
+void
+process_target_file(const char *path, file_type_t type, size_t size,
+ const char *link_target)
+{
+ file_entry_t *entry;
+
+ /*
+ * Do not apply any exclusion filters here. This has advantage to remove
+ * from the target data folder all paths which have been filtered out from
+ * the source data folder when processing the source files.
+ */
+
+ /*
+ * Like in process_source_file, pretend that pg_wal is always a directory.
+ */
+ if (strcmp(path, "pg_wal") == 0 && type == FILE_TYPE_SYMLINK)
+ type = FILE_TYPE_DIRECTORY;
+
+ /* Remember this target file */
+ entry = insert_filehash_entry(path);
+ if (entry->target_exists)
+ pg_fatal("duplicate source file \"%s\"", path);
+ entry->target_exists = true;
+ entry->target_type = type;
+ entry->target_size = size;
+ entry->target_link_target = link_target ? pg_strdup(link_target) : NULL;
+}
+
+/*
+ * This callback gets called while we read the WAL in the target, for every
+ * block that has changed in the target system. It decides if the given
+ * 'blkno' in the target relfile needs to be overwritten from the source, and
+ * if so, records it in 'target_pages_to_overwrite' bitmap.
+ *
+ * NOTE: All the files on both systems must have already been added to the
+ * hash table!
+ */
+void
+process_target_wal_block_change(ForkNumber forknum, RelFileNode rnode,
+ BlockNumber blkno)
+{
+ char *path;
+ file_entry_t *entry;
+ BlockNumber blkno_inseg;
+ int segno;
+
+ segno = blkno / RELSEG_SIZE;
+ blkno_inseg = blkno % RELSEG_SIZE;
+
+ path = datasegpath(rnode, forknum, segno);
+ entry = lookup_filehash_entry(path);
+ pfree(path);
+
+ /*
+ * If the block still exists in both systems, remember it. Otherwise we
+ * can safely ignore it.
+ *
+ * If the block is beyond the EOF in the source system, or the file
+ * doesn't exist in the source at all, we're going to truncate/remove it
+ * away from the target anyway. Likewise, if it doesn't exist in the
+ * target anymore, we will copy it over with the "tail" from the source
+ * system, anyway.
+ *
+ * It is possible to find WAL for a file that doesn't exist on either
+ * system anymore. It means that the relation was dropped later in the
+ * target system, and independently on the source system too, or that it
+ * was created and dropped in the target system and it never existed in
+ * the source. Either way, we can safely ignore it.
+ */
+ if (entry)
+ {
+ Assert(entry->isrelfile);
+
+ if (entry->target_exists)
+ {
+ if (entry->target_type != FILE_TYPE_REGULAR)
+ pg_fatal("unexpected page modification for non-regular file \"%s\"",
+ entry->path);
+
+ if (entry->source_exists)
+ {
+ off_t end_offset;
+
+ end_offset = (blkno_inseg + 1) * BLCKSZ;
+ if (end_offset <= entry->source_size && end_offset <= entry->target_size)
+ datapagemap_add(&entry->target_pages_to_overwrite, blkno_inseg);
+ }
+ }
+ }
+}
+
+/*
+ * Is this the path of file that pg_rewind can skip copying?
+ */
+static bool
+check_file_excluded(const char *path, bool is_source)
+{
+ char localpath[MAXPGPATH];
+ int excludeIdx;
+ const char *filename;
+
+ /*
+ * Skip all temporary files, .../pgsql_tmp/... and .../pgsql_tmp.*
+ */
+ if (strstr(path, "/" PG_TEMP_FILE_PREFIX) != NULL ||
+ strstr(path, "/" PG_TEMP_FILES_DIR "/") != NULL)
+ {
+ return true;
+ }
+
+ /* check individual files... */
+ for (excludeIdx = 0; excludeFiles[excludeIdx].name != NULL; excludeIdx++)
+ {
+ int cmplen = strlen(excludeFiles[excludeIdx].name);
+
+ filename = last_dir_separator(path);
+ if (filename == NULL)
+ filename = path;
+ else
+ filename++;
+
+ if (!excludeFiles[excludeIdx].match_prefix)
+ cmplen++;
+ if (strncmp(filename, excludeFiles[excludeIdx].name, cmplen) == 0)
+ {
+ if (is_source)
+ pg_log_debug("entry \"%s\" excluded from source file list",
+ path);
+ else
+ pg_log_debug("entry \"%s\" excluded from target file list",
+ path);
+ return true;
+ }
+ }
+
+ /*
+ * ... And check some directories. Note that this includes any contents
+ * within the directories themselves.
+ */
+ for (excludeIdx = 0; excludeDirContents[excludeIdx] != NULL; excludeIdx++)
+ {
+ snprintf(localpath, sizeof(localpath), "%s/",
+ excludeDirContents[excludeIdx]);
+ if (strstr(path, localpath) == path)
+ {
+ if (is_source)
+ pg_log_debug("entry \"%s\" excluded from source file list",
+ path);
+ else
+ pg_log_debug("entry \"%s\" excluded from target file list",
+ path);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static const char *
+action_to_str(file_action_t action)
+{
+ switch (action)
+ {
+ case FILE_ACTION_NONE:
+ return "NONE";
+ case FILE_ACTION_COPY:
+ return "COPY";
+ case FILE_ACTION_TRUNCATE:
+ return "TRUNCATE";
+ case FILE_ACTION_COPY_TAIL:
+ return "COPY_TAIL";
+ case FILE_ACTION_CREATE:
+ return "CREATE";
+ case FILE_ACTION_REMOVE:
+ return "REMOVE";
+
+ default:
+ return "unknown";
+ }
+}
+
+/*
+ * Calculate the totals needed for progress reports.
+ */
+void
+calculate_totals(filemap_t *filemap)
+{
+ file_entry_t *entry;
+ int i;
+
+ filemap->total_size = 0;
+ filemap->fetch_size = 0;
+
+ for (i = 0; i < filemap->nentries; i++)
+ {
+ entry = filemap->entries[i];
+
+ if (entry->source_type != FILE_TYPE_REGULAR)
+ continue;
+
+ filemap->total_size += entry->source_size;
+
+ if (entry->action == FILE_ACTION_COPY)
+ {
+ filemap->fetch_size += entry->source_size;
+ continue;
+ }
+
+ if (entry->action == FILE_ACTION_COPY_TAIL)
+ filemap->fetch_size += (entry->source_size - entry->target_size);
+
+ if (entry->target_pages_to_overwrite.bitmapsize > 0)
+ {
+ datapagemap_iterator_t *iter;
+ BlockNumber blk;
+
+ iter = datapagemap_iterate(&entry->target_pages_to_overwrite);
+ while (datapagemap_next(iter, &blk))
+ filemap->fetch_size += BLCKSZ;
+
+ pg_free(iter);
+ }
+ }
+}
+
+void
+print_filemap(filemap_t *filemap)
+{
+ file_entry_t *entry;
+ int i;
+
+ for (i = 0; i < filemap->nentries; i++)
+ {
+ entry = filemap->entries[i];
+ if (entry->action != FILE_ACTION_NONE ||
+ entry->target_pages_to_overwrite.bitmapsize > 0)
+ {
+ pg_log_debug("%s (%s)", entry->path,
+ action_to_str(entry->action));
+
+ if (entry->target_pages_to_overwrite.bitmapsize > 0)
+ datapagemap_print(&entry->target_pages_to_overwrite);
+ }
+ }
+ fflush(stdout);
+}
+
+/*
+ * Does it look like a relation data file?
+ *
+ * For our purposes, only files belonging to the main fork are considered
+ * relation files. Other forks are always copied in toto, because we cannot
+ * reliably track changes to them, because WAL only contains block references
+ * for the main fork.
+ */
+static bool
+isRelDataFile(const char *path)
+{
+ RelFileNode rnode;
+ unsigned int segNo;
+ int nmatch;
+ bool matched;
+
+ /*----
+ * Relation data files can be in one of the following directories:
+ *
+ * global/
+ * shared relations
+ *
+ * base/<db oid>/
+ * regular relations, default tablespace
+ *
+ * pg_tblspc/<tblspc oid>/<tblspc version>/
+ * within a non-default tablespace (the name of the directory
+ * depends on version)
+ *
+ * And the relation data files themselves have a filename like:
+ *
+ * <oid>.<segment number>
+ *
+ *----
+ */
+ rnode.spcNode = InvalidOid;
+ rnode.dbNode = InvalidOid;
+ rnode.relNode = InvalidOid;
+ segNo = 0;
+ matched = false;
+
+ nmatch = sscanf(path, "global/%u.%u", &rnode.relNode, &segNo);
+ if (nmatch == 1 || nmatch == 2)
+ {
+ rnode.spcNode = GLOBALTABLESPACE_OID;
+ rnode.dbNode = 0;
+ matched = true;
+ }
+ else
+ {
+ nmatch = sscanf(path, "base/%u/%u.%u",
+ &rnode.dbNode, &rnode.relNode, &segNo);
+ if (nmatch == 2 || nmatch == 3)
+ {
+ rnode.spcNode = DEFAULTTABLESPACE_OID;
+ matched = true;
+ }
+ else
+ {
+ nmatch = sscanf(path, "pg_tblspc/%u/" TABLESPACE_VERSION_DIRECTORY "/%u/%u.%u",
+ &rnode.spcNode, &rnode.dbNode, &rnode.relNode,
+ &segNo);
+ if (nmatch == 3 || nmatch == 4)
+ matched = true;
+ }
+ }
+
+ /*
+ * The sscanf tests above can match files that have extra characters at
+ * the end. To eliminate such cases, cross-check that GetRelationPath
+ * creates the exact same filename, when passed the RelFileNode
+ * information we extracted from the filename.
+ */
+ if (matched)
+ {
+ char *check_path = datasegpath(rnode, MAIN_FORKNUM, segNo);
+
+ if (strcmp(check_path, path) != 0)
+ matched = false;
+
+ pfree(check_path);
+ }
+
+ return matched;
+}
+
+/*
+ * A helper function to create the path of a relation file and segment.
+ *
+ * The returned path is palloc'd
+ */
+static char *
+datasegpath(RelFileNode rnode, ForkNumber forknum, BlockNumber segno)
+{
+ char *path;
+ char *segpath;
+
+ path = relpathperm(rnode, forknum);
+ if (segno > 0)
+ {
+ segpath = psprintf("%s.%u", path, segno);
+ pfree(path);
+ return segpath;
+ }
+ else
+ return path;
+}
+
+/*
+ * In the final stage, the filemap is sorted so that removals come last.
+ * From disk space usage point of view, it would be better to do removals
+ * first, but for now, safety first. If a whole directory is deleted, all
+ * files and subdirectories inside it need to removed first. On creation,
+ * parent directory needs to be created before files and directories inside
+ * it. To achieve that, the file_action_t enum is ordered so that we can
+ * just sort on that first. Furthermore, sort REMOVE entries in reverse
+ * path order, so that "foo/bar" subdirectory is removed before "foo".
+ */
+static int
+final_filemap_cmp(const void *a, const void *b)
+{
+ file_entry_t *fa = *((file_entry_t **) a);
+ file_entry_t *fb = *((file_entry_t **) b);
+
+ if (fa->action > fb->action)
+ return 1;
+ if (fa->action < fb->action)
+ return -1;
+
+ if (fa->action == FILE_ACTION_REMOVE)
+ return strcmp(fb->path, fa->path);
+ else
+ return strcmp(fa->path, fb->path);
+}
+
+/*
+ * Decide what action to perform to a file.
+ */
+static file_action_t
+decide_file_action(file_entry_t *entry)
+{
+ const char *path = entry->path;
+
+ /*
+ * Don't touch the control file. It is handled specially, after copying
+ * all the other files.
+ */
+ if (strcmp(path, "global/pg_control") == 0)
+ return FILE_ACTION_NONE;
+
+ /*
+ * Remove all files matching the exclusion filters in the target.
+ */
+ if (check_file_excluded(path, true))
+ {
+ if (entry->target_exists)
+ return FILE_ACTION_REMOVE;
+ else
+ return FILE_ACTION_NONE;
+ }
+
+ /*
+ * Handle cases where the file is missing from one of the systems.
+ */
+ if (!entry->target_exists && entry->source_exists)
+ {
+ /*
+ * File exists in source, but not in target. Copy it in toto. (If it's
+ * a relation data file, WAL replay after rewinding should re-create
+ * it anyway. But there's no harm in copying it now.)
+ */
+ switch (entry->source_type)
+ {
+ case FILE_TYPE_DIRECTORY:
+ case FILE_TYPE_SYMLINK:
+ return FILE_ACTION_CREATE;
+ case FILE_TYPE_REGULAR:
+ return FILE_ACTION_COPY;
+ case FILE_TYPE_UNDEFINED:
+ pg_fatal("unknown file type for \"%s\"", entry->path);
+ break;
+ }
+ }
+ else if (entry->target_exists && !entry->source_exists)
+ {
+ /* File exists in target, but not source. Remove it. */
+ return FILE_ACTION_REMOVE;
+ }
+ else if (!entry->target_exists && !entry->source_exists)
+ {
+ /*
+ * Doesn't exist in either server. Why does it have an entry in the
+ * first place??
+ */
+ Assert(false);
+ return FILE_ACTION_NONE;
+ }
+
+ /*
+ * Otherwise, the file exists on both systems
+ */
+ Assert(entry->target_exists && entry->source_exists);
+
+ if (entry->source_type != entry->target_type)
+ {
+ /* But it's a different kind of object. Strange.. */
+ pg_fatal("file \"%s\" is of different type in source and target", entry->path);
+ }
+
+ /*
+ * PG_VERSION files should be identical on both systems, but avoid
+ * overwriting them for paranoia.
+ */
+ if (pg_str_endswith(entry->path, "PG_VERSION"))
+ return FILE_ACTION_NONE;
+
+ switch (entry->source_type)
+ {
+ case FILE_TYPE_DIRECTORY:
+ return FILE_ACTION_NONE;
+
+ case FILE_TYPE_SYMLINK:
+
+ /*
+ * XXX: Should we check if it points to the same target?
+ */
+ return FILE_ACTION_NONE;
+
+ case FILE_TYPE_REGULAR:
+ if (!entry->isrelfile)
+ {
+ /*
+ * It's a non-data file that we have no special processing
+ * for. Copy it in toto.
+ */
+ return FILE_ACTION_COPY;
+ }
+ else
+ {
+ /*
+ * It's a data file that exists in both systems.
+ *
+ * If it's larger in target, we can truncate it. There will
+ * also be a WAL record of the truncation in the source
+ * system, so WAL replay would eventually truncate the target
+ * too, but we might as well do it now.
+ *
+ * If it's smaller in the target, it means that it has been
+ * truncated in the target, or enlarged in the source, or
+ * both. If it was truncated in the target, we need to copy
+ * the missing tail from the source system. If it was enlarged
+ * in the source system, there will be WAL records in the
+ * source system for the new blocks, so we wouldn't need to
+ * copy them here. But we don't know which scenario we're
+ * dealing with, and there's no harm in copying the missing
+ * blocks now, so do it now.
+ *
+ * If it's the same size, do nothing here. Any blocks modified
+ * in the target will be copied based on parsing the target
+ * system's WAL, and any blocks modified in the source will be
+ * updated after rewinding, when the source system's WAL is
+ * replayed.
+ */
+ if (entry->target_size < entry->source_size)
+ return FILE_ACTION_COPY_TAIL;
+ else if (entry->target_size > entry->source_size)
+ return FILE_ACTION_TRUNCATE;
+ else
+ return FILE_ACTION_NONE;
+ }
+ break;
+
+ case FILE_TYPE_UNDEFINED:
+ pg_fatal("unknown file type for \"%s\"", path);
+ break;
+ }
+
+ /* unreachable */
+ pg_fatal("could not decide what to do with file \"%s\"", path);
+}
+
+/*
+ * Decide what to do with each file.
+ *
+ * Returns a 'filemap' with the entries in the order that their actions
+ * should be executed.
+ */
+filemap_t *
+decide_file_actions(void)
+{
+ int i;
+ filehash_iterator it;
+ file_entry_t *entry;
+ filemap_t *filemap;
+
+ filehash_start_iterate(filehash, &it);
+ while ((entry = filehash_iterate(filehash, &it)) != NULL)
+ {
+ entry->action = decide_file_action(entry);
+ }
+
+ /*
+ * Turn the hash table into an array, and sort in the order that the
+ * actions should be performed.
+ */
+ filemap = pg_malloc(offsetof(filemap_t, entries) +
+ filehash->members * sizeof(file_entry_t *));
+ filemap->nentries = filehash->members;
+ filehash_start_iterate(filehash, &it);
+ i = 0;
+ while ((entry = filehash_iterate(filehash, &it)) != NULL)
+ {
+ filemap->entries[i++] = entry;
+ }
+
+ qsort(&filemap->entries, filemap->nentries, sizeof(file_entry_t *),
+ final_filemap_cmp);
+
+ return filemap;
+}
+
+
+/*
+ * Helper function for filemap hash table.
+ */
+static uint32
+hash_string_pointer(const char *s)
+{
+ unsigned char *ss = (unsigned char *) s;
+
+ return hash_bytes(ss, strlen(s));
+}
diff --git a/src/bin/pg_rewind/filemap.h b/src/bin/pg_rewind/filemap.h
new file mode 100644
index 0000000..926463e
--- /dev/null
+++ b/src/bin/pg_rewind/filemap.h
@@ -0,0 +1,113 @@
+/*-------------------------------------------------------------------------
+ *
+ * filemap.h
+ *
+ * Copyright (c) 2013-2021, PostgreSQL Global Development Group
+ *-------------------------------------------------------------------------
+ */
+#ifndef FILEMAP_H
+#define FILEMAP_H
+
+#include "datapagemap.h"
+#include "storage/block.h"
+#include "storage/relfilenode.h"
+
+/* these enum values are sorted in the order we want actions to be processed */
+typedef enum
+{
+ FILE_ACTION_UNDECIDED = 0, /* not decided yet */
+
+ FILE_ACTION_CREATE, /* create local directory or symbolic link */
+ FILE_ACTION_COPY, /* copy whole file, overwriting if exists */
+ FILE_ACTION_COPY_TAIL, /* copy tail from 'source_size' to
+ * 'target_size' */
+ FILE_ACTION_NONE, /* no action (we might still copy modified
+ * blocks based on the parsed WAL) */
+ FILE_ACTION_TRUNCATE, /* truncate local file to 'newsize' bytes */
+ FILE_ACTION_REMOVE /* remove local file / directory / symlink */
+} file_action_t;
+
+typedef enum
+{
+ FILE_TYPE_UNDEFINED = 0,
+
+ FILE_TYPE_REGULAR,
+ FILE_TYPE_DIRECTORY,
+ FILE_TYPE_SYMLINK
+} file_type_t;
+
+/*
+ * For every file found in the local or remote system, we have a file entry
+ * that contains information about the file on both systems. For relation
+ * files, there is also a page map that marks pages in the file that were
+ * changed in the target after the last common checkpoint.
+ *
+ * When gathering information, these are kept in a hash table, private to
+ * filemap.c. decide_file_actions() fills in the 'action' field, sorts all
+ * the entries, and returns them in an array, ready for executing the actions.
+ */
+typedef struct file_entry_t
+{
+ uint32 status; /* hash status */
+
+ const char *path;
+ bool isrelfile; /* is it a relation data file? */
+
+ /*
+ * Status of the file in the target.
+ */
+ bool target_exists;
+ file_type_t target_type;
+ size_t target_size; /* for a regular file */
+ char *target_link_target; /* for a symlink */
+
+ /*
+ * Pages that were modified in the target and need to be replaced from the
+ * source.
+ */
+ datapagemap_t target_pages_to_overwrite;
+
+ /*
+ * Status of the file in the source.
+ */
+ bool source_exists;
+ file_type_t source_type;
+ size_t source_size;
+ char *source_link_target; /* for a symlink */
+
+ /*
+ * What will we do to the file?
+ */
+ file_action_t action;
+} file_entry_t;
+
+/*
+ * This contains the final decisions on what to do with each file.
+ * 'entries' array contains an entry for each file, sorted in the order
+ * that their actions should executed.
+ */
+typedef struct filemap_t
+{
+ /* Summary information, filled by calculate_totals() */
+ uint64 total_size; /* total size of the source cluster */
+ uint64 fetch_size; /* number of bytes that needs to be copied */
+
+ int nentries; /* size of 'entries' array */
+ file_entry_t *entries[FLEXIBLE_ARRAY_MEMBER];
+} filemap_t;
+
+/* Functions for populating the filemap */
+extern void filehash_init(void);
+extern void process_source_file(const char *path, file_type_t type,
+ size_t size, const char *link_target);
+extern void process_target_file(const char *path, file_type_t type,
+ size_t size, const char *link_target);
+extern void process_target_wal_block_change(ForkNumber forknum,
+ RelFileNode rnode,
+ BlockNumber blkno);
+
+extern filemap_t *decide_file_actions(void);
+extern void calculate_totals(filemap_t *filemap);
+extern void print_filemap(filemap_t *filemap);
+
+#endif /* FILEMAP_H */
diff --git a/src/bin/pg_rewind/libpq_source.c b/src/bin/pg_rewind/libpq_source.c
new file mode 100644
index 0000000..8e0783f
--- /dev/null
+++ b/src/bin/pg_rewind/libpq_source.c
@@ -0,0 +1,643 @@
+/*-------------------------------------------------------------------------
+ *
+ * libpq_source.c
+ * Functions for fetching files from a remote server via libpq.
+ *
+ * Copyright (c) 2013-2021, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include "catalog/pg_type_d.h"
+#include "common/connect.h"
+#include "datapagemap.h"
+#include "file_ops.h"
+#include "filemap.h"
+#include "lib/stringinfo.h"
+#include "pg_rewind.h"
+#include "port/pg_bswap.h"
+#include "rewind_source.h"
+
+/*
+ * Files are fetched MAX_CHUNK_SIZE bytes at a time, and with a
+ * maximum of MAX_CHUNKS_PER_QUERY chunks in a single query.
+ */
+#define MAX_CHUNK_SIZE (1024 * 1024)
+#define MAX_CHUNKS_PER_QUERY 1000
+
+/* represents a request to fetch a piece of a file from the source */
+typedef struct
+{
+ const char *path; /* path relative to data directory root */
+ off_t offset;
+ size_t length;
+} fetch_range_request;
+
+typedef struct
+{
+ rewind_source common; /* common interface functions */
+
+ PGconn *conn;
+
+ /*
+ * Queue of chunks that have been requested with the queue_fetch_range()
+ * function, but have not been fetched from the remote server yet.
+ */
+ int num_requests;
+ fetch_range_request request_queue[MAX_CHUNKS_PER_QUERY];
+
+ /* temporary space for process_queued_fetch_requests() */
+ StringInfoData paths;
+ StringInfoData offsets;
+ StringInfoData lengths;
+} libpq_source;
+
+static void init_libpq_conn(PGconn *conn);
+static char *run_simple_query(PGconn *conn, const char *sql);
+static void run_simple_command(PGconn *conn, const char *sql);
+static void appendArrayEscapedString(StringInfo buf, const char *str);
+
+static void process_queued_fetch_requests(libpq_source *src);
+
+/* public interface functions */
+static void libpq_traverse_files(rewind_source *source,
+ process_file_callback_t callback);
+static void libpq_queue_fetch_range(rewind_source *source, const char *path,
+ off_t off, size_t len);
+static void libpq_finish_fetch(rewind_source *source);
+static char *libpq_fetch_file(rewind_source *source, const char *path,
+ size_t *filesize);
+static XLogRecPtr libpq_get_current_wal_insert_lsn(rewind_source *source);
+static void libpq_destroy(rewind_source *source);
+
+/*
+ * Create a new libpq source.
+ *
+ * The caller has already established the connection, but should not try
+ * to use it while the source is active.
+ */
+rewind_source *
+init_libpq_source(PGconn *conn)
+{
+ libpq_source *src;
+
+ init_libpq_conn(conn);
+
+ src = pg_malloc0(sizeof(libpq_source));
+
+ src->common.traverse_files = libpq_traverse_files;
+ src->common.fetch_file = libpq_fetch_file;
+ src->common.queue_fetch_range = libpq_queue_fetch_range;
+ src->common.finish_fetch = libpq_finish_fetch;
+ src->common.get_current_wal_insert_lsn = libpq_get_current_wal_insert_lsn;
+ src->common.destroy = libpq_destroy;
+
+ src->conn = conn;
+
+ initStringInfo(&src->paths);
+ initStringInfo(&src->offsets);
+ initStringInfo(&src->lengths);
+
+ return &src->common;
+}
+
+/*
+ * Initialize a libpq connection for use.
+ */
+static void
+init_libpq_conn(PGconn *conn)
+{
+ PGresult *res;
+ char *str;
+
+ /* disable all types of timeouts */
+ run_simple_command(conn, "SET statement_timeout = 0");
+ run_simple_command(conn, "SET lock_timeout = 0");
+ run_simple_command(conn, "SET idle_in_transaction_session_timeout = 0");
+
+ /*
+ * we don't intend to do any updates, put the connection in read-only mode
+ * to keep us honest
+ */
+ run_simple_command(conn, "SET default_transaction_read_only = on");
+
+ /* secure search_path */
+ res = PQexec(conn, ALWAYS_SECURE_SEARCH_PATH_SQL);
+ if (PQresultStatus(res) != PGRES_TUPLES_OK)
+ pg_fatal("could not clear search_path: %s",
+ PQresultErrorMessage(res));
+ PQclear(res);
+
+ /*
+ * Also check that full_page_writes is 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(conn, "SHOW full_page_writes");
+ if (strcmp(str, "on") != 0)
+ pg_fatal("full_page_writes must be enabled in the source server");
+ pg_free(str);
+
+ /* Prepare a statement we'll use to fetch files */
+ res = PQprepare(conn, "fetch_chunks_stmt",
+ "SELECT path, begin,\n"
+ " pg_read_binary_file(path, begin, len, true) AS chunk\n"
+ "FROM unnest ($1::text[], $2::int8[], $3::int4[]) as x(path, begin, len)",
+ 3, NULL);
+
+ if (PQresultStatus(res) != PGRES_COMMAND_OK)
+ pg_fatal("could not prepare statement to fetch file contents: %s",
+ PQresultErrorMessage(res));
+ PQclear(res);
+}
+
+/*
+ * Run a query that returns a single value.
+ *
+ * The result should be pg_free'd after use.
+ */
+static char *
+run_simple_query(PGconn *conn, const char *sql)
+{
+ PGresult *res;
+ char *result;
+
+ res = PQexec(conn, sql);
+
+ if (PQresultStatus(res) != PGRES_TUPLES_OK)
+ pg_fatal("error running query (%s) on source server: %s",
+ sql, PQresultErrorMessage(res));
+
+ /* sanity check the result set */
+ if (PQnfields(res) != 1 || PQntuples(res) != 1 || PQgetisnull(res, 0, 0))
+ pg_fatal("unexpected result set from query");
+
+ result = pg_strdup(PQgetvalue(res, 0, 0));
+
+ PQclear(res);
+
+ return result;
+}
+
+/*
+ * Run a command.
+ *
+ * In the event of a failure, exit immediately.
+ */
+static void
+run_simple_command(PGconn *conn, const char *sql)
+{
+ PGresult *res;
+
+ res = PQexec(conn, sql);
+
+ if (PQresultStatus(res) != PGRES_COMMAND_OK)
+ pg_fatal("error running query (%s) in source server: %s",
+ sql, PQresultErrorMessage(res));
+
+ PQclear(res);
+}
+
+/*
+ * Call the pg_current_wal_insert_lsn() function in the remote system.
+ */
+static XLogRecPtr
+libpq_get_current_wal_insert_lsn(rewind_source *source)
+{
+ PGconn *conn = ((libpq_source *) source)->conn;
+ XLogRecPtr result;
+ uint32 hi;
+ uint32 lo;
+ char *val;
+
+ val = run_simple_query(conn, "SELECT pg_current_wal_insert_lsn()");
+
+ if (sscanf(val, "%X/%X", &hi, &lo) != 2)
+ pg_fatal("unrecognized result \"%s\" for current WAL insert location", val);
+
+ result = ((uint64) hi) << 32 | lo;
+
+ pg_free(val);
+
+ return result;
+}
+
+/*
+ * Get a list of all files in the data directory.
+ */
+static void
+libpq_traverse_files(rewind_source *source, process_file_callback_t callback)
+{
+ PGconn *conn = ((libpq_source *) source)->conn;
+ PGresult *res;
+ const char *sql;
+ int i;
+
+ /*
+ * Create a recursive directory listing of the whole data directory.
+ *
+ * The WITH RECURSIVE part does most of the work. The second part gets the
+ * targets of the symlinks in pg_tblspc directory.
+ *
+ * XXX: There is no backend function to get a symbolic link's target in
+ * general, so if the admin has put any custom symbolic links in the data
+ * directory, they won't be copied correctly.
+ */
+ sql =
+ "WITH RECURSIVE files (path, filename, size, isdir) AS (\n"
+ " SELECT '' AS path, filename, size, isdir FROM\n"
+ " (SELECT pg_ls_dir('.', true, false) AS filename) AS fn,\n"
+ " pg_stat_file(fn.filename, true) AS this\n"
+ " UNION ALL\n"
+ " SELECT parent.path || parent.filename || '/' AS path,\n"
+ " fn, this.size, this.isdir\n"
+ " FROM files AS parent,\n"
+ " pg_ls_dir(parent.path || parent.filename, true, false) AS fn,\n"
+ " pg_stat_file(parent.path || parent.filename || '/' || fn, true) AS this\n"
+ " WHERE parent.isdir = 't'\n"
+ ")\n"
+ "SELECT path || filename, size, isdir,\n"
+ " pg_tablespace_location(pg_tablespace.oid) AS link_target\n"
+ "FROM files\n"
+ "LEFT OUTER JOIN pg_tablespace ON files.path = 'pg_tblspc/'\n"
+ " AND oid::text = files.filename\n";
+ res = PQexec(conn, sql);
+
+ if (PQresultStatus(res) != PGRES_TUPLES_OK)
+ pg_fatal("could not fetch file list: %s",
+ PQresultErrorMessage(res));
+
+ /* sanity check the result set */
+ if (PQnfields(res) != 4)
+ pg_fatal("unexpected result set while fetching file list");
+
+ /* Read result to local variables */
+ for (i = 0; i < PQntuples(res); i++)
+ {
+ char *path;
+ int64 filesize;
+ bool isdir;
+ char *link_target;
+ file_type_t type;
+
+ if (PQgetisnull(res, i, 1))
+ {
+ /*
+ * The file was removed from the server while the query was
+ * running. Ignore it.
+ */
+ continue;
+ }
+
+ path = PQgetvalue(res, i, 0);
+ filesize = atol(PQgetvalue(res, i, 1));
+ isdir = (strcmp(PQgetvalue(res, i, 2), "t") == 0);
+ link_target = PQgetvalue(res, i, 3);
+
+ if (link_target[0])
+ type = FILE_TYPE_SYMLINK;
+ else if (isdir)
+ type = FILE_TYPE_DIRECTORY;
+ else
+ type = FILE_TYPE_REGULAR;
+
+ process_source_file(path, type, filesize, link_target);
+ }
+ PQclear(res);
+}
+
+/*
+ * Queue up a request to fetch a piece of a file from remote system.
+ */
+static void
+libpq_queue_fetch_range(rewind_source *source, const char *path, off_t off,
+ size_t len)
+{
+ libpq_source *src = (libpq_source *) source;
+
+ /*
+ * Does this request happen to be a continuation of the previous chunk? If
+ * so, merge it with the previous one.
+ *
+ * XXX: We use pointer equality to compare the path. That's good enough
+ * for our purposes; the caller always passes the same pointer for the
+ * same filename. If it didn't, we would fail to merge requests, but it
+ * wouldn't affect correctness.
+ */
+ if (src->num_requests > 0)
+ {
+ fetch_range_request *prev = &src->request_queue[src->num_requests - 1];
+
+ if (prev->offset + prev->length == off &&
+ prev->length < MAX_CHUNK_SIZE &&
+ prev->path == path)
+ {
+ /*
+ * Extend the previous request to cover as much of this new
+ * request as possible, without exceeding MAX_CHUNK_SIZE.
+ */
+ size_t thislen;
+
+ thislen = Min(len, MAX_CHUNK_SIZE - prev->length);
+ prev->length += thislen;
+
+ off += thislen;
+ len -= thislen;
+
+ /*
+ * Fall through to create new requests for any remaining 'len'
+ * that didn't fit in the previous chunk.
+ */
+ }
+ }
+
+ /* Divide the request into pieces of MAX_CHUNK_SIZE bytes each */
+ while (len > 0)
+ {
+ int32 thislen;
+
+ /* if the queue is full, perform all the work queued up so far */
+ if (src->num_requests == MAX_CHUNKS_PER_QUERY)
+ process_queued_fetch_requests(src);
+
+ thislen = Min(len, MAX_CHUNK_SIZE);
+ src->request_queue[src->num_requests].path = path;
+ src->request_queue[src->num_requests].offset = off;
+ src->request_queue[src->num_requests].length = thislen;
+ src->num_requests++;
+
+ off += thislen;
+ len -= thislen;
+ }
+}
+
+/*
+ * Fetch all the queued chunks and write them to the target data directory.
+ */
+static void
+libpq_finish_fetch(rewind_source *source)
+{
+ process_queued_fetch_requests((libpq_source *) source);
+}
+
+static void
+process_queued_fetch_requests(libpq_source *src)
+{
+ const char *params[3];
+ PGresult *res;
+ int chunkno;
+
+ if (src->num_requests == 0)
+ return;
+
+ pg_log_debug("getting %d file chunks", src->num_requests);
+
+ /*
+ * The prepared statement, 'fetch_chunks_stmt', takes three arrays with
+ * the same length as parameters: paths, offsets and lengths. Construct
+ * the string representations of them.
+ */
+ resetStringInfo(&src->paths);
+ resetStringInfo(&src->offsets);
+ resetStringInfo(&src->lengths);
+
+ appendStringInfoChar(&src->paths, '{');
+ appendStringInfoChar(&src->offsets, '{');
+ appendStringInfoChar(&src->lengths, '{');
+ for (int i = 0; i < src->num_requests; i++)
+ {
+ fetch_range_request *rq = &src->request_queue[i];
+
+ if (i > 0)
+ {
+ appendStringInfoChar(&src->paths, ',');
+ appendStringInfoChar(&src->offsets, ',');
+ appendStringInfoChar(&src->lengths, ',');
+ }
+
+ appendArrayEscapedString(&src->paths, rq->path);
+ appendStringInfo(&src->offsets, INT64_FORMAT, (int64) rq->offset);
+ appendStringInfo(&src->lengths, INT64_FORMAT, (int64) rq->length);
+ }
+ appendStringInfoChar(&src->paths, '}');
+ appendStringInfoChar(&src->offsets, '}');
+ appendStringInfoChar(&src->lengths, '}');
+
+ /*
+ * Execute the prepared statement.
+ */
+ params[0] = src->paths.data;
+ params[1] = src->offsets.data;
+ params[2] = src->lengths.data;
+
+ if (PQsendQueryPrepared(src->conn, "fetch_chunks_stmt", 3, params, NULL, NULL, 1) != 1)
+ pg_fatal("could not send query: %s", PQerrorMessage(src->conn));
+
+ if (PQsetSingleRowMode(src->conn) != 1)
+ pg_fatal("could not set libpq connection to single row mode");
+
+ /*----
+ * The result set is of format:
+ *
+ * path text -- path in the data directory, e.g "base/1/123"
+ * begin int8 -- offset within the file
+ * chunk bytea -- file content
+ *----
+ */
+ chunkno = 0;
+ while ((res = PQgetResult(src->conn)) != NULL)
+ {
+ fetch_range_request *rq = &src->request_queue[chunkno];
+ char *filename;
+ int filenamelen;
+ int64 chunkoff;
+ int chunksize;
+ char *chunk;
+
+ switch (PQresultStatus(res))
+ {
+ case PGRES_SINGLE_TUPLE:
+ break;
+
+ case PGRES_TUPLES_OK:
+ PQclear(res);
+ continue; /* final zero-row result */
+
+ default:
+ pg_fatal("unexpected result while fetching remote files: %s",
+ PQresultErrorMessage(res));
+ }
+
+ if (chunkno > src->num_requests)
+ pg_fatal("received more data chunks than requested");
+
+ /* sanity check the result set */
+ if (PQnfields(res) != 3 || PQntuples(res) != 1)
+ pg_fatal("unexpected result set size while fetching remote files");
+
+ if (PQftype(res, 0) != TEXTOID ||
+ PQftype(res, 1) != INT8OID ||
+ PQftype(res, 2) != BYTEAOID)
+ {
+ pg_fatal("unexpected data types in result set while fetching remote files: %u %u %u",
+ PQftype(res, 0), PQftype(res, 1), PQftype(res, 2));
+ }
+
+ if (PQfformat(res, 0) != 1 &&
+ PQfformat(res, 1) != 1 &&
+ PQfformat(res, 2) != 1)
+ {
+ pg_fatal("unexpected result format while fetching remote files");
+ }
+
+ if (PQgetisnull(res, 0, 0) ||
+ PQgetisnull(res, 0, 1))
+ {
+ pg_fatal("unexpected null values in result while fetching remote files");
+ }
+
+ if (PQgetlength(res, 0, 1) != sizeof(int64))
+ pg_fatal("unexpected result length while fetching remote files");
+
+ /* Read result set to local variables */
+ memcpy(&chunkoff, PQgetvalue(res, 0, 1), sizeof(int64));
+ chunkoff = pg_ntoh64(chunkoff);
+ chunksize = PQgetlength(res, 0, 2);
+
+ filenamelen = PQgetlength(res, 0, 0);
+ filename = pg_malloc(filenamelen + 1);
+ memcpy(filename, PQgetvalue(res, 0, 0), filenamelen);
+ filename[filenamelen] = '\0';
+
+ chunk = PQgetvalue(res, 0, 2);
+
+ /*
+ * If a file has been deleted on the source, remove it on the target
+ * as well. Note that multiple unlink() calls may happen on the same
+ * file if multiple data chunks are associated with it, hence ignore
+ * unconditionally anything missing.
+ */
+ if (PQgetisnull(res, 0, 2))
+ {
+ pg_log_debug("received null value for chunk for file \"%s\", file has been deleted",
+ filename);
+ remove_target_file(filename, true);
+ }
+ else
+ {
+ pg_log_debug("received chunk for file \"%s\", offset %lld, size %d",
+ filename, (long long int) chunkoff, chunksize);
+
+ if (strcmp(filename, rq->path) != 0)
+ {
+ pg_fatal("received data for file \"%s\", when requested for \"%s\"",
+ filename, rq->path);
+ }
+ if (chunkoff != rq->offset)
+ pg_fatal("received data at offset %lld of file \"%s\", when requested for offset %lld",
+ (long long int) chunkoff, rq->path, (long long int) rq->offset);
+
+ /*
+ * We should not receive more data than we requested, or
+ * pg_read_binary_file() messed up. We could receive less,
+ * though, if the file was truncated in the source after we
+ * checked its size. That's OK, there should be a WAL record of
+ * the truncation, which will get replayed when you start the
+ * target system for the first time after pg_rewind has completed.
+ */
+ if (chunksize > rq->length)
+ pg_fatal("received more than requested for file \"%s\"", rq->path);
+
+ open_target_file(filename, false);
+
+ write_target_range(chunk, chunkoff, chunksize);
+ }
+
+ pg_free(filename);
+
+ PQclear(res);
+ chunkno++;
+ }
+ if (chunkno != src->num_requests)
+ pg_fatal("unexpected number of data chunks received");
+
+ src->num_requests = 0;
+}
+
+/*
+ * Escape a string to be used as element in a text array constant
+ */
+static void
+appendArrayEscapedString(StringInfo buf, const char *str)
+{
+ appendStringInfoCharMacro(buf, '\"');
+ while (*str)
+ {
+ char ch = *str;
+
+ if (ch == '"' || ch == '\\')
+ appendStringInfoCharMacro(buf, '\\');
+
+ appendStringInfoCharMacro(buf, ch);
+
+ str++;
+ }
+ appendStringInfoCharMacro(buf, '\"');
+}
+
+/*
+ * Fetch a single file as a malloc'd buffer.
+ */
+static char *
+libpq_fetch_file(rewind_source *source, const char *path, size_t *filesize)
+{
+ PGconn *conn = ((libpq_source *) source)->conn;
+ PGresult *res;
+ char *result;
+ int len;
+ const char *paramValues[1];
+
+ paramValues[0] = path;
+ res = PQexecParams(conn, "SELECT pg_read_binary_file($1)",
+ 1, NULL, paramValues, NULL, NULL, 1);
+
+ if (PQresultStatus(res) != PGRES_TUPLES_OK)
+ pg_fatal("could not fetch remote file \"%s\": %s",
+ path, PQresultErrorMessage(res));
+
+ /* sanity check the result set */
+ if (PQntuples(res) != 1 || PQgetisnull(res, 0, 0))
+ pg_fatal("unexpected result set while fetching remote file \"%s\"",
+ path);
+
+ /* Read result to local variables */
+ len = PQgetlength(res, 0, 0);
+ result = pg_malloc(len + 1);
+ memcpy(result, PQgetvalue(res, 0, 0), len);
+ result[len] = '\0';
+
+ PQclear(res);
+
+ pg_log_debug("fetched file \"%s\", length %d", path, len);
+
+ if (filesize)
+ *filesize = len;
+ return result;
+}
+
+/*
+ * Close a libpq source.
+ */
+static void
+libpq_destroy(rewind_source *source)
+{
+ libpq_source *src = (libpq_source *) source;
+
+ pfree(src->paths.data);
+ pfree(src->offsets.data);
+ pfree(src->lengths.data);
+ pfree(src);
+
+ /* NOTE: we don't close the connection here, as it was not opened by us. */
+}
diff --git a/src/bin/pg_rewind/local_source.c b/src/bin/pg_rewind/local_source.c
new file mode 100644
index 0000000..9c3491c
--- /dev/null
+++ b/src/bin/pg_rewind/local_source.c
@@ -0,0 +1,131 @@
+/*-------------------------------------------------------------------------
+ *
+ * local_source.c
+ * Functions for using a local data directory as the source.
+ *
+ * Portions Copyright (c) 2013-2021, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "datapagemap.h"
+#include "file_ops.h"
+#include "filemap.h"
+#include "pg_rewind.h"
+#include "rewind_source.h"
+
+typedef struct
+{
+ rewind_source common; /* common interface functions */
+
+ const char *datadir; /* path to the source data directory */
+} local_source;
+
+static void local_traverse_files(rewind_source *source,
+ process_file_callback_t callback);
+static char *local_fetch_file(rewind_source *source, const char *path,
+ size_t *filesize);
+static void local_fetch_file_range(rewind_source *source, const char *path,
+ off_t off, size_t len);
+static void local_finish_fetch(rewind_source *source);
+static void local_destroy(rewind_source *source);
+
+rewind_source *
+init_local_source(const char *datadir)
+{
+ local_source *src;
+
+ src = pg_malloc0(sizeof(local_source));
+
+ src->common.traverse_files = local_traverse_files;
+ src->common.fetch_file = local_fetch_file;
+ src->common.queue_fetch_range = local_fetch_file_range;
+ src->common.finish_fetch = local_finish_fetch;
+ src->common.get_current_wal_insert_lsn = NULL;
+ src->common.destroy = local_destroy;
+
+ src->datadir = datadir;
+
+ return &src->common;
+}
+
+static void
+local_traverse_files(rewind_source *source, process_file_callback_t callback)
+{
+ traverse_datadir(((local_source *) source)->datadir, &process_source_file);
+}
+
+static char *
+local_fetch_file(rewind_source *source, const char *path, size_t *filesize)
+{
+ return slurpFile(((local_source *) source)->datadir, path, filesize);
+}
+
+/*
+ * Copy a file from source to target, starting at 'off', for 'len' bytes.
+ */
+static void
+local_fetch_file_range(rewind_source *source, const char *path, off_t off,
+ size_t len)
+{
+ const char *datadir = ((local_source *) source)->datadir;
+ PGAlignedBlock buf;
+ char srcpath[MAXPGPATH];
+ int srcfd;
+ off_t begin = off;
+ off_t end = off + len;
+
+ snprintf(srcpath, sizeof(srcpath), "%s/%s", datadir, path);
+
+ srcfd = open(srcpath, O_RDONLY | PG_BINARY, 0);
+ if (srcfd < 0)
+ pg_fatal("could not open source file \"%s\": %m",
+ srcpath);
+
+ if (lseek(srcfd, begin, SEEK_SET) == -1)
+ pg_fatal("could not seek in source file: %m");
+
+ open_target_file(path, false);
+
+ while (end - begin > 0)
+ {
+ ssize_t readlen;
+ size_t len;
+
+ if (end - begin > sizeof(buf))
+ len = sizeof(buf);
+ else
+ len = end - begin;
+
+ readlen = read(srcfd, buf.data, len);
+
+ if (readlen < 0)
+ pg_fatal("could not read file \"%s\": %m", srcpath);
+ else if (readlen == 0)
+ pg_fatal("unexpected EOF while reading file \"%s\"", srcpath);
+
+ write_target_range(buf.data, begin, readlen);
+ begin += readlen;
+ }
+
+ if (close(srcfd) != 0)
+ pg_fatal("could not close file \"%s\": %m", srcpath);
+}
+
+static void
+local_finish_fetch(rewind_source *source)
+{
+ /*
+ * Nothing to do, local_fetch_file_range() copies the ranges immediately.
+ */
+}
+
+static void
+local_destroy(rewind_source *source)
+{
+ pfree(source);
+}
diff --git a/src/bin/pg_rewind/nls.mk b/src/bin/pg_rewind/nls.mk
new file mode 100644
index 0000000..915abc7
--- /dev/null
+++ b/src/bin/pg_rewind/nls.mk
@@ -0,0 +1,8 @@
+# src/bin/pg_rewind/nls.mk
+CATALOG_NAME = pg_rewind
+AVAIL_LANGUAGES = cs de el es fr ja ru sv uk zh_CN
+GETTEXT_FILES = $(FRONTEND_COMMON_GETTEXT_FILES) datapagemap.c file_ops.c filemap.c libpq_source.c local_source.c parsexlog.c pg_rewind.c timeline.c xlogreader.c ../../common/fe_memutils.c ../../common/restricted_token.c ../../fe_utils/archive.c ../../fe_utils/recovery_gen.c
+GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) pg_fatal report_invalid_record:2
+GETTEXT_FLAGS = $(FRONTEND_COMMON_GETTEXT_FLAGS) \
+ pg_fatal:1:c-format \
+ report_invalid_record:2:c-format
diff --git a/src/bin/pg_rewind/parsexlog.c b/src/bin/pg_rewind/parsexlog.c
new file mode 100644
index 0000000..b935129
--- /dev/null
+++ b/src/bin/pg_rewind/parsexlog.c
@@ -0,0 +1,452 @@
+/*-------------------------------------------------------------------------
+ *
+ * parsexlog.c
+ * Functions for reading Write-Ahead-Log
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <unistd.h>
+
+#include "access/rmgr.h"
+#include "access/xact.h"
+#include "access/xlog_internal.h"
+#include "access/xlogreader.h"
+#include "catalog/pg_control.h"
+#include "catalog/storage_xlog.h"
+#include "commands/dbcommands_xlog.h"
+#include "fe_utils/archive.h"
+#include "filemap.h"
+#include "pg_rewind.h"
+
+/*
+ * RmgrNames is an array of resource manager names, to make error messages
+ * a bit nicer.
+ */
+#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
+ name,
+
+static const char *RmgrNames[RM_MAX_ID + 1] = {
+#include "access/rmgrlist.h"
+};
+
+static void extractPageInfo(XLogReaderState *record);
+
+static int xlogreadfd = -1;
+static XLogSegNo xlogreadsegno = -1;
+static char xlogfpath[MAXPGPATH];
+
+typedef struct XLogPageReadPrivate
+{
+ const char *restoreCommand;
+ int tliIndex;
+} XLogPageReadPrivate;
+
+static int SimpleXLogPageRead(XLogReaderState *xlogreader,
+ XLogRecPtr targetPagePtr,
+ int reqLen, XLogRecPtr targetRecPtr, char *readBuf);
+
+/*
+ * Read WAL from the datadir/pg_wal, starting from 'startpoint' on timeline
+ * index 'tliIndex' in target timeline history, until 'endpoint'. Make note of
+ * the data blocks touched by the WAL records, and return them in a page map.
+ *
+ * 'endpoint' is the end of the last record to read. The record starting at
+ * 'endpoint' is the first one that is not read.
+ */
+void
+extractPageMap(const char *datadir, XLogRecPtr startpoint, int tliIndex,
+ XLogRecPtr endpoint, const char *restoreCommand)
+{
+ XLogRecord *record;
+ XLogReaderState *xlogreader;
+ char *errormsg;
+ XLogPageReadPrivate private;
+
+ private.tliIndex = tliIndex;
+ private.restoreCommand = restoreCommand;
+ xlogreader = XLogReaderAllocate(WalSegSz, datadir,
+ XL_ROUTINE(.page_read = &SimpleXLogPageRead),
+ &private);
+ if (xlogreader == NULL)
+ pg_fatal("out of memory");
+
+ XLogBeginRead(xlogreader, startpoint);
+ do
+ {
+ record = XLogReadRecord(xlogreader, &errormsg);
+
+ if (record == NULL)
+ {
+ XLogRecPtr errptr = xlogreader->EndRecPtr;
+
+ if (errormsg)
+ pg_fatal("could not read WAL record at %X/%X: %s",
+ LSN_FORMAT_ARGS(errptr),
+ errormsg);
+ else
+ pg_fatal("could not read WAL record at %X/%X",
+ LSN_FORMAT_ARGS(errptr));
+ }
+
+ extractPageInfo(xlogreader);
+
+ } while (xlogreader->EndRecPtr < endpoint);
+
+ /*
+ * If 'endpoint' didn't point exactly at a record boundary, the caller
+ * messed up.
+ */
+ if (xlogreader->EndRecPtr != endpoint)
+ pg_fatal("end pointer %X/%X is not a valid end point; expected %X/%X",
+ LSN_FORMAT_ARGS(endpoint), LSN_FORMAT_ARGS(xlogreader->EndRecPtr));
+
+ XLogReaderFree(xlogreader);
+ if (xlogreadfd != -1)
+ {
+ close(xlogreadfd);
+ xlogreadfd = -1;
+ }
+}
+
+/*
+ * Reads one WAL record. Returns the end position of the record, without
+ * doing anything with the record itself.
+ */
+XLogRecPtr
+readOneRecord(const char *datadir, XLogRecPtr ptr, int tliIndex,
+ const char *restoreCommand)
+{
+ XLogRecord *record;
+ XLogReaderState *xlogreader;
+ char *errormsg;
+ XLogPageReadPrivate private;
+ XLogRecPtr endptr;
+
+ private.tliIndex = tliIndex;
+ private.restoreCommand = restoreCommand;
+ xlogreader = XLogReaderAllocate(WalSegSz, datadir,
+ XL_ROUTINE(.page_read = &SimpleXLogPageRead),
+ &private);
+ if (xlogreader == NULL)
+ pg_fatal("out of memory");
+
+ XLogBeginRead(xlogreader, ptr);
+ record = XLogReadRecord(xlogreader, &errormsg);
+ if (record == NULL)
+ {
+ if (errormsg)
+ pg_fatal("could not read WAL record at %X/%X: %s",
+ LSN_FORMAT_ARGS(ptr), errormsg);
+ else
+ pg_fatal("could not read WAL record at %X/%X",
+ LSN_FORMAT_ARGS(ptr));
+ }
+ endptr = xlogreader->EndRecPtr;
+
+ XLogReaderFree(xlogreader);
+ if (xlogreadfd != -1)
+ {
+ close(xlogreadfd);
+ xlogreadfd = -1;
+ }
+
+ return endptr;
+}
+
+/*
+ * Find the previous checkpoint preceding given WAL location.
+ */
+void
+findLastCheckpoint(const char *datadir, XLogRecPtr forkptr, int tliIndex,
+ XLogRecPtr *lastchkptrec, TimeLineID *lastchkpttli,
+ XLogRecPtr *lastchkptredo, const char *restoreCommand)
+{
+ /* Walk backwards, starting from the given record */
+ XLogRecord *record;
+ XLogRecPtr searchptr;
+ XLogReaderState *xlogreader;
+ char *errormsg;
+ XLogPageReadPrivate private;
+
+ /*
+ * The given fork pointer points to the end of the last common record,
+ * which is not necessarily the beginning of the next record, if the
+ * previous record happens to end at a page boundary. Skip over the page
+ * header in that case to find the next record.
+ */
+ if (forkptr % XLOG_BLCKSZ == 0)
+ {
+ if (XLogSegmentOffset(forkptr, WalSegSz) == 0)
+ forkptr += SizeOfXLogLongPHD;
+ else
+ forkptr += SizeOfXLogShortPHD;
+ }
+
+ private.tliIndex = tliIndex;
+ private.restoreCommand = restoreCommand;
+ xlogreader = XLogReaderAllocate(WalSegSz, datadir,
+ XL_ROUTINE(.page_read = &SimpleXLogPageRead),
+ &private);
+ if (xlogreader == NULL)
+ pg_fatal("out of memory");
+
+ searchptr = forkptr;
+ for (;;)
+ {
+ uint8 info;
+
+ XLogBeginRead(xlogreader, searchptr);
+ record = XLogReadRecord(xlogreader, &errormsg);
+
+ if (record == NULL)
+ {
+ if (errormsg)
+ pg_fatal("could not find previous WAL record at %X/%X: %s",
+ LSN_FORMAT_ARGS(searchptr),
+ errormsg);
+ else
+ pg_fatal("could not find previous WAL record at %X/%X",
+ LSN_FORMAT_ARGS(searchptr));
+ }
+
+ /*
+ * Check if it is a checkpoint record. This checkpoint record needs to
+ * be the latest checkpoint before WAL forked and not the checkpoint
+ * where the primary has been stopped to be rewound.
+ */
+ info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;
+ if (searchptr < forkptr &&
+ XLogRecGetRmid(xlogreader) == RM_XLOG_ID &&
+ (info == XLOG_CHECKPOINT_SHUTDOWN ||
+ info == XLOG_CHECKPOINT_ONLINE))
+ {
+ CheckPoint checkPoint;
+
+ memcpy(&checkPoint, XLogRecGetData(xlogreader), sizeof(CheckPoint));
+ *lastchkptrec = searchptr;
+ *lastchkpttli = checkPoint.ThisTimeLineID;
+ *lastchkptredo = checkPoint.redo;
+ break;
+ }
+
+ /* Walk backwards to previous record. */
+ searchptr = record->xl_prev;
+ }
+
+ XLogReaderFree(xlogreader);
+ if (xlogreadfd != -1)
+ {
+ close(xlogreadfd);
+ xlogreadfd = -1;
+ }
+}
+
+/* XLogReader callback function, to read a WAL page */
+static int
+SimpleXLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr,
+ int reqLen, XLogRecPtr targetRecPtr, char *readBuf)
+{
+ XLogPageReadPrivate *private = (XLogPageReadPrivate *) xlogreader->private_data;
+ uint32 targetPageOff;
+ XLogRecPtr targetSegEnd;
+ XLogSegNo targetSegNo;
+ int r;
+
+ XLByteToSeg(targetPagePtr, targetSegNo, WalSegSz);
+ XLogSegNoOffsetToRecPtr(targetSegNo + 1, 0, WalSegSz, targetSegEnd);
+ targetPageOff = XLogSegmentOffset(targetPagePtr, WalSegSz);
+
+ /*
+ * See if we need to switch to a new segment because the requested record
+ * is not in the currently open one.
+ */
+ if (xlogreadfd >= 0 &&
+ !XLByteInSeg(targetPagePtr, xlogreadsegno, WalSegSz))
+ {
+ close(xlogreadfd);
+ xlogreadfd = -1;
+ }
+
+ XLByteToSeg(targetPagePtr, xlogreadsegno, WalSegSz);
+
+ if (xlogreadfd < 0)
+ {
+ char xlogfname[MAXFNAMELEN];
+
+ /*
+ * Since incomplete segments are copied into next timelines, switch to
+ * the timeline holding the required segment. Assuming this scan can
+ * be done both forward and backward, consider also switching timeline
+ * accordingly.
+ */
+ while (private->tliIndex < targetNentries - 1 &&
+ targetHistory[private->tliIndex].end < targetSegEnd)
+ private->tliIndex++;
+ while (private->tliIndex > 0 &&
+ targetHistory[private->tliIndex].begin >= targetSegEnd)
+ private->tliIndex--;
+
+ XLogFileName(xlogfname, targetHistory[private->tliIndex].tli,
+ xlogreadsegno, WalSegSz);
+
+ snprintf(xlogfpath, MAXPGPATH, "%s/" XLOGDIR "/%s",
+ xlogreader->segcxt.ws_dir, xlogfname);
+
+ xlogreadfd = open(xlogfpath, O_RDONLY | PG_BINARY, 0);
+
+ if (xlogreadfd < 0)
+ {
+ /*
+ * If we have no restore_command to execute, then exit.
+ */
+ if (private->restoreCommand == NULL)
+ {
+ pg_log_error("could not open file \"%s\": %m", xlogfpath);
+ return -1;
+ }
+
+ /*
+ * Since we have restore_command, then try to retrieve missing WAL
+ * file from the archive.
+ */
+ xlogreadfd = RestoreArchivedFile(xlogreader->segcxt.ws_dir,
+ xlogfname,
+ WalSegSz,
+ private->restoreCommand);
+
+ if (xlogreadfd < 0)
+ return -1;
+ else
+ pg_log_debug("using file \"%s\" restored from archive",
+ xlogfpath);
+ }
+ }
+
+ /*
+ * At this point, we have the right segment open.
+ */
+ Assert(xlogreadfd != -1);
+
+ /* Read the requested page */
+ if (lseek(xlogreadfd, (off_t) targetPageOff, SEEK_SET) < 0)
+ {
+ pg_log_error("could not seek in file \"%s\": %m", xlogfpath);
+ return -1;
+ }
+
+
+ r = read(xlogreadfd, readBuf, XLOG_BLCKSZ);
+ if (r != XLOG_BLCKSZ)
+ {
+ if (r < 0)
+ pg_log_error("could not read file \"%s\": %m", xlogfpath);
+ else
+ pg_log_error("could not read file \"%s\": read %d of %zu",
+ xlogfpath, r, (Size) XLOG_BLCKSZ);
+
+ return -1;
+ }
+
+ Assert(targetSegNo == xlogreadsegno);
+
+ xlogreader->seg.ws_tli = targetHistory[private->tliIndex].tli;
+ return XLOG_BLCKSZ;
+}
+
+/*
+ * Extract information on which blocks the current record modifies.
+ */
+static void
+extractPageInfo(XLogReaderState *record)
+{
+ int block_id;
+ RmgrId rmid = XLogRecGetRmid(record);
+ uint8 info = XLogRecGetInfo(record);
+ uint8 rminfo = info & ~XLR_INFO_MASK;
+
+ /* Is this a special record type that I recognize? */
+
+ if (rmid == RM_DBASE_ID && rminfo == XLOG_DBASE_CREATE)
+ {
+ /*
+ * New databases can be safely ignored. It won't be present in the
+ * source system, so it will be deleted. There's one corner-case,
+ * though: if a new, different, database is also created in the source
+ * system, we'll see that the files already exist and not copy them.
+ * That's OK, though; WAL replay of creating the new database, from
+ * the source systems's WAL, will re-copy the new database,
+ * overwriting the database created in the target system.
+ */
+ }
+ else if (rmid == RM_DBASE_ID && rminfo == XLOG_DBASE_DROP)
+ {
+ /*
+ * An existing database was dropped. We'll see that the files don't
+ * exist in the target data dir, and copy them in toto from the source
+ * system. No need to do anything special here.
+ */
+ }
+ else if (rmid == RM_SMGR_ID && rminfo == XLOG_SMGR_CREATE)
+ {
+ /*
+ * We can safely ignore these. The file will be removed from the
+ * target, if it doesn't exist in source system. If a file with same
+ * name is created in source system, too, there will be WAL records
+ * for all the blocks in it.
+ */
+ }
+ else if (rmid == RM_SMGR_ID && rminfo == XLOG_SMGR_TRUNCATE)
+ {
+ /*
+ * We can safely ignore these. When we compare the sizes later on,
+ * we'll notice that they differ, and copy the missing tail from
+ * source system.
+ */
+ }
+ else if (rmid == RM_XACT_ID &&
+ ((rminfo & XLOG_XACT_OPMASK) == XLOG_XACT_COMMIT ||
+ (rminfo & XLOG_XACT_OPMASK) == XLOG_XACT_COMMIT_PREPARED ||
+ (rminfo & XLOG_XACT_OPMASK) == XLOG_XACT_ABORT ||
+ (rminfo & XLOG_XACT_OPMASK) == XLOG_XACT_ABORT_PREPARED))
+ {
+ /*
+ * These records can include "dropped rels". We can safely ignore
+ * them, we will see that they are missing and copy them from the
+ * source.
+ */
+ }
+ else if (info & XLR_SPECIAL_REL_UPDATE)
+ {
+ /*
+ * This record type modifies a relation file in some special way, but
+ * we don't recognize the type. That's bad - we don't know how to
+ * track that change.
+ */
+ pg_fatal("WAL record modifies a relation, but record type is not recognized: "
+ "lsn: %X/%X, rmgr: %s, info: %02X",
+ LSN_FORMAT_ARGS(record->ReadRecPtr),
+ RmgrNames[rmid], info);
+ }
+
+ for (block_id = 0; block_id <= record->max_block_id; block_id++)
+ {
+ RelFileNode rnode;
+ ForkNumber forknum;
+ BlockNumber blkno;
+
+ if (!XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blkno))
+ continue;
+
+ /* We only care about the main fork; others are copied in toto */
+ if (forknum != MAIN_FORKNUM)
+ continue;
+
+ process_target_wal_block_change(forknum, rnode, blkno);
+ }
+}
diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c
new file mode 100644
index 0000000..2ac4910
--- /dev/null
+++ b/src/bin/pg_rewind/pg_rewind.c
@@ -0,0 +1,1142 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_rewind.c
+ * Synchronizes a PostgreSQL data directory to a new timeline
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "access/timeline.h"
+#include "access/xlog_internal.h"
+#include "catalog/catversion.h"
+#include "catalog/pg_control.h"
+#include "common/controldata_utils.h"
+#include "common/file_perm.h"
+#include "common/restricted_token.h"
+#include "common/string.h"
+#include "fe_utils/recovery_gen.h"
+#include "file_ops.h"
+#include "filemap.h"
+#include "getopt_long.h"
+#include "pg_rewind.h"
+#include "rewind_source.h"
+#include "storage/bufpage.h"
+
+static void usage(const char *progname);
+
+static void perform_rewind(filemap_t *filemap, rewind_source *source,
+ XLogRecPtr chkptrec,
+ TimeLineID chkpttli,
+ XLogRecPtr chkptredo);
+
+static void createBackupLabel(XLogRecPtr startpoint, TimeLineID starttli,
+ XLogRecPtr checkpointloc);
+
+static void digestControlFile(ControlFileData *ControlFile,
+ const char *content, size_t size);
+static void getRestoreCommand(const char *argv0);
+static void sanityChecks(void);
+static void findCommonAncestorTimeline(XLogRecPtr *recptr, int *tliIndex);
+static void ensureCleanShutdown(const char *argv0);
+static void disconnect_atexit(void);
+
+static ControlFileData ControlFile_target;
+static ControlFileData ControlFile_source;
+static ControlFileData ControlFile_source_after;
+
+const char *progname;
+int WalSegSz;
+
+/* Configuration options */
+char *datadir_target = NULL;
+char *datadir_source = NULL;
+char *connstr_source = NULL;
+char *restore_command = NULL;
+
+static bool debug = false;
+bool showprogress = false;
+bool dry_run = false;
+bool do_sync = true;
+bool restore_wal = false;
+
+/* Target history */
+TimeLineHistoryEntry *targetHistory;
+int targetNentries;
+
+/* Progress counters */
+uint64 fetch_size;
+uint64 fetch_done;
+
+static PGconn *conn;
+static rewind_source *source;
+
+static void
+usage(const char *progname)
+{
+ printf(_("%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n\n"), progname);
+ printf(_("Usage:\n %s [OPTION]...\n\n"), progname);
+ printf(_("Options:\n"));
+ printf(_(" -c, --restore-target-wal use restore_command in target configuration to\n"
+ " retrieve WAL files from archives\n"));
+ printf(_(" -D, --target-pgdata=DIRECTORY existing data directory to modify\n"));
+ printf(_(" --source-pgdata=DIRECTORY source data directory to synchronize with\n"));
+ printf(_(" --source-server=CONNSTR source server to synchronize with\n"));
+ printf(_(" -n, --dry-run stop before modifying anything\n"));
+ printf(_(" -N, --no-sync do not wait for changes to be written\n"
+ " safely to disk\n"));
+ printf(_(" -P, --progress write progress messages\n"));
+ printf(_(" -R, --write-recovery-conf write configuration for replication\n"
+ " (requires --source-server)\n"));
+ printf(_(" --debug write a lot of debug messages\n"));
+ printf(_(" --no-ensure-shutdown do not automatically fix unclean shutdown\n"));
+ printf(_(" -V, --version output version information, then exit\n"));
+ printf(_(" -?, --help show this help, then exit\n"));
+ printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ static struct option long_options[] = {
+ {"help", no_argument, NULL, '?'},
+ {"target-pgdata", required_argument, NULL, 'D'},
+ {"write-recovery-conf", no_argument, NULL, 'R'},
+ {"source-pgdata", required_argument, NULL, 1},
+ {"source-server", required_argument, NULL, 2},
+ {"no-ensure-shutdown", no_argument, NULL, 4},
+ {"version", no_argument, NULL, 'V'},
+ {"restore-target-wal", no_argument, NULL, 'c'},
+ {"dry-run", no_argument, NULL, 'n'},
+ {"no-sync", no_argument, NULL, 'N'},
+ {"progress", no_argument, NULL, 'P'},
+ {"debug", no_argument, NULL, 3},
+ {NULL, 0, NULL, 0}
+ };
+ int option_index;
+ int c;
+ XLogRecPtr divergerec;
+ int lastcommontliIndex;
+ XLogRecPtr chkptrec;
+ TimeLineID chkpttli;
+ XLogRecPtr chkptredo;
+ XLogRecPtr target_wal_endrec;
+ size_t size;
+ char *buffer;
+ bool no_ensure_shutdown = false;
+ bool rewind_needed;
+ bool writerecoveryconf = false;
+ filemap_t *filemap;
+
+ pg_logging_init(argv[0]);
+ set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_rewind"));
+ progname = get_progname(argv[0]);
+
+ /* Process command-line arguments */
+ if (argc > 1)
+ {
+ if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
+ {
+ usage(progname);
+ exit(0);
+ }
+ if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
+ {
+ puts("pg_rewind (PostgreSQL) " PG_VERSION);
+ exit(0);
+ }
+ }
+
+ while ((c = getopt_long(argc, argv, "cD:nNPR", long_options, &option_index)) != -1)
+ {
+ switch (c)
+ {
+ case '?':
+ fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ exit(1);
+
+ case 'c':
+ restore_wal = true;
+ break;
+
+ case 'P':
+ showprogress = true;
+ break;
+
+ case 'n':
+ dry_run = true;
+ break;
+
+ case 'N':
+ do_sync = false;
+ break;
+
+ case 'R':
+ writerecoveryconf = true;
+ break;
+
+ case 3:
+ debug = true;
+ pg_logging_increase_verbosity();
+ break;
+
+ case 'D': /* -D or --target-pgdata */
+ datadir_target = pg_strdup(optarg);
+ break;
+
+ case 1: /* --source-pgdata */
+ datadir_source = pg_strdup(optarg);
+ break;
+
+ case 2: /* --source-server */
+ connstr_source = pg_strdup(optarg);
+ break;
+
+ case 4:
+ no_ensure_shutdown = true;
+ break;
+ }
+ }
+
+ if (datadir_source == NULL && connstr_source == NULL)
+ {
+ pg_log_error("no source specified (--source-pgdata or --source-server)");
+ fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ exit(1);
+ }
+
+ if (datadir_source != NULL && connstr_source != NULL)
+ {
+ pg_log_error("only one of --source-pgdata or --source-server can be specified");
+ fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ exit(1);
+ }
+
+ if (datadir_target == NULL)
+ {
+ pg_log_error("no target data directory specified (--target-pgdata)");
+ fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ exit(1);
+ }
+
+ if (writerecoveryconf && connstr_source == NULL)
+ {
+ pg_log_error("no source server information (--source-server) specified for --write-recovery-conf");
+ fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ exit(1);
+ }
+
+ if (optind < argc)
+ {
+ pg_log_error("too many command-line arguments (first is \"%s\")",
+ argv[optind]);
+ fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+ exit(1);
+ }
+
+ /*
+ * Don't allow pg_rewind to be run as root, to avoid overwriting the
+ * ownership of files in the data directory. We need only check for root
+ * -- any other user won't have sufficient permissions to modify files in
+ * the data directory.
+ */
+#ifndef WIN32
+ if (geteuid() == 0)
+ {
+ pg_log_error("cannot be executed by \"root\"");
+ fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"),
+ progname);
+ exit(1);
+ }
+#endif
+
+ get_restricted_token();
+
+ /* Set mask based on PGDATA permissions */
+ if (!GetDataDirectoryCreatePerm(datadir_target))
+ {
+ pg_log_error("could not read permissions of directory \"%s\": %m",
+ datadir_target);
+ exit(1);
+ }
+
+ umask(pg_mode_mask);
+
+ getRestoreCommand(argv[0]);
+
+ atexit(disconnect_atexit);
+
+ /*
+ * Ok, we have all the options and we're ready to start. First, connect to
+ * remote server.
+ */
+ if (connstr_source)
+ {
+ conn = PQconnectdb(connstr_source);
+
+ if (PQstatus(conn) == CONNECTION_BAD)
+ pg_fatal("%s", PQerrorMessage(conn));
+
+ if (showprogress)
+ pg_log_info("connected to server");
+
+ source = init_libpq_source(conn);
+ }
+ else
+ source = init_local_source(datadir_source);
+
+ /*
+ * Check the status of the target instance.
+ *
+ * If the target instance was not cleanly shut down, start and stop the
+ * target cluster once in single-user mode to enforce recovery to finish,
+ * ensuring that the cluster can be used by pg_rewind. Note that if
+ * no_ensure_shutdown is specified, pg_rewind ignores this step, and users
+ * need to make sure by themselves that the target cluster is in a clean
+ * state.
+ */
+ buffer = slurpFile(datadir_target, "global/pg_control", &size);
+ digestControlFile(&ControlFile_target, buffer, size);
+ pg_free(buffer);
+
+ if (!no_ensure_shutdown &&
+ ControlFile_target.state != DB_SHUTDOWNED &&
+ ControlFile_target.state != DB_SHUTDOWNED_IN_RECOVERY)
+ {
+ ensureCleanShutdown(argv[0]);
+
+ buffer = slurpFile(datadir_target, "global/pg_control", &size);
+ digestControlFile(&ControlFile_target, buffer, size);
+ pg_free(buffer);
+ }
+
+ buffer = source->fetch_file(source, "global/pg_control", &size);
+ digestControlFile(&ControlFile_source, buffer, size);
+ pg_free(buffer);
+
+ sanityChecks();
+
+ /*
+ * Find the common ancestor timeline between the clusters.
+ *
+ * If both clusters are already on the same timeline, there's nothing to
+ * do.
+ */
+ if (ControlFile_target.checkPointCopy.ThisTimeLineID ==
+ ControlFile_source.checkPointCopy.ThisTimeLineID)
+ {
+ pg_log_info("source and target cluster are on the same timeline");
+ rewind_needed = false;
+ target_wal_endrec = 0;
+ }
+ else
+ {
+ XLogRecPtr chkptendrec;
+
+ findCommonAncestorTimeline(&divergerec, &lastcommontliIndex);
+ pg_log_info("servers diverged at WAL location %X/%X on timeline %u",
+ LSN_FORMAT_ARGS(divergerec),
+ targetHistory[lastcommontliIndex].tli);
+
+ /*
+ * Determine the end-of-WAL on the target.
+ *
+ * The WAL ends at the last shutdown checkpoint, or at
+ * minRecoveryPoint if it was a standby. (If we supported rewinding a
+ * server that was not shut down cleanly, we would need to replay
+ * until we reach the first invalid record, like crash recovery does.)
+ */
+
+ /* read the checkpoint record on the target to see where it ends. */
+ chkptendrec = readOneRecord(datadir_target,
+ ControlFile_target.checkPoint,
+ targetNentries - 1,
+ restore_command);
+
+ if (ControlFile_target.minRecoveryPoint > chkptendrec)
+ {
+ target_wal_endrec = ControlFile_target.minRecoveryPoint;
+ }
+ else
+ {
+ target_wal_endrec = chkptendrec;
+ }
+
+ /*
+ * Check for the possibility that the target is in fact a direct
+ * ancestor of the source. In that case, there is no divergent history
+ * in the target that needs rewinding.
+ */
+ if (target_wal_endrec > divergerec)
+ {
+ rewind_needed = true;
+ }
+ else
+ {
+ /* the last common checkpoint record must be part of target WAL */
+ Assert(target_wal_endrec == divergerec);
+
+ rewind_needed = false;
+ }
+ }
+
+ if (!rewind_needed)
+ {
+ pg_log_info("no rewind required");
+ if (writerecoveryconf && !dry_run)
+ WriteRecoveryConfig(conn, datadir_target,
+ GenerateRecoveryConfig(conn, NULL));
+ exit(0);
+ }
+
+ findLastCheckpoint(datadir_target, divergerec, lastcommontliIndex,
+ &chkptrec, &chkpttli, &chkptredo, restore_command);
+ pg_log_info("rewinding from last common checkpoint at %X/%X on timeline %u",
+ LSN_FORMAT_ARGS(chkptrec), chkpttli);
+
+ /* Initialize the hash table to track the status of each file */
+ filehash_init();
+
+ /*
+ * Collect information about all files in the both data directories.
+ */
+ if (showprogress)
+ pg_log_info("reading source file list");
+ source->traverse_files(source, &process_source_file);
+
+ if (showprogress)
+ pg_log_info("reading target file list");
+ traverse_datadir(datadir_target, &process_target_file);
+
+ /*
+ * Read the target WAL from last checkpoint before the point of fork, to
+ * extract all the pages that were modified on the target cluster after
+ * the fork.
+ */
+ if (showprogress)
+ pg_log_info("reading WAL in target");
+ extractPageMap(datadir_target, chkptrec, lastcommontliIndex,
+ target_wal_endrec, restore_command);
+
+ /*
+ * We have collected all information we need from both systems. Decide
+ * what to do with each file.
+ */
+ filemap = decide_file_actions();
+ if (showprogress)
+ calculate_totals(filemap);
+
+ /* this is too verbose even for verbose mode */
+ if (debug)
+ print_filemap(filemap);
+
+ /*
+ * Ok, we're ready to start copying things over.
+ */
+ if (showprogress)
+ {
+ pg_log_info("need to copy %lu MB (total source directory size is %lu MB)",
+ (unsigned long) (filemap->fetch_size / (1024 * 1024)),
+ (unsigned long) (filemap->total_size / (1024 * 1024)));
+
+ fetch_size = filemap->fetch_size;
+ fetch_done = 0;
+ }
+
+ /*
+ * We have now collected all the information we need from both systems,
+ * and we are ready to start modifying the target directory.
+ *
+ * This is the point of no return. Once we start copying things, there is
+ * no turning back!
+ */
+ perform_rewind(filemap, source, chkptrec, chkpttli, chkptredo);
+
+ if (showprogress)
+ pg_log_info("syncing target data directory");
+ sync_target_dir();
+
+ /* Also update the standby configuration, if requested. */
+ if (writerecoveryconf && !dry_run)
+ WriteRecoveryConfig(conn, datadir_target,
+ GenerateRecoveryConfig(conn, NULL));
+
+ /* don't need the source connection anymore */
+ source->destroy(source);
+ if (conn)
+ {
+ PQfinish(conn);
+ conn = NULL;
+ }
+
+ pg_log_info("Done!");
+
+ return 0;
+}
+
+/*
+ * Perform the rewind.
+ *
+ * We have already collected all the information we need from the
+ * target and the source.
+ */
+static void
+perform_rewind(filemap_t *filemap, rewind_source *source,
+ XLogRecPtr chkptrec,
+ TimeLineID chkpttli,
+ XLogRecPtr chkptredo)
+{
+ XLogRecPtr endrec;
+ TimeLineID endtli;
+ ControlFileData ControlFile_new;
+ size_t size;
+ char *buffer;
+
+ /*
+ * Execute the actions in the file map, fetching data from the source
+ * system as needed.
+ */
+ for (int i = 0; i < filemap->nentries; i++)
+ {
+ file_entry_t *entry = filemap->entries[i];
+
+ /*
+ * If this is a relation file, copy the modified blocks.
+ *
+ * This is in addition to any other changes.
+ */
+ if (entry->target_pages_to_overwrite.bitmapsize > 0)
+ {
+ datapagemap_iterator_t *iter;
+ BlockNumber blkno;
+ off_t offset;
+
+ iter = datapagemap_iterate(&entry->target_pages_to_overwrite);
+ while (datapagemap_next(iter, &blkno))
+ {
+ offset = blkno * BLCKSZ;
+ source->queue_fetch_range(source, entry->path, offset, BLCKSZ);
+ }
+ pg_free(iter);
+ }
+
+ switch (entry->action)
+ {
+ case FILE_ACTION_NONE:
+ /* nothing else to do */
+ break;
+
+ case FILE_ACTION_COPY:
+ /* Truncate the old file out of the way, if any */
+ open_target_file(entry->path, true);
+ source->queue_fetch_range(source, entry->path,
+ 0, entry->source_size);
+ break;
+
+ case FILE_ACTION_TRUNCATE:
+ truncate_target_file(entry->path, entry->source_size);
+ break;
+
+ case FILE_ACTION_COPY_TAIL:
+ source->queue_fetch_range(source, entry->path,
+ entry->target_size,
+ entry->source_size - entry->target_size);
+ break;
+
+ case FILE_ACTION_REMOVE:
+ remove_target(entry);
+ break;
+
+ case FILE_ACTION_CREATE:
+ create_target(entry);
+ break;
+
+ case FILE_ACTION_UNDECIDED:
+ pg_fatal("no action decided for file \"%s\"", entry->path);
+ break;
+ }
+ }
+
+ /* Complete any remaining range-fetches that we queued up above. */
+ source->finish_fetch(source);
+
+ close_target_file();
+
+ progress_report(true);
+
+ /*
+ * Fetch the control file from the source last. This ensures that the
+ * minRecoveryPoint is up-to-date.
+ */
+ buffer = source->fetch_file(source, "global/pg_control", &size);
+ digestControlFile(&ControlFile_source_after, buffer, size);
+ pg_free(buffer);
+
+ /*
+ * Sanity check: If the source is a local system, the control file should
+ * not have changed since we started.
+ *
+ * XXX: We assume it hasn't been modified, but actually, what could go
+ * wrong? The logic handles a libpq source that's modified concurrently,
+ * why not a local datadir?
+ */
+ if (datadir_source &&
+ memcmp(&ControlFile_source, &ControlFile_source_after,
+ sizeof(ControlFileData)) != 0)
+ {
+ pg_fatal("source system was modified while pg_rewind was running");
+ }
+
+ if (showprogress)
+ pg_log_info("creating backup label and updating control file");
+
+ /*
+ * Create a backup label file, to tell the target where to begin the WAL
+ * replay. Normally, from the last common checkpoint between the source
+ * and the target. But if the source is a standby server, it's possible
+ * that the last common checkpoint is *after* the standby's restartpoint.
+ * That implies that the source server has applied the checkpoint record,
+ * but hasn't performed a corresponding restartpoint yet. Make sure we
+ * start at the restartpoint's redo point in that case.
+ *
+ * Use the old version of the source's control file for this. The server
+ * might have finished the restartpoint after we started copying files,
+ * but we must begin from the redo point at the time that started copying.
+ */
+ if (ControlFile_source.checkPointCopy.redo < chkptredo)
+ {
+ chkptredo = ControlFile_source.checkPointCopy.redo;
+ chkpttli = ControlFile_source.checkPointCopy.ThisTimeLineID;
+ chkptrec = ControlFile_source.checkPoint;
+ }
+ createBackupLabel(chkptredo, chkpttli, chkptrec);
+
+ /*
+ * Update control file of target, to tell the target how far it must
+ * replay the WAL (minRecoveryPoint).
+ */
+ if (connstr_source)
+ {
+ /*
+ * The source is a live server. Like in an online backup, it's
+ * important that we recover all the WAL that was generated while we
+ * were copying files.
+ */
+ if (ControlFile_source_after.state == DB_IN_ARCHIVE_RECOVERY)
+ {
+ /*
+ * Source is a standby server. We must replay to its
+ * minRecoveryPoint.
+ */
+ endrec = ControlFile_source_after.minRecoveryPoint;
+ endtli = ControlFile_source_after.minRecoveryPointTLI;
+ }
+ else
+ {
+ /*
+ * Source is a production, non-standby, server. We must replay to
+ * the last WAL insert location.
+ */
+ if (ControlFile_source_after.state != DB_IN_PRODUCTION)
+ pg_fatal("source system was in unexpected state at end of rewind");
+
+ endrec = source->get_current_wal_insert_lsn(source);
+ endtli = ControlFile_source_after.checkPointCopy.ThisTimeLineID;
+ }
+ }
+ else
+ {
+ /*
+ * Source is a local data directory. It should've shut down cleanly,
+ * and we must replay to the latest shutdown checkpoint.
+ */
+ endrec = ControlFile_source_after.checkPoint;
+ endtli = ControlFile_source_after.checkPointCopy.ThisTimeLineID;
+ }
+
+ memcpy(&ControlFile_new, &ControlFile_source_after, sizeof(ControlFileData));
+ ControlFile_new.minRecoveryPoint = endrec;
+ ControlFile_new.minRecoveryPointTLI = endtli;
+ ControlFile_new.state = DB_IN_ARCHIVE_RECOVERY;
+ if (!dry_run)
+ update_controlfile(datadir_target, &ControlFile_new, do_sync);
+}
+
+static void
+sanityChecks(void)
+{
+ /* TODO Check that there's no backup_label in either cluster */
+
+ /* Check system_identifier match */
+ if (ControlFile_target.system_identifier != ControlFile_source.system_identifier)
+ pg_fatal("source and target clusters are from different systems");
+
+ /* check version */
+ if (ControlFile_target.pg_control_version != PG_CONTROL_VERSION ||
+ ControlFile_source.pg_control_version != PG_CONTROL_VERSION ||
+ ControlFile_target.catalog_version_no != CATALOG_VERSION_NO ||
+ ControlFile_source.catalog_version_no != CATALOG_VERSION_NO)
+ {
+ pg_fatal("clusters are not compatible with this version of pg_rewind");
+ }
+
+ /*
+ * Target cluster need to use checksums or hint bit wal-logging, this to
+ * prevent from data corruption that could occur because of hint bits.
+ */
+ if (ControlFile_target.data_checksum_version != PG_DATA_CHECKSUM_VERSION &&
+ !ControlFile_target.wal_log_hints)
+ {
+ pg_fatal("target server needs to use either data checksums or \"wal_log_hints = on\"");
+ }
+
+ /*
+ * Target cluster better not be running. This doesn't guard against
+ * someone starting the cluster concurrently. Also, this is probably more
+ * strict than necessary; it's OK if the target node was not shut down
+ * cleanly, as long as it isn't running at the moment.
+ */
+ if (ControlFile_target.state != DB_SHUTDOWNED &&
+ ControlFile_target.state != DB_SHUTDOWNED_IN_RECOVERY)
+ pg_fatal("target server must be shut down cleanly");
+
+ /*
+ * 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, but better safe than sorry.
+ */
+ if (datadir_source &&
+ ControlFile_source.state != DB_SHUTDOWNED &&
+ ControlFile_source.state != DB_SHUTDOWNED_IN_RECOVERY)
+ pg_fatal("source data directory must be shut down cleanly");
+}
+
+/*
+ * Print a progress report based on the fetch_size and fetch_done variables.
+ *
+ * Progress report is written at maximum once per second, except that the
+ * last progress report is always printed.
+ *
+ * If finished is set to true, this is the last progress report. The cursor
+ * is moved to the next line.
+ */
+void
+progress_report(bool finished)
+{
+ static pg_time_t last_progress_report = 0;
+ int percent;
+ char fetch_done_str[32];
+ char fetch_size_str[32];
+ pg_time_t now;
+
+ if (!showprogress)
+ return;
+
+ now = time(NULL);
+ if (now == last_progress_report && !finished)
+ return; /* Max once per second */
+
+ last_progress_report = now;
+ percent = fetch_size ? (int) ((fetch_done) * 100 / fetch_size) : 0;
+
+ /*
+ * Avoid overflowing past 100% or the full size. This may make the total
+ * size number change as we approach the end of the backup (the estimate
+ * will always be wrong if WAL is included), but that's better than having
+ * the done column be bigger than the total.
+ */
+ if (percent > 100)
+ percent = 100;
+ if (fetch_done > fetch_size)
+ fetch_size = fetch_done;
+
+ /*
+ * Separate step to keep platform-dependent format code out of
+ * translatable strings. And we only test for INT64_FORMAT availability
+ * in snprintf, not fprintf.
+ */
+ snprintf(fetch_done_str, sizeof(fetch_done_str), INT64_FORMAT,
+ fetch_done / 1024);
+ snprintf(fetch_size_str, sizeof(fetch_size_str), INT64_FORMAT,
+ fetch_size / 1024);
+
+ fprintf(stderr, _("%*s/%s kB (%d%%) copied"),
+ (int) strlen(fetch_size_str), fetch_done_str, fetch_size_str,
+ percent);
+
+ /*
+ * Stay on the same line if reporting to a terminal and we're not done
+ * yet.
+ */
+ fputc((!finished && isatty(fileno(stderr))) ? '\r' : '\n', stderr);
+}
+
+/*
+ * Find minimum from two WAL locations assuming InvalidXLogRecPtr means
+ * infinity as src/include/access/timeline.h states. This routine should
+ * be used only when comparing WAL locations related to history files.
+ */
+static XLogRecPtr
+MinXLogRecPtr(XLogRecPtr a, XLogRecPtr b)
+{
+ if (XLogRecPtrIsInvalid(a))
+ return b;
+ else if (XLogRecPtrIsInvalid(b))
+ return a;
+ else
+ return Min(a, b);
+}
+
+/*
+ * Retrieve timeline history for given control file which should behold
+ * either source or target.
+ */
+static TimeLineHistoryEntry *
+getTimelineHistory(ControlFileData *controlFile, int *nentries)
+{
+ TimeLineHistoryEntry *history;
+ TimeLineID tli;
+
+ tli = controlFile->checkPointCopy.ThisTimeLineID;
+
+ /*
+ * Timeline 1 does not have a history file, so there is no need to check
+ * and fake an entry with infinite start and end positions.
+ */
+ if (tli == 1)
+ {
+ history = (TimeLineHistoryEntry *) pg_malloc(sizeof(TimeLineHistoryEntry));
+ history->tli = tli;
+ history->begin = history->end = InvalidXLogRecPtr;
+ *nentries = 1;
+ }
+ else
+ {
+ char path[MAXPGPATH];
+ char *histfile;
+
+ TLHistoryFilePath(path, tli);
+
+ /* Get history file from appropriate source */
+ if (controlFile == &ControlFile_source)
+ histfile = source->fetch_file(source, path, NULL);
+ else if (controlFile == &ControlFile_target)
+ histfile = slurpFile(datadir_target, path, NULL);
+ else
+ pg_fatal("invalid control file");
+
+ history = rewind_parseTimeLineHistory(histfile, tli, nentries);
+ pg_free(histfile);
+ }
+
+ if (debug)
+ {
+ int i;
+
+ if (controlFile == &ControlFile_source)
+ pg_log_debug("Source timeline history:");
+ else if (controlFile == &ControlFile_target)
+ pg_log_debug("Target timeline history:");
+ else
+ Assert(false);
+
+ /*
+ * Print the target timeline history.
+ */
+ for (i = 0; i < targetNentries; i++)
+ {
+ TimeLineHistoryEntry *entry;
+
+ entry = &history[i];
+ pg_log_debug("%u: %X/%X - %X/%X", entry->tli,
+ LSN_FORMAT_ARGS(entry->begin),
+ LSN_FORMAT_ARGS(entry->end));
+ }
+ }
+
+ return history;
+}
+
+/*
+ * Determine the TLI of the last common timeline in the timeline history of the
+ * two clusters. targetHistory is filled with target timeline history and
+ * targetNentries is number of items in targetHistory. *tliIndex is set to the
+ * index of last common timeline in targetHistory array, and *recptr is set to
+ * the position where the timeline history diverged (ie. the first WAL record
+ * that's not the same in both clusters).
+ *
+ * Control files of both clusters must be read into ControlFile_target/source
+ * before calling this routine.
+ */
+static void
+findCommonAncestorTimeline(XLogRecPtr *recptr, int *tliIndex)
+{
+ TimeLineHistoryEntry *sourceHistory;
+ int sourceNentries;
+ int i,
+ n;
+
+ /* Retrieve timelines for both source and target */
+ sourceHistory = getTimelineHistory(&ControlFile_source, &sourceNentries);
+ targetHistory = getTimelineHistory(&ControlFile_target, &targetNentries);
+
+ /*
+ * Trace the history forward, until we hit the timeline diverge. It may
+ * still be possible that the source and target nodes used the same
+ * timeline number in their history but with different start position
+ * depending on the history files that each node has fetched in previous
+ * recovery processes. Hence check the start position of the new timeline
+ * as well and move down by one extra timeline entry if they do not match.
+ */
+ n = Min(sourceNentries, targetNentries);
+ for (i = 0; i < n; i++)
+ {
+ if (sourceHistory[i].tli != targetHistory[i].tli ||
+ sourceHistory[i].begin != targetHistory[i].begin)
+ break;
+ }
+
+ if (i > 0)
+ {
+ i--;
+ *recptr = MinXLogRecPtr(sourceHistory[i].end, targetHistory[i].end);
+ *tliIndex = i;
+
+ pg_free(sourceHistory);
+ return;
+ }
+ else
+ {
+ pg_fatal("could not find common ancestor of the source and target cluster's timelines");
+ }
+}
+
+
+/*
+ * Create a backup_label file that forces recovery to begin at the last common
+ * checkpoint.
+ */
+static void
+createBackupLabel(XLogRecPtr startpoint, TimeLineID starttli, XLogRecPtr checkpointloc)
+{
+ XLogSegNo startsegno;
+ time_t stamp_time;
+ char strfbuf[128];
+ char xlogfilename[MAXFNAMELEN];
+ struct tm *tmp;
+ char buf[1000];
+ int len;
+
+ XLByteToSeg(startpoint, startsegno, WalSegSz);
+ XLogFileName(xlogfilename, starttli, startsegno, WalSegSz);
+
+ /*
+ * Construct backup label file
+ */
+ stamp_time = time(NULL);
+ tmp = localtime(&stamp_time);
+ strftime(strfbuf, sizeof(strfbuf), "%Y-%m-%d %H:%M:%S %Z", tmp);
+
+ len = snprintf(buf, sizeof(buf),
+ "START WAL LOCATION: %X/%X (file %s)\n"
+ "CHECKPOINT LOCATION: %X/%X\n"
+ "BACKUP METHOD: pg_rewind\n"
+ "BACKUP FROM: standby\n"
+ "START TIME: %s\n",
+ /* omit LABEL: line */
+ LSN_FORMAT_ARGS(startpoint), xlogfilename,
+ LSN_FORMAT_ARGS(checkpointloc),
+ strfbuf);
+ if (len >= sizeof(buf))
+ pg_fatal("backup label buffer too small"); /* shouldn't happen */
+
+ /* TODO: move old file out of the way, if any. */
+ open_target_file("backup_label", true); /* BACKUP_LABEL_FILE */
+ write_target_range(buf, 0, len);
+ close_target_file();
+}
+
+/*
+ * Check CRC of control file
+ */
+static void
+checkControlFile(ControlFileData *ControlFile)
+{
+ pg_crc32c crc;
+
+ /* Calculate CRC */
+ INIT_CRC32C(crc);
+ COMP_CRC32C(crc, (char *) ControlFile, offsetof(ControlFileData, crc));
+ FIN_CRC32C(crc);
+
+ /* And simply compare it */
+ if (!EQ_CRC32C(crc, ControlFile->crc))
+ pg_fatal("unexpected control file CRC");
+}
+
+/*
+ * Verify control file contents in the buffer 'content', and copy it to
+ * *ControlFile.
+ */
+static void
+digestControlFile(ControlFileData *ControlFile, const char *content,
+ size_t size)
+{
+ if (size != PG_CONTROL_FILE_SIZE)
+ pg_fatal("unexpected control file size %d, expected %d",
+ (int) size, PG_CONTROL_FILE_SIZE);
+
+ memcpy(ControlFile, content, sizeof(ControlFileData));
+
+ /* set and validate WalSegSz */
+ WalSegSz = ControlFile->xlog_seg_size;
+
+ if (!IsValidWalSegSize(WalSegSz))
+ pg_fatal(ngettext("WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d byte",
+ "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d bytes",
+ WalSegSz),
+ WalSegSz);
+
+ /* Additional checks on control file */
+ checkControlFile(ControlFile);
+}
+
+/*
+ * Get value of GUC parameter restore_command from the target cluster.
+ *
+ * This uses a logic based on "postgres -C" to get the value from the
+ * cluster.
+ */
+static void
+getRestoreCommand(const char *argv0)
+{
+ int rc;
+ char postgres_exec_path[MAXPGPATH],
+ postgres_cmd[MAXPGPATH],
+ cmd_output[MAXPGPATH];
+
+ if (!restore_wal)
+ return;
+
+ /* find postgres executable */
+ rc = find_other_exec(argv0, "postgres",
+ PG_BACKEND_VERSIONSTR,
+ postgres_exec_path);
+
+ if (rc < 0)
+ {
+ char full_path[MAXPGPATH];
+
+ if (find_my_exec(argv0, full_path) < 0)
+ strlcpy(full_path, progname, sizeof(full_path));
+
+ if (rc == -1)
+ pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
+ "same directory as \"%s\".\n"
+ "Check your installation.",
+ "postgres", progname, full_path);
+ else
+ pg_log_error("The program \"%s\" was found by \"%s\"\n"
+ "but was not the same version as %s.\n"
+ "Check your installation.",
+ "postgres", full_path, progname);
+ exit(1);
+ }
+
+ /*
+ * Build a command able to retrieve the value of GUC parameter
+ * restore_command, if set.
+ */
+ snprintf(postgres_cmd, sizeof(postgres_cmd),
+ "\"%s\" -D \"%s\" -C restore_command",
+ postgres_exec_path, datadir_target);
+
+ if (!pipe_read_line(postgres_cmd, cmd_output, sizeof(cmd_output)))
+ exit(1);
+
+ (void) pg_strip_crlf(cmd_output);
+
+ if (strcmp(cmd_output, "") == 0)
+ pg_fatal("restore_command is not set in the target cluster");
+
+ restore_command = pg_strdup(cmd_output);
+
+ pg_log_debug("using for rewind restore_command = \'%s\'",
+ restore_command);
+}
+
+
+/*
+ * Ensure clean shutdown of target instance by launching single-user mode
+ * postgres to do crash recovery.
+ */
+static void
+ensureCleanShutdown(const char *argv0)
+{
+ int ret;
+#define MAXCMDLEN (2 * MAXPGPATH)
+ char exec_path[MAXPGPATH];
+ char cmd[MAXCMDLEN];
+
+ /* locate postgres binary */
+ if ((ret = find_other_exec(argv0, "postgres",
+ PG_BACKEND_VERSIONSTR,
+ exec_path)) < 0)
+ {
+ char full_path[MAXPGPATH];
+
+ if (find_my_exec(argv0, full_path) < 0)
+ strlcpy(full_path, progname, sizeof(full_path));
+
+ if (ret == -1)
+ pg_fatal("The program \"%s\" is needed by %s but was not found in the\n"
+ "same directory as \"%s\".\n"
+ "Check your installation.",
+ "postgres", progname, full_path);
+ else
+ pg_fatal("The program \"%s\" was found by \"%s\"\n"
+ "but was not the same version as %s.\n"
+ "Check your installation.",
+ "postgres", full_path, progname);
+ }
+
+ pg_log_info("executing \"%s\" for target server to complete crash recovery",
+ exec_path);
+
+ /*
+ * Skip processing if requested, but only after ensuring presence of
+ * postgres.
+ */
+ if (dry_run)
+ return;
+
+ /*
+ * Finally run postgres in single-user mode. There is no need to use
+ * fsync here. This makes the recovery faster, and the target data folder
+ * is synced at the end anyway.
+ */
+ snprintf(cmd, MAXCMDLEN, "\"%s\" --single -F -D \"%s\" template1 < \"%s\"",
+ exec_path, datadir_target, DEVNULL);
+
+ if (system(cmd) != 0)
+ {
+ pg_log_error("postgres single-user mode in target cluster failed");
+ pg_fatal("Command was: %s", cmd);
+ }
+}
+
+static void
+disconnect_atexit(void)
+{
+ if (conn != NULL)
+ PQfinish(conn);
+}
diff --git a/src/bin/pg_rewind/pg_rewind.h b/src/bin/pg_rewind/pg_rewind.h
new file mode 100644
index 0000000..d38635a
--- /dev/null
+++ b/src/bin/pg_rewind/pg_rewind.h
@@ -0,0 +1,59 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_rewind.h
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_REWIND_H
+#define PG_REWIND_H
+
+#include "access/timeline.h"
+#include "common/logging.h"
+#include "datapagemap.h"
+#include "libpq-fe.h"
+#include "storage/block.h"
+#include "storage/relfilenode.h"
+
+/* Configuration options */
+extern char *datadir_target;
+extern bool showprogress;
+extern bool dry_run;
+extern bool do_sync;
+extern int WalSegSz;
+
+/* Target history */
+extern TimeLineHistoryEntry *targetHistory;
+extern int targetNentries;
+
+/* Progress counters */
+extern uint64 fetch_size;
+extern uint64 fetch_done;
+
+/* logging support */
+#define pg_fatal(...) do { pg_log_fatal(__VA_ARGS__); exit(1); } while(0)
+
+/* in parsexlog.c */
+extern void extractPageMap(const char *datadir, XLogRecPtr startpoint,
+ int tliIndex, XLogRecPtr endpoint,
+ const char *restoreCommand);
+extern void findLastCheckpoint(const char *datadir, XLogRecPtr searchptr,
+ int tliIndex,
+ XLogRecPtr *lastchkptrec, TimeLineID *lastchkpttli,
+ XLogRecPtr *lastchkptredo,
+ const char *restoreCommand);
+extern XLogRecPtr readOneRecord(const char *datadir, XLogRecPtr ptr,
+ int tliIndex, const char *restoreCommand);
+
+/* in pg_rewind.c */
+extern void progress_report(bool finished);
+
+/* in timeline.c */
+extern TimeLineHistoryEntry *rewind_parseTimeLineHistory(char *buffer,
+ TimeLineID targetTLI,
+ int *nentries);
+
+#endif /* PG_REWIND_H */
diff --git a/src/bin/pg_rewind/po/cs.po b/src/bin/pg_rewind/po/cs.po
new file mode 100644
index 0000000..1f4a8eb
--- /dev/null
+++ b/src/bin/pg_rewind/po/cs.po
@@ -0,0 +1,1019 @@
+# LANGUAGE message translation file for pg_rewind
+# Copyright (C) 2018 PostgreSQL Global Development Group
+# This file is distributed under the same license as the pg_rewind (PostgreSQL) package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_rewind (PostgreSQL) 11\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2020-10-31 16:16+0000\n"
+"PO-Revision-Date: 2020-10-31 21:24+0100\n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"Language: cs\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"X-Generator: Poedit 2.4.1\n"
+"X-Poedit-Bookmarks: -1,-1,-1,-1,-1,-1,-1,-1,-1,17\n"
+
+#: ../../../src/common/logging.c:236
+#, c-format
+msgid "fatal: "
+msgstr "fatal: "
+
+#: ../../../src/common/logging.c:243
+#, c-format
+msgid "error: "
+msgstr "error: "
+
+#: ../../../src/common/logging.c:250
+#, c-format
+msgid "warning: "
+msgstr "warning: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "nedostatek paměti\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "nelze duplikovat null pointer (interní chyba)\n"
+
+#: ../../common/restricted_token.c:64
+#, c-format
+msgid "could not load library \"%s\": error code %lu"
+msgstr "nelze načíst knihovnu \"%s\": kód chyby %lu"
+
+#: ../../common/restricted_token.c:73
+#, c-format
+msgid "cannot create restricted tokens on this platform: error code %lu"
+msgstr "na této platformě nelze vytvářet vyhrazené tokeny: kód chyby %lu"
+
+#: ../../common/restricted_token.c:82
+#, c-format
+msgid "could not open process token: error code %lu"
+msgstr "nelze otevřít token procesu: chybový kód %lu"
+
+#: ../../common/restricted_token.c:97
+#, c-format
+msgid "could not allocate SIDs: error code %lu"
+msgstr "nelze alokovat SIDs: chybový kód %lu"
+
+#: ../../common/restricted_token.c:119
+#, c-format
+msgid "could not create restricted token: error code %lu"
+msgstr "nelze vytvořit vyhrazený token: chybový kód %lu"
+
+#: ../../common/restricted_token.c:140
+#, c-format
+msgid "could not start process for command \"%s\": error code %lu"
+msgstr "nelze nastartovat proces pro příkaz \"%s\": chybový kód %lu"
+
+#: ../../common/restricted_token.c:178
+#, c-format
+msgid "could not re-execute with restricted token: error code %lu"
+msgstr "nelze znovu spustit s vyhrazeným tokenem: chybový kód %lu"
+
+#: ../../common/restricted_token.c:194
+#, c-format
+msgid "could not get exit code from subprocess: error code %lu"
+msgstr "nelze získat návratový kód z podprovesu: chybový kód %lu"
+
+#: ../../fe_utils/archive.c:53
+#, c-format
+msgid "cannot use restore_command with %%r placeholder"
+msgstr "nelze použít restore_command se zástupnou hodnotou %%r"
+
+#: ../../fe_utils/archive.c:74
+#, c-format
+msgid "unexpected file size for \"%s\": %lu instead of %lu"
+msgstr "neočekávaná velikost souboru \"%s\": %lu namísto %lu"
+
+#: ../../fe_utils/archive.c:85
+#, c-format
+msgid "could not open file \"%s\" restored from archive: %m"
+msgstr "nelze otevřít soubor \"%s\" obnovený z archivu: %m"
+
+#: ../../fe_utils/archive.c:97 copy_fetch.c:88 filemap.c:208
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "nelze přistoupit k souboru \"%s\": %m"
+
+#: ../../fe_utils/archive.c:112
+#, c-format
+msgid "restore_command failed: %s"
+msgstr "restore_command selhal: %s"
+
+#: ../../fe_utils/archive.c:121
+#, c-format
+msgid "could not restore file \"%s\" from archive"
+msgstr "nelze obnovit soubor\"%s\" z archivu"
+
+#: ../../fe_utils/recovery_gen.c:35 ../../fe_utils/recovery_gen.c:49
+#: ../../fe_utils/recovery_gen.c:77 ../../fe_utils/recovery_gen.c:100
+#: ../../fe_utils/recovery_gen.c:171 parsexlog.c:73 parsexlog.c:125
+#: parsexlog.c:185
+#, c-format
+msgid "out of memory"
+msgstr "nedostatek paměti"
+
+#: ../../fe_utils/recovery_gen.c:134 parsexlog.c:298
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "nelze otevřít soubor \"%s\": %m"
+
+#: ../../fe_utils/recovery_gen.c:140
+#, c-format
+msgid "could not write to file \"%s\": %m"
+msgstr "nelze zapsat do souboru \"%s\": %m"
+
+#: ../../fe_utils/recovery_gen.c:152
+#, c-format
+msgid "could not create file \"%s\": %m"
+msgstr "nelze vytvořit soubor \"%s\": %m"
+
+#: copy_fetch.c:59
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "nelze otevřít adresář \"%s\": %m"
+
+#: copy_fetch.c:117
+#, c-format
+msgid "could not read symbolic link \"%s\": %m"
+msgstr "nelze přečíst symbolický odkaz \"%s\": %m"
+
+#: copy_fetch.c:120
+#, c-format
+msgid "symbolic link \"%s\" target is too long"
+msgstr "cíl symbolického odkazu \"%s\" je příliš dlouhý"
+
+#: copy_fetch.c:135
+#, c-format
+msgid "\"%s\" is a symbolic link, but symbolic links are not supported on this platform"
+msgstr "\"%s\" je symbolický odkaz, ale symbolické odkazy nejsou na této platformě podporovány"
+
+#: copy_fetch.c:142
+#, c-format
+msgid "could not read directory \"%s\": %m"
+msgstr "nelze číst z adresáře \"%s\": %m"
+
+#: copy_fetch.c:146
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "nelze uzavřít adresář \"%s\": %m"
+
+#: copy_fetch.c:166
+#, c-format
+msgid "could not open source file \"%s\": %m"
+msgstr "nelze otevřít zdrojový soubor \"%s\": %m"
+
+#: copy_fetch.c:170
+#, c-format
+msgid "could not seek in source file: %m"
+msgstr "nelze změnit pozici (seek) ve zdrojovém souboru: %m"
+
+#: copy_fetch.c:187 file_ops.c:311 parsexlog.c:336
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "nelze číst soubor \"%s\": %m"
+
+#: copy_fetch.c:190
+#, c-format
+msgid "unexpected EOF while reading file \"%s\""
+msgstr "neočekávaný znak EOF při čtení souboru \"%s\""
+
+#: copy_fetch.c:197
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "nelze uzavřít soubor \"%s\": %m"
+
+#: file_ops.c:62
+#, c-format
+msgid "could not open target file \"%s\": %m"
+msgstr "nelze otevřít cílový soubor \"%s\": %m"
+
+#: file_ops.c:76
+#, c-format
+msgid "could not close target file \"%s\": %m"
+msgstr "nelze uzavřít cílový soubor \"%s\": %m"
+
+#: file_ops.c:96
+#, c-format
+msgid "could not seek in target file \"%s\": %m"
+msgstr "nelze změnit pozici (seek) v cílovém souboru \"%s\": %m"
+
+#: file_ops.c:112
+#, c-format
+msgid "could not write file \"%s\": %m"
+msgstr "nelze zapsat soubor \"%s\": %m"
+
+#: file_ops.c:162
+#, c-format
+msgid "invalid action (CREATE) for regular file"
+msgstr "neplatná akce (CREATE) pro obyčejný soubor"
+
+#: file_ops.c:185
+#, c-format
+msgid "could not remove file \"%s\": %m"
+msgstr "nelze odstranit soubor \"%s\": %m"
+
+#: file_ops.c:203
+#, c-format
+msgid "could not open file \"%s\" for truncation: %m"
+msgstr "nelze otevřít soubor \"%s\" pro zkrácení (truncate): %m"
+
+#: file_ops.c:207
+#, c-format
+msgid "could not truncate file \"%s\" to %u: %m"
+msgstr "nelze zkrátit (truncate) soubor \"%s\" na %u: %m"
+
+#: file_ops.c:223
+#, c-format
+msgid "could not create directory \"%s\": %m"
+msgstr "nelze vytvořit adresář \"%s\": %m"
+
+#: file_ops.c:237
+#, c-format
+msgid "could not remove directory \"%s\": %m"
+msgstr "nelze odstranit adresář \"%s\": %m"
+
+#: file_ops.c:251
+#, c-format
+msgid "could not create symbolic link at \"%s\": %m"
+msgstr "nelze vytvořit symbolický odkaz na \"%s\": %m"
+
+#: file_ops.c:265
+#, c-format
+msgid "could not remove symbolic link \"%s\": %m"
+msgstr "nelze odstranit symbolický odkaz \"%s\": %m"
+
+#: file_ops.c:296 file_ops.c:300
+#, c-format
+msgid "could not open file \"%s\" for reading: %m"
+msgstr "nelze otevřít soubor \"%s\" pro čtení: %m"
+
+#: file_ops.c:314 parsexlog.c:338
+#, c-format
+msgid "could not read file \"%s\": read %d of %zu"
+msgstr "nelze číst soubor \"%s\": načteno %d z %zu"
+
+#: filemap.c:200
+#, c-format
+msgid "data file \"%s\" in source is not a regular file"
+msgstr "datový soubor \"%s\" ve zdroji není obyčejný soubor"
+
+#: filemap.c:222
+#, c-format
+msgid "\"%s\" is not a directory"
+msgstr "\"%s\" není adresář"
+
+#: filemap.c:245
+#, c-format
+msgid "\"%s\" is not a symbolic link"
+msgstr "\"%s\" není symbolický odkaz"
+
+#: filemap.c:257
+#, c-format
+msgid "\"%s\" is not a regular file"
+msgstr "\"%s\" není obyčejný soubor"
+
+#: filemap.c:369
+#, c-format
+msgid "source file list is empty"
+msgstr "seznam zdrojových souborů je prázdný"
+
+#: filemap.c:484
+#, c-format
+msgid "unexpected page modification for directory or symbolic link \"%s\""
+msgstr "neočekávaná modifikace stránky pro adresář nebo symbolický odkaz \"%s\""
+
+#: libpq_fetch.c:50
+#, c-format
+msgid "could not connect to server: %s"
+msgstr "nelze se připojit k serveru: %s"
+
+#: libpq_fetch.c:54
+#, c-format
+msgid "connected to server"
+msgstr "připojen k serveru"
+
+#: libpq_fetch.c:63
+#, c-format
+msgid "could not clear search_path: %s"
+msgstr "nelze vyčistit search_path: %s"
+
+#: libpq_fetch.c:75
+#, c-format
+msgid "source server must not be in recovery mode"
+msgstr "zdrojový server musí být v recovery módu"
+
+#: libpq_fetch.c:85
+#, c-format
+msgid "full_page_writes must be enabled in the source server"
+msgstr "full_page_writes musí být zapnuty na zdrojovém serveru"
+
+#: libpq_fetch.c:111
+#, c-format
+msgid "error running query (%s) on source server: %s"
+msgstr "chyba při spuštění dotazu (%s) na zdrojovém serveru: %s"
+
+#: libpq_fetch.c:116
+#, c-format
+msgid "unexpected result set from query"
+msgstr "neočekávaný výsledek dotazu"
+
+#: libpq_fetch.c:137
+#, c-format
+msgid "error running query (%s) in source server: %s"
+msgstr "chyba při spuštění dotazu (%s) na zdrojovém serveru: %s"
+
+#: libpq_fetch.c:157
+#, c-format
+msgid "unrecognized result \"%s\" for current WAL insert location"
+msgstr "nerozpoznaný výsledek \"%s\" pro aktuální WAL insert pozici"
+
+#: libpq_fetch.c:207
+#, c-format
+msgid "could not fetch file list: %s"
+msgstr "nelze načíst seznam souborů: %s"
+
+#: libpq_fetch.c:212
+#, c-format
+msgid "unexpected result set while fetching file list"
+msgstr "neočekávaný výsledek při načítání seznamu souborů"
+
+#: libpq_fetch.c:265
+#, c-format
+msgid "could not send query: %s"
+msgstr "nelze zaslat dotaz: %s"
+
+#: libpq_fetch.c:270
+#, c-format
+msgid "could not set libpq connection to single row mode"
+msgstr "nelze nastavit libpq spojení na single row mód"
+
+#: libpq_fetch.c:290
+#, c-format
+msgid "unexpected result while fetching remote files: %s"
+msgstr "neočekávaný výsledek při načítání vzdálených souborů: %s"
+
+#: libpq_fetch.c:296
+#, c-format
+msgid "unexpected result set size while fetching remote files"
+msgstr "neočekávaná velikost výsledku při načítání vzdálených souborů"
+
+#: libpq_fetch.c:302
+#, c-format
+msgid "unexpected data types in result set while fetching remote files: %u %u %u"
+msgstr "neočekávané datové typy ve vysledku při načítání vzdálených souborů: %u %u %u"
+
+#: libpq_fetch.c:310
+#, c-format
+msgid "unexpected result format while fetching remote files"
+msgstr "neočekávaný formát výsledku při načítání vzdálených souborů"
+
+#: libpq_fetch.c:316
+#, c-format
+msgid "unexpected null values in result while fetching remote files"
+msgstr "neočekávané null hodnoty ve výsledku při načítání vzdálených souborů"
+
+#: libpq_fetch.c:320
+#, c-format
+msgid "unexpected result length while fetching remote files"
+msgstr "neočekávaná délka výsledku při načítání vzdálených souborů"
+
+#: libpq_fetch.c:381
+#, c-format
+msgid "could not fetch remote file \"%s\": %s"
+msgstr "nelze načíst vzdálený soubor \"%s\": %s"
+
+#: libpq_fetch.c:386
+#, c-format
+msgid "unexpected result set while fetching remote file \"%s\""
+msgstr "neočekávaný výsledek při načítání vzdáleného souboru \"%s\""
+
+#: libpq_fetch.c:430
+#, c-format
+msgid "could not send COPY data: %s"
+msgstr "nelze poslat COPY data: %s"
+
+#: libpq_fetch.c:459
+#, c-format
+msgid "could not send file list: %s"
+msgstr "nelze poslat seznam souborů: %s"
+
+#: libpq_fetch.c:501
+#, c-format
+msgid "could not send end-of-COPY: %s"
+msgstr "nelze poslat end-of-COPY: %s"
+
+#: libpq_fetch.c:507
+#, c-format
+msgid "unexpected result while sending file list: %s"
+msgstr "neočekávaný výsledek při posílání seznamu souborů: %s"
+
+#: parsexlog.c:85 parsexlog.c:132
+#, c-format
+msgid "could not read WAL record at %X/%X: %s"
+msgstr "nelze načíst WAL záznam na %X/%X: %s"
+
+#: parsexlog.c:89 parsexlog.c:135
+#, c-format
+msgid "could not read WAL record at %X/%X"
+msgstr "nelze načíst WAL záznam na %X/%X"
+
+#: parsexlog.c:198
+#, c-format
+msgid "could not find previous WAL record at %X/%X: %s"
+msgstr "nelze nalézt předchozí WAL záznam na %X/%X: %s"
+
+#: parsexlog.c:202
+#, c-format
+msgid "could not find previous WAL record at %X/%X"
+msgstr "nelze načíst předchozí WAL záznam na %X/%X"
+
+#: parsexlog.c:327
+#, c-format
+msgid "could not seek in file \"%s\": %m"
+msgstr "nelze nastavit pozici (seek) v souboru \"%s\": %m"
+
+#: parsexlog.c:407
+#, c-format
+msgid "WAL record modifies a relation, but record type is not recognized: lsn: %X/%X, rmgr: %s, info: %02X"
+msgstr "WAL záznam modifikuje relaci, ale typ záznamu není rozpoznán: lsn: %X/%X, rmgr: %s, info: %02X"
+
+#: pg_rewind.c:78
+#, c-format
+msgid ""
+"%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n"
+"\n"
+msgstr ""
+"%s resynchronizuje PostgreSQL cluster s jinou kopií daného clusteru.\n"
+"\n"
+
+#: pg_rewind.c:79
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]...\n"
+"\n"
+msgstr ""
+"Použití:\n"
+" %s [OPTION]...\n"
+"\n"
+
+#: pg_rewind.c:80
+#, c-format
+msgid "Options:\n"
+msgstr "Přepínače:\n"
+
+#: pg_rewind.c:81
+#, c-format
+msgid ""
+" -c, --restore-target-wal use restore_command in target configuration to\n"
+" retrieve WAL files from archives\n"
+msgstr ""
+" -c, --restore-target-wal použij restore_command v cílové konfiguraci pro\n"
+" získání WAL souborů z archivu\n"
+
+#: pg_rewind.c:83
+#, c-format
+msgid " -D, --target-pgdata=DIRECTORY existing data directory to modify\n"
+msgstr " -D, --target-pgdata=ADRESÁŘ existující datový adresář pro modifikaci\n"
+
+#: pg_rewind.c:84
+#, c-format
+msgid " --source-pgdata=DIRECTORY source data directory to synchronize with\n"
+msgstr " --source-pgdata=ADRESÁŘ zdrojový datový adresář proti kterému se synchronizovat\n"
+
+#: pg_rewind.c:85
+#, c-format
+msgid " --source-server=CONNSTR source server to synchronize with\n"
+msgstr " --source-server=CONNSTR zdrojový server se kterým se synchronizovat\n"
+
+#: pg_rewind.c:86
+#, c-format
+msgid " -n, --dry-run stop before modifying anything\n"
+msgstr " -n, --dry-run zastavit před modifikací čehokoliv\n"
+
+#: pg_rewind.c:87
+#, c-format
+msgid ""
+" -N, --no-sync do not wait for changes to be written\n"
+" safely to disk\n"
+msgstr ""
+" -N, --no-sync nečekat na bezpečné zapsání změn na disk\n"
+"\n"
+
+#: pg_rewind.c:89
+#, c-format
+msgid " -P, --progress write progress messages\n"
+msgstr " -P, --progress průběžně vypisovat zprávy o postupu\n"
+
+#: pg_rewind.c:90
+#, c-format
+msgid ""
+" -R, --write-recovery-conf write configuration for replication\n"
+" (requires --source-server)\n"
+msgstr ""
+" -R, --write-recovery-conf zapíše konfiguraci pro replikaci\n"
+" (vyžaduje zadání --source-server)\n"
+"\n"
+
+#: pg_rewind.c:92
+#, c-format
+msgid " --debug write a lot of debug messages\n"
+msgstr " --debug vypisovat mnoho zpráv s debug informacemi\n"
+
+#: pg_rewind.c:93
+#, c-format
+msgid " --no-ensure-shutdown do not automatically fix unclean shutdown\n"
+msgstr " --no-ensure-shutdown neopravuj automaticky nečisté vypnutí databáze\n"
+
+#: pg_rewind.c:94
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version vypíše informaci o verzi, poté skončí\n"
+
+#: pg_rewind.c:95
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help vypíše tuto nápovědu, poté skončí\n"
+
+#: pg_rewind.c:96
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"Chyby oznamujte na <%s>.\n"
+
+#: pg_rewind.c:97
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "%s domácí stránka: <%s>\n"
+
+#: pg_rewind.c:159 pg_rewind.c:208 pg_rewind.c:215 pg_rewind.c:222
+#: pg_rewind.c:229 pg_rewind.c:237
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "Zkuste \"%s --help\" pro více informací.\n"
+
+#: pg_rewind.c:207
+#, c-format
+msgid "no source specified (--source-pgdata or --source-server)"
+msgstr "nespecifikován žádný zdroj (--source-pgdata nebo --source-server)"
+
+#: pg_rewind.c:214
+#, c-format
+msgid "only one of --source-pgdata or --source-server can be specified"
+msgstr "pouze jedna z voleb --source-pgdata nebo --source-server může být zadána"
+
+#: pg_rewind.c:221
+#, c-format
+msgid "no target data directory specified (--target-pgdata)"
+msgstr "cílový datový adresář nespecifikován (--target-pgdata)"
+
+#: pg_rewind.c:228
+#, c-format
+msgid "no source server information (--source-server) specified for --write-recovery-conf"
+msgstr ""
+
+#: pg_rewind.c:235
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "příliš mnoho argumentů v příkazové řádce (první je \"%s\")"
+
+#: pg_rewind.c:250
+#, c-format
+msgid "cannot be executed by \"root\""
+msgstr "nelze spouštět jako \"root\""
+
+#: pg_rewind.c:251
+#, c-format
+msgid "You must run %s as the PostgreSQL superuser.\n"
+msgstr "Musíte spustit %s jako PostgreSQL superuživatel.\n"
+
+#: pg_rewind.c:262
+#, c-format
+msgid "could not read permissions of directory \"%s\": %m"
+msgstr "nelze zjistit přístupová práva adresáře \"%s\": %m"
+
+#: pg_rewind.c:316
+#, c-format
+msgid "source and target cluster are on the same timeline"
+msgstr "zdrojový a cílový cluster jsou na stejné timeline"
+
+#: pg_rewind.c:322
+#, c-format
+msgid "servers diverged at WAL location %X/%X on timeline %u"
+msgstr "servery se rozešly na WAL pozici %X/%X na timeline %u"
+
+#: pg_rewind.c:360
+#, c-format
+msgid "no rewind required"
+msgstr "rewind není potřeba"
+
+#: pg_rewind.c:369
+#, c-format
+msgid "rewinding from last common checkpoint at %X/%X on timeline %u"
+msgstr "provádím rewind z posledního společného checkpointu na %X/%X na timeline %u"
+
+#: pg_rewind.c:378
+#, c-format
+msgid "reading source file list"
+msgstr "načítám seznam zdrojových souborů"
+
+#: pg_rewind.c:381
+#, c-format
+msgid "reading target file list"
+msgstr "načítám seznam cílových souborů"
+
+#: pg_rewind.c:392
+#, c-format
+msgid "reading WAL in target"
+msgstr "čtu WAL na cílovém clusteru"
+
+#: pg_rewind.c:409
+#, c-format
+msgid "need to copy %lu MB (total source directory size is %lu MB)"
+msgstr "je třeba zkopírovat %lu MB (celková velikost zdrojového adresáře je %lu MB)"
+
+#: pg_rewind.c:427
+#, c-format
+msgid "creating backup label and updating control file"
+msgstr "vytvářím backup label a aktualizuji control file"
+
+#: pg_rewind.c:457
+#, c-format
+msgid "syncing target data directory"
+msgstr "provádím sync cílového datového adresáře"
+
+#: pg_rewind.c:464
+#, c-format
+msgid "Done!"
+msgstr "Hotovo!"
+
+#: pg_rewind.c:476
+#, c-format
+msgid "source and target clusters are from different systems"
+msgstr "zdrojový a cílový cluster jsou z různých systémů"
+
+#: pg_rewind.c:484
+#, c-format
+msgid "clusters are not compatible with this version of pg_rewind"
+msgstr "clustery nejsou kompatibilní s touto verzí pg_rewind"
+
+#: pg_rewind.c:494
+#, c-format
+msgid "target server needs to use either data checksums or \"wal_log_hints = on\""
+msgstr "cílový server musí používat buď data checksums nebo \"wal_log_hints = on\""
+
+#: pg_rewind.c:505
+#, c-format
+msgid "target server must be shut down cleanly"
+msgstr "cílový server musí být zastaven čistě"
+
+#: pg_rewind.c:515
+#, c-format
+msgid "source data directory must be shut down cleanly"
+msgstr "zdrojový datový adresář musí být zastaven čistě"
+
+#: pg_rewind.c:567
+#, c-format
+msgid "%*s/%s kB (%d%%) copied"
+msgstr "%*s/%s kB (%d%%) zkopírováno"
+
+#: pg_rewind.c:630
+#, c-format
+msgid "invalid control file"
+msgstr "neplatný control file"
+
+#: pg_rewind.c:714
+#, c-format
+msgid "could not find common ancestor of the source and target cluster's timelines"
+msgstr "nelze najít společného předka pro timeline ze zdrojového a cílového clusteru"
+
+#: pg_rewind.c:755
+#, c-format
+msgid "backup label buffer too small"
+msgstr "backup label buffer je příliš malý"
+
+#: pg_rewind.c:778
+#, c-format
+msgid "unexpected control file CRC"
+msgstr "neočekávaná CRC hodnota control file"
+
+#: pg_rewind.c:788
+#, c-format
+msgid "unexpected control file size %d, expected %d"
+msgstr "neočekávaná velikost control file %d, očekáváno %d"
+
+#: pg_rewind.c:797
+#, c-format
+msgid "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d byte"
+msgid_plural "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d bytes"
+msgstr[0] "Velikost WAL segmentu musí být mocnina dvou mezi 1 MB a 1 GB, ale control file udává %d byte"
+msgstr[1] "Velikost WAL segmentu musí být mocnina dvou mezi 1 MB a 1 GB, ale control file udává %d bytů"
+msgstr[2] "Velikost WAL segmentu musí být mocnina dvou mezi 1 MB a 1 GB, ale control file udává %d bytů"
+
+#: pg_rewind.c:854 pg_rewind.c:912
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"Program \"%s\" je vyžadován aplikací %s, ale nebyl nalezen ve stejném\n"
+"adresáři jako \"%s\".\n"
+"Zkontrolujte vaši instalaci."
+
+#: pg_rewind.c:859 pg_rewind.c:917
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"Program \"%s\" byl nalezen pomocí \"%s\",\n"
+"ale nebyl ve stejné verzi jako %s.\n"
+"Zkontrolujte vaši instalaci."
+
+#: pg_rewind.c:880
+#, c-format
+msgid "restore_command is not set in the target cluster"
+msgstr "restore_command není nastaven pro cílový cluster"
+
+#: pg_rewind.c:923
+#, c-format
+msgid "executing \"%s\" for target server to complete crash recovery"
+msgstr "spouštím \"%s\" na cílovém serveru pro dokončení crash recovery"
+
+#: pg_rewind.c:943
+#, c-format
+msgid "postgres single-user mode in target cluster failed"
+msgstr "postgres single-user mód v cílovém clusteru selhal"
+
+#: pg_rewind.c:944
+#, c-format
+msgid "Command was: %s"
+msgstr "Příkaz byl: %s"
+
+#: timeline.c:75 timeline.c:81
+#, c-format
+msgid "syntax error in history file: %s"
+msgstr "syntaktická chyba v souboru s historií: %s"
+
+#: timeline.c:76
+#, c-format
+msgid "Expected a numeric timeline ID."
+msgstr "Očekávána číselná hodnota timeline ID."
+
+#: timeline.c:82
+#, c-format
+msgid "Expected a write-ahead log switchpoint location."
+msgstr "Očekávána pozice pro switchpoint write-ahead logu."
+
+#: timeline.c:87
+#, c-format
+msgid "invalid data in history file: %s"
+msgstr "chybná data v souboru s historií: %s"
+
+#: timeline.c:88
+#, c-format
+msgid "Timeline IDs must be in increasing sequence."
+msgstr "Timeline IDs musí být rostoucí posloupnost."
+
+#: timeline.c:108
+#, c-format
+msgid "invalid data in history file"
+msgstr "chybná data v souboru s historií"
+
+#: timeline.c:109
+#, c-format
+msgid "Timeline IDs must be less than child timeline's ID."
+msgstr "Timeline IDs musí být nižší než timeline ID potomka."
+
+#: xlogreader.c:349
+#, c-format
+msgid "invalid record offset at %X/%X"
+msgstr "neplatný offset záznamu na %X/%X"
+
+#: xlogreader.c:357
+#, c-format
+msgid "contrecord is requested by %X/%X"
+msgstr "contrecord je vyžadován %X/%X"
+
+#: xlogreader.c:398 xlogreader.c:695
+#, c-format
+msgid "invalid record length at %X/%X: wanted %u, got %u"
+msgstr "neplatná délka záznamu na %X/%X: potřeba %u, získáno %u"
+
+#: xlogreader.c:422
+#, c-format
+msgid "record length %u at %X/%X too long"
+msgstr "délka záznamu %u na %X/%X je příliš vysoká"
+
+#: xlogreader.c:454
+#, c-format
+msgid "there is no contrecord flag at %X/%X"
+msgstr "na %X/%X není nastaven contrecord flag"
+
+#: xlogreader.c:467
+#, c-format
+msgid "invalid contrecord length %u at %X/%X"
+msgstr "chybná contrecord délka %u na %X/%X"
+
+#: xlogreader.c:703
+#, c-format
+msgid "invalid resource manager ID %u at %X/%X"
+msgstr "chybný ID resource managera %u na %X/%X"
+
+#: xlogreader.c:717 xlogreader.c:734
+#, c-format
+msgid "record with incorrect prev-link %X/%X at %X/%X"
+msgstr "záznam s neplatnou hodnotou prev-link %X/%X na %X/%X"
+
+#: xlogreader.c:771
+#, c-format
+msgid "incorrect resource manager data checksum in record at %X/%X"
+msgstr "neplatný data checksum resource managera v záznamu na %X/%X"
+
+#: xlogreader.c:808
+#, c-format
+msgid "invalid magic number %04X in log segment %s, offset %u"
+msgstr "neplatné magické číslo %04X v log segmentu %s, offset %u"
+
+#: xlogreader.c:822 xlogreader.c:863
+#, c-format
+msgid "invalid info bits %04X in log segment %s, offset %u"
+msgstr "neplatné info bity %04X v log segmentu %s, offset %u"
+
+#: xlogreader.c:837
+#, c-format
+msgid "WAL file is from different database system: WAL file database system identifier is %llu, pg_control database system identifier is %llu"
+msgstr "WAL soubor je z jiného databázového systému: systémový identifikátor z WAL souboru je %llu, systémový identifikátor z pg_control je %llu"
+
+#: xlogreader.c:845
+#, c-format
+msgid "WAL file is from different database system: incorrect segment size in page header"
+msgstr "WAL soubor je z jiného databázového systému: neplatná velikost segmentu v hlavičce stránky"
+
+#: xlogreader.c:851
+#, c-format
+msgid "WAL file is from different database system: incorrect XLOG_BLCKSZ in page header"
+msgstr "WAL soubor je z jiného databázového systému: neplatná hodnota XLOG_BLCKSZ v hlavičce stránky"
+
+#: xlogreader.c:882
+#, c-format
+msgid "unexpected pageaddr %X/%X in log segment %s, offset %u"
+msgstr "neočekávaná pageaddr hodnota %X/%X v log segmentu %s, offset %u"
+
+#: xlogreader.c:907
+#, c-format
+msgid "out-of-sequence timeline ID %u (after %u) in log segment %s, offset %u"
+msgstr "timeline ID %u mimo pořadí (po %u) v log segmentu %s, offset %u"
+
+#: xlogreader.c:1247
+#, c-format
+msgid "out-of-order block_id %u at %X/%X"
+msgstr "block_id %u mimo pořadí na %X/%X"
+
+#: xlogreader.c:1270
+#, c-format
+msgid "BKPBLOCK_HAS_DATA set, but no data included at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA flag nastaven, ale žádná data nejsou přiložena na %X/%X"
+
+#: xlogreader.c:1277
+#, c-format
+msgid "BKPBLOCK_HAS_DATA not set, but data length is %u at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA flag nenastaven, ale délka dat je %u na %X/%X"
+
+#: xlogreader.c:1313
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE set, but hole offset %u length %u block image length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE flag nastaven, ale hole offset %u length %u block image length %u na %X/%X"
+
+#: xlogreader.c:1329
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE not set, but hole offset %u length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE flag nenastaven, ale hole offset %u length %u na %X/%X"
+
+#: xlogreader.c:1344
+#, c-format
+msgid "BKPIMAGE_IS_COMPRESSED set, but block image length %u at %X/%X"
+msgstr "BKPIMAGE_IS_COMPRESSED flag nastaven, ale block image length %u na %X/%X"
+
+#: xlogreader.c:1359
+#, c-format
+msgid "neither BKPIMAGE_HAS_HOLE nor BKPIMAGE_IS_COMPRESSED set, but block image length is %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE ani BKPIMAGE_IS_COMPRESSED flag nenastaven, ale block image length je %u na %X/%X"
+
+#: xlogreader.c:1375
+#, c-format
+msgid "BKPBLOCK_SAME_REL set but no previous rel at %X/%X"
+msgstr "BKPBLOCK_SAME_REL flag nastaven, ale žádná předchozí rel hodnota na %X/%X"
+
+#: xlogreader.c:1387
+#, c-format
+msgid "invalid block_id %u at %X/%X"
+msgstr "neplatné block_id %u na %X/%X"
+
+#: xlogreader.c:1476
+#, c-format
+msgid "record with invalid length at %X/%X"
+msgstr "záznam s neplatnou délkou na %X/%X"
+
+#: xlogreader.c:1565
+#, c-format
+msgid "invalid compressed image at %X/%X, block %d"
+msgstr "neplatný komprimovaný image na %X/%X, block %d"
+
+#~ msgid "could not open directory \"%s\": %s\n"
+#~ msgstr "nelze otevřít adresář \"%s\": %s\n"
+
+#~ msgid "could not read file \"%s\": %s\n"
+#~ msgstr "nelze číst soubor \"%s\": %s\n"
+
+#~ msgid " block %u\n"
+#~ msgstr " blok %u\n"
+
+#~ msgid "entry \"%s\" excluded from %s file list\n"
+#~ msgstr "položka \"%s\" vyloučena ze %s seznamu souborů\n"
+
+#~ msgid "%s (%s)\n"
+#~ msgstr "%s (%s)\n"
+
+#~ msgid "could not set up connection context: %s"
+#~ msgstr "nelze nastavit kontext spojení: %s"
+
+#~ msgid "getting file chunks\n"
+#~ msgstr "načítám části souborů\n"
+
+#~ msgid "received null value for chunk for file \"%s\", file has been deleted\n"
+#~ msgstr "přijata null hodnota pro chunk souboru \"%s\", soubor byl smazán\n"
+
+#~ msgid "received chunk for file \"%s\", offset %s, size %d\n"
+#~ msgstr "přijat chunk souboru \"%s\", offset %s, délka %d\n"
+
+#~ msgid "fetched file \"%s\", length %d\n"
+#~ msgstr "načten soubor \"%s\", délka %d\n"
+
+#~ msgid "could not create temporary table: %s"
+#~ msgstr "nelze vytvořit temporary tabulku: %s"
+
+#~ msgid "Failure, exiting\n"
+#~ msgstr "Chyba, končím\n"
+
+#~ msgid "could not open file \"%s\": %s\n"
+#~ msgstr "nelze otevřít soubor \"%s\": %s\n"
+
+#~ msgid "could not read from file \"%s\": %s\n"
+#~ msgstr "nelze číst ze souboru \"%s\": %s\n"
+
+#~ msgid "%s: could not read permissions of directory \"%s\": %s\n"
+#~ msgstr "%s: nelze načíst práva adresáře \"%s\": %s\n"
+
+#~ msgid "Source timeline history:\n"
+#~ msgstr "Zdrojová timeline history:\n"
+
+#~ msgid "Target timeline history:\n"
+#~ msgstr "Cílová timeline history:\n"
+
+#~ msgid "%d: %X/%X - %X/%X\n"
+#~ msgstr "%d: %X/%X - %X/%X\n"
+
+#~ msgid ""
+#~ "The program \"initdb\" is needed by %s but was\n"
+#~ "not found in the same directory as \"%s\".\n"
+#~ "Check your installation.\n"
+#~ msgstr ""
+#~ "Program \"initdb\" je vyžadován %s ale nebyl\n"
+#~ "nalezen ve stejném adresáři jako \"%s\".\n"
+#~ "Zkontrolujte svou instalaci.\n"
+
+#~ msgid ""
+#~ "The program \"initdb\" was found by \"%s\"\n"
+#~ "but was not the same version as %s.\n"
+#~ "Check your installation.\n"
+#~ msgstr ""
+#~ "Program \"initdb\" byl nalezen \"%s\"\n"
+#~ "ale nemá stejnou verzi jako \"%s\".\n"
+#~ "Zkontrolujte svou instalaci.\n"
+
+#~ msgid "sync of target directory failed\n"
+#~ msgstr "sync na cílovém adresáři selhal\n"
+
+#~ msgid ""
+#~ "\n"
+#~ "Report bugs to <pgsql-bugs@lists.postgresql.org>.\n"
+#~ msgstr ""
+#~ "\n"
+#~ "Chyby hlaste na adresu <pgsql-bugs@postgresql.org>.\n"
diff --git a/src/bin/pg_rewind/po/de.po b/src/bin/pg_rewind/po/de.po
new file mode 100644
index 0000000..9938775
--- /dev/null
+++ b/src/bin/pg_rewind/po/de.po
@@ -0,0 +1,964 @@
+# German message translation file for pg_rewind
+# Copyright (C) 2015-2022 PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_rewind (PostgreSQL) 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-03-29 14:49+0000\n"
+"PO-Revision-Date: 2022-03-29 22:48+0200\n"
+"Last-Translator: Peter Eisentraut <peter@eisentraut.org>\n"
+"Language-Team: German <pgsql-translators@postgresql.org>\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "Fatal: "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "Fehler: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "Warnung: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "Speicher aufgebraucht\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "kann NULL-Zeiger nicht kopieren (interner Fehler)\n"
+
+#: ../../common/restricted_token.c:64
+#, c-format
+msgid "could not load library \"%s\": error code %lu"
+msgstr "konnte Bibliothek »%s« nicht laden: Fehlercode %lu"
+
+#: ../../common/restricted_token.c:73
+#, c-format
+msgid "cannot create restricted tokens on this platform: error code %lu"
+msgstr "auf dieser Plattform können keine beschränkten Token erzeugt werden: Fehlercode %lu"
+
+#: ../../common/restricted_token.c:82
+#, c-format
+msgid "could not open process token: error code %lu"
+msgstr "konnte Prozess-Token nicht öffnen: Fehlercode %lu"
+
+#: ../../common/restricted_token.c:97
+#, c-format
+msgid "could not allocate SIDs: error code %lu"
+msgstr "konnte SIDs nicht erzeugen: Fehlercode %lu"
+
+#: ../../common/restricted_token.c:119
+#, c-format
+msgid "could not create restricted token: error code %lu"
+msgstr "konnte beschränktes Token nicht erzeugen: Fehlercode %lu"
+
+#: ../../common/restricted_token.c:140
+#, c-format
+msgid "could not start process for command \"%s\": error code %lu"
+msgstr "konnte Prozess für Befehl »%s« nicht starten: Fehlercode %lu"
+
+#: ../../common/restricted_token.c:178
+#, c-format
+msgid "could not re-execute with restricted token: error code %lu"
+msgstr "konnte Prozess nicht mit beschränktem Token neu starten: Fehlercode %lu"
+
+#: ../../common/restricted_token.c:194
+#, c-format
+msgid "could not get exit code from subprocess: error code %lu"
+msgstr "konnte Statuscode des Subprozesses nicht ermitteln: Fehlercode %lu"
+
+#: ../../fe_utils/archive.c:53
+#, c-format
+msgid "cannot use restore_command with %%r placeholder"
+msgstr "kann restore_command mit Platzhalter %%r nicht verwenden"
+
+#: ../../fe_utils/archive.c:74
+#, c-format
+msgid "unexpected file size for \"%s\": %lld instead of %lld"
+msgstr "unerwartete Dateigröße für »%s«: %lld statt %lld"
+
+#: ../../fe_utils/archive.c:85
+#, c-format
+msgid "could not open file \"%s\" restored from archive: %m"
+msgstr "konnte aus dem Archiv wiederhergestellte Datei »%s« nicht öffnen: %m"
+
+#: ../../fe_utils/archive.c:97 file_ops.c:417
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "konnte »stat« für Datei »%s« nicht ausführen: %m"
+
+#: ../../fe_utils/archive.c:112
+#, c-format
+msgid "restore_command failed: %s"
+msgstr "restore_command fehlgeschlagen: %s"
+
+#: ../../fe_utils/archive.c:121
+#, c-format
+msgid "could not restore file \"%s\" from archive"
+msgstr "konnte Datei »%s« nicht aus Archiv wiederherstellen"
+
+#: ../../fe_utils/recovery_gen.c:35 ../../fe_utils/recovery_gen.c:49
+#: ../../fe_utils/recovery_gen.c:77 ../../fe_utils/recovery_gen.c:100
+#: ../../fe_utils/recovery_gen.c:171 parsexlog.c:77 parsexlog.c:137
+#: parsexlog.c:197
+#, c-format
+msgid "out of memory"
+msgstr "Speicher aufgebraucht"
+
+#: ../../fe_utils/recovery_gen.c:134 parsexlog.c:310
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "konnte Datei »%s« nicht öffnen: %m"
+
+#: ../../fe_utils/recovery_gen.c:140
+#, c-format
+msgid "could not write to file \"%s\": %m"
+msgstr "konnte nicht in Datei »%s« schreiben: %m"
+
+#: ../../fe_utils/recovery_gen.c:152
+#, c-format
+msgid "could not create file \"%s\": %m"
+msgstr "konnte Datei »%s« nicht erstellen: %m"
+
+#: file_ops.c:67
+#, c-format
+msgid "could not open target file \"%s\": %m"
+msgstr "konnte Zieldatei »%s« nicht öffnen: %m"
+
+#: file_ops.c:81
+#, c-format
+msgid "could not close target file \"%s\": %m"
+msgstr "konnte Zieldatei »%s« nicht schließen: %m"
+
+#: file_ops.c:101
+#, c-format
+msgid "could not seek in target file \"%s\": %m"
+msgstr "konnte Positionszeiger in Zieldatei »%s« nicht setzen: %m"
+
+#: file_ops.c:117
+#, c-format
+msgid "could not write file \"%s\": %m"
+msgstr "konnte Datei »%s« nicht schreiben: %m"
+
+#: file_ops.c:150 file_ops.c:177
+#, c-format
+msgid "undefined file type for \"%s\""
+msgstr "undefinierter Dateityp für »%s«"
+
+#: file_ops.c:173
+#, c-format
+msgid "invalid action (CREATE) for regular file"
+msgstr "ungültige Aktion (CREATE) für normale Datei"
+
+#: file_ops.c:200
+#, c-format
+msgid "could not remove file \"%s\": %m"
+msgstr "konnte Datei »%s« nicht löschen: %m"
+
+#: file_ops.c:218
+#, c-format
+msgid "could not open file \"%s\" for truncation: %m"
+msgstr "konnte Datei »%s« nicht zum Kürzen öffnen: %m"
+
+#: file_ops.c:222
+#, c-format
+msgid "could not truncate file \"%s\" to %u: %m"
+msgstr "konnte Datei »%s« nicht auf %u kürzen: %m"
+
+#: file_ops.c:238
+#, c-format
+msgid "could not create directory \"%s\": %m"
+msgstr "konnte Verzeichnis »%s« nicht erzeugen: %m"
+
+#: file_ops.c:252
+#, c-format
+msgid "could not remove directory \"%s\": %m"
+msgstr "konnte Verzeichnis »%s« nicht löschen: %m"
+
+#: file_ops.c:266
+#, c-format
+msgid "could not create symbolic link at \"%s\": %m"
+msgstr "konnte symbolische Verknüpfung »%s« nicht erstellen: %m"
+
+#: file_ops.c:280
+#, c-format
+msgid "could not remove symbolic link \"%s\": %m"
+msgstr "konnte symbolische Verknüpfung »%s« nicht löschen: %m"
+
+#: file_ops.c:326 file_ops.c:330
+#, c-format
+msgid "could not open file \"%s\" for reading: %m"
+msgstr "konnte Datei »%s« nicht zum Lesen öffnen: %m"
+
+#: file_ops.c:341 local_source.c:107 parsexlog.c:348
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "konnte Datei »%s« nicht lesen: %m"
+
+#: file_ops.c:344 parsexlog.c:350
+#, c-format
+msgid "could not read file \"%s\": read %d of %zu"
+msgstr "konnte Datei »%s« nicht lesen: %d von %zu gelesen"
+
+#: file_ops.c:388
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "konnte Verzeichnis »%s« nicht öffnen: %m"
+
+#: file_ops.c:446
+#, c-format
+msgid "could not read symbolic link \"%s\": %m"
+msgstr "konnte symbolische Verknüpfung »%s« nicht lesen: %m"
+
+#: file_ops.c:449
+#, c-format
+msgid "symbolic link \"%s\" target is too long"
+msgstr "Ziel für symbolische Verknüpfung »%s« ist zu lang"
+
+#: file_ops.c:464
+#, c-format
+msgid "\"%s\" is a symbolic link, but symbolic links are not supported on this platform"
+msgstr "»%s« ist eine symbolische Verknüpfung, aber symbolische Verknüpfungen werden auf dieser Plattform nicht unterstützt"
+
+#: file_ops.c:471
+#, c-format
+msgid "could not read directory \"%s\": %m"
+msgstr "konnte Verzeichnis »%s« nicht lesen: %m"
+
+#: file_ops.c:475
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "konnte Verzeichnis »%s« nicht schließen: %m"
+
+#: filemap.c:237
+#, c-format
+msgid "data file \"%s\" in source is not a regular file"
+msgstr "Datendatei »%s« in der Quelle ist keine normale Datei"
+
+#: filemap.c:242 filemap.c:275
+#, c-format
+msgid "duplicate source file \"%s\""
+msgstr "doppelte Quelldatei »%s«"
+
+#: filemap.c:330
+#, c-format
+msgid "unexpected page modification for non-regular file \"%s\""
+msgstr "unerwartete Seitenänderung für nicht normale Datei »%s«"
+
+#: filemap.c:680 filemap.c:774
+#, c-format
+msgid "unknown file type for \"%s\""
+msgstr "unbekannter Dateityp für »%s«"
+
+#: filemap.c:707
+#, c-format
+msgid "file \"%s\" is of different type in source and target"
+msgstr "Datei »%s« hat unterschiedlichen Typ in Quelle und Ziel"
+
+#: filemap.c:779
+#, c-format
+msgid "could not decide what to do with file \"%s\""
+msgstr "konnte nicht entscheiden, was mit Datei »%s« zu tun ist"
+
+#: libpq_source.c:128
+#, c-format
+msgid "could not clear search_path: %s"
+msgstr "konnte search_path nicht auf leer setzen: %s"
+
+#: libpq_source.c:139
+#, c-format
+msgid "full_page_writes must be enabled in the source server"
+msgstr "full_page_writes muss im Quell-Server eingeschaltet sein"
+
+#: libpq_source.c:150
+#, c-format
+msgid "could not prepare statement to fetch file contents: %s"
+msgstr "konnte Anfrage zum Holen des Dateiinhalts nicht vorbereiten: %s"
+
+#: libpq_source.c:169
+#, c-format
+msgid "error running query (%s) on source server: %s"
+msgstr "Fehler beim Ausführen einer Anfrage (%s) auf dem Quellserver: %s"
+
+#: libpq_source.c:174
+#, c-format
+msgid "unexpected result set from query"
+msgstr "Anfrage ergab unerwartete Ergebnismenge"
+
+#: libpq_source.c:196
+#, c-format
+msgid "error running query (%s) in source server: %s"
+msgstr "Fehler beim Ausführen einer Anfrage (%s) im Quellserver: %s"
+
+#: libpq_source.c:217
+#, c-format
+msgid "unrecognized result \"%s\" for current WAL insert location"
+msgstr "unbekanntes Ergebnis »%s« für aktuelle WAL-Einfügeposition"
+
+#: libpq_source.c:268
+#, c-format
+msgid "could not fetch file list: %s"
+msgstr "konnte Dateiliste nicht holen: %s"
+
+#: libpq_source.c:273
+#, c-format
+msgid "unexpected result set while fetching file list"
+msgstr "unerwartete Ergebnismenge beim Holen der Dateiliste"
+
+#: libpq_source.c:435
+#, c-format
+msgid "could not send query: %s"
+msgstr "konnte Anfrage nicht senden: %s"
+
+#: libpq_source.c:438
+#, c-format
+msgid "could not set libpq connection to single row mode"
+msgstr "konnte libpq-Verbindung nicht in den Einzelzeilenmodus setzen"
+
+#: libpq_source.c:468
+#, c-format
+msgid "unexpected result while fetching remote files: %s"
+msgstr "unerwartetes Ergebnis beim Holen von fernen Dateien: %s"
+
+#: libpq_source.c:473
+#, c-format
+msgid "received more data chunks than requested"
+msgstr "mehr Daten-Chunks erhalten als verlangt"
+
+#: libpq_source.c:477
+#, c-format
+msgid "unexpected result set size while fetching remote files"
+msgstr "unerwartete Ergebnismengengröße beim Holen von fernen Dateien"
+
+#: libpq_source.c:483
+#, c-format
+msgid "unexpected data types in result set while fetching remote files: %u %u %u"
+msgstr "unerwartete Datentypen in Ergebnismenge beim Holen von fernen Dateien: %u %u %u"
+
+#: libpq_source.c:491
+#, c-format
+msgid "unexpected result format while fetching remote files"
+msgstr "unerwartetes Ergebnisformat beim Holen von fernen Dateien"
+
+#: libpq_source.c:497
+#, c-format
+msgid "unexpected null values in result while fetching remote files"
+msgstr "unerwartete NULL-Werte im Ergebnis beim Holen von fernen Dateien"
+
+#: libpq_source.c:501
+#, c-format
+msgid "unexpected result length while fetching remote files"
+msgstr "unerwartete Ergebnislänge beim Holen von fernen Dateien"
+
+#: libpq_source.c:534
+#, c-format
+msgid "received data for file \"%s\", when requested for \"%s\""
+msgstr "Daten für Datei »%s« erhalten, aber »%s« wurde verlangt"
+
+#: libpq_source.c:538
+#, c-format
+msgid "received data at offset %lld of file \"%s\", when requested for offset %lld"
+msgstr "Daten für Offset %lld von Datei »%s« erhalten, aber Offset %lld wurde verlangt"
+
+#: libpq_source.c:550
+#, c-format
+msgid "received more than requested for file \"%s\""
+msgstr "mehr als verlangt erhalten für Datei »%s«"
+
+#: libpq_source.c:563
+#, c-format
+msgid "unexpected number of data chunks received"
+msgstr "unerwartete Anzahl Daten-Chunks erhalten"
+
+#: libpq_source.c:606
+#, c-format
+msgid "could not fetch remote file \"%s\": %s"
+msgstr "konnte ferne Datei »%s« nicht holen: %s"
+
+#: libpq_source.c:611
+#, c-format
+msgid "unexpected result set while fetching remote file \"%s\""
+msgstr "unerwartete Ergebnismenge beim Holen der fernen Datei »%s«"
+
+#: local_source.c:86
+#, c-format
+msgid "could not open source file \"%s\": %m"
+msgstr "konnte Quelldatei »%s« nicht öffnen: %m"
+
+#: local_source.c:90
+#, c-format
+msgid "could not seek in source file: %m"
+msgstr "konnte Positionszeiger in Quelldatei nicht setzen: %m"
+
+#: local_source.c:109
+#, c-format
+msgid "unexpected EOF while reading file \"%s\""
+msgstr "unerwartetes EOF beim Lesen der Datei »%s«"
+
+#: local_source.c:116
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "konnte Datei »%s« nicht schließen: %m"
+
+#: parsexlog.c:89 parsexlog.c:144
+#, c-format
+msgid "could not read WAL record at %X/%X: %s"
+msgstr "konnte WAL-Eintrag bei %X/%X nicht lesen: %s"
+
+#: parsexlog.c:93 parsexlog.c:147
+#, c-format
+msgid "could not read WAL record at %X/%X"
+msgstr "konnte WAL-Eintrag bei %X/%X nicht lesen"
+
+#: parsexlog.c:106
+#, c-format
+msgid "end pointer %X/%X is not a valid end point; expected %X/%X"
+msgstr "Endpunkt %X/%X ist kein gültiger Endpunkt; %X/%X erwartet"
+
+#: parsexlog.c:210
+#, c-format
+msgid "could not find previous WAL record at %X/%X: %s"
+msgstr "konnte vorangegangenen WAL-Eintrag bei %X/%X nicht finden: %s"
+
+#: parsexlog.c:214
+#, c-format
+msgid "could not find previous WAL record at %X/%X"
+msgstr "konnte vorangegangenen WAL-Eintrag bei %X/%X nicht finden"
+
+#: parsexlog.c:339
+#, c-format
+msgid "could not seek in file \"%s\": %m"
+msgstr "konnte Positionszeiger in Datei »%s« nicht setzen: %m"
+
+#: parsexlog.c:431
+#, c-format
+msgid "WAL record modifies a relation, but record type is not recognized: lsn: %X/%X, rmgr: %s, info: %02X"
+msgstr "WAL-Eintrag modifiziert eine Relation, aber Typ des Eintrags wurde nicht erkannt: lsn: %X/%X, rmgr: %s, info: %02X"
+
+#: pg_rewind.c:84
+#, c-format
+msgid ""
+"%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n"
+"\n"
+msgstr ""
+"%s resynchronisiert einen PostgreSQL-Cluster mit einer Kopie des Clusters.\n"
+"\n"
+
+#: pg_rewind.c:85
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]...\n"
+"\n"
+msgstr ""
+"Aufruf:\n"
+" %s [OPTION]...\n"
+"\n"
+
+#: pg_rewind.c:86
+#, c-format
+msgid "Options:\n"
+msgstr "Optionen:\n"
+
+#: pg_rewind.c:87
+#, c-format
+msgid ""
+" -c, --restore-target-wal use restore_command in target configuration to\n"
+" retrieve WAL files from archives\n"
+msgstr ""
+" -c, --restore-target-wal restore_command in der Zielkonfiguration zum\n"
+" Laden von WAL-Dateien aus Archiv verwenden\n"
+
+#: pg_rewind.c:89
+#, c-format
+msgid " -D, --target-pgdata=DIRECTORY existing data directory to modify\n"
+msgstr " -D, --target-pgdata=VERZ bestehendes zu modifizierendes Datenverzeichnis\n"
+
+#: pg_rewind.c:90
+#, c-format
+msgid " --source-pgdata=DIRECTORY source data directory to synchronize with\n"
+msgstr ""
+" --source-pgdata=VERZ Quelldatenverzeichnis, mit dem synchronisiert\n"
+" werden soll\n"
+
+#: pg_rewind.c:91
+#, c-format
+msgid " --source-server=CONNSTR source server to synchronize with\n"
+msgstr " --source-server=VERB Quellserver, mit dem synchronisiert werden soll\n"
+
+#: pg_rewind.c:92
+#, c-format
+msgid " -n, --dry-run stop before modifying anything\n"
+msgstr " -n, --dry-run anhalten, bevor etwas geändert wird\n"
+
+#: pg_rewind.c:93
+#, c-format
+msgid ""
+" -N, --no-sync do not wait for changes to be written\n"
+" safely to disk\n"
+msgstr ""
+" -N, --no-sync nicht warten, bis Änderungen sicher auf\n"
+" Festplatte geschrieben sind\n"
+
+#: pg_rewind.c:95
+#, c-format
+msgid " -P, --progress write progress messages\n"
+msgstr " -P, --progress Fortschrittsmeldungen ausgeben\n"
+
+#: pg_rewind.c:96
+#, c-format
+msgid ""
+" -R, --write-recovery-conf write configuration for replication\n"
+" (requires --source-server)\n"
+msgstr ""
+" -R, --write-recovery-conf Konfiguration für Replikation schreiben\n"
+" (benötigt --source-server)\n"
+
+#: pg_rewind.c:98
+#, c-format
+msgid " --debug write a lot of debug messages\n"
+msgstr " --debug viele Debug-Meldungen ausgeben\n"
+
+#: pg_rewind.c:99
+#, c-format
+msgid " --no-ensure-shutdown do not automatically fix unclean shutdown\n"
+msgstr " --no-ensure-shutdown unsauberen Shutdown nicht automatisch reparieren\n"
+
+#: pg_rewind.c:100
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version Versionsinformationen anzeigen, dann beenden\n"
+
+#: pg_rewind.c:101
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help diese Hilfe anzeigen, dann beenden\n"
+
+#: pg_rewind.c:102
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"Berichten Sie Fehler an <%s>.\n"
+
+#: pg_rewind.c:103
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "%s Homepage: <%s>\n"
+
+#: pg_rewind.c:164 pg_rewind.c:213 pg_rewind.c:220 pg_rewind.c:227
+#: pg_rewind.c:234 pg_rewind.c:242
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "Versuchen Sie »%s --help« für weitere Informationen.\n"
+
+#: pg_rewind.c:212
+#, c-format
+msgid "no source specified (--source-pgdata or --source-server)"
+msgstr "keine Quelle angegeben (--source-pgdata oder --source-server)"
+
+#: pg_rewind.c:219
+#, c-format
+msgid "only one of --source-pgdata or --source-server can be specified"
+msgstr "--source-pgdata und --source-server können nicht zusammen angegeben werden"
+
+#: pg_rewind.c:226
+#, c-format
+msgid "no target data directory specified (--target-pgdata)"
+msgstr "kein Zielverzeichnis angegeben (--target-pgdata)"
+
+#: pg_rewind.c:233
+#, c-format
+msgid "no source server information (--source-server) specified for --write-recovery-conf"
+msgstr "kein Quellserver (--source-server) angegeben für --write-recovery-conf"
+
+#: pg_rewind.c:240
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "zu viele Kommandozeilenargumente (das erste ist »%s«)"
+
+#: pg_rewind.c:255
+#, c-format
+msgid "cannot be executed by \"root\""
+msgstr "kann nicht von »root« ausgeführt werden"
+
+#: pg_rewind.c:256
+#, c-format
+msgid "You must run %s as the PostgreSQL superuser.\n"
+msgstr "Sie müssen %s als PostgreSQL-Superuser ausführen.\n"
+
+#: pg_rewind.c:267
+#, c-format
+msgid "could not read permissions of directory \"%s\": %m"
+msgstr "konnte Zugriffsrechte von Verzeichnis »%s« nicht lesen: %m"
+
+#: pg_rewind.c:287
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: pg_rewind.c:290
+#, c-format
+msgid "connected to server"
+msgstr "mit Server verbunden"
+
+#: pg_rewind.c:337
+#, c-format
+msgid "source and target cluster are on the same timeline"
+msgstr "Quell- und Ziel-Cluster sind auf der gleichen Zeitleiste"
+
+#: pg_rewind.c:346
+#, c-format
+msgid "servers diverged at WAL location %X/%X on timeline %u"
+msgstr "Server divergierten bei WAL-Position %X/%X auf Zeitleiste %u"
+
+#: pg_rewind.c:394
+#, c-format
+msgid "no rewind required"
+msgstr "kein Rückspulen nötig"
+
+#: pg_rewind.c:403
+#, c-format
+msgid "rewinding from last common checkpoint at %X/%X on timeline %u"
+msgstr "Rückspulen ab letztem gemeinsamen Checkpoint bei %X/%X auf Zeitleiste %u"
+
+#: pg_rewind.c:413
+#, c-format
+msgid "reading source file list"
+msgstr "lese Quelldateiliste"
+
+#: pg_rewind.c:417
+#, c-format
+msgid "reading target file list"
+msgstr "lese Zieldateiliste"
+
+#: pg_rewind.c:426
+#, c-format
+msgid "reading WAL in target"
+msgstr "lese WAL im Ziel-Cluster"
+
+#: pg_rewind.c:447
+#, c-format
+msgid "need to copy %lu MB (total source directory size is %lu MB)"
+msgstr "%lu MB müssen kopiert werden (Gesamtgröße des Quellverzeichnisses ist %lu MB)"
+
+#: pg_rewind.c:465
+#, c-format
+msgid "syncing target data directory"
+msgstr "synchronisiere Zieldatenverzeichnis"
+
+#: pg_rewind.c:481
+#, c-format
+msgid "Done!"
+msgstr "Fertig!"
+
+#: pg_rewind.c:564
+#, c-format
+msgid "no action decided for file \"%s\""
+msgstr "keine Aktion bestimmt für Datei »%s«"
+
+#: pg_rewind.c:596
+#, c-format
+msgid "source system was modified while pg_rewind was running"
+msgstr "Quellsystem wurde verändert, während pg_rewind lief"
+
+#: pg_rewind.c:600
+#, c-format
+msgid "creating backup label and updating control file"
+msgstr "erzeuge Backup-Label und aktualisiere Kontrolldatei"
+
+#: pg_rewind.c:650
+#, c-format
+msgid "source system was in unexpected state at end of rewind"
+msgstr "Quellsystem war in einem unerwarteten Zustand am Ende des Rückspulens"
+
+#: pg_rewind.c:681
+#, c-format
+msgid "source and target clusters are from different systems"
+msgstr "Quell- und Ziel-Cluster sind von verschiedenen Systemen"
+
+#: pg_rewind.c:689
+#, c-format
+msgid "clusters are not compatible with this version of pg_rewind"
+msgstr "die Cluster sind nicht mit dieser Version von pg_rewind kompatibel"
+
+#: pg_rewind.c:699
+#, c-format
+msgid "target server needs to use either data checksums or \"wal_log_hints = on\""
+msgstr "Zielserver muss entweder Datenprüfsummen oder »wal_log_hints = on« verwenden"
+
+#: pg_rewind.c:710
+#, c-format
+msgid "target server must be shut down cleanly"
+msgstr "Zielserver muss sauber heruntergefahren worden sein"
+
+#: pg_rewind.c:720
+#, c-format
+msgid "source data directory must be shut down cleanly"
+msgstr "Quelldatenverzeichnis muss sauber heruntergefahren worden sein"
+
+#: pg_rewind.c:772
+#, c-format
+msgid "%*s/%s kB (%d%%) copied"
+msgstr "%*s/%s kB (%d%%) kopiert"
+
+#: pg_rewind.c:835
+#, c-format
+msgid "invalid control file"
+msgstr "ungültige Kontrolldatei"
+
+#: pg_rewind.c:919
+#, c-format
+msgid "could not find common ancestor of the source and target cluster's timelines"
+msgstr "konnte keinen gemeinsamen Anfangspunkt in den Zeitleisten von Quell- und Ziel-Cluster finden"
+
+#: pg_rewind.c:960
+#, c-format
+msgid "backup label buffer too small"
+msgstr "Puffer für Backup-Label ist zu klein"
+
+#: pg_rewind.c:983
+#, c-format
+msgid "unexpected control file CRC"
+msgstr "unerwartete CRC in Kontrolldatei"
+
+#: pg_rewind.c:995
+#, c-format
+msgid "unexpected control file size %d, expected %d"
+msgstr "unerwartete Kontrolldateigröße %d, erwartet wurde %d"
+
+#: pg_rewind.c:1004
+#, c-format
+msgid "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d byte"
+msgid_plural "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d bytes"
+msgstr[0] "WAL-Segmentgröße muss eine Zweierpotenz zwischen 1 MB und 1 GB sein, aber die Kontrolldatei gibt %d Byte an"
+msgstr[1] "WAL-Segmentgröße muss eine Zweierpotenz zwischen 1 MB und 1 GB sein, aber die Kontrolldatei gibt %d Bytes an"
+
+#: pg_rewind.c:1043 pg_rewind.c:1101
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"Das Programm »%s« wird von %s benötigt, aber wurde nicht im\n"
+"selben Verzeichnis wie »%s« gefunden.\n"
+"Prüfen Sie Ihre Installation."
+
+#: pg_rewind.c:1048 pg_rewind.c:1106
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"Das Programm »%s« wurde von %s gefunden,\n"
+"aber es hatte nicht die gleiche Version wie %s.\n"
+"Prüfen Sie Ihre Installation."
+
+#: pg_rewind.c:1069
+#, c-format
+msgid "restore_command is not set in the target cluster"
+msgstr "restore_command ist im Ziel-Cluster nicht gesetzt"
+
+#: pg_rewind.c:1112
+#, c-format
+msgid "executing \"%s\" for target server to complete crash recovery"
+msgstr "führe »%s« für Zielserver aus, um Wiederherstellung abzuschließen"
+
+#: pg_rewind.c:1132
+#, c-format
+msgid "postgres single-user mode in target cluster failed"
+msgstr "postgres im Einzelbenutzermodus im Ziel-Cluster fehlgeschlagen"
+
+#: pg_rewind.c:1133
+#, c-format
+msgid "Command was: %s"
+msgstr "Die Anweisung war: %s"
+
+#: timeline.c:75 timeline.c:81
+#, c-format
+msgid "syntax error in history file: %s"
+msgstr "Syntaxfehler in History-Datei: %s"
+
+#: timeline.c:76
+#, c-format
+msgid "Expected a numeric timeline ID."
+msgstr "Eine numerische Zeitleisten-ID wurde erwartet."
+
+#: timeline.c:82
+#, c-format
+msgid "Expected a write-ahead log switchpoint location."
+msgstr "Eine Write-Ahead-Log-Switchpoint-Position wurde erwartet."
+
+#: timeline.c:87
+#, c-format
+msgid "invalid data in history file: %s"
+msgstr "ungültige Daten in History-Datei: %s"
+
+#: timeline.c:88
+#, c-format
+msgid "Timeline IDs must be in increasing sequence."
+msgstr "Zeitleisten-IDs müssen in aufsteigender Folge sein."
+
+#: timeline.c:108
+#, c-format
+msgid "invalid data in history file"
+msgstr "ungültige Daten in History-Datei"
+
+#: timeline.c:109
+#, c-format
+msgid "Timeline IDs must be less than child timeline's ID."
+msgstr "Zeitleisten-IDs müssen kleiner als die Zeitleisten-ID des Kindes sein."
+
+#: xlogreader.c:354
+#, c-format
+msgid "invalid record offset at %X/%X"
+msgstr "ungültiger Datensatz-Offset bei %X/%X"
+
+#: xlogreader.c:362
+#, c-format
+msgid "contrecord is requested by %X/%X"
+msgstr "Contrecord angefordert von %X/%X"
+
+#: xlogreader.c:403 xlogreader.c:733
+#, c-format
+msgid "invalid record length at %X/%X: wanted %u, got %u"
+msgstr "ungültige Datensatzlänge bei %X/%X: %u erwartet, %u erhalten"
+
+#: xlogreader.c:429
+#, c-format
+msgid "record length %u at %X/%X too long"
+msgstr "Datensatzlänge %u bei %X/%X ist zu lang"
+
+#: xlogreader.c:477
+#, c-format
+msgid "there is no contrecord flag at %X/%X"
+msgstr "keine Contrecord-Flag bei %X/%X"
+
+#: xlogreader.c:490
+#, c-format
+msgid "invalid contrecord length %u (expected %lld) at %X/%X"
+msgstr "ungültige Contrecord-Länge %u (erwartet %lld) bei %X/%X"
+
+#: xlogreader.c:741
+#, c-format
+msgid "invalid resource manager ID %u at %X/%X"
+msgstr "ungültige Resource-Manager-ID %u bei %X/%X"
+
+#: xlogreader.c:754 xlogreader.c:770
+#, c-format
+msgid "record with incorrect prev-link %X/%X at %X/%X"
+msgstr "Datensatz mit falschem Prev-Link %X/%X bei %X/%X"
+
+#: xlogreader.c:806
+#, c-format
+msgid "incorrect resource manager data checksum in record at %X/%X"
+msgstr "ungültige Resource-Manager-Datenprüfsumme in Datensatz bei %X/%X"
+
+#: xlogreader.c:843
+#, c-format
+msgid "invalid magic number %04X in log segment %s, offset %u"
+msgstr "ungültige magische Zahl %04X in Logsegment %s, Offset %u"
+
+#: xlogreader.c:857 xlogreader.c:898
+#, c-format
+msgid "invalid info bits %04X in log segment %s, offset %u"
+msgstr "ungültige Info-Bits %04X in Logsegment %s, Offset %u"
+
+#: xlogreader.c:872
+#, c-format
+msgid "WAL file is from different database system: WAL file database system identifier is %llu, pg_control database system identifier is %llu"
+msgstr "WAL-Datei ist von einem anderen Datenbanksystem: Datenbanksystemidentifikator in WAL-Datei ist %llu, Datenbanksystemidentifikator in pg_control ist %llu"
+
+#: xlogreader.c:880
+#, c-format
+msgid "WAL file is from different database system: incorrect segment size in page header"
+msgstr "WAL-Datei ist von einem anderen Datenbanksystem: falsche Segmentgröße im Seitenkopf"
+
+#: xlogreader.c:886
+#, c-format
+msgid "WAL file is from different database system: incorrect XLOG_BLCKSZ in page header"
+msgstr "WAL-Datei ist von einem anderen Datenbanksystem: falsche XLOG_BLCKSZ im Seitenkopf"
+
+#: xlogreader.c:917
+#, c-format
+msgid "unexpected pageaddr %X/%X in log segment %s, offset %u"
+msgstr "unerwartete Pageaddr %X/%X in Logsegment %s, Offset %u"
+
+#: xlogreader.c:942
+#, c-format
+msgid "out-of-sequence timeline ID %u (after %u) in log segment %s, offset %u"
+msgstr "Zeitleisten-ID %u außer der Reihe (nach %u) in Logsegment %s, Offset %u"
+
+#: xlogreader.c:1287
+#, c-format
+msgid "out-of-order block_id %u at %X/%X"
+msgstr "block_id %u außer der Reihe bei %X/%X"
+
+#: xlogreader.c:1309
+#, c-format
+msgid "BKPBLOCK_HAS_DATA set, but no data included at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA gesetzt, aber keine Daten enthalten bei %X/%X"
+
+#: xlogreader.c:1316
+#, c-format
+msgid "BKPBLOCK_HAS_DATA not set, but data length is %u at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA nicht gesetzt, aber Datenlänge ist %u bei %X/%X"
+
+#: xlogreader.c:1352
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE set, but hole offset %u length %u block image length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE gesetzt, aber Loch Offset %u Länge %u Block-Abbild-Länge %u bei %X/%X"
+
+#: xlogreader.c:1368
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE not set, but hole offset %u length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE nicht gesetzt, aber Loch Offset %u Länge %u bei %X/%X"
+
+#: xlogreader.c:1383
+#, c-format
+msgid "BKPIMAGE_IS_COMPRESSED set, but block image length %u at %X/%X"
+msgstr "BKPIMAGE_IS_COMPRESSED gesetzt, aber Block-Abbild-Länge %u bei %X/%X"
+
+#: xlogreader.c:1398
+#, c-format
+msgid "neither BKPIMAGE_HAS_HOLE nor BKPIMAGE_IS_COMPRESSED set, but block image length is %u at %X/%X"
+msgstr "weder BKPIMAGE_HAS_HOLE noch BKPIMAGE_IS_COMPRESSED gesetzt, aber Block-Abbild-Länge ist %u bei %X/%X"
+
+#: xlogreader.c:1414
+#, c-format
+msgid "BKPBLOCK_SAME_REL set but no previous rel at %X/%X"
+msgstr "BKPBLOCK_SAME_REL gesetzt, aber keine vorangehende Relation bei %X/%X"
+
+#: xlogreader.c:1426
+#, c-format
+msgid "invalid block_id %u at %X/%X"
+msgstr "ungültige block_id %u bei %X/%X"
+
+#: xlogreader.c:1513
+#, c-format
+msgid "record with invalid length at %X/%X"
+msgstr "Datensatz mit ungültiger Länge bei %X/%X"
+
+#: xlogreader.c:1602
+#, c-format
+msgid "invalid compressed image at %X/%X, block %d"
+msgstr "ungültiges komprimiertes Abbild bei %X/%X, Block %d"
diff --git a/src/bin/pg_rewind/po/el.po b/src/bin/pg_rewind/po/el.po
new file mode 100644
index 0000000..7fbe5ee
--- /dev/null
+++ b/src/bin/pg_rewind/po/el.po
@@ -0,0 +1,959 @@
+# Greek message translation file for pg_rewind
+# Copyright (C) 2021 PostgreSQL Global Development Group
+# This file is distributed under the same license as the pg_rewind (PostgreSQL) package.
+# Georgios Kokolatos <gkokolatos@pm.me>, 2021.
+#
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_rewind (PostgreSQL) 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-06-09 21:19+0000\n"
+"PO-Revision-Date: 2021-07-05 15:33+0200\n"
+"Last-Translator: Georgios Kokolatos <gkokolatos@pm.me>\n"
+"Language-Team: \n"
+"Language: el\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Poedit 3.0\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "κρίσιμο: "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "σφάλμα: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "προειδοποίηση: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "έλλειψη μνήμης\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "δεν ήταν δυνατή η αντιγραφή δείκτη null (εσωτερικό σφάλμα)\n"
+
+#: ../../common/restricted_token.c:64
+#, c-format
+msgid "could not load library \"%s\": error code %lu"
+msgstr "δεν ήταν δυνατή η φόρτωση της βιβλιοθήκης «%s»: κωδικός σφάλματος %lu"
+
+#: ../../common/restricted_token.c:73
+#, c-format
+msgid "cannot create restricted tokens on this platform: error code %lu"
+msgstr "δεν ήταν δυνατή η δημιουργία διακριτικών περιορισμού στην παρούσα πλατφόρμα: κωδικός σφάλματος %lu"
+
+#: ../../common/restricted_token.c:82
+#, c-format
+msgid "could not open process token: error code %lu"
+msgstr "δεν ήταν δυνατό το άνοιγμα διακριτικού διεργασίας: κωδικός σφάλματος %lu"
+
+#: ../../common/restricted_token.c:97
+#, c-format
+msgid "could not allocate SIDs: error code %lu"
+msgstr "δεν ήταν δυνατή η εκχώρηση SID: κωδικός σφάλματος %lu"
+
+#: ../../common/restricted_token.c:119
+#, c-format
+msgid "could not create restricted token: error code %lu"
+msgstr "δεν ήταν δυνατή η δημιουργία διακριτικού διεργασίας: κωδικός σφάλματος %lu"
+
+#: ../../common/restricted_token.c:140
+#, c-format
+msgid "could not start process for command \"%s\": error code %lu"
+msgstr "δεν ήταν δυνατή η εκκίνηση διεργασίας για την εντολή «%s»: κωδικός σφάλματος %lu"
+
+#: ../../common/restricted_token.c:178
+#, c-format
+msgid "could not re-execute with restricted token: error code %lu"
+msgstr "δεν ήταν δυνατή η επανεκκίνηση με διακριτικό περιορισμού: κωδικός σφάλματος %lu"
+
+#: ../../common/restricted_token.c:194
+#, c-format
+msgid "could not get exit code from subprocess: error code %lu"
+msgstr "δεν ήταν δυνατή η απόκτηση κωδικού εξόδου από την υποδιεργασία: κωδικός σφάλματος %lu"
+
+#: ../../fe_utils/archive.c:53
+#, c-format
+msgid "cannot use restore_command with %%r placeholder"
+msgstr "δεν είναι δυνατή η χρήση restore_command μαζί με %%r placeholder"
+
+#: ../../fe_utils/archive.c:74
+#, c-format
+msgid "unexpected file size for \"%s\": %lld instead of %lld"
+msgstr "μη αναμενόμενο μέγεθος αρχείου για «%s»: %lld αντί για %lld"
+
+#: ../../fe_utils/archive.c:85
+#, c-format
+msgid "could not open file \"%s\" restored from archive: %m"
+msgstr "δεν ήταν δυνατό το άνοιγμα του αρχείου «%s» που έχει επαναφερθεί από την αρχειοθήκη: %m"
+
+#: ../../fe_utils/archive.c:97 file_ops.c:417
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "δεν ήταν δυνατή η εκτέλεση stat στο αρχείο «%s»: %m"
+
+#: ../../fe_utils/archive.c:112
+#, c-format
+msgid "restore_command failed: %s"
+msgstr "restore_command απέτυχε: %s"
+
+#: ../../fe_utils/archive.c:121
+#, c-format
+msgid "could not restore file \"%s\" from archive"
+msgstr "δεν ήταν δυνατή η επαναφορά του αρχείου «%s» από την αρχειοθήκη"
+
+#: ../../fe_utils/recovery_gen.c:35 ../../fe_utils/recovery_gen.c:49
+#: ../../fe_utils/recovery_gen.c:77 ../../fe_utils/recovery_gen.c:100
+#: ../../fe_utils/recovery_gen.c:171 parsexlog.c:77 parsexlog.c:135
+#: parsexlog.c:195
+#, c-format
+msgid "out of memory"
+msgstr "έλλειψη μνήμης"
+
+#: ../../fe_utils/recovery_gen.c:134 parsexlog.c:308
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "δεν ήταν δυνατό το άνοιγμα του αρχείου «%s»: %m"
+
+#: ../../fe_utils/recovery_gen.c:140
+#, c-format
+msgid "could not write to file \"%s\": %m"
+msgstr "δεν ήταν δυνατή η εγγραφή στο αρχείο «%s»: %m"
+
+#: ../../fe_utils/recovery_gen.c:152
+#, c-format
+msgid "could not create file \"%s\": %m"
+msgstr "δεν ήταν δυνατή η δημιουργία αρχείου «%s»: %m"
+
+#: file_ops.c:67
+#, c-format
+msgid "could not open target file \"%s\": %m"
+msgstr "δεν ήταν δυνατό το άνοιγμα του αρχείου προορισμού «%s»: %m"
+
+#: file_ops.c:81
+#, c-format
+msgid "could not close target file \"%s\": %m"
+msgstr "δεν ήταν δυνατό το κλείσιμο του αρχείου προορισμού «%s»: %m"
+
+#: file_ops.c:101
+#, c-format
+msgid "could not seek in target file \"%s\": %m"
+msgstr "δεν ήταν δυνατή η αναζήτηση στο αρχείο προορισμού «%s»: %m"
+
+#: file_ops.c:117
+#, c-format
+msgid "could not write file \"%s\": %m"
+msgstr "δεν ήταν δυνατή η εγγραφή αρχείου «%s»: %m"
+
+#: file_ops.c:150 file_ops.c:177
+#, c-format
+msgid "undefined file type for \"%s\""
+msgstr "απροσδιόριστος τύπος αρχείου για το «%s»"
+
+#: file_ops.c:173
+#, c-format
+msgid "invalid action (CREATE) for regular file"
+msgstr "μη έγκυρη ενέργεια (CREATE) για κανονικό αρχείο"
+
+#: file_ops.c:200
+#, c-format
+msgid "could not remove file \"%s\": %m"
+msgstr "δεν ήταν δυνατή η αφαίρεση του αρχείου «%s»: %m"
+
+#: file_ops.c:218
+#, c-format
+msgid "could not open file \"%s\" for truncation: %m"
+msgstr "δεν ήταν δυνατό το άνοιγμα του αρχείου «%s» για περικοπή: %m"
+
+#: file_ops.c:222
+#, c-format
+msgid "could not truncate file \"%s\" to %u: %m"
+msgstr "δεν ήταν δυνατή η περικοπή του αρχείου «%s» σε %u: %m"
+
+#: file_ops.c:238
+#, c-format
+msgid "could not create directory \"%s\": %m"
+msgstr "δεν ήταν δυνατή η δημιουργία του καταλόγου «%s»: %m"
+
+#: file_ops.c:252
+#, c-format
+msgid "could not remove directory \"%s\": %m"
+msgstr "δεν ήταν δυνατή η αφαίρεση του καταλόγου «%s»: %m"
+
+#: file_ops.c:266
+#, c-format
+msgid "could not create symbolic link at \"%s\": %m"
+msgstr "δεν ήταν δυνατή η δημιουργία του συμβολικού συνδέσμου «%s»: %m"
+
+#: file_ops.c:280
+#, c-format
+msgid "could not remove symbolic link \"%s\": %m"
+msgstr "δεν ήταν δυνατή η αφαίρεση της συμβολικής σύνδεσης «%s»: %m"
+
+#: file_ops.c:326 file_ops.c:330
+#, c-format
+msgid "could not open file \"%s\" for reading: %m"
+msgstr "δεν ήταν δυνατό το άνοιγμα αρχείου «%s» για ανάγνωση: %m"
+
+#: file_ops.c:341 local_source.c:107 parsexlog.c:346
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "δεν ήταν δυνατή η ανάγνωση του αρχείου «%s»: %m"
+
+#: file_ops.c:344 parsexlog.c:348
+#, c-format
+msgid "could not read file \"%s\": read %d of %zu"
+msgstr "δεν ήταν δυνατή η ανάγνωση του αρχείου «%s»: ανέγνωσε %d από %zu"
+
+#: file_ops.c:388
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "δεν ήταν δυνατό το άνοιγμα του καταλόγου «%s»: %m"
+
+#: file_ops.c:446
+#, c-format
+msgid "could not read symbolic link \"%s\": %m"
+msgstr "δεν ήταν δυνατή η ανάγνωση του συμβολικού συνδέσμου «%s»: %m"
+
+#: file_ops.c:449
+#, c-format
+msgid "symbolic link \"%s\" target is too long"
+msgstr "ο συμβολικός σύνδεσμος «%s» είναι πολύ μακρύς"
+
+#: file_ops.c:464
+#, c-format
+msgid "\"%s\" is a symbolic link, but symbolic links are not supported on this platform"
+msgstr "«%s» είναι ένας συμβολικός σύνδεσμος, αλλά οι συμβολικοί σύνδεσμοι δεν υποστηρίζονται σε αυτήν την πλατφόρμα"
+
+#: file_ops.c:471
+#, c-format
+msgid "could not read directory \"%s\": %m"
+msgstr "δεν ήταν δυνατή η ανάγνωση του καταλόγου «%s»: %m"
+
+#: file_ops.c:475
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "δεν ήταν δυνατό το κλείσιμο του καταλόγου «%s»: %m"
+
+#: filemap.c:237
+#, c-format
+msgid "data file \"%s\" in source is not a regular file"
+msgstr "το αρχείο «%s» δεν είναι ένα κανονικό αρχείο"
+
+#: filemap.c:242 filemap.c:275
+#, c-format
+msgid "duplicate source file \"%s\""
+msgstr "διπλότυπο αρχείο προέλευσης «%s»"
+
+#: filemap.c:330
+#, c-format
+msgid "unexpected page modification for non-regular file \"%s\""
+msgstr "μη αναμενόμενη τροποποίηση σελίδας για μη κανονικό αρχείο «%s»"
+
+#: filemap.c:680 filemap.c:774
+#, c-format
+msgid "unknown file type for \"%s\""
+msgstr "άγνωστος τύπος αρχείου για το «%s»"
+
+#: filemap.c:707
+#, c-format
+msgid "file \"%s\" is of different type in source and target"
+msgstr "το αρχείο «%s» είναι διαφορετικού τύπου στην προέλευση και τον προορισμό"
+
+#: filemap.c:779
+#, c-format
+msgid "could not decide what to do with file \"%s\""
+msgstr "δεν ήταν δυνατή η λήψη του αρχείου «%s»"
+
+#: libpq_source.c:128
+#, c-format
+msgid "could not clear search_path: %s"
+msgstr "δεν ήταν δυνατή η εκκαθάριση του search_path: %s"
+
+#: libpq_source.c:139
+#, c-format
+msgid "full_page_writes must be enabled in the source server"
+msgstr "full_page_writes πρέπει να είναι ενεργοποιημένο στο διακομιστή προέλευσης"
+
+#: libpq_source.c:150
+#, c-format
+msgid "could not prepare statement to fetch file contents: %s"
+msgstr "δεν ήταν δυνατή η προετοιμασία της δήλωσης για τη λήψη περιεχομένων αρχείου: %s"
+
+#: libpq_source.c:169
+#, c-format
+msgid "error running query (%s) on source server: %s"
+msgstr "σφάλμα κατά την εκτέλεση ερωτήματος (%s) στο διακομιστή προέλευσης: %s"
+
+#: libpq_source.c:174
+#, c-format
+msgid "unexpected result set from query"
+msgstr "μη αναμενόμενο σύνολο αποτελεσμάτων από ερώτημα"
+
+#: libpq_source.c:196
+#, c-format
+msgid "error running query (%s) in source server: %s"
+msgstr "σφάλμα κατά την εκτέλεση ερωτήματος (%s) στο διακομιστή προέλευσης: %s"
+
+#: libpq_source.c:217
+#, c-format
+msgid "unrecognized result \"%s\" for current WAL insert location"
+msgstr "μη αναγνωρίσιμο αποτέλεσμα «%s» για την τρέχουσα θέση εισαγωγής WAL"
+
+#: libpq_source.c:268
+#, c-format
+msgid "could not fetch file list: %s"
+msgstr "δεν ήταν δυνατή η λήψη λίστας αρχείων: %s"
+
+#: libpq_source.c:273
+#, c-format
+msgid "unexpected result set while fetching file list"
+msgstr "μη αναμενόμενο σύνολο αποτελεσμάτων κατά τη λήψη λίστας αρχείων"
+
+#: libpq_source.c:435
+#, c-format
+msgid "could not send query: %s"
+msgstr "δεν ήταν δυνατή η αποστολή ερωτήματος: %s"
+
+#: libpq_source.c:438
+#, c-format
+msgid "could not set libpq connection to single row mode"
+msgstr "δεν ήταν δυνατή η ρύθμιση της σύνδεσης libpq σε λειτουργία μονής σειράς"
+
+#: libpq_source.c:468
+#, c-format
+msgid "unexpected result while fetching remote files: %s"
+msgstr "μη αναμενόμενο αποτέλεσμα κατά τη λήψη απομακρυσμένων αρχείων: %s"
+
+#: libpq_source.c:473
+#, c-format
+msgid "received more data chunks than requested"
+msgstr "έλαβε περισσότερα τμήματα δεδομένων από όσα ζητήθηκαν"
+
+#: libpq_source.c:477
+#, c-format
+msgid "unexpected result set size while fetching remote files"
+msgstr "μη αναμενόμενο μέγεθος συνόλου αποτελεσμάτων κατά τη λήψη απομακρυσμένων αρχείων"
+
+#: libpq_source.c:483
+#, c-format
+msgid "unexpected data types in result set while fetching remote files: %u %u %u"
+msgstr "μη αναμενόμενοι τύποι δεδομένων στο σύνολο αποτελεσμάτων κατά τη λήψη απομακρυσμένων αρχείων: %u %u %u"
+
+#: libpq_source.c:491
+#, c-format
+msgid "unexpected result format while fetching remote files"
+msgstr "μη αναμενόμενη μορφή αποτελέσματος κατά τη λήψη απομακρυσμένων αρχείων"
+
+#: libpq_source.c:497
+#, c-format
+msgid "unexpected null values in result while fetching remote files"
+msgstr "μη αναμενόμενες τιμές null κατά τη λήψη απομακρυσμένων αρχείων"
+
+#: libpq_source.c:501
+#, c-format
+msgid "unexpected result length while fetching remote files"
+msgstr "μη αναμενόμενο μήκος αποτελέσματος κατά τη λήψη απομακρυσμένων αρχείων"
+
+#: libpq_source.c:534
+#, c-format
+msgid "received data for file \"%s\", when requested for \"%s\""
+msgstr "έλαβε δεδομένα για το αρχείο «%s», όταν ζητήθηκε το «%s»"
+
+#: libpq_source.c:538
+#, c-format
+msgid "received data at offset %lld of file \"%s\", when requested for offset %lld"
+msgstr "έλαβε δεδομένα σε μετατόπιση %lld του αρχείου «%s», όταν ζητήθηκε μετατόπιση %lld"
+
+#: libpq_source.c:550
+#, c-format
+msgid "received more than requested for file \"%s\""
+msgstr "έλαβε περισσότερα από όσα ζήτησε για το αρχείο «%s»"
+
+#: libpq_source.c:563
+#, c-format
+msgid "unexpected number of data chunks received"
+msgstr "έλαβε μη αναμενόμενο αριθμό τμημάτων δεδομένων"
+
+#: libpq_source.c:606
+#, c-format
+msgid "could not fetch remote file \"%s\": %s"
+msgstr "δεν ήταν δυνατή η λήψη απομακρυσμένου αρχείου «%s»: %s"
+
+#: libpq_source.c:611
+#, c-format
+msgid "unexpected result set while fetching remote file \"%s\""
+msgstr "μη αναμενόμενο σύνολο αποτελεσμάτων κατά τη λήψη απομακρυσμένου αρχείου «%s»"
+
+#: local_source.c:86
+#, c-format
+msgid "could not open source file \"%s\": %m"
+msgstr "δεν ήταν δυνατό το άνοιγμα του αρχείου προέλευσης «%s»: %m"
+
+#: local_source.c:90
+#, c-format
+msgid "could not seek in source file: %m"
+msgstr "δεν ήταν δυνατή η αναζήτηση στο αρχείο προέλευσης: %m"
+
+#: local_source.c:109
+#, c-format
+msgid "unexpected EOF while reading file \"%s\""
+msgstr "μη αναμενόμενο EOF κατά την ανάγνωση αρχείου «%s»"
+
+#: local_source.c:116
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "δεν ήταν δυνατό το κλείσιμο του αρχείου «%s»: %m"
+
+#: parsexlog.c:89 parsexlog.c:142
+#, c-format
+msgid "could not read WAL record at %X/%X: %s"
+msgstr "δεν ήταν δυνατή η ανάγνωση WAL εγγραφής στο %X/%X: %s"
+
+#: parsexlog.c:93 parsexlog.c:145
+#, c-format
+msgid "could not read WAL record at %X/%X"
+msgstr "δεν ήταν δυνατή η ανάγνωση WAL εγγραφής στο %X/%X"
+
+#: parsexlog.c:208
+#, c-format
+msgid "could not find previous WAL record at %X/%X: %s"
+msgstr "δεν ήταν δυνατή η εύρεση προηγούμενης WAL εγγραφής σε %X/%X: %s"
+
+#: parsexlog.c:212
+#, c-format
+msgid "could not find previous WAL record at %X/%X"
+msgstr "δεν ήταν δυνατή η εύρεση προηγούμενης WAL εγγραφής σε %X/%X"
+
+#: parsexlog.c:337
+#, c-format
+msgid "could not seek in file \"%s\": %m"
+msgstr "δεν ήταν δυνατή η αναζήτηση στο αρχείο «%s»: %m"
+
+#: parsexlog.c:429
+#, c-format
+msgid "WAL record modifies a relation, but record type is not recognized: lsn: %X/%X, rmgr: %s, info: %02X"
+msgstr "Η εγγραφή WAL τροποποιεί μια σχέση, αλλά ο τύπος εγγραφής δεν αναγνωρίζεται: lsn: %X/%X, rmgr: %s, info: %02X"
+
+#: pg_rewind.c:84
+#, c-format
+msgid ""
+"%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n"
+"\n"
+msgstr ""
+"%s επανασυγχρονίζει μία συστάδα PostgreSQL με ένα άλλο αντίγραφο της συστάδας.\n"
+"\n"
+
+#: pg_rewind.c:85
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]...\n"
+"\n"
+msgstr ""
+"Χρήση:\n"
+" %s [ΕΠΙΛΟΓΗ]...\n"
+"\n"
+
+#: pg_rewind.c:86
+#, c-format
+msgid "Options:\n"
+msgstr "Επιλογές:\n"
+
+#: pg_rewind.c:87
+#, c-format
+msgid ""
+" -c, --restore-target-wal use restore_command in target configuration to\n"
+" retrieve WAL files from archives\n"
+msgstr ""
+" -c, --restore-target-wal χρησιμοποίησε restore_command στη ρύθμιση προορισμού για την\n"
+" ανάκτηση αρχείων WAL από αρχειοθήκες\n"
+
+#: pg_rewind.c:89
+#, c-format
+msgid " -D, --target-pgdata=DIRECTORY existing data directory to modify\n"
+msgstr " -D, --target-pgdata=DIRECTORY υπάρχον κατάλογος δεδομένων προς τροποποιήση\n"
+
+#: pg_rewind.c:90
+#, c-format
+msgid " --source-pgdata=DIRECTORY source data directory to synchronize with\n"
+msgstr " --source-pgdata=DIRECTORY κατάλογος δεδομένων προέλευσης για συγχρονισμό\n"
+
+#: pg_rewind.c:91
+#, c-format
+msgid " --source-server=CONNSTR source server to synchronize with\n"
+msgstr " --source-server=CONNSTR διακομιστής προέλευσης για συγχρονισμό\n"
+
+#: pg_rewind.c:92
+#, c-format
+msgid " -n, --dry-run stop before modifying anything\n"
+msgstr " -n, --dry-run τερματισμός πριν να τροποποιηθεί οτιδήποτε\n"
+
+#: pg_rewind.c:93
+#, c-format
+msgid ""
+" -N, --no-sync do not wait for changes to be written\n"
+" safely to disk\n"
+msgstr " -N, --no-sync να μην αναμένει την ασφαλή εγγραφή αλλαγών στον δίσκο\n"
+
+#: pg_rewind.c:95
+#, c-format
+msgid " -P, --progress write progress messages\n"
+msgstr " -P, --progress εμφάνισε πληροφορίες προόδου\n"
+
+#: pg_rewind.c:96
+#, c-format
+msgid ""
+" -R, --write-recovery-conf write configuration for replication\n"
+" (requires --source-server)\n"
+msgstr ""
+" -R, --write-recovery-conf εγγραφή των ρυθμίσεων αναπαραγωγής\n"
+" (απαιτεί --source-server)\n"
+
+#: pg_rewind.c:98
+#, c-format
+msgid " --debug write a lot of debug messages\n"
+msgstr " --debug εγγραφή πολλών μηνύματων εντοπισμού σφαλμάτων\n"
+
+#: pg_rewind.c:99
+#, c-format
+msgid " --no-ensure-shutdown do not automatically fix unclean shutdown\n"
+msgstr " --no-ensure-shutdown να μην διορθώνει αυτόματα ακάθαρτο τερματισμό\n"
+
+#: pg_rewind.c:100
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version εμφάνισε πληροφορίες έκδοσης, στη συνέχεια έξοδος\n"
+
+#: pg_rewind.c:101
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help εμφάνισε αυτό το μήνυμα βοήθειας, στη συνέχεια έξοδος\n"
+
+#: pg_rewind.c:102
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"Υποβάλετε αναφορές σφάλματων σε <%s>.\n"
+
+#: pg_rewind.c:103
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "%s αρχική σελίδα: <%s>\n"
+
+#: pg_rewind.c:164 pg_rewind.c:213 pg_rewind.c:220 pg_rewind.c:227
+#: pg_rewind.c:234 pg_rewind.c:242
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "Δοκιμάστε «%s --help» για περισσότερες πληροφορίες.\n"
+
+#: pg_rewind.c:212
+#, c-format
+msgid "no source specified (--source-pgdata or --source-server)"
+msgstr "δεν καθορίστηκε προέλευση (--source-pgdata ή --source-server)"
+
+#: pg_rewind.c:219
+#, c-format
+msgid "only one of --source-pgdata or --source-server can be specified"
+msgstr "μόνο ένα από τα --source-pgdata ή --source-server μπορεί να καθοριστεί"
+
+#: pg_rewind.c:226
+#, c-format
+msgid "no target data directory specified (--target-pgdata)"
+msgstr "δεν καθορίστηκε κατάλογος δεδομένων προορισμού (--target-pgdata)"
+
+#: pg_rewind.c:233
+#, c-format
+msgid "no source server information (--source-server) specified for --write-recovery-conf"
+msgstr "δεν καθορίστηκαν πληροφορίες διακομιστή προέλευσης (--source-server) για --write-recovery-conf"
+
+#: pg_rewind.c:240
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "πάρα πολλές παράμετροι εισόδου από την γραμμή εντολών (η πρώτη είναι η «%s»)"
+
+#: pg_rewind.c:255
+#, c-format
+msgid "cannot be executed by \"root\""
+msgstr "δεν είναι δυνατή η εκτέλεση από «root»"
+
+#: pg_rewind.c:256
+#, c-format
+msgid "You must run %s as the PostgreSQL superuser.\n"
+msgstr "Πρέπει να εκτελέσετε %s ως υπερχρήστης PostgreSQL.\n"
+
+#: pg_rewind.c:267
+#, c-format
+msgid "could not read permissions of directory \"%s\": %m"
+msgstr "δεν ήταν δυνατή η ανάγνωση δικαιωμάτων του καταλόγου «%s»: %m"
+
+#: pg_rewind.c:287
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: pg_rewind.c:290
+#, c-format
+msgid "connected to server"
+msgstr "συνδεδεμένος στον διακομιστή"
+
+#: pg_rewind.c:337
+#, c-format
+msgid "source and target cluster are on the same timeline"
+msgstr "συστάδες προορισμού και προέλευσης βρίσκονται στην ίδια χρονογραμμή"
+
+#: pg_rewind.c:346
+#, c-format
+msgid "servers diverged at WAL location %X/%X on timeline %u"
+msgstr "οι διακομιστές αποκλίνουν στην τοποθεσία WAL %X/%X στη χρονογραμμή %u"
+
+#: pg_rewind.c:394
+#, c-format
+msgid "no rewind required"
+msgstr "δεν απαιτείται επαναφορά"
+
+#: pg_rewind.c:403
+#, c-format
+msgid "rewinding from last common checkpoint at %X/%X on timeline %u"
+msgstr "επαναφορά από το τελευταίο κοινό σημείο ελέγχου στο %X/%X στη χρονογραμμή %u"
+
+#: pg_rewind.c:413
+#, c-format
+msgid "reading source file list"
+msgstr "ανάγνωση λίστας αρχείων προέλευσης"
+
+#: pg_rewind.c:417
+#, c-format
+msgid "reading target file list"
+msgstr "ανάγνωση λίστας αρχείων προορισμού"
+
+#: pg_rewind.c:426
+#, c-format
+msgid "reading WAL in target"
+msgstr "ανάγνωση WAL στον προορισμό"
+
+#: pg_rewind.c:447
+#, c-format
+msgid "need to copy %lu MB (total source directory size is %lu MB)"
+msgstr "πρέπει να αντιγραφούν %lu MB (το συνολικό μέγεθος καταλόγου προέλευσης είναι %lu MB)"
+
+#: pg_rewind.c:465
+#, c-format
+msgid "syncing target data directory"
+msgstr "συγχρονισμός καταλόγου δεδομένων προορισμού"
+
+#: pg_rewind.c:481
+#, c-format
+msgid "Done!"
+msgstr "Ολοκληρώθηκε!"
+
+#: pg_rewind.c:564
+#, c-format
+msgid "no action decided for file \"%s\""
+msgstr "καμία ενέργεια δεν αποφασίστηκε για το αρχείο «%s»"
+
+#: pg_rewind.c:596
+#, c-format
+msgid "source system was modified while pg_rewind was running"
+msgstr "το σύστημα προέλευσης τροποποιήθηκε κατά την εκτέλεση του pg_rewind"
+
+#: pg_rewind.c:600
+#, c-format
+msgid "creating backup label and updating control file"
+msgstr "δημιουργία ετικέτας αντιγράφων ασφαλείας και ενημέρωση αρχείου ελέγχου"
+
+#: pg_rewind.c:650
+#, c-format
+msgid "source system was in unexpected state at end of rewind"
+msgstr "το σύστημα προέλευσης βρισκόταν σε μη αναμενόμενη κατάσταση στο τέλος της επαναφοράς"
+
+#: pg_rewind.c:681
+#, c-format
+msgid "source and target clusters are from different systems"
+msgstr "οι συστάδες προέλευσης και προορισμού προέρχονται από διαφορετικά συστήματα"
+
+#: pg_rewind.c:689
+#, c-format
+msgid "clusters are not compatible with this version of pg_rewind"
+msgstr "η συστάδα δεν είναι συμβατή με αυτήν την έκδοση pg_rewind"
+
+#: pg_rewind.c:699
+#, c-format
+msgid "target server needs to use either data checksums or \"wal_log_hints = on\""
+msgstr "ο διακομιστής προορισμού πρέπει να χρησιμοποιεί είτε άθροισμα ελέγχου δεδομένων είτε «wal_log_hints = on»"
+
+#: pg_rewind.c:710
+#, c-format
+msgid "target server must be shut down cleanly"
+msgstr "ο διακομιστής προορισμού πρέπει να τερματιστεί καθαρά"
+
+#: pg_rewind.c:720
+#, c-format
+msgid "source data directory must be shut down cleanly"
+msgstr "ο κατάλογος δεδομένων προέλευσης πρέπει να τερματιστεί καθαρά"
+
+#: pg_rewind.c:772
+#, c-format
+msgid "%*s/%s kB (%d%%) copied"
+msgstr "%*s/%s kB (%d%%) αντιγράφηκαν"
+
+#: pg_rewind.c:835
+#, c-format
+msgid "invalid control file"
+msgstr "μη έγκυρο αρχείο ελέγχου"
+
+#: pg_rewind.c:919
+#, c-format
+msgid "could not find common ancestor of the source and target cluster's timelines"
+msgstr "δεν ήταν δυνατή η εύρεση κοινού προγόνου των χρονογραμμών των συστάδων προέλευσης και προορισμού"
+
+#: pg_rewind.c:960
+#, c-format
+msgid "backup label buffer too small"
+msgstr "ενδιάμεση μνήμη ετικέτας αντιγράφων ασφαλείας πολύ μικρή"
+
+#: pg_rewind.c:983
+#, c-format
+msgid "unexpected control file CRC"
+msgstr "μη αναμενόμενο αρχείο ελέγχου CRC"
+
+#: pg_rewind.c:995
+#, c-format
+msgid "unexpected control file size %d, expected %d"
+msgstr "μη αναμενόμενο μέγεθος αρχείου ελέγχου %d, αναμένεται %d"
+
+#: pg_rewind.c:1004
+#, c-format
+msgid "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d byte"
+msgid_plural "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d bytes"
+msgstr[0] "η τιμή του μεγέθους τμήματος WAL πρέπει να ανήκει σε δύναμη του δύο μεταξύ 1 MB και 1 GB, αλλά το αρχείο ελέγχου καθορίζει %d byte"
+msgstr[1] "η τιμή του μεγέθους τμήματος WAL πρέπει να ανήκει σε δύναμη του δύο μεταξύ 1 MB και 1 GB, αλλά το αρχείο ελέγχου καθορίζει %d bytes"
+
+#: pg_rewind.c:1043 pg_rewind.c:1101
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"Το πρόγραμμα «%s» απαιτείται από %s αλλά δεν βρέθηκε στον\n"
+"ίδιο κατάλογο με το «%s».\n"
+"Ελέγξτε την εγκατάστασή σας."
+
+#: pg_rewind.c:1048 pg_rewind.c:1106
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"Το πρόγραμμα «%s» βρέθηκε από το \"%s\"\n"
+"αλλά δεν ήταν η ίδια εκδοχή με %s.\n"
+"Ελέγξτε την εγκατάστασή σας."
+
+#: pg_rewind.c:1069
+#, c-format
+msgid "restore_command is not set in the target cluster"
+msgstr "η εντολή restore_command δεν έχει οριστεί στη συστάδα προορισμού"
+
+#: pg_rewind.c:1112
+#, c-format
+msgid "executing \"%s\" for target server to complete crash recovery"
+msgstr "εκτέλεση «%s» για την ολοκλήρωση της αποκατάστασης σφαλμάτων του διακομιστή προορισμού"
+
+#: pg_rewind.c:1132
+#, c-format
+msgid "postgres single-user mode in target cluster failed"
+msgstr "λειτουργία μοναδικού-χρήστη postgres στο σύμπλεγμα προορισμού απέτυχε"
+
+#: pg_rewind.c:1133
+#, c-format
+msgid "Command was: %s"
+msgstr "Η εντολή ήταν: %s"
+
+#: timeline.c:75 timeline.c:81
+#, c-format
+msgid "syntax error in history file: %s"
+msgstr "συντακτικό σφάλμα στο αρχείο ιστορικού: %s"
+
+#: timeline.c:76
+#, c-format
+msgid "Expected a numeric timeline ID."
+msgstr "Αναμένεται αριθμητικό ID χρονογραμμής."
+
+#: timeline.c:82
+#, c-format
+msgid "Expected a write-ahead log switchpoint location."
+msgstr "Αναμένεται μια θέση write-ahead log switchpoint."
+
+#: timeline.c:87
+#, c-format
+msgid "invalid data in history file: %s"
+msgstr "μη έγκυρα δεδομένα στο αρχείο ιστορικού: %s"
+
+#: timeline.c:88
+#, c-format
+msgid "Timeline IDs must be in increasing sequence."
+msgstr "Τα IDs χρονογραμμής πρέπει να βρίσκονται σε αυξάνουσα σειρά."
+
+#: timeline.c:108
+#, c-format
+msgid "invalid data in history file"
+msgstr "μη έγκυρα δεδομένα στο αρχείο ιστορικού"
+
+#: timeline.c:109
+#, c-format
+msgid "Timeline IDs must be less than child timeline's ID."
+msgstr "Τα ID χρονογραμμής πρέπει να είναι λιγότερα από τα ID της χρονογραμμής απογόνου."
+
+#: xlogreader.c:349
+#, c-format
+msgid "invalid record offset at %X/%X"
+msgstr "μη έγκυρη μετατόπιση εγγραφών σε %X/%X"
+
+#: xlogreader.c:357
+#, c-format
+msgid "contrecord is requested by %X/%X"
+msgstr "contrecord ζητείται από %X/%X"
+
+#: xlogreader.c:398 xlogreader.c:695
+#, c-format
+msgid "invalid record length at %X/%X: wanted %u, got %u"
+msgstr "μη έγκυρο μήκος εγγραφής σε %X/%X: χρειαζόταν %u, έλαβε %u"
+
+#: xlogreader.c:422
+#, c-format
+msgid "record length %u at %X/%X too long"
+msgstr "μήκος εγγραφής %u σε %X/%X πολύ μακρύ"
+
+#: xlogreader.c:453
+#, c-format
+msgid "there is no contrecord flag at %X/%X"
+msgstr "δεν υπάρχει σημαία contrecord στο %X/%X"
+
+#: xlogreader.c:466
+#, c-format
+msgid "invalid contrecord length %u (expected %lld) at %X/%X"
+msgstr "μη έγκυρο μήκος contrecord %u (αναμένεται %lld) σε %X/%X"
+
+#: xlogreader.c:703
+#, c-format
+msgid "invalid resource manager ID %u at %X/%X"
+msgstr "μη έγκυρο ID %u διαχειριστή πόρων στο %X/%X"
+
+#: xlogreader.c:716 xlogreader.c:732
+#, c-format
+msgid "record with incorrect prev-link %X/%X at %X/%X"
+msgstr "εγγραφή με εσφαλμένο prev-link %X/%X σε %X/%X"
+
+#: xlogreader.c:768
+#, c-format
+msgid "incorrect resource manager data checksum in record at %X/%X"
+msgstr "εσφαλμένο άθροισμα ελέγχου δεδομένων διαχειριστή πόρων σε εγγραφή στο %X/%X"
+
+#: xlogreader.c:805
+#, c-format
+msgid "invalid magic number %04X in log segment %s, offset %u"
+msgstr "μη έγκυρος μαγικός αριθμός %04X στο τμήμα καταγραφής %s, μετατόπιση %u"
+
+#: xlogreader.c:819 xlogreader.c:860
+#, c-format
+msgid "invalid info bits %04X in log segment %s, offset %u"
+msgstr "μη έγκυρα info bits %04X στο τμήμα καταγραφής %s, μετατόπιση %u"
+
+#: xlogreader.c:834
+#, c-format
+msgid "WAL file is from different database system: WAL file database system identifier is %llu, pg_control database system identifier is %llu"
+msgstr "WAL αρχείο προέρχεται από διαφορετικό σύστημα βάσης δεδομένων: το WAL αναγνωριστικό συστήματος βάσης δεδομένων αρχείων είναι %llu, το pg_control αναγνωριστικό συστήματος βάσης δεδομένων είναι %llu"
+
+#: xlogreader.c:842
+#, c-format
+msgid "WAL file is from different database system: incorrect segment size in page header"
+msgstr "WAL αρχείο προέρχεται από διαφορετικό σύστημα βάσης δεδομένων: εσφαλμένο μέγεθος τμήματος στην κεφαλίδα σελίδας"
+
+#: xlogreader.c:848
+#, c-format
+msgid "WAL file is from different database system: incorrect XLOG_BLCKSZ in page header"
+msgstr "WAL αρχείο προέρχεται από διαφορετικό σύστημα βάσης δεδομένων: εσφαλμένο XLOG_BLCKSZ στην κεφαλίδα σελίδας"
+
+#: xlogreader.c:879
+#, c-format
+msgid "unexpected pageaddr %X/%X in log segment %s, offset %u"
+msgstr "μη αναμενόμενο pageaddr %X/%X στο τμήμα καταγραφής %s, μετατόπιση %u"
+
+#: xlogreader.c:904
+#, c-format
+msgid "out-of-sequence timeline ID %u (after %u) in log segment %s, offset %u"
+msgstr "εκτός ακολουθίας ID χρονογραμμής %u (μετά %u) στο τμήμα καταγραφής %s, μετατόπιση %u"
+
+#: xlogreader.c:1249
+#, c-format
+msgid "out-of-order block_id %u at %X/%X"
+msgstr "εκτός ακολουθίας block_id %u στο %X/%X"
+
+#: xlogreader.c:1271
+#, c-format
+msgid "BKPBLOCK_HAS_DATA set, but no data included at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA έχει οριστεί, αλλά δεν περιλαμβάνονται δεδομένα σε %X/%X"
+
+#: xlogreader.c:1278
+#, c-format
+msgid "BKPBLOCK_HAS_DATA not set, but data length is %u at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA δεν έχει οριστεί, αλλά το μήκος των δεδομένων είναι %u σε %X/%X"
+
+#: xlogreader.c:1314
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE set, but hole offset %u length %u block image length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE έχει οριστεί, αλλά οπή με μετατόπιση %u μήκος %u μήκος μπλοκ εικόνας %u σε %X/%X"
+
+#: xlogreader.c:1330
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE not set, but hole offset %u length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE δεν έχει οριστεί, αλλά οπή με μετατόπιση %u μήκος %u σε %X/%X"
+
+#: xlogreader.c:1345
+#, c-format
+msgid "BKPIMAGE_IS_COMPRESSED set, but block image length %u at %X/%X"
+msgstr "BKPIMAGE_IS_COMPRESSED έχει οριστεί, αλλά μέγεθοσ μπλοκ εικόνας %u σε %X/%X"
+
+#: xlogreader.c:1360
+#, c-format
+msgid "neither BKPIMAGE_HAS_HOLE nor BKPIMAGE_IS_COMPRESSED set, but block image length is %u at %X/%X"
+msgstr "ούτε BKPIMAGE_HAS_HOLE ούτε BKPIMAGE_IS_COMPRESSED είναι ορισμένα, αλλά το μήκος της εικόνας μπλοκ είναι %u στο %X/%X"
+
+#: xlogreader.c:1376
+#, c-format
+msgid "BKPBLOCK_SAME_REL set but no previous rel at %X/%X"
+msgstr "BKPBLOCK_SAME_REL είναι ορισμένο, αλλά καμία προηγούμενη rel στο %X/%X"
+
+#: xlogreader.c:1388
+#, c-format
+msgid "invalid block_id %u at %X/%X"
+msgstr "μη έγκυρο block_id %u στο %X/%X"
+
+#: xlogreader.c:1475
+#, c-format
+msgid "record with invalid length at %X/%X"
+msgstr "εγγραφή με μη έγκυρο μήκος στο %X/%X"
+
+#: xlogreader.c:1564
+#, c-format
+msgid "invalid compressed image at %X/%X, block %d"
+msgstr "μη έγκυρη συμπιεσμένη εικόνα στο %X/%X, μπλοκ %d"
diff --git a/src/bin/pg_rewind/po/es.po b/src/bin/pg_rewind/po/es.po
new file mode 100644
index 0000000..4cc541d
--- /dev/null
+++ b/src/bin/pg_rewind/po/es.po
@@ -0,0 +1,967 @@
+# Spanish message translation file for pg_rewind
+#
+# Copyright (c) 2015-2021, PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+#
+# Álvaro Herrera <alvherre@alvh.no-ip.org>, 2015.
+# Carlos Chapi <carloswaldo@babelruins.org>, 2017, 2021.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_rewind (PostgreSQL) 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-08-07 20:36+0000\n"
+"PO-Revision-Date: 2022-08-08 01:00+0200\n"
+"Last-Translator: Carlos Chapi <carloswaldo@babelruins.org>\n"
+"Language-Team: PgSQL-es-Ayuda <pgsql-es-ayuda@lists.postgresql.org>\n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.4.3\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "fatal: "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "error: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "precaución: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "memoria agotada\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "no se puede duplicar un puntero nulo (error interno)\n"
+
+#: ../../common/restricted_token.c:64
+#, c-format
+msgid "could not load library \"%s\": error code %lu"
+msgstr "no se pudo cargar la biblioteca «%s»: código de error %lu"
+
+#: ../../common/restricted_token.c:73
+#, c-format
+msgid "cannot create restricted tokens on this platform: error code %lu"
+msgstr "no se pueden crear tokens restrigidos en esta plataforma: código de error %lu"
+
+#: ../../common/restricted_token.c:82
+#, c-format
+msgid "could not open process token: error code %lu"
+msgstr "no se pudo abrir el token de proceso: código de error %lu"
+
+#: ../../common/restricted_token.c:97
+#, c-format
+msgid "could not allocate SIDs: error code %lu"
+msgstr "no se pudo emplazar los SIDs: código de error %lu"
+
+#: ../../common/restricted_token.c:119
+#, c-format
+msgid "could not create restricted token: error code %lu"
+msgstr "no se pudo crear el token restringido: código de error %lu"
+
+#: ../../common/restricted_token.c:140
+#, c-format
+msgid "could not start process for command \"%s\": error code %lu"
+msgstr "no se pudo iniciar el proceso para la orden «%s»: código de error %lu"
+
+#: ../../common/restricted_token.c:178
+#, c-format
+msgid "could not re-execute with restricted token: error code %lu"
+msgstr "no se pudo re-ejecutar con el token restringido: código de error %lu"
+
+#: ../../common/restricted_token.c:194
+#, c-format
+msgid "could not get exit code from subprocess: error code %lu"
+msgstr "no se pudo obtener el código de salida del subproceso»: código de error %lu"
+
+#: ../../fe_utils/archive.c:53
+#, c-format
+msgid "cannot use restore_command with %%r placeholder"
+msgstr "no se puede usar restore_command con el marcador %%r"
+
+#: ../../fe_utils/archive.c:74
+#, c-format
+msgid "unexpected file size for \"%s\": %lld instead of %lld"
+msgstr "el archivo «%s» tiene tamaño inesperado: %lld en lugar de %lld"
+
+#: ../../fe_utils/archive.c:85
+#, c-format
+msgid "could not open file \"%s\" restored from archive: %m"
+msgstr "no se pudo abrir el archivo «%s» restaurado del archivo: %m"
+
+#: ../../fe_utils/archive.c:97 file_ops.c:417
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "no se pudo hacer stat al archivo «%s»: %m"
+
+#: ../../fe_utils/archive.c:112
+#, c-format
+msgid "restore_command failed: %s"
+msgstr "restore_command falló: %s"
+
+#: ../../fe_utils/archive.c:121
+#, c-format
+msgid "could not restore file \"%s\" from archive"
+msgstr "no se pudo recuperar el archivo «%s» del archivo"
+
+#: ../../fe_utils/recovery_gen.c:35 ../../fe_utils/recovery_gen.c:49
+#: ../../fe_utils/recovery_gen.c:77 ../../fe_utils/recovery_gen.c:100
+#: ../../fe_utils/recovery_gen.c:171 parsexlog.c:77 parsexlog.c:137
+#: parsexlog.c:197
+#, c-format
+msgid "out of memory"
+msgstr "memoria agotada"
+
+#: ../../fe_utils/recovery_gen.c:134 parsexlog.c:310
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "no se pudo abrir el archivo «%s»: %m"
+
+#: ../../fe_utils/recovery_gen.c:140
+#, c-format
+msgid "could not write to file \"%s\": %m"
+msgstr "no se pudo escribir a archivo «%s»: %m"
+
+#: ../../fe_utils/recovery_gen.c:152
+#, c-format
+msgid "could not create file \"%s\": %m"
+msgstr "no se pudo crear archivo «%s»: %m"
+
+#: file_ops.c:67
+#, c-format
+msgid "could not open target file \"%s\": %m"
+msgstr "no se pudo abrir el archivo de destino «%s»: %m"
+
+#: file_ops.c:81
+#, c-format
+msgid "could not close target file \"%s\": %m"
+msgstr "no se pudo cerrar el archivo de destino «%s»: %m"
+
+#: file_ops.c:101
+#, c-format
+msgid "could not seek in target file \"%s\": %m"
+msgstr "no se pudo posicionar en archivo de destino «%s»: %m"
+
+#: file_ops.c:117
+#, c-format
+msgid "could not write file \"%s\": %m"
+msgstr "no se pudo escribir el archivo «%s»: %m"
+
+#: file_ops.c:150 file_ops.c:177
+#, c-format
+msgid "undefined file type for \"%s\""
+msgstr "tipo de archivo no definido para «%s»"
+
+#: file_ops.c:173
+#, c-format
+msgid "invalid action (CREATE) for regular file"
+msgstr "acción no válida (CREATE) para archivo regular"
+
+#: file_ops.c:200
+#, c-format
+msgid "could not remove file \"%s\": %m"
+msgstr "no se pudo eliminar el archivo «%s»: %m"
+
+#: file_ops.c:218
+#, c-format
+msgid "could not open file \"%s\" for truncation: %m"
+msgstr "no se pudo abrir el archivo «%s» para truncarlo: %m"
+
+#: file_ops.c:222
+#, c-format
+msgid "could not truncate file \"%s\" to %u: %m"
+msgstr "no se pudo truncar el archivo «%s» a %u: %m"
+
+#: file_ops.c:238
+#, c-format
+msgid "could not create directory \"%s\": %m"
+msgstr "no se pudo crear el directorio «%s»: %m"
+
+#: file_ops.c:252
+#, c-format
+msgid "could not remove directory \"%s\": %m"
+msgstr "no se pudo eliminar el directorio «%s»: %m"
+
+#: file_ops.c:266
+#, c-format
+msgid "could not create symbolic link at \"%s\": %m"
+msgstr "no se pudo crear el link simbólico en «%s»: %m"
+
+#: file_ops.c:280
+#, c-format
+msgid "could not remove symbolic link \"%s\": %m"
+msgstr "no se pudo eliminar el enlace simbólico «%s»: %m"
+
+#: file_ops.c:326 file_ops.c:330
+#, c-format
+msgid "could not open file \"%s\" for reading: %m"
+msgstr "no se pudo abrir archivo «%s» para lectura: %m"
+
+#: file_ops.c:341 local_source.c:107 parsexlog.c:348
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "no se pudo leer el archivo «%s»: %m"
+
+#: file_ops.c:344 parsexlog.c:350
+#, c-format
+msgid "could not read file \"%s\": read %d of %zu"
+msgstr "no se pudo leer el archivo «%s»: leídos %d de %zu"
+
+#: file_ops.c:388
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "no se pudo abrir el directorio «%s»: %m"
+
+#: file_ops.c:446
+#, c-format
+msgid "could not read symbolic link \"%s\": %m"
+msgstr "no se pudo leer el enlace simbólico «%s»: %m"
+
+#: file_ops.c:449
+#, c-format
+msgid "symbolic link \"%s\" target is too long"
+msgstr "la ruta «%s» del enlace simbólico es demasiado larga"
+
+#: file_ops.c:464
+#, c-format
+msgid "\"%s\" is a symbolic link, but symbolic links are not supported on this platform"
+msgstr "«%s» es un link simbólico, pero los links simbólicos no están soportados en esta plataforma"
+
+#: file_ops.c:471
+#, c-format
+msgid "could not read directory \"%s\": %m"
+msgstr "no se pudo leer el directorio «%s»: %m"
+
+#: file_ops.c:475
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "no se pudo abrir el directorio «%s»: %m"
+
+#: filemap.c:237
+#, c-format
+msgid "data file \"%s\" in source is not a regular file"
+msgstr "el archivo de datos «%s» en el origen no es un archivo regular"
+
+#: filemap.c:242 filemap.c:275
+#, c-format
+msgid "duplicate source file \"%s\""
+msgstr "archivo origen duplicado «%s»"
+
+#: filemap.c:330
+#, c-format
+msgid "unexpected page modification for non-regular file \"%s\""
+msgstr "modificación de página inesperada para el archivo no regular «%s»"
+
+#: filemap.c:680 filemap.c:774
+#, c-format
+msgid "unknown file type for \"%s\""
+msgstr "tipo de archivo desconocido para «%s»"
+
+#: filemap.c:707
+#, c-format
+msgid "file \"%s\" is of different type in source and target"
+msgstr "el archivo «%s» tiene un tipo diferente en el origen y en el destino"
+
+#: filemap.c:779
+#, c-format
+msgid "could not decide what to do with file \"%s\""
+msgstr "no se pudo decidir qué hacer con el archivo «%s»"
+
+#: libpq_source.c:128
+#, c-format
+msgid "could not clear search_path: %s"
+msgstr "no se pudo limpiar search_path: %s"
+
+#: libpq_source.c:139
+#, c-format
+msgid "full_page_writes must be enabled in the source server"
+msgstr "full_page_writes debe estar activado en el servidor de origen"
+
+#: libpq_source.c:150
+#, c-format
+msgid "could not prepare statement to fetch file contents: %s"
+msgstr "no se pudo preparar sentencia para obtener el contenido del archivo: %s"
+
+#: libpq_source.c:169
+#, c-format
+msgid "error running query (%s) on source server: %s"
+msgstr "error ejecutando consulta (%s) en el servidor de origen: %s"
+
+#: libpq_source.c:174
+#, c-format
+msgid "unexpected result set from query"
+msgstr "conjunto de resultados inesperados de la consulta"
+
+#: libpq_source.c:196
+#, c-format
+msgid "error running query (%s) in source server: %s"
+msgstr "error ejecutando consulta (%s) en el servidor de origen: %s"
+
+#: libpq_source.c:217
+#, c-format
+msgid "unrecognized result \"%s\" for current WAL insert location"
+msgstr "resultado «%s» no reconocido para la ubicación de inserción WAL actual"
+
+#: libpq_source.c:268
+#, c-format
+msgid "could not fetch file list: %s"
+msgstr "no se pudo obtener el listado de archivos: %s"
+
+#: libpq_source.c:273
+#, c-format
+msgid "unexpected result set while fetching file list"
+msgstr "conjunto de resultados inesperado mientras se obtenía el listado de archivos"
+
+#: libpq_source.c:435
+#, c-format
+msgid "could not send query: %s"
+msgstr "no se pudo enviar la consulta: %s"
+
+#: libpq_source.c:438
+#, c-format
+msgid "could not set libpq connection to single row mode"
+msgstr "no se pudo establecer la coneción libpq a modo «single row»"
+
+#: libpq_source.c:468
+#, c-format
+msgid "unexpected result while fetching remote files: %s"
+msgstr "resultados inesperados mientras se obtenían archivos remotos: %s"
+
+#: libpq_source.c:473
+#, c-format
+msgid "received more data chunks than requested"
+msgstr "se recibieron más trozos de datos que los solicitados"
+
+#: libpq_source.c:477
+#, c-format
+msgid "unexpected result set size while fetching remote files"
+msgstr "tamaño del conjunto de resultados inesperado mientras se obtenían archivos remotos"
+
+#: libpq_source.c:483
+#, c-format
+msgid "unexpected data types in result set while fetching remote files: %u %u %u"
+msgstr "tipos de dato inesperados en el conjunto de resultados mientras se obtenían archivos remotos: %u %u %u"
+
+#: libpq_source.c:491
+#, c-format
+msgid "unexpected result format while fetching remote files"
+msgstr "formato de resultados inesperado mientras se obtenían archivos remotos"
+
+#: libpq_source.c:497
+#, c-format
+msgid "unexpected null values in result while fetching remote files"
+msgstr "valores nulos inesperados en el resultado mientras se obtenían archivos remotos"
+
+#: libpq_source.c:501
+#, c-format
+msgid "unexpected result length while fetching remote files"
+msgstr "largo del resultado inesperado mientras se obtenían los archivos remotos"
+
+#: libpq_source.c:534
+#, c-format
+msgid "received data for file \"%s\", when requested for \"%s\""
+msgstr "se recibieron datos para el archivo «%s», cuando se solicitó para «%s»"
+
+#: libpq_source.c:538
+#, c-format
+msgid "received data at offset %lld of file \"%s\", when requested for offset %lld"
+msgstr "se recibieron datos en la posición %lld del archivo «%s», cuando se solicitó para la posición %lld"
+
+#: libpq_source.c:550
+#, c-format
+msgid "received more than requested for file \"%s\""
+msgstr "se recibió más de lo solicitado para el archivo «%s»"
+
+#: libpq_source.c:563
+#, c-format
+msgid "unexpected number of data chunks received"
+msgstr "se recibió un número inesperado de trozos de datos"
+
+#: libpq_source.c:606
+#, c-format
+msgid "could not fetch remote file \"%s\": %s"
+msgstr "no se pudo obtener el archivo remoto «%s»: %s"
+
+#: libpq_source.c:611
+#, c-format
+msgid "unexpected result set while fetching remote file \"%s\""
+msgstr "conjunto de resultados inesperado mientras se obtenía el archivo remoto «%s»"
+
+#: local_source.c:86
+#, c-format
+msgid "could not open source file \"%s\": %m"
+msgstr "no se pudo abrir el archivo de origen «%s»: %m"
+
+#: local_source.c:90
+#, c-format
+msgid "could not seek in source file: %m"
+msgstr "no se pudo posicionar en archivo de origen: %m"
+
+#: local_source.c:109
+#, c-format
+msgid "unexpected EOF while reading file \"%s\""
+msgstr "EOF inesperado mientras se leía el archivo «%s»"
+
+#: local_source.c:116
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "no se pudo cerrar el archivo «%s»: %m"
+
+#: parsexlog.c:89 parsexlog.c:144
+#, c-format
+msgid "could not read WAL record at %X/%X: %s"
+msgstr "no se pudo leer el registro WAL en %X/%X: %s"
+
+#: parsexlog.c:93 parsexlog.c:147
+#, c-format
+msgid "could not read WAL record at %X/%X"
+msgstr "no se pudo leer el registro WAL en %X/%X"
+
+#: parsexlog.c:106
+#, c-format
+msgid "end pointer %X/%X is not a valid end point; expected %X/%X"
+msgstr "ubicación final %X/%X no es un punto válid de término; se esperaba %X/%X"
+
+#: parsexlog.c:210
+#, c-format
+msgid "could not find previous WAL record at %X/%X: %s"
+msgstr "no se pudo encontrar el registro WAL anterior en %X/%X: %s"
+
+#: parsexlog.c:214
+#, c-format
+msgid "could not find previous WAL record at %X/%X"
+msgstr "no se pudo encontrar el registro WAL anterior en %X/%X"
+
+#: parsexlog.c:339
+#, c-format
+msgid "could not seek in file \"%s\": %m"
+msgstr "no se pudo posicionar (seek) el archivo «%s»: %m"
+
+#: parsexlog.c:431
+#, c-format
+msgid "WAL record modifies a relation, but record type is not recognized: lsn: %X/%X, rmgr: %s, info: %02X"
+msgstr "el registro WAL modifica una relación, pero el tipo de registro no es reconocido lsn: %X/%X, rmgr: %s, info: %02X"
+
+#: pg_rewind.c:84
+#, c-format
+msgid ""
+"%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n"
+"\n"
+msgstr ""
+"%s resincroniza un cluster PostgreSQL con otra copia del cluster.\n"
+"\n"
+
+#: pg_rewind.c:85
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]...\n"
+"\n"
+msgstr ""
+"Empleo:\n"
+" %s [OPCION]...\n"
+"\n"
+
+#: pg_rewind.c:86
+#, c-format
+msgid "Options:\n"
+msgstr "Opciones:\n"
+
+#: pg_rewind.c:87
+#, c-format
+msgid ""
+" -c, --restore-target-wal use restore_command in target configuration to\n"
+" retrieve WAL files from archives\n"
+msgstr ""
+" -c, --restore-target-wal utilizar restore_command de la configuración\n"
+" de destino para obtener archivos WAL\n"
+
+#: pg_rewind.c:89
+#, c-format
+msgid " -D, --target-pgdata=DIRECTORY existing data directory to modify\n"
+msgstr " -D, --target-pgdata=DIRECTORIO directorio de datos existente a modificar\n"
+
+#: pg_rewind.c:90
+#, c-format
+msgid " --source-pgdata=DIRECTORY source data directory to synchronize with\n"
+msgstr " --source-pgdata=DIRECTORIO directorio de datos de origen a sincronizar\n"
+
+#: pg_rewind.c:91
+#, c-format
+msgid " --source-server=CONNSTR source server to synchronize with\n"
+msgstr " --source-server=CONN servidor de origen a sincronizar\n"
+
+#: pg_rewind.c:92
+#, c-format
+msgid " -n, --dry-run stop before modifying anything\n"
+msgstr " -n, --dry-run detener antes de modificar nada\n"
+
+#: pg_rewind.c:93
+#, c-format
+msgid ""
+" -N, --no-sync do not wait for changes to be written\n"
+" safely to disk\n"
+msgstr " -N, --no-sync no esperar que los cambios se sincronicen a disco\n"
+
+#: pg_rewind.c:95
+#, c-format
+msgid " -P, --progress write progress messages\n"
+msgstr " -P, --progress escribir mensajes de progreso\n"
+
+#: pg_rewind.c:96
+#, c-format
+msgid ""
+" -R, --write-recovery-conf write configuration for replication\n"
+" (requires --source-server)\n"
+msgstr ""
+" -R, --write-recovery-conf escribe configuración para replicación\n"
+" (requiere --source-server)\n"
+
+#: pg_rewind.c:98
+#, c-format
+msgid " --debug write a lot of debug messages\n"
+msgstr " --debug escribir muchos mensajes de depuración\n"
+
+#: pg_rewind.c:99
+#, c-format
+msgid " --no-ensure-shutdown do not automatically fix unclean shutdown\n"
+msgstr ""
+" --no-ensure-shutdown no corregir automáticamente un apagado\n"
+" no-limpio\n"
+
+#: pg_rewind.c:100
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version mostrar información de versión y salir\n"
+
+#: pg_rewind.c:101
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help mostrar esta ayuda y salir\n"
+
+#: pg_rewind.c:102
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"Reporte errores a <%s>.\n"
+
+#: pg_rewind.c:103
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "Sitio web de %s: <%s>\n"
+
+#: pg_rewind.c:164 pg_rewind.c:213 pg_rewind.c:220 pg_rewind.c:227
+#: pg_rewind.c:234 pg_rewind.c:242
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "Pruebe «%s --help» para mayor información.\n"
+
+#: pg_rewind.c:212
+#, c-format
+msgid "no source specified (--source-pgdata or --source-server)"
+msgstr "no se especificó origen (--source-pgdata o --source-server)"
+
+#: pg_rewind.c:219
+#, c-format
+msgid "only one of --source-pgdata or --source-server can be specified"
+msgstr "sólo uno de --source-pgdata o --source-server puede ser especificado"
+
+#: pg_rewind.c:226
+#, c-format
+msgid "no target data directory specified (--target-pgdata)"
+msgstr "no se especificó directorio de datos de destino (--target-pgdata)"
+
+#: pg_rewind.c:233
+#, c-format
+msgid "no source server information (--source-server) specified for --write-recovery-conf"
+msgstr "no se especificó información de servidor de origen (--source-server) para --write-recovery-conf"
+
+#: pg_rewind.c:240
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "demasiados argumentos en la línea de órdenes (el primero es «%s»)"
+
+#: pg_rewind.c:255
+#, c-format
+msgid "cannot be executed by \"root\""
+msgstr "no puede ser ejecutado por «root»"
+
+#: pg_rewind.c:256
+#, c-format
+msgid "You must run %s as the PostgreSQL superuser.\n"
+msgstr "Debe ejecutar %s con el superusuario de PostgreSQL.\n"
+
+#: pg_rewind.c:267
+#, c-format
+msgid "could not read permissions of directory \"%s\": %m"
+msgstr "no se pudo obtener los permisos del directorio «%s»: %m"
+
+#: pg_rewind.c:287
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: pg_rewind.c:290
+#, c-format
+msgid "connected to server"
+msgstr "conectado al servidor"
+
+#: pg_rewind.c:337
+#, c-format
+msgid "source and target cluster are on the same timeline"
+msgstr "el cluster de origen y destino están en el mismo timeline"
+
+#: pg_rewind.c:346
+#, c-format
+msgid "servers diverged at WAL location %X/%X on timeline %u"
+msgstr "servidores divergieron en la posición de WAL %X/%X en el timeline %u"
+
+#: pg_rewind.c:394
+#, c-format
+msgid "no rewind required"
+msgstr "no se requiere rebobinar"
+
+#: pg_rewind.c:403
+#, c-format
+msgid "rewinding from last common checkpoint at %X/%X on timeline %u"
+msgstr "rebobinando desde el último checkpoint común en %X/%X en el timeline %u"
+
+#: pg_rewind.c:413
+#, c-format
+msgid "reading source file list"
+msgstr "leyendo la lista de archivos de origen"
+
+#: pg_rewind.c:417
+#, c-format
+msgid "reading target file list"
+msgstr "leyendo la lista de archivos de destino"
+
+#: pg_rewind.c:426
+#, c-format
+msgid "reading WAL in target"
+msgstr "leyendo WAL en destino"
+
+#: pg_rewind.c:447
+#, c-format
+msgid "need to copy %lu MB (total source directory size is %lu MB)"
+msgstr "se necesitan copiar %lu MB (tamaño total de directorio de origen es %lu MB)"
+
+#: pg_rewind.c:465
+#, c-format
+msgid "syncing target data directory"
+msgstr "sincronizando directorio de datos de destino"
+
+#: pg_rewind.c:481
+#, c-format
+msgid "Done!"
+msgstr "¡Listo!"
+
+#: pg_rewind.c:564
+#, c-format
+msgid "no action decided for file \"%s\""
+msgstr "no se decidió una acción para el archivo «%s»"
+
+#: pg_rewind.c:596
+#, c-format
+msgid "source system was modified while pg_rewind was running"
+msgstr "el sistema origen fue modificado mientras pg_rewind estaba en ejecución"
+
+#: pg_rewind.c:600
+#, c-format
+msgid "creating backup label and updating control file"
+msgstr "creando etiqueta de respaldo y actualizando archivo de control"
+
+#: pg_rewind.c:650
+#, c-format
+msgid "source system was in unexpected state at end of rewind"
+msgstr "el sistema origen estaba en un estado inesperado al final del rebobinado"
+
+#: pg_rewind.c:681
+#, c-format
+msgid "source and target clusters are from different systems"
+msgstr "clusters de origen y destino son de sistemas diferentes"
+
+#: pg_rewind.c:689
+#, c-format
+msgid "clusters are not compatible with this version of pg_rewind"
+msgstr "los clusters no son compatibles con esta versión de pg_rewind"
+
+#: pg_rewind.c:699
+#, c-format
+msgid "target server needs to use either data checksums or \"wal_log_hints = on\""
+msgstr "el servidor de destino necesita tener sumas de verificación de datos o «wal_log_hints» activados"
+
+#: pg_rewind.c:710
+#, c-format
+msgid "target server must be shut down cleanly"
+msgstr "el directorio de destino debe estar apagado limpiamente"
+
+#: pg_rewind.c:720
+#, c-format
+msgid "source data directory must be shut down cleanly"
+msgstr "el directorio de origen debe estar apagado limpiamente"
+
+#: pg_rewind.c:772
+#, c-format
+msgid "%*s/%s kB (%d%%) copied"
+msgstr "%*s/%s kB (%d%%) copiados"
+
+#: pg_rewind.c:835
+#, c-format
+msgid "invalid control file"
+msgstr "archivo de control no válido"
+
+#: pg_rewind.c:919
+#, c-format
+msgid "could not find common ancestor of the source and target cluster's timelines"
+msgstr "no se pudo encontrar un ancestro común en el timeline de los clusters de origen y destino"
+
+#: pg_rewind.c:960
+#, c-format
+msgid "backup label buffer too small"
+msgstr "el búfer del backup label es demasiado pequeño"
+
+#: pg_rewind.c:983
+#, c-format
+msgid "unexpected control file CRC"
+msgstr "CRC de archivo de control inesperado"
+
+#: pg_rewind.c:995
+#, c-format
+msgid "unexpected control file size %d, expected %d"
+msgstr "tamaño del archivo de control %d inesperado, se esperaba %d"
+
+#: pg_rewind.c:1004
+#, c-format
+msgid "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d byte"
+msgid_plural "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d bytes"
+msgstr[0] "El tamaño del segmento de WAL debe ser una potencia de dos entre 1 MB y 1 GB, pero el archivo de control especifica %d byte"
+msgstr[1] "El tamaño del segmento de WAL debe ser una potencia de dos entre 1 MB y 1 GB, pero el archivo de control especifica %d bytes"
+
+#: pg_rewind.c:1043 pg_rewind.c:1101
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"%s necesita el programa «%s», pero no pudo encontrarlo en el mismo\n"
+"directorio que «%s».\n"
+"Verifique su instalación."
+
+#: pg_rewind.c:1048 pg_rewind.c:1106
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"El programa «%s» fue encontrado por «%s»,\n"
+"pero no es de la misma versión que %s.\n"
+"Verifique su instalación."
+
+#: pg_rewind.c:1069
+#, c-format
+msgid "restore_command is not set in the target cluster"
+msgstr "restore_command no está definido en el clúster de destino"
+
+#: pg_rewind.c:1112
+#, c-format
+msgid "executing \"%s\" for target server to complete crash recovery"
+msgstr "ejecutando «%s» en el servidor de destino para completar la recuperación de caídas"
+
+#: pg_rewind.c:1132
+#, c-format
+msgid "postgres single-user mode in target cluster failed"
+msgstr "el modo «single-user» en el servidor de destino falló"
+
+#: pg_rewind.c:1133
+#, c-format
+msgid "Command was: %s"
+msgstr "La orden era: % s"
+
+#: timeline.c:75 timeline.c:81
+#, c-format
+msgid "syntax error in history file: %s"
+msgstr "error de sintaxis en archivo de historia: %s"
+
+#: timeline.c:76
+#, c-format
+msgid "Expected a numeric timeline ID."
+msgstr "Se esperaba un ID numérico de timeline."
+
+#: timeline.c:82
+#, c-format
+msgid "Expected a write-ahead log switchpoint location."
+msgstr "Se esperaba una ubicación de punto de cambio del «write-ahead log»."
+
+#: timeline.c:87
+#, c-format
+msgid "invalid data in history file: %s"
+msgstr "datos no válidos en archivo de historia: %s"
+
+#: timeline.c:88
+#, c-format
+msgid "Timeline IDs must be in increasing sequence."
+msgstr "IDs de timeline deben ser una secuencia creciente."
+
+#: timeline.c:108
+#, c-format
+msgid "invalid data in history file"
+msgstr "datos no válidos en archivo de historia"
+
+#: timeline.c:109
+#, c-format
+msgid "Timeline IDs must be less than child timeline's ID."
+msgstr "IDs de timeline deben ser menores que el ID de timeline del hijo."
+
+#: xlogreader.c:354
+#, c-format
+msgid "invalid record offset at %X/%X"
+msgstr "posición de registro no válida en %X/%X"
+
+#: xlogreader.c:362
+#, c-format
+msgid "contrecord is requested by %X/%X"
+msgstr "contrecord solicitado por %X/%X"
+
+#: xlogreader.c:403 xlogreader.c:733
+#, c-format
+msgid "invalid record length at %X/%X: wanted %u, got %u"
+msgstr "largo de registro no válido en %X/%X: se esperaba %u, se obtuvo %u"
+
+#: xlogreader.c:429
+#, c-format
+msgid "record length %u at %X/%X too long"
+msgstr "largo de registro %u en %X/%X demasiado largo"
+
+#: xlogreader.c:477
+#, c-format
+msgid "there is no contrecord flag at %X/%X"
+msgstr "no hay bandera de contrecord en %X/%X"
+
+#: xlogreader.c:490
+#, c-format
+msgid "invalid contrecord length %u (expected %lld) at %X/%X"
+msgstr "largo de contrecord %u no válido (se esperaba %lld) en %X/%X"
+
+#: xlogreader.c:741
+#, c-format
+msgid "invalid resource manager ID %u at %X/%X"
+msgstr "ID de gestor de recursos %u no válido en %X/%X"
+
+#: xlogreader.c:754 xlogreader.c:770
+#, c-format
+msgid "record with incorrect prev-link %X/%X at %X/%X"
+msgstr "registro con prev-link %X/%X incorrecto en %X/%X"
+
+#: xlogreader.c:806
+#, c-format
+msgid "incorrect resource manager data checksum in record at %X/%X"
+msgstr "suma de verificación de los datos del gestor de recursos incorrecta en el registro en %X/%X"
+
+#: xlogreader.c:843
+#, c-format
+msgid "invalid magic number %04X in log segment %s, offset %u"
+msgstr "número mágico %04X no válido en archivo %s, posición %u"
+
+#: xlogreader.c:857 xlogreader.c:898
+#, c-format
+msgid "invalid info bits %04X in log segment %s, offset %u"
+msgstr "info bits %04X no válidos en archivo %s, posición %u"
+
+#: xlogreader.c:872
+#, c-format
+msgid "WAL file is from different database system: WAL file database system identifier is %llu, pg_control database system identifier is %llu"
+msgstr "archivo WAL es de un sistema de bases de datos distinto: identificador de sistema en archivo WAL es %llu, identificador en pg_control es %llu"
+
+#: xlogreader.c:880
+#, c-format
+msgid "WAL file is from different database system: incorrect segment size in page header"
+msgstr "archivo WAL es de un sistema de bases de datos distinto: tamaño de segmento incorrecto en cabecera de paǵina"
+
+#: xlogreader.c:886
+#, c-format
+msgid "WAL file is from different database system: incorrect XLOG_BLCKSZ in page header"
+msgstr "archivo WAL es de un sistema de bases de datos distinto: XLOG_BLCKSZ incorrecto en cabecera de paǵina"
+
+#: xlogreader.c:917
+#, c-format
+msgid "unexpected pageaddr %X/%X in log segment %s, offset %u"
+msgstr "pageaddr %X/%X inesperado en archivo %s, posición %u"
+
+#: xlogreader.c:942
+#, c-format
+msgid "out-of-sequence timeline ID %u (after %u) in log segment %s, offset %u"
+msgstr "ID de timeline %u fuera de secuencia (después de %u) en archivo %s, posición %u"
+
+#: xlogreader.c:1287
+#, c-format
+msgid "out-of-order block_id %u at %X/%X"
+msgstr "block_id %u fuera de orden en %X/%X"
+
+#: xlogreader.c:1309
+#, c-format
+msgid "BKPBLOCK_HAS_DATA set, but no data included at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA está definido, pero no hay datos en %X/%X"
+
+#: xlogreader.c:1316
+#, c-format
+msgid "BKPBLOCK_HAS_DATA not set, but data length is %u at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA no está definido, pero el largo de los datos es %u en %X/%X"
+
+#: xlogreader.c:1352
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE set, but hole offset %u length %u block image length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE está definido, pero posición del agujero es %u largo %u largo de imagen %u en %X/%X"
+
+#: xlogreader.c:1368
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE not set, but hole offset %u length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE no está definido, pero posición del agujero es %u largo %u en %X/%X"
+
+#: xlogreader.c:1383
+#, c-format
+msgid "BKPIMAGE_IS_COMPRESSED set, but block image length %u at %X/%X"
+msgstr "BKPIMAGE_IS_COMPRESSED definido, pero largo de imagen de bloque es %u en %X/%X"
+
+#: xlogreader.c:1398
+#, c-format
+msgid "neither BKPIMAGE_HAS_HOLE nor BKPIMAGE_IS_COMPRESSED set, but block image length is %u at %X/%X"
+msgstr "ni BKPIMAGE_HAS_HOLE ni BKPIMAGE_IS_COMPRESSED está definido, pero largo de imagen de bloque es %u en %X/%X"
+
+#: xlogreader.c:1414
+#, c-format
+msgid "BKPBLOCK_SAME_REL set but no previous rel at %X/%X"
+msgstr "BKPBLOCK_SAME_REL está definido, pero no hay «rel» anterior en %X/%X "
+
+#: xlogreader.c:1426
+#, c-format
+msgid "invalid block_id %u at %X/%X"
+msgstr "block_id %u no válido en %X/%X"
+
+#: xlogreader.c:1513
+#, c-format
+msgid "record with invalid length at %X/%X"
+msgstr "registro con largo no válido en %X/%X"
+
+#: xlogreader.c:1602
+#, c-format
+msgid "invalid compressed image at %X/%X, block %d"
+msgstr "imagen comprimida no válida en %X/%X, bloque %d"
diff --git a/src/bin/pg_rewind/po/fr.po b/src/bin/pg_rewind/po/fr.po
new file mode 100644
index 0000000..7e8b6d3
--- /dev/null
+++ b/src/bin/pg_rewind/po/fr.po
@@ -0,0 +1,1222 @@
+# LANGUAGE message translation file for pg_rewind
+# Copyright (C) 2016 PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2016.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_rewind (PostgreSQL) 12\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-04-05 02:49+0000\n"
+"PO-Revision-Date: 2022-04-05 08:34+0200\n"
+"Last-Translator: Guillaume Lelarge <guillaume@lelarge.info>\n"
+"Language-Team: \n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Poedit 3.0.1\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "fatal : "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "erreur : "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "attention : "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "mémoire épuisée\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "ne peut pas dupliquer un pointeur nul (erreur interne)\n"
+
+#: ../../common/restricted_token.c:64
+#, c-format
+msgid "could not load library \"%s\": error code %lu"
+msgstr "n'a pas pu charger la bibliothèque « %s » : code d'erreur %lu"
+
+#: ../../common/restricted_token.c:73
+#, c-format
+msgid "cannot create restricted tokens on this platform: error code %lu"
+msgstr "ne peut pas créer les jetons restreints sur cette plateforme : code d'erreur %lu"
+
+#: ../../common/restricted_token.c:82
+#, c-format
+msgid "could not open process token: error code %lu"
+msgstr "n'a pas pu ouvrir le jeton du processus : code d'erreur %lu"
+
+#: ../../common/restricted_token.c:97
+#, c-format
+msgid "could not allocate SIDs: error code %lu"
+msgstr "n'a pas pu allouer les SID : code d'erreur %lu"
+
+#: ../../common/restricted_token.c:119
+#, c-format
+msgid "could not create restricted token: error code %lu"
+msgstr "n'a pas pu créer le jeton restreint : code d'erreur %lu"
+
+#: ../../common/restricted_token.c:140
+#, c-format
+msgid "could not start process for command \"%s\": error code %lu"
+msgstr "n'a pas pu démarrer le processus pour la commande « %s » : code d'erreur %lu"
+
+#: ../../common/restricted_token.c:178
+#, c-format
+msgid "could not re-execute with restricted token: error code %lu"
+msgstr "n'a pas pu ré-exécuter le jeton restreint : code d'erreur %lu"
+
+#: ../../common/restricted_token.c:194
+#, c-format
+msgid "could not get exit code from subprocess: error code %lu"
+msgstr "n'a pas pu récupérer le code de statut du sous-processus : code d'erreur %lu"
+
+#: ../../fe_utils/archive.c:53
+#, c-format
+msgid "cannot use restore_command with %%r placeholder"
+msgstr "ne peut pas utiliser restore_command avec le joker %%r"
+
+#: ../../fe_utils/archive.c:74
+#, c-format
+msgid "unexpected file size for \"%s\": %lld instead of %lld"
+msgstr "taille de fichier inattendu pour « %s » : %lld au lieu de %lld"
+
+#: ../../fe_utils/archive.c:85
+#, c-format
+msgid "could not open file \"%s\" restored from archive: %m"
+msgstr "n'a pas pu ouvrir le fichier « %s » à partir de l'archive : %m"
+
+#: ../../fe_utils/archive.c:97 file_ops.c:417
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "n'a pas pu tester le fichier « %s » : %m"
+
+#: ../../fe_utils/archive.c:112
+#, c-format
+msgid "restore_command failed: %s"
+msgstr "échec de la restore_command : %s"
+
+#: ../../fe_utils/archive.c:121
+#, c-format
+msgid "could not restore file \"%s\" from archive"
+msgstr "n'a pas pu restaurer le fichier « %s » à partir de l'archive"
+
+#: ../../fe_utils/recovery_gen.c:35 ../../fe_utils/recovery_gen.c:49
+#: ../../fe_utils/recovery_gen.c:77 ../../fe_utils/recovery_gen.c:100
+#: ../../fe_utils/recovery_gen.c:171 parsexlog.c:77 parsexlog.c:137
+#: parsexlog.c:197
+#, c-format
+msgid "out of memory"
+msgstr "mémoire épuisée"
+
+#: ../../fe_utils/recovery_gen.c:134 parsexlog.c:310
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "n'a pas pu ouvrir le fichier « %s » : %m"
+
+#: ../../fe_utils/recovery_gen.c:140
+#, c-format
+msgid "could not write to file \"%s\": %m"
+msgstr "n'a pas pu écrire dans le fichier « %s » : %m"
+
+#: ../../fe_utils/recovery_gen.c:152
+#, c-format
+msgid "could not create file \"%s\": %m"
+msgstr "n'a pas pu créer le fichier « %s » : %m"
+
+#: file_ops.c:67
+#, c-format
+msgid "could not open target file \"%s\": %m"
+msgstr "n'a pas pu ouvrir le fichier cible « %s » : %m"
+
+#: file_ops.c:81
+#, c-format
+msgid "could not close target file \"%s\": %m"
+msgstr "n'a pas pu fermer le fichier cible « %s » : %m"
+
+#: file_ops.c:101
+#, c-format
+msgid "could not seek in target file \"%s\": %m"
+msgstr "n'a pas pu chercher dans le fichier cible « %s » : %m"
+
+#: file_ops.c:117
+#, c-format
+msgid "could not write file \"%s\": %m"
+msgstr "impossible d'écrire le fichier « %s » : %m"
+
+#: file_ops.c:150 file_ops.c:177
+#, c-format
+msgid "undefined file type for \"%s\""
+msgstr "type de fichier non défini pour « %s »"
+
+#: file_ops.c:173
+#, c-format
+msgid "invalid action (CREATE) for regular file"
+msgstr "action (CREATE) invalide pour le fichier régulier"
+
+#: file_ops.c:200
+#, c-format
+msgid "could not remove file \"%s\": %m"
+msgstr "n'a pas pu supprimer le fichier « %s » : %m"
+
+#: file_ops.c:218
+#, c-format
+msgid "could not open file \"%s\" for truncation: %m"
+msgstr "n'a pas pu ouvrir le fichier « %s » pour le troncage : %m"
+
+#: file_ops.c:222
+#, c-format
+msgid "could not truncate file \"%s\" to %u: %m"
+msgstr "n'a pas pu tronquer le fichier « %s » en %u : %m"
+
+#: file_ops.c:238
+#, c-format
+msgid "could not create directory \"%s\": %m"
+msgstr "n'a pas pu créer le répertoire « %s » : %m"
+
+#: file_ops.c:252
+#, c-format
+msgid "could not remove directory \"%s\": %m"
+msgstr "n'a pas pu supprimer le répertoire « %s » : %m"
+
+#: file_ops.c:266
+#, c-format
+msgid "could not create symbolic link at \"%s\": %m"
+msgstr "n'a pas pu créer le lien symbolique à « %s » : %m"
+
+#: file_ops.c:280
+#, c-format
+msgid "could not remove symbolic link \"%s\": %m"
+msgstr "n'a pas pu supprimer le lien symbolique « %s » : %m"
+
+#: file_ops.c:326 file_ops.c:330
+#, c-format
+msgid "could not open file \"%s\" for reading: %m"
+msgstr "n'a pas pu ouvrir le fichier « %s » pour une lecture : %m"
+
+#: file_ops.c:341 local_source.c:107 parsexlog.c:348
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "n'a pas pu lire le fichier « %s » : %m"
+
+#: file_ops.c:344 parsexlog.c:350
+#, c-format
+msgid "could not read file \"%s\": read %d of %zu"
+msgstr "n'a pas pu lire le fichier « %s » : a lu %d sur %zu"
+
+#: file_ops.c:388
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "n'a pas pu ouvrir le répertoire « %s » : %m"
+
+#: file_ops.c:446
+#, c-format
+msgid "could not read symbolic link \"%s\": %m"
+msgstr "n'a pas pu lire le lien symbolique « %s » : %m"
+
+#: file_ops.c:449
+#, c-format
+msgid "symbolic link \"%s\" target is too long"
+msgstr "la cible du lien symbolique « %s » est trop longue"
+
+#: file_ops.c:464
+#, c-format
+msgid "\"%s\" is a symbolic link, but symbolic links are not supported on this platform"
+msgstr "« %s » est un lien symbolique mais les liens symboliques ne sont pas supportés sur cette plateforme"
+
+#: file_ops.c:471
+#, c-format
+msgid "could not read directory \"%s\": %m"
+msgstr "n'a pas pu lire le répertoire « %s » : %m"
+
+#: file_ops.c:475
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "n'a pas pu fermer le répertoire « %s » : %m"
+
+#: filemap.c:237
+#, c-format
+msgid "data file \"%s\" in source is not a regular file"
+msgstr "le fichier de données « %s » en source n'est pas un fichier standard"
+
+#: filemap.c:242 filemap.c:275
+#, c-format
+msgid "duplicate source file \"%s\""
+msgstr "fichier source « %s » dupliqué"
+
+#: filemap.c:330
+#, c-format
+msgid "unexpected page modification for non-regular file \"%s\""
+msgstr "modification inattendue de page pour le fichier non standard « %s »"
+
+#: filemap.c:680 filemap.c:774
+#, c-format
+msgid "unknown file type for \"%s\""
+msgstr "type de fichier inconnu pour « %s »"
+
+#: filemap.c:707
+#, c-format
+msgid "file \"%s\" is of different type in source and target"
+msgstr "le fichier « %s » a un type différent pour la source et la cible"
+
+#: filemap.c:779
+#, c-format
+msgid "could not decide what to do with file \"%s\""
+msgstr "n'a pas pu décider que faire avec le fichier « %s » : %m"
+
+#: libpq_source.c:128
+#, c-format
+msgid "could not clear search_path: %s"
+msgstr "n'a pas pu effacer search_path : %s"
+
+#: libpq_source.c:139
+#, c-format
+msgid "full_page_writes must be enabled in the source server"
+msgstr "full_page_writes doit être activé sur le serveur source"
+
+#: libpq_source.c:150
+#, c-format
+msgid "could not prepare statement to fetch file contents: %s"
+msgstr "n'a pas pu préparer l'instruction pour récupérer le contenu du fichier : %s"
+
+#: libpq_source.c:169
+#, c-format
+msgid "error running query (%s) on source server: %s"
+msgstr "erreur lors de l'exécution de la requête (%s) sur le serveur source : %s"
+
+#: libpq_source.c:174
+#, c-format
+msgid "unexpected result set from query"
+msgstr "ensemble de résultats inattendu provenant de la requête"
+
+#: libpq_source.c:196
+#, c-format
+msgid "error running query (%s) in source server: %s"
+msgstr "erreur lors de l'exécution de la requête (%s) dans le serveur source : %s"
+
+#: libpq_source.c:217
+#, c-format
+msgid "unrecognized result \"%s\" for current WAL insert location"
+msgstr "résultat non reconnu « %s » pour l'emplacement d'insertion actuel dans les WAL"
+
+#: libpq_source.c:268
+#, c-format
+msgid "could not fetch file list: %s"
+msgstr "n'a pas pu récupérer la liste des fichiers : %s"
+
+#: libpq_source.c:273
+#, c-format
+msgid "unexpected result set while fetching file list"
+msgstr "ensemble de résultats inattendu lors de la récupération de la liste des fichiers"
+
+#: libpq_source.c:435
+#, c-format
+msgid "could not send query: %s"
+msgstr "n'a pas pu envoyer la requête : %s"
+
+#: libpq_source.c:438
+#, c-format
+msgid "could not set libpq connection to single row mode"
+msgstr "n'a pas pu configurer la connexion libpq en mode ligne seule"
+
+#: libpq_source.c:468
+#, c-format
+msgid "unexpected result while fetching remote files: %s"
+msgstr "résultat inattendu lors de la récupération des fichiers cibles : %s"
+
+#: libpq_source.c:473
+#, c-format
+msgid "received more data chunks than requested"
+msgstr "a reçu plus de morceaux de données que demandé"
+
+#: libpq_source.c:477
+#, c-format
+msgid "unexpected result set size while fetching remote files"
+msgstr "taille inattendue de l'ensemble de résultats lors de la récupération des fichiers distants"
+
+#: libpq_source.c:483
+#, c-format
+msgid "unexpected data types in result set while fetching remote files: %u %u %u"
+msgstr "types de données inattendus dans l'ensemble de résultats lors de la récupération des fichiers distants : %u %u %u"
+
+#: libpq_source.c:491
+#, c-format
+msgid "unexpected result format while fetching remote files"
+msgstr "format de résultat inattendu lors de la récupération des fichiers distants"
+
+#: libpq_source.c:497
+#, c-format
+msgid "unexpected null values in result while fetching remote files"
+msgstr "valeurs NULL inattendues dans le résultat lors de la récupération des fichiers distants"
+
+#: libpq_source.c:501
+#, c-format
+msgid "unexpected result length while fetching remote files"
+msgstr "longueur de résultats inattendu lors de la récupération des fichiers distants"
+
+#: libpq_source.c:534
+#, c-format
+msgid "received data for file \"%s\", when requested for \"%s\""
+msgstr "a reçu des données du fichier « %s » alors que « %s » était demandé"
+
+#: libpq_source.c:538
+#, c-format
+msgid "received data at offset %lld of file \"%s\", when requested for offset %lld"
+msgstr "a reçu des données au décalage %lld du fichier « %s » alors que le décalage %lld était demandé"
+
+#: libpq_source.c:550
+#, c-format
+msgid "received more than requested for file \"%s\""
+msgstr "a reçu plus que demandé pour le fichier « %s »"
+
+#: libpq_source.c:563
+#, c-format
+msgid "unexpected number of data chunks received"
+msgstr "nombre de morceaux de données reçus inattendu"
+
+#: libpq_source.c:606
+#, c-format
+msgid "could not fetch remote file \"%s\": %s"
+msgstr "n'a pas pu récupérer le fichier distant « %s » : %s"
+
+#: libpq_source.c:611
+#, c-format
+msgid "unexpected result set while fetching remote file \"%s\""
+msgstr "ensemble de résultats inattendu lors de la récupération du fichier distant « %s »"
+
+#: local_source.c:86
+#, c-format
+msgid "could not open source file \"%s\": %m"
+msgstr "n'a pas pu ouvrir le fichier source « %s » : %m"
+
+#: local_source.c:90
+#, c-format
+msgid "could not seek in source file: %m"
+msgstr "n'a pas pu chercher dans le fichier source : %m"
+
+#: local_source.c:109
+#, c-format
+msgid "unexpected EOF while reading file \"%s\""
+msgstr "EOF inattendu lors de la lecture du fichier « %s »"
+
+#: local_source.c:116
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "n'a pas pu fermer le fichier « %s » : %m"
+
+#: parsexlog.c:89 parsexlog.c:144
+#, c-format
+msgid "could not read WAL record at %X/%X: %s"
+msgstr "n'a pas pu lire l'enregistrement WAL précédent à %X/%X : %s"
+
+#: parsexlog.c:93 parsexlog.c:147
+#, c-format
+msgid "could not read WAL record at %X/%X"
+msgstr "n'a pas pu lire l'enregistrement WAL précédent à %X/%X"
+
+#: parsexlog.c:106
+#, c-format
+msgid "end pointer %X/%X is not a valid end point; expected %X/%X"
+msgstr "le pointeur de fin %X/%X n'est pas un pointeur de fin valide ; %X/%X attendu"
+
+#: parsexlog.c:210
+#, c-format
+msgid "could not find previous WAL record at %X/%X: %s"
+msgstr "n'a pas pu trouver l'enregistrement WAL précédent à %X/%X : %s"
+
+#: parsexlog.c:214
+#, c-format
+msgid "could not find previous WAL record at %X/%X"
+msgstr "n'a pas pu trouver l'enregistrement WAL précédent à %X/%X"
+
+#: parsexlog.c:339
+#, c-format
+msgid "could not seek in file \"%s\": %m"
+msgstr "n'a pas pu parcourir le fichier « %s » : %m"
+
+#: parsexlog.c:431
+#, c-format
+msgid "WAL record modifies a relation, but record type is not recognized: lsn: %X/%X, rmgr: %s, info: %02X"
+msgstr "l'enregistrement WAL modifie une relation mais le type d'enregistrement n'est pas reconnu: lsn : %X/%X, rmgr : %s, info : %02X"
+
+#: pg_rewind.c:84
+#, c-format
+msgid ""
+"%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n"
+"\n"
+msgstr ""
+"%s resynchronise une instance PostgreSQL avec une autre copie de\n"
+"l'instance.\n"
+"\n"
+
+#: pg_rewind.c:85
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]...\n"
+"\n"
+msgstr ""
+"Usage :\n"
+" %s [OPTION]...\n"
+"\n"
+
+#: pg_rewind.c:86
+#, c-format
+msgid "Options:\n"
+msgstr "Options :\n"
+
+#: pg_rewind.c:87
+#, c-format
+msgid ""
+" -c, --restore-target-wal use restore_command in target configuration to\n"
+" retrieve WAL files from archives\n"
+msgstr ""
+" -c, --restore-target-wal utilise restore_command pour la configuration\n"
+" cible de récupération des fichiers WAL des\n"
+" archives\n"
+
+#: pg_rewind.c:89
+#, c-format
+msgid " -D, --target-pgdata=DIRECTORY existing data directory to modify\n"
+msgstr " -D, --target-pgdata=RÉPERTOIRE répertoire de données existant à modifier\n"
+
+#: pg_rewind.c:90
+#, c-format
+msgid " --source-pgdata=DIRECTORY source data directory to synchronize with\n"
+msgstr " --source-pgdata=RÉPERTOIRE répertoire des données source\n"
+
+#: pg_rewind.c:91
+#, c-format
+msgid " --source-server=CONNSTR source server to synchronize with\n"
+msgstr " --source-server=CHÂINE serveur source pour la synchronisation\n"
+
+#: pg_rewind.c:92
+#, c-format
+msgid " -n, --dry-run stop before modifying anything\n"
+msgstr " -n, --dry-run arrête avant de modifier quoi que ce soit\n"
+
+#: pg_rewind.c:93
+#, c-format
+msgid ""
+" -N, --no-sync do not wait for changes to be written\n"
+" safely to disk\n"
+msgstr ""
+" -N, --nosync n'attend pas que les modifications soient\n"
+" proprement écrites sur disque\n"
+
+#: pg_rewind.c:95
+#, c-format
+msgid " -P, --progress write progress messages\n"
+msgstr " -P, --progress écrit les messages de progression\n"
+
+#: pg_rewind.c:96
+#, c-format
+msgid ""
+" -R, --write-recovery-conf write configuration for replication\n"
+" (requires --source-server)\n"
+msgstr ""
+" -R, --write-recovery-conf écrit la configuration pour la réplication\n"
+" (requiert --source-server)\n"
+"\n"
+
+#: pg_rewind.c:98
+#, c-format
+msgid " --debug write a lot of debug messages\n"
+msgstr " --debug écrit beaucoup de messages de débogage\n"
+
+#: pg_rewind.c:99
+#, c-format
+msgid " --no-ensure-shutdown do not automatically fix unclean shutdown\n"
+msgstr ""
+" --no-ensure-shutdown ne corrige pas automatiquement l'arrêt non\n"
+" propre\n"
+
+#: pg_rewind.c:100
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version affiche la version, puis quitte\n"
+
+#: pg_rewind.c:101
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help affiche cette aide, puis quitte\n"
+
+#: pg_rewind.c:102
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"Rapporter les bogues à <%s>.\n"
+
+#: pg_rewind.c:103
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "Page d'accueil de %s : <%s>\n"
+
+#: pg_rewind.c:164 pg_rewind.c:213 pg_rewind.c:220 pg_rewind.c:227
+#: pg_rewind.c:234 pg_rewind.c:242
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "Essayez « %s --help » pour plus d'informations.\n"
+
+#: pg_rewind.c:212
+#, c-format
+msgid "no source specified (--source-pgdata or --source-server)"
+msgstr "aucune source indiquée (--source-pgdata ou --source-server)"
+
+#: pg_rewind.c:219
+#, c-format
+msgid "only one of --source-pgdata or --source-server can be specified"
+msgstr "une seule des options --source-pgdata et --source-server peut être indiquée"
+
+#: pg_rewind.c:226
+#, c-format
+msgid "no target data directory specified (--target-pgdata)"
+msgstr "aucun répertoire de données cible indiqué (--target-pgdata)"
+
+#: pg_rewind.c:233
+#, c-format
+msgid "no source server information (--source-server) specified for --write-recovery-conf"
+msgstr "aucune information sur le serveur source (--source-server) indiquée pour --write-recovery-conf"
+
+#: pg_rewind.c:240
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "trop d'arguments en ligne de commande (le premier étant « %s »)"
+
+#: pg_rewind.c:255
+#, c-format
+msgid "cannot be executed by \"root\""
+msgstr "ne peut pas être exécuté par « root »"
+
+#: pg_rewind.c:256
+#, c-format
+msgid "You must run %s as the PostgreSQL superuser.\n"
+msgstr "Vous devez exécuter %s en tant que super-utilisateur PostgreSQL.\n"
+
+#: pg_rewind.c:267
+#, c-format
+msgid "could not read permissions of directory \"%s\": %m"
+msgstr "n'a pas pu lire les droits du répertoire « %s » : %m"
+
+#: pg_rewind.c:287
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: pg_rewind.c:290
+#, c-format
+msgid "connected to server"
+msgstr "connecté au serveur"
+
+#: pg_rewind.c:337
+#, c-format
+msgid "source and target cluster are on the same timeline"
+msgstr "les instances source et cible sont sur la même ligne de temps"
+
+#: pg_rewind.c:346
+#, c-format
+msgid "servers diverged at WAL location %X/%X on timeline %u"
+msgstr "les serveurs ont divergé à la position %X/%X des WAL sur la timeline %u"
+
+#: pg_rewind.c:394
+#, c-format
+msgid "no rewind required"
+msgstr "pas de retour en arrière requis"
+
+#: pg_rewind.c:403
+#, c-format
+msgid "rewinding from last common checkpoint at %X/%X on timeline %u"
+msgstr "retour en arrière depuis le dernier checkpoint commun à %X/%X sur la ligne de temps %u"
+
+#: pg_rewind.c:413
+#, c-format
+msgid "reading source file list"
+msgstr "lecture de la liste des fichiers sources"
+
+#: pg_rewind.c:417
+#, c-format
+msgid "reading target file list"
+msgstr "lecture de la liste des fichiers cibles"
+
+#: pg_rewind.c:426
+#, c-format
+msgid "reading WAL in target"
+msgstr "lecture du WAL dans la cible"
+
+#: pg_rewind.c:447
+#, c-format
+msgid "need to copy %lu MB (total source directory size is %lu MB)"
+msgstr "a besoin de copier %lu Mo (la taille totale du répertoire source est %lu Mo)"
+
+#: pg_rewind.c:465
+#, c-format
+msgid "syncing target data directory"
+msgstr "synchronisation du répertoire des données cible"
+
+#: pg_rewind.c:481
+#, c-format
+msgid "Done!"
+msgstr "Terminé !"
+
+#: pg_rewind.c:564
+#, c-format
+msgid "no action decided for file \"%s\""
+msgstr "aucune action décidée pour le fichier « %s »"
+
+#: pg_rewind.c:596
+#, c-format
+msgid "source system was modified while pg_rewind was running"
+msgstr "le système source a été modifié alors que pg_rewind était en cours d'exécution"
+
+#: pg_rewind.c:600
+#, c-format
+msgid "creating backup label and updating control file"
+msgstr "création du fichier backup_label et mise à jour du fichier contrôle"
+
+#: pg_rewind.c:650
+#, c-format
+msgid "source system was in unexpected state at end of rewind"
+msgstr "le système source était dans un état inattendu en fin de rewind"
+
+#: pg_rewind.c:681
+#, c-format
+msgid "source and target clusters are from different systems"
+msgstr "les instances source et cible proviennent de systèmes différents"
+
+#: pg_rewind.c:689
+#, c-format
+msgid "clusters are not compatible with this version of pg_rewind"
+msgstr "les instances ne sont pas compatibles avec cette version de pg_rewind"
+
+#: pg_rewind.c:699
+#, c-format
+msgid "target server needs to use either data checksums or \"wal_log_hints = on\""
+msgstr "le serveur cible doit soit utiliser les sommes de contrôle sur les données soit avoir wal_log_hints configuré à on"
+
+#: pg_rewind.c:710
+#, c-format
+msgid "target server must be shut down cleanly"
+msgstr "le serveur cible doit être arrêté proprement"
+
+#: pg_rewind.c:720
+#, c-format
+msgid "source data directory must be shut down cleanly"
+msgstr "le répertoire de données source doit être arrêté proprement"
+
+#: pg_rewind.c:772
+#, c-format
+msgid "%*s/%s kB (%d%%) copied"
+msgstr "%*s/%s Ko (%d%%) copiés"
+
+#: pg_rewind.c:835
+#, c-format
+msgid "invalid control file"
+msgstr "fichier de contrôle invalide"
+
+#: pg_rewind.c:919
+#, c-format
+msgid "could not find common ancestor of the source and target cluster's timelines"
+msgstr "n'a pas pu trouver l'ancêtre commun des lignes de temps des instances source et cible"
+
+#: pg_rewind.c:960
+#, c-format
+msgid "backup label buffer too small"
+msgstr "tampon du label de sauvegarde trop petit"
+
+#: pg_rewind.c:983
+#, c-format
+msgid "unexpected control file CRC"
+msgstr "CRC inattendu pour le fichier de contrôle"
+
+#: pg_rewind.c:995
+#, c-format
+msgid "unexpected control file size %d, expected %d"
+msgstr "taille %d inattendue du fichier de contrôle, %d attendu"
+
+#: pg_rewind.c:1004
+#, c-format
+msgid "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d byte"
+msgid_plural "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d bytes"
+msgstr[0] "La taille du segment WAL doit être une puissance de deux comprise entre 1 Mo et 1 Go, mais le fichier de contrôle indique %d octet"
+msgstr[1] "La taille du segment WAL doit être une puissance de deux comprise entre 1 Mo et 1 Go, mais le fichier de contrôle indique %d octets"
+
+#: pg_rewind.c:1043 pg_rewind.c:1101
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"Le programme « %s » est nécessaire pour %s, mais n'a pas été trouvé\n"
+"dans le même répertoire que « %s ».\n"
+"Vérifiez votre installation."
+
+#: pg_rewind.c:1048 pg_rewind.c:1106
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"Le programme « %s » a été trouvé par « %s »\n"
+"mais n'est pas de la même version que %s.\n"
+"Vérifiez votre installation."
+
+#: pg_rewind.c:1069
+#, c-format
+msgid "restore_command is not set in the target cluster"
+msgstr "restore_command n'est pas configuré sur l'instance cible"
+
+#: pg_rewind.c:1112
+#, c-format
+msgid "executing \"%s\" for target server to complete crash recovery"
+msgstr "exécution de « %s » pour terminer la restauration après crash du serveur cible"
+
+#: pg_rewind.c:1132
+#, c-format
+msgid "postgres single-user mode in target cluster failed"
+msgstr "le mot simple-utilisateur de postgres a échoué pour l'instance cible"
+
+#: pg_rewind.c:1133
+#, c-format
+msgid "Command was: %s"
+msgstr "La commande était : %s"
+
+#: timeline.c:75 timeline.c:81
+#, c-format
+msgid "syntax error in history file: %s"
+msgstr "erreur de syntaxe dans le fichier historique : %s"
+
+#: timeline.c:76
+#, c-format
+msgid "Expected a numeric timeline ID."
+msgstr "Attendait un identifiant timeline numérique."
+
+#: timeline.c:82
+#, c-format
+msgid "Expected a write-ahead log switchpoint location."
+msgstr "Attendait un emplacement de bascule de journal de transactions."
+
+#: timeline.c:87
+#, c-format
+msgid "invalid data in history file: %s"
+msgstr "données invalides dans le fichier historique : %s"
+
+#: timeline.c:88
+#, c-format
+msgid "Timeline IDs must be in increasing sequence."
+msgstr "Les identifiants timeline doivent être en ordre croissant."
+
+#: timeline.c:108
+#, c-format
+msgid "invalid data in history file"
+msgstr "données invalides dans le fichier historique"
+
+#: timeline.c:109
+#, c-format
+msgid "Timeline IDs must be less than child timeline's ID."
+msgstr ""
+"Les identifiants timeline doivent être plus petits que les enfants des\n"
+"identifiants timeline."
+
+#: xlogreader.c:354
+#, c-format
+msgid "invalid record offset at %X/%X"
+msgstr "décalage invalide de l'enregistrement %X/%X"
+
+#: xlogreader.c:362
+#, c-format
+msgid "contrecord is requested by %X/%X"
+msgstr "« contrecord » est requis par %X/%X"
+
+#: xlogreader.c:403 xlogreader.c:733
+#, c-format
+msgid "invalid record length at %X/%X: wanted %u, got %u"
+msgstr "longueur invalide de l'enregistrement à %X/%X : voulait %u, a eu %u"
+
+#: xlogreader.c:429
+#, c-format
+msgid "record length %u at %X/%X too long"
+msgstr "longueur trop importante de l'enregistrement %u à %X/%X"
+
+#: xlogreader.c:477
+#, c-format
+msgid "there is no contrecord flag at %X/%X"
+msgstr "il n'existe pas de drapeau contrecord à %X/%X"
+
+#: xlogreader.c:490
+#, c-format
+msgid "invalid contrecord length %u (expected %lld) at %X/%X"
+msgstr "longueur %u invalide du contrecord (%lld attendu) à %X/%X"
+
+#: xlogreader.c:741
+#, c-format
+msgid "invalid resource manager ID %u at %X/%X"
+msgstr "identifiant du gestionnaire de ressources invalide %u à %X/%X"
+
+#: xlogreader.c:754 xlogreader.c:770
+#, c-format
+msgid "record with incorrect prev-link %X/%X at %X/%X"
+msgstr "enregistrement avec prev-link %X/%X incorrect à %X/%X"
+
+#: xlogreader.c:806
+#, c-format
+msgid "incorrect resource manager data checksum in record at %X/%X"
+msgstr ""
+"somme de contrôle des données du gestionnaire de ressources incorrecte à\n"
+"l'enregistrement %X/%X"
+
+#: xlogreader.c:843
+#, c-format
+msgid "invalid magic number %04X in log segment %s, offset %u"
+msgstr "numéro magique invalide %04X dans le segment %s, décalage %u"
+
+#: xlogreader.c:857 xlogreader.c:898
+#, c-format
+msgid "invalid info bits %04X in log segment %s, offset %u"
+msgstr "bits d'information %04X invalides dans le segment %s, décalage %u"
+
+#: xlogreader.c:872
+#, c-format
+msgid "WAL file is from different database system: WAL file database system identifier is %llu, pg_control database system identifier is %llu"
+msgstr "Le fichier WAL provient d'une instance différente : l'identifiant système de la base dans le fichier WAL est %llu, alors que l'identifiant système de la base dans pg_control est %llu"
+
+#: xlogreader.c:880
+#, c-format
+msgid "WAL file is from different database system: incorrect segment size in page header"
+msgstr "Le fichier WAL provient d'une instance différente : taille invalide du segment dans l'en-tête de page"
+
+#: xlogreader.c:886
+#, c-format
+msgid "WAL file is from different database system: incorrect XLOG_BLCKSZ in page header"
+msgstr "Le fichier WAL provient d'une instance différente : XLOG_BLCKSZ incorrect dans l'en-tête de page"
+
+#: xlogreader.c:917
+#, c-format
+msgid "unexpected pageaddr %X/%X in log segment %s, offset %u"
+msgstr "pageaddr %X/%X inattendue dans le journal de transactions %s, segment %u"
+
+#: xlogreader.c:942
+#, c-format
+msgid "out-of-sequence timeline ID %u (after %u) in log segment %s, offset %u"
+msgstr "identifiant timeline %u hors de la séquence (après %u) dans le segment %s, décalage %u"
+
+#: xlogreader.c:1287
+#, c-format
+msgid "out-of-order block_id %u at %X/%X"
+msgstr "block_id %u désordonné à %X/%X"
+
+#: xlogreader.c:1309
+#, c-format
+msgid "BKPBLOCK_HAS_DATA set, but no data included at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA configuré, mais aucune donnée inclus à %X/%X"
+
+#: xlogreader.c:1316
+#, c-format
+msgid "BKPBLOCK_HAS_DATA not set, but data length is %u at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA non configuré, mais la longueur des données est %u à %X/%X"
+
+#: xlogreader.c:1352
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE set, but hole offset %u length %u block image length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE activé, mais décalage trou %u longueur %u longueur image bloc %u à %X/%X"
+
+#: xlogreader.c:1368
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE not set, but hole offset %u length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE désactivé, mais décalage trou %u longueur %u à %X/%X"
+
+#: xlogreader.c:1383
+#, c-format
+msgid "BKPIMAGE_IS_COMPRESSED set, but block image length %u at %X/%X"
+msgstr "BKPIMAGE_IS_COMPRESSED configuré, mais la longueur de l'image du bloc est %u à %X/%X"
+
+#: xlogreader.c:1398
+#, c-format
+msgid "neither BKPIMAGE_HAS_HOLE nor BKPIMAGE_IS_COMPRESSED set, but block image length is %u at %X/%X"
+msgstr "ni BKPIMAGE_HAS_HOLE ni BKPIMAGE_IS_COMPRESSED configuré, mais la longueur de l'image du bloc est %u à %X/%X"
+
+#: xlogreader.c:1414
+#, c-format
+msgid "BKPBLOCK_SAME_REL set but no previous rel at %X/%X"
+msgstr "BKPBLOCK_SAME_REL configuré, mais pas de relation précédente à %X/%X"
+
+#: xlogreader.c:1426
+#, c-format
+msgid "invalid block_id %u at %X/%X"
+msgstr "block_id %u invalide à %X/%X"
+
+#: xlogreader.c:1513
+#, c-format
+msgid "record with invalid length at %X/%X"
+msgstr "enregistrement de longueur invalide à %X/%X"
+
+#: xlogreader.c:1602
+#, c-format
+msgid "invalid compressed image at %X/%X, block %d"
+msgstr "image compressée invalide à %X/%X, bloc %d"
+
+#~ msgid ""
+#~ "\n"
+#~ "Report bugs to <pgsql-bugs@lists.postgresql.org>.\n"
+#~ msgstr ""
+#~ "\n"
+#~ "Rapporter les bogues à <pgsql-bugs@lists.postgresql.org>.\n"
+
+#~ msgid " block %u\n"
+#~ msgstr " bloc %u\n"
+
+#~ msgid "\"%s\" is not a directory"
+#~ msgstr "« %s » n'est pas un répertoire"
+
+#~ msgid "\"%s\" is not a regular file"
+#~ msgstr "« %s » n'est pas un fichier standard"
+
+#~ msgid "\"%s\" is not a symbolic link"
+#~ msgstr "« %s » n'est pas un lien symbolique"
+
+#~ msgid "%d: %X/%X - %X/%X\n"
+#~ msgstr "%d : %X/%X - %X/%X\n"
+
+#~ msgid "%s (%s)\n"
+#~ msgstr "%s (%s)\n"
+
+#~ msgid "%s: WARNING: cannot create restricted tokens on this platform\n"
+#~ msgstr "%s : ATTENTION : ne peut pas créer les jetons restreints sur cette plateforme\n"
+
+#~ msgid "%s: could not allocate SIDs: error code %lu\n"
+#~ msgstr "%s : n'a pas pu allouer les SID : code d'erreur %lu\n"
+
+#~ msgid "%s: could not create restricted token: error code %lu\n"
+#~ msgstr "%s : n'a pas pu créer le jeton restreint : code d'erreur %lu\n"
+
+#~ msgid "%s: could not get exit code from subprocess: error code %lu\n"
+#~ msgstr "%s : n'a pas pu récupérer le code de statut du sous-processus : code d'erreur %lu\n"
+
+#~ msgid "%s: could not open process token: error code %lu\n"
+#~ msgstr "%s : n'a pas pu ouvrir le jeton du processus : code d'erreur %lu\n"
+
+#~ msgid "%s: could not re-execute with restricted token: error code %lu\n"
+#~ msgstr "%s : n'a pas pu ré-exécuter le jeton restreint : code d'erreur %lu\n"
+
+#~ msgid "%s: could not read permissions of directory \"%s\": %s\n"
+#~ msgstr "%s : n'a pas pu lire les droits sur le répertoire « %s » : %s\n"
+
+#~ msgid "%s: could not start process for command \"%s\": error code %lu\n"
+#~ msgstr "%s : n'a pas pu démarrer le processus pour la commande « %s » : code d'erreur %lu\n"
+
+#~ msgid "%s: too many command-line arguments (first is \"%s\")\n"
+#~ msgstr "%s : trop d'arguments en ligne de commande (le premier étant « %s »)\n"
+
+#~ msgid "Expected a numeric timeline ID.\n"
+#~ msgstr "Attendait un identifiant numérique de ligne de temps.\n"
+
+#~ msgid "Expected a write-ahead log switchpoint location.\n"
+#~ msgstr "Attendait un emplacement de bascule de journal de transactions.\n"
+
+#~ msgid "Failure, exiting\n"
+#~ msgstr "Échec, sortie\n"
+
+#~ msgid "Source timeline history:\n"
+#~ msgstr "Historique de la ligne de temps source :\n"
+
+#~ msgid "Target timeline history:\n"
+#~ msgstr "Historique de la ligne de temps cible :\n"
+
+#~ msgid ""
+#~ "The program \"%s\" is needed by %s but was\n"
+#~ "not found in the same directory as \"%s\".\n"
+#~ "Check your installation."
+#~ msgstr ""
+#~ "Le programme « %s » est nécessaire pour %s, mais n'a pas été trouvé\n"
+#~ "dans le même répertoire que « %s ».\n"
+#~ "Vérifiez votre installation."
+
+#~ msgid ""
+#~ "The program \"%s\" was found by \"%s\" but was\n"
+#~ "not the same version as %s.\n"
+#~ "Check your installation."
+#~ msgstr ""
+#~ "Le programme « %s » a été trouvé par « %s » mais n'était pas de la même version\n"
+#~ "que %s.\n"
+#~ "Vérifiez votre installation."
+
+#~ msgid ""
+#~ "The program \"initdb\" is needed by %s but was\n"
+#~ "not found in the same directory as \"%s\".\n"
+#~ "Check your installation.\n"
+#~ msgstr ""
+#~ "Le programme « initdb » est nécessaire pour %s, mais n'a pas été trouvé\n"
+#~ "dans le même répertoire que « %s ».\n"
+#~ "Vérifiez votre installation.\n"
+
+#~ msgid ""
+#~ "The program \"initdb\" was found by \"%s\"\n"
+#~ "but was not the same version as %s.\n"
+#~ "Check your installation.\n"
+#~ msgstr ""
+#~ "Le programme « initdb » a été trouvé par « %s », mais n'est pas de la même version\n"
+#~ "que %s.\n"
+#~ "Vérifiez votre installation.\n"
+
+#~ msgid ""
+#~ "The program \"postgres\" is needed by %s but was not found in the\n"
+#~ "same directory as \"%s\".\n"
+#~ "Check your installation."
+#~ msgstr ""
+#~ "Le programme « postgres » est nécessaire à %s mais n'a pas été trouvé dans\n"
+#~ "le même répertoire que « %s ».\n"
+#~ "Vérifiez votre installation."
+
+#~ msgid ""
+#~ "The program \"postgres\" was found by \"%s\"\n"
+#~ "but was not the same version as %s.\n"
+#~ "Check your installation."
+#~ msgstr ""
+#~ "Le programme « postgres » a été trouvé par « %s » mais n'est pas de la même\n"
+#~ "version que « %s ».\n"
+#~ "Vérifiez votre installation."
+
+#~ msgid "Timeline IDs must be in increasing sequence.\n"
+#~ msgstr "Les identifiants de ligne de temps doivent être dans une séquence croissante.\n"
+
+#~ msgid "Timeline IDs must be less than child timeline's ID.\n"
+#~ msgstr "Les identifiants de ligne de temps doivent être inférieurs à l'identifiant de la ligne de temps enfant.\n"
+
+#~ msgid "WAL file is from different database system: incorrect XLOG_SEG_SIZE in page header"
+#~ msgstr "le fichier WAL provient d'un système différent : XLOG_SEG_SIZE invalide dans l'en-tête de page"
+
+#~ msgid "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d byte\n"
+#~ msgid_plural "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d bytes\n"
+#~ msgstr[0] "La taille du segment WAL doit être une puissance de deux comprise entre 1 Mo et 1 Go, mais le fichier de contrôle indique %d octet\n"
+#~ msgstr[1] "La taille du segment WAL doit être une puissance de deux comprise entre 1 Mo et 1 Go, mais le fichier de contrôle indique %d octets\n"
+
+#~ msgid "could not close directory \"%s\": %s\n"
+#~ msgstr "n'a pas pu fermer le répertoire « %s » : %s\n"
+
+#~ msgid "could not close file \"%s\": %s\n"
+#~ msgstr "n'a pas pu fermer le fichier « %s » : %s\n"
+
+#~ msgid "could not connect to server: %s"
+#~ msgstr "n'a pas pu se connecter au serveur : %s"
+
+#~ msgid "could not create directory \"%s\": %s\n"
+#~ msgstr "n'a pas pu créer le répertoire « %s » : %s\n"
+
+#~ msgid "could not create temporary table: %s"
+#~ msgstr "n'a pas pu créer la table temporaire : %s"
+
+#~ msgid "could not open directory \"%s\": %s\n"
+#~ msgstr "n'a pas pu ouvrir le répertoire « %s » : %s\n"
+
+#~ msgid "could not open file \"%s\" for reading: %s\n"
+#~ msgstr "n'a pas pu ouvrir le fichier « %s » pour une lecture : %s\n"
+
+#~ msgid "could not open file \"%s\": %s\n"
+#~ msgstr "n'a pas pu ouvrir le fichier « %s » : %s\n"
+
+#~ msgid "could not read directory \"%s\": %s\n"
+#~ msgstr "n'a pas pu lire le répertoire « %s » : %s\n"
+
+#~ msgid "could not read file \"%s\": %s\n"
+#~ msgstr "n'a pas pu lire le fichier « %s » : %s\n"
+
+#~ msgid "could not read from file \"%s\": %s\n"
+#~ msgstr "n'a pas pu lire le fichier « %s » : %s\n"
+
+#~ msgid "could not read symbolic link \"%s\": %s\n"
+#~ msgstr "n'a pas pu lire le lien symbolique « %s » : %s\n"
+
+#~ msgid "could not remove directory \"%s\": %s\n"
+#~ msgstr "n'a pas pu supprimer le répertoire « %s » : %s\n"
+
+#~ msgid "could not remove file \"%s\": %s\n"
+#~ msgstr "n'a pas pu supprimer le fichier « %s » : %s\n"
+
+#~ msgid "could not remove symbolic link \"%s\": %s\n"
+#~ msgstr "n'a pas pu supprimer le lien symbolique « %s » : %s\n"
+
+#~ msgid "could not seek in file \"%s\": %s\n"
+#~ msgstr "n'a pas pu chercher dans le fichier « %s » : %s\n"
+
+#~ msgid "could not send COPY data: %s"
+#~ msgstr "n'a pas pu envoyer les données COPY : %s"
+
+#~ msgid "could not send end-of-COPY: %s"
+#~ msgstr "n'a pas pu envoyer end-of-COPY : %s"
+
+#~ msgid "could not send file list: %s"
+#~ msgstr "n'a pas pu envoyer la liste de fichiers : %s"
+
+#~ msgid "could not set up connection context: %s"
+#~ msgstr "n'a pas pu initialiser le contexte de connexion : « %s »"
+
+#~ msgid "could not stat file \"%s\": %s\n"
+#~ msgstr "n'a pas pu tester le fichier « %s » : %s\n"
+
+#~ msgid "could not truncate file \"%s\" to %u: %s\n"
+#~ msgstr "n'a pas pu tronquer le fichier « %s » à %u : %s\n"
+
+#~ msgid "could not write file \"%s\": %s\n"
+#~ msgstr "n'a pas pu écrire le fichier « %s » : %s\n"
+
+#~ msgid "entry \"%s\" excluded from source file list\n"
+#~ msgstr "enregistrement « %s » exclus de la liste des fichiers sources\n"
+
+#~ msgid "entry \"%s\" excluded from target file list\n"
+#~ msgstr "enregistrement « %s » exclus de la liste des fichiers cibles\n"
+
+#~ msgid "fetched file \"%s\", length %d\n"
+#~ msgstr "fichier récupéré « %s », longueur %d\n"
+
+#~ msgid "getting file chunks\n"
+#~ msgstr "récupération des parties de fichier\n"
+
+#~ msgid "invalid contrecord length %u at %X/%X reading %X/%X, expected %u"
+#~ msgstr "longueur %u invalide du contrecord à %X/%X en lisant %X/%X, attendait %u"
+
+#~ msgid "invalid data in history file: %s\n"
+#~ msgstr "données invalides dans le fichier historique : %s\n"
+
+#~ msgid "received data at offset "
+#~ msgstr "a reçu des données au décalage "
+
+#~ msgid "received null value for chunk for file \"%s\", file has been deleted\n"
+#~ msgstr "a reçu une valeur NULL pour une partie du fichier « %s », le fichier a été supprimé\n"
+
+#~ msgid "source file list is empty"
+#~ msgstr "la liste de fichiers sources est vide"
+
+#~ msgid "source server must not be in recovery mode"
+#~ msgstr "le serveur source ne doit pas être en mode restauration"
+
+#~ msgid "symbolic link \"%s\" target is too long\n"
+#~ msgstr "la cible du lien symbolique « %s » est trop long\n"
+
+#~ msgid "sync of target directory failed\n"
+#~ msgstr "échec de la synchronisation du répertoire cible\n"
+
+#~ msgid "syntax error in history file: %s\n"
+#~ msgstr "erreur de syntaxe dans le fichier historique : %s\n"
+
+#~ msgid "there is no contrecord flag at %X/%X reading %X/%X"
+#~ msgstr "il n'existe pas de drapeau contrecord à %X/%X en lisant %X/%X"
+
+#~ msgid "unexpected result while sending file list: %s"
+#~ msgstr "résultat inattendu lors de l'envoi de la liste de fichiers : %s"
diff --git a/src/bin/pg_rewind/po/ja.po b/src/bin/pg_rewind/po/ja.po
new file mode 100644
index 0000000..ac13d01
--- /dev/null
+++ b/src/bin/pg_rewind/po/ja.po
@@ -0,0 +1,964 @@
+# pg_rewind.po
+# Japanese message translation file for pg_rewind
+#
+# Copyright (C) 2016-2022 PostgreSQL Global Development Group
+#
+# This file is distributed under the same license as the PostgreSQL package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_rewind (PostgreSQL 14)\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-05-12 14:05+0900\n"
+"PO-Revision-Date: 2022-05-12 14:44+0900\n"
+"Last-Translator: Kyotaro Horiguchi <horikyota.ntt@gmail.com>\n"
+"Language-Team: Japan PostgreSQL Users Group <jpug-doc@ml.postgresql.jp>\n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.8.13\n"
+"Plural-Forms: nplurals=2; plural=n!=1;\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "致命的エラー: "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "エラー: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "警告: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "メモリ不足\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "nullポインタは複製できません (内部エラー)\n"
+
+#: ../../common/restricted_token.c:64
+#, c-format
+msgid "could not load library \"%s\": error code %lu"
+msgstr "ライブラリ\"%s\"をロードできませんでした: エラーコード %lu"
+
+#: ../../common/restricted_token.c:73
+#, c-format
+msgid "cannot create restricted tokens on this platform: error code %lu"
+msgstr "このプラットフォームでは制限付きトークンを生成できません: エラーコード %lu"
+
+#: ../../common/restricted_token.c:82
+#, c-format
+msgid "could not open process token: error code %lu"
+msgstr "プロセストークンをオープンできませんでした: エラーコード %lu"
+
+#: ../../common/restricted_token.c:97
+#, c-format
+msgid "could not allocate SIDs: error code %lu"
+msgstr "SIDを割り当てられませんでした: エラーコード %lu"
+
+#: ../../common/restricted_token.c:119
+#, c-format
+msgid "could not create restricted token: error code %lu"
+msgstr "制限付きトークンを生成できませんでした: エラーコード %lu"
+
+#: ../../common/restricted_token.c:140
+#, c-format
+msgid "could not start process for command \"%s\": error code %lu"
+msgstr "コマンド\"%s\"のプロセスを起動できませんでした: エラーコード %lu"
+
+#: ../../common/restricted_token.c:178
+#, c-format
+msgid "could not re-execute with restricted token: error code %lu"
+msgstr "制限付きトークンで再実行できませんでした: %lu"
+
+#: ../../common/restricted_token.c:194
+#, c-format
+msgid "could not get exit code from subprocess: error code %lu"
+msgstr "サブプロセスの終了コードを取得できませんでした: エラーコード %lu"
+
+#: ../../fe_utils/archive.c:53
+#, c-format
+msgid "cannot use restore_command with %%r placeholder"
+msgstr "restore_commandで%%r指定は使用できません"
+
+#: ../../fe_utils/archive.c:74
+#, c-format
+msgid "unexpected file size for \"%s\": %lld instead of %lld"
+msgstr "予期しない\"%1$s\"のサイズ: %3$lld ではなく %2$lld"
+
+#: ../../fe_utils/archive.c:85
+#, c-format
+msgid "could not open file \"%s\" restored from archive: %m"
+msgstr "アーカイブからリストアされたファイル\"%s\"のオープンに失敗しました: %m"
+
+#: ../../fe_utils/archive.c:97 file_ops.c:417
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "ファイル\"%s\"のstatに失敗しました: %m"
+
+#: ../../fe_utils/archive.c:112
+#, c-format
+msgid "restore_command failed: %s"
+msgstr "restore_commandが失敗しました: %s"
+
+#: ../../fe_utils/archive.c:121
+#, c-format
+msgid "could not restore file \"%s\" from archive"
+msgstr "ファイル\"%s\"をアーカイブからリストアできませんでした"
+
+#: ../../fe_utils/recovery_gen.c:35 ../../fe_utils/recovery_gen.c:49
+#: ../../fe_utils/recovery_gen.c:77 ../../fe_utils/recovery_gen.c:100
+#: ../../fe_utils/recovery_gen.c:171 parsexlog.c:77 parsexlog.c:137
+#: parsexlog.c:197
+#, c-format
+msgid "out of memory"
+msgstr "メモリ不足"
+
+#: ../../fe_utils/recovery_gen.c:134 parsexlog.c:310
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "ファイル\"%s\"をオープンできませんでした: %m"
+
+#: ../../fe_utils/recovery_gen.c:140
+#, c-format
+msgid "could not write to file \"%s\": %m"
+msgstr "ファイル\"%s\"に書き込めませんでした: %m"
+
+#: ../../fe_utils/recovery_gen.c:152
+#, c-format
+msgid "could not create file \"%s\": %m"
+msgstr "ファイル\"%s\"を作成できませんでした: %m"
+
+#: file_ops.c:67
+#, c-format
+msgid "could not open target file \"%s\": %m"
+msgstr "ターゲットファイル\"%s\"をオープンできませんでした: %m"
+
+#: file_ops.c:81
+#, c-format
+msgid "could not close target file \"%s\": %m"
+msgstr "ターゲットファイル\"%s\"をクローズできませんでした: %m"
+
+#: file_ops.c:101
+#, c-format
+msgid "could not seek in target file \"%s\": %m"
+msgstr "ターゲットファイル\"%s\"をシークできませんでした: %m"
+
+#: file_ops.c:117
+#, c-format
+msgid "could not write file \"%s\": %m"
+msgstr "ファイル\"%s\"を書き出せませんでした: %m"
+
+#: file_ops.c:150 file_ops.c:177
+#, c-format
+msgid "undefined file type for \"%s\""
+msgstr "認識不能のファイルタイプ\"%s\""
+
+#: file_ops.c:173
+#, c-format
+msgid "invalid action (CREATE) for regular file"
+msgstr "通常のファイルに対する不正なアクション(CREATE)です"
+
+#: file_ops.c:200
+#, c-format
+msgid "could not remove file \"%s\": %m"
+msgstr "ファイル\"%s\"を削除できませんでした: %m"
+
+#: file_ops.c:218
+#, c-format
+msgid "could not open file \"%s\" for truncation: %m"
+msgstr "ファイル\"%s\"を切り詰めのためにオープンできませんでした: %m"
+
+#: file_ops.c:222
+#, c-format
+msgid "could not truncate file \"%s\" to %u: %m"
+msgstr "ファイル\"%s\"を%uバイトに切り詰められませんでした: %m"
+
+#: file_ops.c:238
+#, c-format
+msgid "could not create directory \"%s\": %m"
+msgstr "ディレクトリ\"%s\"を作成できませんでした: %m"
+
+#: file_ops.c:252
+#, c-format
+msgid "could not remove directory \"%s\": %m"
+msgstr "ディレクトリ\"%s\"を削除できませんでした: %m"
+
+#: file_ops.c:266
+#, c-format
+msgid "could not create symbolic link at \"%s\": %m"
+msgstr "\"%s\"にシンボリックリンクを作成できませんでした: %m"
+
+#: file_ops.c:280
+#, c-format
+msgid "could not remove symbolic link \"%s\": %m"
+msgstr "シンボリックリンク\"%s\"を削除できませんでした: %m"
+
+#: file_ops.c:326 file_ops.c:330
+#, c-format
+msgid "could not open file \"%s\" for reading: %m"
+msgstr "ファイル\"%s\"を読み取り用にオープンできませんでした: %m"
+
+#: file_ops.c:341 local_source.c:107 parsexlog.c:348
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "ファイル\"%s\"の読み取りに失敗しました: %m"
+
+#: file_ops.c:344 parsexlog.c:350
+#, c-format
+msgid "could not read file \"%s\": read %d of %zu"
+msgstr "ファイル\"%1$s\"を読み込めませんでした: %3$zuバイトのうち%2$dバイトを読み込みました"
+
+#: file_ops.c:388
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "ディレクトリ\"%s\"をオープンできませんでした: %m"
+
+#: file_ops.c:446
+#, c-format
+msgid "could not read symbolic link \"%s\": %m"
+msgstr "シンボリックリンク\"%s\"を読めませんでした: %m"
+
+#: file_ops.c:449
+#, c-format
+msgid "symbolic link \"%s\" target is too long"
+msgstr "シンボリックリンク\"%s\"の参照先が長すぎます"
+
+#: file_ops.c:464
+#, c-format
+msgid "\"%s\" is a symbolic link, but symbolic links are not supported on this platform"
+msgstr "\"%s\"はシンボリックリンクですが、このプラットフォームではシンボリックリンクをサポートしていません"
+
+#: file_ops.c:471
+#, c-format
+msgid "could not read directory \"%s\": %m"
+msgstr "ディレクトリ\"%s\"を読み取れませんでした: %m"
+
+#: file_ops.c:475
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "ディレクトリ\"%s\"をクローズできませんでした: %m"
+
+#: filemap.c:237
+#, c-format
+msgid "data file \"%s\" in source is not a regular file"
+msgstr "ソースのデータファイル\"%s\"は通常のファイルではありません"
+
+#: filemap.c:242 filemap.c:275
+#, c-format
+msgid "duplicate source file \"%s\""
+msgstr "重複したソースファイル\"%s\""
+
+#: filemap.c:330
+#, c-format
+msgid "unexpected page modification for non-regular file \"%s\""
+msgstr "非通常ファイル\"%s\"に対する想定外のページの書き換え"
+
+#: filemap.c:680 filemap.c:774
+#, c-format
+msgid "unknown file type for \"%s\""
+msgstr "\"%s\"で未知のファイルタイプ"
+
+#: filemap.c:707
+#, c-format
+msgid "file \"%s\" is of different type in source and target"
+msgstr "ファイル\"%s\"のタイプがソースとターゲットとで異なっています"
+
+#: filemap.c:779
+#, c-format
+msgid "could not decide what to do with file \"%s\""
+msgstr "ファイル\"%s\"に対して行う操作を決定できませんでした"
+
+#: libpq_source.c:128
+#, c-format
+msgid "could not clear search_path: %s"
+msgstr "search_pathを消去できませんでした: %s"
+
+#: libpq_source.c:139
+#, c-format
+msgid "full_page_writes must be enabled in the source server"
+msgstr "ソースサーバーではfull_pate_writesは有効でなければなりません"
+
+#: libpq_source.c:150
+#, c-format
+msgid "could not prepare statement to fetch file contents: %s"
+msgstr "内容を取得するための文を準備できませんでした: %s"
+
+#: libpq_source.c:169
+#, c-format
+msgid "error running query (%s) on source server: %s"
+msgstr "ソースサーバーで実行中のクエリ(%s)でエラー: %s"
+
+#: libpq_source.c:174
+#, c-format
+msgid "unexpected result set from query"
+msgstr "クエリから想定外の結果セット"
+
+#: libpq_source.c:196
+#, c-format
+msgid "error running query (%s) in source server: %s"
+msgstr "ソースサーバーの実行中のクエリ(%s)でエラー: %s"
+
+#: libpq_source.c:217
+#, c-format
+msgid "unrecognized result \"%s\" for current WAL insert location"
+msgstr "現在のWAL挿入位置として認識不可の結果\"%s\""
+
+#: libpq_source.c:268
+#, c-format
+msgid "could not fetch file list: %s"
+msgstr "ファイルリストをフェッチできませんでした: %s"
+
+#: libpq_source.c:273
+#, c-format
+msgid "unexpected result set while fetching file list"
+msgstr "ファイルリストのフェッチ中に想定外の結果セット"
+
+#: libpq_source.c:435
+#, c-format
+msgid "could not send query: %s"
+msgstr "クエリを送信できませんでした: %s"
+
+#: libpq_source.c:438
+#, c-format
+msgid "could not set libpq connection to single row mode"
+msgstr "libpq接続を単一行モードに設定できませんでした"
+
+#: libpq_source.c:468
+#, c-format
+msgid "unexpected result while fetching remote files: %s"
+msgstr "リモートファイルをフェッチ中に想定外の結果: %s"
+
+#: libpq_source.c:473
+#, c-format
+msgid "received more data chunks than requested"
+msgstr "要求より多くのデータチャンクを受け取りました"
+
+#: libpq_source.c:477
+#, c-format
+msgid "unexpected result set size while fetching remote files"
+msgstr "リモートファイルのフェッチ中に想定外の結果セットサイズ"
+
+#: libpq_source.c:483
+#, c-format
+msgid "unexpected data types in result set while fetching remote files: %u %u %u"
+msgstr "リモートファイルのフェッチ中の結果セットに想定外のデータ型: %u %u %u"
+
+#: libpq_source.c:491
+#, c-format
+msgid "unexpected result format while fetching remote files"
+msgstr "リモートファイルのフェッチ中に想定外の結果形式"
+
+#: libpq_source.c:497
+#, c-format
+msgid "unexpected null values in result while fetching remote files"
+msgstr "リモートファイルのフェッチ中の結果に想定外のNULL値"
+
+#: libpq_source.c:501
+#, c-format
+msgid "unexpected result length while fetching remote files"
+msgstr "リモートファイルのフェッチ中に想定外の結果の長さ"
+
+#: libpq_source.c:534
+#, c-format
+msgid "received data for file \"%s\", when requested for \"%s\""
+msgstr "ファイル\"%2$s\"の要求をしていましたが、ファイル\"%1$s\"のデータを受け取りました"
+
+#: libpq_source.c:538
+#, c-format
+msgid "received data at offset %lld of file \"%s\", when requested for offset %lld"
+msgstr "ファイル\"%2$s\"のオフセット%1$lldでデータを受け取りました、しかし要求したのはオフセット%3$lldです"
+
+#: libpq_source.c:550
+#, c-format
+msgid "received more than requested for file \"%s\""
+msgstr "ファイル\"%s\"に対して要求以上のデータを受け取りました"
+
+#: libpq_source.c:563
+#, c-format
+msgid "unexpected number of data chunks received"
+msgstr "想定外の数のデータチャンクを受け取りました"
+
+#: libpq_source.c:606
+#, c-format
+msgid "could not fetch remote file \"%s\": %s"
+msgstr "リモートファイル\"%s\"をフェッチできませんでした: %s"
+
+#: libpq_source.c:611
+#, c-format
+msgid "unexpected result set while fetching remote file \"%s\""
+msgstr "リモートファイル\"%s\"のフェッチ中に想定外の結果セット"
+
+#: local_source.c:86
+#, c-format
+msgid "could not open source file \"%s\": %m"
+msgstr "ソースファイル\"%s\"をオープンすることができませんでした: %m"
+
+#: local_source.c:90
+#, c-format
+msgid "could not seek in source file: %m"
+msgstr "ソースファイルをシークすることができませんでした: %m"
+
+#: local_source.c:109
+#, c-format
+msgid "unexpected EOF while reading file \"%s\""
+msgstr "ファイル\"%s\"を読み込み中に想定外のEOF"
+
+#: local_source.c:116
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "ファイル\"%s\"をクローズできませんでした: %m"
+
+#: parsexlog.c:89 parsexlog.c:144
+#, c-format
+msgid "could not read WAL record at %X/%X: %s"
+msgstr "%X/%XのWALレコードを読み取れませんでした: %s"
+
+#: parsexlog.c:93 parsexlog.c:147
+#, c-format
+msgid "could not read WAL record at %X/%X"
+msgstr "%X/%XのWALレコードを読み取れませんでした"
+
+#: parsexlog.c:106
+#, c-format
+msgid "end pointer %X/%X is not a valid end point; expected %X/%X"
+msgstr "終了点%X/%Xは妥当な終了点ではありません; %X/%Xを期待していました"
+
+#: parsexlog.c:210
+#, c-format
+msgid "could not find previous WAL record at %X/%X: %s"
+msgstr "%X/%Xの前のWALレコードが見つかりませんでした: %s"
+
+#: parsexlog.c:214
+#, c-format
+msgid "could not find previous WAL record at %X/%X"
+msgstr "%X/%Xの前のWALレコードが見つかりませんでした"
+
+#: parsexlog.c:339
+#, c-format
+msgid "could not seek in file \"%s\": %m"
+msgstr "ファイル\"%s\"をシークできませんでした: %m"
+
+#: parsexlog.c:431
+#, c-format
+msgid "WAL record modifies a relation, but record type is not recognized: lsn: %X/%X, rmgr: %s, info: %02X"
+msgstr "WALレコードはリレーションを修正しますが、レコードの型を認識できません: lsn: %X/%X、rmgr: %s、info: %02X"
+
+#: pg_rewind.c:84
+#, c-format
+msgid ""
+"%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n"
+"\n"
+msgstr ""
+"%s はPostgreSQLクラスタをそのクラスタのコピーで再同期します。\n"
+"\n"
+
+#: pg_rewind.c:85
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]...\n"
+"\n"
+msgstr ""
+"使用方法:\n"
+" %s [オプション]...\n"
+"\n"
+
+#: pg_rewind.c:86
+#, c-format
+msgid "Options:\n"
+msgstr "オプション:\n"
+
+#: pg_rewind.c:87
+#, c-format
+msgid ""
+" -c, --restore-target-wal use restore_command in target configuration to\n"
+" retrieve WAL files from archives\n"
+msgstr ""
+" -c, --restore-target-wal ターゲットの設定の中のrestore_commandを使用して\n"
+" アーカイブからWALファイルを取得する\n"
+
+#: pg_rewind.c:89
+#, c-format
+msgid " -D, --target-pgdata=DIRECTORY existing data directory to modify\n"
+msgstr " -D, --target-pgdata=DIRECTORY 修正を行う既存データディレクトリ\n"
+
+#: pg_rewind.c:90
+#, c-format
+msgid " --source-pgdata=DIRECTORY source data directory to synchronize with\n"
+msgstr " --source-pgdata=DIRECTORY 同期元とするデータディレクトリ\n"
+
+#: pg_rewind.c:91
+#, c-format
+msgid " --source-server=CONNSTR source server to synchronize with\n"
+msgstr " --source-server=CONNSTR 同期元とするサーバー\n"
+
+#: pg_rewind.c:92
+#, c-format
+msgid " -n, --dry-run stop before modifying anything\n"
+msgstr " -n, --dry-run 修正を始める前に停止する\n"
+
+#: pg_rewind.c:93
+#, c-format
+msgid ""
+" -N, --no-sync do not wait for changes to be written\n"
+" safely to disk\n"
+msgstr " -N, --no-sync 変更のディスクへの安全な書き出しを待機しない\n"
+
+#: pg_rewind.c:95
+#, c-format
+msgid " -P, --progress write progress messages\n"
+msgstr " -P, --progress 進捗メッセージを出力\n"
+
+#: pg_rewind.c:96
+#, c-format
+msgid ""
+" -R, --write-recovery-conf write configuration for replication\n"
+" (requires --source-server)\n"
+msgstr ""
+" -R, --write-recovery-conf レプリケーションのための設定を書き込む\n"
+" (--source-server が必要となります)\n"
+
+#: pg_rewind.c:98
+#, c-format
+msgid " --debug write a lot of debug messages\n"
+msgstr " --debug 多量のデバッグメッセージを出力\n"
+
+#: pg_rewind.c:99
+#, c-format
+msgid " --no-ensure-shutdown do not automatically fix unclean shutdown\n"
+msgstr " --no-ensure-shutdown 非クリーンシャットダウン後の修正を自動で行わない\n"
+
+#: pg_rewind.c:100
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version バージョン情報を表示して終了\n"
+
+#: pg_rewind.c:101
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help このヘルプを表示して終了\n"
+
+#: pg_rewind.c:102
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"バグは<%s>に報告してください。\n"
+
+#: pg_rewind.c:103
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "%s ホームページ: <%s>\n"
+
+#: pg_rewind.c:164 pg_rewind.c:213 pg_rewind.c:220 pg_rewind.c:227
+#: pg_rewind.c:234 pg_rewind.c:242
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "詳細は\"%s --help\"で確認してください。\n"
+
+#: pg_rewind.c:212
+#, c-format
+msgid "no source specified (--source-pgdata or --source-server)"
+msgstr "ソースが指定されていません(--source-pgdata または --source-server)"
+
+#: pg_rewind.c:219
+#, c-format
+msgid "only one of --source-pgdata or --source-server can be specified"
+msgstr "--source-pgdataか--source-server はいずれか一方のみ指定可能です"
+
+#: pg_rewind.c:226
+#, c-format
+msgid "no target data directory specified (--target-pgdata)"
+msgstr "ターゲットデータディレクトリが指定されていません(--target-pgdata)"
+
+#: pg_rewind.c:233
+#, c-format
+msgid "no source server information (--source-server) specified for --write-recovery-conf"
+msgstr "--write-recovery-confにソースサーバー情報(--source-server)が指定されていません"
+
+#: pg_rewind.c:240
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "コマンドライン引数が多すぎます (先頭は\"%s\")"
+
+#: pg_rewind.c:255
+#, c-format
+msgid "cannot be executed by \"root\""
+msgstr "\"root\"では実行できません"
+
+#: pg_rewind.c:256
+#, c-format
+msgid "You must run %s as the PostgreSQL superuser.\n"
+msgstr "PostgreSQLのスーパーユーザーで%sを実行しなければなりません\n"
+
+#: pg_rewind.c:267
+#, c-format
+msgid "could not read permissions of directory \"%s\": %m"
+msgstr "ディレクトリ\"%s\"の権限を読み取れませんでした: %m"
+
+#: pg_rewind.c:287
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: pg_rewind.c:290
+#, c-format
+msgid "connected to server"
+msgstr "サーバーへ接続しました"
+
+#: pg_rewind.c:337
+#, c-format
+msgid "source and target cluster are on the same timeline"
+msgstr "ソースとターゲットのクラスタが同一タイムライン上にあります"
+
+#: pg_rewind.c:346
+#, c-format
+msgid "servers diverged at WAL location %X/%X on timeline %u"
+msgstr "タイムライン%3$uのWAL位置%1$X/%2$Xで両サーバーが分岐しています"
+
+#: pg_rewind.c:394
+#, c-format
+msgid "no rewind required"
+msgstr "巻き戻しは必要ありません"
+
+#: pg_rewind.c:403
+#, c-format
+msgid "rewinding from last common checkpoint at %X/%X on timeline %u"
+msgstr "タイムライン%3$uの%1$X/%2$Xにある最新の共通チェックポイントから巻き戻しています"
+
+#: pg_rewind.c:413
+#, c-format
+msgid "reading source file list"
+msgstr "ソースファイルリストを読み込んでいます"
+
+#: pg_rewind.c:417
+#, c-format
+msgid "reading target file list"
+msgstr "ターゲットファイルリストを読み込んでいます"
+
+#: pg_rewind.c:426
+#, c-format
+msgid "reading WAL in target"
+msgstr "ターゲットでWALを読み込んでいます"
+
+#: pg_rewind.c:447
+#, c-format
+msgid "need to copy %lu MB (total source directory size is %lu MB)"
+msgstr "%lu MBコピーする必要があります(ソースディレクトリの合計サイズは%lu MBです)"
+
+#: pg_rewind.c:465
+#, c-format
+msgid "syncing target data directory"
+msgstr "ターゲットデータディレクトリを同期しています"
+
+#: pg_rewind.c:481
+#, c-format
+msgid "Done!"
+msgstr "完了!"
+
+#: pg_rewind.c:564
+#, c-format
+msgid "no action decided for file \"%s\""
+msgstr "ファイル\"%s\"に対する操作が決定されていません"
+
+#: pg_rewind.c:596
+#, c-format
+msgid "source system was modified while pg_rewind was running"
+msgstr "ソースシステムはpg_rewindの実行中に更新されました"
+
+#: pg_rewind.c:600
+#, c-format
+msgid "creating backup label and updating control file"
+msgstr "backup labelを作成して制御ファイルを更新しています"
+
+#: pg_rewind.c:650
+#, c-format
+msgid "source system was in unexpected state at end of rewind"
+msgstr "ソースシステムは巻き戻し終了時に予期しない状態になっていました"
+
+#: pg_rewind.c:681
+#, c-format
+msgid "source and target clusters are from different systems"
+msgstr "ソースクラスタとターゲットクラスタは異なるシステムのものです"
+
+#: pg_rewind.c:689
+#, c-format
+msgid "clusters are not compatible with this version of pg_rewind"
+msgstr "クラスタは、このバージョンのpg_rewindとの互換性がありません"
+
+#: pg_rewind.c:699
+#, c-format
+msgid "target server needs to use either data checksums or \"wal_log_hints = on\""
+msgstr "ターゲットサーバーはデータチェックサムを利用している、または\"wal_log_hints = on\"である必要があります"
+
+#: pg_rewind.c:710
+#, c-format
+msgid "target server must be shut down cleanly"
+msgstr "ターゲットサーバーはきれいにシャットダウンされていなければなりません"
+
+#: pg_rewind.c:720
+#, c-format
+msgid "source data directory must be shut down cleanly"
+msgstr "ソースデータディレクトリはきれいにシャットダウンされていなければなりません"
+
+#: pg_rewind.c:772
+#, c-format
+msgid "%*s/%s kB (%d%%) copied"
+msgstr "%*s/%s kB (%d%%) コピーしました"
+
+#: pg_rewind.c:835
+#, c-format
+msgid "invalid control file"
+msgstr "不正な制御ファイル"
+
+#: pg_rewind.c:919
+#, c-format
+msgid "could not find common ancestor of the source and target cluster's timelines"
+msgstr "ソースクラスタとターゲットクラスタのタイムラインの共通の祖先を見つけられません"
+
+#: pg_rewind.c:960
+#, c-format
+msgid "backup label buffer too small"
+msgstr "バックアップラベルのバッファが小さすぎます"
+
+#: pg_rewind.c:983
+#, c-format
+msgid "unexpected control file CRC"
+msgstr "想定外の制御ファイルCRCです"
+
+#: pg_rewind.c:995
+#, c-format
+msgid "unexpected control file size %d, expected %d"
+msgstr "想定外の制御ファイルのサイズ%d、想定は%d"
+
+#: pg_rewind.c:1004
+#, c-format
+msgid "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d byte"
+msgid_plural "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d bytes"
+msgstr[0] "WALセグメントのサイズ指定は1MBと1GBの間の2の累乗でなければなりません、しかしコントロールファイルでは%dバイトとなっています"
+msgstr[1] "WALセグメントのサイズ指定は1MBと1GBの間の2の累乗でなければなりません、しかしコントロールファイルでは%dバイトとなっています"
+
+#: pg_rewind.c:1043 pg_rewind.c:1101
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"%2$sにはプログラム\"%1$s\"が必要ですが、\"%3$s\"と同じディレクトリ\n"
+"にはありませんでした。\n"
+"インストールの状態を確認してください。"
+
+#: pg_rewind.c:1048 pg_rewind.c:1106
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"\"%2$s\"がプログラム\"%1$s\"を見つけましたが、これは%3$sと同じ\n"
+"バージョンではありませんでした。\n"
+"インストールの状態を確認してください。"
+
+#: pg_rewind.c:1069
+#, c-format
+msgid "restore_command is not set in the target cluster"
+msgstr "ターゲットクラスタでrestore_commandが設定されていません"
+
+#: pg_rewind.c:1112
+#, c-format
+msgid "executing \"%s\" for target server to complete crash recovery"
+msgstr "ターゲットサーバーに対して\"%s\"を実行してクラッシュリカバリを完了させます"
+
+#: pg_rewind.c:1132
+#, c-format
+msgid "postgres single-user mode in target cluster failed"
+msgstr "ターゲットクラスタでのpostgresコマンドのシングルユーザーモード実行に失敗しました"
+
+#: pg_rewind.c:1133
+#, c-format
+msgid "Command was: %s"
+msgstr "コマンド: %s"
+
+#: timeline.c:75 timeline.c:81
+#, c-format
+msgid "syntax error in history file: %s"
+msgstr "履歴ファイル内の構文エラー: %s"
+
+#: timeline.c:76
+#, c-format
+msgid "Expected a numeric timeline ID."
+msgstr "数字のタイムラインIDを想定しました。"
+
+#: timeline.c:82
+#, c-format
+msgid "Expected a write-ahead log switchpoint location."
+msgstr "先行書き込みログの切り替え点の場所があるはずでした。"
+
+#: timeline.c:87
+#, c-format
+msgid "invalid data in history file: %s"
+msgstr "履歴ファイル内の不正なデータ: %s"
+
+#: timeline.c:88
+#, c-format
+msgid "Timeline IDs must be in increasing sequence."
+msgstr "タイムラインIDは昇順でなければなりません"
+
+#: timeline.c:108
+#, c-format
+msgid "invalid data in history file"
+msgstr "履歴ファイル内の不正なデータ"
+
+#: timeline.c:109
+#, c-format
+msgid "Timeline IDs must be less than child timeline's ID."
+msgstr "タイムラインIDは子のタイムラインIDより小さくなければなりません。"
+
+#: xlogreader.c:354
+#, c-format
+msgid "invalid record offset at %X/%X"
+msgstr "%X/%Xのレコードオフセットが不正です"
+
+#: xlogreader.c:362
+#, c-format
+msgid "contrecord is requested by %X/%X"
+msgstr "%X/%Xで継続レコードが要求されました"
+
+#: xlogreader.c:403 xlogreader.c:733
+#, c-format
+msgid "invalid record length at %X/%X: wanted %u, got %u"
+msgstr "%X/%Xのレコード長が不正です: 長さは%uである必要がありますが、実際は%uでした"
+
+#: xlogreader.c:429
+#, c-format
+msgid "record length %u at %X/%X too long"
+msgstr "%2$X/%3$Xのレコード長%1$uが大きすぎます"
+
+#: xlogreader.c:477
+#, c-format
+msgid "there is no contrecord flag at %X/%X"
+msgstr "%X/%Xで継続レコードフラグが設定されていません"
+
+#: xlogreader.c:490
+#, c-format
+msgid "invalid contrecord length %u (expected %lld) at %X/%X"
+msgstr "%3$X/%4$Xの継続レコードの長さ%1$uが不正です(%2$lldを期待していました)"
+
+#: xlogreader.c:741
+#, c-format
+msgid "invalid resource manager ID %u at %X/%X"
+msgstr "%2$X/%3$XのリソースマネージャID %1$uが不正です"
+
+#: xlogreader.c:754 xlogreader.c:770
+#, c-format
+msgid "record with incorrect prev-link %X/%X at %X/%X"
+msgstr "%3$X/%4$Xのレコードの後方リンク%1$X/%2$Xが不正です"
+
+#: xlogreader.c:806
+#, c-format
+msgid "incorrect resource manager data checksum in record at %X/%X"
+msgstr "%X/%Xのレコード内のリソースマネージャデータのチェックサムが不正です"
+
+#: xlogreader.c:843
+#, c-format
+msgid "invalid magic number %04X in log segment %s, offset %u"
+msgstr "ログセグメント%2$s、オフセット%3$uのマジックナンバー%1$04Xは不正です"
+
+#: xlogreader.c:857 xlogreader.c:898
+#, c-format
+msgid "invalid info bits %04X in log segment %s, offset %u"
+msgstr "ログセグメント %2$s、オフセット%3$uの情報ビット%1$04Xは不正です"
+
+#: xlogreader.c:872
+#, c-format
+msgid "WAL file is from different database system: WAL file database system identifier is %llu, pg_control database system identifier is %llu"
+msgstr "WALファイルは異なるデータベースシステム由来のものです: WALファイルのデータベースシステム識別子は %lluで、pg_control におけるデータベースシステム識別子は %lluです"
+
+#: xlogreader.c:880
+#, c-format
+msgid "WAL file is from different database system: incorrect segment size in page header"
+msgstr "WAL ファイルは異なるデータベースシステム由来のものです: ページヘッダーのセグメントサイズが正しくありません"
+
+#: xlogreader.c:886
+#, c-format
+msgid "WAL file is from different database system: incorrect XLOG_BLCKSZ in page header"
+msgstr "WAL ファイルは異なるデータベースシステム由来のものです: ページヘッダーのXLOG_BLCKSZが正しくありません"
+
+#: xlogreader.c:917
+#, c-format
+msgid "unexpected pageaddr %X/%X in log segment %s, offset %u"
+msgstr "ログセグメント%3$s、オフセット%4$uに想定外のページアドレス%1$X/%2$X"
+
+#: xlogreader.c:942
+#, c-format
+msgid "out-of-sequence timeline ID %u (after %u) in log segment %s, offset %u"
+msgstr "ログセグメント%3$s、オフセット%4$uの時系列ID %1$u(%2$uの後)は順序に従っていません"
+
+#: xlogreader.c:1287
+#, c-format
+msgid "out-of-order block_id %u at %X/%X"
+msgstr "block_id %uが%X/%Xで不正です"
+
+#: xlogreader.c:1309
+#, c-format
+msgid "BKPBLOCK_HAS_DATA set, but no data included at %X/%X"
+msgstr "BKPBLOCK_HAS_DATAが設定されていますが、%X/%Xにデータがありません"
+
+#: xlogreader.c:1316
+#, c-format
+msgid "BKPBLOCK_HAS_DATA not set, but data length is %u at %X/%X"
+msgstr "BKPBLOCK_HAS_DATAが設定されていませんが、%2$X/%3$Xのデータ長は%1$uです"
+
+#: xlogreader.c:1352
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE set, but hole offset %u length %u block image length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLEが設定されていますが、%4$X/%5$Xにおいてホールオフセットが%1$u、長さが%2$u、ブロックイメージ長が%3$uです"
+
+#: xlogreader.c:1368
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE not set, but hole offset %u length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLEが設定されていませんが、%3$X/%4$Xにおいてホールオフセットが%1$u、長さが%2$uです"
+
+#: xlogreader.c:1383
+#, c-format
+msgid "BKPIMAGE_IS_COMPRESSED set, but block image length %u at %X/%X"
+msgstr "BKPIMAGE_IS_COMPRESSEDが設定されていますが、%2$X/%3$Xにおいてブロックイメージ長が%1$uです"
+
+#: xlogreader.c:1398
+#, c-format
+msgid "neither BKPIMAGE_HAS_HOLE nor BKPIMAGE_IS_COMPRESSED set, but block image length is %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLEもBKPIMAGE_IS_COMPRESSEDも設定されていませんが、%2$X/%3$Xにおいてブロックイメージ長が%1$uです"
+
+#: xlogreader.c:1414
+#, c-format
+msgid "BKPBLOCK_SAME_REL set but no previous rel at %X/%X"
+msgstr "BKPBLOCK_SAME_RELが設定されていますが、%X/%Xにおいて以前のリレーションがありません"
+
+#: xlogreader.c:1426
+#, c-format
+msgid "invalid block_id %u at %X/%X"
+msgstr "%2$X/%3$Xにおけるblock_id %1$uが不正です"
+
+#: xlogreader.c:1513
+#, c-format
+msgid "record with invalid length at %X/%X"
+msgstr "%X/%Xのレコードのサイズが不正です"
+
+#: xlogreader.c:1602
+#, c-format
+msgid "invalid compressed image at %X/%X, block %d"
+msgstr "%X/%X、ブロック %d での圧縮イメージが不正です"
diff --git a/src/bin/pg_rewind/po/ru.po b/src/bin/pg_rewind/po/ru.po
new file mode 100644
index 0000000..abb414b
--- /dev/null
+++ b/src/bin/pg_rewind/po/ru.po
@@ -0,0 +1,1146 @@
+# Russian message translation file for pg_rewind
+# Copyright (C) 2015-2016 PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+# Alexander Lakhin <exclusion@gmail.com>, 2015-2017, 2018, 2019, 2020, 2021, 2022.
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_rewind (PostgreSQL current)\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-05-07 06:06+0300\n"
+"PO-Revision-Date: 2022-05-07 06:27+0300\n"
+"Last-Translator: Alexander Lakhin <exclusion@gmail.com>\n"
+"Language-Team: Russian <pgsql-ru-general@postgresql.org>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "важно: "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "ошибка: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "предупреждение: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "нехватка памяти\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "попытка дублирования нулевого указателя (внутренняя ошибка)\n"
+
+#: ../../common/restricted_token.c:64
+#, c-format
+msgid "could not load library \"%s\": error code %lu"
+msgstr "не удалось загрузить библиотеку \"%s\" (код ошибки: %lu)"
+
+#: ../../common/restricted_token.c:73
+#, c-format
+msgid "cannot create restricted tokens on this platform: error code %lu"
+msgstr "в этой ОС нельзя создавать ограниченные маркеры (код ошибки: %lu)"
+
+#: ../../common/restricted_token.c:82
+#, c-format
+msgid "could not open process token: error code %lu"
+msgstr "не удалось открыть маркер процесса (код ошибки: %lu)"
+
+#: ../../common/restricted_token.c:97
+#, c-format
+msgid "could not allocate SIDs: error code %lu"
+msgstr "не удалось подготовить структуры SID (код ошибки: %lu)"
+
+#: ../../common/restricted_token.c:119
+#, c-format
+msgid "could not create restricted token: error code %lu"
+msgstr "не удалось создать ограниченный маркер (код ошибки: %lu)"
+
+#: ../../common/restricted_token.c:140
+#, c-format
+msgid "could not start process for command \"%s\": error code %lu"
+msgstr "не удалось запустить процесс для команды \"%s\" (код ошибки: %lu)"
+
+#: ../../common/restricted_token.c:178
+#, c-format
+msgid "could not re-execute with restricted token: error code %lu"
+msgstr "не удалось перезапуститься с ограниченным маркером (код ошибки: %lu)"
+
+#: ../../common/restricted_token.c:194
+#, c-format
+msgid "could not get exit code from subprocess: error code %lu"
+msgstr "не удалось получить код выхода от подпроцесса (код ошибки: %lu)"
+
+#: ../../fe_utils/archive.c:53
+#, c-format
+msgid "cannot use restore_command with %%r placeholder"
+msgstr "нельзя использовать restore_command со знаком подстановки %%r"
+
+#: ../../fe_utils/archive.c:74
+#, c-format
+msgid "unexpected file size for \"%s\": %lld instead of %lld"
+msgstr "неподходящий размер файла \"%s\": %lld вместо %lld байт"
+
+#: ../../fe_utils/archive.c:85
+#, c-format
+msgid "could not open file \"%s\" restored from archive: %m"
+msgstr "не удалось открыть файл \"%s\", восстановленный из архива: %m"
+
+#: ../../fe_utils/archive.c:97 file_ops.c:417
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "не удалось получить информацию о файле \"%s\": %m"
+
+#: ../../fe_utils/archive.c:112
+#, c-format
+msgid "restore_command failed: %s"
+msgstr "ошибка при выполнении restore_command: %s"
+
+#: ../../fe_utils/archive.c:121
+#, c-format
+msgid "could not restore file \"%s\" from archive"
+msgstr "восстановить файл \"%s\" из архива не удалось"
+
+#: ../../fe_utils/recovery_gen.c:35 ../../fe_utils/recovery_gen.c:49
+#: ../../fe_utils/recovery_gen.c:77 ../../fe_utils/recovery_gen.c:100
+#: ../../fe_utils/recovery_gen.c:171 parsexlog.c:77 parsexlog.c:137
+#: parsexlog.c:197
+#, c-format
+msgid "out of memory"
+msgstr "нехватка памяти"
+
+#: ../../fe_utils/recovery_gen.c:134 parsexlog.c:310
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "не удалось открыть файл \"%s\": %m"
+
+#: ../../fe_utils/recovery_gen.c:140
+#, c-format
+msgid "could not write to file \"%s\": %m"
+msgstr "не удалось записать в файл \"%s\": %m"
+
+#: ../../fe_utils/recovery_gen.c:152
+#, c-format
+msgid "could not create file \"%s\": %m"
+msgstr "не удалось создать файл \"%s\": %m"
+
+#: file_ops.c:67
+#, c-format
+msgid "could not open target file \"%s\": %m"
+msgstr "не удалось открыть целевой файл \"%s\": %m"
+
+#: file_ops.c:81
+#, c-format
+msgid "could not close target file \"%s\": %m"
+msgstr "не удалось закрыть целевой файл \"%s\": %m"
+
+#: file_ops.c:101
+#, c-format
+msgid "could not seek in target file \"%s\": %m"
+msgstr "не удалось переместиться в целевом файле \"%s\": %m"
+
+#: file_ops.c:117
+#, c-format
+msgid "could not write file \"%s\": %m"
+msgstr "не удалось записать файл \"%s\": %m"
+
+#: file_ops.c:150 file_ops.c:177
+#, c-format
+msgid "undefined file type for \"%s\""
+msgstr "неопределённый тип файла \"%s\""
+
+#: file_ops.c:173
+#, c-format
+msgid "invalid action (CREATE) for regular file"
+msgstr "неверное действие (CREATE) для обычного файла"
+
+#: file_ops.c:200
+#, c-format
+msgid "could not remove file \"%s\": %m"
+msgstr "не удалось стереть файл \"%s\": %m"
+
+#: file_ops.c:218
+#, c-format
+msgid "could not open file \"%s\" for truncation: %m"
+msgstr "не удалось открыть файл \"%s\" для усечения: %m"
+
+#: file_ops.c:222
+#, c-format
+msgid "could not truncate file \"%s\" to %u: %m"
+msgstr "не удалось обрезать файл \"%s\" до нужного размера (%u): %m"
+
+#: file_ops.c:238
+#, c-format
+msgid "could not create directory \"%s\": %m"
+msgstr "не удалось создать каталог \"%s\": %m"
+
+#: file_ops.c:252
+#, c-format
+msgid "could not remove directory \"%s\": %m"
+msgstr "ошибка при удалении каталога \"%s\": %m"
+
+#: file_ops.c:266
+#, c-format
+msgid "could not create symbolic link at \"%s\": %m"
+msgstr "не удалось создать символическую ссылку \"%s\": %m"
+
+#: file_ops.c:280
+#, c-format
+msgid "could not remove symbolic link \"%s\": %m"
+msgstr "ошибка при удалении символической ссылки \"%s\": %m"
+
+#: file_ops.c:326 file_ops.c:330
+#, c-format
+msgid "could not open file \"%s\" for reading: %m"
+msgstr "не удалось открыть файл \"%s\" для чтения: %m"
+
+#: file_ops.c:341 local_source.c:107 parsexlog.c:348
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "не удалось прочитать файл \"%s\": %m"
+
+#: file_ops.c:344 parsexlog.c:350
+#, c-format
+msgid "could not read file \"%s\": read %d of %zu"
+msgstr "не удалось прочитать файл \"%s\" (прочитано байт: %d из %zu)"
+
+#: file_ops.c:388
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "не удалось открыть каталог \"%s\": %m"
+
+#: file_ops.c:446
+#, c-format
+msgid "could not read symbolic link \"%s\": %m"
+msgstr "не удалось прочитать символическую ссылку \"%s\": %m"
+
+#: file_ops.c:449
+#, c-format
+msgid "symbolic link \"%s\" target is too long"
+msgstr "целевой путь символической ссылки \"%s\" слишком длинный"
+
+#: file_ops.c:464
+#, c-format
+msgid ""
+"\"%s\" is a symbolic link, but symbolic links are not supported on this "
+"platform"
+msgstr ""
+"\"%s\" — символическая ссылка, но в этой ОС символические ссылки не "
+"поддерживаются"
+
+#: file_ops.c:471
+#, c-format
+msgid "could not read directory \"%s\": %m"
+msgstr "не удалось прочитать каталог \"%s\": %m"
+
+#: file_ops.c:475
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "не удалось закрыть каталог \"%s\": %m"
+
+#: filemap.c:237
+#, c-format
+msgid "data file \"%s\" in source is not a regular file"
+msgstr "файл данных \"%s\" в источнике не является обычным файлом"
+
+#: filemap.c:242 filemap.c:275
+#, c-format
+msgid "duplicate source file \"%s\""
+msgstr "повторный исходный файл \"%s\""
+
+#: filemap.c:330
+#, c-format
+msgid "unexpected page modification for non-regular file \"%s\""
+msgstr "неожиданная модификация страницы для файла особого вида \"%s\""
+
+#: filemap.c:680 filemap.c:774
+#, c-format
+msgid "unknown file type for \"%s\""
+msgstr "неизвестный тип файла \"%s\""
+
+#: filemap.c:707
+#, c-format
+msgid "file \"%s\" is of different type in source and target"
+msgstr "файл \"%s\" имеет разный тип в исходном и целевом кластере"
+
+#: filemap.c:779
+#, c-format
+msgid "could not decide what to do with file \"%s\""
+msgstr "не удалось определить, что делать с файлом \"%s\""
+
+#: libpq_source.c:128
+#, c-format
+msgid "could not clear search_path: %s"
+msgstr "не удалось очистить search_path: %s"
+
+#: libpq_source.c:139
+#, c-format
+msgid "full_page_writes must be enabled in the source server"
+msgstr "на исходном сервере должен быть включён режим full_page_writes"
+
+#: libpq_source.c:150
+#, c-format
+msgid "could not prepare statement to fetch file contents: %s"
+msgstr "не удалось подготовить оператор для извлечения содержимого файла: %s"
+
+#: libpq_source.c:169
+#, c-format
+msgid "error running query (%s) on source server: %s"
+msgstr "ошибка выполнения запроса (%s) на исходном сервере: %s"
+
+#: libpq_source.c:174
+#, c-format
+msgid "unexpected result set from query"
+msgstr "неожиданный результат запроса"
+
+#: libpq_source.c:196
+#, c-format
+msgid "error running query (%s) in source server: %s"
+msgstr "ошибка выполнения запроса (%s) на исходном сервере: %s"
+
+#: libpq_source.c:217
+#, c-format
+msgid "unrecognized result \"%s\" for current WAL insert location"
+msgstr ""
+"нераспознанный результат \"%s\" вместо текущей позиции добавления в WAL"
+
+#: libpq_source.c:268
+#, c-format
+msgid "could not fetch file list: %s"
+msgstr "не удалось получить список файлов: %s"
+
+#: libpq_source.c:273
+#, c-format
+msgid "unexpected result set while fetching file list"
+msgstr "неожиданный результат при получении списка файлов"
+
+#: libpq_source.c:435
+#, c-format
+msgid "could not send query: %s"
+msgstr "не удалось отправить запрос: %s"
+
+#: libpq_source.c:438
+#, c-format
+msgid "could not set libpq connection to single row mode"
+msgstr "не удалось перевести подключение libpq в однострочный режим"
+
+#: libpq_source.c:468
+#, c-format
+msgid "unexpected result while fetching remote files: %s"
+msgstr "неожиданный результат при получении файлов с сервера: %s"
+
+#: libpq_source.c:473
+#, c-format
+msgid "received more data chunks than requested"
+msgstr "получено больше сегментов данных, чем запрошено"
+
+#: libpq_source.c:477
+#, c-format
+msgid "unexpected result set size while fetching remote files"
+msgstr "неожиданный размер набора результатов при получении файлов с сервера"
+
+#: libpq_source.c:483
+#, c-format
+msgid ""
+"unexpected data types in result set while fetching remote files: %u %u %u"
+msgstr ""
+"неожиданные типы данных в наборе результатов при получении файлов с сервера: "
+"%u %u %u"
+
+#: libpq_source.c:491
+#, c-format
+msgid "unexpected result format while fetching remote files"
+msgstr "неожиданный формат результата при получении файлов с сервера"
+
+#: libpq_source.c:497
+#, c-format
+msgid "unexpected null values in result while fetching remote files"
+msgstr "неожиданные значения NULL в результате при получении файлов с сервера"
+
+#: libpq_source.c:501
+#, c-format
+msgid "unexpected result length while fetching remote files"
+msgstr "неожиданная длина результата при получении файлов с сервера"
+
+#: libpq_source.c:534
+#, c-format
+msgid "received data for file \"%s\", when requested for \"%s\""
+msgstr "получены данные для файла \"%s\", а запрашивались данные для \"%s\""
+
+#: libpq_source.c:538
+#, c-format
+msgid ""
+"received data at offset %lld of file \"%s\", when requested for offset %lld"
+msgstr ""
+"получены данные по смещению %lld в файле \"%s\", а запрашивались по смещению "
+"%lld"
+
+#: libpq_source.c:550
+#, c-format
+msgid "received more than requested for file \"%s\""
+msgstr "получено больше данных, чем запрошено для файла \"%s\""
+
+#: libpq_source.c:563
+#, c-format
+msgid "unexpected number of data chunks received"
+msgstr "получено неожиданное количество сегментов данных"
+
+#: libpq_source.c:606
+#, c-format
+msgid "could not fetch remote file \"%s\": %s"
+msgstr "не удалось получить с сервера файл \"%s\": %s"
+
+#: libpq_source.c:611
+#, c-format
+msgid "unexpected result set while fetching remote file \"%s\""
+msgstr "неожиданный набор результатов при получении файла \"%s\" с сервера"
+
+#: local_source.c:86
+#, c-format
+msgid "could not open source file \"%s\": %m"
+msgstr "не удалось открыть исходный файл \"%s\": %m"
+
+#: local_source.c:90
+#, c-format
+msgid "could not seek in source file: %m"
+msgstr "не удалось переместиться в исходном файле: %m"
+
+#: local_source.c:109
+#, c-format
+msgid "unexpected EOF while reading file \"%s\""
+msgstr "неожиданный конец файла при чтении \"%s\""
+
+#: local_source.c:116
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "не удалось закрыть файл \"%s\": %m"
+
+#: parsexlog.c:89 parsexlog.c:144
+#, c-format
+msgid "could not read WAL record at %X/%X: %s"
+msgstr "не удалось прочитать запись WAL в позиции %X/%X: %s"
+
+#: parsexlog.c:93 parsexlog.c:147
+#, c-format
+msgid "could not read WAL record at %X/%X"
+msgstr "не удалось прочитать запись WAL в позиции %X/%X"
+
+#: parsexlog.c:106
+#, c-format
+msgid "end pointer %X/%X is not a valid end point; expected %X/%X"
+msgstr ""
+"конечный указатель %X/%X неверно задаёт конечную точку; ожидается %X/%X"
+
+#: parsexlog.c:210
+#, c-format
+msgid "could not find previous WAL record at %X/%X: %s"
+msgstr "не удалось найти предыдущую запись WAL в позиции %X/%X: %s"
+
+#: parsexlog.c:214
+#, c-format
+msgid "could not find previous WAL record at %X/%X"
+msgstr "не удалось найти предыдущую запись WAL в позиции %X/%X"
+
+#: parsexlog.c:339
+#, c-format
+msgid "could not seek in file \"%s\": %m"
+msgstr "не удалось переместиться в файле \"%s\": %m"
+
+#: parsexlog.c:431
+#, c-format
+msgid ""
+"WAL record modifies a relation, but record type is not recognized: lsn: %X/"
+"%X, rmgr: %s, info: %02X"
+msgstr ""
+"Запись WAL модифицирует отношение, но тип записи не распознан: lsn: %X/%X, "
+"rmgr: %s, info: %02X"
+
+#: pg_rewind.c:84
+#, c-format
+msgid ""
+"%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n"
+"\n"
+msgstr ""
+"%s синхронизирует кластер PostgreSQL с другой копией кластера.\n"
+"\n"
+
+#: pg_rewind.c:85
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]...\n"
+"\n"
+msgstr ""
+"Использование:\n"
+" %s [ПАРАМЕТР]...\n"
+"\n"
+
+#: pg_rewind.c:86
+#, c-format
+msgid "Options:\n"
+msgstr "Параметры:\n"
+
+#: pg_rewind.c:87
+#, c-format
+msgid ""
+" -c, --restore-target-wal use restore_command in target configuration "
+"to\n"
+" retrieve WAL files from archives\n"
+msgstr ""
+" -c, --restore-target-wal использовать для получения файлов WAL из\n"
+" архива команду restore_command из целевой\n"
+" конфигурации\n"
+
+#: pg_rewind.c:89
+#, c-format
+msgid " -D, --target-pgdata=DIRECTORY existing data directory to modify\n"
+msgstr ""
+" -D, --target-pgdata=КАТАЛОГ существующий каталог, куда будут записаны "
+"данные\n"
+
+#: pg_rewind.c:90
+#, c-format
+msgid ""
+" --source-pgdata=DIRECTORY source data directory to synchronize with\n"
+msgstr ""
+" --source-pgdata=КАТАЛОГ исходный каталог, с которым будет проведена "
+"синхронизация\n"
+
+# well-spelled: ПОДКЛ
+#: pg_rewind.c:91
+#, c-format
+msgid " --source-server=CONNSTR source server to synchronize with\n"
+msgstr ""
+" --source-server=СТР_ПОДКЛ сервер, с которым будет проведена "
+"синхронизация\n"
+
+#: pg_rewind.c:92
+#, c-format
+msgid " -n, --dry-run stop before modifying anything\n"
+msgstr ""
+" -n, --dry-run остановиться до внесения каких-либо "
+"изменений\n"
+
+#: pg_rewind.c:93
+#, c-format
+msgid ""
+" -N, --no-sync do not wait for changes to be written\n"
+" safely to disk\n"
+msgstr ""
+" -N, --no-sync не ждать завершения сохранения данных на "
+"диске\n"
+
+#: pg_rewind.c:95
+#, c-format
+msgid " -P, --progress write progress messages\n"
+msgstr " -P, --progress выводить сообщения о ходе процесса\n"
+
+#: pg_rewind.c:96
+#, c-format
+msgid ""
+" -R, --write-recovery-conf write configuration for replication\n"
+" (requires --source-server)\n"
+msgstr ""
+" -R, --write-recovery-conf записать конфигурацию для репликации\n"
+" (требуется указание --source-server)\n"
+
+#: pg_rewind.c:98
+#, c-format
+msgid " --debug write a lot of debug messages\n"
+msgstr ""
+" --debug выдавать множество отладочных сообщений\n"
+
+#: pg_rewind.c:99
+#, c-format
+msgid ""
+" --no-ensure-shutdown do not automatically fix unclean shutdown\n"
+msgstr ""
+" --no-ensure-shutdown не исправлять автоматически состояние,\n"
+" возникающее при нештатном отключении\n"
+
+#: pg_rewind.c:100
+#, c-format
+msgid ""
+" -V, --version output version information, then exit\n"
+msgstr " -V, --version показать версию и выйти\n"
+
+#: pg_rewind.c:101
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help показать эту справку и выйти\n"
+
+#: pg_rewind.c:102
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"Об ошибках сообщайте по адресу <%s>.\n"
+
+#: pg_rewind.c:103
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "Домашняя страница %s: <%s>\n"
+
+#: pg_rewind.c:164 pg_rewind.c:213 pg_rewind.c:220 pg_rewind.c:227
+#: pg_rewind.c:234 pg_rewind.c:242
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "Для дополнительной информации попробуйте \"%s --help\".\n"
+
+#: pg_rewind.c:212
+#, c-format
+msgid "no source specified (--source-pgdata or --source-server)"
+msgstr "источник не указан (требуется --source-pgdata или --source-server)"
+
+#: pg_rewind.c:219
+#, c-format
+msgid "only one of --source-pgdata or --source-server can be specified"
+msgstr "указать можно только --source-pgdata либо --source-server"
+
+#: pg_rewind.c:226
+#, c-format
+msgid "no target data directory specified (--target-pgdata)"
+msgstr "целевой каталог данных не указан (--target-pgdata)"
+
+#: pg_rewind.c:233
+#, c-format
+msgid ""
+"no source server information (--source-server) specified for --write-"
+"recovery-conf"
+msgstr ""
+"отсутствует информация об исходном сервере (--source-server) для --write-"
+"recovery-conf"
+
+#: pg_rewind.c:240
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "слишком много аргументов командной строки (первый: \"%s\")"
+
+#: pg_rewind.c:255
+#, c-format
+msgid "cannot be executed by \"root\""
+msgstr "программу не должен запускать root"
+
+#: pg_rewind.c:256
+#, c-format
+msgid "You must run %s as the PostgreSQL superuser.\n"
+msgstr "Запускать %s нужно от имени суперпользователя PostgreSQL.\n"
+
+#: pg_rewind.c:267
+#, c-format
+msgid "could not read permissions of directory \"%s\": %m"
+msgstr "не удалось считать права на каталог \"%s\": %m"
+
+#: pg_rewind.c:287
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: pg_rewind.c:290
+#, c-format
+msgid "connected to server"
+msgstr "подключение к серверу установлено"
+
+#: pg_rewind.c:337
+#, c-format
+msgid "source and target cluster are on the same timeline"
+msgstr "исходный и целевой кластер уже на одной линии времени"
+
+#: pg_rewind.c:346
+#, c-format
+msgid "servers diverged at WAL location %X/%X on timeline %u"
+msgstr "серверы разошлись в позиции WAL %X/%X на линии времени %u"
+
+#: pg_rewind.c:394
+#, c-format
+msgid "no rewind required"
+msgstr "перемотка не требуется"
+
+#: pg_rewind.c:403
+#, c-format
+msgid "rewinding from last common checkpoint at %X/%X on timeline %u"
+msgstr ""
+"перемотка от последней общей контрольной точки в позиции %X/%X на линии "
+"времени %u"
+
+#: pg_rewind.c:413
+#, c-format
+msgid "reading source file list"
+msgstr "чтение списка исходных файлов"
+
+#: pg_rewind.c:417
+#, c-format
+msgid "reading target file list"
+msgstr "чтение списка целевых файлов"
+
+#: pg_rewind.c:426
+#, c-format
+msgid "reading WAL in target"
+msgstr "чтение WAL в целевом кластере"
+
+#: pg_rewind.c:447
+#, c-format
+msgid "need to copy %lu MB (total source directory size is %lu MB)"
+msgstr "требуется скопировать %lu МБ (общий размер исходного каталога: %lu МБ)"
+
+#: pg_rewind.c:465
+#, c-format
+msgid "syncing target data directory"
+msgstr "синхронизация целевого каталога данных"
+
+#: pg_rewind.c:481
+#, c-format
+msgid "Done!"
+msgstr "Готово!"
+
+#: pg_rewind.c:564
+#, c-format
+msgid "no action decided for file \"%s\""
+msgstr "действие не определено для файла \"%s\""
+
+#: pg_rewind.c:596
+#, c-format
+msgid "source system was modified while pg_rewind was running"
+msgstr "в исходной системе произошли изменения в процессе работы pg_rewind"
+
+#: pg_rewind.c:600
+#, c-format
+msgid "creating backup label and updating control file"
+msgstr "создание метки копии и модификация управляющего файла"
+
+#: pg_rewind.c:650
+#, c-format
+msgid "source system was in unexpected state at end of rewind"
+msgstr "исходная система оказалась в неожиданном состоянии после перемотки"
+
+#: pg_rewind.c:681
+#, c-format
+msgid "source and target clusters are from different systems"
+msgstr "исходный и целевой кластеры относятся к разным системам"
+
+#: pg_rewind.c:689
+#, c-format
+msgid "clusters are not compatible with this version of pg_rewind"
+msgstr "кластеры несовместимы с этой версией pg_rewind"
+
+#: pg_rewind.c:699
+#, c-format
+msgid ""
+"target server needs to use either data checksums or \"wal_log_hints = on\""
+msgstr ""
+"на целевом сервере должны быть контрольные суммы данных или \"wal_log_hints "
+"= on\""
+
+#: pg_rewind.c:710
+#, c-format
+msgid "target server must be shut down cleanly"
+msgstr "целевой сервер должен быть выключен штатно"
+
+#: pg_rewind.c:720
+#, c-format
+msgid "source data directory must be shut down cleanly"
+msgstr "работа с исходным каталогом данных должна быть завершена штатно"
+
+#: pg_rewind.c:772
+#, c-format
+msgid "%*s/%s kB (%d%%) copied"
+msgstr "%*s/%s КБ (%d%%) скопировано"
+
+#: pg_rewind.c:835
+#, c-format
+msgid "invalid control file"
+msgstr "неверный управляющий файл"
+
+#: pg_rewind.c:919
+#, c-format
+msgid ""
+"could not find common ancestor of the source and target cluster's timelines"
+msgstr ""
+"не удалось найти общего предка линий времени исходного и целевого кластеров"
+
+#: pg_rewind.c:960
+#, c-format
+msgid "backup label buffer too small"
+msgstr "буфер для метки копии слишком мал"
+
+#: pg_rewind.c:983
+#, c-format
+msgid "unexpected control file CRC"
+msgstr "неверная контрольная сумма управляющего файла"
+
+#: pg_rewind.c:995
+#, c-format
+msgid "unexpected control file size %d, expected %d"
+msgstr "неверный размер управляющего файла (%d), ожидалось: %d"
+
+#: pg_rewind.c:1004
+#, c-format
+msgid ""
+"WAL segment size must be a power of two between 1 MB and 1 GB, but the "
+"control file specifies %d byte"
+msgid_plural ""
+"WAL segment size must be a power of two between 1 MB and 1 GB, but the "
+"control file specifies %d bytes"
+msgstr[0] ""
+"размер сегмента WAL должен задаваться степенью 2 в интервале от 1 МБ до 1 "
+"ГБ, но в управляющем файле указано значение: %d"
+msgstr[1] ""
+"Размер сегмента WAL должен задаваться степенью 2 в интервале от 1 МБ до 1 "
+"ГБ, но в управляющем файле указано значение: %d"
+msgstr[2] ""
+"Размер сегмента WAL должен задаваться степенью 2 в интервале от 1 МБ до 1 "
+"ГБ, но в управляющем файле указано значение: %d"
+
+#: pg_rewind.c:1043 pg_rewind.c:1101
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"Программа \"%s\" нужна для %s, но она не найдена\n"
+"в каталоге \"%s\".\n"
+"Проверьте правильность установки СУБД."
+
+#: pg_rewind.c:1048 pg_rewind.c:1106
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"Программа \"%s\" найдена программой \"%s\",\n"
+"но её версия отличается от версии %s.\n"
+"Проверьте правильность установки СУБД."
+
+#: pg_rewind.c:1069
+#, c-format
+msgid "restore_command is not set in the target cluster"
+msgstr "команда restore_command в целевом кластере не определена"
+
+#: pg_rewind.c:1112
+#, c-format
+msgid "executing \"%s\" for target server to complete crash recovery"
+msgstr ""
+"выполнение \"%s\" для восстановления согласованности на целевом сервере"
+
+#: pg_rewind.c:1132
+#, c-format
+msgid "postgres single-user mode in target cluster failed"
+msgstr ""
+"не удалось запустить postgres в целевом кластере в однопользовательском "
+"режиме"
+
+#: pg_rewind.c:1133
+#, c-format
+msgid "Command was: %s"
+msgstr "Выполнялась команда: %s"
+
+#: timeline.c:75 timeline.c:81
+#, c-format
+msgid "syntax error in history file: %s"
+msgstr "синтаксическая ошибка в файле истории: %s"
+
+#: timeline.c:76
+#, c-format
+msgid "Expected a numeric timeline ID."
+msgstr "Ожидается числовой идентификатор линии времени."
+
+#: timeline.c:82
+#, c-format
+msgid "Expected a write-ahead log switchpoint location."
+msgstr "Ожидается положение точки переключения журнала предзаписи."
+
+#: timeline.c:87
+#, c-format
+msgid "invalid data in history file: %s"
+msgstr "неверные данные в файле истории: %s"
+
+#: timeline.c:88
+#, c-format
+msgid "Timeline IDs must be in increasing sequence."
+msgstr "Идентификаторы линий времени должны возрастать."
+
+#: timeline.c:108
+#, c-format
+msgid "invalid data in history file"
+msgstr "неверные данные в файле истории"
+
+#: timeline.c:109
+#, c-format
+msgid "Timeline IDs must be less than child timeline's ID."
+msgstr ""
+"Идентификаторы линий времени должны быть меньше идентификатора линии-потомка."
+
+#: xlogreader.c:354
+#, c-format
+msgid "invalid record offset at %X/%X"
+msgstr "неверное смещение записи: %X/%X"
+
+#: xlogreader.c:362
+#, c-format
+msgid "contrecord is requested by %X/%X"
+msgstr "по смещению %X/%X запрошено продолжение записи"
+
+#: xlogreader.c:403 xlogreader.c:733
+#, c-format
+msgid "invalid record length at %X/%X: wanted %u, got %u"
+msgstr "неверная длина записи по смещению %X/%X: ожидалось %u, получено %u"
+
+#: xlogreader.c:429
+#, c-format
+msgid "record length %u at %X/%X too long"
+msgstr "длина записи %u по смещению %X/%X слишком велика"
+
+#: xlogreader.c:477
+#, c-format
+msgid "there is no contrecord flag at %X/%X"
+msgstr "нет флага contrecord в позиции %X/%X"
+
+#: xlogreader.c:490
+#, c-format
+msgid "invalid contrecord length %u (expected %lld) at %X/%X"
+msgstr "неверная длина contrecord: %u (ожидалось %lld) в позиции %X/%X"
+
+#: xlogreader.c:741
+#, c-format
+msgid "invalid resource manager ID %u at %X/%X"
+msgstr "неверный ID менеджера ресурсов %u по смещению %X/%X"
+
+#: xlogreader.c:754 xlogreader.c:770
+#, c-format
+msgid "record with incorrect prev-link %X/%X at %X/%X"
+msgstr "запись с неверной ссылкой назад %X/%X по смещению %X/%X"
+
+#: xlogreader.c:806
+#, c-format
+msgid "incorrect resource manager data checksum in record at %X/%X"
+msgstr ""
+"некорректная контрольная сумма данных менеджера ресурсов в записи по "
+"смещению %X/%X"
+
+#: xlogreader.c:843
+#, c-format
+msgid "invalid magic number %04X in log segment %s, offset %u"
+msgstr "неверное магическое число %04X в сегменте журнала %s, смещение %u"
+
+#: xlogreader.c:857 xlogreader.c:898
+#, c-format
+msgid "invalid info bits %04X in log segment %s, offset %u"
+msgstr "неверные информационные биты %04X в сегменте журнала %s, смещение %u"
+
+#: xlogreader.c:872
+#, c-format
+msgid ""
+"WAL file is from different database system: WAL file database system "
+"identifier is %llu, pg_control database system identifier is %llu"
+msgstr ""
+"файл WAL принадлежит другой СУБД: в нём указан идентификатор системы БД "
+"%llu, а идентификатор системы pg_control: %llu"
+
+#: xlogreader.c:880
+#, c-format
+msgid ""
+"WAL file is from different database system: incorrect segment size in page "
+"header"
+msgstr ""
+"файл WAL принадлежит другой СУБД: некорректный размер сегмента в заголовке "
+"страницы"
+
+#: xlogreader.c:886
+#, c-format
+msgid ""
+"WAL file is from different database system: incorrect XLOG_BLCKSZ in page "
+"header"
+msgstr ""
+"файл WAL принадлежит другой СУБД: некорректный XLOG_BLCKSZ в заголовке "
+"страницы"
+
+#: xlogreader.c:917
+#, c-format
+msgid "unexpected pageaddr %X/%X in log segment %s, offset %u"
+msgstr "неожиданный pageaddr %X/%X в сегменте журнала %s, смещение %u"
+
+#: xlogreader.c:942
+#, c-format
+msgid "out-of-sequence timeline ID %u (after %u) in log segment %s, offset %u"
+msgstr ""
+"нарушение последовательности ID линии времени %u (после %u) в сегменте "
+"журнала %s, смещение %u"
+
+#: xlogreader.c:1287
+#, c-format
+msgid "out-of-order block_id %u at %X/%X"
+msgstr "идентификатор блока %u идёт не по порядку в позиции %X/%X"
+
+#: xlogreader.c:1309
+#, c-format
+msgid "BKPBLOCK_HAS_DATA set, but no data included at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA установлен, но данных в позиции %X/%X нет"
+
+#: xlogreader.c:1316
+#, c-format
+msgid "BKPBLOCK_HAS_DATA not set, but data length is %u at %X/%X"
+msgstr ""
+"BKPBLOCK_HAS_DATA не установлен, но длина данных равна %u в позиции %X/%X"
+
+#: xlogreader.c:1352
+#, c-format
+msgid ""
+"BKPIMAGE_HAS_HOLE set, but hole offset %u length %u block image length %u at "
+"%X/%X"
+msgstr ""
+"BKPIMAGE_HAS_HOLE установлен, но для пропуска заданы смещение %u и длина %u "
+"при длине образа блока %u в позиции %X/%X"
+
+#: xlogreader.c:1368
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE not set, but hole offset %u length %u at %X/%X"
+msgstr ""
+"BKPIMAGE_HAS_HOLE не установлен, но для пропуска заданы смещение %u и длина "
+"%u в позиции %X/%X"
+
+#: xlogreader.c:1383
+#, c-format
+msgid "BKPIMAGE_IS_COMPRESSED set, but block image length %u at %X/%X"
+msgstr ""
+"BKPIMAGE_IS_COMPRESSED установлен, но длина образа блока равна %u в позиции "
+"%X/%X"
+
+#: xlogreader.c:1398
+#, c-format
+msgid ""
+"neither BKPIMAGE_HAS_HOLE nor BKPIMAGE_IS_COMPRESSED set, but block image "
+"length is %u at %X/%X"
+msgstr ""
+"ни BKPIMAGE_HAS_HOLE, ни BKPIMAGE_IS_COMPRESSED не установлены, но длина "
+"образа блока равна %u в позиции %X/%X"
+
+#: xlogreader.c:1414
+#, c-format
+msgid "BKPBLOCK_SAME_REL set but no previous rel at %X/%X"
+msgstr ""
+"BKPBLOCK_SAME_REL установлен, но предыдущее значение не задано в позиции %X/"
+"%X"
+
+#: xlogreader.c:1426
+#, c-format
+msgid "invalid block_id %u at %X/%X"
+msgstr "неверный идентификатор блока %u в позиции %X/%X"
+
+#: xlogreader.c:1513
+#, c-format
+msgid "record with invalid length at %X/%X"
+msgstr "запись с неверной длиной в позиции %X/%X"
+
+#: xlogreader.c:1602
+#, c-format
+msgid "invalid compressed image at %X/%X, block %d"
+msgstr "неверный сжатый образ в позиции %X/%X, блок %d"
+
+#~ msgid "\"%s\" is not a directory"
+#~ msgstr "\"%s\" не является каталогом"
+
+#~ msgid "\"%s\" is not a symbolic link"
+#~ msgstr "\"%s\" не является символической ссылкой"
+
+#~ msgid "\"%s\" is not a regular file"
+#~ msgstr "\"%s\" не является обычным файлом"
+
+#~ msgid "source file list is empty"
+#~ msgstr "список файлов в источнике пуст"
+
+#~ msgid "source server must not be in recovery mode"
+#~ msgstr "исходный сервер должен выйти из режима восстановления"
+
+#~ msgid "could not send COPY data: %s"
+#~ msgstr "не удалось отправить данные COPY: %s"
+
+#~ msgid "could not send file list: %s"
+#~ msgstr "не удалось отправить список файлов: %s"
+
+#~ msgid "could not send end-of-COPY: %s"
+#~ msgstr "не удалось отправить сообщение о завершении копирования: %s"
+
+#~ msgid "unexpected result while sending file list: %s"
+#~ msgstr "неожиданный результат при передаче списка: %s"
+
+#~ msgid "could not connect to server: %s"
+#~ msgstr "не удалось подключиться к серверу: %s"
+
+#~ msgid ""
+#~ "\n"
+#~ "Report bugs to <pgsql-bugs@lists.postgresql.org>.\n"
+#~ msgstr ""
+#~ "\n"
+#~ "Об ошибках сообщайте по адресу <pgsql-bugs@lists.postgresql.org>.\n"
+
+#~ msgid " block %u\n"
+#~ msgstr " блок %u\n"
+
+#~ msgid "entry \"%s\" excluded from source file list\n"
+#~ msgstr "\"%s\" исключён из списка исходных файлов\n"
+
+#~ msgid "entry \"%s\" excluded from target file list\n"
+#~ msgstr "\"%s\" исключён из списка целевых файлов\n"
+
+#~ msgid "%s (%s)\n"
+#~ msgstr "%s (%s)\n"
+
+#, fuzzy
+#~ msgid "could not set up connection context: %s"
+#~ msgstr "не удалось настроить контекст подключения: %s"
+
+#~ msgid "getting file chunks\n"
+#~ msgstr "получение сегментов файлов\n"
+
+#~ msgid ""
+#~ "received null value for chunk for file \"%s\", file has been deleted\n"
+#~ msgstr ""
+#~ "для файла \"%s\" вместо сегмента получено NULL-значение, файл удалён\n"
+
+#~ msgid "fetched file \"%s\", length %d\n"
+#~ msgstr "получен файл \"%s\", длина %d\n"
+
+#, fuzzy
+#~ msgid "could not create temporary table: %s"
+#~ msgstr "не удалось создать временную таблицу: %s"
+
+#~ msgid "Failure, exiting\n"
+#~ msgstr "Ошибка, выполняется выход\n"
+
+#~ msgid "could not read from file \"%s\": %s\n"
+#~ msgstr "не удалось прочитать файл \"%s\": %s\n"
+
+#~ msgid "Source timeline history:\n"
+#~ msgstr "История линии времени источника:\n"
+
+#~ msgid "Target timeline history:\n"
+#~ msgstr "История линии времени получателя:\n"
+
+#~ msgid "%d: %X/%X - %X/%X\n"
+#~ msgstr "%d: %X/%X - %X/%X\n"
+
+#~ msgid "sync of target directory failed\n"
+#~ msgstr "сбой синхронизации целевого каталога\n"
+
+#~ msgid ""
+#~ "WAL file is from different database system: incorrect XLOG_SEG_SIZE in "
+#~ "page header"
+#~ msgstr ""
+#~ "файл WAL принадлежит другой СУБД: некорректный XLOG_SEG_SIZE в заголовке "
+#~ "страницы"
diff --git a/src/bin/pg_rewind/po/sv.po b/src/bin/pg_rewind/po/sv.po
new file mode 100644
index 0000000..529ec2c
--- /dev/null
+++ b/src/bin/pg_rewind/po/sv.po
@@ -0,0 +1,964 @@
+# Swedish message translation file for pg_rewind
+# Copyright (C) 2017 PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+# Dennis Björklund <db@zigo.dhs.org>, 2017, 2018, 2019, 2020, 2021, 2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PostgreSQL 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-04-06 21:19+0000\n"
+"PO-Revision-Date: 2022-04-11 14:02+0200\n"
+"Last-Translator: Dennis Björklund <db@zigo.dhs.org>\n"
+"Language-Team: Swedish <pgsql-translators@postgresql.org>\n"
+"Language: sv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "fatalt: "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "fel: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "varning: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "slut på minne\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "kan inte duplicera null-pekare (internt fel)\n"
+
+#: ../../common/restricted_token.c:64
+#, c-format
+msgid "could not load library \"%s\": error code %lu"
+msgstr "kunde inte ladda länkbibliotek \"%s\": felkod %lu"
+
+#: ../../common/restricted_token.c:73
+#, c-format
+msgid "cannot create restricted tokens on this platform: error code %lu"
+msgstr "kan inte skapa token för begränsad åtkomst på denna plattorm: felkod %lu"
+
+#: ../../common/restricted_token.c:82
+#, c-format
+msgid "could not open process token: error code %lu"
+msgstr "kunde inte öppna process-token: felkod %lu"
+
+#: ../../common/restricted_token.c:97
+#, c-format
+msgid "could not allocate SIDs: error code %lu"
+msgstr "kunde inte allokera SID: felkod %lu"
+
+#: ../../common/restricted_token.c:119
+#, c-format
+msgid "could not create restricted token: error code %lu"
+msgstr "kunde inte skapa token för begränsad åtkomst: felkod %lu"
+
+#: ../../common/restricted_token.c:140
+#, c-format
+msgid "could not start process for command \"%s\": error code %lu"
+msgstr "kunde inte starta process för kommando \"%s\": felkod %lu"
+
+#: ../../common/restricted_token.c:178
+#, c-format
+msgid "could not re-execute with restricted token: error code %lu"
+msgstr "kunde inte köra igen med token för begränsad åtkomst: felkod %lu"
+
+#: ../../common/restricted_token.c:194
+#, c-format
+msgid "could not get exit code from subprocess: error code %lu"
+msgstr "kunde inte hämta statuskod för underprocess: felkod %lu"
+
+#: ../../fe_utils/archive.c:53
+#, c-format
+msgid "cannot use restore_command with %%r placeholder"
+msgstr "kan inte använda restore_command med %%r-platshållare"
+
+#: ../../fe_utils/archive.c:74
+#, c-format
+msgid "unexpected file size for \"%s\": %lld instead of %lld"
+msgstr "oväntad filstorlek på \"%s\": %lld istället för %lld"
+
+#: ../../fe_utils/archive.c:85
+#, c-format
+msgid "could not open file \"%s\" restored from archive: %m"
+msgstr "kunde inte öppna fil \"%s\" återställd från arkiv: %m"
+
+#: ../../fe_utils/archive.c:97 file_ops.c:417
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "kunde inte göra stat() på fil \"%s\": %m"
+
+#: ../../fe_utils/archive.c:112
+#, c-format
+msgid "restore_command failed: %s"
+msgstr "restore_command misslyckades: %s"
+
+#: ../../fe_utils/archive.c:121
+#, c-format
+msgid "could not restore file \"%s\" from archive"
+msgstr "kunde inte återställa fil \"%s\" från arkiv"
+
+#: ../../fe_utils/recovery_gen.c:35 ../../fe_utils/recovery_gen.c:49
+#: ../../fe_utils/recovery_gen.c:77 ../../fe_utils/recovery_gen.c:100
+#: ../../fe_utils/recovery_gen.c:171 parsexlog.c:77 parsexlog.c:137
+#: parsexlog.c:197
+#, c-format
+msgid "out of memory"
+msgstr "slut på minne"
+
+#: ../../fe_utils/recovery_gen.c:134 parsexlog.c:310
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "kunde inte öppna fil \"%s\": %m"
+
+#: ../../fe_utils/recovery_gen.c:140
+#, c-format
+msgid "could not write to file \"%s\": %m"
+msgstr "kunde inte skriva till fil \"%s\": %m"
+
+#: ../../fe_utils/recovery_gen.c:152
+#, c-format
+msgid "could not create file \"%s\": %m"
+msgstr "kan inte skapa fil \"%s\": %m"
+
+#: file_ops.c:67
+#, c-format
+msgid "could not open target file \"%s\": %m"
+msgstr "kunde inte öppna målfil \"%s\": %m"
+
+#: file_ops.c:81
+#, c-format
+msgid "could not close target file \"%s\": %m"
+msgstr "kunde inte stänga målfil \"%s\": %m"
+
+#: file_ops.c:101
+#, c-format
+msgid "could not seek in target file \"%s\": %m"
+msgstr "kunde inte söka i målfil \"%s\": %m"
+
+#: file_ops.c:117
+#, c-format
+msgid "could not write file \"%s\": %m"
+msgstr "kunde inte skriva fil \"%s\": %m"
+
+#: file_ops.c:150 file_ops.c:177
+#, c-format
+msgid "undefined file type for \"%s\""
+msgstr "odefinierad filtyp på \"%s\""
+
+#: file_ops.c:173
+#, c-format
+msgid "invalid action (CREATE) for regular file"
+msgstr "ogiltig aktion (CREATE) för vanlig fil"
+
+#: file_ops.c:200
+#, c-format
+msgid "could not remove file \"%s\": %m"
+msgstr "kunde inte ta bort fil \"%s\": %m"
+
+#: file_ops.c:218
+#, c-format
+msgid "could not open file \"%s\" for truncation: %m"
+msgstr "kunde inte öppna fil \"%s\" för trunkering: %m"
+
+#: file_ops.c:222
+#, c-format
+msgid "could not truncate file \"%s\" to %u: %m"
+msgstr "kunde inte trunkera fil \"%s\" till %u: %m"
+
+#: file_ops.c:238
+#, c-format
+msgid "could not create directory \"%s\": %m"
+msgstr "kunde inte skapa katalog \"%s\": %m"
+
+#: file_ops.c:252
+#, c-format
+msgid "could not remove directory \"%s\": %m"
+msgstr "kunde inte ta bort katalog \"%s\": %m"
+
+#: file_ops.c:266
+#, c-format
+msgid "could not create symbolic link at \"%s\": %m"
+msgstr "kunde inte skapa en symnbolisk länk vid \"%s\": %m"
+
+#: file_ops.c:280
+#, c-format
+msgid "could not remove symbolic link \"%s\": %m"
+msgstr "kan inte ta bort symbolisk länk \"%s\": %m"
+
+#: file_ops.c:326 file_ops.c:330
+#, c-format
+msgid "could not open file \"%s\" for reading: %m"
+msgstr "kunde inte öppna filen \"%s\" för läsning: %m"
+
+#: file_ops.c:341 local_source.c:107 parsexlog.c:348
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "kunde inte läsa fil \"%s\": %m"
+
+#: file_ops.c:344 parsexlog.c:350
+#, c-format
+msgid "could not read file \"%s\": read %d of %zu"
+msgstr "kunde inte läsa fil \"%s\": läste %d av %zu"
+
+#: file_ops.c:388
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "kunde inte öppna katalog \"%s\": %m"
+
+#: file_ops.c:446
+#, c-format
+msgid "could not read symbolic link \"%s\": %m"
+msgstr "kan inte läsa symbolisk länk \"%s\": %m"
+
+#: file_ops.c:449
+#, c-format
+msgid "symbolic link \"%s\" target is too long"
+msgstr "mål för symbolisk länk \"%s\" är för lång"
+
+#: file_ops.c:464
+#, c-format
+msgid "\"%s\" is a symbolic link, but symbolic links are not supported on this platform"
+msgstr "\"%s\" är en symbolisk länk men symboliska länkar stöds inte på denna plattform"
+
+#: file_ops.c:471
+#, c-format
+msgid "could not read directory \"%s\": %m"
+msgstr "kunde inte läsa katalog \"%s\": %m"
+
+#: file_ops.c:475
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "kunde inte stänga katalog \"%s\": %m"
+
+#: filemap.c:237
+#, c-format
+msgid "data file \"%s\" in source is not a regular file"
+msgstr "datafil \"%s\" i källan är inte en vanlig fil"
+
+#: filemap.c:242 filemap.c:275
+#, c-format
+msgid "duplicate source file \"%s\""
+msgstr "duplicerad källflagga \"%s\""
+
+#: filemap.c:330
+#, c-format
+msgid "unexpected page modification for non-regular file \"%s\""
+msgstr "oväntad sidmodifiering för icke-regulär fil \"%s\""
+
+#: filemap.c:680 filemap.c:774
+#, c-format
+msgid "unknown file type for \"%s\""
+msgstr "okänd filtyp på \"%s\""
+
+#: filemap.c:707
+#, c-format
+msgid "file \"%s\" is of different type in source and target"
+msgstr "filen \"%s\" har olika typ i källa och mål"
+
+#: filemap.c:779
+#, c-format
+msgid "could not decide what to do with file \"%s\""
+msgstr "kunde inte bestämma vad som skulle göras med filen \"%s\""
+
+#: libpq_source.c:128
+#, c-format
+msgid "could not clear search_path: %s"
+msgstr "kunde inte nollställa search_path: %s"
+
+#: libpq_source.c:139
+#, c-format
+msgid "full_page_writes must be enabled in the source server"
+msgstr "full_page_writes måste vara påslagen i källservern"
+
+#: libpq_source.c:150
+#, c-format
+msgid "could not prepare statement to fetch file contents: %s"
+msgstr "kunde inte förbereda satsen för att hämta filinnehåll: %s"
+
+#: libpq_source.c:169
+#, c-format
+msgid "error running query (%s) on source server: %s"
+msgstr "fel vid körande av fråga (%s) på källserver: %s"
+
+#: libpq_source.c:174
+#, c-format
+msgid "unexpected result set from query"
+msgstr "oväntad resultatmängd från fråga"
+
+#: libpq_source.c:196
+#, c-format
+msgid "error running query (%s) in source server: %s"
+msgstr "fel vid körande av fråga (%s) i källserver: %s"
+
+#: libpq_source.c:217
+#, c-format
+msgid "unrecognized result \"%s\" for current WAL insert location"
+msgstr "oväntat resultat \"%s\" för nuvarande WAL-insättningsposition"
+
+#: libpq_source.c:268
+#, c-format
+msgid "could not fetch file list: %s"
+msgstr "kunde inte hämta fillista: %s"
+
+#: libpq_source.c:273
+#, c-format
+msgid "unexpected result set while fetching file list"
+msgstr "oväntad resultatmängd vid hämtning av fillista"
+
+#: libpq_source.c:435
+#, c-format
+msgid "could not send query: %s"
+msgstr "kunde inte skicka fråga: %s"
+
+#: libpq_source.c:438
+#, c-format
+msgid "could not set libpq connection to single row mode"
+msgstr "kunde inte sätta libpq-anslutning till enradsläge"
+
+#: libpq_source.c:468
+#, c-format
+msgid "unexpected result while fetching remote files: %s"
+msgstr "oväntat resultat vid hämtning av extern fil: %s"
+
+#: libpq_source.c:473
+#, c-format
+msgid "received more data chunks than requested"
+msgstr "tog emot fler datastycken än efterfrågat"
+
+#: libpq_source.c:477
+#, c-format
+msgid "unexpected result set size while fetching remote files"
+msgstr "oväntad resultatmängdstorlek vid hämtning av externa filer"
+
+#: libpq_source.c:483
+#, c-format
+msgid "unexpected data types in result set while fetching remote files: %u %u %u"
+msgstr "oväntade datayper i resultatmängd vid hämtning av externa filer: %u %u %u"
+
+#: libpq_source.c:491
+#, c-format
+msgid "unexpected result format while fetching remote files"
+msgstr "oväntat resultatformat vid hämtning av externa filer"
+
+#: libpq_source.c:497
+#, c-format
+msgid "unexpected null values in result while fetching remote files"
+msgstr "oväntade null-värden i resultat vid hämtning av externa filer"
+
+#: libpq_source.c:501
+#, c-format
+msgid "unexpected result length while fetching remote files"
+msgstr "oväntad resultatlängd vid hämtning av externa filer"
+
+#: libpq_source.c:534
+#, c-format
+msgid "received data for file \"%s\", when requested for \"%s\""
+msgstr "fick data för filen \"%s\", men efterfrågade för \"%s\""
+
+#: libpq_source.c:538
+#, c-format
+msgid "received data at offset %lld of file \"%s\", when requested for offset %lld"
+msgstr "fick data från offset %lld i fil \"%s\", men efterfrågade offset %lld"
+
+#: libpq_source.c:550
+#, c-format
+msgid "received more than requested for file \"%s\""
+msgstr "tog emot mer än efterfrågat för filen \"%s\""
+
+#: libpq_source.c:563
+#, c-format
+msgid "unexpected number of data chunks received"
+msgstr "oväntat antal datastycken togs emot"
+
+#: libpq_source.c:606
+#, c-format
+msgid "could not fetch remote file \"%s\": %s"
+msgstr "kunde inte hämta extern fil \"%s\": %s"
+
+#: libpq_source.c:611
+#, c-format
+msgid "unexpected result set while fetching remote file \"%s\""
+msgstr "oväntat resultatmängd vid hämtning av extern fil \"%s\""
+
+#: local_source.c:86
+#, c-format
+msgid "could not open source file \"%s\": %m"
+msgstr "kunde inte öppna källfil \"%s\": %m"
+
+#: local_source.c:90
+#, c-format
+msgid "could not seek in source file: %m"
+msgstr "kunde inte söka i källfil: %m"
+
+#: local_source.c:109
+#, c-format
+msgid "unexpected EOF while reading file \"%s\""
+msgstr "oväntad EOF under läsning av fil \"%s\""
+
+#: local_source.c:116
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "kunde inte stänga fil \"%s\": %m"
+
+#: parsexlog.c:89 parsexlog.c:144
+#, c-format
+msgid "could not read WAL record at %X/%X: %s"
+msgstr "kunde inte läsa WAL-post vid %X/%X: %s"
+
+#: parsexlog.c:93 parsexlog.c:147
+#, c-format
+msgid "could not read WAL record at %X/%X"
+msgstr "kunde inte läsa WAL-post vid %X/%X"
+
+#: parsexlog.c:106
+#, c-format
+msgid "end pointer %X/%X is not a valid end point; expected %X/%X"
+msgstr "slutpekare %X/%X är inte en giltig slutposition; förväntade %X/%X"
+
+#: parsexlog.c:210
+#, c-format
+msgid "could not find previous WAL record at %X/%X: %s"
+msgstr "kunde inte hitta föregående WAL-post vid %X/%X: %s"
+
+#: parsexlog.c:214
+#, c-format
+msgid "could not find previous WAL record at %X/%X"
+msgstr "kunde inte hitta förgående WAL-post vid %X/%X"
+
+#: parsexlog.c:339
+#, c-format
+msgid "could not seek in file \"%s\": %m"
+msgstr "kunde inte söka (seek) i fil \"%s\": %m"
+
+#: parsexlog.c:431
+#, c-format
+msgid "WAL record modifies a relation, but record type is not recognized: lsn: %X/%X, rmgr: %s, info: %02X"
+msgstr "WAL-post modifierar en relation, men posttypen känns inte igen: lsn: %X/%X, rmgr: %s, info: %02X"
+
+#: pg_rewind.c:84
+#, c-format
+msgid ""
+"%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n"
+"\n"
+msgstr ""
+"%s resynkroniserar ett PostgreSQL-kluster med en annan kopia av klustret.\n"
+"\n"
+
+#: pg_rewind.c:85
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]...\n"
+"\n"
+msgstr ""
+"Användning:\n"
+" %s [FLAGGA]...\n"
+"\n"
+
+#: pg_rewind.c:86
+#, c-format
+msgid "Options:\n"
+msgstr "Flaggor:\n"
+
+#: pg_rewind.c:87
+#, c-format
+msgid ""
+" -c, --restore-target-wal use restore_command in target configuration to\n"
+" retrieve WAL files from archives\n"
+msgstr ""
+" -c, --restore-target-wal använd restore_command i målkonfigurationen\n"
+" för att hämta WAL-filer från arkiv\n"
+
+#: pg_rewind.c:89
+#, c-format
+msgid " -D, --target-pgdata=DIRECTORY existing data directory to modify\n"
+msgstr " -D, --target-pgdata=KATALOG existerande datakatalog att modifiera\n"
+
+#: pg_rewind.c:90
+#, c-format
+msgid " --source-pgdata=DIRECTORY source data directory to synchronize with\n"
+msgstr " --source-pgdata=KATALOG källdatakatalog att synkronisera med\n"
+
+#: pg_rewind.c:91
+#, c-format
+msgid " --source-server=CONNSTR source server to synchronize with\n"
+msgstr " --source-server=ANSLSTR källserver att synkronisera med\n"
+
+#: pg_rewind.c:92
+#, c-format
+msgid " -n, --dry-run stop before modifying anything\n"
+msgstr " -n, --dry-run stoppa innan något modifieras\n"
+
+#: pg_rewind.c:93
+#, c-format
+msgid ""
+" -N, --no-sync do not wait for changes to be written\n"
+" safely to disk\n"
+msgstr ""
+" -N, --no-sync vänta inte på att ändingar säkert\n"
+" skrivits till disk\n"
+
+#: pg_rewind.c:95
+#, c-format
+msgid " -P, --progress write progress messages\n"
+msgstr " -P, --progress skriv ut förloppmeddelanden\n"
+
+#: pg_rewind.c:96
+#, c-format
+msgid ""
+" -R, --write-recovery-conf write configuration for replication\n"
+" (requires --source-server)\n"
+msgstr ""
+" -R, --write-recovery-conf\n"
+" skriv konfiguration för replikering\n"
+" (kräver --source-server)\n"
+
+#: pg_rewind.c:98
+#, c-format
+msgid " --debug write a lot of debug messages\n"
+msgstr " --debug skriv ut en massa debugmeddelanden\n"
+
+#: pg_rewind.c:99
+#, c-format
+msgid " --no-ensure-shutdown do not automatically fix unclean shutdown\n"
+msgstr " --no-ensure-shutdown ingen automatisk hantering av trasig nedstängning\n"
+
+#: pg_rewind.c:100
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version skriv ut versioninformation och avsluta sedan\n"
+
+#: pg_rewind.c:101
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help visa denna hjälp och avsluta sedan\n"
+
+#: pg_rewind.c:102
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"Rapportera fel till <%s>.\n"
+
+#: pg_rewind.c:103
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "hemsida för %s: <%s>\n"
+
+#: pg_rewind.c:164 pg_rewind.c:213 pg_rewind.c:220 pg_rewind.c:227
+#: pg_rewind.c:234 pg_rewind.c:242
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "Försök med \"%s --help\" för mer information.\n"
+
+#: pg_rewind.c:212
+#, c-format
+msgid "no source specified (--source-pgdata or --source-server)"
+msgstr "ingen källa angavs (--source-pgdata eller --source-server)"
+
+#: pg_rewind.c:219
+#, c-format
+msgid "only one of --source-pgdata or --source-server can be specified"
+msgstr "bara en av --source-pgdata och --source-server får anges"
+
+#: pg_rewind.c:226
+#, c-format
+msgid "no target data directory specified (--target-pgdata)"
+msgstr "ingen måldatakatalog angiven (--target-pgdata)"
+
+#: pg_rewind.c:233
+#, c-format
+msgid "no source server information (--source-server) specified for --write-recovery-conf"
+msgstr "ingen källserverinformation (--source-server) angiven för --write-recovery-conf"
+
+#: pg_rewind.c:240
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "för många kommandoradsargument (första är \"%s\")"
+
+#: pg_rewind.c:255
+#, c-format
+msgid "cannot be executed by \"root\""
+msgstr "kan inte köras av \"root\""
+
+#: pg_rewind.c:256
+#, c-format
+msgid "You must run %s as the PostgreSQL superuser.\n"
+msgstr "Du måste köra %s som PostgreSQL:s superuser.\n"
+
+#: pg_rewind.c:267
+#, c-format
+msgid "could not read permissions of directory \"%s\": %m"
+msgstr "kunde inte läsa rättigheter på katalog \"%s\": %m"
+
+#: pg_rewind.c:287
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: pg_rewind.c:290
+#, c-format
+msgid "connected to server"
+msgstr "ansluten till server"
+
+#: pg_rewind.c:337
+#, c-format
+msgid "source and target cluster are on the same timeline"
+msgstr "källa och målkluster är på samma tidslinje"
+
+#: pg_rewind.c:346
+#, c-format
+msgid "servers diverged at WAL location %X/%X on timeline %u"
+msgstr "servrarna divergerade vid WAL-position %X/%X på tidslinje %u"
+
+#: pg_rewind.c:394
+#, c-format
+msgid "no rewind required"
+msgstr "ingen rewind krävs"
+
+#: pg_rewind.c:403
+#, c-format
+msgid "rewinding from last common checkpoint at %X/%X on timeline %u"
+msgstr "rewind från senaste gemensamma checkpoint vid %X/%X på tidslinje %u"
+
+#: pg_rewind.c:413
+#, c-format
+msgid "reading source file list"
+msgstr "läser källfillista"
+
+#: pg_rewind.c:417
+#, c-format
+msgid "reading target file list"
+msgstr "läser målfillista"
+
+#: pg_rewind.c:426
+#, c-format
+msgid "reading WAL in target"
+msgstr "läser WAL i målet"
+
+#: pg_rewind.c:447
+#, c-format
+msgid "need to copy %lu MB (total source directory size is %lu MB)"
+msgstr "behöver kopiera %lu MB (total källkatalogstorlek är %lu MB)"
+
+#: pg_rewind.c:465
+#, c-format
+msgid "syncing target data directory"
+msgstr "synkar måldatakatalog"
+
+#: pg_rewind.c:481
+#, c-format
+msgid "Done!"
+msgstr "Klar!"
+
+#: pg_rewind.c:564
+#, c-format
+msgid "no action decided for file \"%s\""
+msgstr "ingen åtgärd beslutades för filen \"%s\""
+
+#: pg_rewind.c:596
+#, c-format
+msgid "source system was modified while pg_rewind was running"
+msgstr "källsystemet ändrades samtidigt som pg_rewind kördes"
+
+#: pg_rewind.c:600
+#, c-format
+msgid "creating backup label and updating control file"
+msgstr "skapar backupetikett och uppdaterar kontrollfil"
+
+#: pg_rewind.c:650
+#, c-format
+msgid "source system was in unexpected state at end of rewind"
+msgstr "källsystemet var i ett oväntat tillstånd vid slutet av återspolningen"
+
+#: pg_rewind.c:681
+#, c-format
+msgid "source and target clusters are from different systems"
+msgstr "källa och målkluster är från olika system"
+
+#: pg_rewind.c:689
+#, c-format
+msgid "clusters are not compatible with this version of pg_rewind"
+msgstr "klustren är inte kompatibla med denna version av pg_rewind"
+
+#: pg_rewind.c:699
+#, c-format
+msgid "target server needs to use either data checksums or \"wal_log_hints = on\""
+msgstr "målservern behöver använda antingen datachecksums eller \"wal_log_hints = on\""
+
+#: pg_rewind.c:710
+#, c-format
+msgid "target server must be shut down cleanly"
+msgstr "målserver måste stängas ner utan fel"
+
+#: pg_rewind.c:720
+#, c-format
+msgid "source data directory must be shut down cleanly"
+msgstr "måldatakatalog måste stängas ner utan fel"
+
+#: pg_rewind.c:772
+#, c-format
+msgid "%*s/%s kB (%d%%) copied"
+msgstr "%*s/%s kB (%d%%) kopierad"
+
+#: pg_rewind.c:835
+#, c-format
+msgid "invalid control file"
+msgstr "ogiltig kontrollfil"
+
+#: pg_rewind.c:919
+#, c-format
+msgid "could not find common ancestor of the source and target cluster's timelines"
+msgstr "kunde inte finna en gemensam anfader av källa och målklusterets tidslinjer"
+
+#: pg_rewind.c:960
+#, c-format
+msgid "backup label buffer too small"
+msgstr "backupetikett-buffer för liten"
+
+#: pg_rewind.c:983
+#, c-format
+msgid "unexpected control file CRC"
+msgstr "oväntad kontrollfil-CRC"
+
+#: pg_rewind.c:995
+#, c-format
+msgid "unexpected control file size %d, expected %d"
+msgstr "oväntad kontrollfilstorlek %d, förväntade %d"
+
+#: pg_rewind.c:1004
+#, c-format
+msgid "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d byte"
+msgid_plural "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d bytes"
+msgstr[0] "WAL-segmentstorlek måste vara en tvåpotens mellan 1MB och 1GB men kontrollfilen anger %d byte"
+msgstr[1] "WAL-segmentstorlek måste vara en tvåpotens mellan 1MB och 1GB men kontrollfilen anger %d byte"
+
+#: pg_rewind.c:1043 pg_rewind.c:1101
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"Programmet \"%s\" behövs av %s men hittades inte i samma\n"
+"katalog som \"%s\".\n"
+"Kontrollera din installation."
+
+#: pg_rewind.c:1048 pg_rewind.c:1106
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"Programmet \"%s\" hittades av \"%s\"\n"
+"men är inte av samma version som %s.\n"
+"Kontrollera din installation."
+
+#: pg_rewind.c:1069
+#, c-format
+msgid "restore_command is not set in the target cluster"
+msgstr "restore_command är inte satt i målklustret"
+
+#: pg_rewind.c:1112
+#, c-format
+msgid "executing \"%s\" for target server to complete crash recovery"
+msgstr "kör \"%s\" för målservern för att slutföra krashåterställning"
+
+#: pg_rewind.c:1132
+#, c-format
+msgid "postgres single-user mode in target cluster failed"
+msgstr "postgres enanvändarläge misslyckades i målklustret"
+
+#: pg_rewind.c:1133
+#, c-format
+msgid "Command was: %s"
+msgstr "Kommandot var: %s"
+
+#: timeline.c:75 timeline.c:81
+#, c-format
+msgid "syntax error in history file: %s"
+msgstr "syntaxfel i history-fil: %s"
+
+#: timeline.c:76
+#, c-format
+msgid "Expected a numeric timeline ID."
+msgstr "Förväntade ett numeriskt tidslinje-ID."
+
+#: timeline.c:82
+#, c-format
+msgid "Expected a write-ahead log switchpoint location."
+msgstr "Förväntade en write-ahead-logg:s switchpoint-position."
+
+#: timeline.c:87
+#, c-format
+msgid "invalid data in history file: %s"
+msgstr "felaktig data i history-fil: %s"
+
+#: timeline.c:88
+#, c-format
+msgid "Timeline IDs must be in increasing sequence."
+msgstr "Tidslinje-ID måste komma i en stigande sekvens."
+
+#: timeline.c:108
+#, c-format
+msgid "invalid data in history file"
+msgstr "ogiltig data i historikfil"
+
+#: timeline.c:109
+#, c-format
+msgid "Timeline IDs must be less than child timeline's ID."
+msgstr "Tidslinje-ID:er måste vara mindre än barnens tidslinje-ID:er."
+
+#: xlogreader.c:354
+#, c-format
+msgid "invalid record offset at %X/%X"
+msgstr "ogiltig postoffset vid %X/%X"
+
+#: xlogreader.c:362
+#, c-format
+msgid "contrecord is requested by %X/%X"
+msgstr "contrecord är begärd vid %X/%X"
+
+#: xlogreader.c:403 xlogreader.c:733
+#, c-format
+msgid "invalid record length at %X/%X: wanted %u, got %u"
+msgstr "ogiltig postlängd vid %X/%X: förväntade %u, fick %u"
+
+#: xlogreader.c:429
+#, c-format
+msgid "record length %u at %X/%X too long"
+msgstr "postlängd %u vid %X/%X är för lång"
+
+#: xlogreader.c:477
+#, c-format
+msgid "there is no contrecord flag at %X/%X"
+msgstr "det finns ingen contrecord-flagga vid %X/%X"
+
+#: xlogreader.c:490
+#, c-format
+msgid "invalid contrecord length %u (expected %lld) at %X/%X"
+msgstr "ogiltig contrecord-längd %u (förväntade %lld) vid %X/%X"
+
+#: xlogreader.c:741
+#, c-format
+msgid "invalid resource manager ID %u at %X/%X"
+msgstr "ogiltigt resurshanterar-ID %u vid %X/%X"
+
+#: xlogreader.c:754 xlogreader.c:770
+#, c-format
+msgid "record with incorrect prev-link %X/%X at %X/%X"
+msgstr "post med inkorrekt prev-link %X/%X vid %X/%X"
+
+#: xlogreader.c:806
+#, c-format
+msgid "incorrect resource manager data checksum in record at %X/%X"
+msgstr "felaktig resurshanterardatakontrollsumma i post vid %X/%X"
+
+#: xlogreader.c:843
+#, c-format
+msgid "invalid magic number %04X in log segment %s, offset %u"
+msgstr "felaktigt magiskt nummer %04X i loggsegment %s, offset %u"
+
+#: xlogreader.c:857 xlogreader.c:898
+#, c-format
+msgid "invalid info bits %04X in log segment %s, offset %u"
+msgstr "ogiltiga infobitar %04X i loggsegment %s, offset %u"
+
+#: xlogreader.c:872
+#, c-format
+msgid "WAL file is from different database system: WAL file database system identifier is %llu, pg_control database system identifier is %llu"
+msgstr "WAL-fil är från ett annat databassystem: WAL-filens databassystemidentifierare är %llu, pg_control databassystemidentifierare är %llu"
+
+#: xlogreader.c:880
+#, c-format
+msgid "WAL file is from different database system: incorrect segment size in page header"
+msgstr "WAL-fil är från ett annat databassystem: inkorrekt segmentstorlek i sidhuvud"
+
+#: xlogreader.c:886
+#, c-format
+msgid "WAL file is from different database system: incorrect XLOG_BLCKSZ in page header"
+msgstr "WAL-fil är från ett annat databassystem: inkorrekt XLOG_BLCKSZ i sidhuvud"
+
+#: xlogreader.c:917
+#, c-format
+msgid "unexpected pageaddr %X/%X in log segment %s, offset %u"
+msgstr "oväntad sidadress %X/%X i loggsegment %s, offset %u"
+
+#: xlogreader.c:942
+#, c-format
+msgid "out-of-sequence timeline ID %u (after %u) in log segment %s, offset %u"
+msgstr "ej-i-sekvens för tidslinje-ID %u (efter %u) i loggsegment %s, offset %u"
+
+#: xlogreader.c:1287
+#, c-format
+msgid "out-of-order block_id %u at %X/%X"
+msgstr "ej-i-sekvens block_id %u vid %X/%X"
+
+#: xlogreader.c:1309
+#, c-format
+msgid "BKPBLOCK_HAS_DATA set, but no data included at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA satt, men ingen data inkluderad vid %X/%X"
+
+#: xlogreader.c:1316
+#, c-format
+msgid "BKPBLOCK_HAS_DATA not set, but data length is %u at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA ej satt, men datalängd är %u vid %X/%X"
+
+#: xlogreader.c:1352
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE set, but hole offset %u length %u block image length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE satt, men håloffset %u längd %u block-image-längd %u vid %X/%X"
+
+#: xlogreader.c:1368
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE not set, but hole offset %u length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE ej satt, men håloffset %u längd %u vid %X/%X"
+
+#: xlogreader.c:1383
+#, c-format
+msgid "BKPIMAGE_IS_COMPRESSED set, but block image length %u at %X/%X"
+msgstr "BKPIMAGE_IS_COMPRESSED satt, men block-image-längd %u vid %X/%X"
+
+#: xlogreader.c:1398
+#, c-format
+msgid "neither BKPIMAGE_HAS_HOLE nor BKPIMAGE_IS_COMPRESSED set, but block image length is %u at %X/%X"
+msgstr "varken BKPIMAGE_HAS_HOLE eller BKPIMAGE_IS_COMPRESSED satt, men block-image-längd är %u vid %X/%X"
+
+#: xlogreader.c:1414
+#, c-format
+msgid "BKPBLOCK_SAME_REL set but no previous rel at %X/%X"
+msgstr "BKPBLOCK_SAME_REL satt men ingen tidigare rel vid %X/%X"
+
+#: xlogreader.c:1426
+#, c-format
+msgid "invalid block_id %u at %X/%X"
+msgstr "ogiltig block_id %u vid %X/%X"
+
+#: xlogreader.c:1513
+#, c-format
+msgid "record with invalid length at %X/%X"
+msgstr "post med ogiltig längd vid %X/%X"
+
+#: xlogreader.c:1602
+#, c-format
+msgid "invalid compressed image at %X/%X, block %d"
+msgstr "ogiltig komprimerad image vid %X/%X, block %d"
diff --git a/src/bin/pg_rewind/po/uk.po b/src/bin/pg_rewind/po/uk.po
new file mode 100644
index 0000000..a639de0
--- /dev/null
+++ b/src/bin/pg_rewind/po/uk.po
@@ -0,0 +1,943 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: postgresql\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-04-18 03:34+0000\n"
+"PO-Revision-Date: 2022-06-19 10:10\n"
+"Last-Translator: \n"
+"Language-Team: Ukrainian\n"
+"Language: uk_UA\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
+"X-Crowdin-Project: postgresql\n"
+"X-Crowdin-Project-ID: 324573\n"
+"X-Crowdin-Language: uk\n"
+"X-Crowdin-File: /REL_14_STABLE/pg_rewind.pot\n"
+"X-Crowdin-File-ID: 738\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "збій: "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "помилка: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "попередження: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "недостатньо пам'яті\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "неможливо дублювати нульовий покажчик (внутрішня помилка)\n"
+
+#: ../../common/restricted_token.c:64
+#, c-format
+msgid "could not load library \"%s\": error code %lu"
+msgstr "не вдалося завантажити бібліотеку \"%s\": код помилки %lu"
+
+#: ../../common/restricted_token.c:73
+#, c-format
+msgid "cannot create restricted tokens on this platform: error code %lu"
+msgstr "не вдалося створити обмежені токени на цій платформі: код помилки %lu"
+
+#: ../../common/restricted_token.c:82
+#, c-format
+msgid "could not open process token: error code %lu"
+msgstr "не вдалося відкрити токен процесу: код помилки %lu"
+
+#: ../../common/restricted_token.c:97
+#, c-format
+msgid "could not allocate SIDs: error code %lu"
+msgstr "не вдалося виділити SID: код помилки %lu"
+
+#: ../../common/restricted_token.c:119
+#, c-format
+msgid "could not create restricted token: error code %lu"
+msgstr "не вдалося створити обмежений токен: код помилки %lu"
+
+#: ../../common/restricted_token.c:140
+#, c-format
+msgid "could not start process for command \"%s\": error code %lu"
+msgstr "не вдалося запустити процес для команди \"%s\": код помилки %lu"
+
+#: ../../common/restricted_token.c:178
+#, c-format
+msgid "could not re-execute with restricted token: error code %lu"
+msgstr "не вдалося перезапустити з обмеженим токеном: код помилки %lu"
+
+#: ../../common/restricted_token.c:194
+#, c-format
+msgid "could not get exit code from subprocess: error code %lu"
+msgstr "не вдалося отримати код завершення підпроцесу: код помилки %lu"
+
+#: ../../fe_utils/archive.c:53
+#, c-format
+msgid "cannot use restore_command with %%r placeholder"
+msgstr "не вдалося використати restore_command із заповнювачем %%r"
+
+#: ../../fe_utils/archive.c:74
+#, c-format
+msgid "unexpected file size for \"%s\": %lld instead of %lld"
+msgstr "неочікуваний розмір файлу для \"%s\": %lld замість %lld"
+
+#: ../../fe_utils/archive.c:85
+#, c-format
+msgid "could not open file \"%s\" restored from archive: %m"
+msgstr "не вдалося відкрити файл \"%s\" відновлений з архіву: %m"
+
+#: ../../fe_utils/archive.c:97 file_ops.c:417
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "не вдалося отримати інформацію від файлу \"%s\": %m"
+
+#: ../../fe_utils/archive.c:112
+#, c-format
+msgid "restore_command failed: %s"
+msgstr "помилка restore_command: %s"
+
+#: ../../fe_utils/archive.c:121
+#, c-format
+msgid "could not restore file \"%s\" from archive"
+msgstr "не вдалося відновити файл \"%s\" з архіву"
+
+#: ../../fe_utils/recovery_gen.c:35 ../../fe_utils/recovery_gen.c:49
+#: ../../fe_utils/recovery_gen.c:77 ../../fe_utils/recovery_gen.c:100
+#: ../../fe_utils/recovery_gen.c:171 parsexlog.c:77 parsexlog.c:137
+#: parsexlog.c:197
+#, c-format
+msgid "out of memory"
+msgstr "недостатньо пам'яті"
+
+#: ../../fe_utils/recovery_gen.c:134 parsexlog.c:310
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "не можливо відкрити файл \"%s\": %m"
+
+#: ../../fe_utils/recovery_gen.c:140
+#, c-format
+msgid "could not write to file \"%s\": %m"
+msgstr "неможливо записати до файлу \"%s\": %m"
+
+#: ../../fe_utils/recovery_gen.c:152
+#, c-format
+msgid "could not create file \"%s\": %m"
+msgstr "неможливо створити файл \"%s\": %m"
+
+#: file_ops.c:67
+#, c-format
+msgid "could not open target file \"%s\": %m"
+msgstr "не вдалося відкрити цільовий файл \"%s\": %m"
+
+#: file_ops.c:81
+#, c-format
+msgid "could not close target file \"%s\": %m"
+msgstr "не вдалося закрити цільовий файл \"%s\": %m"
+
+#: file_ops.c:101
+#, c-format
+msgid "could not seek in target file \"%s\": %m"
+msgstr "не вдалося знайти в цільовому файлі \"%s\": %m"
+
+#: file_ops.c:117
+#, c-format
+msgid "could not write file \"%s\": %m"
+msgstr "не вдалося записати файл \"%s\": %m"
+
+#: file_ops.c:150 file_ops.c:177
+#, c-format
+msgid "undefined file type for \"%s\""
+msgstr "невизначений тип файлу для \"%s\""
+
+#: file_ops.c:173
+#, c-format
+msgid "invalid action (CREATE) for regular file"
+msgstr "неприпустима дія (CREATE) для звичайного файлу"
+
+#: file_ops.c:200
+#, c-format
+msgid "could not remove file \"%s\": %m"
+msgstr "не можливо видалити файл \"%s\": %m"
+
+#: file_ops.c:218
+#, c-format
+msgid "could not open file \"%s\" for truncation: %m"
+msgstr "не вдалося відкрити файл \"%s\" для скорочення: %m"
+
+#: file_ops.c:222
+#, c-format
+msgid "could not truncate file \"%s\" to %u: %m"
+msgstr "не вдалося скоротити файл \"%s\" до потрібного розміру %u: %m"
+
+#: file_ops.c:238
+#, c-format
+msgid "could not create directory \"%s\": %m"
+msgstr "не вдалося створити каталог \"%s\": %m"
+
+#: file_ops.c:252
+#, c-format
+msgid "could not remove directory \"%s\": %m"
+msgstr "не вдалося видалити каталог \"%s\": %m"
+
+#: file_ops.c:266
+#, c-format
+msgid "could not create symbolic link at \"%s\": %m"
+msgstr "неможливо створити символічне послання на \"%s\": %m"
+
+#: file_ops.c:280
+#, c-format
+msgid "could not remove symbolic link \"%s\": %m"
+msgstr "не вдалося видалити символьне посилання \"%s\": %m"
+
+#: file_ops.c:326 file_ops.c:330
+#, c-format
+msgid "could not open file \"%s\" for reading: %m"
+msgstr "не вдалося відкрити файл \"%s\" для читання: %m"
+
+#: file_ops.c:341 local_source.c:107 parsexlog.c:348
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "не вдалося прочитати файл \"%s\": %m"
+
+#: file_ops.c:344 parsexlog.c:350
+#, c-format
+msgid "could not read file \"%s\": read %d of %zu"
+msgstr "не вдалося прочитати файл \"%s\": прочитано %d з %zu"
+
+#: file_ops.c:388
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "не вдалося відкрити каталог \"%s\": %m"
+
+#: file_ops.c:446
+#, c-format
+msgid "could not read symbolic link \"%s\": %m"
+msgstr "не можливо прочитати символічне послання \"%s\": %m"
+
+#: file_ops.c:449
+#, c-format
+msgid "symbolic link \"%s\" target is too long"
+msgstr "таргет символічного посилання \"%s\" задовгий"
+
+#: file_ops.c:464
+#, c-format
+msgid "\"%s\" is a symbolic link, but symbolic links are not supported on this platform"
+msgstr "\"%s\"є символічним посиланням, але символічні посилання не підтримуються на даній платформі"
+
+#: file_ops.c:471
+#, c-format
+msgid "could not read directory \"%s\": %m"
+msgstr "не вдалося прочитати каталог \"%s\": %m"
+
+#: file_ops.c:475
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "не вдалося закрити каталог \"%s\": %m"
+
+#: filemap.c:237
+#, c-format
+msgid "data file \"%s\" in source is not a regular file"
+msgstr "файл даних \"%s\" в джерелі не є регулярним файлом"
+
+#: filemap.c:242 filemap.c:275
+#, c-format
+msgid "duplicate source file \"%s\""
+msgstr "дублікат вихідного файлу \"%s\""
+
+#: filemap.c:330
+#, c-format
+msgid "unexpected page modification for non-regular file \"%s\""
+msgstr "неочікувана модифікація сторінки для нерегулярного файлу \"%s\""
+
+#: filemap.c:680 filemap.c:774
+#, c-format
+msgid "unknown file type for \"%s\""
+msgstr "невідомий тип файлу для \"%s\""
+
+#: filemap.c:707
+#, c-format
+msgid "file \"%s\" is of different type in source and target"
+msgstr "файл \"%s\" має різні типи у джерелі та цілі"
+
+#: filemap.c:779
+#, c-format
+msgid "could not decide what to do with file \"%s\""
+msgstr "не вдалося вирішити, що робити з файлом \"%s\""
+
+#: libpq_source.c:128
+#, c-format
+msgid "could not clear search_path: %s"
+msgstr "не вдалося очистити search_path: %s"
+
+#: libpq_source.c:139
+#, c-format
+msgid "full_page_writes must be enabled in the source server"
+msgstr "на початковому сервері повинно бути увімкнено full_page_writes"
+
+#: libpq_source.c:150
+#, c-format
+msgid "could not prepare statement to fetch file contents: %s"
+msgstr "не вдалося підготувати інструкцію щоб отримати вміст файлу: %s"
+
+#: libpq_source.c:169
+#, c-format
+msgid "error running query (%s) on source server: %s"
+msgstr "помилка при виконанні запиту (%s) на вихідному сервері: %s"
+
+#: libpq_source.c:174
+#, c-format
+msgid "unexpected result set from query"
+msgstr "неочікуваний результат запиту"
+
+#: libpq_source.c:196
+#, c-format
+msgid "error running query (%s) in source server: %s"
+msgstr "помилка при виконанні запиту (%s) на початковому сервері: %s"
+
+#: libpq_source.c:217
+#, c-format
+msgid "unrecognized result \"%s\" for current WAL insert location"
+msgstr "нерозпізнаний результат \"%s\" замість поточної добавленої позиції WAL"
+
+#: libpq_source.c:268
+#, c-format
+msgid "could not fetch file list: %s"
+msgstr "не вдалося отримати список файлів: %s"
+
+#: libpq_source.c:273
+#, c-format
+msgid "unexpected result set while fetching file list"
+msgstr "неочікуваний результат при отриманні списку файлів"
+
+#: libpq_source.c:435
+#, c-format
+msgid "could not send query: %s"
+msgstr "не вдалося надіслати запит: %s"
+
+#: libpq_source.c:438
+#, c-format
+msgid "could not set libpq connection to single row mode"
+msgstr "не вдалося встановити libpq з'єднання для однорядкового режиму"
+
+#: libpq_source.c:468
+#, c-format
+msgid "unexpected result while fetching remote files: %s"
+msgstr "неочікуваний результат при отриманні віддалених файлів: %s"
+
+#: libpq_source.c:473
+#, c-format
+msgid "received more data chunks than requested"
+msgstr "отримано більше фрагментів даних, ніж запитувалось"
+
+#: libpq_source.c:477
+#, c-format
+msgid "unexpected result set size while fetching remote files"
+msgstr "неочікуваний розмір набору результатів при отриманні віддалених файлів"
+
+#: libpq_source.c:483
+#, c-format
+msgid "unexpected data types in result set while fetching remote files: %u %u %u"
+msgstr "неочікувані типи даних в результаті при отриманні віддалених файлів: %u %u %u"
+
+#: libpq_source.c:491
+#, c-format
+msgid "unexpected result format while fetching remote files"
+msgstr "неочікуваний формат результату при отриманні віддалених файлів"
+
+#: libpq_source.c:497
+#, c-format
+msgid "unexpected null values in result while fetching remote files"
+msgstr "неочікувані нульові значення в результаті при отриманні віддалених файлів"
+
+#: libpq_source.c:501
+#, c-format
+msgid "unexpected result length while fetching remote files"
+msgstr "неочікувана довжина результату при отриманні віддалених файлів"
+
+#: libpq_source.c:534
+#, c-format
+msgid "received data for file \"%s\", when requested for \"%s\""
+msgstr "отримані дані для файлу \"%s\", коли запитувалось для \"%s\""
+
+#: libpq_source.c:538
+#, c-format
+msgid "received data at offset %lld of file \"%s\", when requested for offset %lld"
+msgstr "отримано дані по зсуву %lld файлу \"%s\", коли запитувалось про зсув %lld"
+
+#: libpq_source.c:550
+#, c-format
+msgid "received more than requested for file \"%s\""
+msgstr "отримано більше, ніж запитувалось для файлу \"%s\""
+
+#: libpq_source.c:563
+#, c-format
+msgid "unexpected number of data chunks received"
+msgstr "отримано неочікувану кількість фрагментів даних"
+
+#: libpq_source.c:606
+#, c-format
+msgid "could not fetch remote file \"%s\": %s"
+msgstr "не вдалося отримати віддалений файл \"%s\": %s"
+
+#: libpq_source.c:611
+#, c-format
+msgid "unexpected result set while fetching remote file \"%s\""
+msgstr "неочікуваний набір результатів при отриманні віддаленого файлу \"%s\""
+
+#: local_source.c:86
+#, c-format
+msgid "could not open source file \"%s\": %m"
+msgstr "не вдалося відкрити вихідний файл \"%s\": %m"
+
+#: local_source.c:90
+#, c-format
+msgid "could not seek in source file: %m"
+msgstr "не вдалося знайти у вихідному файлі: %m"
+
+#: local_source.c:109
+#, c-format
+msgid "unexpected EOF while reading file \"%s\""
+msgstr "неочікуваний кінець при читанні файлу \"%s\""
+
+#: local_source.c:116
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "неможливо закрити файл \"%s\": %m"
+
+#: parsexlog.c:89 parsexlog.c:144
+#, c-format
+msgid "could not read WAL record at %X/%X: %s"
+msgstr "не вдалося прочитати запис WAL на %X/%X: %s"
+
+#: parsexlog.c:93 parsexlog.c:147
+#, c-format
+msgid "could not read WAL record at %X/%X"
+msgstr "не вдалося прочитати запис WAL на %X/%X"
+
+#: parsexlog.c:106
+#, c-format
+msgid "end pointer %X/%X is not a valid end point; expected %X/%X"
+msgstr "кінцевий покажчик %X/%X не є допустимою кінцевою точкою; очікувалось %X/%X"
+
+#: parsexlog.c:210
+#, c-format
+msgid "could not find previous WAL record at %X/%X: %s"
+msgstr "не вдалося знайти попередній запис WAL на %X/%X: %s"
+
+#: parsexlog.c:214
+#, c-format
+msgid "could not find previous WAL record at %X/%X"
+msgstr "не вдалося знайти попередній запис WAL на %X/%X"
+
+#: parsexlog.c:339
+#, c-format
+msgid "could not seek in file \"%s\": %m"
+msgstr "не вдалося знайти в файлі \"%s\": %m"
+
+#: parsexlog.c:431
+#, c-format
+msgid "WAL record modifies a relation, but record type is not recognized: lsn: %X/%X, rmgr: %s, info: %02X"
+msgstr "WAL модифікує відношення, але тип запису не розпізнано: lsn: %X/%X, rmgr: %s, info: %02X"
+
+#: pg_rewind.c:84
+#, c-format
+msgid "%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n\n"
+msgstr "%s синхронізує кластер PostgreSQL з іншою копією кластеру.\n\n"
+
+#: pg_rewind.c:85
+#, c-format
+msgid "Usage:\n"
+" %s [OPTION]...\n\n"
+msgstr "Використання:\n"
+" %s [OPTION]...\n\n"
+
+#: pg_rewind.c:86
+#, c-format
+msgid "Options:\n"
+msgstr "Параметри:\n"
+
+#: pg_rewind.c:87
+#, c-format
+msgid " -c, --restore-target-wal use restore_command in target configuration to\n"
+" retrieve WAL files from archives\n"
+msgstr " -c, --restore-target-wal використовує restore_command в цільовій конфігурації, щоб\n"
+" отримати файли WAL з архівів\n"
+
+#: pg_rewind.c:89
+#, c-format
+msgid " -D, --target-pgdata=DIRECTORY existing data directory to modify\n"
+msgstr " -D, --target-pgdata=DIRECTORY існуючий каталог для змін\n"
+
+#: pg_rewind.c:90
+#, c-format
+msgid " --source-pgdata=DIRECTORY source data directory to synchronize with\n"
+msgstr " --source-pgdata=DIRECTORY початковий каталог даних для синхронізації\n"
+
+#: pg_rewind.c:91
+#, c-format
+msgid " --source-server=CONNSTR source server to synchronize with\n"
+msgstr " --source-server=CONNSTR початковий сервер для синхронізації\n"
+
+#: pg_rewind.c:92
+#, c-format
+msgid " -n, --dry-run stop before modifying anything\n"
+msgstr " -n, --dry-run зупинитися до внесення будь-яких змін\n"
+
+#: pg_rewind.c:93
+#, c-format
+msgid " -N, --no-sync do not wait for changes to be written\n"
+" safely to disk\n"
+msgstr " -N, --no-sync не чекати поки зміни будуть записані на диск\n"
+
+#: pg_rewind.c:95
+#, c-format
+msgid " -P, --progress write progress messages\n"
+msgstr " -P, --progress повідомляти про хід процесу\n"
+
+#: pg_rewind.c:96
+#, c-format
+msgid " -R, --write-recovery-conf write configuration for replication\n"
+" (requires --source-server)\n"
+msgstr " -R, --write-recovery-conf записує конфігурацію для реплікації \n"
+" (потребує --source-server)\n"
+
+#: pg_rewind.c:98
+#, c-format
+msgid " --debug write a lot of debug messages\n"
+msgstr " --debug виводити багато налагоджувальних повідомлень\n"
+
+#: pg_rewind.c:99
+#, c-format
+msgid " --no-ensure-shutdown do not automatically fix unclean shutdown\n"
+msgstr " --no-ensure-shutdown не виправляти автоматично неочищене завершення роботи\n"
+
+#: pg_rewind.c:100
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version вивести інформацію про версію і вийти\n"
+
+#: pg_rewind.c:101
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help показати довідку, потім вийти\n"
+
+#: pg_rewind.c:102
+#, c-format
+msgid "\n"
+"Report bugs to <%s>.\n"
+msgstr "\n"
+"Повідомляти про помилки на <%s>.\n"
+
+#: pg_rewind.c:103
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "Домашня сторінка %s: <%s>\n"
+
+#: pg_rewind.c:164 pg_rewind.c:213 pg_rewind.c:220 pg_rewind.c:227
+#: pg_rewind.c:234 pg_rewind.c:242
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "Спробуйте \"%s --help\" для додаткової інформації.\n"
+
+#: pg_rewind.c:212
+#, c-format
+msgid "no source specified (--source-pgdata or --source-server)"
+msgstr "джерело не вказано (--source-pgdata чи --source-server)"
+
+#: pg_rewind.c:219
+#, c-format
+msgid "only one of --source-pgdata or --source-server can be specified"
+msgstr "може бути вказано лише --source-pgdata чи --source-server"
+
+#: pg_rewind.c:226
+#, c-format
+msgid "no target data directory specified (--target-pgdata)"
+msgstr "не вказано жодного каталогу цільових даних (--target-pgdata)"
+
+#: pg_rewind.c:233
+#, c-format
+msgid "no source server information (--source-server) specified for --write-recovery-conf"
+msgstr "немає інформації про вихідний сервер (--source-server) вказаної для --write-recovery-conf"
+
+#: pg_rewind.c:240
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "забагато аргументів у командному рядку (перший \"%s\")"
+
+#: pg_rewind.c:255
+#, c-format
+msgid "cannot be executed by \"root\""
+msgstr "\"root\" не може це виконувати"
+
+#: pg_rewind.c:256
+#, c-format
+msgid "You must run %s as the PostgreSQL superuser.\n"
+msgstr "Запускати %s треба від суперкористувача PostgreSQL.\n"
+
+#: pg_rewind.c:267
+#, c-format
+msgid "could not read permissions of directory \"%s\": %m"
+msgstr "не вдалося прочитати дозволи на каталог \"%s\": %m"
+
+#: pg_rewind.c:287
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: pg_rewind.c:290
+#, c-format
+msgid "connected to server"
+msgstr "під'єднано до серверу"
+
+#: pg_rewind.c:337
+#, c-format
+msgid "source and target cluster are on the same timeline"
+msgstr "початковий і цільовий кластери знаходяться на одній лінії часу"
+
+#: pg_rewind.c:346
+#, c-format
+msgid "servers diverged at WAL location %X/%X on timeline %u"
+msgstr "сервери розійшлись в позиції WAL %X/%X на лінії часу %u"
+
+#: pg_rewind.c:394
+#, c-format
+msgid "no rewind required"
+msgstr "перемотування не потрібне"
+
+#: pg_rewind.c:403
+#, c-format
+msgid "rewinding from last common checkpoint at %X/%X on timeline %u"
+msgstr "перемотування від останньої спільної контрольної точки на %X/%X на лінії часу %u"
+
+#: pg_rewind.c:413
+#, c-format
+msgid "reading source file list"
+msgstr "читання списку файлів із джерела"
+
+#: pg_rewind.c:417
+#, c-format
+msgid "reading target file list"
+msgstr "читання списку цільових файлів"
+
+#: pg_rewind.c:426
+#, c-format
+msgid "reading WAL in target"
+msgstr "читання WAL у цілі"
+
+#: pg_rewind.c:447
+#, c-format
+msgid "need to copy %lu MB (total source directory size is %lu MB)"
+msgstr "треба скопіювати %lu МБ (загальний розмір каталогу джерела становить %lu МБ)"
+
+#: pg_rewind.c:465
+#, c-format
+msgid "syncing target data directory"
+msgstr "синхронізація цільового каталогу даних"
+
+#: pg_rewind.c:481
+#, c-format
+msgid "Done!"
+msgstr "Готово!"
+
+#: pg_rewind.c:564
+#, c-format
+msgid "no action decided for file \"%s\""
+msgstr "жодних дій щодо файлу \"%s\" не прийнято"
+
+#: pg_rewind.c:596
+#, c-format
+msgid "source system was modified while pg_rewind was running"
+msgstr "вихідну систему було змінено під час роботи pg_rewind"
+
+#: pg_rewind.c:600
+#, c-format
+msgid "creating backup label and updating control file"
+msgstr "створення мітки резервного копіювання і оновлення контрольного файлу"
+
+#: pg_rewind.c:650
+#, c-format
+msgid "source system was in unexpected state at end of rewind"
+msgstr "вихідна система була в неочікуваному стані наприкінці перемотування"
+
+#: pg_rewind.c:681
+#, c-format
+msgid "source and target clusters are from different systems"
+msgstr "початковий і цільовий кластер належать до різних систем"
+
+#: pg_rewind.c:689
+#, c-format
+msgid "clusters are not compatible with this version of pg_rewind"
+msgstr "кластери не сумісні з даною версією pg_rewind"
+
+#: pg_rewind.c:699
+#, c-format
+msgid "target server needs to use either data checksums or \"wal_log_hints = on\""
+msgstr "цільовий сервер потребує використання контрольної суми даних або \"wal_log_hints = on\""
+
+#: pg_rewind.c:710
+#, c-format
+msgid "target server must be shut down cleanly"
+msgstr "цільовий сервер повинен бути вимкненим штатно"
+
+#: pg_rewind.c:720
+#, c-format
+msgid "source data directory must be shut down cleanly"
+msgstr "робота з початковим каталогом даних повинна бути завершена штатно"
+
+#: pg_rewind.c:772
+#, c-format
+msgid "%*s/%s kB (%d%%) copied"
+msgstr "скопійовано %*s/%s кБ (%d%%)"
+
+#: pg_rewind.c:835
+#, c-format
+msgid "invalid control file"
+msgstr "неприпустимий контрольний файл"
+
+#: pg_rewind.c:919
+#, c-format
+msgid "could not find common ancestor of the source and target cluster's timelines"
+msgstr "не вдалося знайти спільного предка ліній часу початкового та цільового кластерів"
+
+#: pg_rewind.c:960
+#, c-format
+msgid "backup label buffer too small"
+msgstr "буфер для мітки резервного копіювання замалий"
+
+#: pg_rewind.c:983
+#, c-format
+msgid "unexpected control file CRC"
+msgstr "неочікуваний контрольний файл CRC"
+
+#: pg_rewind.c:995
+#, c-format
+msgid "unexpected control file size %d, expected %d"
+msgstr "неочікуваний розмір контрольного файлу %d, очікувалося %d"
+
+#: pg_rewind.c:1004
+#, c-format
+msgid "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d byte"
+msgid_plural "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d bytes"
+msgstr[0] "Розмір сегменту WAL повинен задаватись ступенем 2 в інтервалі від 1 МБ до 1 ГБ, але в керуючому файлі вказано значення %d"
+msgstr[1] "Розмір сегменту WAL повинен задаватись ступенем 2 в інтервалі від 1 МБ до 1 ГБ, але в керуючому файлі вказано значення %d"
+msgstr[2] "Розмір сегменту WAL повинен задаватись ступенем 2 в інтервалі від 1 МБ до 1 ГБ, але в керуючому файлі вказано значення %d"
+msgstr[3] "Розмір сегменту WAL повинен задаватись ступенем 2 в інтервалі від 1 МБ до 1 ГБ, але в керуючому файлі вказано значення %d"
+
+#: pg_rewind.c:1043 pg_rewind.c:1101
+#, c-format
+msgid "The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr "Програма \"%s\" потрібна для %s, але не знайдена в тому ж каталозі, що й \"%s\".\n"
+"Перевірте вашу установку."
+
+#: pg_rewind.c:1048 pg_rewind.c:1106
+#, c-format
+msgid "The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr "Програма \"%s\" була знайдена \"%s\", але не була тієї ж версії, що %s.\n"
+"Перевірте вашу установку."
+
+#: pg_rewind.c:1069
+#, c-format
+msgid "restore_command is not set in the target cluster"
+msgstr "команда restore_command не встановлена в цільовому кластері"
+
+#: pg_rewind.c:1112
+#, c-format
+msgid "executing \"%s\" for target server to complete crash recovery"
+msgstr "виконання \"%s\" для цільового серверу, щоб завершити відновлення після аварійного завершення роботи"
+
+#: pg_rewind.c:1132
+#, c-format
+msgid "postgres single-user mode in target cluster failed"
+msgstr "не вдалося ввімкнути однокористувацький режим postgres в цільовому кластері"
+
+#: pg_rewind.c:1133
+#, c-format
+msgid "Command was: %s"
+msgstr "Команда була: %s"
+
+#: timeline.c:75 timeline.c:81
+#, c-format
+msgid "syntax error in history file: %s"
+msgstr "синтаксична помилка у файлі історії: %s"
+
+#: timeline.c:76
+#, c-format
+msgid "Expected a numeric timeline ID."
+msgstr "Очікується числовий ідентифікатор лінії часу."
+
+#: timeline.c:82
+#, c-format
+msgid "Expected a write-ahead log switchpoint location."
+msgstr "Очікується положення точки випереджувального журналювання."
+
+#: timeline.c:87
+#, c-format
+msgid "invalid data in history file: %s"
+msgstr "неприпустимі дані у файлу історії: %s"
+
+#: timeline.c:88
+#, c-format
+msgid "Timeline IDs must be in increasing sequence."
+msgstr "Ідентифікатори ліній часу повинні збільшуватись."
+
+#: timeline.c:108
+#, c-format
+msgid "invalid data in history file"
+msgstr "неприпустимі дані у файлі історії"
+
+#: timeline.c:109
+#, c-format
+msgid "Timeline IDs must be less than child timeline's ID."
+msgstr "Ідентифікатори ліній часу повинні бути меншими від ідентифікатора дочірньої лінії."
+
+#: xlogreader.c:354
+#, c-format
+msgid "invalid record offset at %X/%X"
+msgstr "невірний зсув запису: %X/%X"
+
+#: xlogreader.c:362
+#, c-format
+msgid "contrecord is requested by %X/%X"
+msgstr "по зсуву %X/%X запитано продовження запису"
+
+#: xlogreader.c:403 xlogreader.c:733
+#, c-format
+msgid "invalid record length at %X/%X: wanted %u, got %u"
+msgstr "невірна довжина запису по зсуву %X/%X: очікувалось %u, отримано %u"
+
+#: xlogreader.c:429
+#, c-format
+msgid "record length %u at %X/%X too long"
+msgstr "довжина запису %u на %X/%X є задовгою"
+
+#: xlogreader.c:477
+#, c-format
+msgid "there is no contrecord flag at %X/%X"
+msgstr "немає прапора contrecord на %X/%X"
+
+#: xlogreader.c:490
+#, c-format
+msgid "invalid contrecord length %u (expected %lld) at %X/%X"
+msgstr "неприпустима довжина contrecord %u (очікувалось %lld) на %X/%X"
+
+#: xlogreader.c:741
+#, c-format
+msgid "invalid resource manager ID %u at %X/%X"
+msgstr "невірний ID менеджера ресурсів %u в %X/%X"
+
+#: xlogreader.c:754 xlogreader.c:770
+#, c-format
+msgid "record with incorrect prev-link %X/%X at %X/%X"
+msgstr "запис з неправильним попереднім посиланням %X/%X на %X/%X"
+
+#: xlogreader.c:806
+#, c-format
+msgid "incorrect resource manager data checksum in record at %X/%X"
+msgstr "некоректна контрольна сума даних менеджера ресурсів у запису по зсуву %X/%X"
+
+#: xlogreader.c:843
+#, c-format
+msgid "invalid magic number %04X in log segment %s, offset %u"
+msgstr "невірне магічне число %04X в сегменті журналу %s, зсув %u"
+
+#: xlogreader.c:857 xlogreader.c:898
+#, c-format
+msgid "invalid info bits %04X in log segment %s, offset %u"
+msgstr "невірні інформаційні біти %04X в сегменті журналу %s, зсув %u"
+
+#: xlogreader.c:872
+#, c-format
+msgid "WAL file is from different database system: WAL file database system identifier is %llu, pg_control database system identifier is %llu"
+msgstr "WAL файл належить іншій системі баз даних: ідентифікатор системи баз даних де міститься WAL файл - %llu, а ідентифікатор системи баз даних pg_control - %llu"
+
+#: xlogreader.c:880
+#, c-format
+msgid "WAL file is from different database system: incorrect segment size in page header"
+msgstr "Файл WAL належить іншій системі баз даних: некоректний розмір сегменту в заголовку сторінки"
+
+#: xlogreader.c:886
+#, c-format
+msgid "WAL file is from different database system: incorrect XLOG_BLCKSZ in page header"
+msgstr "Файл WAL належить іншій системі баз даних: некоректний XLOG_BLCKSZ в заголовку сторінки"
+
+#: xlogreader.c:917
+#, c-format
+msgid "unexpected pageaddr %X/%X in log segment %s, offset %u"
+msgstr "неочікуваний pageaddr %X/%X в сегменті журналу %s, зсув %u"
+
+#: xlogreader.c:942
+#, c-format
+msgid "out-of-sequence timeline ID %u (after %u) in log segment %s, offset %u"
+msgstr "порушення послідовності ID лінії часу %u (після %u) в сегменті журналу %s, зсув %u"
+
+#: xlogreader.c:1287
+#, c-format
+msgid "out-of-order block_id %u at %X/%X"
+msgstr "ідентифікатор блока %u out-of-order в позиції %X/%X"
+
+#: xlogreader.c:1309
+#, c-format
+msgid "BKPBLOCK_HAS_DATA set, but no data included at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA встановлений, але немає даних в позиції %X/%X"
+
+#: xlogreader.c:1316
+#, c-format
+msgid "BKPBLOCK_HAS_DATA not set, but data length is %u at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA встановлений, але довжина даних дорівнює %u в позиції %X/%X"
+
+#: xlogreader.c:1352
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE set, but hole offset %u length %u block image length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE встановлений, але для пропуску задані: зсув %u, довжина %u, при довжині образу блока %u в позиції %X/%X"
+
+#: xlogreader.c:1368
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE not set, but hole offset %u length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE не встановлений, але для пропуску задані: зсув %u, довжина %u в позиції %X/%X"
+
+#: xlogreader.c:1383
+#, c-format
+msgid "BKPIMAGE_IS_COMPRESSED set, but block image length %u at %X/%X"
+msgstr "BKPIMAGE_IS_COMPRESSED встановлений, але довжина образу блока дорівнює %u в позиції %X/%X"
+
+#: xlogreader.c:1398
+#, c-format
+msgid "neither BKPIMAGE_HAS_HOLE nor BKPIMAGE_IS_COMPRESSED set, but block image length is %u at %X/%X"
+msgstr "ні BKPIMAGE_HAS_HOLE, ні BKPIMAGE_IS_COMPRESSED не встановлені, але довжина образу блока дорвінює %u в позиції %X/%X"
+
+#: xlogreader.c:1414
+#, c-format
+msgid "BKPBLOCK_SAME_REL set but no previous rel at %X/%X"
+msgstr "BKPBLOCK_SAME_REL встановлений, але попереднє значення не задано в позиції %X/%X"
+
+#: xlogreader.c:1426
+#, c-format
+msgid "invalid block_id %u at %X/%X"
+msgstr "невірний ідентифікатор блоку %u в позиції %X/%X"
+
+#: xlogreader.c:1513
+#, c-format
+msgid "record with invalid length at %X/%X"
+msgstr "запис з невірною довжиною на %X/%X"
+
+#: xlogreader.c:1602
+#, c-format
+msgid "invalid compressed image at %X/%X, block %d"
+msgstr "невірно стиснутий образ в позиції %X/%X, блок %d"
+
diff --git a/src/bin/pg_rewind/po/zh_CN.po b/src/bin/pg_rewind/po/zh_CN.po
new file mode 100644
index 0000000..28d76c3
--- /dev/null
+++ b/src/bin/pg_rewind/po/zh_CN.po
@@ -0,0 +1,959 @@
+# LANGUAGE message translation file for pg_rewind
+# Copyright (C) 2019 PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2015.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pg_rewind (PostgreSQL) 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-08-14 05:48+0000\n"
+"PO-Revision-Date: 2021-08-15 10:00+0800\n"
+"Last-Translator: Jie Zhang <zhangjie2@fujitsu.com>\n"
+"Language-Team: Chinese (Simplified) <zhangjie2@fujitsu.com>\n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 1.5.7\n"
+
+#: ../../../src/common/logging.c:259
+#, c-format
+msgid "fatal: "
+msgstr "致命的: "
+
+#: ../../../src/common/logging.c:266
+#, c-format
+msgid "error: "
+msgstr "错误: "
+
+#: ../../../src/common/logging.c:273
+#, c-format
+msgid "warning: "
+msgstr "警告: "
+
+#: ../../common/fe_memutils.c:35 ../../common/fe_memutils.c:75
+#: ../../common/fe_memutils.c:98 ../../common/fe_memutils.c:162
+#, c-format
+msgid "out of memory\n"
+msgstr "内存不足\n"
+
+#: ../../common/fe_memutils.c:92 ../../common/fe_memutils.c:154
+#, c-format
+msgid "cannot duplicate null pointer (internal error)\n"
+msgstr "无法复制空指针 (内部错误)\n"
+
+#: ../../common/restricted_token.c:64
+#, c-format
+msgid "could not load library \"%s\": error code %lu"
+msgstr "无法加载库 \"%s\": 错误码 %lu"
+
+#: ../../common/restricted_token.c:73
+#, c-format
+msgid "cannot create restricted tokens on this platform: error code %lu"
+msgstr "无法为该平台创建受限制的令牌:错误码 %lu"
+
+#: ../../common/restricted_token.c:82
+#, c-format
+msgid "could not open process token: error code %lu"
+msgstr "无法打开进程令牌 (token): 错误码 %lu"
+
+#: ../../common/restricted_token.c:97
+#, c-format
+msgid "could not allocate SIDs: error code %lu"
+msgstr "无法分配SID: 错误码 %lu"
+
+#: ../../common/restricted_token.c:119
+#, c-format
+msgid "could not create restricted token: error code %lu"
+msgstr "无法创建受限令牌: 错误码为 %lu"
+
+#: ../../common/restricted_token.c:140
+#, c-format
+msgid "could not start process for command \"%s\": error code %lu"
+msgstr "无法为命令 \"%s\"创建进程: 错误码 %lu"
+
+#: ../../common/restricted_token.c:178
+#, c-format
+msgid "could not re-execute with restricted token: error code %lu"
+msgstr "无法使用受限令牌再次执行: 错误码 %lu"
+
+#: ../../common/restricted_token.c:194
+#, c-format
+msgid "could not get exit code from subprocess: error code %lu"
+msgstr "无法从子进程得到退出码: 错误码 %lu"
+
+#: ../../fe_utils/archive.c:53
+#, c-format
+msgid "cannot use restore_command with %%r placeholder"
+msgstr "无法对%%r占位符使用restore_command"
+
+#: ../../fe_utils/archive.c:74
+#, c-format
+msgid "unexpected file size for \"%s\": %lld instead of %lld"
+msgstr "\"%s\"的意外文件大小:%lld而不是%lld"
+
+#: ../../fe_utils/archive.c:85
+#, c-format
+msgid "could not open file \"%s\" restored from archive: %m"
+msgstr "无法打开从存档还原的文件\"%s\": %m"
+
+#: ../../fe_utils/archive.c:97 file_ops.c:417
+#, c-format
+msgid "could not stat file \"%s\": %m"
+msgstr "无法取文件 \"%s\" 的状态: %m"
+
+#: ../../fe_utils/archive.c:112
+#, c-format
+msgid "restore_command failed: %s"
+msgstr "restore_command失败: %s"
+
+#: ../../fe_utils/archive.c:121
+#, c-format
+msgid "could not restore file \"%s\" from archive"
+msgstr "无法从存档还原文件\"%s\""
+
+#: ../../fe_utils/recovery_gen.c:35 ../../fe_utils/recovery_gen.c:49
+#: ../../fe_utils/recovery_gen.c:77 ../../fe_utils/recovery_gen.c:100
+#: ../../fe_utils/recovery_gen.c:171 parsexlog.c:77 parsexlog.c:135
+#: parsexlog.c:195
+#, c-format
+msgid "out of memory"
+msgstr "内存不足"
+
+#: ../../fe_utils/recovery_gen.c:134 parsexlog.c:308
+#, c-format
+msgid "could not open file \"%s\": %m"
+msgstr "无法打开文件 \"%s\": %m"
+
+#: ../../fe_utils/recovery_gen.c:140
+#, c-format
+msgid "could not write to file \"%s\": %m"
+msgstr "无法写入文件 \"%s\": %m"
+
+#: ../../fe_utils/recovery_gen.c:152
+#, c-format
+msgid "could not create file \"%s\": %m"
+msgstr "无法创建文件 \"%s\": %m"
+
+#: file_ops.c:67
+#, c-format
+msgid "could not open target file \"%s\": %m"
+msgstr "无法打开目标文件\"%s\": %m"
+
+#: file_ops.c:81
+#, c-format
+msgid "could not close target file \"%s\": %m"
+msgstr "无法关闭目标文件\"%s\": %m"
+
+#: file_ops.c:101
+#, c-format
+msgid "could not seek in target file \"%s\": %m"
+msgstr "无法在目标文件\"%s\"中定位(seek): %m"
+
+#: file_ops.c:117
+#, c-format
+msgid "could not write file \"%s\": %m"
+msgstr "无法写入文件 \"%s\": %m"
+
+#: file_ops.c:150 file_ops.c:177
+#, c-format
+msgid "undefined file type for \"%s\""
+msgstr "不可识别的文件格式 \"%s\""
+
+#: file_ops.c:173
+#, c-format
+msgid "invalid action (CREATE) for regular file"
+msgstr "对常规文件无效的动作(CREATE)"
+
+#: file_ops.c:200
+#, c-format
+msgid "could not remove file \"%s\": %m"
+msgstr "无法删除文件 \"%s\": %m"
+
+#: file_ops.c:218
+#, c-format
+msgid "could not open file \"%s\" for truncation: %m"
+msgstr "无法打开文件\"%s\"用于截断:%m"
+
+#: file_ops.c:222
+#, c-format
+msgid "could not truncate file \"%s\" to %u: %m"
+msgstr "无法将文件\"%s\"截断为%u:%m"
+
+#: file_ops.c:238
+#, c-format
+msgid "could not create directory \"%s\": %m"
+msgstr "无法创建目录 \"%s\": %m"
+
+#: file_ops.c:252
+#, c-format
+msgid "could not remove directory \"%s\": %m"
+msgstr "无法删除目录 \"%s\": %m"
+
+#: file_ops.c:266
+#, c-format
+msgid "could not create symbolic link at \"%s\": %m"
+msgstr "无法在\"%s\"创建符号链接: %m"
+
+#: file_ops.c:280
+#, c-format
+msgid "could not remove symbolic link \"%s\": %m"
+msgstr "无法删除符号链接 \"%s\": %m"
+
+#: file_ops.c:326 file_ops.c:330
+#, c-format
+msgid "could not open file \"%s\" for reading: %m"
+msgstr "为了读取, 无法打开文件 \"%s\": %m"
+
+#: file_ops.c:341 local_source.c:107 parsexlog.c:346
+#, c-format
+msgid "could not read file \"%s\": %m"
+msgstr "无法读取文件 \"%s\": %m"
+
+#: file_ops.c:344 parsexlog.c:348
+#, c-format
+msgid "could not read file \"%s\": read %d of %zu"
+msgstr "无法读取文件\"%1$s\":读取了%3$zu中的%2$d"
+
+#: file_ops.c:388
+#, c-format
+msgid "could not open directory \"%s\": %m"
+msgstr "无法打开目录 \"%s\": %m"
+
+#: file_ops.c:446
+#, c-format
+msgid "could not read symbolic link \"%s\": %m"
+msgstr "无法读取符号链接 \"%s\": %m"
+
+#: file_ops.c:449
+#, c-format
+msgid "symbolic link \"%s\" target is too long"
+msgstr "符号链接 \"%s\" 目标超长"
+
+#: file_ops.c:464
+#, c-format
+msgid "\"%s\" is a symbolic link, but symbolic links are not supported on this platform"
+msgstr "\"%s\"是一个符号链接,但是这个平台上不支持平台链接"
+
+#: file_ops.c:471
+#, c-format
+msgid "could not read directory \"%s\": %m"
+msgstr "无法读取目录 \"%s\": %m"
+
+#: file_ops.c:475
+#, c-format
+msgid "could not close directory \"%s\": %m"
+msgstr "无法关闭目录 \"%s\": %m"
+
+#: filemap.c:237
+#, c-format
+msgid "data file \"%s\" in source is not a regular file"
+msgstr "源头的数据文件\"%s\"不是一个常规文件"
+
+#: filemap.c:242 filemap.c:275
+#, c-format
+msgid "duplicate source file \"%s\""
+msgstr "复制源文件\"%s\""
+
+#: filemap.c:330
+#, c-format
+msgid "unexpected page modification for non-regular file \"%s\""
+msgstr "非常规文件\"%s\"的意外页面修改"
+
+#: filemap.c:680 filemap.c:774
+#, c-format
+msgid "unknown file type for \"%s\""
+msgstr "\"%s\"的未知文件类型"
+
+#: filemap.c:707
+#, c-format
+msgid "file \"%s\" is of different type in source and target"
+msgstr "文件 \"%s\"在源和目标中的类型不同"
+
+#: filemap.c:779
+#, c-format
+msgid "could not decide what to do with file \"%s\""
+msgstr "无法决定如何处理文件\"%s\""
+
+#: libpq_source.c:128
+#, c-format
+msgid "could not clear search_path: %s"
+msgstr "无法清除search_path: %s"
+
+#: libpq_source.c:139
+#, c-format
+msgid "full_page_writes must be enabled in the source server"
+msgstr "源服务器中的full_page_writes必须被启用"
+
+#: libpq_source.c:150
+#, c-format
+msgid "could not prepare statement to fetch file contents: %s"
+msgstr "无法准备语句以获取文件内容: %s"
+
+#: libpq_source.c:169
+#, c-format
+msgid "error running query (%s) on source server: %s"
+msgstr "源服务器中有错误运行的查询(%s):%s"
+
+#: libpq_source.c:174
+#, c-format
+msgid "unexpected result set from query"
+msgstr "从查询得到意料之外的结果集"
+
+#: libpq_source.c:196
+#, c-format
+msgid "error running query (%s) in source server: %s"
+msgstr "源服务器中有错误运行的查询(%s):%s"
+
+#: libpq_source.c:217
+#, c-format
+msgid "unrecognized result \"%s\" for current WAL insert location"
+msgstr "当前WAL插入位置的未识别结果\"%s\""
+
+#: libpq_source.c:268
+#, c-format
+msgid "could not fetch file list: %s"
+msgstr "无法取得文件列表:%s"
+
+#: libpq_source.c:273
+#, c-format
+msgid "unexpected result set while fetching file list"
+msgstr "在取得文件列表时得到意料之外的结果集"
+
+#: libpq_source.c:435
+#, c-format
+msgid "could not send query: %s"
+msgstr "无法发送查询:%s"
+
+#: libpq_source.c:438
+#, c-format
+msgid "could not set libpq connection to single row mode"
+msgstr "无法设置libpq连接为单行模式"
+
+#: libpq_source.c:468
+#, c-format
+msgid "unexpected result while fetching remote files: %s"
+msgstr "在取得远程文件时得到意料之外的结果:%s"
+
+#: libpq_source.c:473
+#, c-format
+msgid "received more data chunks than requested"
+msgstr "收到的数据块比请求的多"
+
+#: libpq_source.c:477
+#, c-format
+msgid "unexpected result set size while fetching remote files"
+msgstr "在取得远程文件时得到意料之外的结果集大小"
+
+#: libpq_source.c:483
+#, c-format
+msgid "unexpected data types in result set while fetching remote files: %u %u %u"
+msgstr "在取得远程文件时结果集中有意料之外的数据类型:%u %u %u"
+
+#: libpq_source.c:491
+#, c-format
+msgid "unexpected result format while fetching remote files"
+msgstr "在取得远程文件时得到意料之外的结果格式"
+
+#: libpq_source.c:497
+#, c-format
+msgid "unexpected null values in result while fetching remote files"
+msgstr "在取得远程文件时结果中有意料之外的空值"
+
+#: libpq_source.c:501
+#, c-format
+msgid "unexpected result length while fetching remote files"
+msgstr "在取得远程文件时得到意料之外的结果长度"
+
+#: libpq_source.c:534
+#, c-format
+msgid "received data for file \"%s\", when requested for \"%s\""
+msgstr "当为文件\"%2$s\"请求时,接收到文件\"%1$s\"的数据"
+
+#: libpq_source.c:538
+#, c-format
+msgid "received data at offset %lld of file \"%s\", when requested for offset %lld"
+msgstr "当请求偏移量%3$lld时,在文件\"%2$s\"的偏移量%1$lld处接收到数据"
+
+#: libpq_source.c:550
+#, c-format
+msgid "received more than requested for file \"%s\""
+msgstr "收到的文件\"%s\"比要求的多"
+
+#: libpq_source.c:563
+#, c-format
+msgid "unexpected number of data chunks received"
+msgstr "接收到意外的数据块数"
+
+#: libpq_source.c:606
+#, c-format
+msgid "could not fetch remote file \"%s\": %s"
+msgstr "无法取得远程文件\"%s\": %s"
+
+#: libpq_source.c:611
+#, c-format
+msgid "unexpected result set while fetching remote file \"%s\""
+msgstr "在取得远程文件\"%s\"时得到意料之外的结果集"
+
+#: local_source.c:86
+#, c-format
+msgid "could not open source file \"%s\": %m"
+msgstr "无法打开源文件\"%s\": %m"
+
+#: local_source.c:90
+#, c-format
+msgid "could not seek in source file: %m"
+msgstr "无法在源文件中定位(seek):%m"
+
+#: local_source.c:109
+#, c-format
+msgid "unexpected EOF while reading file \"%s\""
+msgstr "读取文件\"%s\"时遇到意料之外的EOF"
+
+#: local_source.c:116
+#, c-format
+msgid "could not close file \"%s\": %m"
+msgstr "无法关闭文件 \"%s\": %m"
+
+#: parsexlog.c:89 parsexlog.c:142
+#, c-format
+msgid "could not read WAL record at %X/%X: %s"
+msgstr "无法读取%X/%X处的WAL记录:%s"
+
+#: parsexlog.c:93 parsexlog.c:145
+#, c-format
+msgid "could not read WAL record at %X/%X"
+msgstr "无法读取%X/%X处的WAL记录"
+
+#: parsexlog.c:208
+#, c-format
+msgid "could not find previous WAL record at %X/%X: %s"
+msgstr "无法在%X/%X找到前一个WAL记录:%s"
+
+#: parsexlog.c:212
+#, c-format
+msgid "could not find previous WAL record at %X/%X"
+msgstr "无法在%X/%X找到前一个WAL记录"
+
+#: parsexlog.c:337
+#, c-format
+msgid "could not seek in file \"%s\": %m"
+msgstr "无法在文件\"%s\"进行查找: %m"
+
+#: parsexlog.c:429
+#, c-format
+msgid "WAL record modifies a relation, but record type is not recognized: lsn: %X/%X, rmgr: %s, info: %02X"
+msgstr "WAL记录修改了一个关系,但是记录类型无法识别: lsn: %X/%X, rmgr: %s, info: %02X"
+
+#: pg_rewind.c:84
+#, c-format
+msgid ""
+"%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n"
+"\n"
+msgstr ""
+"%s用一个PostgreSQL集簇的另一个拷贝重新同步了该集簇。\n"
+"\n"
+
+#: pg_rewind.c:85
+#, c-format
+msgid ""
+"Usage:\n"
+" %s [OPTION]...\n"
+"\n"
+msgstr ""
+"用法:\n"
+" %s [选项]...\n"
+"\n"
+
+#: pg_rewind.c:86
+#, c-format
+msgid "Options:\n"
+msgstr "选项:\n"
+
+#: pg_rewind.c:87
+#, c-format
+msgid ""
+" -c, --restore-target-wal use restore_command in target configuration to\n"
+" retrieve WAL files from archives\n"
+msgstr ""
+" -c, --restore-target-wal 在目标配置中使用restore_command\n"
+" 从存档中检索WAL文件\n"
+
+#: pg_rewind.c:89
+#, c-format
+msgid " -D, --target-pgdata=DIRECTORY existing data directory to modify\n"
+msgstr " -D, --target-pgdata=DIRECTORY 已有的要修改的数据目录\n"
+
+#: pg_rewind.c:90
+#, c-format
+msgid " --source-pgdata=DIRECTORY source data directory to synchronize with\n"
+msgstr " --source-pgdata=DIRECTORY 要与之同步的源数据目录\n"
+
+#: pg_rewind.c:91
+#, c-format
+msgid " --source-server=CONNSTR source server to synchronize with\n"
+msgstr " --source-server=CONNSTR 要与之同步的源服务器\n"
+
+#: pg_rewind.c:92
+#, c-format
+msgid " -n, --dry-run stop before modifying anything\n"
+msgstr " -n, --dry-run 在修改任何东西之前停止\n"
+
+#: pg_rewind.c:93
+#, c-format
+msgid ""
+" -N, --no-sync do not wait for changes to be written\n"
+" safely to disk\n"
+msgstr ""
+" -N, --no-sync 不用等待变化安全\n"
+" 写入磁盘\n"
+
+#: pg_rewind.c:95
+#, c-format
+msgid " -P, --progress write progress messages\n"
+msgstr " -P, --progress 写出进度消息\n"
+
+#: pg_rewind.c:96
+#, c-format
+msgid ""
+" -R, --write-recovery-conf write configuration for replication\n"
+" (requires --source-server)\n"
+msgstr ""
+" -R, --write-recovery-conf 为复制写配置文\n"
+" (requires --source-server)\n"
+
+#: pg_rewind.c:98
+#, c-format
+msgid " --debug write a lot of debug messages\n"
+msgstr " --debug 写出很多调试消息\n"
+
+#: pg_rewind.c:99
+#, c-format
+msgid " --no-ensure-shutdown do not automatically fix unclean shutdown\n"
+msgstr " --no-ensure-shutdown 不要自动修复不干净的关机\n"
+
+#: pg_rewind.c:100
+#, c-format
+msgid " -V, --version output version information, then exit\n"
+msgstr " -V, --version 输出版本信息,然后退出\n"
+
+#: pg_rewind.c:101
+#, c-format
+msgid " -?, --help show this help, then exit\n"
+msgstr " -?, --help 显示本帮助,然后退出\n"
+
+#: pg_rewind.c:102
+#, c-format
+msgid ""
+"\n"
+"Report bugs to <%s>.\n"
+msgstr ""
+"\n"
+"臭虫报告至<%s>.\n"
+
+#: pg_rewind.c:103
+#, c-format
+msgid "%s home page: <%s>\n"
+msgstr "%s 主页: <%s>\n"
+
+#: pg_rewind.c:164 pg_rewind.c:213 pg_rewind.c:220 pg_rewind.c:227
+#: pg_rewind.c:234 pg_rewind.c:242
+#, c-format
+msgid "Try \"%s --help\" for more information.\n"
+msgstr "请用 \"%s --help\" 获取更多的信息.\n"
+
+#: pg_rewind.c:212
+#, c-format
+msgid "no source specified (--source-pgdata or --source-server)"
+msgstr "没有指定源 (--source-pgdata 或者 --source-server)"
+
+#: pg_rewind.c:219
+#, c-format
+msgid "only one of --source-pgdata or --source-server can be specified"
+msgstr "只能指定--source-pgdata和--source-server这两个选项之一"
+
+#: pg_rewind.c:226
+#, c-format
+msgid "no target data directory specified (--target-pgdata)"
+msgstr "没有指定目标数据目录 (--target-pgdata)"
+
+#: pg_rewind.c:233
+#, c-format
+msgid "no source server information (--source-server) specified for --write-recovery-conf"
+msgstr "没有为--write-recovery-conf指定源服务器信息(--source-server)"
+
+#: pg_rewind.c:240
+#, c-format
+msgid "too many command-line arguments (first is \"%s\")"
+msgstr "命令行参数太多 (第一个是 \"%s\")"
+
+#: pg_rewind.c:255
+#, c-format
+msgid "cannot be executed by \"root\""
+msgstr "不能由\"root\"执行"
+
+#: pg_rewind.c:256
+#, c-format
+msgid "You must run %s as the PostgreSQL superuser.\n"
+msgstr "您现在作为PostgreSQL超级用户运行%s.\n"
+
+#: pg_rewind.c:267
+#, c-format
+msgid "could not read permissions of directory \"%s\": %m"
+msgstr "没有读取目录 \"%s\" 的权限: %m"
+
+#: pg_rewind.c:287
+#, c-format
+msgid "%s"
+msgstr "%s"
+
+#: pg_rewind.c:290
+#, c-format
+msgid "connected to server"
+msgstr "已连接服务器"
+
+#: pg_rewind.c:337
+#, c-format
+msgid "source and target cluster are on the same timeline"
+msgstr "源集簇和目标集簇处于同一时间线"
+
+#: pg_rewind.c:346
+#, c-format
+msgid "servers diverged at WAL location %X/%X on timeline %u"
+msgstr "服务器在时间线%3$u上的WAL位置%1$X/%2$X处发生了分歧"
+
+#: pg_rewind.c:394
+#, c-format
+msgid "no rewind required"
+msgstr "不需要倒带(rewind)"
+
+#: pg_rewind.c:403
+#, c-format
+msgid "rewinding from last common checkpoint at %X/%X on timeline %u"
+msgstr "从时间线%3$u上%1$X/%2$X处的最后一个普通检查点倒带"
+
+#: pg_rewind.c:413
+#, c-format
+msgid "reading source file list"
+msgstr "读取源文件列表"
+
+#: pg_rewind.c:417
+#, c-format
+msgid "reading target file list"
+msgstr "读取目标文件列表"
+
+#: pg_rewind.c:426
+#, c-format
+msgid "reading WAL in target"
+msgstr "读取目标中的WAL"
+
+#: pg_rewind.c:447
+#, c-format
+msgid "need to copy %lu MB (total source directory size is %lu MB)"
+msgstr "需要复制 %lu MB(整个源目录的大小是 %lu MB)"
+
+#: pg_rewind.c:465
+#, c-format
+msgid "syncing target data directory"
+msgstr "正在同步目标数据目录"
+
+#: pg_rewind.c:481
+#, c-format
+msgid "Done!"
+msgstr "完成!"
+
+#: pg_rewind.c:564
+#, c-format
+msgid "no action decided for file \"%s\""
+msgstr "未决定对文件\"%s\"执行任何操作"
+
+#: pg_rewind.c:596
+#, c-format
+msgid "source system was modified while pg_rewind was running"
+msgstr "pg_rewind运行时修改了源系统"
+
+#: pg_rewind.c:600
+#, c-format
+msgid "creating backup label and updating control file"
+msgstr "正在创建备份标签并且更新控制文件"
+
+#: pg_rewind.c:650
+#, c-format
+msgid "source system was in unexpected state at end of rewind"
+msgstr "源系统在rewind结束时处于意外状态"
+
+#: pg_rewind.c:681
+#, c-format
+msgid "source and target clusters are from different systems"
+msgstr "源集簇和目标集簇来自不同的系统"
+
+#: pg_rewind.c:689
+#, c-format
+msgid "clusters are not compatible with this version of pg_rewind"
+msgstr "集簇与这个pg_rewind的版本不兼容"
+
+#: pg_rewind.c:699
+#, c-format
+msgid "target server needs to use either data checksums or \"wal_log_hints = on\""
+msgstr "目标服务器需要使用数据校验和或者让\"wal_log_hints = on\""
+
+#: pg_rewind.c:710
+#, c-format
+msgid "target server must be shut down cleanly"
+msgstr "目标服务器必须被干净地关闭"
+
+#: pg_rewind.c:720
+#, c-format
+msgid "source data directory must be shut down cleanly"
+msgstr "源数据目录必须被干净地关闭"
+
+#: pg_rewind.c:772
+#, c-format
+msgid "%*s/%s kB (%d%%) copied"
+msgstr "已复制%*s/%s kB (%d%%)"
+
+#: pg_rewind.c:835
+#, c-format
+msgid "invalid control file"
+msgstr "无效的控制文件"
+
+#: pg_rewind.c:919
+#, c-format
+msgid "could not find common ancestor of the source and target cluster's timelines"
+msgstr "无法找到源集簇和目标集簇的时间线的共同祖先"
+
+#: pg_rewind.c:960
+#, c-format
+msgid "backup label buffer too small"
+msgstr "备份标签缓冲太小"
+
+#: pg_rewind.c:983
+#, c-format
+msgid "unexpected control file CRC"
+msgstr "意料之外的控制文件CRC"
+
+#: pg_rewind.c:995
+#, c-format
+msgid "unexpected control file size %d, expected %d"
+msgstr "意料之外的控制文件大小%d,应该是%d"
+
+#: pg_rewind.c:1004
+#, c-format
+msgid "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d byte"
+msgid_plural "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d bytes"
+msgstr[0] "WAL段大小必须是1 MB到1 GB之间的2的幂,但控制文件指定了%d字节"
+msgstr[1] "WAL段大小必须是1 MB到1 GB之间的2的幂,但控制文件指定了%d字节"
+
+#: pg_rewind.c:1043 pg_rewind.c:1101
+#, c-format
+msgid ""
+"The program \"%s\" is needed by %s but was not found in the\n"
+"same directory as \"%s\".\n"
+"Check your installation."
+msgstr ""
+"%2$s需要程序\"%1$s\"\n"
+"但在与\"%3$s\"相同的目录中找不到该程序.\n"
+"检查您的安装."
+
+#: pg_rewind.c:1048 pg_rewind.c:1106
+#, c-format
+msgid ""
+"The program \"%s\" was found by \"%s\"\n"
+"but was not the same version as %s.\n"
+"Check your installation."
+msgstr ""
+"程序\"%s\"是由\"%s\"找到的\n"
+"但与%s的版本不同.\n"
+"检查您的安装."
+
+#: pg_rewind.c:1069
+#, c-format
+msgid "restore_command is not set in the target cluster"
+msgstr "目标群集中未设置restore_command"
+
+#: pg_rewind.c:1112
+#, c-format
+msgid "executing \"%s\" for target server to complete crash recovery"
+msgstr "对目标服务器执行\"%s\"以完成崩溃恢复"
+
+#: pg_rewind.c:1132
+#, c-format
+msgid "postgres single-user mode in target cluster failed"
+msgstr "目标群集中的postgres单用户模式失败"
+
+#: pg_rewind.c:1133
+#, c-format
+msgid "Command was: %s"
+msgstr "命令是: %s"
+
+#: timeline.c:75 timeline.c:81
+#, c-format
+msgid "syntax error in history file: %s"
+msgstr "历史文件中的语法错误: %s"
+
+#: timeline.c:76
+#, c-format
+msgid "Expected a numeric timeline ID."
+msgstr "期望一个数字 timeline ID."
+
+#: timeline.c:82
+#, c-format
+msgid "Expected a write-ahead log switchpoint location."
+msgstr "期望一个预写日志切换点位置."
+
+#: timeline.c:87
+#, c-format
+msgid "invalid data in history file: %s"
+msgstr "历史文件中的无效数据: %s"
+
+#: timeline.c:88
+#, c-format
+msgid "Timeline IDs must be in increasing sequence."
+msgstr "TimeLine ID 必须为递增序列."
+
+#: timeline.c:108
+#, c-format
+msgid "invalid data in history file"
+msgstr "历史文件中有无效数据"
+
+#: timeline.c:109
+#, c-format
+msgid "Timeline IDs must be less than child timeline's ID."
+msgstr "Timeline ID 必须小于子 timeline 的 ID."
+
+#: xlogreader.c:349
+#, c-format
+msgid "invalid record offset at %X/%X"
+msgstr "%X/%X处有无效的记录偏移量"
+
+#: xlogreader.c:357
+#, c-format
+msgid "contrecord is requested by %X/%X"
+msgstr "%X/%X位置处要求继续记录"
+
+#: xlogreader.c:398 xlogreader.c:695
+#, c-format
+msgid "invalid record length at %X/%X: wanted %u, got %u"
+msgstr "%X/%X处有无效记录长度: 应该是%u, 但实际是%u"
+
+#: xlogreader.c:422
+#, c-format
+msgid "record length %u at %X/%X too long"
+msgstr "%2$X/%3$X处的记录长度%1$u太长"
+
+#: xlogreader.c:453
+#, c-format
+msgid "there is no contrecord flag at %X/%X"
+msgstr "在%X/%X处没有继续记录标志"
+
+#: xlogreader.c:466
+#, c-format
+msgid "invalid contrecord length %u (expected %lld) at %X/%X"
+msgstr "%3$X/%4$X处有无效的继续记录长度%1$u(应为 %2$lld)"
+
+#: xlogreader.c:703
+#, c-format
+msgid "invalid resource manager ID %u at %X/%X"
+msgstr "%2$X/%3$X处有无效的资源管理器 ID %1$u"
+
+#: xlogreader.c:716 xlogreader.c:732
+#, c-format
+msgid "record with incorrect prev-link %X/%X at %X/%X"
+msgstr "具有不正确向前链接%X/%X的记录出现在%X/%X"
+
+#: xlogreader.c:768
+#, c-format
+msgid "incorrect resource manager data checksum in record at %X/%X"
+msgstr "在%X/%X处的记录中的资源管理器数据校验和不正确"
+
+#: xlogreader.c:805
+#, c-format
+msgid "invalid magic number %04X in log segment %s, offset %u"
+msgstr "在日志段%2$s的偏移量%3$u处有无效的magic号%1$04X"
+
+#: xlogreader.c:819 xlogreader.c:860
+#, c-format
+msgid "invalid info bits %04X in log segment %s, offset %u"
+msgstr "在日志段%2$s的偏移量%3$u处有无效的info位%1$04X"
+
+#: xlogreader.c:834
+#, c-format
+msgid "WAL file is from different database system: WAL file database system identifier is %llu, pg_control database system identifier is %llu"
+msgstr "WAL文件来自于不同的数据库系统:WAL文件数据库系统标识符是%llu,pg_control数据库系统标识符是%llu"
+
+#: xlogreader.c:842
+#, c-format
+msgid "WAL file is from different database system: incorrect segment size in page header"
+msgstr "WAL文件来自于不同的数据库系统:页头部中有不正确的段大小"
+
+#: xlogreader.c:848
+#, c-format
+msgid "WAL file is from different database system: incorrect XLOG_BLCKSZ in page header"
+msgstr "WAL文件来自于不同的数据库系统:页头部中有不正确的XLOG_BLCKSZ"
+
+#: xlogreader.c:879
+#, c-format
+msgid "unexpected pageaddr %X/%X in log segment %s, offset %u"
+msgstr "在日志段%3$s的偏移量%4$u处有意料之外的pageaddr %1$X/%2$X"
+
+#: xlogreader.c:904
+#, c-format
+msgid "out-of-sequence timeline ID %u (after %u) in log segment %s, offset %u"
+msgstr "在日志段%3$s的偏移量%4$u处有失序的时间线 ID %1$u(在%2$u之后)"
+
+#: xlogreader.c:1249
+#, c-format
+msgid "out-of-order block_id %u at %X/%X"
+msgstr "在%2$X/%3$X处有无序的block_id %1$u"
+
+#: xlogreader.c:1271
+#, c-format
+msgid "BKPBLOCK_HAS_DATA set, but no data included at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA已被设置,但是在%X/%X处没有包括数据"
+
+#: xlogreader.c:1278
+#, c-format
+msgid "BKPBLOCK_HAS_DATA not set, but data length is %u at %X/%X"
+msgstr "BKPBLOCK_HAS_DATA没有被设置,但是在%2$X/%3$X处的数据长度为%1$u"
+
+#: xlogreader.c:1314
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE set, but hole offset %u length %u block image length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE已被设置,但是%4$X/%5$X处记录了洞偏移量为%1$u、长度为%2$u、块映像长度为%3$u"
+
+#: xlogreader.c:1330
+#, c-format
+msgid "BKPIMAGE_HAS_HOLE not set, but hole offset %u length %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE没有被设置,但是%3$X/%4$X处记录了洞偏移量为%1$u、长度为%2$u"
+
+#: xlogreader.c:1345
+#, c-format
+msgid "BKPIMAGE_IS_COMPRESSED set, but block image length %u at %X/%X"
+msgstr "BKPIMAGE_IS_COMPRESSED已被设置,但是%2$X/%3$X处记录的块映像长度为%1$u"
+
+#: xlogreader.c:1360
+#, c-format
+msgid "neither BKPIMAGE_HAS_HOLE nor BKPIMAGE_IS_COMPRESSED set, but block image length is %u at %X/%X"
+msgstr "BKPIMAGE_HAS_HOLE和BKPIMAGE_IS_COMPRESSED都没有被设置,但是%2$X/%3$X处记录的块映像长度为%1$u"
+
+#: xlogreader.c:1376
+#, c-format
+msgid "BKPBLOCK_SAME_REL set but no previous rel at %X/%X"
+msgstr "设置了BKPBLOCK_SAME_REL,但是在%X/%X位置没有记录先前的关系"
+
+#: xlogreader.c:1388
+#, c-format
+msgid "invalid block_id %u at %X/%X"
+msgstr "%2$X/%3$X处的block_id %1$u无效"
+
+#: xlogreader.c:1475
+#, c-format
+msgid "record with invalid length at %X/%X"
+msgstr "在%X/%X处的记录的长度无效"
+
+#: xlogreader.c:1564
+#, c-format
+msgid "invalid compressed image at %X/%X, block %d"
+msgstr "%X/%X处是块%d的无效压缩映像"
diff --git a/src/bin/pg_rewind/rewind_source.h b/src/bin/pg_rewind/rewind_source.h
new file mode 100644
index 0000000..2da92db
--- /dev/null
+++ b/src/bin/pg_rewind/rewind_source.h
@@ -0,0 +1,73 @@
+/*-------------------------------------------------------------------------
+ *
+ * rewind_source.h
+ * Abstraction for fetching from source server.
+ *
+ * The source server can be either a libpq connection to a live system,
+ * or a local data directory. The 'rewind_source' struct abstracts the
+ * operations to fetch data from the source system, so that the rest of
+ * the code doesn't need to care what kind of a source its dealing with.
+ *
+ * Copyright (c) 2013-2021, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef REWIND_SOURCE_H
+#define REWIND_SOURCE_H
+
+#include "access/xlogdefs.h"
+#include "file_ops.h"
+#include "filemap.h"
+#include "libpq-fe.h"
+
+typedef struct rewind_source
+{
+ /*
+ * Traverse all files in the source data directory, and call 'callback' on
+ * each file.
+ */
+ void (*traverse_files) (struct rewind_source *,
+ process_file_callback_t callback);
+
+ /*
+ * Fetch a single file into a malloc'd buffer. The file size is returned
+ * in *filesize. The returned buffer is always zero-terminated, which is
+ * handy for text files.
+ */
+ char *(*fetch_file) (struct rewind_source *, const char *path,
+ size_t *filesize);
+
+ /*
+ * Request to fetch (part of) a file in the source system, specified by an
+ * offset and length, and write it to the same offset in the corresponding
+ * target file. The source implementation may queue up the request and
+ * execute it later when convenient. Call finish_fetch() to flush the
+ * queue and execute all requests.
+ */
+ void (*queue_fetch_range) (struct rewind_source *, const char *path,
+ off_t offset, size_t len);
+
+ /*
+ * Execute all requests queued up with queue_fetch_range().
+ */
+ void (*finish_fetch) (struct rewind_source *);
+
+ /*
+ * Get the current WAL insert position in the source system.
+ */
+ XLogRecPtr (*get_current_wal_insert_lsn) (struct rewind_source *);
+
+ /*
+ * Free this rewind_source object.
+ */
+ void (*destroy) (struct rewind_source *);
+
+} rewind_source;
+
+/* in libpq_source.c */
+extern rewind_source *init_libpq_source(PGconn *conn);
+
+/* in local_source.c */
+extern rewind_source *init_local_source(const char *datadir);
+
+#endif /* FETCH_H */
diff --git a/src/bin/pg_rewind/t/001_basic.pl b/src/bin/pg_rewind/t/001_basic.pl
new file mode 100644
index 0000000..d636f35
--- /dev/null
+++ b/src/bin/pg_rewind/t/001_basic.pl
@@ -0,0 +1,194 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+use strict;
+use warnings;
+use TestLib;
+use Test::More tests => 23;
+
+use FindBin;
+use lib $FindBin::RealBin;
+
+use RewindTest;
+
+sub run_test
+{
+ my $test_mode = shift;
+
+ RewindTest::setup_cluster($test_mode);
+ RewindTest::start_primary();
+
+ # Create a test table and insert a row in primary.
+ primary_psql("CREATE TABLE tbl1 (d text)");
+ primary_psql("INSERT INTO tbl1 VALUES ('in primary')");
+
+ # This test table will be used to test truncation, i.e. the table
+ # is extended in the old primary after promotion
+ primary_psql("CREATE TABLE trunc_tbl (d text)");
+ primary_psql("INSERT INTO trunc_tbl VALUES ('in primary')");
+
+ # This test table will be used to test the "copy-tail" case, i.e. the
+ # table is truncated in the old primary after promotion
+ primary_psql("CREATE TABLE tail_tbl (id integer, d text)");
+ primary_psql("INSERT INTO tail_tbl VALUES (0, 'in primary')");
+
+ # This test table is dropped in the old primary after promotion.
+ primary_psql("CREATE TABLE drop_tbl (d text)");
+ primary_psql("INSERT INTO drop_tbl VALUES ('in primary')");
+
+ primary_psql("CHECKPOINT");
+
+ RewindTest::create_standby($test_mode);
+
+ # Insert additional data on primary that will be replicated to standby
+ primary_psql("INSERT INTO tbl1 values ('in primary, before promotion')");
+ primary_psql(
+ "INSERT INTO trunc_tbl values ('in primary, before promotion')");
+ primary_psql(
+ "INSERT INTO tail_tbl SELECT g, 'in primary, before promotion: ' || g FROM generate_series(1, 10000) g"
+ );
+
+ primary_psql('CHECKPOINT');
+
+ RewindTest::promote_standby();
+
+ # Insert a row in the old primary. This causes the primary and standby
+ # to have "diverged", it's no longer possible to just apply the
+ # standy's logs over primary directory - you need to rewind.
+ primary_psql("INSERT INTO tbl1 VALUES ('in primary, after promotion')");
+
+ # Also insert a new row in the standby, which won't be present in the
+ # old primary.
+ standby_psql("INSERT INTO tbl1 VALUES ('in standby, after promotion')");
+
+ # Insert enough rows to trunc_tbl to extend the file. pg_rewind should
+ # truncate it back to the old size.
+ primary_psql(
+ "INSERT INTO trunc_tbl SELECT 'in primary, after promotion: ' || g FROM generate_series(1, 10000) g"
+ );
+
+ # Truncate tail_tbl. pg_rewind should copy back the truncated part
+ # (We cannot use an actual TRUNCATE command here, as that creates a
+ # whole new relfilenode)
+ primary_psql("DELETE FROM tail_tbl WHERE id > 10");
+ primary_psql("VACUUM tail_tbl");
+
+ # Drop drop_tbl. pg_rewind should copy it back.
+ primary_psql(
+ "insert into drop_tbl values ('in primary, after promotion')");
+ primary_psql("DROP TABLE drop_tbl");
+
+ # Before running pg_rewind, do a couple of extra tests with several
+ # option combinations. As the code paths taken by those tests
+ # do not change for the "local" and "remote" modes, just run them
+ # in "local" mode for simplicity's sake.
+ if ($test_mode eq 'local')
+ {
+ my $primary_pgdata = $node_primary->data_dir;
+ my $standby_pgdata = $node_standby->data_dir;
+
+ # First check that pg_rewind fails if the target cluster is
+ # not stopped as it fails to start up for the forced recovery
+ # step.
+ command_fails(
+ [
+ 'pg_rewind', '--debug',
+ '--source-pgdata', $standby_pgdata,
+ '--target-pgdata', $primary_pgdata,
+ '--no-sync'
+ ],
+ 'pg_rewind with running target');
+
+ # Again with --no-ensure-shutdown, which should equally fail.
+ # This time pg_rewind complains without attempting to perform
+ # recovery once.
+ command_fails(
+ [
+ 'pg_rewind', '--debug',
+ '--source-pgdata', $standby_pgdata,
+ '--target-pgdata', $primary_pgdata,
+ '--no-sync', '--no-ensure-shutdown'
+ ],
+ 'pg_rewind --no-ensure-shutdown with running target');
+
+ # Stop the target, and attempt to run with a local source
+ # still running. This fails as pg_rewind requires to have
+ # a source cleanly stopped.
+ $node_primary->stop;
+ command_fails(
+ [
+ 'pg_rewind', '--debug',
+ '--source-pgdata', $standby_pgdata,
+ '--target-pgdata', $primary_pgdata,
+ '--no-sync', '--no-ensure-shutdown'
+ ],
+ 'pg_rewind with unexpected running source');
+
+ # Stop the target cluster cleanly, and run again pg_rewind
+ # with --dry-run mode. If anything gets generated in the data
+ # folder, the follow-up run of pg_rewind will most likely fail,
+ # so keep this test as the last one of this subset.
+ $node_standby->stop;
+ command_ok(
+ [
+ 'pg_rewind', '--debug',
+ '--source-pgdata', $standby_pgdata,
+ '--target-pgdata', $primary_pgdata,
+ '--no-sync', '--dry-run'
+ ],
+ 'pg_rewind --dry-run');
+
+ # Both clusters need to be alive moving forward.
+ $node_standby->start;
+ $node_primary->start;
+ }
+
+ RewindTest::run_pg_rewind($test_mode);
+
+ check_query(
+ 'SELECT * FROM tbl1',
+ qq(in primary
+in primary, before promotion
+in standby, after promotion
+),
+ 'table content');
+
+ check_query(
+ 'SELECT * FROM trunc_tbl',
+ qq(in primary
+in primary, before promotion
+),
+ 'truncation');
+
+ check_query(
+ 'SELECT count(*) FROM tail_tbl',
+ qq(10001
+),
+ 'tail-copy');
+
+ check_query(
+ 'SELECT * FROM drop_tbl',
+ qq(in primary
+),
+ 'drop');
+
+ # Permissions on PGDATA should be default
+ SKIP:
+ {
+ skip "unix-style permissions not supported on Windows", 1
+ if ($windows_os);
+
+ ok(check_mode_recursive($node_primary->data_dir(), 0700, 0600),
+ 'check PGDATA permissions');
+ }
+
+ RewindTest::clean_rewind_test();
+ return;
+}
+
+# Run the test in both modes
+run_test('local');
+run_test('remote');
+run_test('archive');
+
+exit(0);
diff --git a/src/bin/pg_rewind/t/002_databases.pl b/src/bin/pg_rewind/t/002_databases.pl
new file mode 100644
index 0000000..72c4b22
--- /dev/null
+++ b/src/bin/pg_rewind/t/002_databases.pl
@@ -0,0 +1,77 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+use strict;
+use warnings;
+use TestLib;
+use Test::More tests => 7;
+
+use FindBin;
+use lib $FindBin::RealBin;
+
+use RewindTest;
+
+sub run_test
+{
+ my $test_mode = shift;
+
+ RewindTest::setup_cluster($test_mode, ['-g']);
+ RewindTest::start_primary();
+
+ # Create a database in primary with a table.
+ primary_psql('CREATE DATABASE inprimary');
+ primary_psql('CREATE TABLE inprimary_tab (a int)', 'inprimary');
+
+ RewindTest::create_standby($test_mode);
+
+ # Create another database with another table, the creation is
+ # replicated to the standby.
+ primary_psql('CREATE DATABASE beforepromotion');
+ primary_psql('CREATE TABLE beforepromotion_tab (a int)',
+ 'beforepromotion');
+
+ RewindTest::promote_standby();
+
+ # Create databases in the old primary and the new promoted standby.
+ primary_psql('CREATE DATABASE primary_afterpromotion');
+ primary_psql('CREATE TABLE primary_promotion_tab (a int)',
+ 'primary_afterpromotion');
+ standby_psql('CREATE DATABASE standby_afterpromotion');
+ standby_psql('CREATE TABLE standby_promotion_tab (a int)',
+ 'standby_afterpromotion');
+
+ # The clusters are now diverged.
+
+ RewindTest::run_pg_rewind($test_mode);
+
+ # Check that the correct databases are present after pg_rewind.
+ check_query(
+ 'SELECT datname FROM pg_database ORDER BY 1',
+ qq(beforepromotion
+inprimary
+postgres
+standby_afterpromotion
+template0
+template1
+),
+ 'database names');
+
+ # Permissions on PGDATA should have group permissions
+ SKIP:
+ {
+ skip "unix-style permissions not supported on Windows", 1
+ if ($windows_os);
+
+ ok(check_mode_recursive($node_primary->data_dir(), 0750, 0640),
+ 'check PGDATA permissions');
+ }
+
+ RewindTest::clean_rewind_test();
+ return;
+}
+
+# Run the test in both modes.
+run_test('local');
+run_test('remote');
+
+exit(0);
diff --git a/src/bin/pg_rewind/t/003_extrafiles.pl b/src/bin/pg_rewind/t/003_extrafiles.pl
new file mode 100644
index 0000000..672c5e5
--- /dev/null
+++ b/src/bin/pg_rewind/t/003_extrafiles.pl
@@ -0,0 +1,106 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+# Test how pg_rewind reacts to extra files and directories in the data dirs.
+
+use strict;
+use warnings;
+use TestLib;
+use Test::More tests => 5;
+
+use File::Find;
+
+use FindBin;
+use lib $FindBin::RealBin;
+
+use RewindTest;
+
+
+sub run_test
+{
+ my $test_mode = shift;
+
+ RewindTest::setup_cluster($test_mode);
+ RewindTest::start_primary();
+
+ my $test_primary_datadir = $node_primary->data_dir;
+
+ # Create a subdir and files that will be present in both
+ mkdir "$test_primary_datadir/tst_both_dir";
+ append_to_file "$test_primary_datadir/tst_both_dir/both_file1",
+ "in both1";
+ append_to_file "$test_primary_datadir/tst_both_dir/both_file2",
+ "in both2";
+ mkdir "$test_primary_datadir/tst_both_dir/both_subdir/";
+ append_to_file
+ "$test_primary_datadir/tst_both_dir/both_subdir/both_file3",
+ "in both3";
+
+ RewindTest::create_standby($test_mode);
+
+ # Create different subdirs and files in primary and standby
+ my $test_standby_datadir = $node_standby->data_dir;
+
+ mkdir "$test_standby_datadir/tst_standby_dir";
+ append_to_file "$test_standby_datadir/tst_standby_dir/standby_file1",
+ "in standby1";
+ append_to_file "$test_standby_datadir/tst_standby_dir/standby_file2",
+ "in standby2";
+ append_to_file
+ "$test_standby_datadir/tst_standby_dir/standby_file3 with 'quotes'",
+ "in standby3";
+ mkdir "$test_standby_datadir/tst_standby_dir/standby_subdir/";
+ append_to_file
+ "$test_standby_datadir/tst_standby_dir/standby_subdir/standby_file4",
+ "in standby4";
+
+ mkdir "$test_primary_datadir/tst_primary_dir";
+ append_to_file "$test_primary_datadir/tst_primary_dir/primary_file1",
+ "in primary1";
+ append_to_file "$test_primary_datadir/tst_primary_dir/primary_file2",
+ "in primary2";
+ mkdir "$test_primary_datadir/tst_primary_dir/primary_subdir/";
+ append_to_file
+ "$test_primary_datadir/tst_primary_dir/primary_subdir/primary_file3",
+ "in primary3";
+
+ RewindTest::promote_standby();
+ RewindTest::run_pg_rewind($test_mode);
+
+ # List files in the data directory after rewind. All the files that
+ # were present in the standby should be present after rewind, and
+ # all the files that were added on the primary should be removed.
+ my @paths;
+ find(
+ sub {
+ push @paths, $File::Find::name
+ if $File::Find::name =~ m/.*tst_.*/;
+ },
+ $test_primary_datadir);
+ @paths = sort @paths;
+ is_deeply(
+ \@paths,
+ [
+ "$test_primary_datadir/tst_both_dir",
+ "$test_primary_datadir/tst_both_dir/both_file1",
+ "$test_primary_datadir/tst_both_dir/both_file2",
+ "$test_primary_datadir/tst_both_dir/both_subdir",
+ "$test_primary_datadir/tst_both_dir/both_subdir/both_file3",
+ "$test_primary_datadir/tst_standby_dir",
+ "$test_primary_datadir/tst_standby_dir/standby_file1",
+ "$test_primary_datadir/tst_standby_dir/standby_file2",
+ "$test_primary_datadir/tst_standby_dir/standby_file3 with 'quotes'",
+ "$test_primary_datadir/tst_standby_dir/standby_subdir",
+ "$test_primary_datadir/tst_standby_dir/standby_subdir/standby_file4"
+ ],
+ "file lists match");
+
+ RewindTest::clean_rewind_test();
+ return;
+}
+
+# Run the test in both modes.
+run_test('local');
+run_test('remote');
+
+exit(0);
diff --git a/src/bin/pg_rewind/t/004_pg_xlog_symlink.pl b/src/bin/pg_rewind/t/004_pg_xlog_symlink.pl
new file mode 100644
index 0000000..8fb0ab3
--- /dev/null
+++ b/src/bin/pg_rewind/t/004_pg_xlog_symlink.pl
@@ -0,0 +1,79 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+#
+# Test pg_rewind when the target's pg_wal directory is a symlink.
+#
+use strict;
+use warnings;
+use File::Copy;
+use File::Path qw(rmtree);
+use TestLib;
+use Test::More tests => 5;
+
+use FindBin;
+use lib $FindBin::RealBin;
+
+use RewindTest;
+
+sub run_test
+{
+ my $test_mode = shift;
+
+ my $primary_xlogdir = "${TestLib::tmp_check}/xlog_primary";
+
+ rmtree($primary_xlogdir);
+ RewindTest::setup_cluster($test_mode);
+
+ my $test_primary_datadir = $node_primary->data_dir;
+
+ # turn pg_wal into a symlink
+ print("moving $test_primary_datadir/pg_wal to $primary_xlogdir\n");
+ move("$test_primary_datadir/pg_wal", $primary_xlogdir) or die;
+ dir_symlink($primary_xlogdir, "$test_primary_datadir/pg_wal") or die;
+
+ RewindTest::start_primary();
+
+ # Create a test table and insert a row in primary.
+ primary_psql("CREATE TABLE tbl1 (d text)");
+ primary_psql("INSERT INTO tbl1 VALUES ('in primary')");
+
+ primary_psql("CHECKPOINT");
+
+ RewindTest::create_standby($test_mode);
+
+ # Insert additional data on primary that will be replicated to standby
+ primary_psql("INSERT INTO tbl1 values ('in primary, before promotion')");
+
+ primary_psql('CHECKPOINT');
+
+ RewindTest::promote_standby();
+
+ # Insert a row in the old primary. This causes the primary and standby
+ # to have "diverged", it's no longer possible to just apply the
+ # standy's logs over primary directory - you need to rewind.
+ primary_psql("INSERT INTO tbl1 VALUES ('in primary, after promotion')");
+
+ # Also insert a new row in the standby, which won't be present in the
+ # old primary.
+ standby_psql("INSERT INTO tbl1 VALUES ('in standby, after promotion')");
+
+ RewindTest::run_pg_rewind($test_mode);
+
+ check_query(
+ 'SELECT * FROM tbl1',
+ qq(in primary
+in primary, before promotion
+in standby, after promotion
+),
+ 'table content');
+
+ RewindTest::clean_rewind_test();
+ return;
+}
+
+# Run the test in both modes
+run_test('local');
+run_test('remote');
+
+exit(0);
diff --git a/src/bin/pg_rewind/t/005_same_timeline.pl b/src/bin/pg_rewind/t/005_same_timeline.pl
new file mode 100644
index 0000000..efe1d4c
--- /dev/null
+++ b/src/bin/pg_rewind/t/005_same_timeline.pl
@@ -0,0 +1,23 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+#
+# Test that running pg_rewind with the source and target clusters
+# on the same timeline runs successfully.
+#
+use strict;
+use warnings;
+use TestLib;
+use Test::More tests => 1;
+
+use FindBin;
+use lib $FindBin::RealBin;
+
+use RewindTest;
+
+RewindTest::setup_cluster();
+RewindTest::start_primary();
+RewindTest::create_standby();
+RewindTest::run_pg_rewind('local');
+RewindTest::clean_rewind_test();
+exit(0);
diff --git a/src/bin/pg_rewind/t/006_options.pl b/src/bin/pg_rewind/t/006_options.pl
new file mode 100644
index 0000000..8179389
--- /dev/null
+++ b/src/bin/pg_rewind/t/006_options.pl
@@ -0,0 +1,43 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+#
+# Test checking options of pg_rewind.
+#
+use strict;
+use warnings;
+use TestLib;
+use Test::More tests => 12;
+
+program_help_ok('pg_rewind');
+program_version_ok('pg_rewind');
+program_options_handling_ok('pg_rewind');
+
+my $primary_pgdata = TestLib::tempdir;
+my $standby_pgdata = TestLib::tempdir;
+command_fails(
+ [
+ 'pg_rewind', '--debug',
+ '--target-pgdata', $primary_pgdata,
+ '--source-pgdata', $standby_pgdata,
+ 'extra_arg1'
+ ],
+ 'too many arguments');
+command_fails([ 'pg_rewind', '--target-pgdata', $primary_pgdata ],
+ 'no source specified');
+command_fails(
+ [
+ 'pg_rewind', '--debug',
+ '--target-pgdata', $primary_pgdata,
+ '--source-pgdata', $standby_pgdata,
+ '--source-server', 'incorrect_source'
+ ],
+ 'both remote and local sources specified');
+command_fails(
+ [
+ 'pg_rewind', '--debug',
+ '--target-pgdata', $primary_pgdata,
+ '--source-pgdata', $standby_pgdata,
+ '--write-recovery-conf'
+ ],
+ 'no local source with --write-recovery-conf');
diff --git a/src/bin/pg_rewind/t/007_standby_source.pl b/src/bin/pg_rewind/t/007_standby_source.pl
new file mode 100644
index 0000000..44319a8
--- /dev/null
+++ b/src/bin/pg_rewind/t/007_standby_source.pl
@@ -0,0 +1,181 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+#
+# Test using a standby server as the source.
+#
+# This sets up three nodes: A, B and C. First, A is the primary,
+# B follows A, and C follows B:
+#
+# A (primary) <--- B (standby) <--- C (standby)
+#
+#
+# Then we promote C, and insert some divergent rows in A and C:
+#
+# A (primary) <--- B (standby) C (primary)
+#
+#
+# Finally, we run pg_rewind on C, to re-point it at B again:
+#
+# A (primary) <--- B (standby) <--- C (standby)
+#
+#
+# The test is similar to the basic tests, but since we're dealing with
+# three nodes, not two, we cannot use most of the RewindTest functions
+# as is.
+
+use strict;
+use warnings;
+use TestLib;
+use Test::More tests => 3;
+
+use FindBin;
+use lib $FindBin::RealBin;
+use File::Copy;
+use PostgresNode;
+use RewindTest;
+
+my $tmp_folder = TestLib::tempdir;
+
+my $node_a;
+my $node_b;
+my $node_c;
+
+# Set up node A, as primary
+#
+# A (primary)
+
+setup_cluster('a');
+start_primary();
+$node_a = $node_primary;
+
+# Create a test table and insert a row in primary.
+$node_a->safe_psql('postgres', "CREATE TABLE tbl1 (d text)");
+$node_a->safe_psql('postgres', "INSERT INTO tbl1 VALUES ('in A')");
+primary_psql("CHECKPOINT");
+
+# Set up node B and C, as cascaded standbys
+#
+# A (primary) <--- B (standby) <--- C (standby)
+$node_a->backup('my_backup');
+$node_b = get_new_node('node_b');
+$node_b->init_from_backup($node_a, 'my_backup', has_streaming => 1);
+$node_b->set_standby_mode();
+$node_b->start;
+
+$node_b->backup('my_backup');
+$node_c = get_new_node('node_c');
+$node_c->init_from_backup($node_b, 'my_backup', has_streaming => 1);
+$node_c->set_standby_mode();
+$node_c->start;
+
+# Insert additional data on A, and wait for both standbys to catch up.
+$node_a->safe_psql('postgres',
+ "INSERT INTO tbl1 values ('in A, before promotion')");
+$node_a->safe_psql('postgres', 'CHECKPOINT');
+
+my $lsn = $node_a->lsn('insert');
+$node_a->wait_for_catchup('node_b', 'write', $lsn);
+$node_b->wait_for_catchup('node_c', 'write', $lsn);
+
+# Promote C
+#
+# A (primary) <--- B (standby) C (primary)
+
+$node_c->promote;
+$node_c->safe_psql('postgres', "checkpoint");
+
+
+# Insert a row in A. This causes A/B and C to have "diverged", so that it's
+# no longer possible to just apply the standy's logs over primary directory
+# - you need to rewind.
+$node_a->safe_psql('postgres',
+ "INSERT INTO tbl1 VALUES ('in A, after C was promoted')");
+
+# make sure it's replicated to B before we continue
+$lsn = $node_a->lsn('insert');
+$node_a->wait_for_catchup('node_b', 'replay', $lsn);
+
+# Also insert a new row in the standby, which won't be present in the
+# old primary.
+$node_c->safe_psql('postgres',
+ "INSERT INTO tbl1 VALUES ('in C, after C was promoted')");
+
+
+#
+# All set up. We're ready to run pg_rewind.
+#
+my $node_c_pgdata = $node_c->data_dir;
+
+# Stop the node and be ready to perform the rewind.
+$node_c->stop('fast');
+
+# Keep a temporary postgresql.conf or it would be overwritten during the rewind.
+copy(
+ "$node_c_pgdata/postgresql.conf",
+ "$tmp_folder/node_c-postgresql.conf.tmp");
+
+{
+ # Temporarily unset PGAPPNAME so that the server doesn't
+ # inherit it. Otherwise this could affect libpqwalreceiver
+ # connections in confusing ways.
+ local %ENV = %ENV;
+ delete $ENV{PGAPPNAME};
+
+ # Do rewind using a remote connection as source, generating
+ # recovery configuration automatically.
+ command_ok(
+ [
+ 'pg_rewind', "--debug",
+ "--source-server", $node_b->connstr('postgres'),
+ "--target-pgdata=$node_c_pgdata", "--no-sync",
+ "--write-recovery-conf"
+ ],
+ 'pg_rewind remote');
+}
+
+# Now move back postgresql.conf with old settings
+move(
+ "$tmp_folder/node_c-postgresql.conf.tmp",
+ "$node_c_pgdata/postgresql.conf");
+
+# Restart the node.
+$node_c->start;
+
+# set RewindTest::node_primary to point to the rewinded node, so that we can
+# use check_query()
+$node_primary = $node_c;
+
+# Run some checks to verify that C has been successfully rewound,
+# and connected back to follow B.
+
+check_query(
+ 'SELECT * FROM tbl1',
+ qq(in A
+in A, before promotion
+in A, after C was promoted
+),
+ 'table content after rewind');
+
+# Insert another row, and observe that it's cascaded from A to B to C.
+$node_a->safe_psql('postgres',
+ "INSERT INTO tbl1 values ('in A, after rewind')");
+
+$lsn = $node_a->lsn('insert');
+$node_b->wait_for_catchup('node_c', 'replay', $lsn);
+
+check_query(
+ 'SELECT * FROM tbl1',
+ qq(in A
+in A, before promotion
+in A, after C was promoted
+in A, after rewind
+),
+ 'table content after rewind and insert');
+
+# clean up
+$node_a->teardown_node;
+$node_b->teardown_node;
+$node_c->teardown_node;
+
+exit(0);
diff --git a/src/bin/pg_rewind/t/008_min_recovery_point.pl b/src/bin/pg_rewind/t/008_min_recovery_point.pl
new file mode 100644
index 0000000..9ebcbad
--- /dev/null
+++ b/src/bin/pg_rewind/t/008_min_recovery_point.pl
@@ -0,0 +1,177 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+#
+# Test situation where a target data directory contains
+# WAL records beyond both the last checkpoint and the divergence
+# point:
+#
+# Target WAL (TLI 2):
+#
+# backup ... Checkpoint A ... INSERT 'rewind this'
+# (TLI 1 -> 2)
+#
+# ^ last common ^ minRecoveryPoint
+# checkpoint
+#
+# Source WAL (TLI 3):
+#
+# backup ... Checkpoint A ... Checkpoint B ... INSERT 'keep this'
+# (TLI 1 -> 2) (TLI 2 -> 3)
+#
+#
+# The last common checkpoint is Checkpoint A. But there is WAL on TLI 2
+# after the last common checkpoint that needs to be rewound. We used to
+# have a bug where minRecoveryPoint was ignored, and pg_rewind concluded
+# that the target doesn't need rewinding in this scenario, because the
+# last checkpoint on the target TLI was an ancestor of the source TLI.
+#
+#
+# This test does not make use of RewindTest as it requires three
+# nodes.
+
+use strict;
+use warnings;
+use PostgresNode;
+use TestLib;
+use Test::More tests => 3;
+
+use File::Copy;
+
+my $tmp_folder = TestLib::tempdir;
+
+my $node_1 = get_new_node('node_1');
+$node_1->init(allows_streaming => 1);
+$node_1->append_conf(
+ 'postgresql.conf', qq(
+wal_keep_size='100 MB'
+));
+
+$node_1->start;
+
+# Create a couple of test tables
+$node_1->safe_psql('postgres', 'CREATE TABLE public.foo (t TEXT)');
+$node_1->safe_psql('postgres', 'CREATE TABLE public.bar (t TEXT)');
+$node_1->safe_psql('postgres', "INSERT INTO public.bar VALUES ('in both')");
+
+#
+# Create node_2 and node_3 as standbys following node_1
+#
+my $backup_name = 'my_backup';
+$node_1->backup($backup_name);
+
+my $node_2 = get_new_node('node_2');
+$node_2->init_from_backup($node_1, $backup_name, has_streaming => 1);
+$node_2->start;
+
+my $node_3 = get_new_node('node_3');
+$node_3->init_from_backup($node_1, $backup_name, has_streaming => 1);
+$node_3->start;
+
+# Wait until node 3 has connected and caught up
+my $lsn = $node_1->lsn('insert');
+$node_1->wait_for_catchup('node_3', 'replay', $lsn);
+
+#
+# Swap the roles of node_1 and node_3, so that node_1 follows node_3.
+#
+$node_1->stop('fast');
+$node_3->promote;
+# Force a checkpoint after the promotion. pg_rewind looks at the control
+# file to determine what timeline the server is on, and that isn't updated
+# immediately at promotion, but only at the next checkpoint. When running
+# pg_rewind in remote mode, it's possible that we complete the test steps
+# after promotion so quickly that when pg_rewind runs, the standby has not
+# performed a checkpoint after promotion yet.
+$node_3->safe_psql('postgres', "checkpoint");
+
+# reconfigure node_1 as a standby following node_3
+my $node_3_connstr = $node_3->connstr;
+$node_1->append_conf(
+ 'postgresql.conf', qq(
+primary_conninfo='$node_3_connstr'
+));
+$node_1->set_standby_mode();
+$node_1->start();
+
+# also reconfigure node_2 to follow node_3
+$node_2->append_conf(
+ 'postgresql.conf', qq(
+primary_conninfo='$node_3_connstr'
+));
+$node_2->restart();
+
+#
+# Promote node_1, to create a split-brain scenario.
+#
+
+# make sure node_1 is full caught up with node_3 first
+$lsn = $node_3->lsn('insert');
+$node_3->wait_for_catchup('node_1', 'replay', $lsn);
+
+$node_1->promote;
+# Force a checkpoint after promotion, like earlier.
+$node_1->safe_psql('postgres', "checkpoint");
+
+#
+# We now have a split-brain with two primaries. Insert a row on both to
+# demonstratively create a split brain. After the rewind, we should only
+# see the insert on 1, as the insert on node 3 is rewound away.
+#
+$node_1->safe_psql('postgres',
+ "INSERT INTO public.foo (t) VALUES ('keep this')");
+# 'bar' is unmodified in node 1, so it won't be overwritten by replaying the
+# WAL from node 1.
+$node_3->safe_psql('postgres',
+ "INSERT INTO public.bar (t) VALUES ('rewind this')");
+
+# Insert more rows in node 1, to bump up the XID counter. Otherwise, if
+# rewind doesn't correctly rewind the changes made on the other node,
+# we might fail to notice if the inserts are invisible because the XIDs
+# are not marked as committed.
+$node_1->safe_psql('postgres',
+ "INSERT INTO public.foo (t) VALUES ('and this')");
+$node_1->safe_psql('postgres',
+ "INSERT INTO public.foo (t) VALUES ('and this too')");
+
+# Wait for node 2 to catch up
+$node_2->poll_query_until('postgres',
+ q|SELECT COUNT(*) > 1 FROM public.bar|, 't');
+
+# At this point node_2 will shut down without a shutdown checkpoint,
+# but with WAL entries beyond the preceding shutdown checkpoint.
+$node_2->stop('fast');
+$node_3->stop('fast');
+
+my $node_2_pgdata = $node_2->data_dir;
+my $node_1_connstr = $node_1->connstr;
+
+# Keep a temporary postgresql.conf or it would be overwritten during the rewind.
+copy(
+ "$node_2_pgdata/postgresql.conf",
+ "$tmp_folder/node_2-postgresql.conf.tmp");
+
+command_ok(
+ [
+ 'pg_rewind', "--source-server=$node_1_connstr",
+ "--target-pgdata=$node_2_pgdata", "--debug"
+ ],
+ 'run pg_rewind');
+
+# Now move back postgresql.conf with old settings
+move(
+ "$tmp_folder/node_2-postgresql.conf.tmp",
+ "$node_2_pgdata/postgresql.conf");
+
+$node_2->start;
+
+# Check contents of the test tables after rewind. The rows inserted in node 3
+# before rewind should've been overwritten with the data from node 1.
+my $result;
+$result = $node_2->safe_psql('postgres', 'SELECT * FROM public.foo');
+is( $result, qq(keep this
+and this
+and this too), 'table foo after rewind');
+
+$result = $node_2->safe_psql('postgres', 'SELECT * FROM public.bar');
+is($result, qq(in both), 'table bar after rewind');
diff --git a/src/bin/pg_rewind/t/RewindTest.pm b/src/bin/pg_rewind/t/RewindTest.pm
new file mode 100644
index 0000000..356c9ba
--- /dev/null
+++ b/src/bin/pg_rewind/t/RewindTest.pm
@@ -0,0 +1,396 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+package RewindTest;
+
+# Test driver for pg_rewind. Each test consists of a cycle where a new cluster
+# is first created with initdb, and a streaming replication standby is set up
+# to follow the primary. Then the primary is shut down and the standby is
+# promoted, and finally pg_rewind is used to rewind the old primary, using the
+# standby as the source.
+#
+# To run a test, the test script (in t/ subdirectory) calls the functions
+# in this module. These functions should be called in this sequence:
+#
+# 1. setup_cluster - creates a PostgreSQL cluster that runs as the primary
+#
+# 2. start_primary - starts the primary server
+#
+# 3. create_standby - runs pg_basebackup to initialize a standby server, and
+# sets it up to follow the primary.
+#
+# 4. promote_standby - runs "pg_ctl promote" to promote the standby server.
+# The old primary keeps running.
+#
+# 5. run_pg_rewind - stops the old primary (if it's still running) and runs
+# pg_rewind to synchronize it with the now-promoted standby server.
+#
+# 6. clean_rewind_test - stops both servers used in the test, if they're
+# still running.
+#
+# The test script can use the helper functions primary_psql and standby_psql
+# to run psql against the primary and standby servers, respectively.
+
+use strict;
+use warnings;
+
+use Carp;
+use Config;
+use Exporter 'import';
+use File::Copy;
+use File::Path qw(rmtree);
+use IPC::Run qw(run);
+use PostgresNode;
+use RecursiveCopy;
+use TestLib;
+use Test::More;
+
+our @EXPORT = qw(
+ $node_primary
+ $node_standby
+
+ primary_psql
+ standby_psql
+ check_query
+
+ setup_cluster
+ start_primary
+ create_standby
+ promote_standby
+ run_pg_rewind
+ clean_rewind_test
+);
+
+# Our nodes.
+our $node_primary;
+our $node_standby;
+
+sub primary_psql
+{
+ my $cmd = shift;
+ my $dbname = shift || 'postgres';
+
+ system_or_bail 'psql', '-q', '--no-psqlrc', '-d',
+ $node_primary->connstr($dbname), '-c', "$cmd";
+ return;
+}
+
+sub standby_psql
+{
+ my $cmd = shift;
+ my $dbname = shift || 'postgres';
+
+ system_or_bail 'psql', '-q', '--no-psqlrc', '-d',
+ $node_standby->connstr($dbname), '-c', "$cmd";
+ return;
+}
+
+# Run a query against the primary, and check that the output matches what's
+# expected
+sub check_query
+{
+ local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+ my ($query, $expected_stdout, $test_name) = @_;
+ my ($stdout, $stderr);
+
+ # we want just the output, no formatting
+ my $result = run [
+ 'psql', '-q', '-A', '-t', '--no-psqlrc', '-d',
+ $node_primary->connstr('postgres'),
+ '-c', $query
+ ],
+ '>', \$stdout, '2>', \$stderr;
+
+ # We don't use ok() for the exit code and stderr, because we want this
+ # check to be just a single test.
+ if (!$result)
+ {
+ fail("$test_name: psql exit code");
+ }
+ elsif ($stderr ne '')
+ {
+ diag $stderr;
+ fail("$test_name: psql no stderr");
+ }
+ else
+ {
+ is($stdout, $expected_stdout, "$test_name: query result matches");
+ }
+ return;
+}
+
+sub setup_cluster
+{
+ my $extra_name = shift; # Used to differentiate clusters
+ my $extra = shift; # Extra params for initdb
+
+ # Initialize primary, data checksums are mandatory
+ $node_primary =
+ get_new_node('primary' . ($extra_name ? "_${extra_name}" : ''));
+
+ # Set up pg_hba.conf and pg_ident.conf for the role running
+ # pg_rewind. This role is used for all the tests, and has
+ # minimal permissions enough to rewind from an online source.
+ $node_primary->init(
+ allows_streaming => 1,
+ extra => $extra,
+ auth_extra => [ '--create-role', 'rewind_user' ]);
+
+ # Set wal_keep_size to prevent WAL segment recycling after enforced
+ # checkpoints in the tests.
+ $node_primary->append_conf(
+ 'postgresql.conf', qq(
+wal_keep_size = 320MB
+));
+ return;
+}
+
+sub start_primary
+{
+ $node_primary->start;
+
+ # Create custom role which is used to run pg_rewind, and adjust its
+ # permissions to the minimum necessary.
+ $node_primary->safe_psql(
+ 'postgres', "
+ CREATE ROLE rewind_user LOGIN;
+ GRANT EXECUTE ON function pg_catalog.pg_ls_dir(text, boolean, boolean)
+ TO rewind_user;
+ GRANT EXECUTE ON function pg_catalog.pg_stat_file(text, boolean)
+ TO rewind_user;
+ GRANT EXECUTE ON function pg_catalog.pg_read_binary_file(text)
+ TO rewind_user;
+ GRANT EXECUTE ON function pg_catalog.pg_read_binary_file(text, bigint, bigint, boolean)
+ TO rewind_user;");
+
+ #### Now run the test-specific parts to initialize the primary before setting
+ # up standby
+
+ return;
+}
+
+sub create_standby
+{
+ my $extra_name = shift;
+
+ $node_standby =
+ get_new_node('standby' . ($extra_name ? "_${extra_name}" : ''));
+ $node_primary->backup('my_backup');
+ $node_standby->init_from_backup($node_primary, 'my_backup');
+ my $connstr_primary = $node_primary->connstr();
+
+ $node_standby->append_conf(
+ "postgresql.conf", qq(
+primary_conninfo='$connstr_primary'
+));
+
+ $node_standby->set_standby_mode();
+
+ # Start standby
+ $node_standby->start;
+
+ # The standby may have WAL to apply before it matches the primary. That
+ # is fine, because no test examines the standby before promotion.
+
+ return;
+}
+
+sub promote_standby
+{
+ #### Now run the test-specific parts to run after standby has been started
+ # up standby
+
+ # Wait for the standby to receive and write all WAL.
+ $node_primary->wait_for_catchup($node_standby, 'write');
+
+ # Now promote standby and insert some new data on primary, this will put
+ # the primary out-of-sync with the standby.
+ $node_standby->promote;
+
+ # Force a checkpoint after the promotion. pg_rewind looks at the control
+ # file to determine what timeline the server is on, and that isn't updated
+ # immediately at promotion, but only at the next checkpoint. When running
+ # pg_rewind in remote mode, it's possible that we complete the test steps
+ # after promotion so quickly that when pg_rewind runs, the standby has not
+ # performed a checkpoint after promotion yet.
+ standby_psql("checkpoint");
+
+ return;
+}
+
+sub run_pg_rewind
+{
+ my $test_mode = shift;
+ my $primary_pgdata = $node_primary->data_dir;
+ my $standby_pgdata = $node_standby->data_dir;
+ my $standby_connstr = $node_standby->connstr('postgres');
+ my $tmp_folder = TestLib::tempdir;
+
+ # Append the rewind-specific role to the connection string.
+ $standby_connstr = "$standby_connstr user=rewind_user";
+
+ if ($test_mode eq 'archive')
+ {
+ # pg_rewind is tested with --restore-target-wal by moving all
+ # WAL files to a secondary location. Note that this leads to
+ # a failure in ensureCleanShutdown(), forcing to the use of
+ # --no-ensure-shutdown in this mode as the initial set of WAL
+ # files needed to ensure a clean restart is gone. This could
+ # be improved by keeping around only a minimum set of WAL
+ # segments but that would just make the test more costly,
+ # without improving the coverage. Hence, instead, stop
+ # gracefully the primary here.
+ $node_primary->stop;
+ }
+ else
+ {
+ # Stop the primary and be ready to perform the rewind. The cluster
+ # needs recovery to finish once, and pg_rewind makes sure that it
+ # happens automatically.
+ $node_primary->stop('immediate');
+ }
+
+ # At this point, the rewind processing is ready to run.
+ # We now have a very simple scenario with a few diverged WAL record.
+ # The real testing begins really now with a bifurcation of the possible
+ # scenarios that pg_rewind supports.
+
+ # Keep a temporary postgresql.conf for primary node or it would be
+ # overwritten during the rewind.
+ copy(
+ "$primary_pgdata/postgresql.conf",
+ "$tmp_folder/primary-postgresql.conf.tmp");
+
+ # Now run pg_rewind
+ if ($test_mode eq "local")
+ {
+
+ # Do rewind using a local pgdata as source
+ # Stop the primary and be ready to perform the rewind
+ $node_standby->stop;
+ command_ok(
+ [
+ 'pg_rewind',
+ "--debug",
+ "--source-pgdata=$standby_pgdata",
+ "--target-pgdata=$primary_pgdata",
+ "--no-sync"
+ ],
+ 'pg_rewind local');
+ }
+ elsif ($test_mode eq "remote")
+ {
+ # Do rewind using a remote connection as source, generating
+ # recovery configuration automatically.
+ command_ok(
+ [
+ 'pg_rewind', "--debug",
+ "--source-server", $standby_connstr,
+ "--target-pgdata=$primary_pgdata", "--no-sync",
+ "--write-recovery-conf"
+ ],
+ 'pg_rewind remote');
+
+ # Check that standby.signal is here as recovery configuration
+ # was requested.
+ ok( -e "$primary_pgdata/standby.signal",
+ 'standby.signal created after pg_rewind');
+
+ # Now, when pg_rewind apparently succeeded with minimal permissions,
+ # add REPLICATION privilege. So we could test that new standby
+ # is able to connect to the new primary with generated config.
+ $node_standby->safe_psql('postgres',
+ "ALTER ROLE rewind_user WITH REPLICATION;");
+ }
+ elsif ($test_mode eq "archive")
+ {
+
+ # Do rewind using a local pgdata as source and specified
+ # directory with target WAL archive. The old primary has
+ # to be stopped at this point.
+
+ # Remove the existing archive directory and move all WAL
+ # segments from the old primary to the archives. These
+ # will be used by pg_rewind.
+ rmtree($node_primary->archive_dir);
+ RecursiveCopy::copypath($node_primary->data_dir . "/pg_wal",
+ $node_primary->archive_dir);
+
+ # Fast way to remove entire directory content
+ rmtree($node_primary->data_dir . "/pg_wal");
+ mkdir($node_primary->data_dir . "/pg_wal");
+
+ # Make sure that directories have the right umask as this is
+ # required by a follow-up check on permissions, and better
+ # safe than sorry.
+ chmod(0700, $node_primary->archive_dir);
+ chmod(0700, $node_primary->data_dir . "/pg_wal");
+
+ # Add appropriate restore_command to the target cluster
+ $node_primary->enable_restoring($node_primary, 0);
+
+ # Stop the new primary and be ready to perform the rewind.
+ $node_standby->stop;
+
+ # Note the use of --no-ensure-shutdown here. WAL files are
+ # gone in this mode and the primary has been stopped
+ # gracefully already.
+ command_ok(
+ [
+ 'pg_rewind',
+ "--debug",
+ "--source-pgdata=$standby_pgdata",
+ "--target-pgdata=$primary_pgdata",
+ "--no-sync",
+ "--no-ensure-shutdown",
+ "--restore-target-wal"
+ ],
+ 'pg_rewind archive');
+ }
+ else
+ {
+
+ # Cannot come here normally
+ croak("Incorrect test mode specified");
+ }
+
+ # Now move back postgresql.conf with old settings
+ move(
+ "$tmp_folder/primary-postgresql.conf.tmp",
+ "$primary_pgdata/postgresql.conf");
+
+ chmod(
+ $node_primary->group_access() ? 0640 : 0600,
+ "$primary_pgdata/postgresql.conf")
+ or BAIL_OUT(
+ "unable to set permissions for $primary_pgdata/postgresql.conf");
+
+ # Plug-in rewound node to the now-promoted standby node
+ if ($test_mode ne "remote")
+ {
+ my $port_standby = $node_standby->port;
+ $node_primary->append_conf(
+ 'postgresql.conf', qq(
+primary_conninfo='port=$port_standby'));
+
+ $node_primary->set_standby_mode();
+ }
+
+ # Restart the primary to check that rewind went correctly
+ $node_primary->start;
+
+ #### Now run the test-specific parts to check the result
+
+ return;
+}
+
+# Clean up after the test. Stop both servers, if they're still running.
+sub clean_rewind_test
+{
+ $node_primary->teardown_node if defined $node_primary;
+ $node_standby->teardown_node if defined $node_standby;
+ return;
+}
+
+1;
diff --git a/src/bin/pg_rewind/timeline.c b/src/bin/pg_rewind/timeline.c
new file mode 100644
index 0000000..6756c5d
--- /dev/null
+++ b/src/bin/pg_rewind/timeline.c
@@ -0,0 +1,130 @@
+/*-------------------------------------------------------------------------
+ *
+ * timeline.c
+ * timeline-related functions.
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include "access/timeline.h"
+#include "access/xlog_internal.h"
+#include "pg_rewind.h"
+
+/*
+ * This is copy-pasted from the backend readTimeLineHistory, modified to
+ * return a malloc'd array and to work without backend functions.
+ */
+/*
+ * Try to read a timeline's history file.
+ *
+ * If successful, return the list of component TLIs (the given TLI followed by
+ * its ancestor TLIs). If we can't find the history file, assume that the
+ * timeline has no parents, and return a list of just the specified timeline
+ * ID.
+ */
+TimeLineHistoryEntry *
+rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
+{
+ char *fline;
+ TimeLineHistoryEntry *entry;
+ TimeLineHistoryEntry *entries = NULL;
+ int nlines = 0;
+ TimeLineID lasttli = 0;
+ XLogRecPtr prevend;
+ char *bufptr;
+ bool lastline = false;
+
+ /*
+ * Parse the file...
+ */
+ prevend = InvalidXLogRecPtr;
+ bufptr = buffer;
+ while (!lastline)
+ {
+ char *ptr;
+ TimeLineID tli;
+ uint32 switchpoint_hi;
+ uint32 switchpoint_lo;
+ int nfields;
+
+ fline = bufptr;
+ while (*bufptr && *bufptr != '\n')
+ bufptr++;
+ if (!(*bufptr))
+ lastline = true;
+ else
+ *bufptr++ = '\0';
+
+ /* skip leading whitespace and check for # comment */
+ for (ptr = fline; *ptr; ptr++)
+ {
+ if (!isspace((unsigned char) *ptr))
+ break;
+ }
+ if (*ptr == '\0' || *ptr == '#')
+ continue;
+
+ nfields = sscanf(fline, "%u\t%X/%X", &tli, &switchpoint_hi, &switchpoint_lo);
+
+ if (nfields < 1)
+ {
+ /* expect a numeric timeline ID as first field of line */
+ pg_log_error("syntax error in history file: %s", fline);
+ pg_log_error("Expected a numeric timeline ID.");
+ exit(1);
+ }
+ if (nfields != 3)
+ {
+ pg_log_error("syntax error in history file: %s", fline);
+ pg_log_error("Expected a write-ahead log switchpoint location.");
+ exit(1);
+ }
+ if (entries && tli <= lasttli)
+ {
+ pg_log_error("invalid data in history file: %s", fline);
+ pg_log_error("Timeline IDs must be in increasing sequence.");
+ exit(1);
+ }
+
+ lasttli = tli;
+
+ nlines++;
+ entries = pg_realloc(entries, nlines * sizeof(TimeLineHistoryEntry));
+
+ entry = &entries[nlines - 1];
+ entry->tli = tli;
+ entry->begin = prevend;
+ entry->end = ((uint64) (switchpoint_hi)) << 32 | (uint64) switchpoint_lo;
+ prevend = entry->end;
+
+ /* we ignore the remainder of each line */
+ }
+
+ if (entries && targetTLI <= lasttli)
+ {
+ pg_log_error("invalid data in history file");
+ pg_log_error("Timeline IDs must be less than child timeline's ID.");
+ exit(1);
+ }
+
+ /*
+ * Create one more entry for the "tip" of the timeline, which has no entry
+ * in the history file.
+ */
+ nlines++;
+ if (entries)
+ entries = pg_realloc(entries, nlines * sizeof(TimeLineHistoryEntry));
+ else
+ entries = pg_malloc(1 * sizeof(TimeLineHistoryEntry));
+
+ entry = &entries[nlines - 1];
+ entry->tli = targetTLI;
+ entry->begin = prevend;
+ entry->end = InvalidXLogRecPtr;
+
+ *nentries = nlines;
+ return entries;
+}