PostgreSQL Source Code  git master
pg_rewind.c File Reference
#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 "fe_utils/string_utils.h"
#include "file_ops.h"
#include "filemap.h"
#include "getopt_long.h"
#include "pg_rewind.h"
#include "rewind_source.h"
#include "storage/bufpage.h"
Include dependency graph for pg_rewind.c:

Go to the source code of this file.

Macros

#define MAXCMDLEN   (2 * MAXPGPATH)
 

Functions

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 TimeLineHistoryEntrygetTimelineHistory (TimeLineID tli, bool is_source, int *nentries)
 
static void findCommonAncestorTimeline (TimeLineHistoryEntry *a_history, int a_nentries, TimeLineHistoryEntry *b_history, int b_nentries, XLogRecPtr *recptr, int *tliIndex)
 
static void ensureCleanShutdown (const char *argv0)
 
static void disconnect_atexit (void)
 
int main (int argc, char **argv)
 
void progress_report (bool finished)
 
static XLogRecPtr MinXLogRecPtr (XLogRecPtr a, XLogRecPtr b)
 
static void checkControlFile (ControlFileData *ControlFile)
 

Variables

static ControlFileData ControlFile_target
 
static ControlFileData ControlFile_source
 
static ControlFileData ControlFile_source_after
 
const char * progname
 
int WalSegSz
 
char * datadir_target = NULL
 
char * datadir_source = NULL
 
char * connstr_source = NULL
 
char * restore_command = NULL
 
char * config_file = NULL
 
static bool debug = false
 
bool showprogress = false
 
bool dry_run = false
 
bool do_sync = true
 
bool restore_wal = false
 
TimeLineHistoryEntrytargetHistory
 
int targetNentries
 
uint64 fetch_size
 
uint64 fetch_done
 
static PGconnconn
 
static rewind_sourcesource
 

Macro Definition Documentation

◆ MAXCMDLEN

#define MAXCMDLEN   (2 * MAXPGPATH)

Function Documentation

◆ checkControlFile()

static void checkControlFile ( ControlFileData ControlFile)
static

Definition at line 994 of file pg_rewind.c.

995 {
996  pg_crc32c crc;
997 
998  /* Calculate CRC */
999  INIT_CRC32C(crc);
1000  COMP_CRC32C(crc, (char *) ControlFile, offsetof(ControlFileData, crc));
1001  FIN_CRC32C(crc);
1002 
1003  /* And simply compare it */
1004  if (!EQ_CRC32C(crc, ControlFile->crc))
1005  pg_fatal("unexpected control file CRC");
1006 }
#define pg_fatal(...)
uint32 pg_crc32c
Definition: pg_crc32c.h:38
#define COMP_CRC32C(crc, data, len)
Definition: pg_crc32c.h:89
#define EQ_CRC32C(c1, c2)
Definition: pg_crc32c.h:42
#define INIT_CRC32C(crc)
Definition: pg_crc32c.h:41
#define FIN_CRC32C(crc)
Definition: pg_crc32c.h:94
return crc
pg_crc32c crc
Definition: pg_control.h:230
static ControlFileData * ControlFile
Definition: xlog.c:570

References COMP_CRC32C, ControlFile, ControlFileData::crc, crc, EQ_CRC32C, FIN_CRC32C, INIT_CRC32C, and pg_fatal.

Referenced by digestControlFile().

◆ createBackupLabel()

static void createBackupLabel ( XLogRecPtr  startpoint,
TimeLineID  starttli,
XLogRecPtr  checkpointloc 
)
static

Definition at line 951 of file pg_rewind.c.

952 {
953  XLogSegNo startsegno;
954  time_t stamp_time;
955  char strfbuf[128];
956  char xlogfilename[MAXFNAMELEN];
957  struct tm *tmp;
958  char buf[1000];
959  int len;
960 
961  XLByteToSeg(startpoint, startsegno, WalSegSz);
962  XLogFileName(xlogfilename, starttli, startsegno, WalSegSz);
963 
964  /*
965  * Construct backup label file
966  */
967  stamp_time = time(NULL);
968  tmp = localtime(&stamp_time);
969  strftime(strfbuf, sizeof(strfbuf), "%Y-%m-%d %H:%M:%S %Z", tmp);
970 
971  len = snprintf(buf, sizeof(buf),
972  "START WAL LOCATION: %X/%X (file %s)\n"
973  "CHECKPOINT LOCATION: %X/%X\n"
974  "BACKUP METHOD: pg_rewind\n"
975  "BACKUP FROM: standby\n"
976  "START TIME: %s\n",
977  /* omit LABEL: line */
978  LSN_FORMAT_ARGS(startpoint), xlogfilename,
979  LSN_FORMAT_ARGS(checkpointloc),
980  strfbuf);
981  if (len >= sizeof(buf))
982  pg_fatal("backup label buffer too small"); /* shouldn't happen */
983 
984  /* TODO: move old file out of the way, if any. */
985  open_target_file("backup_label", true); /* BACKUP_LABEL_FILE */
988 }
void close_target_file(void)
Definition: file_ops.c:75
void open_target_file(const char *path, bool trunc)
Definition: file_ops.c:47
void write_target_range(char *buf, off_t begin, size_t size)
Definition: file_ops.c:88
static struct pg_tm tm
Definition: localtime.c:104
const void size_t len
int WalSegSz
Definition: pg_rewind.c:63
static char * buf
Definition: pg_test_fsync.c:67
#define snprintf
Definition: port.h:238
#define MAXFNAMELEN
#define XLByteToSeg(xlrp, logSegNo, wal_segsz_bytes)
static void XLogFileName(char *fname, TimeLineID tli, XLogSegNo logSegNo, int wal_segsz_bytes)
#define LSN_FORMAT_ARGS(lsn)
Definition: xlogdefs.h:43
uint64 XLogSegNo
Definition: xlogdefs.h:48

References buf, close_target_file(), len, LSN_FORMAT_ARGS, MAXFNAMELEN, open_target_file(), pg_fatal, snprintf, tm, WalSegSz, write_target_range(), XLByteToSeg, and XLogFileName().

Referenced by perform_rewind().

◆ digestControlFile()

static void digestControlFile ( ControlFileData ControlFile,
const char *  content,
size_t  size 
)
static

Definition at line 1013 of file pg_rewind.c.

1015 {
1016  if (size != PG_CONTROL_FILE_SIZE)
1017  pg_fatal("unexpected control file size %d, expected %d",
1018  (int) size, PG_CONTROL_FILE_SIZE);
1019 
1020  memcpy(ControlFile, content, sizeof(ControlFileData));
1021 
1022  /* set and validate WalSegSz */
1024 
1026  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",
1027  "WAL segment size must be a power of two between 1 MB and 1 GB, but the control file specifies %d bytes",
1028  WalSegSz),
1029  WalSegSz);
1030 
1031  /* Additional checks on control file */
1033 }
#define ngettext(s, p, n)
Definition: c.h:1171
#define PG_CONTROL_FILE_SIZE
Definition: pg_control.h:248
static void checkControlFile(ControlFileData *ControlFile)
Definition: pg_rewind.c:994
uint32 xlog_seg_size
Definition: pg_control.h:209
#define IsValidWalSegSize(size)
Definition: xlog_internal.h:96

References checkControlFile(), ControlFile, IsValidWalSegSize, ngettext, PG_CONTROL_FILE_SIZE, pg_fatal, WalSegSz, and ControlFileData::xlog_seg_size.

Referenced by main(), and perform_rewind().

