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