4 * controldata functions
6 * Copyright (c) 2010-2025, PostgreSQL Global Development Group
7 * src/bin/pg_upgrade/controldata.c
13#include <limits.h> /* for CHAR_MIN */
23 * gets pg_control information in "ctrl". Assumes that bindir and
24 * datadir are valid absolute paths to postgresql bin and pgdata
25 * directories respectively *and* pg_resetwal is version compatible
26 * with datadir. The main purpose of this function is to get pg_control
27 * data in a version independent manner.
29 * The approach taken here is to invoke pg_resetwal with -n option
30 * and then pipe its output. With little string parsing we get the
31 * pg_control data. pg_resetwal cannot be run while the server is running
32 * so we use pg_controldata; pg_controldata doesn't provide all the fields
33 * we need to actually perform the upgrade, but it provides enough for
34 * check mode. We do not implement pg_resetwal -n because it is hard to
35 * return valid xid data for a running server.
45 bool got_log_id =
false;
46 bool got_log_seg =
false;
49 bool got_multi =
false;
50 bool got_oldestmulti =
false;
51 bool got_oldestxid =
false;
52 bool got_mxoff =
false;
53 bool got_nextxlogfile =
false;
54 bool got_float8_pass_by_value =
false;
55 bool got_align =
false;
56 bool got_blocksz =
false;
57 bool got_largesz =
false;
58 bool got_walsz =
false;
59 bool got_walseg =
false;
60 bool got_ident =
false;
61 bool got_index =
false;
62 bool got_toast =
false;
63 bool got_large_object =
false;
64 bool got_date_is_int =
false;
65 bool got_data_checksum_version =
false;
66 bool got_cluster_state =
false;
67 bool got_default_char_signedness =
false;
74 char *language = NULL;
85 * Because we test the pg_resetwal output as strings, it has to be in
86 * English. Copied from pg_regress.c.
88 if (getenv(
"LC_COLLATE"))
90 if (getenv(
"LC_CTYPE"))
92 if (getenv(
"LC_MONETARY"))
94 if (getenv(
"LC_NUMERIC"))
96 if (getenv(
"LC_TIME"))
100 if (getenv(
"LANGUAGE"))
101 language =
pg_strdup(getenv(
"LANGUAGE"));
102 if (getenv(
"LC_ALL"))
104 if (getenv(
"LC_MESSAGES"))
115 /* On Windows the default locale may not be English, so force it */
120 setenv(
"LC_MESSAGES",
"C", 1);
123 * Check for clean shutdown
127 /* only pg_controldata outputs the cluster state */
128 snprintf(cmd,
sizeof(cmd),
"\"%s/pg_controldata\" \"%s\"",
132 if ((
output = popen(cmd,
"r")) == NULL)
133 pg_fatal(
"could not get control data using %s: %m", cmd);
135 /* we have the result of cmd in "output". so parse it line by line now */
136 while (fgets(bufin,
sizeof(bufin),
output))
138 if ((p = strstr(bufin,
"Database cluster state:")) != NULL)
142 if (p == NULL || strlen(p) <= 1)
143 pg_fatal(
"%d: database cluster state problem", __LINE__);
145 p++;
/* remove ':' char */
148 * We checked earlier for a postmaster lock file, and if we
149 * found one, we tried to start/stop the server to replay the
150 * WAL. However, pg_ctl -m immediate doesn't leave a lock
151 * file, but does require WAL replay, so we check here that
152 * the server was shut down cleanly, from the controldata
155 /* Remove trailing newline and leading spaces */
159 if (strcmp(p,
"shut down in recovery") == 0)
162 pg_fatal(
"The source cluster was shut down while in recovery mode. To upgrade, use \"rsync\" as documented or shut it down as a primary.");
164 pg_fatal(
"The target cluster was shut down while in recovery mode. To upgrade, use \"rsync\" as documented or shut it down as a primary.");
166 else if (strcmp(p,
"shut down") != 0)
169 pg_fatal(
"The source cluster was not shut down cleanly, state reported as: \"%s\"", p);
171 pg_fatal(
"The target cluster was not shut down cleanly, state reported as: \"%s\"", p);
173 got_cluster_state =
true;
179 pg_fatal(
"could not get control data using %s: %s",
182 if (!got_cluster_state)
185 pg_fatal(
"The source cluster lacks cluster state information:");
187 pg_fatal(
"The target cluster lacks cluster state information:");
191 /* pg_resetxlog has been renamed to pg_resetwal in version 10 */
193 resetwal_bin =
"pg_resetxlog\" -n";
195 resetwal_bin =
"pg_resetwal\" -n";
196 snprintf(cmd,
sizeof(cmd),
"\"%s/%s \"%s\"",
198 live_check ?
"pg_controldata\"" : resetwal_bin,
202 if ((
output = popen(cmd,
"r")) == NULL)
203 pg_fatal(
"could not get control data using %s: %m", cmd);
208 cluster->controldata.data_checksum_version = 0;
209 got_data_checksum_version =
true;
212 /* we have the result of cmd in "output". so parse it line by line now */
213 while (fgets(bufin,
sizeof(bufin),
output))
215 /* In verbose mode, log each line */
219 if ((p = strstr(bufin,
"pg_control version number:")) != NULL)
223 if (p == NULL || strlen(p) <= 1)
224 pg_fatal(
"%d: pg_resetwal problem", __LINE__);
226 p++;
/* remove ':' char */
229 else if ((p = strstr(bufin,
"Catalog version number:")) != NULL)
233 if (p == NULL || strlen(p) <= 1)
234 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
236 p++;
/* remove ':' char */
239 else if ((p = strstr(bufin,
"Latest checkpoint's TimeLineID:")) != NULL)
243 if (p == NULL || strlen(p) <= 1)
244 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
246 p++;
/* remove ':' char */
250 else if ((p = strstr(bufin,
"First log file ID after reset:")) != NULL)
254 if (p == NULL || strlen(p) <= 1)
255 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
257 p++;
/* remove ':' char */
261 else if ((p = strstr(bufin,
"First log file segment after reset:")) != NULL)
265 if (p == NULL || strlen(p) <= 1)
266 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
268 p++;
/* remove ':' char */
272 else if ((p = strstr(bufin,
"Latest checkpoint's NextXID:")) != NULL)
276 if (p == NULL || strlen(p) <= 1)
277 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
279 p++;
/* remove ':' char */
283 * Delimiter changed from '/' to ':' in 9.6. We don't test for
284 * the catalog version of the change because the catalog version
285 * is pulled from pg_controldata too, and it isn't worth adding an
286 * order dependency for this --- we just check the string.
288 if (strchr(p,
'/') != NULL)
295 if (p == NULL || strlen(p) <= 1)
296 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
298 p++;
/* remove '/' or ':' char */
302 else if ((p = strstr(bufin,
"Latest checkpoint's NextOID:")) != NULL)
306 if (p == NULL || strlen(p) <= 1)
307 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
309 p++;
/* remove ':' char */
313 else if ((p = strstr(bufin,
"Latest checkpoint's NextMultiXactId:")) != NULL)
317 if (p == NULL || strlen(p) <= 1)
318 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
320 p++;
/* remove ':' char */
324 else if ((p = strstr(bufin,
"Latest checkpoint's oldestXID:")) != NULL)
328 if (p == NULL || strlen(p) <= 1)
329 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
331 p++;
/* remove ':' char */
333 got_oldestxid =
true;
335 else if ((p = strstr(bufin,
"Latest checkpoint's oldestMultiXid:")) != NULL)
339 if (p == NULL || strlen(p) <= 1)
340 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
342 p++;
/* remove ':' char */
344 got_oldestmulti =
true;
346 else if ((p = strstr(bufin,
"Latest checkpoint's NextMultiOffset:")) != NULL)
350 if (p == NULL || strlen(p) <= 1)
351 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
353 p++;
/* remove ':' char */
357 else if ((p = strstr(bufin,
"First log segment after reset:")) != NULL)
359 /* Skip the colon and any whitespace after it */
361 if (p == NULL || strlen(p) <= 1)
362 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
363 p = strpbrk(p,
"01234567890ABCDEF");
364 if (p == NULL || strlen(p) <= 1)
365 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
367 /* Make sure it looks like a valid WAL file name */
368 if (strspn(p,
"0123456789ABCDEF") != 24)
369 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
372 got_nextxlogfile =
true;
374 else if ((p = strstr(bufin,
"Float8 argument passing:")) != NULL)
378 if (p == NULL || strlen(p) <= 1)
379 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
381 p++;
/* remove ':' char */
382 /* used later for contrib check */
383 cluster->controldata.float8_pass_by_value = strstr(p,
"by value") != NULL;
384 got_float8_pass_by_value =
true;
386 else if ((p = strstr(bufin,
"Maximum data alignment:")) != NULL)
390 if (p == NULL || strlen(p) <= 1)
391 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
393 p++;
/* remove ':' char */
397 else if ((p = strstr(bufin,
"Database block size:")) != NULL)
401 if (p == NULL || strlen(p) <= 1)
402 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
404 p++;
/* remove ':' char */
408 else if ((p = strstr(bufin,
"Blocks per segment of large relation:")) != NULL)
412 if (p == NULL || strlen(p) <= 1)
413 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
415 p++;
/* remove ':' char */
419 else if ((p = strstr(bufin,
"WAL block size:")) != NULL)
423 if (p == NULL || strlen(p) <= 1)
424 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
426 p++;
/* remove ':' char */
430 else if ((p = strstr(bufin,
"Bytes per WAL segment:")) != NULL)
434 if (p == NULL || strlen(p) <= 1)
435 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
437 p++;
/* remove ':' char */
441 else if ((p = strstr(bufin,
"Maximum length of identifiers:")) != NULL)
445 if (p == NULL || strlen(p) <= 1)
446 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
448 p++;
/* remove ':' char */
452 else if ((p = strstr(bufin,
"Maximum columns in an index:")) != NULL)
456 if (p == NULL || strlen(p) <= 1)
457 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
459 p++;
/* remove ':' char */
463 else if ((p = strstr(bufin,
"Maximum size of a TOAST chunk:")) != NULL)
467 if (p == NULL || strlen(p) <= 1)
468 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
470 p++;
/* remove ':' char */
474 else if ((p = strstr(bufin,
"Size of a large-object chunk:")) != NULL)
478 if (p == NULL || strlen(p) <= 1)
479 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
481 p++;
/* remove ':' char */
483 got_large_object =
true;
485 else if ((p = strstr(bufin,
"Date/time type storage:")) != NULL)
489 if (p == NULL || strlen(p) <= 1)
490 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
492 p++;
/* remove ':' char */
493 cluster->controldata.date_is_int = strstr(p,
"64-bit integers") != NULL;
494 got_date_is_int =
true;
496 else if ((p = strstr(bufin,
"checksum")) != NULL)
500 if (p == NULL || strlen(p) <= 1)
501 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
503 p++;
/* remove ':' char */
505 got_data_checksum_version =
true;
507 else if ((p = strstr(bufin,
"Default char data signedness:")) != NULL)
511 if (p == NULL || strlen(p) <= 1)
512 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
514 /* Skip the colon and any whitespace after it */
516 while (isspace((
unsigned char) *p))
519 /* The value should be either 'signed' or 'unsigned' */
520 if (strcmp(p,
"signed") != 0 && strcmp(p,
"unsigned") != 0)
521 pg_fatal(
"%d: controldata retrieval problem", __LINE__);
523 cluster->controldata.default_char_signedness = strcmp(p,
"signed") == 0;
524 got_default_char_signedness =
true;
530 pg_fatal(
"could not get control data using %s: %s",
534 * Restore environment variables. Note all but LANG and LC_MESSAGES were
552 setenv(
"LANGUAGE", language, 1);
554 setenv(
"LC_ALL", lc_all, 1);
571 * Before 9.3, pg_resetwal reported the xlogid and segno of the first log
572 * file after reset as separate lines. Starting with 9.3, it reports the
573 * WAL file name. If the old cluster is older than 9.3, we construct the
574 * WAL file name from the xlogid and segno.
578 if (got_tli && got_log_id && got_log_seg)
582 got_nextxlogfile =
true;
587 * Pre-v18 database clusters don't have the default char signedness
588 * information. We use the char signedness of the platform where
589 * pg_upgrade was built.
593 Assert(!got_default_char_signedness);
595 cluster->controldata.default_char_signedness =
true;
597 cluster->controldata.default_char_signedness =
false;
601 /* verify that we got all the mandatory pg_control data */
602 if (!got_xid || !got_oid ||
603 !got_multi || !got_oldestxid ||
606 !got_mxoff || (!live_check && !got_nextxlogfile) ||
607 !got_float8_pass_by_value || !got_align || !got_blocksz ||
608 !got_largesz || !got_walsz || !got_walseg || !got_ident ||
609 !got_index || !got_toast ||
610 (!got_large_object &&
612 !got_date_is_int || !got_data_checksum_version ||
613 (!got_default_char_signedness &&
618 "The source cluster lacks some required control information:");
621 "The target cluster lacks some required control information:");
632 if (!got_oldestmulti &&
642 if (!live_check && !got_nextxlogfile)
645 if (!got_float8_pass_by_value)
672 if (!got_large_object &&
676 if (!got_date_is_int)
679 /* value added in Postgres 9.3 */
680 if (!got_data_checksum_version)
683 /* value added in Postgres 18 */
684 if (!got_default_char_signedness)
687 pg_fatal(
"Cannot continue without required control information, terminating");
693 * check_control_data()
695 * check to make sure the control data settings are compatible
702 pg_fatal(
"old and new pg_controldata alignments are invalid or do not match.\n"
703 "Likely one cluster is a 32-bit install, the other 64-bit");
706 pg_fatal(
"old and new pg_controldata block sizes are invalid or do not match");
709 pg_fatal(
"old and new pg_controldata maximum relation segment sizes are invalid or do not match");
712 pg_fatal(
"old and new pg_controldata WAL block sizes are invalid or do not match");
715 pg_fatal(
"old and new pg_controldata WAL segment sizes are invalid or do not match");
718 pg_fatal(
"old and new pg_controldata maximum identifier lengths are invalid or do not match");
721 pg_fatal(
"old and new pg_controldata maximum indexed columns are invalid or do not match");
724 pg_fatal(
"old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
726 /* large_object added in 9.5, so it might not exist in the old cluster */
729 pg_fatal(
"old and new pg_controldata large-object chunk sizes are invalid or do not match");
732 pg_fatal(
"old and new pg_controldata date/time storage types do not match");
735 * float8_pass_by_value does not need to match, but is used in
736 * check_for_isn_and_int8_passing_mismatch().
740 * We might eventually allow upgrades from checksum to no-checksum
745 pg_fatal(
"old cluster does not use data checksums but the new one does");
748 pg_fatal(
"old cluster uses data checksums but the new one does not");
750 pg_fatal(
"old and new cluster pg_controldata checksum versions do not match");
760 /* rename pg_control so old server cannot be accidentally started */
761 /* translator: %s is the file path of the control file */
767 pg_fatal(
"could not rename file \"%s\" to \"%s\": %m",
772 /* translator: %s/%s is the file path of the control file */
774 "If you want to start the old cluster, you will need to remove\n"
775 "the \".old\" suffix from \"%s/%s.old\".\n"
776 "Because \"link\" mode was used, the old cluster cannot be safely\n"
777 "started once the new cluster has been started.",
781 "Because \"swap\" mode was used, the old cluster can no longer be\n"
784 pg_fatal(
"unrecognized transfer mode");
void cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
void get_control_data(ClusterInfo *cluster)
void check_control_data(ControlData *oldctrl, ControlData *newctrl)
void disable_old_cluster(transferMode transfer_mode)
char * pg_strdup(const char *in)
Assert(PointerIsAligned(start, uint64))
static char * lc_messages
static void check_ok(void)
static char * lc_monetary
#define LARGE_OBJECT_SIZE_PG_CONTROL_VER
#define MULTIXACT_FORMATCHANGE_CAT_VER
void void unsigned int str2uint(const char *str)
void void pg_log(eLogType type, const char *fmt,...) pg_attribute_printf(2
#define GET_MAJOR_VERSION(v)
#define DEFAULT_CHAR_SIGNEDNESS_CAT_VER
void prep_status(const char *fmt,...) pg_attribute_printf(1
size_t strlcpy(char *dst, const char *src, size_t siz)
int pg_strip_crlf(char *str)
uint32 data_checksum_version
char * wait_result_to_str(int exitstatus)
#define XLOG_CONTROL_FILE