◆ disconnect_atexit()

static void disconnect_atexit ( void  )
static

Definition at line 1189 of file pg_rewind.c.

1190 {
1191  if (conn != NULL)
1192  PQfinish(conn);
1193 }
void PQfinish(PGconn *conn)
Definition: fe-connect.c:4560
static PGconn * conn
Definition: pg_rewind.c:86

References conn, and PQfinish().

Referenced by main().

◆ ensureCleanShutdown()

static void ensureCleanShutdown ( const char *  argv0)
static

Definition at line 1117 of file pg_rewind.c.

1118 {
1119  int ret;
1120 #define MAXCMDLEN (2 * MAXPGPATH)
1121  char exec_path[MAXPGPATH];
1122  PQExpBuffer postgres_cmd;
1123 
1124  /* locate postgres binary */
1125  if ((ret = find_other_exec(argv0, "postgres",
1127  exec_path)) < 0)
1128  {
1129  char full_path[MAXPGPATH];
1130 
1131  if (find_my_exec(argv0, full_path) < 0)
1132  strlcpy(full_path, progname, sizeof(full_path));
1133 
1134  if (ret == -1)
1135  pg_fatal("program \"%s\" is needed by %s but was not found in the same directory as \"%s\"",
1136  "postgres", progname, full_path);
1137  else
1138  pg_fatal("program \"%s\" was found by \"%s\" but was not the same version as %s",
1139  "postgres", full_path, progname);
1140  }
1141 
1142  pg_log_info("executing \"%s\" for target server to complete crash recovery",
1143  exec_path);
1144 
1145  /*
1146  * Skip processing if requested, but only after ensuring presence of
1147  * postgres.
1148  */
1149  if (dry_run)
1150  return;
1151 
1152  /*
1153  * Finally run postgres in single-user mode. There is no need to use
1154  * fsync here. This makes the recovery faster, and the target data folder
1155  * is synced at the end anyway.
1156  */
1157  postgres_cmd = createPQExpBuffer();
1158 
1159  /* path to postgres, properly quoted */
1160  appendShellString(postgres_cmd, exec_path);
1161 
1162  /* add set of options with properly quoted data directory */
1163  appendPQExpBufferStr(postgres_cmd, " --single -F -D ");
1164  appendShellString(postgres_cmd, datadir_target);
1165 
1166  /* add custom configuration file only if requested */
1167  if (config_file != NULL)
1168  {
1169  appendPQExpBufferStr(postgres_cmd, " -c config_file=");
1170  appendShellString(postgres_cmd, config_file);
1171  }
1172 
1173  /* finish with the database name, and a properly quoted redirection */
1174  appendPQExpBufferStr(postgres_cmd, " template1 < ");
1175  appendShellString(postgres_cmd, DEVNULL);
1176 
1177  fflush(NULL);
1178  if (system(postgres_cmd->data) != 0)
1179  {
1180  pg_log_error("postgres single-user mode in target cluster failed");
1181  pg_log_error_detail("Command was: %s", postgres_cmd->data);
1182  exit(1);
1183  }
1184 
1185  destroyPQExpBuffer(postgres_cmd);
1186 }
int find_my_exec(const char *argv0, char *retpath)
Definition: exec.c:158
int find_other_exec(const char *argv0, const char *target, const char *versionstr, char *retpath)
Definition: exec.c:327
static void const char fflush(stdout)
exit(1)
#define pg_log_error(...)
Definition: logging.h:106
#define pg_log_info(...)
Definition: logging.h:124
#define pg_log_error_detail(...)
Definition: logging.h:109
#define MAXPGPATH
static char * argv0
Definition: pg_ctl.c:92
static char * exec_path
Definition: pg_ctl.c:87
char * datadir_target
Definition: pg_rewind.c:66
char * config_file
Definition: pg_rewind.c:70
bool dry_run
Definition: pg_rewind.c:74
const char * progname
Definition: pg_rewind.c:62
#define DEVNULL
Definition: port.h:160
#define PG_BACKEND_VERSIONSTR
Definition: port.h:143
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
PQExpBuffer createPQExpBuffer(void)
Definition: pqexpbuffer.c:72
void destroyPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:114
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:367
void appendShellString(PQExpBuffer buf, const char *str)
Definition: string_utils.c:429

References appendPQExpBufferStr(), appendShellString(), argv0, config_file, createPQExpBuffer(), PQExpBufferData::data, datadir_target, destroyPQExpBuffer(), DEVNULL, dry_run, exec_path, exit(), fflush(), find_my_exec(), find_other_exec(), MAXPGPATH, PG_BACKEND_VERSIONSTR, pg_fatal, pg_log_error, pg_log_error_detail, pg_log_info, progname, and strlcpy().

Referenced by main().

◆ findCommonAncestorTimeline()

static void findCommonAncestorTimeline ( TimeLineHistoryEntry a_history,
int  a_nentries,
TimeLineHistoryEntry b_history,
int  b_nentries,
XLogRecPtr recptr,
int *  tliIndex 
)
static

Definition at line 909 of file pg_rewind.c.

912 {
913  int i,
914  n;
915 
916  /*
917  * Trace the history forward, until we hit the timeline diverge. It may
918  * still be possible that the source and target nodes used the same
919  * timeline number in their history but with different start position
920  * depending on the history files that each node has fetched in previous
921  * recovery processes. Hence check the start position of the new timeline
922  * as well and move down by one extra timeline entry if they do not match.
923  */
924  n = Min(a_nentries, b_nentries);
925  for (i = 0; i < n; i++)
926  {
927  if (a_history[i].tli != b_history[i].tli ||
928  a_history[i].begin != b_history[i].begin)
929  break;
930  }
931 
932  if (i > 0)
933  {
934  i--;
935  *recptr = MinXLogRecPtr(a_history[i].end, b_history[i].end);
936  *tliIndex = i;
937  return;
938  }
939  else
940  {
941  pg_fatal("could not find common ancestor of the source and target cluster's timelines");
942  }
943 }
#define Min(x, y)
Definition: c.h:988
int i
Definition: isn.c:73
static XLogRecPtr MinXLogRecPtr(XLogRecPtr a, XLogRecPtr b)
Definition: pg_rewind.c:830

References i, Min, MinXLogRecPtr(), and pg_fatal.

Referenced by main().

◆ getRestoreCommand()

static void getRestoreCommand ( const char *  argv0)
static

Definition at line 1042 of file pg_rewind.c.

