Branch data Line data Source code
1 : : /*
2 : : * controldata.c
3 : : *
4 : : * controldata functions
5 : : *
6 : : * Copyright (c) 2010-2026, PostgreSQL Global Development Group
7 : : * src/bin/pg_upgrade/controldata.c
8 : : */
9 : :
10 : : #include "postgres_fe.h"
11 : :
12 : : #include <ctype.h>
13 : : #include <limits.h> /* for CHAR_MIN */
14 : :
15 : : #include "access/xlog_internal.h"
16 : : #include "common/string.h"
17 : : #include "pg_upgrade.h"
18 : : #include "storage/checksum.h"
19 : :
20 : :
21 : : /*
22 : : * get_control_data()
23 : : *
24 : : * gets pg_control information in "ctrl". Assumes that bindir and
25 : : * datadir are valid absolute paths to postgresql bin and pgdata
26 : : * directories respectively *and* pg_resetwal is version compatible
27 : : * with datadir. The main purpose of this function is to get pg_control
28 : : * data in a version independent manner.
29 : : *
30 : : * The approach taken here is to invoke pg_resetwal with -n option
31 : : * and then pipe its output. With little string parsing we get the
32 : : * pg_control data. pg_resetwal cannot be run while the server is running
33 : : * so we use pg_controldata; pg_controldata doesn't provide all the fields
34 : : * we need to actually perform the upgrade, but it provides enough for
35 : : * check mode. We do not implement pg_resetwal -n because it is hard to
36 : : * return valid xid data for a running server.
37 : : */
38 : : void
39 : 36 : get_control_data(ClusterInfo *cluster)
40 : : {
41 : : char cmd[MAXPGPATH];
42 : : char bufin[MAX_STRING];
43 : : FILE *output;
44 : : char *p;
45 : 36 : bool got_xid = false;
46 : 36 : bool got_oid = false;
47 : 36 : bool got_multi = false;
48 : 36 : bool got_oldestmulti = false;
49 : 36 : bool got_oldestxid = false;
50 : 36 : bool got_mxoff = false;
51 : 36 : bool got_nextxlogfile = false;
52 : 36 : bool got_float8_pass_by_value = false;
53 : 36 : bool got_align = false;
54 : 36 : bool got_blocksz = false;
55 : 36 : bool got_largesz = false;
56 : 36 : bool got_walsz = false;
57 : 36 : bool got_walseg = false;
58 : 36 : bool got_ident = false;
59 : 36 : bool got_index = false;
60 : 36 : bool got_toast = false;
61 : 36 : bool got_large_object = false;
62 : 36 : bool got_date_is_int = false;
63 : 36 : bool got_data_checksum_version = false;
64 : 36 : bool got_cluster_state = false;
65 : 36 : bool got_default_char_signedness = false;
66 : 36 : char *lc_collate = NULL;
67 : 36 : char *lc_ctype = NULL;
68 : 36 : char *lc_monetary = NULL;
69 : 36 : char *lc_numeric = NULL;
70 : 36 : char *lc_time = NULL;
71 : 36 : char *lang = NULL;
72 : 36 : char *language = NULL;
73 : 36 : char *lc_all = NULL;
74 : 36 : char *lc_messages = NULL;
75 : : int rc;
76 [ + + - + ]: 36 : bool live_check = (cluster == &old_cluster && user_opts.live_check);
77 : :
78 : : /*
79 : : * Because we test the pg_resetwal output as strings, it has to be in
80 : : * English. Copied from pg_regress.c.
81 : : */
82 [ - + ]: 36 : if (getenv("LC_COLLATE"))
83 : 0 : lc_collate = pg_strdup(getenv("LC_COLLATE"));
84 [ - + ]: 36 : if (getenv("LC_CTYPE"))
85 : 0 : lc_ctype = pg_strdup(getenv("LC_CTYPE"));
86 [ - + ]: 36 : if (getenv("LC_MONETARY"))
87 : 0 : lc_monetary = pg_strdup(getenv("LC_MONETARY"));
88 [ + - ]: 36 : if (getenv("LC_NUMERIC"))
89 : 36 : lc_numeric = pg_strdup(getenv("LC_NUMERIC"));
90 [ - + ]: 36 : if (getenv("LC_TIME"))
91 : 0 : lc_time = pg_strdup(getenv("LC_TIME"));
92 [ + - ]: 36 : if (getenv("LANG"))
93 : 36 : lang = pg_strdup(getenv("LANG"));
94 [ - + ]: 36 : if (getenv("LANGUAGE"))
95 : 0 : language = pg_strdup(getenv("LANGUAGE"));
96 [ - + ]: 36 : if (getenv("LC_ALL"))
97 : 0 : lc_all = pg_strdup(getenv("LC_ALL"));
98 [ + - ]: 36 : if (getenv("LC_MESSAGES"))
99 : 36 : lc_messages = pg_strdup(getenv("LC_MESSAGES"));
100 : :
101 : 36 : unsetenv("LC_COLLATE");
102 : 36 : unsetenv("LC_CTYPE");
103 : 36 : unsetenv("LC_MONETARY");
104 : 36 : unsetenv("LC_NUMERIC");
105 : 36 : unsetenv("LC_TIME");
106 : : #ifndef WIN32
107 : 36 : unsetenv("LANG");
108 : : #else
109 : : /* On Windows the default locale may not be English, so force it */
110 : : setenv("LANG", "en", 1);
111 : : #endif
112 : 36 : unsetenv("LANGUAGE");
113 : 36 : unsetenv("LC_ALL");
114 : 36 : setenv("LC_MESSAGES", "C", 1);
115 : :
116 : : /*
117 : : * Check for clean shutdown
118 : : */
119 [ - + - - ]: 36 : if (!live_check || cluster == &new_cluster)
120 : : {
121 : : /* only pg_controldata outputs the cluster state */
122 : 36 : snprintf(cmd, sizeof(cmd), "\"%s/pg_controldata\" \"%s\"",
123 : : cluster->bindir, cluster->pgdata);
124 : 36 : fflush(NULL);
125 : :
126 [ - + ]: 36 : if ((output = popen(cmd, "r")) == NULL)
127 : 0 : pg_fatal("could not get control data using %s: %m", cmd);
128 : :
129 : : /* we have the result of cmd in "output". so parse it line by line now */
130 [ + + ]: 1980 : while (fgets(bufin, sizeof(bufin), output))
131 : : {
132 [ + + ]: 1908 : if ((p = strstr(bufin, "Database cluster state:")) != NULL)
133 : : {
134 : 36 : p = strchr(p, ':');
135 : :
136 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
137 : 0 : pg_fatal("%d: database cluster state problem", __LINE__);
138 : :
139 : 36 : p++; /* remove ':' char */
140 : :
141 : : /*
142 : : * We checked earlier for a postmaster lock file, and if we
143 : : * found one, we tried to start/stop the server to replay the
144 : : * WAL. However, pg_ctl -m immediate doesn't leave a lock
145 : : * file, but does require WAL replay, so we check here that
146 : : * the server was shut down cleanly, from the controldata
147 : : * perspective.
148 : : */
149 : : /* Remove trailing newline and leading spaces */
150 : 36 : (void) pg_strip_crlf(p);
151 [ + + ]: 576 : while (*p == ' ')
152 : 540 : p++;
153 [ - + ]: 36 : if (strcmp(p, "shut down in recovery") == 0)
154 : : {
155 [ # # ]: 0 : if (cluster == &old_cluster)
156 : 0 : 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.");
157 : : else
158 : 0 : 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.");
159 : : }
160 [ - + ]: 36 : else if (strcmp(p, "shut down") != 0)
161 : : {
162 [ # # ]: 0 : if (cluster == &old_cluster)
163 : 0 : pg_fatal("The source cluster was not shut down cleanly, state reported as: \"%s\"", p);
164 : : else
165 : 0 : pg_fatal("The target cluster was not shut down cleanly, state reported as: \"%s\"", p);
166 : : }
167 : 36 : got_cluster_state = true;
168 : : }
169 : : }
170 : :
171 : 36 : rc = pclose(output);
172 [ - + ]: 36 : if (rc != 0)
173 : 0 : pg_fatal("could not get control data using %s: %s",
174 : : cmd, wait_result_to_str(rc));
175 : :
176 [ - + ]: 36 : if (!got_cluster_state)
177 : : {
178 [ # # ]: 0 : if (cluster == &old_cluster)
179 : 0 : pg_fatal("The source cluster lacks cluster state information:");
180 : : else
181 : 0 : pg_fatal("The target cluster lacks cluster state information:");
182 : : }
183 : : }
184 : :
185 [ - + ]: 36 : snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"",
186 : : cluster->bindir,
187 : : live_check ? "pg_controldata\"" : "pg_resetwal\" -n",
188 : : cluster->pgdata);
189 : 36 : fflush(NULL);
190 : :
191 [ - + ]: 36 : if ((output = popen(cmd, "r")) == NULL)
192 : 0 : pg_fatal("could not get control data using %s: %m", cmd);
193 : :
194 : : /* we have the result of cmd in "output". so parse it line by line now */
195 [ + + ]: 1368 : while (fgets(bufin, sizeof(bufin), output))
196 : : {
197 : : /* In verbose mode, log each line */
198 : 1332 : pg_strip_crlf(bufin);
199 : 1332 : pg_log(PG_VERBOSE, "%s", bufin);
200 : :
201 [ + + ]: 1332 : if ((p = strstr(bufin, "pg_control version number:")) != NULL)
202 : : {
203 : 36 : p = strchr(p, ':');
204 : :
205 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
206 : 0 : pg_fatal("%d: pg_resetwal problem", __LINE__);
207 : :
208 : 36 : p++; /* remove ':' char */
209 : 36 : cluster->controldata.ctrl_ver = str2uint(p);
210 : : }
211 [ + + ]: 1296 : else if ((p = strstr(bufin, "Catalog version number:")) != NULL)
212 : : {
213 : 36 : p = strchr(p, ':');
214 : :
215 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
216 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
217 : :
218 : 36 : p++; /* remove ':' char */
219 : 36 : cluster->controldata.cat_ver = str2uint(p);
220 : : }
221 [ + + ]: 1260 : else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
222 : : {
223 : 36 : p = strchr(p, ':');
224 : :
225 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
226 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
227 : :
228 : 36 : p++; /* remove ':' char */
229 : 36 : cluster->controldata.chkpnt_nxtepoch = str2uint(p);
230 : :
231 : 36 : p = strchr(p, ':');
232 : :
233 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
234 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
235 : :
236 : 36 : p++; /* remove ':' char */
237 : 36 : cluster->controldata.chkpnt_nxtxid = str2uint(p);
238 : 36 : got_xid = true;
239 : : }
240 [ + + ]: 1224 : else if ((p = strstr(bufin, "Latest checkpoint's NextOID:")) != NULL)
241 : : {
242 : 36 : p = strchr(p, ':');
243 : :
244 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
245 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
246 : :
247 : 36 : p++; /* remove ':' char */
248 : 36 : cluster->controldata.chkpnt_nxtoid = str2uint(p);
249 : 36 : got_oid = true;
250 : : }
251 [ + + ]: 1188 : else if ((p = strstr(bufin, "Latest checkpoint's NextMultiXactId:")) != NULL)
252 : : {
253 : 36 : p = strchr(p, ':');
254 : :
255 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
256 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
257 : :
258 : 36 : p++; /* remove ':' char */
259 : 36 : cluster->controldata.chkpnt_nxtmulti = str2uint(p);
260 : 36 : got_multi = true;
261 : : }
262 [ + + ]: 1152 : else if ((p = strstr(bufin, "Latest checkpoint's oldestXID:")) != NULL)
263 : : {
264 : 36 : p = strchr(p, ':');
265 : :
266 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
267 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
268 : :
269 : 36 : p++; /* remove ':' char */
270 : 36 : cluster->controldata.chkpnt_oldstxid = str2uint(p);
271 : 36 : got_oldestxid = true;
272 : : }
273 [ + + ]: 1116 : else if ((p = strstr(bufin, "Latest checkpoint's oldestMultiXid:")) != NULL)
274 : : {
275 : 36 : p = strchr(p, ':');
276 : :
277 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
278 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
279 : :
280 : 36 : p++; /* remove ':' char */
281 : 36 : cluster->controldata.chkpnt_oldstMulti = str2uint(p);
282 : 36 : got_oldestmulti = true;
283 : : }
284 [ + + ]: 1080 : else if ((p = strstr(bufin, "Latest checkpoint's NextMultiOffset:")) != NULL)
285 : : {
286 : 36 : p = strchr(p, ':');
287 : :
288 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
289 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
290 : :
291 : 36 : p++; /* remove ':' char */
292 : 36 : cluster->controldata.chkpnt_nxtmxoff = str2uint(p);
293 : 36 : got_mxoff = true;
294 : : }
295 [ + + ]: 1044 : else if ((p = strstr(bufin, "First log segment after reset:")) != NULL)
296 : : {
297 : : /* Skip the colon and any whitespace after it */
298 : 36 : p = strchr(p, ':');
299 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
300 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
301 : 36 : p = strpbrk(p, "0123456789ABCDEF");
302 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
303 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
304 : :
305 : : /* Make sure it looks like a valid WAL file name */
306 [ - + ]: 36 : if (strspn(p, "0123456789ABCDEF") != 24)
307 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
308 : :
309 : 36 : strlcpy(cluster->controldata.nextxlogfile, p, 25);
310 : 36 : got_nextxlogfile = true;
311 : : }
312 [ + + ]: 1008 : else if ((p = strstr(bufin, "Float8 argument passing:")) != NULL)
313 : : {
314 : 36 : p = strchr(p, ':');
315 : :
316 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
317 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
318 : :
319 : 36 : p++; /* remove ':' char */
320 : : /* used later for contrib check */
321 : 36 : cluster->controldata.float8_pass_by_value = strstr(p, "by value") != NULL;
322 : 36 : got_float8_pass_by_value = true;
323 : : }
324 [ + + ]: 972 : else if ((p = strstr(bufin, "Maximum data alignment:")) != NULL)
325 : : {
326 : 36 : p = strchr(p, ':');
327 : :
328 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
329 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
330 : :
331 : 36 : p++; /* remove ':' char */
332 : 36 : cluster->controldata.align = str2uint(p);
333 : 36 : got_align = true;
334 : : }
335 [ + + ]: 936 : else if ((p = strstr(bufin, "Database block size:")) != NULL)
336 : : {
337 : 36 : p = strchr(p, ':');
338 : :
339 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
340 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
341 : :
342 : 36 : p++; /* remove ':' char */
343 : 36 : cluster->controldata.blocksz = str2uint(p);
344 : 36 : got_blocksz = true;
345 : : }
346 [ + + ]: 900 : else if ((p = strstr(bufin, "Blocks per segment of large relation:")) != NULL)
347 : : {
348 : 36 : p = strchr(p, ':');
349 : :
350 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
351 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
352 : :
353 : 36 : p++; /* remove ':' char */
354 : 36 : cluster->controldata.largesz = str2uint(p);
355 : 36 : got_largesz = true;
356 : : }
357 [ + + ]: 864 : else if ((p = strstr(bufin, "WAL block size:")) != NULL)
358 : : {
359 : 36 : p = strchr(p, ':');
360 : :
361 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
362 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
363 : :
364 : 36 : p++; /* remove ':' char */
365 : 36 : cluster->controldata.walsz = str2uint(p);
366 : 36 : got_walsz = true;
367 : : }
368 [ + + ]: 828 : else if ((p = strstr(bufin, "Bytes per WAL segment:")) != NULL)
369 : : {
370 : 36 : p = strchr(p, ':');
371 : :
372 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
373 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
374 : :
375 : 36 : p++; /* remove ':' char */
376 : 36 : cluster->controldata.walseg = str2uint(p);
377 : 36 : got_walseg = true;
378 : : }
379 [ + + ]: 792 : else if ((p = strstr(bufin, "Maximum length of identifiers:")) != NULL)
380 : : {
381 : 36 : p = strchr(p, ':');
382 : :
383 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
384 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
385 : :
386 : 36 : p++; /* remove ':' char */
387 : 36 : cluster->controldata.ident = str2uint(p);
388 : 36 : got_ident = true;
389 : : }
390 [ + + ]: 756 : else if ((p = strstr(bufin, "Maximum columns in an index:")) != NULL)
391 : : {
392 : 36 : p = strchr(p, ':');
393 : :
394 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
395 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
396 : :
397 : 36 : p++; /* remove ':' char */
398 : 36 : cluster->controldata.index = str2uint(p);
399 : 36 : got_index = true;
400 : : }
401 [ + + ]: 720 : else if ((p = strstr(bufin, "Maximum size of a TOAST chunk:")) != NULL)
402 : : {
403 : 36 : p = strchr(p, ':');
404 : :
405 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
406 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
407 : :
408 : 36 : p++; /* remove ':' char */
409 : 36 : cluster->controldata.toast = str2uint(p);
410 : 36 : got_toast = true;
411 : : }
412 [ + + ]: 684 : else if ((p = strstr(bufin, "Size of a large-object chunk:")) != NULL)
413 : : {
414 : 36 : p = strchr(p, ':');
415 : :
416 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
417 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
418 : :
419 : 36 : p++; /* remove ':' char */
420 : 36 : cluster->controldata.large_object = str2uint(p);
421 : 36 : got_large_object = true;
422 : : }
423 [ + + ]: 648 : else if ((p = strstr(bufin, "Date/time type storage:")) != NULL)
424 : : {
425 : 36 : p = strchr(p, ':');
426 : :
427 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
428 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
429 : :
430 : 36 : p++; /* remove ':' char */
431 : 36 : cluster->controldata.date_is_int = strstr(p, "64-bit integers") != NULL;
432 : 36 : got_date_is_int = true;
433 : : }
434 [ + + ]: 612 : else if ((p = strstr(bufin, "checksum")) != NULL)
435 : : {
436 : 36 : p = strchr(p, ':');
437 : :
438 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
439 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
440 : :
441 : 36 : p++; /* remove ':' char */
442 : 36 : cluster->controldata.data_checksum_version = str2uint(p);
443 : 36 : got_data_checksum_version = true;
444 : : }
445 [ + + ]: 576 : else if ((p = strstr(bufin, "Default char data signedness:")) != NULL)
446 : : {
447 : 36 : p = strchr(p, ':');
448 : :
449 [ + - - + ]: 36 : if (p == NULL || strlen(p) <= 1)
450 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
451 : :
452 : : /* Skip the colon and any whitespace after it */
453 : 36 : p++;
454 [ + + ]: 360 : while (isspace((unsigned char) *p))
455 : 324 : p++;
456 : :
457 : : /* The value should be either 'signed' or 'unsigned' */
458 [ + + - + ]: 36 : if (strcmp(p, "signed") != 0 && strcmp(p, "unsigned") != 0)
459 : 0 : pg_fatal("%d: controldata retrieval problem", __LINE__);
460 : :
461 : 36 : cluster->controldata.default_char_signedness = strcmp(p, "signed") == 0;
462 : 36 : got_default_char_signedness = true;
463 : : }
464 : : }
465 : :
466 : 36 : rc = pclose(output);
467 [ - + ]: 36 : if (rc != 0)
468 : 0 : pg_fatal("could not get control data using %s: %s",
469 : : cmd, wait_result_to_str(rc));
470 : :
471 : : /*
472 : : * Restore environment variables. Note all but LANG and LC_MESSAGES were
473 : : * unset above.
474 : : */
475 [ - + ]: 36 : if (lc_collate)
476 : 0 : setenv("LC_COLLATE", lc_collate, 1);
477 [ - + ]: 36 : if (lc_ctype)
478 : 0 : setenv("LC_CTYPE", lc_ctype, 1);
479 [ - + ]: 36 : if (lc_monetary)
480 : 0 : setenv("LC_MONETARY", lc_monetary, 1);
481 [ + - ]: 36 : if (lc_numeric)
482 : 36 : setenv("LC_NUMERIC", lc_numeric, 1);
483 [ - + ]: 36 : if (lc_time)
484 : 0 : setenv("LC_TIME", lc_time, 1);
485 [ + - ]: 36 : if (lang)
486 : 36 : setenv("LANG", lang, 1);
487 : : else
488 : 0 : unsetenv("LANG");
489 [ - + ]: 36 : if (language)
490 : 0 : setenv("LANGUAGE", language, 1);
491 [ - + ]: 36 : if (lc_all)
492 : 0 : setenv("LC_ALL", lc_all, 1);
493 [ + - ]: 36 : if (lc_messages)
494 : 36 : setenv("LC_MESSAGES", lc_messages, 1);
495 : : else
496 : 0 : unsetenv("LC_MESSAGES");
497 : :
498 : 36 : pg_free(lc_collate);
499 : 36 : pg_free(lc_ctype);
500 : 36 : pg_free(lc_monetary);
501 : 36 : pg_free(lc_numeric);
502 : 36 : pg_free(lc_time);
503 : 36 : pg_free(lang);
504 : 36 : pg_free(language);
505 : 36 : pg_free(lc_all);
506 : 36 : pg_free(lc_messages);
507 : :
508 : : /*
509 : : * Pre-v18 database clusters don't have the default char signedness
510 : : * information. We use the char signedness of the platform where
511 : : * pg_upgrade was built.
512 : : */
513 [ - + ]: 36 : if (cluster->controldata.cat_ver < DEFAULT_CHAR_SIGNEDNESS_CAT_VER)
514 : : {
515 : : Assert(!got_default_char_signedness);
516 : : #if CHAR_MIN != 0
517 : 0 : cluster->controldata.default_char_signedness = true;
518 : : #else
519 : : cluster->controldata.default_char_signedness = false;
520 : : #endif
521 : : }
522 : :
523 : : /* verify that we got all the mandatory pg_control data */
524 [ + - + - ]: 36 : if (!got_xid || !got_oid ||
525 [ + - + - ]: 36 : !got_multi || !got_oldestxid ||
526 [ + - ]: 36 : !got_oldestmulti ||
527 [ + - + - : 36 : !got_mxoff || (!live_check && !got_nextxlogfile) ||
+ - ]
528 [ + - + - : 36 : !got_float8_pass_by_value || !got_align || !got_blocksz ||
+ - ]
529 [ + - + - : 36 : !got_largesz || !got_walsz || !got_walseg || !got_ident ||
+ - + - ]
530 [ + - + - ]: 36 : !got_index || !got_toast ||
531 [ + - ]: 36 : !got_large_object ||
532 [ + - + - ]: 36 : !got_date_is_int || !got_data_checksum_version ||
533 [ - + ]: 36 : (!got_default_char_signedness &&
534 [ # # ]: 0 : cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
535 : : {
536 [ # # ]: 0 : if (cluster == &old_cluster)
537 : 0 : pg_log(PG_REPORT,
538 : : "The source cluster lacks some required control information:");
539 : : else
540 : 0 : pg_log(PG_REPORT,
541 : : "The target cluster lacks some required control information:");
542 : :
543 [ # # ]: 0 : if (!got_xid)
544 : 0 : pg_log(PG_REPORT, " checkpoint next XID");
545 : :
546 [ # # ]: 0 : if (!got_oid)
547 : 0 : pg_log(PG_REPORT, " latest checkpoint next OID");
548 : :
549 [ # # ]: 0 : if (!got_multi)
550 : 0 : pg_log(PG_REPORT, " latest checkpoint next MultiXactId");
551 : :
552 [ # # ]: 0 : if (!got_oldestmulti)
553 : 0 : pg_log(PG_REPORT, " latest checkpoint oldest MultiXactId");
554 : :
555 [ # # ]: 0 : if (!got_oldestxid)
556 : 0 : pg_log(PG_REPORT, " latest checkpoint oldestXID");
557 : :
558 [ # # ]: 0 : if (!got_mxoff)
559 : 0 : pg_log(PG_REPORT, " latest checkpoint next MultiXactOffset");
560 : :
561 [ # # # # ]: 0 : if (!live_check && !got_nextxlogfile)
562 : 0 : pg_log(PG_REPORT, " first WAL segment after reset");
563 : :
564 [ # # ]: 0 : if (!got_float8_pass_by_value)
565 : 0 : pg_log(PG_REPORT, " float8 argument passing method");
566 : :
567 [ # # ]: 0 : if (!got_align)
568 : 0 : pg_log(PG_REPORT, " maximum alignment");
569 : :
570 [ # # ]: 0 : if (!got_blocksz)
571 : 0 : pg_log(PG_REPORT, " block size");
572 : :
573 [ # # ]: 0 : if (!got_largesz)
574 : 0 : pg_log(PG_REPORT, " large relation segment size");
575 : :
576 [ # # ]: 0 : if (!got_walsz)
577 : 0 : pg_log(PG_REPORT, " WAL block size");
578 : :
579 [ # # ]: 0 : if (!got_walseg)
580 : 0 : pg_log(PG_REPORT, " WAL segment size");
581 : :
582 [ # # ]: 0 : if (!got_ident)
583 : 0 : pg_log(PG_REPORT, " maximum identifier length");
584 : :
585 [ # # ]: 0 : if (!got_index)
586 : 0 : pg_log(PG_REPORT, " maximum number of indexed columns");
587 : :
588 [ # # ]: 0 : if (!got_toast)
589 : 0 : pg_log(PG_REPORT, " maximum TOAST chunk size");
590 : :
591 [ # # ]: 0 : if (!got_large_object)
592 : 0 : pg_log(PG_REPORT, " large-object chunk size");
593 : :
594 [ # # ]: 0 : if (!got_date_is_int)
595 : 0 : pg_log(PG_REPORT, " dates/times are integers?");
596 : :
597 : : /* value added in Postgres 9.3 */
598 [ # # ]: 0 : if (!got_data_checksum_version)
599 : 0 : pg_log(PG_REPORT, " data checksum version");
600 : :
601 : : /* value added in Postgres 18 */
602 [ # # ]: 0 : if (!got_default_char_signedness)
603 : 0 : pg_log(PG_REPORT, " default char signedness");
604 : :
605 : 0 : pg_fatal("Cannot continue without required control information, terminating");
606 : : }
607 : 36 : }
608 : :
609 : :
610 : : /*
611 : : * check_control_data()
612 : : *
613 : : * check to make sure the control data settings are compatible
614 : : */
615 : : void
616 : 18 : check_control_data(ControlData *oldctrl,
617 : : ControlData *newctrl)
618 : : {
619 [ + - - + ]: 18 : if (oldctrl->align == 0 || oldctrl->align != newctrl->align)
620 : 0 : pg_fatal("old and new pg_controldata alignments are invalid or do not match.\n"
621 : : "Likely one cluster is a 32-bit install, the other 64-bit");
622 : :
623 [ + - - + ]: 18 : if (oldctrl->blocksz == 0 || oldctrl->blocksz != newctrl->blocksz)
624 : 0 : pg_fatal("old and new pg_controldata block sizes are invalid or do not match");
625 : :
626 [ + - - + ]: 18 : if (oldctrl->largesz == 0 || oldctrl->largesz != newctrl->largesz)
627 : 0 : pg_fatal("old and new pg_controldata maximum relation segment sizes are invalid or do not match");
628 : :
629 [ + - - + ]: 18 : if (oldctrl->walsz == 0 || oldctrl->walsz != newctrl->walsz)
630 : 0 : pg_fatal("old and new pg_controldata WAL block sizes are invalid or do not match");
631 : :
632 [ + - - + ]: 18 : if (oldctrl->walseg == 0 || oldctrl->walseg != newctrl->walseg)
633 : 0 : pg_fatal("old and new pg_controldata WAL segment sizes are invalid or do not match");
634 : :
635 [ + - - + ]: 18 : if (oldctrl->ident == 0 || oldctrl->ident != newctrl->ident)
636 : 0 : pg_fatal("old and new pg_controldata maximum identifier lengths are invalid or do not match");
637 : :
638 [ + - - + ]: 18 : if (oldctrl->index == 0 || oldctrl->index != newctrl->index)
639 : 0 : pg_fatal("old and new pg_controldata maximum indexed columns are invalid or do not match");
640 : :
641 [ + - - + ]: 18 : if (oldctrl->toast == 0 || oldctrl->toast != newctrl->toast)
642 : 0 : pg_fatal("old and new pg_controldata maximum TOAST chunk sizes are invalid or do not match");
643 : :
644 [ + - ]: 18 : if (oldctrl->large_object == 0 ||
645 [ - + ]: 18 : oldctrl->large_object != newctrl->large_object)
646 : 0 : pg_fatal("old and new pg_controldata large-object chunk sizes are invalid or do not match");
647 : :
648 [ - + ]: 18 : if (oldctrl->date_is_int != newctrl->date_is_int)
649 : 0 : pg_fatal("old and new pg_controldata date/time storage types do not match");
650 : :
651 : : /*
652 : : * float8_pass_by_value does not need to match, but is used in
653 : : * check_for_isn_and_int8_passing_mismatch().
654 : : */
655 : :
656 : : /*
657 : : * If data checksums are in any in-progress state then disallow the
658 : : * upgrade. The user should either let the process finish, or turn off
659 : : * data checksums, before retrying.
660 : : */
661 [ - + ]: 18 : if (oldctrl->data_checksum_version > PG_DATA_CHECKSUM_VERSION)
662 : 0 : pg_fatal("checksums are being enabled in the old cluster");
663 : :
664 : : /*
665 : : * We might eventually allow upgrades from checksum to no-checksum
666 : : * clusters.
667 : : */
668 [ - + ]: 18 : if (oldctrl->data_checksum_version == PG_DATA_CHECKSUM_OFF &&
669 [ # # ]: 0 : newctrl->data_checksum_version != PG_DATA_CHECKSUM_OFF)
670 : 0 : pg_fatal("old cluster does not use data checksums but the new one does");
671 [ + - ]: 18 : else if (oldctrl->data_checksum_version != PG_DATA_CHECKSUM_OFF &&
672 [ - + ]: 18 : newctrl->data_checksum_version == PG_DATA_CHECKSUM_OFF)
673 : 0 : pg_fatal("old cluster uses data checksums but the new one does not");
674 [ - + ]: 18 : else if (oldctrl->data_checksum_version != newctrl->data_checksum_version)
675 : 0 : pg_fatal("old and new cluster pg_controldata checksum versions do not match");
676 : 18 : }
677 : :
678 : :
679 : : void
680 : 2 : disable_old_cluster(transferMode transfer_mode)
681 : : {
682 : : char old_path[MAXPGPATH],
683 : : new_path[MAXPGPATH];
684 : :
685 : : /* rename pg_control so old server cannot be accidentally started */
686 : : /* translator: %s is the file path of the control file */
687 : 2 : prep_status("Adding \".old\" suffix to old \"%s\"", XLOG_CONTROL_FILE);
688 : :
689 : 2 : snprintf(old_path, sizeof(old_path), "%s/%s", old_cluster.pgdata, XLOG_CONTROL_FILE);
690 : 2 : snprintf(new_path, sizeof(new_path), "%s/%s.old", old_cluster.pgdata, XLOG_CONTROL_FILE);
691 [ - + ]: 2 : if (pg_mv_file(old_path, new_path) != 0)
692 : 0 : pg_fatal("could not rename file \"%s\" to \"%s\": %m",
693 : : old_path, new_path);
694 : 2 : check_ok();
695 : :
696 [ + + ]: 2 : if (transfer_mode == TRANSFER_MODE_LINK)
697 : : /* translator: %s/%s is the file path of the control file */
698 : 1 : pg_log(PG_REPORT, "\n"
699 : : "If you want to start the old cluster, you will need to remove\n"
700 : : "the \".old\" suffix from \"%s/%s.old\".\n"
701 : : "Because \"link\" mode was used, the old cluster cannot be safely\n"
702 : : "started once the new cluster has been started.",
703 : : old_cluster.pgdata, XLOG_CONTROL_FILE);
704 [ + - ]: 1 : else if (transfer_mode == TRANSFER_MODE_SWAP)
705 : 1 : pg_log(PG_REPORT, "\n"
706 : : "Because \"swap\" mode was used, the old cluster can no longer be\n"
707 : : "safely started.");
708 : : else
709 : 0 : pg_fatal("unrecognized transfer mode");
710 : 2 : }
|