summaryrefslogtreecommitdiff
path: root/contrib/pg_upgrade/check.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/pg_upgrade/check.c')
-rw-r--r--contrib/pg_upgrade/check.c435
1 files changed, 435 insertions, 0 deletions
diff --git a/contrib/pg_upgrade/check.c b/contrib/pg_upgrade/check.c
new file mode 100644
index 00000000000..bdf7fd6318f
--- /dev/null
+++ b/contrib/pg_upgrade/check.c
@@ -0,0 +1,435 @@
+/*
+ * check.c
+ *
+ * server checks and output routines
+ */
+
+#include "pg_upgrade.h"
+
+
+static void set_locale_and_encoding(migratorContext *ctx, Cluster whichCluster);
+static void check_new_db_is_empty(migratorContext *ctx);
+static void check_locale_and_encoding(migratorContext *ctx, ControlData *oldctrl,
+ ControlData *newctrl);
+
+
+void
+output_check_banner(migratorContext *ctx, bool *live_check)
+{
+ if (ctx->check && is_server_running(ctx, ctx->old.pgdata))
+ {
+ *live_check = true;
+ if (ctx->old.port == ctx->new.port)
+ pg_log(ctx, PG_FATAL, "When checking a live server, "
+ "the old and new port numbers must be different.\n");
+ pg_log(ctx, PG_REPORT, "PerForming Consistency Checks on Old Live Server\n");
+ pg_log(ctx, PG_REPORT, "------------------------------------------------\n");
+ }
+ else
+ {
+ pg_log(ctx, PG_REPORT, "Performing Consistency Checks\n");
+ pg_log(ctx, PG_REPORT, "-----------------------------\n");
+ }
+}
+
+
+void
+check_old_cluster(migratorContext *ctx, bool live_check,
+ char **sequence_script_file_name)
+{
+ /* -- OLD -- */
+
+ if (!live_check)
+ start_postmaster(ctx, CLUSTER_OLD, false);
+
+ set_locale_and_encoding(ctx, CLUSTER_OLD);
+
+ get_pg_database_relfilenode(ctx, CLUSTER_OLD);
+
+ /* Extract a list of databases and tables from the old cluster */
+ get_db_and_rel_infos(ctx, &ctx->old.dbarr, CLUSTER_OLD);
+
+ init_tablespaces(ctx);
+
+ get_loadable_libraries(ctx);
+
+
+ /*
+ * Check for various failure cases
+ */
+
+ old_8_3_check_for_isn_and_int8_passing_mismatch(ctx, CLUSTER_OLD);
+
+ /* old = PG 8.3 checks? */
+ if (GET_MAJOR_VERSION(ctx->old.major_version) <= 803)
+ {
+ old_8_3_check_for_name_data_type_usage(ctx, CLUSTER_OLD);
+ old_8_3_check_for_tsquery_usage(ctx, CLUSTER_OLD);
+ if (ctx->check)
+ {
+ old_8_3_rebuild_tsvector_tables(ctx, true, CLUSTER_OLD);
+ old_8_3_invalidate_hash_gin_indexes(ctx, true, CLUSTER_OLD);
+ old_8_3_invalidate_bpchar_pattern_ops_indexes(ctx, true, CLUSTER_OLD);
+ }
+ else
+
+ /*
+ * While we have the old server running, create the script to
+ * properly restore its sequence values but we report this at the
+ * end.
+ */
+ *sequence_script_file_name =
+ old_8_3_create_sequence_script(ctx, CLUSTER_OLD);
+ }
+
+ /* Pre-PG 9.0 had no large object permissions */
+ if (GET_MAJOR_VERSION(ctx->old.major_version) <= 804)
+ new_9_0_populate_pg_largeobject_metadata(ctx, true, CLUSTER_OLD);
+
+ /*
+ * While not a check option, we do this now because this is the only time
+ * the old server is running.
+ */
+ if (!ctx->check)
+ {
+ generate_old_dump(ctx);
+ split_old_dump(ctx);
+ }
+
+ if (!live_check)
+ stop_postmaster(ctx, false, false);
+}
+
+
+void
+check_new_cluster(migratorContext *ctx)
+{
+ set_locale_and_encoding(ctx, CLUSTER_NEW);
+
+ check_new_db_is_empty(ctx);
+
+ check_loadable_libraries(ctx);
+
+ check_locale_and_encoding(ctx, &ctx->old.controldata, &ctx->new.controldata);
+
+ if (ctx->transfer_mode == TRANSFER_MODE_LINK)
+ check_hard_link(ctx);
+}
+
+
+void
+report_clusters_compatible(migratorContext *ctx)
+{
+ if (ctx->check)
+ {
+ pg_log(ctx, PG_REPORT, "\n*Clusters are compatible*\n");
+ /* stops new cluster */
+ stop_postmaster(ctx, false, false);
+ exit_nicely(ctx, false);
+ }
+
+ pg_log(ctx, PG_REPORT, "\n"
+ "| If pg_upgrade fails after this point, you must\n"
+ "| re-initdb the new cluster before continuing.\n"
+ "| You will also need to remove the \".old\" suffix\n"
+ "| from %s/global/pg_control.old.\n", ctx->old.pgdata);
+}
+
+
+void
+issue_warnings(migratorContext *ctx, char *sequence_script_file_name)
+{
+ /* old = PG 8.3 warnings? */
+ if (GET_MAJOR_VERSION(ctx->old.major_version) <= 803)
+ {
+ start_postmaster(ctx, CLUSTER_NEW, true);
+
+ /* restore proper sequence values using file created from old server */
+ if (sequence_script_file_name)
+ {
+ prep_status(ctx, "Adjusting sequences");
+ exec_prog(ctx, true,
+ SYSTEMQUOTE "\"%s/%s\" --set ON_ERROR_STOP=on --port %d "
+ "-f \"%s\" --dbname template1 >> \"%s\"" SYSTEMQUOTE,
+ ctx->new.bindir, ctx->new.psql_exe, ctx->new.port,
+ sequence_script_file_name, ctx->logfile);
+ unlink(sequence_script_file_name);
+ pg_free(sequence_script_file_name);
+ check_ok(ctx);
+ }
+
+ old_8_3_rebuild_tsvector_tables(ctx, false, CLUSTER_NEW);
+ old_8_3_invalidate_hash_gin_indexes(ctx, false, CLUSTER_NEW);
+ old_8_3_invalidate_bpchar_pattern_ops_indexes(ctx, false, CLUSTER_NEW);
+ stop_postmaster(ctx, false, true);
+ }
+
+ /* Create dummy large object permissions for old < PG 9.0? */
+ if (GET_MAJOR_VERSION(ctx->old.major_version) <= 804)
+ {
+ start_postmaster(ctx, CLUSTER_NEW, true);
+ new_9_0_populate_pg_largeobject_metadata(ctx, false, CLUSTER_NEW);
+ stop_postmaster(ctx, false, true);
+ }
+}
+
+
+void
+output_completion_banner(migratorContext *ctx, char *deletion_script_file_name)
+{
+ /* Did we migrate the free space files? */
+ if (GET_MAJOR_VERSION(ctx->old.major_version) >= 804)
+ pg_log(ctx, PG_REPORT,
+ "| Optimizer statistics is not transferred by pg_upgrade\n"
+ "| so consider running:\n"
+ "| \tvacuumdb --all --analyze-only\n"
+ "| on the newly-upgraded cluster.\n\n");
+ else
+ pg_log(ctx, PG_REPORT,
+ "| Optimizer statistics and free space information\n"
+ "| are not transferred by pg_upgrade so consider\n"
+ "| running:\n"
+ "| \tvacuumdb --all --analyze\n"
+ "| on the newly-upgraded cluster.\n\n");
+
+ pg_log(ctx, PG_REPORT,
+ "| Running this script will delete the old cluster's data files:\n"
+ "| \t%s\n",
+ deletion_script_file_name);
+}
+
+
+void
+check_cluster_versions(migratorContext *ctx)
+{
+ /* get old and new cluster versions */
+ ctx->old.major_version = get_major_server_version(ctx, &ctx->old.major_version_str, CLUSTER_OLD);
+ ctx->new.major_version = get_major_server_version(ctx, &ctx->new.major_version_str, CLUSTER_NEW);
+
+ /* We allow migration from/to the same major version for beta upgrades */
+
+ if (GET_MAJOR_VERSION(ctx->old.major_version) < 803)
+ pg_log(ctx, PG_FATAL, "This utility can only upgrade from PostgreSQL version 8.3 and later.\n");
+
+ /* Only current PG version is supported as a target */
+ if (GET_MAJOR_VERSION(ctx->new.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
+ pg_log(ctx, PG_FATAL, "This utility can only upgrade to PostgreSQL version %s.\n",
+ PG_MAJORVERSION);
+
+ /*
+ * We can't allow downgrading because we use the target pg_dumpall, and
+ * pg_dumpall cannot operate on new datbase versions, only older versions.
+ */
+ if (ctx->old.major_version > ctx->new.major_version)
+ pg_log(ctx, PG_FATAL, "This utility cannot be used to downgrade to older major PostgreSQL versions.\n");
+}
+
+
+void
+check_cluster_compatibility(migratorContext *ctx, bool live_check)
+{
+ char libfile[MAXPGPATH];
+ FILE *lib_test;
+
+ /*
+ * Test pg_upgrade_sysoids.so is in the proper place. We cannot copy it
+ * ourselves because install directories are typically root-owned.
+ */
+ snprintf(libfile, sizeof(libfile), "%s/pg_upgrade_sysoids%s", ctx->new.libpath,
+ DLSUFFIX);
+
+ if ((lib_test = fopen(libfile, "r")) == NULL)
+ pg_log(ctx, PG_FATAL,
+ "\npg_upgrade%s must be created and installed in %s\n", DLSUFFIX, libfile);
+ else
+ fclose(lib_test);
+
+ /* get/check pg_control data of servers */
+ get_control_data(ctx, &ctx->old, live_check);
+ get_control_data(ctx, &ctx->new, false);
+ check_control_data(ctx, &ctx->old.controldata, &ctx->new.controldata);
+
+ /* Is it 9.0 but without tablespace directories? */
+ if (GET_MAJOR_VERSION(ctx->new.major_version) == 900 &&
+ ctx->new.controldata.cat_ver < TABLE_SPACE_SUBDIRS)
+ pg_log(ctx, PG_FATAL, "This utility can only upgrade to PostgreSQL version 9.0 after 2010-01-11\n"
+ "because of backend API changes made during development.\n");
+}
+
+
+/*
+ * set_locale_and_encoding()
+ *
+ * query the database to get the template0 locale
+ */
+static void
+set_locale_and_encoding(migratorContext *ctx, Cluster whichCluster)
+{
+ PGconn *conn;
+ PGresult *res;
+ int i_encoding;
+ ControlData *ctrl = (whichCluster == CLUSTER_OLD) ?
+ &ctx->old.controldata : &ctx->new.controldata;
+ int cluster_version = (whichCluster == CLUSTER_OLD) ?
+ ctx->old.major_version : ctx->new.major_version;
+
+ conn = connectToServer(ctx, "template1", whichCluster);
+
+ /* for pg < 80400, we got the values from pg_controldata */
+ if (cluster_version >= 80400)
+ {
+ int i_datcollate;
+ int i_datctype;
+
+ res = executeQueryOrDie(ctx, conn,
+ "SELECT datcollate, datctype "
+ "FROM pg_catalog.pg_database "
+ "WHERE datname = 'template0' ");
+ assert(PQntuples(res) == 1);
+
+ i_datcollate = PQfnumber(res, "datcollate");
+ i_datctype = PQfnumber(res, "datctype");
+
+ ctrl->lc_collate = pg_strdup(ctx, PQgetvalue(res, 0, i_datcollate));
+ ctrl->lc_ctype = pg_strdup(ctx, PQgetvalue(res, 0, i_datctype));
+
+ PQclear(res);
+ }
+
+ res = executeQueryOrDie(ctx, conn,
+ "SELECT pg_catalog.pg_encoding_to_char(encoding) "
+ "FROM pg_catalog.pg_database "
+ "WHERE datname = 'template0' ");
+ assert(PQntuples(res) == 1);
+
+ i_encoding = PQfnumber(res, "pg_encoding_to_char");
+ ctrl->encoding = pg_strdup(ctx, PQgetvalue(res, 0, i_encoding));
+
+ PQclear(res);
+
+ PQfinish(conn);
+}
+
+
+/*
+ * check_locale_and_encoding()
+ *
+ * locale is not in pg_controldata in 8.4 and later so
+ * we probably had to get via a database query.
+ */
+static void
+check_locale_and_encoding(migratorContext *ctx, ControlData *oldctrl,
+ ControlData *newctrl)
+{
+ if (strcmp(oldctrl->lc_collate, newctrl->lc_collate) != 0)
+ pg_log(ctx, PG_FATAL,
+ "old and new cluster lc_collate values do not match\n");
+ if (strcmp(oldctrl->lc_ctype, newctrl->lc_ctype) != 0)
+ pg_log(ctx, PG_FATAL,
+ "old and new cluster lc_ctype values do not match\n");
+ if (strcmp(oldctrl->encoding, newctrl->encoding) != 0)
+ pg_log(ctx, PG_FATAL,
+ "old and new cluster encoding values do not match\n");
+}
+
+
+static void
+check_new_db_is_empty(migratorContext *ctx)
+{
+ int dbnum;
+ bool found = false;
+
+ get_db_and_rel_infos(ctx, &ctx->new.dbarr, CLUSTER_NEW);
+
+ for (dbnum = 0; dbnum < ctx->new.dbarr.ndbs; dbnum++)
+ {
+ int relnum;
+ RelInfoArr *rel_arr = &ctx->new.dbarr.dbs[dbnum].rel_arr;
+
+ for (relnum = 0; relnum < rel_arr->nrels;
+ relnum++)
+ {
+ /* pg_largeobject and its index should be skipped */
+ if (strcmp(rel_arr->rels[relnum].nspname, "pg_catalog") != 0)
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ dbarr_free(&ctx->new.dbarr);
+
+ if (found)
+ pg_log(ctx, PG_FATAL, "New cluster is not empty; exiting\n");
+}
+
+
+/*
+ * create_script_for_old_cluster_deletion()
+ *
+ * This is particularly useful for tablespace deletion.
+ */
+void
+create_script_for_old_cluster_deletion(migratorContext *ctx,
+ char **deletion_script_file_name)
+{
+ FILE *script = NULL;
+ int tblnum;
+
+ *deletion_script_file_name = pg_malloc(ctx, MAXPGPATH);
+
+ prep_status(ctx, "Creating script to delete old cluster");
+
+ snprintf(*deletion_script_file_name, MAXPGPATH, "%s/delete_old_cluster.%s",
+ ctx->output_dir, EXEC_EXT);
+
+ if ((script = fopen(*deletion_script_file_name, "w")) == NULL)
+ pg_log(ctx, PG_FATAL, "Could not create necessary file: %s\n",
+ *deletion_script_file_name);
+
+#ifndef WIN32
+ /* add shebang header */
+ fprintf(script, "#!/bin/sh\n\n");
+#endif
+
+ /* delete old cluster's default tablespace */
+ fprintf(script, RMDIR_CMD " %s\n", ctx->old.pgdata);
+
+ /* delete old cluster's alternate tablespaces */
+ for (tblnum = 0; tblnum < ctx->num_tablespaces; tblnum++)
+ {
+ /*
+ * Do the old cluster's per-database directories share a directory
+ * with a new version-specific tablespace?
+ */
+ if (strlen(ctx->old.tablespace_suffix) == 0)
+ {
+ /* delete per-database directories */
+ int dbnum;
+
+ fprintf(script, "\n");
+ for (dbnum = 0; dbnum < ctx->new.dbarr.ndbs; dbnum++)
+ {
+ fprintf(script, RMDIR_CMD " %s%s/%d\n",
+ ctx->tablespaces[tblnum], ctx->old.tablespace_suffix,
+ ctx->old.dbarr.dbs[dbnum].db_oid);
+ }
+ }
+ else
+ /*
+ * Simply delete the tablespace directory, which might be ".old"
+ * or a version-specific subdirectory.
+ */
+ fprintf(script, RMDIR_CMD " %s%s\n",
+ ctx->tablespaces[tblnum], ctx->old.tablespace_suffix);
+ }
+
+ fclose(script);
+
+ if (chmod(*deletion_script_file_name, S_IRWXU) != 0)
+ pg_log(ctx, PG_FATAL, "Could not add execute permission to file: %s\n",
+ *deletion_script_file_name);
+
+ check_ok(ctx);
+}