1043 {
1044  int rc;
1045  char postgres_exec_path[MAXPGPATH],
1046  cmd_output[MAXPGPATH];
1047  PQExpBuffer postgres_cmd;
1048 
1049  if (!restore_wal)
1050  return;
1051 
1052  /* find postgres executable */
1053  rc = find_other_exec(argv0, "postgres",
1055  postgres_exec_path);
1056 
1057  if (rc < 0)
1058  {
1059  char full_path[MAXPGPATH];
1060 
1061  if (find_my_exec(argv0, full_path) < 0)
1062  strlcpy(full_path, progname, sizeof(full_path));
1063 
1064  if (rc == -1)
1065  pg_fatal("program \"%s\" is needed by %s but was not found in the same directory as \"%s\"",
1066  "postgres", progname, full_path);
1067  else
1068  pg_fatal("program \"%s\" was found by \"%s\" but was not the same version as %s",
1069  "postgres", full_path, progname);
1070  }
1071 
1072  /*
1073  * Build a command able to retrieve the value of GUC parameter
1074  * restore_command, if set.
1075  */
1076  postgres_cmd = createPQExpBuffer();
1077 
1078  /* path to postgres, properly quoted */
1079  appendShellString(postgres_cmd, postgres_exec_path);
1080 
1081  /* add -D switch, with properly quoted data directory */
1082  appendPQExpBufferStr(postgres_cmd, " -D ");
1083  appendShellString(postgres_cmd, datadir_target);
1084 
1085  /* add custom configuration file only if requested */
1086  if (config_file != NULL)
1087  {
1088  appendPQExpBufferStr(postgres_cmd, " -c config_file=");
1089  appendShellString(postgres_cmd, config_file);
1090  }
1091 
1092  /* add -C switch, for restore_command */
1093  appendPQExpBufferStr(postgres_cmd, " -C restore_command");
1094 
1095  if (!pipe_read_line(postgres_cmd->data, cmd_output, sizeof(cmd_output)))
1096  exit(1);
1097 
1098  (void) pg_strip_crlf(cmd_output);
1099 
1100  if (strcmp(cmd_output, "") == 0)
1101  pg_fatal("restore_command is not set in the target cluster");
1102 
1103  restore_command = pg_strdup(cmd_output);
1104 
1105  pg_log_debug("using for rewind restore_command = \'%s\'",
1106  restore_command);
1107 
1108  destroyPQExpBuffer(postgres_cmd);
1109 }
char * pipe_read_line(char *cmd, char *line, int maxsize)
Definition: exec.c:363
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
#define pg_log_debug(...)
Definition: logging.h:133
char * restore_command
Definition: pg_rewind.c:69
bool restore_wal
Definition: pg_rewind.c:76
int pg_strip_crlf(char *str)
Definition: string.c:155

References appendPQExpBufferStr(), appendShellString(), argv0, config_file, createPQExpBuffer(), PQExpBufferData::data, datadir_target, destroyPQExpBuffer(), exit(), find_my_exec(), find_other_exec(), MAXPGPATH, PG_BACKEND_VERSIONSTR, pg_fatal, pg_log_debug, pg_strdup(), pg_strip_crlf(), pipe_read_line(), progname, restore_command, restore_wal, and strlcpy().

Referenced by main().

◆ getTimelineHistory()

static TimeLineHistoryEntry * getTimelineHistory ( TimeLineID  tli,
bool  is_source,
int *  nentries 
)
static

Definition at line 844 of file pg_rewind.c.

845 {
846  TimeLineHistoryEntry *history;
847 
848  /*
849  * Timeline 1 does not have a history file, so there is no need to check
850  * and fake an entry with infinite start and end positions.
851  */
852  if (tli == 1)
853  {
854  history = (TimeLineHistoryEntry *) pg_malloc(sizeof(TimeLineHistoryEntry));
855  history->tli = tli;
856  history->begin = history->end = InvalidXLogRecPtr;
857  *nentries = 1;
858  }
859  else
860  {
861  char path[MAXPGPATH];
862  char *histfile;
863 
864  TLHistoryFilePath(path, tli);
865 
866  /* Get history file from appropriate source */
867  if (is_source)
868  histfile = source->fetch_file(source, path, NULL);
869  else
870  histfile = slurpFile(datadir_target, path, NULL);
871 
872  history = rewind_parseTimeLineHistory(histfile, tli, nentries);
873  pg_free(histfile);
874  }
875 
876  if (debug)
877  {
878  int i;
879 
880  if (is_source)
881  pg_log_debug("Source timeline history:");
882  else
883  pg_log_debug("Target timeline history:");
884 
885  /*
886  * Print the target timeline history.
887  */
888  for (i = 0; i < targetNentries; i++)
889  {
890  TimeLineHistoryEntry *entry;
891 
892  entry = &history[i];
893  pg_log_debug("%u: %X/%X - %X/%X", entry->tli,
894  LSN_FORMAT_ARGS(entry->begin),
895  LSN_FORMAT_ARGS(entry->end));
896  }
897  }
898 
899  return history;
900 }
void pg_free(void *ptr)
Definition: fe_memutils.c:105
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
char * slurpFile(const char *datadir, const char *path, size_t *filesize)
Definition: file_ops.c:314
static bool debug
Definition: pg_rewind.c:72
static rewind_source * source
Definition: pg_rewind.c:87
int targetNentries
Definition: pg_rewind.c:80
TimeLineHistoryEntry * rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
Definition: timeline.c:29
XLogRecPtr begin
Definition: timeline.h:28
TimeLineID tli
Definition: timeline.h:27
XLogRecPtr end
Definition: timeline.h:29
char *(* fetch_file)(struct rewind_source *, const char *path, size_t *filesize)
Definition: rewind_source.h:37
static void TLHistoryFilePath(char *path, TimeLineID tli)
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28

References TimeLineHistoryEntry::begin, datadir_target, debug, TimeLineHistoryEntry::end, rewind_source::fetch_file, i, InvalidXLogRecPtr, LSN_FORMAT_ARGS, MAXPGPATH, pg_free(), pg_log_debug, pg_malloc(), rewind_parseTimeLineHistory(), slurpFile(), source, targetNentries, TLHistoryFilePath(), and TimeLineHistoryEntry::tli.

Referenced by main().

◆ main()

int main ( int  argc,
char **  argv 
)

Definition at line 118 of file pg_rewind.c.

