Branch data Line data Source code
1 : : /*
2 : : * check.c
3 : : *
4 : : * server checks and output routines
5 : : *
6 : : * Copyright (c) 2010-2026, PostgreSQL Global Development Group
7 : : * src/bin/pg_upgrade/check.c
8 : : */
9 : :
10 : : #include "postgres_fe.h"
11 : :
12 : : #include "catalog/pg_am_d.h"
13 : : #include "catalog/pg_authid_d.h"
14 : : #include "catalog/pg_class_d.h"
15 : : #include "fe_utils/string_utils.h"
16 : : #include "mb/pg_wchar.h"
17 : : #include "pg_upgrade.h"
18 : : #include "common/unicode_version.h"
19 : :
20 : : static void check_new_cluster_is_empty(void);
21 : : static void check_is_install_user(ClusterInfo *cluster);
22 : : static void check_for_unsupported_encodings(ClusterInfo *cluster);
23 : : static void check_for_connection_status(ClusterInfo *cluster);
24 : : static void check_for_prepared_transactions(ClusterInfo *cluster);
25 : : static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
26 : : static void check_for_user_defined_postfix_ops(ClusterInfo *cluster);
27 : : static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
28 : : static void check_for_tables_with_oids(ClusterInfo *cluster);
29 : : static void check_for_not_null_inheritance(ClusterInfo *cluster);
30 : : static void check_for_gist_inet_ops(ClusterInfo *cluster);
31 : : static void check_for_new_tablespace_dir(void);
32 : : static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
33 : : static void check_for_unicode_update(ClusterInfo *cluster);
34 : : static void check_new_cluster_replication_slots(void);
35 : : static void check_new_cluster_subscription_configuration(void);
36 : : static void check_old_cluster_for_valid_slots(void);
37 : : static void check_old_cluster_subscription_state(void);
38 : : static void check_old_cluster_global_names(ClusterInfo *cluster);
39 : :
40 : : /*
41 : : * DataTypesUsageChecks - definitions of data type checks for the old cluster
42 : : * in order to determine if an upgrade can be performed. See the comment on
43 : : * data_types_usage_checks below for a more detailed description.
44 : : */
45 : : typedef struct
46 : : {
47 : : /* Status line to print to the user */
48 : : const char *status;
49 : : /* Filename to store report to */
50 : : const char *report_filename;
51 : : /* Query to extract the oid of the datatype */
52 : : const char *base_query;
53 : : /* Text to store to report in case of error */
54 : : const char *report_text;
55 : : /* The latest version where the check applies */
56 : : int threshold_version;
57 : : /* A function pointer for determining if the check applies */
58 : : DataTypesUsageVersionCheck version_hook;
59 : : } DataTypesUsageChecks;
60 : :
61 : : /*
62 : : * Special values for threshold_version for indicating that a check applies to
63 : : * all versions, or that a custom function needs to be invoked to determine
64 : : * if the check applies.
65 : : */
66 : : #define MANUAL_CHECK 1
67 : : #define ALL_VERSIONS -1
68 : :
69 : : /*--
70 : : * Data type usage checks. Each check for problematic data type usage is
71 : : * defined in this array with metadata, SQL query for finding the data type
72 : : * and functionality for deciding if the check is applicable to the version
73 : : * of the old cluster. The struct members are described in detail below:
74 : : *
75 : : * status A oneline string which can be printed to the user to
76 : : * inform about progress. Should not end with newline.
77 : : * report_filename The filename in which the list of problems detected by
78 : : * the check will be printed.
79 : : * base_query A query which extracts the Oid of the datatype checked
80 : : * for.
81 : : * report_text The text which will be printed to the user to explain
82 : : * what the check did, and why it failed. The text should
83 : : * end with a newline, and does not need to refer to the
84 : : * report_filename as that is automatically appended to
85 : : * the report with the path to the log folder.
86 : : * threshold_version The major version of PostgreSQL for which to run the
87 : : * check. Iff the old cluster is less than, or equal to,
88 : : * the threshold version then the check will be executed.
89 : : * If the old version is greater than the threshold then
90 : : * the check is skipped. If the threshold_version is set
91 : : * to ALL_VERSIONS then it will be run unconditionally,
92 : : * if set to MANUAL_CHECK then the version_hook function
93 : : * will be executed in order to determine whether or not
94 : : * to run.
95 : : * version_hook A function pointer to a version check function of type
96 : : * DataTypesUsageVersionCheck which is used to determine
97 : : * if the check is applicable to the old cluster. If the
98 : : * version_hook returns true then the check will be run,
99 : : * else it will be skipped. The function will only be
100 : : * executed iff threshold_version is set to MANUAL_CHECK.
101 : : */
102 : : static DataTypesUsageChecks data_types_usage_checks[] =
103 : : {
104 : : /*
105 : : * Look for composite types that were made during initdb *or* belong to
106 : : * information_schema; that's important in case information_schema was
107 : : * dropped and reloaded.
108 : : *
109 : : * The cutoff OID here should match the source cluster's value of
110 : : * FirstNormalObjectId. We hardcode it rather than using that C #define
111 : : * because, if that #define is ever changed, our own version's value is
112 : : * NOT what to use. Eventually we may need a test on the source cluster's
113 : : * version to select the correct value.
114 : : */
115 : : {
116 : : .status = gettext_noop("Checking for system-defined composite types in user tables"),
117 : : .report_filename = "tables_using_composite.txt",
118 : : .base_query =
119 : : "SELECT t.oid FROM pg_catalog.pg_type t "
120 : : "LEFT JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid "
121 : : " WHERE typtype = 'c' AND (t.oid < 16384 OR nspname = 'information_schema')",
122 : : .report_text =
123 : : gettext_noop("Your installation contains system-defined composite types in user tables.\n"
124 : : "These type OIDs are not stable across PostgreSQL versions,\n"
125 : : "so this cluster cannot currently be upgraded. You can drop the\n"
126 : : "problem columns and restart the upgrade.\n"),
127 : : .threshold_version = ALL_VERSIONS
128 : : },
129 : :
130 : : /*
131 : : * pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
132 : : * pg_enum.oid
133 : : *
134 : : * Many of the reg* data types reference system catalog info that is not
135 : : * preserved, and hence these data types cannot be used in user tables
136 : : * upgraded by pg_upgrade.
137 : : */
138 : : {
139 : : .status = gettext_noop("Checking for reg* data types in user tables"),
140 : : .report_filename = "tables_using_reg.txt",
141 : :
142 : : /*
143 : : * Note: older servers will not have all of these reg* types, so we
144 : : * have to write the query like this rather than depending on casts to
145 : : * regtype.
146 : : */
147 : : .base_query =
148 : : "SELECT oid FROM pg_catalog.pg_type t "
149 : : "WHERE t.typnamespace = "
150 : : " (SELECT oid FROM pg_catalog.pg_namespace "
151 : : " WHERE nspname = 'pg_catalog') "
152 : : " AND t.typname IN ( "
153 : : /* pg_class.oid is preserved, so 'regclass' is OK */
154 : : " 'regcollation', "
155 : : " 'regconfig', "
156 : : /* pg_database.oid is preserved, so 'regdatabase' is OK */
157 : : " 'regdictionary', "
158 : : " 'regnamespace', "
159 : : " 'regoper', "
160 : : " 'regoperator', "
161 : : " 'regproc', "
162 : : " 'regprocedure' "
163 : : /* pg_authid.oid is preserved, so 'regrole' is OK */
164 : : /* pg_type.oid is (mostly) preserved, so 'regtype' is OK */
165 : : " )",
166 : : .report_text =
167 : : gettext_noop("Your installation contains one of the reg* data types in user tables.\n"
168 : : "These data types reference system OIDs that are not preserved by\n"
169 : : "pg_upgrade, so this cluster cannot currently be upgraded. You can\n"
170 : : "drop the problem columns and restart the upgrade.\n"),
171 : : .threshold_version = ALL_VERSIONS
172 : : },
173 : :
174 : : /*
175 : : * PG 16 increased the size of the 'aclitem' type, which breaks the
176 : : * on-disk format for existing data.
177 : : */
178 : : {
179 : : .status = gettext_noop("Checking for incompatible \"aclitem\" data type"),
180 : : .report_filename = "tables_using_aclitem.txt",
181 : : .base_query =
182 : : "SELECT 'pg_catalog.aclitem'::pg_catalog.regtype AS oid",
183 : : .report_text =
184 : : gettext_noop("Your installation contains the \"aclitem\" data type in user tables.\n"
185 : : "The internal format of \"aclitem\" changed in PostgreSQL version 16\n"
186 : : "so this cluster cannot currently be upgraded. You can drop the\n"
187 : : "problem columns and restart the upgrade.\n"),
188 : : .threshold_version = 1500
189 : : },
190 : :
191 : : /*
192 : : * PG 12 changed the 'sql_identifier' type storage to be based on name,
193 : : * not varchar, which breaks on-disk format for existing data. So we need
194 : : * to prevent upgrade when used in user objects (tables, indexes, ...). In
195 : : * 12, the sql_identifier data type was switched from name to varchar,
196 : : * which does affect the storage (name is by-ref, but not varlena). This
197 : : * means user tables using sql_identifier for columns are broken because
198 : : * the on-disk format is different.
199 : : */
200 : : {
201 : : .status = gettext_noop("Checking for invalid \"sql_identifier\" user columns"),
202 : : .report_filename = "tables_using_sql_identifier.txt",
203 : : .base_query =
204 : : "SELECT 'information_schema.sql_identifier'::pg_catalog.regtype AS oid",
205 : : .report_text =
206 : : gettext_noop("Your installation contains the \"sql_identifier\" data type in user tables.\n"
207 : : "The on-disk format for this data type has changed, so this\n"
208 : : "cluster cannot currently be upgraded. You can drop the problem\n"
209 : : "columns and restart the upgrade.\n"),
210 : : .threshold_version = 1100
211 : : },
212 : :
213 : : /*
214 : : * PG 12 removed types abstime, reltime, tinterval.
215 : : */
216 : : {
217 : : .status = gettext_noop("Checking for removed \"abstime\" data type in user tables"),
218 : : .report_filename = "tables_using_abstime.txt",
219 : : .base_query =
220 : : "SELECT 'pg_catalog.abstime'::pg_catalog.regtype AS oid",
221 : : .report_text =
222 : : gettext_noop("Your installation contains the \"abstime\" data type in user tables.\n"
223 : : "The \"abstime\" type has been removed in PostgreSQL version 12,\n"
224 : : "so this cluster cannot currently be upgraded. You can drop the\n"
225 : : "problem columns, or change them to another data type, and restart\n"
226 : : "the upgrade.\n"),
227 : : .threshold_version = 1100
228 : : },
229 : : {
230 : : .status = gettext_noop("Checking for removed \"reltime\" data type in user tables"),
231 : : .report_filename = "tables_using_reltime.txt",
232 : : .base_query =
233 : : "SELECT 'pg_catalog.reltime'::pg_catalog.regtype AS oid",
234 : : .report_text =
235 : : gettext_noop("Your installation contains the \"reltime\" data type in user tables.\n"
236 : : "The \"reltime\" type has been removed in PostgreSQL version 12,\n"
237 : : "so this cluster cannot currently be upgraded. You can drop the\n"
238 : : "problem columns, or change them to another data type, and restart\n"
239 : : "the upgrade.\n"),
240 : : .threshold_version = 1100
241 : : },
242 : : {
243 : : .status = gettext_noop("Checking for removed \"tinterval\" data type in user tables"),
244 : : .report_filename = "tables_using_tinterval.txt",
245 : : .base_query =
246 : : "SELECT 'pg_catalog.tinterval'::pg_catalog.regtype AS oid",
247 : : .report_text =
248 : : gettext_noop("Your installation contains the \"tinterval\" data type in user tables.\n"
249 : : "The \"tinterval\" type has been removed in PostgreSQL version 12,\n"
250 : : "so this cluster cannot currently be upgraded. You can drop the\n"
251 : : "problem columns, or change them to another data type, and restart\n"
252 : : "the upgrade.\n"),
253 : : .threshold_version = 1100
254 : : },
255 : :
256 : : /* End of checks marker, must remain last */
257 : : {
258 : : NULL, NULL, NULL, NULL, 0, NULL
259 : : }
260 : : };
261 : :
262 : : /*
263 : : * Private state for check_for_data_types_usage()'s UpgradeTask.
264 : : */
265 : : struct data_type_check_state
266 : : {
267 : : DataTypesUsageChecks *check; /* the check for this step */
268 : : bool result; /* true if check failed for any database */
269 : : PQExpBuffer *report; /* buffer for report on failed checks */
270 : : };
271 : :
272 : : /*
273 : : * Returns a palloc'd query string for the data type check, for use by
274 : : * check_for_data_types_usage()'s UpgradeTask.
275 : : */
276 : : static char *
277 : 30 : data_type_check_query(int checknum)
278 : : {
279 : 30 : DataTypesUsageChecks *check = &data_types_usage_checks[checknum];
280 : :
281 : 30 : return psprintf("WITH RECURSIVE oids AS ( "
282 : : /* start with the type(s) returned by base_query */
283 : : " %s "
284 : : " UNION ALL "
285 : : " SELECT * FROM ( "
286 : : /* inner WITH because we can only reference the CTE once */
287 : : " WITH x AS (SELECT oid FROM oids) "
288 : : /* domains on any type selected so far */
289 : : " SELECT t.oid FROM pg_catalog.pg_type t, x WHERE typbasetype = x.oid AND typtype = 'd' "
290 : : " UNION ALL "
291 : : /* arrays over any type selected so far */
292 : : " SELECT t.oid FROM pg_catalog.pg_type t, x WHERE typelem = x.oid AND typtype = 'b' "
293 : : " UNION ALL "
294 : : /* composite types containing any type selected so far */
295 : : " SELECT t.oid FROM pg_catalog.pg_type t, pg_catalog.pg_class c, pg_catalog.pg_attribute a, x "
296 : : " WHERE t.typtype = 'c' AND "
297 : : " t.oid = c.reltype AND "
298 : : " c.oid = a.attrelid AND "
299 : : " NOT a.attisdropped AND "
300 : : " a.atttypid = x.oid "
301 : : " UNION ALL "
302 : : /* ranges containing any type selected so far */
303 : : " SELECT t.oid FROM pg_catalog.pg_type t, pg_catalog.pg_range r, x "
304 : : " WHERE t.typtype = 'r' AND r.rngtypid = t.oid AND r.rngsubtype = x.oid"
305 : : " ) foo "
306 : : ") "
307 : : /* now look for stored columns of any such type */
308 : : "SELECT n.nspname, c.relname, a.attname "
309 : : "FROM pg_catalog.pg_class c, "
310 : : " pg_catalog.pg_namespace n, "
311 : : " pg_catalog.pg_attribute a "
312 : : "WHERE c.oid = a.attrelid AND "
313 : : " NOT a.attisdropped AND "
314 : : " a.atttypid IN (SELECT oid FROM oids) AND "
315 : : " c.relkind IN ("
316 : : CppAsString2(RELKIND_RELATION) ", "
317 : : CppAsString2(RELKIND_MATVIEW) ", "
318 : : CppAsString2(RELKIND_INDEX) ") AND "
319 : : " c.relnamespace = n.oid AND "
320 : : /* exclude possible orphaned temp tables */
321 : : " n.nspname !~ '^pg_temp_' AND "
322 : : " n.nspname !~ '^pg_toast_temp_' AND "
323 : : /* exclude system catalogs, too */
324 : : " n.nspname NOT IN ('pg_catalog', 'information_schema')",
325 : : check->base_query);
326 : : }
327 : :
328 : : /*
329 : : * Callback function for processing results of queries for
330 : : * check_for_data_types_usage()'s UpgradeTask. If the query returned any rows
331 : : * (i.e., the check failed), write the details to the report file.
332 : : */
333 : : static void
334 : 96 : process_data_type_check(DbInfo *dbinfo, PGresult *res, void *arg)
335 : : {
336 : 96 : struct data_type_check_state *state = (struct data_type_check_state *) arg;
337 : 96 : int ntups = PQntuples(res);
338 : : char output_path[MAXPGPATH];
339 : 96 : int i_nspname = PQfnumber(res, "nspname");
340 : 96 : int i_relname = PQfnumber(res, "relname");
341 : 96 : int i_attname = PQfnumber(res, "attname");
342 : 96 : FILE *script = NULL;
343 : :
344 [ + - ]: 96 : if (ntups == 0)
345 : 96 : return;
346 : :
347 : 0 : snprintf(output_path, sizeof(output_path), "%s/%s",
348 : : log_opts.basedir,
349 : 0 : state->check->report_filename);
350 : :
351 : : /*
352 : : * Make sure we have a buffer to save reports to now that we found a first
353 : : * failing check.
354 : : */
355 [ # # ]: 0 : if (*state->report == NULL)
356 : 0 : *state->report = createPQExpBuffer();
357 : :
358 : : /*
359 : : * If this is the first time we see an error for the check in question
360 : : * then print a status message of the failure.
361 : : */
362 [ # # ]: 0 : if (!state->result)
363 : : {
364 : 0 : pg_log(PG_REPORT, "failed check: %s", _(state->check->status));
365 : 0 : appendPQExpBuffer(*state->report, "\n%s\n%s\n %s\n",
366 : 0 : _(state->check->report_text),
367 : : _("A list of the problem columns is in the file:"),
368 : : output_path);
369 : : }
370 : 0 : state->result = true;
371 : :
372 [ # # ]: 0 : if ((script = fopen_priv(output_path, "a")) == NULL)
373 : 0 : pg_fatal("could not open file \"%s\": %m", output_path);
374 : :
375 : 0 : fprintf(script, "In database: %s\n", dbinfo->db_name);
376 : :
377 [ # # ]: 0 : for (int rowno = 0; rowno < ntups; rowno++)
378 : 0 : fprintf(script, " %s.%s.%s\n",
379 : : PQgetvalue(res, rowno, i_nspname),
380 : : PQgetvalue(res, rowno, i_relname),
381 : : PQgetvalue(res, rowno, i_attname));
382 : :
383 : 0 : fclose(script);
384 : : }
385 : :
386 : : /*
387 : : * check_for_data_types_usage()
388 : : * Detect whether there are any stored columns depending on given type(s)
389 : : *
390 : : * If so, write a report to the given file name and signal a failure to the
391 : : * user.
392 : : *
393 : : * The checks to run are defined in a DataTypesUsageChecks structure where
394 : : * each check has a metadata for explaining errors to the user, a base_query,
395 : : * a report filename and a function pointer hook for validating if the check
396 : : * should be executed given the cluster at hand.
397 : : *
398 : : * base_query should be a SELECT yielding a single column named "oid",
399 : : * containing the pg_type OIDs of one or more types that are known to have
400 : : * inconsistent on-disk representations across server versions.
401 : : *
402 : : * We check for the type(s) in tables, matviews, and indexes, but not views;
403 : : * there's no storage involved in a view.
404 : : */
405 : : static void
406 : 15 : check_for_data_types_usage(ClusterInfo *cluster)
407 : : {
408 : 15 : PQExpBuffer report = NULL;
409 : 15 : DataTypesUsageChecks *tmp = data_types_usage_checks;
410 : 15 : int n_data_types_usage_checks = 0;
411 : 15 : UpgradeTask *task = upgrade_task_create();
412 : 15 : char **queries = NULL;
413 : : struct data_type_check_state *states;
414 : :
415 : 15 : prep_status("Checking data type usage");
416 : :
417 : : /* Gather number of checks to perform */
418 [ + + ]: 120 : while (tmp->status != NULL)
419 : : {
420 : 105 : n_data_types_usage_checks++;
421 : 105 : tmp++;
422 : : }
423 : :
424 : : /* Allocate memory for queries and for task states */
425 : 15 : queries = pg_malloc0_array(char *, n_data_types_usage_checks);
426 : 15 : states = pg_malloc0_array(struct data_type_check_state, n_data_types_usage_checks);
427 : :
428 [ + + ]: 120 : for (int i = 0; i < n_data_types_usage_checks; i++)
429 : : {
430 : 105 : DataTypesUsageChecks *check = &data_types_usage_checks[i];
431 : :
432 [ - + ]: 105 : if (check->threshold_version == MANUAL_CHECK)
433 : : {
434 : : Assert(check->version_hook);
435 : :
436 : : /*
437 : : * Make sure that the check applies to the current cluster version
438 : : * and skip it if not.
439 : : */
440 [ # # ]: 0 : if (!check->version_hook(cluster))
441 : 0 : continue;
442 : : }
443 [ + + ]: 105 : else if (check->threshold_version != ALL_VERSIONS)
444 : : {
445 [ + - ]: 75 : if (GET_MAJOR_VERSION(cluster->major_version) > check->threshold_version)
446 : 75 : continue;
447 : : }
448 : : else
449 : : Assert(check->threshold_version == ALL_VERSIONS);
450 : :
451 : 30 : queries[i] = data_type_check_query(i);
452 : :
453 : 30 : states[i].check = check;
454 : 30 : states[i].report = &report;
455 : :
456 : 30 : upgrade_task_add_step(task, queries[i], process_data_type_check,
457 : 30 : true, &states[i]);
458 : : }
459 : :
460 : : /*
461 : : * Connect to each database in the cluster and run all defined checks
462 : : * against that database before trying the next one.
463 : : */
464 : 15 : upgrade_task_run(task, cluster);
465 : 15 : upgrade_task_free(task);
466 : :
467 [ - + ]: 15 : if (report)
468 : : {
469 : 0 : pg_fatal("Data type checks failed: %s", report->data);
470 : : destroyPQExpBuffer(report);
471 : : }
472 : :
473 [ + + ]: 120 : for (int i = 0; i < n_data_types_usage_checks; i++)
474 : : {
475 [ + + ]: 105 : if (queries[i])
476 : 30 : pg_free(queries[i]);
477 : : }
478 : 15 : pg_free(queries);
479 : 15 : pg_free(states);
480 : :
481 : 15 : check_ok();
482 : 15 : }
483 : :
484 : : /*
485 : : * fix_path_separator
486 : : * For non-Windows, just return the argument.
487 : : * For Windows convert any forward slash to a backslash
488 : : * such as is suitable for arguments to builtin commands
489 : : * like RMDIR and DEL.
490 : : */
491 : : static char *
492 : 24 : fix_path_separator(char *path)
493 : : {
494 : : #ifdef WIN32
495 : :
496 : : char *result;
497 : : char *c;
498 : :
499 : : result = pg_strdup(path);
500 : :
501 : : for (c = result; *c != '\0'; c++)
502 : : if (*c == '/')
503 : : *c = '\\';
504 : :
505 : : return result;
506 : : #else
507 : :
508 : 24 : return path;
509 : : #endif
510 : : }
511 : :
512 : : void
513 : 19 : output_check_banner(void)
514 : : {
515 [ - + ]: 19 : if (user_opts.live_check)
516 : : {
517 : 0 : pg_log(PG_REPORT,
518 : : "Performing Consistency Checks on Old Live Server\n"
519 : : "------------------------------------------------");
520 : : }
521 : : else
522 : : {
523 : 19 : pg_log(PG_REPORT,
524 : : "Performing Consistency Checks\n"
525 : : "-----------------------------");
526 : : }
527 : 19 : }
528 : :
529 : :
530 : : void
531 : 18 : check_and_dump_old_cluster(void)
532 : : {
533 : : /* -- OLD -- */
534 : :
535 [ + - ]: 18 : if (!user_opts.live_check)
536 : 18 : start_postmaster(&old_cluster, true);
537 : :
538 : : /*
539 : : * First check that all databases allow connections since we'll otherwise
540 : : * fail in later stages.
541 : : */
542 : 18 : check_for_connection_status(&old_cluster);
543 : :
544 : : /*
545 : : * Check for encodings that are no longer supported.
546 : : */
547 : 17 : check_for_unsupported_encodings(&old_cluster);
548 : :
549 : : /*
550 : : * Validate database, user, role and tablespace names from the old
551 : : * cluster. No need to check in 19 or newer as newline and carriage return
552 : : * are not allowed at the creation time of the object.
553 : : */
554 [ - + ]: 17 : if (GET_MAJOR_VERSION(old_cluster.major_version) < 1900)
555 : 0 : check_old_cluster_global_names(&old_cluster);
556 : :
557 : : /*
558 : : * Extract a list of databases, tables, and logical replication slots from
559 : : * the old cluster.
560 : : */
561 : 17 : get_db_rel_and_slot_infos(&old_cluster);
562 : :
563 : 17 : init_tablespaces();
564 : :
565 : 17 : get_loadable_libraries();
566 : :
567 : :
568 : : /*
569 : : * Check for various failure cases
570 : : */
571 : 17 : check_is_install_user(&old_cluster);
572 : 17 : check_for_prepared_transactions(&old_cluster);
573 : 17 : check_for_isn_and_int8_passing_mismatch(&old_cluster);
574 : :
575 [ + - ]: 17 : if (GET_MAJOR_VERSION(old_cluster.major_version) >= 1700)
576 : : {
577 : : /*
578 : : * Logical replication slots can be migrated since PG17. See comments
579 : : * in get_db_rel_and_slot_infos().
580 : : */
581 : 17 : check_old_cluster_for_valid_slots();
582 : :
583 : : /*
584 : : * Subscriptions and their dependencies can be migrated since PG17.
585 : : * Before that the logical slots are not upgraded, so we will not be
586 : : * able to upgrade the logical replication clusters completely.
587 : : */
588 : 16 : get_subscription_info(&old_cluster);
589 : 16 : check_old_cluster_subscription_state();
590 : : }
591 : :
592 : 15 : check_for_data_types_usage(&old_cluster);
593 : :
594 : : /*
595 : : * Unicode updates can affect some objects that use expressions with
596 : : * functions dependent on Unicode.
597 : : */
598 : 15 : check_for_unicode_update(&old_cluster);
599 : :
600 : : /*
601 : : * PG 14 changed the function signature of encoding conversion functions.
602 : : * Conversions from older versions cannot be upgraded automatically
603 : : * because the user-defined functions used by the encoding conversions
604 : : * need to be changed to match the new signature.
605 : : */
606 [ - + ]: 15 : if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1300)
607 : 0 : check_for_user_defined_encoding_conversions(&old_cluster);
608 : :
609 : : /*
610 : : * Pre-PG 14 allowed user defined postfix operators, which are not
611 : : * supported anymore. Verify there are none, iff applicable.
612 : : */
613 [ - + ]: 15 : if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1300)
614 : 0 : check_for_user_defined_postfix_ops(&old_cluster);
615 : :
616 : : /*
617 : : * PG 14 changed polymorphic functions from anyarray to
618 : : * anycompatiblearray.
619 : : */
620 [ - + ]: 15 : if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1300)
621 : 0 : check_for_incompatible_polymorphics(&old_cluster);
622 : :
623 : : /*
624 : : * Pre-PG 12 allowed tables to be declared WITH OIDS, which is not
625 : : * supported anymore. Verify there are none, iff applicable.
626 : : */
627 [ - + ]: 15 : if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1100)
628 : 0 : check_for_tables_with_oids(&old_cluster);
629 : :
630 : : /*
631 : : * Pre-PG 18 allowed child tables to omit not-null constraints that their
632 : : * parents columns have, but schema restore fails for them. Verify there
633 : : * are none, iff applicable.
634 : : */
635 [ - + ]: 15 : if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
636 : 0 : check_for_not_null_inheritance(&old_cluster);
637 : :
638 : : /*
639 : : * The btree_gist extension contains gist_inet_ops and gist_cidr_ops
640 : : * opclasses that do not reliably give correct answers. We want to
641 : : * deprecate and eventually remove those, and as a first step v19 marks
642 : : * them not-opcdefault and instead marks the replacement in-core opclass
643 : : * "inet_ops" as opcdefault. That creates a problem for pg_upgrade: in
644 : : * versions where those opclasses were marked opcdefault, pg_dump will
645 : : * dump indexes using them with no explicit opclass specification, so that
646 : : * restore would create them using the inet_ops opclass. That would be
647 : : * incompatible with what's actually in the on-disk files. So refuse to
648 : : * upgrade if there are any such indexes.
649 : : */
650 [ - + ]: 15 : if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
651 : 0 : check_for_gist_inet_ops(&old_cluster);
652 : :
653 : : /*
654 : : * While not a check option, we do this now because this is the only time
655 : : * the old server is running.
656 : : */
657 [ + + ]: 15 : if (!user_opts.check)
658 : 12 : generate_old_dump();
659 : :
660 [ + - ]: 15 : if (!user_opts.live_check)
661 : 15 : stop_postmaster(false);
662 : 15 : }
663 : :
664 : :
665 : : void
666 : 15 : check_new_cluster(void)
667 : : {
668 : 15 : get_db_rel_and_slot_infos(&new_cluster);
669 : :
670 : 15 : check_new_cluster_is_empty();
671 : :
672 : 15 : check_loadable_libraries();
673 : :
674 [ + + + + : 15 : switch (user_opts.transfer_mode)
+ - ]
675 : : {
676 : 1 : case TRANSFER_MODE_CLONE:
677 : 1 : check_file_clone();
678 : 0 : break;
679 : 11 : case TRANSFER_MODE_COPY:
680 : 11 : break;
681 : 1 : case TRANSFER_MODE_COPY_FILE_RANGE:
682 : 1 : check_copy_file_range();
683 : 1 : break;
684 : 1 : case TRANSFER_MODE_LINK:
685 : 1 : check_hard_link(TRANSFER_MODE_LINK);
686 : 1 : break;
687 : 1 : case TRANSFER_MODE_SWAP:
688 : :
689 : : /*
690 : : * We do the hard link check for --swap, too, since it's an easy
691 : : * way to verify the clusters are in the same file system. This
692 : : * allows us to take some shortcuts in the file synchronization
693 : : * step. With some more effort, we could probably support the
694 : : * separate-file-system use case, but this mode is unlikely to
695 : : * offer much benefit if we have to copy the files across file
696 : : * system boundaries.
697 : : */
698 : 1 : check_hard_link(TRANSFER_MODE_SWAP);
699 : 1 : break;
700 : : }
701 : :
702 : 14 : check_is_install_user(&new_cluster);
703 : :
704 : 14 : check_for_prepared_transactions(&new_cluster);
705 : :
706 : 14 : check_for_new_tablespace_dir();
707 : :
708 : 14 : check_new_cluster_replication_slots();
709 : :
710 : 12 : check_new_cluster_subscription_configuration();
711 : 11 : }
712 : :
713 : :
714 : : void
715 : 11 : report_clusters_compatible(void)
716 : : {
717 [ + + ]: 11 : if (user_opts.check)
718 : : {
719 : 1 : pg_log(PG_REPORT, "\n*Clusters are compatible*");
720 : : /* stops new cluster */
721 : 1 : stop_postmaster(false);
722 : :
723 : 1 : cleanup_output_dirs();
724 : 1 : exit(0);
725 : : }
726 : :
727 : 10 : pg_log(PG_REPORT, "\n"
728 : : "If pg_upgrade fails after this point, you must re-initdb the\n"
729 : : "new cluster before continuing.");
730 : 10 : }
731 : :
732 : :
733 : : void
734 : 10 : issue_warnings_and_set_wal_level(void)
735 : : {
736 : : /*
737 : : * We unconditionally start/stop the new server because pg_resetwal -o set
738 : : * wal_level to 'minimum'. If the user is upgrading standby servers using
739 : : * the rsync instructions, they will need pg_upgrade to write its final
740 : : * WAL record showing wal_level as 'replica'.
741 : : */
742 : 10 : start_postmaster(&new_cluster, true);
743 : :
744 : 10 : report_extension_updates(&new_cluster);
745 : :
746 : 10 : stop_postmaster(false);
747 : 10 : }
748 : :
749 : :
750 : : void
751 : 10 : output_completion_banner(char *deletion_script_file_name)
752 : : {
753 : : PQExpBufferData user_specification;
754 : :
755 : 10 : initPQExpBuffer(&user_specification);
756 [ - + ]: 10 : if (os_info.user_specified)
757 : : {
758 : 0 : appendPQExpBufferStr(&user_specification, "-U ");
759 : 0 : appendShellString(&user_specification, os_info.user);
760 : 0 : appendPQExpBufferChar(&user_specification, ' ');
761 : : }
762 : :
763 : 10 : pg_log(PG_REPORT,
764 : : "Some statistics are not transferred by pg_upgrade.\n"
765 : : "Once you start the new server, consider running these two commands:\n"
766 : : " %s/vacuumdb %s--all --analyze-in-stages --missing-stats-only\n"
767 : : " %s/vacuumdb %s--all --analyze-only",
768 : : new_cluster.bindir, user_specification.data,
769 : : new_cluster.bindir, user_specification.data);
770 : :
771 [ + - ]: 10 : if (deletion_script_file_name)
772 : 10 : pg_log(PG_REPORT,
773 : : "Running this script will delete the old cluster's data files:\n"
774 : : " %s",
775 : : deletion_script_file_name);
776 : : else
777 : 0 : pg_log(PG_REPORT,
778 : : "Could not create a script to delete the old cluster's data files\n"
779 : : "because user-defined tablespaces or the new cluster's data directory\n"
780 : : "exist in the old cluster directory. The old cluster's contents must\n"
781 : : "be deleted manually.");
782 : :
783 : 10 : termPQExpBuffer(&user_specification);
784 : 10 : }
785 : :
786 : :
787 : : void
788 : 19 : check_cluster_versions(void)
789 : : {
790 : 19 : prep_status("Checking cluster versions");
791 : :
792 : : /* cluster versions should already have been obtained */
793 : : Assert(old_cluster.major_version != 0);
794 : : Assert(new_cluster.major_version != 0);
795 : :
796 : : /*
797 : : * We allow upgrades from/to the same major version for alpha/beta
798 : : * upgrades
799 : : */
800 : :
801 [ - + ]: 19 : if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
802 : 0 : pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
803 : : "10");
804 : :
805 : : /* Only current PG version is supported as a target */
806 [ - + ]: 19 : if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
807 : 0 : pg_fatal("This utility can only upgrade to PostgreSQL version %s.",
808 : : PG_MAJORVERSION);
809 : :
810 : : /*
811 : : * We can't allow downgrading because we use the target pg_dump, and
812 : : * pg_dump cannot operate on newer database versions, only current and
813 : : * older versions.
814 : : */
815 [ - + ]: 19 : if (old_cluster.major_version > new_cluster.major_version)
816 : 0 : pg_fatal("This utility cannot be used to downgrade to older major PostgreSQL versions.");
817 : :
818 : : /* Ensure binaries match the designated data directories */
819 : 19 : if (GET_MAJOR_VERSION(old_cluster.major_version) !=
820 [ - + ]: 19 : GET_MAJOR_VERSION(old_cluster.bin_version))
821 : 0 : pg_fatal("Old cluster data and binary directories are from different major versions.");
822 : 19 : if (GET_MAJOR_VERSION(new_cluster.major_version) !=
823 [ - + ]: 19 : GET_MAJOR_VERSION(new_cluster.bin_version))
824 : 0 : pg_fatal("New cluster data and binary directories are from different major versions.");
825 : :
826 : : /*
827 : : * Since from version 18, newly created database clusters always have
828 : : * 'signed' default char-signedness, it makes less sense to use
829 : : * --set-char-signedness option for upgrading from version 18 or later.
830 : : * Users who want to change the default char signedness of the new
831 : : * cluster, they can use pg_resetwal manually before the upgrade.
832 : : */
833 [ + - ]: 19 : if (GET_MAJOR_VERSION(old_cluster.major_version) >= 1800 &&
834 [ + + ]: 19 : user_opts.char_signedness != -1)
835 : 1 : pg_fatal("The option %s cannot be used for upgrades from PostgreSQL %s and later.",
836 : : "--set-char-signedness", "18");
837 : :
838 : 18 : check_ok();
839 : 18 : }
840 : :
841 : :
842 : : void
843 : 18 : check_cluster_compatibility(void)
844 : : {
845 : : /* get/check pg_control data of servers */
846 : 18 : get_control_data(&old_cluster);
847 : 18 : get_control_data(&new_cluster);
848 : 18 : check_control_data(&old_cluster.controldata, &new_cluster.controldata);
849 : :
850 [ - + - - ]: 18 : if (user_opts.live_check && old_cluster.port == new_cluster.port)
851 : 0 : pg_fatal("When checking a live server, "
852 : : "the old and new port numbers must be different.");
853 : 18 : }
854 : :
855 : :
856 : : static void
857 : 15 : check_new_cluster_is_empty(void)
858 : : {
859 : : int dbnum;
860 : :
861 [ + + ]: 45 : for (dbnum = 0; dbnum < new_cluster.dbarr.ndbs; dbnum++)
862 : : {
863 : : int relnum;
864 : 30 : RelInfoArr *rel_arr = &new_cluster.dbarr.dbs[dbnum].rel_arr;
865 : :
866 [ + + ]: 150 : for (relnum = 0; relnum < rel_arr->nrels;
867 : 120 : relnum++)
868 : : {
869 : : /* pg_largeobject and its index should be skipped */
870 [ - + ]: 120 : if (strcmp(rel_arr->rels[relnum].nspname, "pg_catalog") != 0)
871 : 0 : pg_fatal("New cluster database \"%s\" is not empty: found relation \"%s.%s\"",
872 : 0 : new_cluster.dbarr.dbs[dbnum].db_name,
873 : 0 : rel_arr->rels[relnum].nspname,
874 : 0 : rel_arr->rels[relnum].relname);
875 : : }
876 : : }
877 : 15 : }
878 : :
879 : : /*
880 : : * A previous run of pg_upgrade might have failed and the new cluster
881 : : * directory recreated, but they might have forgotten to remove
882 : : * the new cluster's tablespace directories. Therefore, check that
883 : : * new cluster tablespace directories do not already exist. If
884 : : * they do, it would cause an error while restoring global objects.
885 : : * This allows the failure to be detected at check time, rather than
886 : : * during schema restore.
887 : : */
888 : : static void
889 : 14 : check_for_new_tablespace_dir(void)
890 : : {
891 : : int tblnum;
892 : : char new_tablespace_dir[MAXPGPATH];
893 : :
894 : 14 : prep_status("Checking new cluster tablespace directories");
895 : :
896 [ + + ]: 18 : for (tblnum = 0; tblnum < new_cluster.num_tablespaces; tblnum++)
897 : : {
898 : : struct stat statbuf;
899 : :
900 : 4 : snprintf(new_tablespace_dir, MAXPGPATH, "%s%s",
901 : 4 : new_cluster.tablespaces[tblnum],
902 : : new_cluster.tablespace_suffix);
903 : :
904 [ + - - + ]: 4 : if (stat(new_tablespace_dir, &statbuf) == 0 || errno != ENOENT)
905 : 0 : pg_fatal("new cluster tablespace directory already exists: \"%s\"",
906 : : new_tablespace_dir);
907 : : }
908 : :
909 : 14 : check_ok();
910 : 14 : }
911 : :
912 : : /*
913 : : * create_script_for_old_cluster_deletion()
914 : : *
915 : : * This is particularly useful for tablespace deletion.
916 : : */
917 : : void
918 : 10 : create_script_for_old_cluster_deletion(char **deletion_script_file_name)
919 : : {
920 : 10 : FILE *script = NULL;
921 : : int tblnum;
922 : : char old_cluster_pgdata[MAXPGPATH],
923 : : new_cluster_pgdata[MAXPGPATH];
924 : : char *old_tblspc_suffix;
925 : :
926 : 10 : *deletion_script_file_name = psprintf("%sdelete_old_cluster.%s",
927 : : SCRIPT_PREFIX, SCRIPT_EXT);
928 : :
929 : 10 : strlcpy(old_cluster_pgdata, old_cluster.pgdata, MAXPGPATH);
930 : 10 : canonicalize_path(old_cluster_pgdata);
931 : :
932 : 10 : strlcpy(new_cluster_pgdata, new_cluster.pgdata, MAXPGPATH);
933 : 10 : canonicalize_path(new_cluster_pgdata);
934 : :
935 : : /* Some people put the new data directory inside the old one. */
936 [ - + ]: 10 : if (path_is_prefix_of_path(old_cluster_pgdata, new_cluster_pgdata))
937 : : {
938 : 0 : pg_log(PG_WARNING,
939 : : "\nWARNING: new data directory should not be inside the old data directory, i.e. %s", old_cluster_pgdata);
940 : :
941 : : /* Unlink file in case it is left over from a previous run. */
942 : 0 : unlink(*deletion_script_file_name);
943 : 0 : pfree(*deletion_script_file_name);
944 : 0 : *deletion_script_file_name = NULL;
945 : 0 : return;
946 : : }
947 : :
948 : : /*
949 : : * Some users (oddly) create tablespaces inside the cluster data
950 : : * directory. We can't create a proper old cluster delete script in that
951 : : * case.
952 : : */
953 [ + + ]: 14 : for (tblnum = 0; tblnum < new_cluster.num_tablespaces; tblnum++)
954 : : {
955 : : char new_tablespace_dir[MAXPGPATH];
956 : :
957 : 4 : strlcpy(new_tablespace_dir, new_cluster.tablespaces[tblnum], MAXPGPATH);
958 : 4 : canonicalize_path(new_tablespace_dir);
959 [ - + ]: 4 : if (path_is_prefix_of_path(old_cluster_pgdata, new_tablespace_dir))
960 : : {
961 : : /* reproduce warning from CREATE TABLESPACE that is in the log */
962 : 0 : pg_log(PG_WARNING,
963 : : "\nWARNING: user-defined tablespace locations should not be inside the data directory, i.e. %s", new_tablespace_dir);
964 : :
965 : : /* Unlink file in case it is left over from a previous run. */
966 : 0 : unlink(*deletion_script_file_name);
967 : 0 : pfree(*deletion_script_file_name);
968 : 0 : *deletion_script_file_name = NULL;
969 : 0 : return;
970 : : }
971 : : }
972 : :
973 : 10 : prep_status("Creating script to delete old cluster");
974 : :
975 [ - + ]: 10 : if ((script = fopen_priv(*deletion_script_file_name, "w")) == NULL)
976 : 0 : pg_fatal("could not open file \"%s\": %m",
977 : : *deletion_script_file_name);
978 : :
979 : : #ifndef WIN32
980 : : /* add shebang header */
981 : 10 : fprintf(script, "#!/bin/sh\n\n");
982 : : #endif
983 : :
984 : : /* delete old cluster's default tablespace */
985 : 10 : fprintf(script, RMDIR_CMD " %c%s%c\n", PATH_QUOTE,
986 : : fix_path_separator(old_cluster.pgdata), PATH_QUOTE);
987 : :
988 : : /* delete old cluster's alternate tablespaces */
989 : 10 : old_tblspc_suffix = pg_strdup(old_cluster.tablespace_suffix);
990 : 10 : fix_path_separator(old_tblspc_suffix);
991 [ + + ]: 14 : for (tblnum = 0; tblnum < old_cluster.num_tablespaces; tblnum++)
992 : 4 : fprintf(script, RMDIR_CMD " %c%s%s%c\n", PATH_QUOTE,
993 : 4 : fix_path_separator(old_cluster.tablespaces[tblnum]),
994 : : old_tblspc_suffix, PATH_QUOTE);
995 : 10 : pg_free(old_tblspc_suffix);
996 : :
997 : 10 : fclose(script);
998 : :
999 : : #ifndef WIN32
1000 [ - + ]: 10 : if (chmod(*deletion_script_file_name, S_IRWXU) != 0)
1001 : 0 : pg_fatal("could not add execute permission to file \"%s\": %m",
1002 : : *deletion_script_file_name);
1003 : : #endif
1004 : :
1005 : 10 : check_ok();
1006 : : }
1007 : :
1008 : :
1009 : : /*
1010 : : * check_is_install_user()
1011 : : *
1012 : : * Check we are the install user, and that the new cluster
1013 : : * has no other users.
1014 : : */
1015 : : static void
1016 : 31 : check_is_install_user(ClusterInfo *cluster)
1017 : : {
1018 : : PGresult *res;
1019 : 31 : PGconn *conn = connectToServer(cluster, "template1");
1020 : :
1021 : 31 : prep_status("Checking database user is the install user");
1022 : :
1023 : : /* Can't use pg_authid because only superusers can view it. */
1024 : 31 : res = executeQueryOrDie(conn,
1025 : : "SELECT rolsuper, oid "
1026 : : "FROM pg_catalog.pg_roles "
1027 : : "WHERE rolname = current_user "
1028 : : "AND rolname !~ '^pg_'");
1029 : :
1030 : : /*
1031 : : * We only allow the install user in the new cluster (see comment below)
1032 : : * and we preserve pg_authid.oid, so this must be the install user in the
1033 : : * old cluster too.
1034 : : */
1035 [ + - ]: 31 : if (PQntuples(res) != 1 ||
1036 [ - + ]: 31 : atooid(PQgetvalue(res, 0, 1)) != BOOTSTRAP_SUPERUSERID)
1037 : 0 : pg_fatal("database user \"%s\" is not the install user",
1038 : : os_info.user);
1039 : :
1040 : 31 : PQclear(res);
1041 : :
1042 : 31 : res = executeQueryOrDie(conn,
1043 : : "SELECT COUNT(*) "
1044 : : "FROM pg_catalog.pg_roles "
1045 : : "WHERE rolname !~ '^pg_'");
1046 : :
1047 [ - + ]: 31 : if (PQntuples(res) != 1)
1048 : 0 : pg_fatal("could not determine the number of users");
1049 : :
1050 : : /*
1051 : : * We only allow the install user in the new cluster because other defined
1052 : : * users might match users defined in the old cluster and generate an
1053 : : * error during pg_dump restore.
1054 : : */
1055 [ + + - + ]: 31 : if (cluster == &new_cluster && strcmp(PQgetvalue(res, 0, 0), "1") != 0)
1056 : 0 : pg_fatal("Only the install user can be defined in the new cluster.");
1057 : :
1058 : 31 : PQclear(res);
1059 : :
1060 : 31 : PQfinish(conn);
1061 : :
1062 : 31 : check_ok();
1063 : 31 : }
1064 : :
1065 : :
1066 : : /*
1067 : : * check_for_connection_status
1068 : : *
1069 : : * Ensure that all non-template0 databases allow connections since they
1070 : : * otherwise won't be restored; and that template0 explicitly doesn't allow
1071 : : * connections since it would make pg_dumpall --globals restore fail.
1072 : : */
1073 : : static void
1074 : 18 : check_for_connection_status(ClusterInfo *cluster)
1075 : : {
1076 : : int dbnum;
1077 : : PGconn *conn_template1;
1078 : : PGresult *dbres;
1079 : : int ntups;
1080 : : int i_datname;
1081 : : int i_datallowconn;
1082 : : int i_datconnlimit;
1083 : 18 : FILE *script = NULL;
1084 : : char output_path[MAXPGPATH];
1085 : :
1086 : 18 : prep_status("Checking database connection settings");
1087 : :
1088 : 18 : snprintf(output_path, sizeof(output_path), "%s/%s",
1089 : : log_opts.basedir,
1090 : : "databases_cannot_connect_to.txt");
1091 : :
1092 : 18 : conn_template1 = connectToServer(cluster, "template1");
1093 : :
1094 : : /* get database names */
1095 : 18 : dbres = executeQueryOrDie(conn_template1,
1096 : : "SELECT datname, datallowconn, datconnlimit "
1097 : : "FROM pg_catalog.pg_database");
1098 : :
1099 : 18 : i_datname = PQfnumber(dbres, "datname");
1100 : 18 : i_datallowconn = PQfnumber(dbres, "datallowconn");
1101 : 18 : i_datconnlimit = PQfnumber(dbres, "datconnlimit");
1102 : :
1103 : 18 : ntups = PQntuples(dbres);
1104 [ + + ]: 95 : for (dbnum = 0; dbnum < ntups; dbnum++)
1105 : : {
1106 : 77 : char *datname = PQgetvalue(dbres, dbnum, i_datname);
1107 : 77 : char *datallowconn = PQgetvalue(dbres, dbnum, i_datallowconn);
1108 : 77 : char *datconnlimit = PQgetvalue(dbres, dbnum, i_datconnlimit);
1109 : :
1110 [ + + ]: 77 : if (strcmp(datname, "template0") == 0)
1111 : : {
1112 : : /* avoid restore failure when pg_dumpall tries to create template0 */
1113 [ - + ]: 18 : if (strcmp(datallowconn, "t") == 0)
1114 : 0 : pg_fatal("template0 must not allow connections, "
1115 : : "i.e. its pg_database.datallowconn must be false");
1116 : : }
1117 : : else
1118 : : {
1119 : : /*
1120 : : * Avoid datallowconn == false databases from being skipped on
1121 : : * restore, and ensure that no databases are marked invalid with
1122 : : * datconnlimit == -2.
1123 : : */
1124 [ + - + + ]: 59 : if ((strcmp(datallowconn, "f") == 0) || strcmp(datconnlimit, "-2") == 0)
1125 : : {
1126 [ + - - + ]: 1 : if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
1127 : 0 : pg_fatal("could not open file \"%s\": %m", output_path);
1128 : :
1129 : 1 : fprintf(script, "%s\n", datname);
1130 : : }
1131 : : }
1132 : : }
1133 : :
1134 : 18 : PQclear(dbres);
1135 : :
1136 : 18 : PQfinish(conn_template1);
1137 : :
1138 [ + + ]: 18 : if (script)
1139 : : {
1140 : 1 : fclose(script);
1141 : 1 : pg_log(PG_REPORT, "fatal");
1142 : 1 : pg_fatal("All non-template0 databases must allow connections, i.e. their\n"
1143 : : "pg_database.datallowconn must be true and pg_database.datconnlimit\n"
1144 : : "must not be -2. Your installation contains non-template0 databases\n"
1145 : : "which cannot be connected to. Consider allowing connection for all\n"
1146 : : "non-template0 databases or drop the databases which do not allow\n"
1147 : : "connections. A list of databases with the problem is in the file:\n"
1148 : : " %s", output_path);
1149 : : }
1150 : : else
1151 : 17 : check_ok();
1152 : 17 : }
1153 : :
1154 : :
1155 : : /*
1156 : : * check_for_unsupported_encodings()
1157 : : */
1158 : : static void
1159 : 17 : check_for_unsupported_encodings(ClusterInfo *cluster)
1160 : : {
1161 : : int i_datname;
1162 : : int i_encoding;
1163 : : int ntups;
1164 : : PGresult *res;
1165 : : PGconn *conn;
1166 : 17 : FILE *script = NULL;
1167 : : char output_path[MAXPGPATH];
1168 : :
1169 : 17 : prep_status("Checking for unsupported encodings");
1170 : :
1171 : 17 : snprintf(output_path, sizeof(output_path), "%s/%s",
1172 : : log_opts.basedir,
1173 : : "databases_unsupported_encoding.txt");
1174 : :
1175 : 17 : conn = connectToServer(cluster, "template1");
1176 : :
1177 : 17 : res = executeQueryOrDie(conn,
1178 : : "SELECT datname, encoding "
1179 : : "FROM pg_catalog.pg_database");
1180 : 17 : ntups = PQntuples(res);
1181 : 17 : i_datname = PQfnumber(res, "datname");
1182 : 17 : i_encoding = PQfnumber(res, "encoding");
1183 [ + + ]: 86 : for (int rowno = 0; rowno < ntups; rowno++)
1184 : : {
1185 : 69 : char *datname = PQgetvalue(res, rowno, i_datname);
1186 : 69 : int encoding = atoi(PQgetvalue(res, rowno, i_encoding));
1187 : :
1188 [ + - + - : 69 : if (!PG_VALID_BE_ENCODING(encoding))
- + ]
1189 : : {
1190 [ # # # # ]: 0 : if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
1191 : 0 : pg_fatal("could not open file \"%s\": %m", output_path);
1192 : :
1193 : 0 : fprintf(script, "%s\n", datname);
1194 : : }
1195 : : }
1196 : 17 : PQclear(res);
1197 : 17 : PQfinish(conn);
1198 : :
1199 [ - + ]: 17 : if (script)
1200 : : {
1201 : 0 : fclose(script);
1202 : 0 : pg_log(PG_REPORT, "fatal");
1203 : 0 : pg_fatal("Your installation contains databases using encodings that are\n"
1204 : : "no longer supported. Consider dumping and restoring with UTF8.\n"
1205 : : "A list of databases with unsupported encodings is in the file:\n"
1206 : : " %s", output_path);
1207 : : }
1208 : : else
1209 : 17 : check_ok();
1210 : 17 : }
1211 : :
1212 : :
1213 : : /*
1214 : : * check_for_prepared_transactions()
1215 : : *
1216 : : * Make sure there are no prepared transactions because the storage format
1217 : : * might have changed.
1218 : : */
1219 : : static void
1220 : 31 : check_for_prepared_transactions(ClusterInfo *cluster)
1221 : : {
1222 : : PGresult *res;
1223 : 31 : PGconn *conn = connectToServer(cluster, "template1");
1224 : :
1225 : 31 : prep_status("Checking for prepared transactions");
1226 : :
1227 : 31 : res = executeQueryOrDie(conn,
1228 : : "SELECT * "
1229 : : "FROM pg_catalog.pg_prepared_xacts");
1230 : :
1231 [ - + ]: 31 : if (PQntuples(res) != 0)
1232 : : {
1233 [ # # ]: 0 : if (cluster == &old_cluster)
1234 : 0 : pg_fatal("The source cluster contains prepared transactions");
1235 : : else
1236 : 0 : pg_fatal("The target cluster contains prepared transactions");
1237 : : }
1238 : :
1239 : 31 : PQclear(res);
1240 : :
1241 : 31 : PQfinish(conn);
1242 : :
1243 : 31 : check_ok();
1244 : 31 : }
1245 : :
1246 : : /*
1247 : : * Callback function for processing result of query for
1248 : : * check_for_isn_and_int8_passing_mismatch()'s UpgradeTask. If the query
1249 : : * returned any rows (i.e., the check failed), write the details to the report
1250 : : * file.
1251 : : */
1252 : : static void
1253 : 0 : process_isn_and_int8_passing_mismatch(DbInfo *dbinfo, PGresult *res, void *arg)
1254 : : {
1255 : 0 : int ntups = PQntuples(res);
1256 : 0 : int i_nspname = PQfnumber(res, "nspname");
1257 : 0 : int i_proname = PQfnumber(res, "proname");
1258 : 0 : UpgradeTaskReport *report = (UpgradeTaskReport *) arg;
1259 : :
1260 [ # # ]: 0 : if (ntups == 0)
1261 : 0 : return;
1262 : :
1263 [ # # ]: 0 : if (report->file == NULL &&
1264 [ # # ]: 0 : (report->file = fopen_priv(report->path, "w")) == NULL)
1265 : 0 : pg_fatal("could not open file \"%s\": %m", report->path);
1266 : :
1267 : 0 : fprintf(report->file, "In database: %s\n", dbinfo->db_name);
1268 : :
1269 [ # # ]: 0 : for (int rowno = 0; rowno < ntups; rowno++)
1270 : 0 : fprintf(report->file, " %s.%s\n",
1271 : : PQgetvalue(res, rowno, i_nspname),
1272 : : PQgetvalue(res, rowno, i_proname));
1273 : : }
1274 : :
1275 : : /*
1276 : : * check_for_isn_and_int8_passing_mismatch()
1277 : : *
1278 : : * contrib/isn relies on data type int8, and in 8.4 int8 can now be passed
1279 : : * by value. The schema dumps the CREATE TYPE PASSEDBYVALUE setting so
1280 : : * it must match for the old and new servers.
1281 : : */
1282 : : static void
1283 : 17 : check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster)
1284 : : {
1285 : : UpgradeTask *task;
1286 : : UpgradeTaskReport report;
1287 : 17 : const char *query = "SELECT n.nspname, p.proname "
1288 : : "FROM pg_catalog.pg_proc p, "
1289 : : " pg_catalog.pg_namespace n "
1290 : : "WHERE p.pronamespace = n.oid AND "
1291 : : " p.probin = '$libdir/isn'";
1292 : :
1293 : 17 : prep_status("Checking for contrib/isn with bigint-passing mismatch");
1294 : :
1295 : 17 : if (old_cluster.controldata.float8_pass_by_value ==
1296 [ + - ]: 17 : new_cluster.controldata.float8_pass_by_value)
1297 : : {
1298 : : /* no mismatch */
1299 : 17 : check_ok();
1300 : 17 : return;
1301 : : }
1302 : :
1303 : 0 : report.file = NULL;
1304 : 0 : snprintf(report.path, sizeof(report.path), "%s/%s",
1305 : : log_opts.basedir,
1306 : : "contrib_isn_and_int8_pass_by_value.txt");
1307 : :
1308 : 0 : task = upgrade_task_create();
1309 : 0 : upgrade_task_add_step(task, query, process_isn_and_int8_passing_mismatch,
1310 : : true, &report);
1311 : 0 : upgrade_task_run(task, cluster);
1312 : 0 : upgrade_task_free(task);
1313 : :
1314 [ # # ]: 0 : if (report.file)
1315 : : {
1316 : 0 : fclose(report.file);
1317 : 0 : pg_log(PG_REPORT, "fatal");
1318 : 0 : pg_fatal("Your installation contains \"contrib/isn\" functions which rely on the\n"
1319 : : "bigint data type. Your old and new clusters pass bigint values\n"
1320 : : "differently so this cluster cannot currently be upgraded. You can\n"
1321 : : "manually dump databases in the old cluster that use \"contrib/isn\"\n"
1322 : : "facilities, drop them, perform the upgrade, and then restore them. A\n"
1323 : : "list of the problem functions is in the file:\n"
1324 : : " %s", report.path);
1325 : : }
1326 : : else
1327 : 0 : check_ok();
1328 : : }
1329 : :
1330 : : /*
1331 : : * Callback function for processing result of query for
1332 : : * check_for_user_defined_postfix_ops()'s UpgradeTask. If the query returned
1333 : : * any rows (i.e., the check failed), write the details to the report file.
1334 : : */
1335 : : static void
1336 : 0 : process_user_defined_postfix_ops(DbInfo *dbinfo, PGresult *res, void *arg)
1337 : : {
1338 : 0 : UpgradeTaskReport *report = (UpgradeTaskReport *) arg;
1339 : 0 : int ntups = PQntuples(res);
1340 : 0 : int i_oproid = PQfnumber(res, "oproid");
1341 : 0 : int i_oprnsp = PQfnumber(res, "oprnsp");
1342 : 0 : int i_oprname = PQfnumber(res, "oprname");
1343 : 0 : int i_typnsp = PQfnumber(res, "typnsp");
1344 : 0 : int i_typname = PQfnumber(res, "typname");
1345 : :
1346 [ # # ]: 0 : if (ntups == 0)
1347 : 0 : return;
1348 : :
1349 [ # # ]: 0 : if (report->file == NULL &&
1350 [ # # ]: 0 : (report->file = fopen_priv(report->path, "w")) == NULL)
1351 : 0 : pg_fatal("could not open file \"%s\": %m", report->path);
1352 : :
1353 : 0 : fprintf(report->file, "In database: %s\n", dbinfo->db_name);
1354 : :
1355 [ # # ]: 0 : for (int rowno = 0; rowno < ntups; rowno++)
1356 : 0 : fprintf(report->file, " (oid=%s) %s.%s (%s.%s, NONE)\n",
1357 : : PQgetvalue(res, rowno, i_oproid),
1358 : : PQgetvalue(res, rowno, i_oprnsp),
1359 : : PQgetvalue(res, rowno, i_oprname),
1360 : : PQgetvalue(res, rowno, i_typnsp),
1361 : : PQgetvalue(res, rowno, i_typname));
1362 : : }
1363 : :
1364 : : /*
1365 : : * Verify that no user defined postfix operators exist.
1366 : : */
1367 : : static void
1368 : 0 : check_for_user_defined_postfix_ops(ClusterInfo *cluster)
1369 : : {
1370 : : UpgradeTaskReport report;
1371 : 0 : UpgradeTask *task = upgrade_task_create();
1372 : : const char *query;
1373 : :
1374 : : /*
1375 : : * The query below hardcodes FirstNormalObjectId as 16384 rather than
1376 : : * interpolating that C #define into the query because, if that #define is
1377 : : * ever changed, the cutoff we want to use is the value used by
1378 : : * pre-version 14 servers, not that of some future version.
1379 : : */
1380 : 0 : query = "SELECT o.oid AS oproid, "
1381 : : " n.nspname AS oprnsp, "
1382 : : " o.oprname, "
1383 : : " tn.nspname AS typnsp, "
1384 : : " t.typname "
1385 : : "FROM pg_catalog.pg_operator o, "
1386 : : " pg_catalog.pg_namespace n, "
1387 : : " pg_catalog.pg_type t, "
1388 : : " pg_catalog.pg_namespace tn "
1389 : : "WHERE o.oprnamespace = n.oid AND "
1390 : : " o.oprleft = t.oid AND "
1391 : : " t.typnamespace = tn.oid AND "
1392 : : " o.oprright = 0 AND "
1393 : : " o.oid >= 16384";
1394 : :
1395 : 0 : prep_status("Checking for user-defined postfix operators");
1396 : :
1397 : 0 : report.file = NULL;
1398 : 0 : snprintf(report.path, sizeof(report.path), "%s/%s",
1399 : : log_opts.basedir,
1400 : : "postfix_ops.txt");
1401 : :
1402 : 0 : upgrade_task_add_step(task, query, process_user_defined_postfix_ops,
1403 : : true, &report);
1404 : 0 : upgrade_task_run(task, cluster);
1405 : 0 : upgrade_task_free(task);
1406 : :
1407 [ # # ]: 0 : if (report.file)
1408 : : {
1409 : 0 : fclose(report.file);
1410 : 0 : pg_log(PG_REPORT, "fatal");
1411 : 0 : pg_fatal("Your installation contains user-defined postfix operators, which are not\n"
1412 : : "supported anymore. Consider dropping the postfix operators and replacing\n"
1413 : : "them with prefix operators or function calls.\n"
1414 : : "A list of user-defined postfix operators is in the file:\n"
1415 : : " %s", report.path);
1416 : : }
1417 : : else
1418 : 0 : check_ok();
1419 : 0 : }
1420 : :
1421 : : /*
1422 : : * Callback function for processing results of query for
1423 : : * check_for_incompatible_polymorphics()'s UpgradeTask. If the query returned
1424 : : * any rows (i.e., the check failed), write the details to the report file.
1425 : : */
1426 : : static void
1427 : 0 : process_incompat_polymorphics(DbInfo *dbinfo, PGresult *res, void *arg)
1428 : : {
1429 : 0 : UpgradeTaskReport *report = (UpgradeTaskReport *) arg;
1430 : 0 : int ntups = PQntuples(res);
1431 : 0 : int i_objkind = PQfnumber(res, "objkind");
1432 : 0 : int i_objname = PQfnumber(res, "objname");
1433 : :
1434 [ # # ]: 0 : if (ntups == 0)
1435 : 0 : return;
1436 : :
1437 [ # # ]: 0 : if (report->file == NULL &&
1438 [ # # ]: 0 : (report->file = fopen_priv(report->path, "w")) == NULL)
1439 : 0 : pg_fatal("could not open file \"%s\": %m", report->path);
1440 : :
1441 : 0 : fprintf(report->file, "In database: %s\n", dbinfo->db_name);
1442 : :
1443 [ # # ]: 0 : for (int rowno = 0; rowno < ntups; rowno++)
1444 : 0 : fprintf(report->file, " %s: %s\n",
1445 : : PQgetvalue(res, rowno, i_objkind),
1446 : : PQgetvalue(res, rowno, i_objname));
1447 : : }
1448 : :
1449 : : /*
1450 : : * check_for_incompatible_polymorphics()
1451 : : *
1452 : : * Make sure nothing is using old polymorphic functions with
1453 : : * anyarray/anyelement rather than the new anycompatible variants.
1454 : : */
1455 : : static void
1456 : 0 : check_for_incompatible_polymorphics(ClusterInfo *cluster)
1457 : : {
1458 : : PQExpBufferData old_polymorphics;
1459 : 0 : UpgradeTask *task = upgrade_task_create();
1460 : : UpgradeTaskReport report;
1461 : : char *query;
1462 : :
1463 : 0 : prep_status("Checking for incompatible polymorphic functions");
1464 : :
1465 : 0 : report.file = NULL;
1466 : 0 : snprintf(report.path, sizeof(report.path), "%s/%s",
1467 : : log_opts.basedir,
1468 : : "incompatible_polymorphics.txt");
1469 : :
1470 : : /* The set of problematic functions varies a bit in different versions */
1471 : 0 : initPQExpBuffer(&old_polymorphics);
1472 : :
1473 : 0 : appendPQExpBufferStr(&old_polymorphics,
1474 : : "'array_append(anyarray,anyelement)'"
1475 : : ", 'array_cat(anyarray,anyarray)'"
1476 : : ", 'array_prepend(anyelement,anyarray)'");
1477 : :
1478 : 0 : appendPQExpBufferStr(&old_polymorphics,
1479 : : ", 'array_remove(anyarray,anyelement)'"
1480 : : ", 'array_replace(anyarray,anyelement,anyelement)'");
1481 : :
1482 : 0 : appendPQExpBufferStr(&old_polymorphics,
1483 : : ", 'array_position(anyarray,anyelement)'"
1484 : : ", 'array_position(anyarray,anyelement,integer)'"
1485 : : ", 'array_positions(anyarray,anyelement)'"
1486 : : ", 'width_bucket(anyelement,anyarray)'");
1487 : :
1488 : : /*
1489 : : * The query below hardcodes FirstNormalObjectId as 16384 rather than
1490 : : * interpolating that C #define into the query because, if that #define is
1491 : : * ever changed, the cutoff we want to use is the value used by
1492 : : * pre-version 14 servers, not that of some future version.
1493 : : */
1494 : :
1495 : : /* Aggregate transition functions */
1496 : 0 : query = psprintf("SELECT 'aggregate' AS objkind, p.oid::regprocedure::text AS objname "
1497 : : "FROM pg_proc AS p "
1498 : : "JOIN pg_aggregate AS a ON a.aggfnoid=p.oid "
1499 : : "JOIN pg_proc AS transfn ON transfn.oid=a.aggtransfn "
1500 : : "WHERE p.oid >= 16384 "
1501 : : "AND a.aggtransfn = ANY(ARRAY[%s]::regprocedure[]) "
1502 : : "AND a.aggtranstype = ANY(ARRAY['anyarray', 'anyelement']::regtype[]) "
1503 : :
1504 : : /* Aggregate final functions */
1505 : : "UNION ALL "
1506 : : "SELECT 'aggregate' AS objkind, p.oid::regprocedure::text AS objname "
1507 : : "FROM pg_proc AS p "
1508 : : "JOIN pg_aggregate AS a ON a.aggfnoid=p.oid "
1509 : : "JOIN pg_proc AS finalfn ON finalfn.oid=a.aggfinalfn "
1510 : : "WHERE p.oid >= 16384 "
1511 : : "AND a.aggfinalfn = ANY(ARRAY[%s]::regprocedure[]) "
1512 : : "AND a.aggtranstype = ANY(ARRAY['anyarray', 'anyelement']::regtype[]) "
1513 : :
1514 : : /* Operators */
1515 : : "UNION ALL "
1516 : : "SELECT 'operator' AS objkind, op.oid::regoperator::text AS objname "
1517 : : "FROM pg_operator AS op "
1518 : : "WHERE op.oid >= 16384 "
1519 : : "AND oprcode = ANY(ARRAY[%s]::regprocedure[]) "
1520 : : "AND oprleft = ANY(ARRAY['anyarray', 'anyelement']::regtype[])",
1521 : : old_polymorphics.data,
1522 : : old_polymorphics.data,
1523 : : old_polymorphics.data);
1524 : :
1525 : 0 : upgrade_task_add_step(task, query, process_incompat_polymorphics,
1526 : : true, &report);
1527 : 0 : upgrade_task_run(task, cluster);
1528 : 0 : upgrade_task_free(task);
1529 : :
1530 [ # # ]: 0 : if (report.file)
1531 : : {
1532 : 0 : fclose(report.file);
1533 : 0 : pg_log(PG_REPORT, "fatal");
1534 : 0 : pg_fatal("Your installation contains user-defined objects that refer to internal\n"
1535 : : "polymorphic functions with arguments of type \"anyarray\" or \"anyelement\".\n"
1536 : : "These user-defined objects must be dropped before upgrading and restored\n"
1537 : : "afterwards, changing them to refer to the new corresponding functions with\n"
1538 : : "arguments of type \"anycompatiblearray\" and \"anycompatible\".\n"
1539 : : "A list of the problematic objects is in the file:\n"
1540 : : " %s", report.path);
1541 : : }
1542 : : else
1543 : 0 : check_ok();
1544 : :
1545 : 0 : termPQExpBuffer(&old_polymorphics);
1546 : 0 : pfree(query);
1547 : 0 : }
1548 : :
1549 : : /*
1550 : : * Callback function for processing results of query for
1551 : : * check_for_tables_with_oids()'s UpgradeTask. If the query returned any rows
1552 : : * (i.e., the check failed), write the details to the report file.
1553 : : */
1554 : : static void
1555 : 0 : process_with_oids_check(DbInfo *dbinfo, PGresult *res, void *arg)
1556 : : {
1557 : 0 : UpgradeTaskReport *report = (UpgradeTaskReport *) arg;
1558 : 0 : int ntups = PQntuples(res);
1559 : 0 : int i_nspname = PQfnumber(res, "nspname");
1560 : 0 : int i_relname = PQfnumber(res, "relname");
1561 : :
1562 [ # # ]: 0 : if (ntups == 0)
1563 : 0 : return;
1564 : :
1565 [ # # ]: 0 : if (report->file == NULL &&
1566 [ # # ]: 0 : (report->file = fopen_priv(report->path, "w")) == NULL)
1567 : 0 : pg_fatal("could not open file \"%s\": %m", report->path);
1568 : :
1569 : 0 : fprintf(report->file, "In database: %s\n", dbinfo->db_name);
1570 : :
1571 [ # # ]: 0 : for (int rowno = 0; rowno < ntups; rowno++)
1572 : 0 : fprintf(report->file, " %s.%s\n",
1573 : : PQgetvalue(res, rowno, i_nspname),
1574 : : PQgetvalue(res, rowno, i_relname));
1575 : : }
1576 : :
1577 : : /*
1578 : : * Verify that no tables are declared WITH OIDS.
1579 : : */
1580 : : static void
1581 : 0 : check_for_tables_with_oids(ClusterInfo *cluster)
1582 : : {
1583 : : UpgradeTaskReport report;
1584 : 0 : UpgradeTask *task = upgrade_task_create();
1585 : 0 : const char *query = "SELECT n.nspname, c.relname "
1586 : : "FROM pg_catalog.pg_class c, "
1587 : : " pg_catalog.pg_namespace n "
1588 : : "WHERE c.relnamespace = n.oid AND "
1589 : : " c.relhasoids AND"
1590 : : " n.nspname NOT IN ('pg_catalog')";
1591 : :
1592 : 0 : prep_status("Checking for tables WITH OIDS");
1593 : :
1594 : 0 : report.file = NULL;
1595 : 0 : snprintf(report.path, sizeof(report.path), "%s/%s",
1596 : : log_opts.basedir,
1597 : : "tables_with_oids.txt");
1598 : :
1599 : 0 : upgrade_task_add_step(task, query, process_with_oids_check,
1600 : : true, &report);
1601 : 0 : upgrade_task_run(task, cluster);
1602 : 0 : upgrade_task_free(task);
1603 : :
1604 [ # # ]: 0 : if (report.file)
1605 : : {
1606 : 0 : fclose(report.file);
1607 : 0 : pg_log(PG_REPORT, "fatal");
1608 : 0 : pg_fatal("Your installation contains tables declared WITH OIDS, which is not\n"
1609 : : "supported anymore. Consider removing the oid column using\n"
1610 : : " ALTER TABLE ... SET WITHOUT OIDS;\n"
1611 : : "A list of tables with the problem is in the file:\n"
1612 : : " %s", report.path);
1613 : : }
1614 : : else
1615 : 0 : check_ok();
1616 : 0 : }
1617 : :
1618 : : /*
1619 : : * Callback function for processing results of query for
1620 : : * check_for_not_null_inheritance.
1621 : : */
1622 : : static void
1623 : 0 : process_inconsistent_notnull(DbInfo *dbinfo, PGresult *res, void *arg)
1624 : : {
1625 : 0 : UpgradeTaskReport *report = (UpgradeTaskReport *) arg;
1626 : 0 : int ntups = PQntuples(res);
1627 : 0 : int i_nspname = PQfnumber(res, "nspname");
1628 : 0 : int i_relname = PQfnumber(res, "relname");
1629 : 0 : int i_attname = PQfnumber(res, "attname");
1630 : :
1631 [ # # ]: 0 : if (ntups == 0)
1632 : 0 : return;
1633 : :
1634 [ # # ]: 0 : if (report->file == NULL &&
1635 [ # # ]: 0 : (report->file = fopen_priv(report->path, "w")) == NULL)
1636 : 0 : pg_fatal("could not open file \"%s\": %m", report->path);
1637 : :
1638 : 0 : fprintf(report->file, "In database: %s\n", dbinfo->db_name);
1639 : :
1640 [ # # ]: 0 : for (int rowno = 0; rowno < ntups; rowno++)
1641 : : {
1642 : 0 : fprintf(report->file, " %s.%s.%s\n",
1643 : : PQgetvalue(res, rowno, i_nspname),
1644 : : PQgetvalue(res, rowno, i_relname),
1645 : : PQgetvalue(res, rowno, i_attname));
1646 : : }
1647 : : }
1648 : :
1649 : : /*
1650 : : * check_for_not_null_inheritance()
1651 : : *
1652 : : * An attempt to create child tables lacking not-null constraints that are
1653 : : * present in their parents errors out. This can no longer occur since 18,
1654 : : * but previously there were various ways for that to happen. Check that
1655 : : * the cluster to be upgraded doesn't have any of those problems.
1656 : : */
1657 : : static void
1658 : 0 : check_for_not_null_inheritance(ClusterInfo *cluster)
1659 : : {
1660 : : UpgradeTaskReport report;
1661 : : UpgradeTask *task;
1662 : : const char *query;
1663 : :
1664 : 0 : prep_status("Checking for not-null constraint inconsistencies");
1665 : :
1666 : 0 : report.file = NULL;
1667 : 0 : snprintf(report.path, sizeof(report.path), "%s/%s",
1668 : : log_opts.basedir,
1669 : : "not_null_inconsistent_columns.txt");
1670 : :
1671 : 0 : query = "SELECT nspname, cc.relname, ac.attname "
1672 : : "FROM pg_catalog.pg_inherits i, pg_catalog.pg_attribute ac, "
1673 : : " pg_catalog.pg_attribute ap, pg_catalog.pg_class cc, "
1674 : : " pg_catalog.pg_namespace nc "
1675 : : "WHERE cc.oid = ac.attrelid AND i.inhrelid = ac.attrelid "
1676 : : " AND i.inhparent = ap.attrelid AND ac.attname = ap.attname "
1677 : : " AND cc.relnamespace = nc.oid "
1678 : : " AND ap.attnum > 0 and ap.attnotnull AND NOT ac.attnotnull";
1679 : :
1680 : 0 : task = upgrade_task_create();
1681 : 0 : upgrade_task_add_step(task, query,
1682 : : process_inconsistent_notnull,
1683 : : true, &report);
1684 : 0 : upgrade_task_run(task, cluster);
1685 : 0 : upgrade_task_free(task);
1686 : :
1687 [ # # ]: 0 : if (report.file)
1688 : : {
1689 : 0 : fclose(report.file);
1690 : 0 : pg_log(PG_REPORT, "fatal");
1691 : 0 : pg_fatal("Your installation contains inconsistent NOT NULL constraints.\n"
1692 : : "If the parent column(s) are NOT NULL, then the child column must\n"
1693 : : "also be marked NOT NULL, or the upgrade will fail.\n"
1694 : : "You can fix this by running\n"
1695 : : " ALTER TABLE tablename ALTER column SET NOT NULL;\n"
1696 : : "on each column listed in the file:\n"
1697 : : " %s", report.path);
1698 : : }
1699 : : else
1700 : 0 : check_ok();
1701 : 0 : }
1702 : :
1703 : : /*
1704 : : * Callback function for processing results of query for
1705 : : * check_for_gist_inet_ops()'s UpgradeTask. If the query returned any rows
1706 : : * (i.e., the check failed), write the details to the report file.
1707 : : */
1708 : : static void
1709 : 0 : process_gist_inet_ops_check(DbInfo *dbinfo, PGresult *res, void *arg)
1710 : : {
1711 : 0 : UpgradeTaskReport *report = (UpgradeTaskReport *) arg;
1712 : 0 : int ntups = PQntuples(res);
1713 : 0 : int i_nspname = PQfnumber(res, "nspname");
1714 : 0 : int i_relname = PQfnumber(res, "relname");
1715 : :
1716 [ # # ]: 0 : if (ntups == 0)
1717 : 0 : return;
1718 : :
1719 [ # # ]: 0 : if (report->file == NULL &&
1720 [ # # ]: 0 : (report->file = fopen_priv(report->path, "w")) == NULL)
1721 : 0 : pg_fatal("could not open file \"%s\": %m", report->path);
1722 : :
1723 : 0 : fprintf(report->file, "In database: %s\n", dbinfo->db_name);
1724 : :
1725 [ # # ]: 0 : for (int rowno = 0; rowno < ntups; rowno++)
1726 : 0 : fprintf(report->file, " %s.%s\n",
1727 : : PQgetvalue(res, rowno, i_nspname),
1728 : : PQgetvalue(res, rowno, i_relname));
1729 : : }
1730 : :
1731 : : /*
1732 : : * Verify that no indexes use gist_inet_ops/gist_cidr_ops, unless the
1733 : : * opclasses have been changed to not-opcdefault (which would allow
1734 : : * the old server to dump the index definitions with explicit opclasses).
1735 : : */
1736 : : static void
1737 : 0 : check_for_gist_inet_ops(ClusterInfo *cluster)
1738 : : {
1739 : : UpgradeTaskReport report;
1740 : 0 : UpgradeTask *task = upgrade_task_create();
1741 : 0 : const char *query = "SELECT nc.nspname, cc.relname "
1742 : : "FROM pg_catalog.pg_opclass oc, pg_catalog.pg_index i, "
1743 : : " pg_catalog.pg_class cc, pg_catalog.pg_namespace nc "
1744 : : "WHERE oc.opcmethod = " CppAsString2(GIST_AM_OID)
1745 : : " AND oc.opcname IN ('gist_inet_ops', 'gist_cidr_ops')"
1746 : : " AND oc.opcdefault"
1747 : : " AND oc.oid = any(i.indclass)"
1748 : : " AND i.indexrelid = cc.oid AND cc.relnamespace = nc.oid";
1749 : :
1750 : 0 : prep_status("Checking for uses of gist_inet_ops/gist_cidr_ops");
1751 : :
1752 : 0 : report.file = NULL;
1753 : 0 : snprintf(report.path, sizeof(report.path), "%s/%s",
1754 : : log_opts.basedir,
1755 : : "gist_inet_ops.txt");
1756 : :
1757 : 0 : upgrade_task_add_step(task, query, process_gist_inet_ops_check,
1758 : : true, &report);
1759 : 0 : upgrade_task_run(task, cluster);
1760 : 0 : upgrade_task_free(task);
1761 : :
1762 [ # # ]: 0 : if (report.file)
1763 : : {
1764 : 0 : fclose(report.file);
1765 : 0 : pg_log(PG_REPORT, "fatal");
1766 : 0 : pg_fatal("Your installation contains indexes that use btree_gist extension's\n"
1767 : : "gist_inet_ops or gist_cidr_ops operator classes, which cannot be\n"
1768 : : "binary-upgraded. Replace them with indexes that use the built-in GiST\n"
1769 : : "inet_ops operator class.\n"
1770 : : "A list of indexes with the problem is in the file:\n"
1771 : : " %s", report.path);
1772 : : }
1773 : : else
1774 : 0 : check_ok();
1775 : 0 : }
1776 : :
1777 : : /*
1778 : : * Callback function for processing results of query for
1779 : : * check_for_user_defined_encoding_conversions()'s UpgradeTask. If the query
1780 : : * returned any rows (i.e., the check failed), write the details to the report
1781 : : * file.
1782 : : */
1783 : : static void
1784 : 0 : process_user_defined_encoding_conversions(DbInfo *dbinfo, PGresult *res, void *arg)
1785 : : {
1786 : 0 : UpgradeTaskReport *report = (UpgradeTaskReport *) arg;
1787 : 0 : int ntups = PQntuples(res);
1788 : 0 : int i_conoid = PQfnumber(res, "conoid");
1789 : 0 : int i_conname = PQfnumber(res, "conname");
1790 : 0 : int i_nspname = PQfnumber(res, "nspname");
1791 : :
1792 [ # # ]: 0 : if (ntups == 0)
1793 : 0 : return;
1794 : :
1795 [ # # ]: 0 : if (report->file == NULL &&
1796 [ # # ]: 0 : (report->file = fopen_priv(report->path, "w")) == NULL)
1797 : 0 : pg_fatal("could not open file \"%s\": %m", report->path);
1798 : :
1799 : 0 : fprintf(report->file, "In database: %s\n", dbinfo->db_name);
1800 : :
1801 [ # # ]: 0 : for (int rowno = 0; rowno < ntups; rowno++)
1802 : 0 : fprintf(report->file, " (oid=%s) %s.%s\n",
1803 : : PQgetvalue(res, rowno, i_conoid),
1804 : : PQgetvalue(res, rowno, i_nspname),
1805 : : PQgetvalue(res, rowno, i_conname));
1806 : : }
1807 : :
1808 : : /*
1809 : : * Verify that no user-defined encoding conversions exist.
1810 : : */
1811 : : static void
1812 : 0 : check_for_user_defined_encoding_conversions(ClusterInfo *cluster)
1813 : : {
1814 : : UpgradeTaskReport report;
1815 : 0 : UpgradeTask *task = upgrade_task_create();
1816 : : const char *query;
1817 : :
1818 : 0 : prep_status("Checking for user-defined encoding conversions");
1819 : :
1820 : 0 : report.file = NULL;
1821 : 0 : snprintf(report.path, sizeof(report.path), "%s/%s",
1822 : : log_opts.basedir,
1823 : : "encoding_conversions.txt");
1824 : :
1825 : : /*
1826 : : * The query below hardcodes FirstNormalObjectId as 16384 rather than
1827 : : * interpolating that C #define into the query because, if that #define is
1828 : : * ever changed, the cutoff we want to use is the value used by
1829 : : * pre-version 14 servers, not that of some future version.
1830 : : */
1831 : 0 : query = "SELECT c.oid as conoid, c.conname, n.nspname "
1832 : : "FROM pg_catalog.pg_conversion c, "
1833 : : " pg_catalog.pg_namespace n "
1834 : : "WHERE c.connamespace = n.oid AND "
1835 : : " c.oid >= 16384";
1836 : :
1837 : 0 : upgrade_task_add_step(task, query,
1838 : : process_user_defined_encoding_conversions,
1839 : : true, &report);
1840 : 0 : upgrade_task_run(task, cluster);
1841 : 0 : upgrade_task_free(task);
1842 : :
1843 [ # # ]: 0 : if (report.file)
1844 : : {
1845 : 0 : fclose(report.file);
1846 : 0 : pg_log(PG_REPORT, "fatal");
1847 : 0 : pg_fatal("Your installation contains user-defined encoding conversions.\n"
1848 : : "The conversion function parameters changed in PostgreSQL version 14\n"
1849 : : "so this cluster cannot currently be upgraded. You can remove the\n"
1850 : : "encoding conversions in the old cluster and restart the upgrade.\n"
1851 : : "A list of user-defined encoding conversions is in the file:\n"
1852 : : " %s", report.path);
1853 : : }
1854 : : else
1855 : 0 : check_ok();
1856 : 0 : }
1857 : :
1858 : : /*
1859 : : * Callback function for processing results of query for
1860 : : * check_for_unicode_update()'s UpgradeTask. If the query returned any rows
1861 : : * (i.e., the check failed), write the details to the report file.
1862 : : */
1863 : : static void
1864 : 0 : process_unicode_update(DbInfo *dbinfo, PGresult *res, void *arg)
1865 : : {
1866 : 0 : UpgradeTaskReport *report = (UpgradeTaskReport *) arg;
1867 : 0 : int ntups = PQntuples(res);
1868 : 0 : int i_reloid = PQfnumber(res, "reloid");
1869 : 0 : int i_nspname = PQfnumber(res, "nspname");
1870 : 0 : int i_relname = PQfnumber(res, "relname");
1871 : :
1872 [ # # ]: 0 : if (ntups == 0)
1873 : 0 : return;
1874 : :
1875 [ # # ]: 0 : if (report->file == NULL &&
1876 [ # # ]: 0 : (report->file = fopen_priv(report->path, "w")) == NULL)
1877 : 0 : pg_fatal("could not open file \"%s\": %m", report->path);
1878 : :
1879 : 0 : fprintf(report->file, "In database: %s\n", dbinfo->db_name);
1880 : :
1881 [ # # ]: 0 : for (int rowno = 0; rowno < ntups; rowno++)
1882 : 0 : fprintf(report->file, " (oid=%s) %s.%s\n",
1883 : : PQgetvalue(res, rowno, i_reloid),
1884 : : PQgetvalue(res, rowno, i_nspname),
1885 : : PQgetvalue(res, rowno, i_relname));
1886 : : }
1887 : :
1888 : : /*
1889 : : * Check if the Unicode version built into Postgres changed between the old
1890 : : * cluster and the new cluster.
1891 : : */
1892 : : static bool
1893 : 15 : unicode_version_changed(ClusterInfo *cluster)
1894 : : {
1895 : 15 : PGconn *conn_template1 = connectToServer(cluster, "template1");
1896 : : PGresult *res;
1897 : : char *old_unicode_version;
1898 : : bool unicode_updated;
1899 : :
1900 : 15 : res = executeQueryOrDie(conn_template1, "SELECT unicode_version()");
1901 : 15 : old_unicode_version = PQgetvalue(res, 0, 0);
1902 : 15 : unicode_updated = (strcmp(old_unicode_version, PG_UNICODE_VERSION) != 0);
1903 : :
1904 : 15 : PQclear(res);
1905 : 15 : PQfinish(conn_template1);
1906 : :
1907 : 15 : return unicode_updated;
1908 : : }
1909 : :
1910 : : /*
1911 : : * check_for_unicode_update()
1912 : : *
1913 : : * Check if the version of Unicode in the old server and the new server
1914 : : * differ. If so, check for indexes, partitioned tables, or constraints that
1915 : : * use expressions with functions dependent on Unicode behavior.
1916 : : */
1917 : : static void
1918 : 15 : check_for_unicode_update(ClusterInfo *cluster)
1919 : : {
1920 : : UpgradeTaskReport report;
1921 : : UpgradeTask *task;
1922 : : const char *query;
1923 : :
1924 : : /*
1925 : : * The builtin provider did not exist prior to version 17. While there are
1926 : : * still problems that could potentially be caught from earlier versions,
1927 : : * such as an index on NORMALIZE(), we don't check for that here.
1928 : : */
1929 [ - + ]: 15 : if (GET_MAJOR_VERSION(cluster->major_version) < 1700)
1930 : 15 : return;
1931 : :
1932 : 15 : prep_status("Checking for objects affected by Unicode update");
1933 : :
1934 [ + - ]: 15 : if (!unicode_version_changed(cluster))
1935 : : {
1936 : 15 : check_ok();
1937 : 15 : return;
1938 : : }
1939 : :
1940 : 0 : report.file = NULL;
1941 : 0 : snprintf(report.path, sizeof(report.path), "%s/%s",
1942 : : log_opts.basedir,
1943 : : "unicode_dependent_rels.txt");
1944 : :
1945 : 0 : query =
1946 : : /* collations that use built-in Unicode for character semantics */
1947 : : "WITH collations(collid) AS ( "
1948 : : " SELECT oid FROM pg_collation "
1949 : : " WHERE collprovider='b' AND colllocale IN ('C.UTF-8','PG_UNICODE_FAST') "
1950 : : /* include default collation, if appropriate */
1951 : : " UNION "
1952 : : " SELECT 'pg_catalog.default'::regcollation FROM pg_database "
1953 : : " WHERE datname = current_database() AND "
1954 : : " datlocprovider='b' AND datlocale IN ('C.UTF-8','PG_UNICODE_FAST') "
1955 : : "), "
1956 : : /* functions that use built-in Unicode */
1957 : : "functions(procid) AS ( "
1958 : : " SELECT proc.oid FROM pg_proc proc "
1959 : : " WHERE proname IN ('normalize','unicode_assigned','unicode_version','is_normalized') AND "
1960 : : " pronamespace='pg_catalog'::regnamespace "
1961 : : "), "
1962 : : /* operators that use the input collation for character semantics */
1963 : : "coll_operators(operid, procid, collid) AS ( "
1964 : : " SELECT oper.oid, oper.oprcode, collid FROM pg_operator oper, collations "
1965 : : " WHERE oprname IN ('~', '~*', '!~', '!~*', '~~*', '!~~*') AND "
1966 : : " oprnamespace='pg_catalog'::regnamespace AND "
1967 : : " oprright='pg_catalog.text'::pg_catalog.regtype "
1968 : : "), "
1969 : : /* functions that use the input collation for character semantics */
1970 : : "coll_functions(procid, collid) AS ( "
1971 : : " SELECT proc.oid, collid FROM pg_proc proc, collations "
1972 : : " WHERE pronamespace='pg_catalog'::regnamespace AND "
1973 : : " ((proname IN ('lower','initcap','upper','casefold') AND "
1974 : : " pronargs = 1 AND "
1975 : : " proargtypes[0] = 'pg_catalog.text'::pg_catalog.regtype) OR "
1976 : : " (proname = 'substring' AND pronargs = 2 AND "
1977 : : " proargtypes[0] = 'pg_catalog.text'::pg_catalog.regtype AND "
1978 : : " proargtypes[1] = 'pg_catalog.text'::pg_catalog.regtype) OR "
1979 : : " proname LIKE 'regexp_%') "
1980 : : /* include functions behind the operators listed above */
1981 : : " UNION "
1982 : : " SELECT procid, collid FROM coll_operators "
1983 : : "), "
1984 : :
1985 : : /*
1986 : : * Generate patterns to search a pg_node_tree for the above functions and
1987 : : * operators.
1988 : : */
1989 : : "patterns(p) AS ( "
1990 : : " SELECT '{FUNCEXPR :funcid ' || procid::text || '[ }]' FROM functions "
1991 : : " UNION "
1992 : : " SELECT '{OPEXPR :opno ' || operid::text || ' (:\\w+ \\w+ )*' || "
1993 : : " ':inputcollid ' || collid::text || '[ }]' FROM coll_operators "
1994 : : " UNION "
1995 : : " SELECT '{FUNCEXPR :funcid ' || procid::text || ' (:\\w+ \\w+ )*' || "
1996 : : " ':inputcollid ' || collid::text || '[ }]' FROM coll_functions "
1997 : : ") "
1998 : :
1999 : : /*
2000 : : * Match the patterns against expressions used for relation contents.
2001 : : */
2002 : : "SELECT reloid, relkind, nspname, relname "
2003 : : " FROM ( "
2004 : : " SELECT conrelid "
2005 : : " FROM pg_constraint, patterns WHERE conbin::text ~ p "
2006 : : " UNION "
2007 : : " SELECT indexrelid "
2008 : : " FROM pg_index, patterns WHERE indexprs::text ~ p OR indpred::text ~ p "
2009 : : " UNION "
2010 : : " SELECT partrelid "
2011 : : " FROM pg_partitioned_table, patterns WHERE partexprs::text ~ p "
2012 : : " UNION "
2013 : : " SELECT ev_class "
2014 : : " FROM pg_rewrite, pg_class, patterns "
2015 : : " WHERE ev_class = pg_class.oid AND relkind = 'm' AND ev_action::text ~ p"
2016 : : " ) s(reloid), pg_class c, pg_namespace n, pg_database d "
2017 : : " WHERE s.reloid = c.oid AND c.relnamespace = n.oid AND "
2018 : : " d.datname = current_database() AND "
2019 : : " d.encoding = pg_char_to_encoding('UTF8');";
2020 : :
2021 : 0 : task = upgrade_task_create();
2022 : 0 : upgrade_task_add_step(task, query,
2023 : : process_unicode_update,
2024 : : true, &report);
2025 : 0 : upgrade_task_run(task, cluster);
2026 : 0 : upgrade_task_free(task);
2027 : :
2028 [ # # ]: 0 : if (report.file)
2029 : : {
2030 : 0 : fclose(report.file);
2031 : 0 : report_status(PG_WARNING, "warning");
2032 : 0 : pg_log(PG_WARNING, "Your installation contains relations that might be affected by a new version of Unicode.\n"
2033 : : "A list of potentially-affected relations is in the file:\n"
2034 : : " %s", report.path);
2035 : : }
2036 : : else
2037 : 0 : check_ok();
2038 : : }
2039 : :
2040 : : /*
2041 : : * check_new_cluster_replication_slots()
2042 : : *
2043 : : * Validate the new cluster's readiness for migrating replication slots:
2044 : : * - Ensures no existing logical replication slots in the new cluster when
2045 : : * migrating logical slots.
2046 : : * - Ensure conflict detection slot does not exist in the new cluster when
2047 : : * migrating subscriptions with retain_dead_tuples enabled.
2048 : : * - Ensure that the parameter settings in the new cluster necessary for
2049 : : * creating slots are sufficient.
2050 : : */
2051 : : static void
2052 : 14 : check_new_cluster_replication_slots(void)
2053 : : {
2054 : : PGresult *res;
2055 : : PGconn *conn;
2056 : : int nslots_on_old;
2057 : : int nslots_on_new;
2058 : : int rdt_slot_on_new;
2059 : : int max_replication_slots;
2060 : : char *wal_level;
2061 : : int i_nslots_on_new;
2062 : : int i_rdt_slot_on_new;
2063 : :
2064 : : /*
2065 : : * Logical slots can be migrated since PG17 and a physical slot
2066 : : * CONFLICT_DETECTION_SLOT can be migrated since PG19.
2067 : : */
2068 [ - + ]: 14 : if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1600)
2069 : 0 : return;
2070 : :
2071 : 14 : nslots_on_old = count_old_cluster_logical_slots();
2072 : :
2073 : : /*
2074 : : * Quick return if there are no slots to be migrated and no subscriptions
2075 : : * have the retain_dead_tuples option enabled.
2076 : : */
2077 [ + + + + ]: 14 : if (nslots_on_old == 0 && !old_cluster.sub_retain_dead_tuples)
2078 : 10 : return;
2079 : :
2080 : 4 : conn = connectToServer(&new_cluster, "template1");
2081 : :
2082 : 4 : prep_status("Checking new cluster replication slots");
2083 : :
2084 [ + + ]: 4 : res = executeQueryOrDie(conn, "SELECT %s AS nslots_on_new, %s AS rdt_slot_on_new "
2085 : : "FROM pg_catalog.pg_replication_slots",
2086 : : nslots_on_old > 0
2087 : : ? "COUNT(*) FILTER (WHERE slot_type = 'logical' AND temporary IS FALSE)"
2088 : : : "0",
2089 [ + + ]: 4 : old_cluster.sub_retain_dead_tuples
2090 : : ? "COUNT(*) FILTER (WHERE slot_name = 'pg_conflict_detection')"
2091 : : : "0");
2092 : :
2093 [ - + ]: 4 : if (PQntuples(res) != 1)
2094 : 0 : pg_fatal("could not count the number of replication slots");
2095 : :
2096 : 4 : i_nslots_on_new = PQfnumber(res, "nslots_on_new");
2097 : 4 : i_rdt_slot_on_new = PQfnumber(res, "rdt_slot_on_new");
2098 : :
2099 : 4 : nslots_on_new = atoi(PQgetvalue(res, 0, i_nslots_on_new));
2100 : :
2101 [ - + ]: 4 : if (nslots_on_new)
2102 : : {
2103 : : Assert(nslots_on_old);
2104 : 0 : pg_fatal("expected 0 logical replication slots but found %d",
2105 : : nslots_on_new);
2106 : : }
2107 : :
2108 : 4 : rdt_slot_on_new = atoi(PQgetvalue(res, 0, i_rdt_slot_on_new));
2109 : :
2110 [ - + ]: 4 : if (rdt_slot_on_new)
2111 : : {
2112 : : Assert(old_cluster.sub_retain_dead_tuples);
2113 : 0 : pg_fatal("replication slot \"%s\" already exists in the new cluster", "pg_conflict_detection");
2114 : : }
2115 : :
2116 : 4 : PQclear(res);
2117 : :
2118 : 4 : res = executeQueryOrDie(conn, "SELECT setting FROM pg_settings "
2119 : : "WHERE name IN ('wal_level', 'max_replication_slots') "
2120 : : "ORDER BY name DESC;");
2121 : :
2122 [ - + ]: 4 : if (PQntuples(res) != 2)
2123 : 0 : pg_fatal("could not determine parameter settings on new cluster");
2124 : :
2125 : 4 : wal_level = PQgetvalue(res, 0, 0);
2126 : :
2127 [ + + + - ]: 4 : if ((nslots_on_old > 0 || old_cluster.sub_retain_dead_tuples) &&
2128 [ - + ]: 4 : strcmp(wal_level, "minimal") == 0)
2129 : 0 : pg_fatal("\"wal_level\" must be \"replica\" or \"logical\" but is set to \"%s\"",
2130 : : wal_level);
2131 : :
2132 : 4 : max_replication_slots = atoi(PQgetvalue(res, 1, 0));
2133 : :
2134 [ + + ]: 4 : if (old_cluster.sub_retain_dead_tuples &&
2135 [ + + ]: 2 : nslots_on_old + 1 > max_replication_slots)
2136 : 1 : pg_fatal("\"max_replication_slots\" (%d) must be greater than or equal to the number of "
2137 : : "logical replication slots in the old cluster plus one additional slot required "
2138 : : "for retaining conflict detection information (%d)",
2139 : : max_replication_slots, nslots_on_old + 1);
2140 : :
2141 [ + + ]: 3 : if (nslots_on_old > max_replication_slots)
2142 : 1 : pg_fatal("\"max_replication_slots\" (%d) must be greater than or equal to the number of "
2143 : : "logical replication slots (%d) in the old cluster",
2144 : : max_replication_slots, nslots_on_old);
2145 : :
2146 : 2 : PQclear(res);
2147 : 2 : PQfinish(conn);
2148 : :
2149 : 2 : check_ok();
2150 : : }
2151 : :
2152 : : /*
2153 : : * check_new_cluster_subscription_configuration()
2154 : : *
2155 : : * Verify that the max_active_replication_origins configuration specified is
2156 : : * enough for creating the subscriptions. This is required to create the
2157 : : * replication origin for each subscription.
2158 : : */
2159 : : static void
2160 : 12 : check_new_cluster_subscription_configuration(void)
2161 : : {
2162 : : PGresult *res;
2163 : : PGconn *conn;
2164 : : int max_active_replication_origins;
2165 : :
2166 : : /* Subscriptions and their dependencies can be migrated since PG17. */
2167 [ - + ]: 12 : if (GET_MAJOR_VERSION(old_cluster.major_version) < 1700)
2168 : 0 : return;
2169 : :
2170 : : /* Quick return if there are no subscriptions to be migrated. */
2171 [ + + ]: 12 : if (old_cluster.nsubs == 0)
2172 : 10 : return;
2173 : :
2174 : 2 : prep_status("Checking new cluster configuration for subscriptions");
2175 : :
2176 : 2 : conn = connectToServer(&new_cluster, "template1");
2177 : :
2178 : 2 : res = executeQueryOrDie(conn, "SELECT setting FROM pg_settings "
2179 : : "WHERE name = 'max_active_replication_origins';");
2180 : :
2181 [ - + ]: 2 : if (PQntuples(res) != 1)
2182 : 0 : pg_fatal("could not determine parameter settings on new cluster");
2183 : :
2184 : 2 : max_active_replication_origins = atoi(PQgetvalue(res, 0, 0));
2185 [ + + ]: 2 : if (old_cluster.nsubs > max_active_replication_origins)
2186 : 1 : pg_fatal("\"max_active_replication_origins\" (%d) must be greater than or equal to the number of "
2187 : : "subscriptions (%d) in the old cluster",
2188 : : max_active_replication_origins, old_cluster.nsubs);
2189 : :
2190 : 1 : PQclear(res);
2191 : 1 : PQfinish(conn);
2192 : :
2193 : 1 : check_ok();
2194 : : }
2195 : :
2196 : : /*
2197 : : * check_old_cluster_for_valid_slots()
2198 : : *
2199 : : * Verify that all the logical slots are valid and have consumed all the WAL
2200 : : * before shutdown.
2201 : : */
2202 : : static void
2203 : 17 : check_old_cluster_for_valid_slots(void)
2204 : : {
2205 : : char output_path[MAXPGPATH];
2206 : 17 : FILE *script = NULL;
2207 : :
2208 : 17 : prep_status("Checking logical replication slots");
2209 : :
2210 : 17 : snprintf(output_path, sizeof(output_path), "%s/%s",
2211 : : log_opts.basedir,
2212 : : "invalid_logical_slots.txt");
2213 : :
2214 [ + + ]: 69 : for (int dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
2215 : : {
2216 : 52 : LogicalSlotInfoArr *slot_arr = &old_cluster.dbarr.dbs[dbnum].slot_arr;
2217 : :
2218 [ + + ]: 59 : for (int slotnum = 0; slotnum < slot_arr->nslots; slotnum++)
2219 : : {
2220 : 7 : LogicalSlotInfo *slot = &slot_arr->slots[slotnum];
2221 : :
2222 : : /* Is the slot usable? */
2223 [ - + ]: 7 : if (slot->invalid)
2224 : : {
2225 [ # # # # ]: 0 : if (script == NULL &&
2226 : 0 : (script = fopen_priv(output_path, "w")) == NULL)
2227 : 0 : pg_fatal("could not open file \"%s\": %m", output_path);
2228 : :
2229 : 0 : fprintf(script, "The slot \"%s\" is invalid\n",
2230 : : slot->slotname);
2231 : :
2232 : 0 : continue;
2233 : : }
2234 : :
2235 : : /*
2236 : : * Do additional check to ensure that all logical replication
2237 : : * slots have consumed all the WAL before shutdown.
2238 : : *
2239 : : * Note: This can be satisfied only when the old cluster has been
2240 : : * shut down, so we skip this for live checks.
2241 : : */
2242 [ + - + + ]: 7 : if (!user_opts.live_check && !slot->caught_up)
2243 : : {
2244 [ + + - + ]: 3 : if (script == NULL &&
2245 : 1 : (script = fopen_priv(output_path, "w")) == NULL)
2246 : 0 : pg_fatal("could not open file \"%s\": %m", output_path);
2247 : :
2248 : 2 : fprintf(script,
2249 : : "The slot \"%s\" has not consumed the WAL yet\n",
2250 : : slot->slotname);
2251 : : }
2252 : :
2253 : : /*
2254 : : * The name "pg_conflict_detection" (defined as
2255 : : * CONFLICT_DETECTION_SLOT) has been reserved for logical
2256 : : * replication conflict detection slot since PG19.
2257 : : */
2258 [ - + ]: 7 : if (strcmp(slot->slotname, "pg_conflict_detection") == 0)
2259 : : {
2260 [ # # # # ]: 0 : if (script == NULL &&
2261 : 0 : (script = fopen_priv(output_path, "w")) == NULL)
2262 : 0 : pg_fatal("could not open file \"%s\": %m", output_path);
2263 : :
2264 : 0 : fprintf(script,
2265 : : "The slot name \"%s\" is reserved\n",
2266 : : slot->slotname);
2267 : : }
2268 : : }
2269 : : }
2270 : :
2271 [ + + ]: 17 : if (script)
2272 : : {
2273 : 1 : fclose(script);
2274 : :
2275 : 1 : pg_log(PG_REPORT, "fatal");
2276 : 1 : pg_fatal("Your installation contains logical replication slots that cannot be upgraded.\n"
2277 : : "You can remove invalid slots and/or consume the pending WAL for other slots,\n"
2278 : : "and then restart the upgrade.\n"
2279 : : "A list of the problematic slots is in the file:\n"
2280 : : " %s", output_path);
2281 : : }
2282 : :
2283 : 16 : check_ok();
2284 : 16 : }
2285 : :
2286 : : /*
2287 : : * Callback function for processing results of query for
2288 : : * check_old_cluster_subscription_state()'s UpgradeTask. If the query returned
2289 : : * any rows (i.e., the check failed), write the details to the report file.
2290 : : */
2291 : : static void
2292 : 50 : process_old_sub_state_check(DbInfo *dbinfo, PGresult *res, void *arg)
2293 : : {
2294 : 50 : UpgradeTaskReport *report = (UpgradeTaskReport *) arg;
2295 : 50 : int ntups = PQntuples(res);
2296 : 50 : int i_srsubstate = PQfnumber(res, "srsubstate");
2297 : 50 : int i_subname = PQfnumber(res, "subname");
2298 : 50 : int i_nspname = PQfnumber(res, "nspname");
2299 : 50 : int i_relname = PQfnumber(res, "relname");
2300 : :
2301 [ + + ]: 50 : if (ntups == 0)
2302 : 49 : return;
2303 : :
2304 [ - + ]: 1 : if (report->file == NULL &&
2305 [ # # ]: 0 : (report->file = fopen_priv(report->path, "w")) == NULL)
2306 : 0 : pg_fatal("could not open file \"%s\": %m", report->path);
2307 : :
2308 [ + + ]: 2 : for (int i = 0; i < ntups; i++)
2309 : 1 : fprintf(report->file, "The table sync state \"%s\" is not allowed for database:\"%s\" subscription:\"%s\" schema:\"%s\" relation:\"%s\"\n",
2310 : : PQgetvalue(res, i, i_srsubstate),
2311 : : dbinfo->db_name,
2312 : : PQgetvalue(res, i, i_subname),
2313 : : PQgetvalue(res, i, i_nspname),
2314 : : PQgetvalue(res, i, i_relname));
2315 : : }
2316 : :
2317 : : /*
2318 : : * check_old_cluster_subscription_state()
2319 : : *
2320 : : * Verify that the replication origin corresponding to each of the
2321 : : * subscriptions are present and each of the subscribed tables is in
2322 : : * 'i' (initialize) or 'r' (ready) state.
2323 : : */
2324 : : static void
2325 : 16 : check_old_cluster_subscription_state(void)
2326 : : {
2327 : 16 : UpgradeTask *task = upgrade_task_create();
2328 : : UpgradeTaskReport report;
2329 : : const char *query;
2330 : : PGresult *res;
2331 : : PGconn *conn;
2332 : : int ntup;
2333 : :
2334 : 16 : prep_status("Checking subscription state");
2335 : :
2336 : 16 : report.file = NULL;
2337 : 16 : snprintf(report.path, sizeof(report.path), "%s/%s",
2338 : : log_opts.basedir,
2339 : : "subs_invalid.txt");
2340 : :
2341 : : /*
2342 : : * Check that all the subscriptions have their respective replication
2343 : : * origin. This check only needs to run once.
2344 : : */
2345 : 16 : conn = connectToServer(&old_cluster, old_cluster.dbarr.dbs[0].db_name);
2346 : 16 : res = executeQueryOrDie(conn,
2347 : : "SELECT d.datname, s.subname "
2348 : : "FROM pg_catalog.pg_subscription s "
2349 : : "LEFT OUTER JOIN pg_catalog.pg_replication_origin o "
2350 : : " ON o.roname = 'pg_' || s.oid "
2351 : : "INNER JOIN pg_catalog.pg_database d "
2352 : : " ON d.oid = s.subdbid "
2353 : : "WHERE o.roname IS NULL;");
2354 : 16 : ntup = PQntuples(res);
2355 [ + + ]: 17 : for (int i = 0; i < ntup; i++)
2356 : : {
2357 [ + - ]: 1 : if (report.file == NULL &&
2358 [ - + ]: 1 : (report.file = fopen_priv(report.path, "w")) == NULL)
2359 : 0 : pg_fatal("could not open file \"%s\": %m", report.path);
2360 : 1 : fprintf(report.file, "The replication origin is missing for database:\"%s\" subscription:\"%s\"\n",
2361 : : PQgetvalue(res, i, 0),
2362 : : PQgetvalue(res, i, 1));
2363 : : }
2364 : 16 : PQclear(res);
2365 : 16 : PQfinish(conn);
2366 : :
2367 : : /*
2368 : : * We don't allow upgrade if there is a risk of dangling slot or origin
2369 : : * corresponding to initial sync after upgrade.
2370 : : *
2371 : : * A slot/origin not created yet refers to the 'i' (initialize) state,
2372 : : * while 'r' (ready) state refers to a slot/origin created previously but
2373 : : * already dropped. These states are supported for pg_upgrade. The other
2374 : : * states listed below are not supported:
2375 : : *
2376 : : * a) SUBREL_STATE_DATASYNC: A relation upgraded while in this state would
2377 : : * retain a replication slot and origin. The sync worker spawned after the
2378 : : * upgrade cannot drop them because the subscription ID used for the slot
2379 : : * and origin name no longer matches.
2380 : : *
2381 : : * b) SUBREL_STATE_SYNCDONE: A relation upgraded while in this state would
2382 : : * retain the replication origin when there is a failure in tablesync
2383 : : * worker immediately after dropping the replication slot in the
2384 : : * publisher.
2385 : : *
2386 : : * c) SUBREL_STATE_FINISHEDCOPY: A tablesync worker spawned to work on a
2387 : : * relation upgraded while in this state would expect an origin ID with
2388 : : * the OID of the subscription used before the upgrade, causing it to
2389 : : * fail.
2390 : : *
2391 : : * d) SUBREL_STATE_SYNCWAIT, SUBREL_STATE_CATCHUP and
2392 : : * SUBREL_STATE_UNKNOWN: These states are not stored in the catalog, so we
2393 : : * need not allow these states.
2394 : : */
2395 : 16 : query = "SELECT r.srsubstate, s.subname, n.nspname, c.relname "
2396 : : "FROM pg_catalog.pg_subscription_rel r "
2397 : : "LEFT JOIN pg_catalog.pg_subscription s"
2398 : : " ON r.srsubid = s.oid "
2399 : : "LEFT JOIN pg_catalog.pg_class c"
2400 : : " ON r.srrelid = c.oid "
2401 : : "LEFT JOIN pg_catalog.pg_namespace n"
2402 : : " ON c.relnamespace = n.oid "
2403 : : "WHERE r.srsubstate NOT IN ('i', 'r') "
2404 : : "ORDER BY s.subname";
2405 : :
2406 : 16 : upgrade_task_add_step(task, query, process_old_sub_state_check,
2407 : : true, &report);
2408 : :
2409 : 16 : upgrade_task_run(task, &old_cluster);
2410 : 16 : upgrade_task_free(task);
2411 : :
2412 [ + + ]: 16 : if (report.file)
2413 : : {
2414 : 1 : fclose(report.file);
2415 : 1 : pg_log(PG_REPORT, "fatal");
2416 : 1 : pg_fatal("Your installation contains subscriptions without origin or having relations not in i (initialize) or r (ready) state.\n"
2417 : : "You can allow the initial sync to finish for all relations and then restart the upgrade.\n"
2418 : : "A list of the problematic subscriptions is in the file:\n"
2419 : : " %s", report.path);
2420 : : }
2421 : : else
2422 : 15 : check_ok();
2423 : 15 : }
2424 : :
2425 : : /*
2426 : : * check_old_cluster_global_names()
2427 : : *
2428 : : * Raise an error if any database, role, or tablespace name contains a newline
2429 : : * or carriage return character. Such names are not allowed in v19 and later.
2430 : : */
2431 : : static void
2432 : 0 : check_old_cluster_global_names(ClusterInfo *cluster)
2433 : : {
2434 : : int i;
2435 : : PGconn *conn_template1;
2436 : : PGresult *res;
2437 : : int ntups;
2438 : 0 : FILE *script = NULL;
2439 : : char output_path[MAXPGPATH];
2440 : 0 : int count = 0;
2441 : :
2442 : 0 : prep_status("Checking names of databases, roles, and tablespaces");
2443 : :
2444 : 0 : snprintf(output_path, sizeof(output_path), "%s/%s",
2445 : : log_opts.basedir,
2446 : : "db_role_tablespace_invalid_names.txt");
2447 : :
2448 : 0 : conn_template1 = connectToServer(cluster, "template1");
2449 : :
2450 : : /*
2451 : : * Get database, user/role and tablespace names from cluster. Can't use
2452 : : * pg_authid because only superusers can view it.
2453 : : */
2454 : 0 : res = executeQueryOrDie(conn_template1,
2455 : : "SELECT datname AS objname, 'database' AS objtype "
2456 : : "FROM pg_catalog.pg_database UNION ALL "
2457 : : "SELECT rolname AS objname, 'role' AS objtype "
2458 : : "FROM pg_catalog.pg_roles UNION ALL "
2459 : : "SELECT spcname AS objname, 'tablespace' AS objtype "
2460 : : "FROM pg_catalog.pg_tablespace ORDER BY 2 ");
2461 : :
2462 : 0 : ntups = PQntuples(res);
2463 [ # # ]: 0 : for (i = 0; i < ntups; i++)
2464 : : {
2465 : 0 : char *objname = PQgetvalue(res, i, 0);
2466 : 0 : char *objtype = PQgetvalue(res, i, 1);
2467 : :
2468 : : /* If name has \n or \r, then report it. */
2469 [ # # ]: 0 : if (strpbrk(objname, "\n\r"))
2470 : : {
2471 [ # # # # ]: 0 : if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
2472 : 0 : pg_fatal("could not open file \"%s\": %m", output_path);
2473 : :
2474 : 0 : fprintf(script, "%d : %s name = \"%s\"\n", ++count, objtype, objname);
2475 : : }
2476 : : }
2477 : :
2478 : 0 : PQclear(res);
2479 : 0 : PQfinish(conn_template1);
2480 : :
2481 [ # # ]: 0 : if (script)
2482 : : {
2483 : 0 : fclose(script);
2484 : 0 : pg_log(PG_REPORT, "fatal");
2485 : 0 : pg_fatal("Your installation contains databases, roles, or tablespaces with names\n"
2486 : : "with invalid characters (newline or carriage return). To fix this,\n"
2487 : : "rename these objects.\n"
2488 : : "A list of all objects with invalid names is in the file:\n"
2489 : : " %s", output_path);
2490 : : }
2491 : : else
2492 : 0 : check_ok();
2493 : 0 : }
|