119 {
120  static struct option long_options[] = {
121  {"help", no_argument, NULL, '?'},
122  {"target-pgdata", required_argument, NULL, 'D'},
123  {"write-recovery-conf", no_argument, NULL, 'R'},
124  {"source-pgdata", required_argument, NULL, 1},
125  {"source-server", required_argument, NULL, 2},
126  {"no-ensure-shutdown", no_argument, NULL, 4},
127  {"config-file", required_argument, NULL, 5},
128  {"version", no_argument, NULL, 'V'},
129  {"restore-target-wal", no_argument, NULL, 'c'},
130  {"dry-run", no_argument, NULL, 'n'},
131  {"no-sync", no_argument, NULL, 'N'},
132  {"progress", no_argument, NULL, 'P'},
133  {"debug", no_argument, NULL, 3},
134  {NULL, 0, NULL, 0}
135  };
136  int option_index;
137  int c;
138  XLogRecPtr divergerec;
139  int lastcommontliIndex;
140  XLogRecPtr chkptrec;
141  TimeLineID chkpttli;
142  XLogRecPtr chkptredo;
143  TimeLineID source_tli;
144  TimeLineID target_tli;
145  XLogRecPtr target_wal_endrec;
146  size_t size;
147  char *buffer;
148  bool no_ensure_shutdown = false;
149  bool rewind_needed;
150  bool writerecoveryconf = false;
151  filemap_t *filemap;
152 
153  pg_logging_init(argv[0]);
154  set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_rewind"));
155  progname = get_progname(argv[0]);
156 
157  /* Process command-line arguments */
158  if (argc > 1)
159  {
160  if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
161  {
162  usage(progname);
163  exit(0);
164  }
165  if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
166  {
167  puts("pg_rewind (PostgreSQL) " PG_VERSION);
168  exit(0);
169  }
170  }
171 
172  while ((c = getopt_long(argc, argv, "cD:nNPR", long_options, &option_index)) != -1)
173  {
174  switch (c)
175  {
176  case 'c':
177  restore_wal = true;
178  break;
179 
180  case 'P':
181  showprogress = true;
182  break;
183 
184  case 'n':
185  dry_run = true;
186  break;
187 
188  case 'N':
189  do_sync = false;
190  break;
191 
192  case 'R':
193  writerecoveryconf = true;
194  break;
195 
196  case 3:
197  debug = true;
199  break;
200 
201  case 'D': /* -D or --target-pgdata */
203  break;
204 
205  case 1: /* --source-pgdata */
207  break;
208 
209  case 2: /* --source-server */
211  break;
212 
213  case 4:
214  no_ensure_shutdown = true;
215  break;
216 
217  case 5:
219  break;
220 
221  default:
222  /* getopt_long already emitted a complaint */
223  pg_log_error_hint("Try \"%s --help\" for more information.", progname);
224  exit(1);
225  }
226  }
227 
228  if (datadir_source == NULL && connstr_source == NULL)
229  {
230  pg_log_error("no source specified (--source-pgdata or --source-server)");
231  pg_log_error_hint("Try \"%s --help\" for more information.", progname);
232  exit(1);
233  }
234 
235  if (datadir_source != NULL && connstr_source != NULL)
236  {
237  pg_log_error("only one of --source-pgdata or --source-server can be specified");
238  pg_log_error_hint("Try \"%s --help\" for more information.", progname);
239  exit(1);
240  }
241 
242  if (datadir_target == NULL)
243  {
244  pg_log_error("no target data directory specified (--target-pgdata)");
245  pg_log_error_hint("Try \"%s --help\" for more information.", progname);
246  exit(1);
247  }
248 
249  if (writerecoveryconf && connstr_source == NULL)
250  {
251  pg_log_error("no source server information (--source-server) specified for --write-recovery-conf");
252  pg_log_error_hint("Try \"%s --help\" for more information.", progname);
253  exit(1);
254  }
255 
256  if (optind < argc)
257  {
258  pg_log_error("too many command-line arguments (first is \"%s\")",
259  argv[optind]);
260  pg_log_error_hint("Try \"%s --help\" for more information.", progname);
261  exit(1);
262  }
263 
264  /*
265  * Don't allow pg_rewind to be run as root, to avoid overwriting the
266  * ownership of files in the data directory. We need only check for root
267  * -- any other user won't have sufficient permissions to modify files in
268  * the data directory.
269  */
270 #ifndef WIN32
271  if (geteuid() == 0)
272  {
273  pg_log_error("cannot be executed by \"root\"");
274  pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
275  progname);
276  exit(1);
277  }
278 #endif
279 
281 
282  /* Set mask based on PGDATA permissions */
284  pg_fatal("could not read permissions of directory \"%s\": %m",
286 
287  umask(pg_mode_mask);
288 
289  getRestoreCommand(argv[0]);
290 
291  atexit(disconnect_atexit);
292 
293  /*
294  * Ok, we have all the options and we're ready to start. First, connect to
295  * remote server.
296  */
297  if (connstr_source)
298  {
300 
301  if (PQstatus(conn) == CONNECTION_BAD)
302  pg_fatal("%s", PQerrorMessage(conn));
303 
304  if (showprogress)
305  pg_log_info("connected to server");
306 
308  }
309  else
311 
312  /*
313  * Check the status of the target instance.
314  *
315  * If the target instance was not cleanly shut down, start and stop the
316  * target cluster once in single-user mode to enforce recovery to finish,
317  * ensuring that the cluster can be used by pg_rewind. Note that if
318  * no_ensure_shutdown is specified, pg_rewind ignores this step, and users
319  * need to make sure by themselves that the target cluster is in a clean
320  * state.
321  */
322  buffer = slurpFile(datadir_target, "global/pg_control", &size);
323  digestControlFile(&ControlFile_target, buffer, size);
324  pg_free(buffer);
325 
326  if (!no_ensure_shutdown &&
329  {
330  ensureCleanShutdown(argv[0]);
331 
332  buffer = slurpFile(datadir_target, "global/pg_control", &size);
333  digestControlFile(&ControlFile_target, buffer, size);
334  pg_free(buffer);
335  }
336 
337  buffer = source->fetch_file(source, "global/pg_control", &size);
338  digestControlFile(&ControlFile_source, buffer, size);
339  pg_free(buffer);
340 
341  sanityChecks();
342 
343  /*
344  * Usually, the TLI can be found in the latest checkpoint record. But if
345  * the source server is just being promoted (or it's a standby that's
346  * following a primary that's just being promoted), and the checkpoint
347  * requested by the promotion hasn't completed yet, the latest timeline is
348  * in minRecoveryPoint. So we check which is later, the TLI of the
349  * minRecoveryPoint or the latest checkpoint.
350  */
353 
354  /* Similarly for the target. */
357 
358  /*
359  * Find the common ancestor timeline between the clusters.
360  *
361  * If both clusters are already on the same timeline, there's nothing to
362  * do.
363  */
364  if (target_tli == source_tli)
365  {
366  pg_log_info("source and target cluster are on the same timeline");
367  rewind_needed = false;
368  target_wal_endrec = 0;
369  }
370  else
371  {
372  XLogRecPtr chkptendrec;
373  TimeLineHistoryEntry *sourceHistory;
374  int sourceNentries;
375 
376  /*
377  * Retrieve timelines for both source and target, and find the point
378  * where they diverged.
379  */
380  sourceHistory = getTimelineHistory(source_tli, true, &sourceNentries);
381  targetHistory = getTimelineHistory(target_tli, false, &targetNentries);
382 
383  findCommonAncestorTimeline(sourceHistory, sourceNentries,
385  &divergerec, &lastcommontliIndex);
386 
387  pg_log_info("servers diverged at WAL location %X/%X on timeline %u",
388  LSN_FORMAT_ARGS(divergerec),
389  targetHistory[lastcommontliIndex].tli);
390 
391  /*
392  * Don't need the source history anymore. The target history is still
393  * needed by the routines in parsexlog.c, when we read the target WAL.
394  */
395  pfree(sourceHistory);
396 
397 
398  /*
399  * Determine the end-of-WAL on the target.
400  *
401  * The WAL ends at the last shutdown checkpoint, or at
402  * minRecoveryPoint if it was a standby. (If we supported rewinding a
403  * server that was not shut down cleanly, we would need to replay
404  * until we reach the first invalid record, like crash recovery does.)
405  */
406 
407  /* read the checkpoint record on the target to see where it ends. */
408  chkptendrec = readOneRecord(datadir_target,
410  targetNentries - 1,
412 
413  if (ControlFile_target.minRecoveryPoint > chkptendrec)
414  {
415  target_wal_endrec = ControlFile_target.minRecoveryPoint;
416  }
417  else
418  {
419  target_wal_endrec = chkptendrec;
420  }
421 
422  /*
423  * Check for the possibility that the target is in fact a direct
424  * ancestor of the source. In that case, there is no divergent history
425  * in the target that needs rewinding.
426  */
427  if (target_wal_endrec > divergerec)
428  {
429  rewind_needed = true;
430  }
431  else
432  {
433  /* the last common checkpoint record must be part of target WAL */
434  Assert(target_wal_endrec == divergerec);
435 
436  rewind_needed = false;
437  }
438  }
439 
440  if (!rewind_needed)
441  {
442  pg_log_info("no rewind required");
443  if (writerecoveryconf && !dry_run)
446  exit(0);
447  }
448 
449  findLastCheckpoint(datadir_target, divergerec, lastcommontliIndex,
450  &chkptrec, &chkpttli, &chkptredo, restore_command);
451  pg_log_info("rewinding from last common checkpoint at %X/%X on timeline %u",
452  LSN_FORMAT_ARGS(chkptrec), chkpttli);
453 
454  /* Initialize the hash table to track the status of each file */
455  filehash_init();
456 
457  /*
458  * Collect information about all files in the both data directories.
459  */
460  if (showprogress)
461  pg_log_info("reading source file list");
463 
464  if (showprogress)
465  pg_log_info("reading target file list");
467 
468  /*
469  * Read the target WAL from last checkpoint before the point of fork, to
470  * extract all the pages that were modified on the target cluster after
471  * the fork.
472  */
473  if (showprogress)
474  pg_log_info("reading WAL in target");
475  extractPageMap(datadir_target, chkptrec, lastcommontliIndex,
476  target_wal_endrec, restore_command);
477 
478  /*
479  * We have collected all information we need from both systems. Decide
480  * what to do with each file.
481  */
482  filemap = decide_file_actions();
483  if (showprogress)
484  calculate_totals(filemap);
485 
486  /* this is too verbose even for verbose mode */
487  if (debug)
488  print_filemap(filemap);
489 
490  /*
491  * Ok, we're ready to start copying things over.
492  */
493  if (showprogress)
494  {
495  pg_log_info("need to copy %lu MB (total source directory size is %lu MB)",
496  (unsigned long) (filemap->fetch_size / (1024 * 1024)),
497  (unsigned long) (filemap->total_size / (1024 * 1024)));
498 
499  fetch_size = filemap->fetch_size;
500  fetch_done = 0;
501  }
502 
503  /*
504  * We have now collected all the information we need from both systems,
505  * and we are ready to start modifying the target directory.
506  *
507  * This is the point of no return. Once we start copying things, there is
508  * no turning back!
509  */
510  perform_rewind(filemap, source, chkptrec, chkpttli, chkptredo);
511 
512  if (showprogress)
513  pg_log_info("syncing target data directory");
514  sync_target_dir();
515 
516  /* Also update the standby configuration, if requested. */
517  if (writerecoveryconf && !dry_run)
520 
521  /* don't need the source connection anymore */
523  if (conn)
524  {
525  PQfinish(conn);
526  conn = NULL;
527  }
528 
529  pg_log_info("Done!");
530 
531  return 0;
532 }
#define Max(x, y)
Definition: c.h:982
#define PG_TEXTDOMAIN(domain)
Definition: c.h:1204
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition: exec.c:436
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:7173
ConnStatusType PQstatus(const PGconn *conn)
Definition: fe-connect.c:7120
PGconn * PQconnectdb(const char *conninfo)
Definition: fe-connect.c:728
void traverse_datadir(const char *datadir, process_file_callback_t callback)
Definition: file_ops.c:362
void sync_target_dir(void)
Definition: file_ops.c:294
int pg_mode_mask
Definition: file_perm.c:25
bool GetDataDirectoryCreatePerm(const char *dataDir)
void filehash_init(void)
Definition: filemap.c:168
void process_source_file(const char *path, file_type_t type, size_t size, const char *link_target)
Definition: filemap.c:218
void print_filemap(filemap_t *filemap)
Definition: filemap.c:479
void process_target_file(const char *path, file_type_t type, size_t size, const char *link_target)
Definition: filemap.c:254
filemap_t * decide_file_actions(void)
Definition: filemap.c:788
void calculate_totals(filemap_t *filemap)
Definition: filemap.c:438
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition: getopt_long.c:57
#define no_argument
Definition: getopt_long.h:24
#define required_argument
Definition: getopt_long.h:25
@ CONNECTION_BAD
Definition: libpq-fe.h:61
Assert(fmt[strlen(fmt) - 1] !='\n')
rewind_source * init_libpq_source(PGconn *conn)
Definition: libpq_source.c:82
rewind_source * init_local_source(const char *datadir)
Definition: local_source.c:40
void pg_logging_increase_verbosity(void)
Definition: logging.c:182
void pg_logging_init(const char *argv0)
Definition: logging.c:83
#define pg_log_error_hint(...)
Definition: logging.h:112
void pfree(void *pointer)
Definition: mcxt.c:1436
void extractPageMap(const char *datadir, XLogRecPtr startpoint, int tliIndex, XLogRecPtr endpoint, const char *restoreCommand)
Definition: parsexlog.c:66
void findLastCheckpoint(const char *datadir, XLogRecPtr forkptr, int tliIndex, XLogRecPtr *lastchkptrec, TimeLineID *lastchkpttli, XLogRecPtr *lastchkptredo, const char *restoreCommand)
Definition: parsexlog.c:168
XLogRecPtr readOneRecord(const char *datadir, XLogRecPtr ptr, int tliIndex, const char *restoreCommand)
Definition: parsexlog.c:124
static bool writerecoveryconf
@ DB_SHUTDOWNED_IN_RECOVERY
Definition: pg_control.h:91
@ DB_SHUTDOWNED
Definition: pg_control.h:90
PGDLLIMPORT int optind
Definition: getopt.c:50
PGDLLIMPORT char * optarg
Definition: getopt.c:52
static ControlFileData ControlFile_source
Definition: pg_rewind.c:59
static void usage(const char *progname)
Definition: pg_rewind.c:90
static void sanityChecks(void)
Definition: pg_rewind.c:721
char * datadir_source
Definition: pg_rewind.c:67
static void findCommonAncestorTimeline(TimeLineHistoryEntry *a_history, int a_nentries, TimeLineHistoryEntry *b_history, int b_nentries, XLogRecPtr *recptr, int *tliIndex)
Definition: pg_rewind.c:909
static void ensureCleanShutdown(const char *argv0)
Definition: pg_rewind.c:1117
TimeLineHistoryEntry * targetHistory
Definition: pg_rewind.c:79
static ControlFileData ControlFile_target
Definition: pg_rewind.c:58
static TimeLineHistoryEntry * getTimelineHistory(TimeLineID tli, bool is_source, int *nentries)
Definition: pg_rewind.c:844
static void digestControlFile(ControlFileData *ControlFile, const char *content, size_t size)
Definition: pg_rewind.c:1013
char * connstr_source
Definition: pg_rewind.c:68
static void getRestoreCommand(const char *argv0)
Definition: pg_rewind.c:1042
bool do_sync
Definition: pg_rewind.c:75
uint64 fetch_done
Definition: pg_rewind.c:84
uint64 fetch_size
Definition: pg_rewind.c:83
bool showprogress
Definition: pg_rewind.c:73
static void perform_rewind(filemap_t *filemap, rewind_source *source, XLogRecPtr chkptrec, TimeLineID chkpttli, XLogRecPtr chkptredo)
Definition: pg_rewind.c:541
static void disconnect_atexit(void)
Definition: pg_rewind.c:1189
const char * get_progname(const char *argv0)
Definition: path.c:574
char * c
void WriteRecoveryConfig(PGconn *pgconn, char *target_dir, PQExpBuffer contents)
Definition: recovery_gen.c:105
PQExpBuffer GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
Definition: recovery_gen.c:23
void get_restricted_token(void)
TimeLineID ThisTimeLineID
Definition: pg_control.h:39
CheckPoint checkPointCopy
Definition: pg_control.h:133
XLogRecPtr minRecoveryPoint
Definition: pg_control.h:166
XLogRecPtr checkPoint
Definition: pg_control.h:131
TimeLineID minRecoveryPointTLI
Definition: pg_control.h:167
uint64 total_size
Definition: filemap.h:92
uint64 fetch_size
Definition: filemap.h:93
void(* traverse_files)(struct rewind_source *, process_file_callback_t callback)
Definition: rewind_source.h:29
void(* destroy)(struct rewind_source *)
Definition: rewind_source.h:76
uint64 XLogRecPtr
Definition: xlogdefs.h:21
uint32 TimeLineID
Definition: xlogdefs.h:59

References Assert(), calculate_totals(), ControlFileData::checkPoint, ControlFileData::checkPointCopy, config_file, conn, CONNECTION_BAD, connstr_source, ControlFile_source, ControlFile_target, datadir_source, datadir_target, DB_SHUTDOWNED, DB_SHUTDOWNED_IN_RECOVERY, debug, decide_file_actions(), rewind_source::destroy, digestControlFile(), disconnect_atexit(), do_sync, dry_run, ensureCleanShutdown(), exit(), extractPageMap(), fetch_done, rewind_source::fetch_file, filemap_t::fetch_size, fetch_size, filehash_init(), findCommonAncestorTimeline(), findLastCheckpoint(), GenerateRecoveryConfig(), get_progname(), get_restricted_token(), GetDataDirectoryCreatePerm(), getopt_long(), getRestoreCommand(), getTimelineHistory(), init_libpq_source(), init_local_source(), LSN_FORMAT_ARGS, Max, ControlFileData::minRecoveryPoint, ControlFileData::minRecoveryPointTLI, no_argument, optarg, optind, perform_rewind(), pfree(), pg_fatal, pg_free(), pg_log_error, pg_log_error_hint, pg_log_info, pg_logging_increase_verbosity(), pg_logging_init(), pg_mode_mask, pg_strdup(), PG_TEXTDOMAIN, PQconnectdb(), PQerrorMessage(), PQfinish(), PQstatus(), print_filemap(), process_source_file(), process_target_file(), progname, readOneRecord(), required_argument, restore_command, restore_wal, sanityChecks(), set_pglocale_pgservice(), showprogress, slurpFile(), source, ControlFileData::state, sync_target_dir(), targetHistory, targetNentries, CheckPoint::ThisTimeLineID, filemap_t::total_size, traverse_datadir(), rewind_source::traverse_files, usage(), writerecoveryconf, and WriteRecoveryConfig().

◆ MinXLogRecPtr()

static XLogRecPtr MinXLogRecPtr ( XLogRecPtr  a,
XLogRecPtr  b 
)
static

Definition at line 830 of file pg_rewind.c.

831 {
832  if (XLogRecPtrIsInvalid(a))
833  return b;
834  else if (XLogRecPtrIsInvalid(b))
835  return a;
836  else
837  return Min(a, b);
838 }
int b
Definition: isn.c:70
int a
Definition: isn.c:69
#define XLogRecPtrIsInvalid(r)
Definition: xlogdefs.h:29

References a, b, Min, and XLogRecPtrIsInvalid.

Referenced by findCommonAncestorTimeline().

◆ perform_rewind()

static void perform_rewind ( filemap_t filemap,
rewind_source source,
XLogRecPtr  chkptrec,
TimeLineID  chkpttli,
XLogRecPtr  chkptredo 
)
static

Definition at line 541 of file pg_rewind.c.

545 {
546  XLogRecPtr endrec;
547  TimeLineID endtli;
548  ControlFileData ControlFile_new;
549  size_t size;
550  char *buffer;
551 
552  /*
553  * Execute the actions in the file map, fetching data from the source
554  * system as needed.
555  */
556  for (int i = 0; i < filemap->nentries; i++)
557  {
558  file_entry_t *entry = filemap->entries[i];
559 
560  /*
561  * If this is a relation file, copy the modified blocks.
562  *
563  * This is in addition to any other changes.
564  */
565  if (entry->target_pages_to_overwrite.bitmapsize > 0)
566  {
568  BlockNumber blkno;
569  off_t offset;
570 
572  while (datapagemap_next(iter, &blkno))
573  {
574  offset = blkno * BLCKSZ;
575  source->queue_fetch_range(source, entry->path, offset, BLCKSZ);
576  }
577  pg_free(iter);
578  }
579 
580  switch (entry->action)
581  {
582  case FILE_ACTION_NONE:
583  /* nothing else to do */
584  break;
585 
586  case FILE_ACTION_COPY:
587  source->queue_fetch_file(source, entry->path, entry->source_size);
588  break;
589 
591  truncate_target_file(entry->path, entry->source_size);
592  break;
593 
596  entry->target_size,
597  entry->source_size - entry->target_size);
598  break;
599 
600  case FILE_ACTION_REMOVE:
601  remove_target(entry);
602  break;
603 
604  case FILE_ACTION_CREATE:
605  create_target(entry);
606  break;
607 
609  pg_fatal("no action decided for file \"%s\"", entry->path);
610  break;
611  }
612  }
613 
614  /* Complete any remaining range-fetches that we queued up above. */
616 
618 
619  progress_report(true);
620 
621  /*
622  * Fetch the control file from the source last. This ensures that the
623  * minRecoveryPoint is up-to-date.
624  */
625  buffer = source->fetch_file(source, "global/pg_control", &size);
627  pg_free(buffer);
628 
629  /*
630  * Sanity check: If the source is a local system, the control file should
631  * not have changed since we started.
632  *
633  * XXX: We assume it hasn't been modified, but actually, what could go
634  * wrong? The logic handles a libpq source that's modified concurrently,
635  * why not a local datadir?
636  */
637  if (datadir_source &&
639  sizeof(ControlFileData)) != 0)
640  {
641  pg_fatal("source system was modified while pg_rewind was running");
642  }
643 
644  if (showprogress)
645  pg_log_info("creating backup label and updating control file");
646 
647  /*
648  * Create a backup label file, to tell the target where to begin the WAL
649  * replay. Normally, from the last common checkpoint between the source
650  * and the target. But if the source is a standby server, it's possible
651  * that the last common checkpoint is *after* the standby's restartpoint.
652  * That implies that the source server has applied the checkpoint record,
653  * but hasn't performed a corresponding restartpoint yet. Make sure we
654  * start at the restartpoint's redo point in that case.
655  *
656  * Use the old version of the source's control file for this. The server
657  * might have finished the restartpoint after we started copying files,
658  * but we must begin from the redo point at the time that started copying.
659  */
660  if (ControlFile_source.checkPointCopy.redo < chkptredo)
661  {
664  chkptrec = ControlFile_source.checkPoint;
665  }
666  createBackupLabel(chkptredo, chkpttli, chkptrec);
667 
668  /*
669  * Update control file of target, to tell the target how far it must
670  * replay the WAL (minRecoveryPoint).
671  */
672  if (connstr_source)
673  {
674  /*
675  * The source is a live server. Like in an online backup, it's
676  * important that we recover all the WAL that was generated while we
677  * were copying files.
678  */
680  {
681  /*
682  * Source is a standby server. We must replay to its
683  * minRecoveryPoint.
684  */
687  }
688  else
689  {
690  /*
691  * Source is a production, non-standby, server. We must replay to
692  * the last WAL insert location.
693  */
695  pg_fatal("source system was in unexpected state at end of rewind");
696 
700  }
701  }
702  else
703  {
704  /*
705  * Source is a local data directory. It should've shut down cleanly,
706  * and we must replay to the latest shutdown checkpoint.
707  */
710  }
711 
712  memcpy(&ControlFile_new, &ControlFile_source_after, sizeof(ControlFileData));
713  ControlFile_new.minRecoveryPoint = endrec;
714  ControlFile_new.minRecoveryPointTLI = endtli;
715  ControlFile_new.state = DB_IN_ARCHIVE_RECOVERY;
716  if (!dry_run)
717  update_controlfile(datadir_target, &ControlFile_new, do_sync);
718 }
uint32 BlockNumber
Definition: block.h:31
void update_controlfile(const char *DataDir, ControlFileData *ControlFile, bool do_sync)
bool datapagemap_next(datapagemap_iterator_t *iter, BlockNumber *blkno)
Definition: datapagemap.c:87
datapagemap_iterator_t * datapagemap_iterate(datapagemap_t *map)
Definition: datapagemap.c:75
void truncate_target_file(const char *path, off_t newsize)
Definition: file_ops.c:206
void remove_target(file_entry_t *entry)
Definition: file_ops.c:130
void create_target(file_entry_t *entry)
Definition: file_ops.c:156
@ FILE_ACTION_REMOVE
Definition: filemap.h:27
@ FILE_ACTION_COPY
Definition: filemap.h:21
@ FILE_ACTION_NONE
Definition: filemap.h:24
@ FILE_ACTION_COPY_TAIL
Definition: filemap.h:22
@ FILE_ACTION_UNDECIDED
Definition: filemap.h:18
@ FILE_ACTION_TRUNCATE
Definition: filemap.h:26
@ FILE_ACTION_CREATE
Definition: filemap.h:20
@ DB_IN_PRODUCTION
Definition: pg_control.h:95
@ DB_IN_ARCHIVE_RECOVERY
Definition: pg_control.h:94
static void createBackupLabel(XLogRecPtr startpoint, TimeLineID starttli, XLogRecPtr checkpointloc)
Definition: pg_rewind.c:951
static ControlFileData ControlFile_source_after
Definition: pg_rewind.c:60
void progress_report(bool finished)
Definition: pg_rewind.c:779
XLogRecPtr redo
Definition: pg_control.h:37
int bitmapsize
Definition: datapagemap.h:18
Definition: filemap.h:50
datapagemap_t target_pages_to_overwrite
Definition: filemap.h:68
const char * path
Definition: filemap.h:53
size_t source_size
Definition: filemap.h:75
size_t target_size
Definition: filemap.h:61
file_action_t action
Definition: filemap.h:81
file_entry_t * entries[FLEXIBLE_ARRAY_MEMBER]
Definition: filemap.h:96
int nentries
Definition: filemap.h:95
void(* queue_fetch_file)(struct rewind_source *, const char *path, size_t len)
Definition: rewind_source.h:60
void(* finish_fetch)(struct rewind_source *)
Definition: rewind_source.h:66
XLogRecPtr(* get_current_wal_insert_lsn)(struct rewind_source *)
Definition: rewind_source.h:71
void(* queue_fetch_range)(struct rewind_source *, const char *path, off_t offset, size_t len)
Definition: rewind_source.h:47

References file_entry_t::action, datapagemap::bitmapsize, ControlFileData::checkPoint, ControlFileData::checkPointCopy, close_target_file(), connstr_source, ControlFile_source, ControlFile_source_after, create_target(), createBackupLabel(), datadir_source, datadir_target, datapagemap_iterate(), datapagemap_next(), DB_IN_ARCHIVE_RECOVERY, DB_IN_PRODUCTION, digestControlFile(), do_sync, dry_run, filemap_t::entries, rewind_source::fetch_file, FILE_ACTION_COPY, FILE_ACTION_COPY_TAIL, FILE_ACTION_CREATE, FILE_ACTION_NONE, FILE_ACTION_REMOVE, FILE_ACTION_TRUNCATE, FILE_ACTION_UNDECIDED, rewind_source::finish_fetch, rewind_source::get_current_wal_insert_lsn, i, Max, ControlFileData::minRecoveryPoint, ControlFileData::minRecoveryPointTLI, filemap_t::nentries, file_entry_t::path, pg_fatal, pg_free(), pg_log_info, progress_report(), rewind_source::queue_fetch_file, rewind_source::queue_fetch_range, CheckPoint::redo, remove_target(), showprogress, source, file_entry_t::source_size, ControlFileData::state, file_entry_t::target_pages_to_overwrite, file_entry_t::target_size, CheckPoint::ThisTimeLineID, truncate_target_file(), and update_controlfile().

Referenced by main().

◆ progress_report()

static void progress_report ( bool  finished)

Definition at line 779 of file pg_rewind.c.

780 {
781  static pg_time_t last_progress_report = 0;
782  int percent;
783  char fetch_done_str[32];
784  char fetch_size_str[32];
785  pg_time_t now;
786 
787  if (!showprogress)
788  return;
789 
790  now = time(NULL);
791  if (now == last_progress_report && !finished)
792  return; /* Max once per second */
793 
795  percent = fetch_size ? (int) ((fetch_done) * 100 / fetch_size) : 0;
796 
797  /*
798  * Avoid overflowing past 100% or the full size. This may make the total
799  * size number change as we approach the end of the backup (the estimate
800  * will always be wrong if WAL is included), but that's better than having
801  * the done column be bigger than the total.
802  */
803  if (percent > 100)
804  percent = 100;
805  if (fetch_done > fetch_size)
807 
808  snprintf(fetch_done_str, sizeof(fetch_done_str), UINT64_FORMAT,
809  fetch_done / 1024);
810  snprintf(fetch_size_str, sizeof(fetch_size_str), UINT64_FORMAT,
811  fetch_size / 1024);
812 
813  fprintf(stderr, _("%*s/%s kB (%d%%) copied"),
814  (int) strlen(fetch_size_str), fetch_done_str, fetch_size_str,
815  percent);
816 
817  /*
818  * Stay on the same line if reporting to a terminal and we're not done
819  * yet.
820  */
821  fputc((!finished && isatty(fileno(stderr))) ? '\r' : '\n', stderr);
822 }
Datum now(PG_FUNCTION_ARGS)
Definition: timestamp.c:1546
#define UINT64_FORMAT
Definition: c.h:533
#define _(x)
Definition: elog.c:91
static pg_time_t last_progress_report
Definition: pg_amcheck.c:144
int64 pg_time_t
Definition: pgtime.h:23
#define fprintf
Definition: port.h:242

References _, fetch_done, fetch_size, fprintf, last_progress_report, now(), showprogress, snprintf, and UINT64_FORMAT.

Referenced by perform_rewind().

◆ sanityChecks()

static void sanityChecks ( void  )
static

Definition at line 721 of file pg_rewind.c.

722 {
723  /* TODO Check that there's no backup_label in either cluster */
724 
725  /* Check system_identifier match */
727  pg_fatal("source and target clusters are from different systems");
728 
729  /* check version */
734  {
735  pg_fatal("clusters are not compatible with this version of pg_rewind");
736  }
737 
738  /*
739  * Target cluster need to use checksums or hint bit wal-logging, this to
740  * prevent from data corruption that could occur because of hint bits.
741  */
744  {
745  pg_fatal("target server needs to use either data checksums or \"wal_log_hints = on\"");
746  }
747 
748  /*
749  * Target cluster better not be running. This doesn't guard against
750  * someone starting the cluster concurrently. Also, this is probably more
751  * strict than necessary; it's OK if the target node was not shut down
752  * cleanly, as long as it isn't running at the moment.
753  */
756  pg_fatal("target server must be shut down cleanly");
757 
758  /*
759  * When the source is a data directory, also require that the source
760  * server is shut down. There isn't any very strong reason for this
761  * limitation, but better safe than sorry.
762  */
763  if (datadir_source &&
766  pg_fatal("source data directory must be shut down cleanly");
767 }
#define PG_DATA_CHECKSUM_VERSION
Definition: bufpage.h:203
#define CATALOG_VERSION_NO
Definition: catversion.h:60
#define PG_CONTROL_VERSION
Definition: pg_control.h:25
uint32 pg_control_version
Definition: pg_control.h:123
uint32 data_checksum_version
Definition: pg_control.h:220
uint64 system_identifier
Definition: pg_control.h:108
uint32 catalog_version_no
Definition: pg_control.h:124

References CATALOG_VERSION_NO, ControlFileData::catalog_version_no, ControlFile_source, ControlFile_target, ControlFileData::data_checksum_version, datadir_source, DB_SHUTDOWNED, DB_SHUTDOWNED_IN_RECOVERY, PG_CONTROL_VERSION, ControlFileData::pg_control_version, PG_DATA_CHECKSUM_VERSION, pg_fatal, ControlFileData::state, ControlFileData::system_identifier, and ControlFileData::wal_log_hints.

Referenced by main().

◆ usage()

static void usage ( const char *  progname)
static

Definition at line 90 of file pg_rewind.c.

91 {
92  printf(_("%s resynchronizes a PostgreSQL cluster with another copy of the cluster.\n\n"), progname);
93  printf(_("Usage:\n %s [OPTION]...\n\n"), progname);
94  printf(_("Options:\n"));
95  printf(_(" -c, --restore-target-wal use restore_command in target configuration to\n"
96  " retrieve WAL files from archives\n"));
97  printf(_(" -D, --target-pgdata=DIRECTORY existing data directory to modify\n"));
98  printf(_(" --source-pgdata=DIRECTORY source data directory to synchronize with\n"));
99  printf(_(" --source-server=CONNSTR source server to synchronize with\n"));
100  printf(_(" -n, --dry-run stop before modifying anything\n"));
101  printf(_(" -N, --no-sync do not wait for changes to be written\n"
102  " safely to disk\n"));
103  printf(_(" -P, --progress write progress messages\n"));
104  printf(_(" -R, --write-recovery-conf write configuration for replication\n"
105  " (requires --source-server)\n"));
106  printf(_(" --config-file=FILENAME use specified main server configuration\n"
107  " file when running target cluster\n"));
108  printf(_(" --debug write a lot of debug messages\n"));
109  printf(_(" --no-ensure-shutdown do not automatically fix unclean shutdown\n"));
110  printf(_(" -V, --version output version information, then exit\n"));
111  printf(_(" -?, --help show this help, then exit\n"));
112  printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
113  printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
114 }
#define printf(...)
Definition: port.h:244

References _, printf, and progname.

Referenced by main().

Variable Documentation

◆ config_file

char* config_file = NULL

Definition at line 70 of file pg_rewind.c.

Referenced by ensureCleanShutdown(), getRestoreCommand(), and main().

◆ conn

PGconn* conn
static

Definition at line 86 of file pg_rewind.c.

Referenced by disconnect_atexit(), and main().

◆ connstr_source

char* connstr_source = NULL

Definition at line 68 of file pg_rewind.c.

Referenced by main(), and perform_rewind().

◆ ControlFile_source

ControlFileData ControlFile_source
static

Definition at line 59 of file pg_rewind.c.

Referenced by main(), perform_rewind(), and sanityChecks().

◆ ControlFile_source_after

ControlFileData ControlFile_source_after
static

Definition at line 60 of file pg_rewind.c.

Referenced by perform_rewind().

◆ ControlFile_target

ControlFileData ControlFile_target
static

Definition at line 58 of file pg_rewind.c.

Referenced by main(), and sanityChecks().

◆ datadir_source

char* datadir_source = NULL

Definition at line 67 of file pg_rewind.c.

Referenced by main(), perform_rewind(), and sanityChecks().

◆ datadir_target

◆ debug

bool debug = false
static

Definition at line 72 of file pg_rewind.c.

Referenced by getTimelineHistory(), and main().

◆ do_sync

bool do_sync = true

Definition at line 75 of file pg_rewind.c.

Referenced by main(), and perform_rewind().

◆ dry_run

◆ fetch_done

uint64 fetch_done

Definition at line 84 of file pg_rewind.c.

Referenced by main(), progress_report(), and write_target_range().

◆ fetch_size

uint64 fetch_size

Definition at line 83 of file pg_rewind.c.

Referenced by main(), postgresAcquireSampleRowsFunc(), and progress_report().

◆ progname

const char* progname

Definition at line 62 of file pg_rewind.c.

Referenced by ensureCleanShutdown(), getRestoreCommand(), main(), and usage().

◆ restore_command

char* restore_command = NULL

Definition at line 69 of file pg_rewind.c.

Referenced by getRestoreCommand(), and main().

◆ restore_wal

bool restore_wal = false

Definition at line 76 of file pg_rewind.c.

Referenced by getRestoreCommand(), and main().

◆ showprogress

bool showprogress = false

Definition at line 73 of file pg_rewind.c.

Referenced by main(), perform_rewind(), and progress_report().

◆ source

rewind_source* source
static

Definition at line 87 of file pg_rewind.c.

Referenced by append_pathkeys(), appendByteaLiteral(), appendStringLiteral(), ApplySetting(), bpchar(), BufFileAppend(), call_bool_check_hook(), call_enum_check_hook(), call_int_check_hook(), call_real_check_hook(), call_string_check_hook(), check_datestyle(), check_default_table_access_method(), check_default_tablespace(), check_default_text_search_config(), check_locale_messages(), check_random_seed(), check_role(), check_session_authorization(), check_temp_buffers(), check_temp_tablespaces(), check_timezone_abbreviations(), CopyIndexTuple(), exprLocation(), gets_fromFile(), getTimelineHistory(), index_truncate_tuple(), initClosestMatch(), InitializeGUCOptionsFromEnvironment(), interval_support(), libpq_destroy(), libpq_fetch_file(), libpq_finish_fetch(), libpq_get_current_wal_insert_lsn(), libpq_queue_fetch_file(), libpq_queue_fetch_range(), libpq_traverse_files(), local_destroy(), local_fetch_file(), local_queue_fetch_file(), local_queue_fetch_range(), local_traverse_files(), main(), MainLoop(), numeric_support(), packGraph(), parse_and_validate_value(), perform_rewind(), pg_utf8_islegal(), pg_utf8_string_len(), pglz_compress(), pglz_decompress(), plsample_func_handler(), plsample_trigger_handler(), PQescapeStringInternal(), process_backslash_command(), ProcessGUCArray(), quote_if_needed(), raw_expression_tree_walker_impl(), selectColorTrigrams(), set_config_option(), set_config_option_ext(), set_debug_options(), set_plan_disabling_options(), SetConfigOption(), string_to_uuid(), strip_quotes(), syntax_error(), TemporalSimplify(), translate(), varbit_support(), varchar(), varchar_support(), varstr_levenshtein(), XLogCompressBackupBlock(), XLogFileRead(), and XLogFileReadAnyTLI().

◆ targetHistory

TimeLineHistoryEntry* targetHistory

Definition at line 79 of file pg_rewind.c.

Referenced by main(), and SimpleXLogPageRead().

◆ targetNentries

int targetNentries

Definition at line 80 of file pg_rewind.c.

Referenced by getTimelineHistory(), main(), and SimpleXLogPageRead().

◆ WalSegSz

int WalSegSz

Definition at line 63 of file pg_rewind.c.

Referenced by createBackupLabel(), and digestControlFile().