diff options
Diffstat (limited to 'contrib/pg_upgrade/check.c')
-rw-r--r-- | contrib/pg_upgrade/check.c | 1016 |
1 files changed, 0 insertions, 1016 deletions
diff --git a/contrib/pg_upgrade/check.c b/contrib/pg_upgrade/check.c deleted file mode 100644 index 6a498c3bd5c..00000000000 --- a/contrib/pg_upgrade/check.c +++ /dev/null @@ -1,1016 +0,0 @@ -/* - * check.c - * - * server checks and output routines - * - * Copyright (c) 2010-2015, PostgreSQL Global Development Group - * contrib/pg_upgrade/check.c - */ - -#include "postgres_fe.h" - -#include "catalog/pg_authid.h" -#include "mb/pg_wchar.h" -#include "pg_upgrade.h" - - -static void check_new_cluster_is_empty(void); -static void check_databases_are_compatible(void); -static void check_locale_and_encoding(DbInfo *olddb, DbInfo *newdb); -static bool equivalent_locale(int category, const char *loca, const char *locb); -static void check_is_install_user(ClusterInfo *cluster); -static void check_for_prepared_transactions(ClusterInfo *cluster); -static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster); -static void check_for_reg_data_type_usage(ClusterInfo *cluster); -static void check_for_jsonb_9_4_usage(ClusterInfo *cluster); -static void get_bin_version(ClusterInfo *cluster); -static char *get_canonical_locale_name(int category, const char *locale); - - -/* - * fix_path_separator - * For non-Windows, just return the argument. - * For Windows convert any forward slash to a backslash - * such as is suitable for arguments to builtin commands - * like RMDIR and DEL. - */ -static char * -fix_path_separator(char *path) -{ -#ifdef WIN32 - - char *result; - char *c; - - result = pg_strdup(path); - - for (c = result; *c != '\0'; c++) - if (*c == '/') - *c = '\\'; - - return result; -#else - - return path; -#endif -} - -void -output_check_banner(bool live_check) -{ - if (user_opts.check && live_check) - { - pg_log(PG_REPORT, "Performing Consistency Checks on Old Live Server\n"); - pg_log(PG_REPORT, "------------------------------------------------\n"); - } - else - { - pg_log(PG_REPORT, "Performing Consistency Checks\n"); - pg_log(PG_REPORT, "-----------------------------\n"); - } -} - - -void -check_and_dump_old_cluster(bool live_check) -{ - /* -- OLD -- */ - - if (!live_check) - start_postmaster(&old_cluster, true); - - get_pg_database_relfilenode(&old_cluster); - - /* Extract a list of databases and tables from the old cluster */ - get_db_and_rel_infos(&old_cluster); - - init_tablespaces(); - - get_loadable_libraries(); - - - /* - * Check for various failure cases - */ - check_is_install_user(&old_cluster); - check_for_prepared_transactions(&old_cluster); - check_for_reg_data_type_usage(&old_cluster); - check_for_isn_and_int8_passing_mismatch(&old_cluster); - if (GET_MAJOR_VERSION(old_cluster.major_version) == 904 && - old_cluster.controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER) - check_for_jsonb_9_4_usage(&old_cluster); - - /* Pre-PG 9.4 had a different 'line' data type internal format */ - if (GET_MAJOR_VERSION(old_cluster.major_version) <= 903) - old_9_3_check_for_line_data_type_usage(&old_cluster); - - /* Pre-PG 9.0 had no large object permissions */ - if (GET_MAJOR_VERSION(old_cluster.major_version) <= 804) - new_9_0_populate_pg_largeobject_metadata(&old_cluster, true); - - /* - * While not a check option, we do this now because this is the only time - * the old server is running. - */ - if (!user_opts.check) - generate_old_dump(); - - if (!live_check) - stop_postmaster(false); -} - - -void -check_new_cluster(void) -{ - get_db_and_rel_infos(&new_cluster); - - check_new_cluster_is_empty(); - check_databases_are_compatible(); - - check_loadable_libraries(); - - if (user_opts.transfer_mode == TRANSFER_MODE_LINK) - check_hard_link(); - - check_is_install_user(&new_cluster); - - check_for_prepared_transactions(&new_cluster); -} - - -void -report_clusters_compatible(void) -{ - if (user_opts.check) - { - pg_log(PG_REPORT, "\n*Clusters are compatible*\n"); - /* stops new cluster */ - stop_postmaster(false); - exit(0); - } - - pg_log(PG_REPORT, "\n" - "If pg_upgrade fails after this point, you must re-initdb the\n" - "new cluster before continuing.\n"); -} - - -void -issue_warnings(void) -{ - /* Create dummy large object permissions for old < PG 9.0? */ - if (GET_MAJOR_VERSION(old_cluster.major_version) <= 804) - { - start_postmaster(&new_cluster, true); - new_9_0_populate_pg_largeobject_metadata(&new_cluster, false); - stop_postmaster(false); - } -} - - -void -output_completion_banner(char *analyze_script_file_name, - char *deletion_script_file_name) -{ - /* Did we copy the free space files? */ - if (GET_MAJOR_VERSION(old_cluster.major_version) >= 804) - pg_log(PG_REPORT, - "Optimizer statistics are not transferred by pg_upgrade so,\n" - "once you start the new server, consider running:\n" - " %s\n\n", analyze_script_file_name); - else - pg_log(PG_REPORT, - "Optimizer statistics and free space information are not transferred\n" - "by pg_upgrade so, once you start the new server, consider running:\n" - " %s\n\n", analyze_script_file_name); - - - if (deletion_script_file_name) - pg_log(PG_REPORT, - "Running this script will delete the old cluster's data files:\n" - " %s\n", - deletion_script_file_name); - else - pg_log(PG_REPORT, - "Could not create a script to delete the old cluster's data\n" - "files because user-defined tablespaces exist in the old cluster\n" - "directory. The old cluster's contents must be deleted manually.\n"); -} - - -void -check_cluster_versions(void) -{ - prep_status("Checking cluster versions"); - - /* get old and new cluster versions */ - old_cluster.major_version = get_major_server_version(&old_cluster); - new_cluster.major_version = get_major_server_version(&new_cluster); - - /* - * We allow upgrades from/to the same major version for alpha/beta - * upgrades - */ - - if (GET_MAJOR_VERSION(old_cluster.major_version) < 804) - pg_fatal("This utility can only upgrade from PostgreSQL version 8.4 and later.\n"); - - /* Only current PG version is supported as a target */ - if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM)) - 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_dump, and - * pg_dump cannot operate on newer database versions, only current and - * older versions. - */ - if (old_cluster.major_version > new_cluster.major_version) - pg_fatal("This utility cannot be used to downgrade to older major PostgreSQL versions.\n"); - - /* get old and new binary versions */ - get_bin_version(&old_cluster); - get_bin_version(&new_cluster); - - /* Ensure binaries match the designated data directories */ - if (GET_MAJOR_VERSION(old_cluster.major_version) != - GET_MAJOR_VERSION(old_cluster.bin_version)) - pg_fatal("Old cluster data and binary directories are from different major versions.\n"); - if (GET_MAJOR_VERSION(new_cluster.major_version) != - GET_MAJOR_VERSION(new_cluster.bin_version)) - pg_fatal("New cluster data and binary directories are from different major versions.\n"); - - check_ok(); -} - - -void -check_cluster_compatibility(bool live_check) -{ - /* get/check pg_control data of servers */ - get_control_data(&old_cluster, live_check); - get_control_data(&new_cluster, false); - check_control_data(&old_cluster.controldata, &new_cluster.controldata); - - /* Is it 9.0 but without tablespace directories? */ - if (GET_MAJOR_VERSION(new_cluster.major_version) == 900 && - new_cluster.controldata.cat_ver < TABLE_SPACE_SUBDIRS_CAT_VER) - 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"); - - /* We read the real port number for PG >= 9.1 */ - if (live_check && GET_MAJOR_VERSION(old_cluster.major_version) < 901 && - old_cluster.port == DEF_PGUPORT) - pg_fatal("When checking a pre-PG 9.1 live old server, " - "you must specify the old server's port number.\n"); - - if (live_check && old_cluster.port == new_cluster.port) - pg_fatal("When checking a live server, " - "the old and new port numbers must be different.\n"); -} - - -/* - * check_locale_and_encoding() - * - * Check that locale and encoding of a database in the old and new clusters - * are compatible. - */ -static void -check_locale_and_encoding(DbInfo *olddb, DbInfo *newdb) -{ - if (olddb->db_encoding != newdb->db_encoding) - pg_fatal("encodings for database \"%s\" do not match: old \"%s\", new \"%s\"\n", - olddb->db_name, - pg_encoding_to_char(olddb->db_encoding), - pg_encoding_to_char(newdb->db_encoding)); - if (!equivalent_locale(LC_COLLATE, olddb->db_collate, newdb->db_collate)) - pg_fatal("lc_collate values for database \"%s\" do not match: old \"%s\", new \"%s\"\n", - olddb->db_name, olddb->db_collate, newdb->db_collate); - if (!equivalent_locale(LC_CTYPE, olddb->db_ctype, newdb->db_ctype)) - pg_fatal("lc_ctype values for database \"%s\" do not match: old \"%s\", new \"%s\"\n", - olddb->db_name, olddb->db_ctype, newdb->db_ctype); -} - -/* - * equivalent_locale() - * - * Best effort locale-name comparison. Return false if we are not 100% sure - * the locales are equivalent. - * - * Note: The encoding parts of the names are ignored. This function is - * currently used to compare locale names stored in pg_database, and - * pg_database contains a separate encoding field. That's compared directly - * in check_locale_and_encoding(). - */ -static bool -equivalent_locale(int category, const char *loca, const char *locb) -{ - const char *chara; - const char *charb; - char *canona; - char *canonb; - int lena; - int lenb; - - /* - * If the names are equal, the locales are equivalent. Checking this - * first avoids calling setlocale() in the common case that the names - * are equal. That's a good thing, if setlocale() is buggy, for example. - */ - if (pg_strcasecmp(loca, locb) == 0) - return true; - - /* - * Not identical. Canonicalize both names, remove the encoding parts, - * and try again. - */ - canona = get_canonical_locale_name(category, loca); - chara = strrchr(canona, '.'); - lena = chara ? (chara - canona) : strlen(canona); - - canonb = get_canonical_locale_name(category, locb); - charb = strrchr(canonb, '.'); - lenb = charb ? (charb - canonb) : strlen(canonb); - - if (lena == lenb && pg_strncasecmp(canona, canonb, lena) == 0) - return true; - - return false; -} - - -static void -check_new_cluster_is_empty(void) -{ - int dbnum; - - for (dbnum = 0; dbnum < new_cluster.dbarr.ndbs; dbnum++) - { - int relnum; - RelInfoArr *rel_arr = &new_cluster.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) - pg_fatal("New cluster database \"%s\" is not empty\n", - new_cluster.dbarr.dbs[dbnum].db_name); - } - } -} - -/* - * Check that every database that already exists in the new cluster is - * compatible with the corresponding database in the old one. - */ -static void -check_databases_are_compatible(void) -{ - int newdbnum; - int olddbnum; - DbInfo *newdbinfo; - DbInfo *olddbinfo; - - for (newdbnum = 0; newdbnum < new_cluster.dbarr.ndbs; newdbnum++) - { - newdbinfo = &new_cluster.dbarr.dbs[newdbnum]; - - /* Find the corresponding database in the old cluster */ - for (olddbnum = 0; olddbnum < old_cluster.dbarr.ndbs; olddbnum++) - { - olddbinfo = &old_cluster.dbarr.dbs[olddbnum]; - if (strcmp(newdbinfo->db_name, olddbinfo->db_name) == 0) - { - check_locale_and_encoding(olddbinfo, newdbinfo); - break; - } - } - } -} - - -/* - * create_script_for_cluster_analyze() - * - * This incrementally generates better optimizer statistics - */ -void -create_script_for_cluster_analyze(char **analyze_script_file_name) -{ - FILE *script = NULL; - char *user_specification = ""; - - prep_status("Creating script to analyze new cluster"); - - if (os_info.user_specified) - user_specification = psprintf("-U \"%s\" ", os_info.user); - - *analyze_script_file_name = psprintf("%sanalyze_new_cluster.%s", - SCRIPT_PREFIX, SCRIPT_EXT); - - if ((script = fopen_priv(*analyze_script_file_name, "w")) == NULL) - pg_fatal("Could not open file \"%s\": %s\n", - *analyze_script_file_name, getErrorText(errno)); - -#ifndef WIN32 - /* add shebang header */ - fprintf(script, "#!/bin/sh\n\n"); -#else - /* suppress command echoing */ - fprintf(script, "@echo off\n"); -#endif - - fprintf(script, "echo %sThis script will generate minimal optimizer statistics rapidly%s\n", - ECHO_QUOTE, ECHO_QUOTE); - fprintf(script, "echo %sso your system is usable, and then gather statistics twice more%s\n", - ECHO_QUOTE, ECHO_QUOTE); - fprintf(script, "echo %swith increasing accuracy. When it is done, your system will%s\n", - ECHO_QUOTE, ECHO_QUOTE); - fprintf(script, "echo %shave the default level of optimizer statistics.%s\n", - ECHO_QUOTE, ECHO_QUOTE); - fprintf(script, "echo%s\n\n", ECHO_BLANK); - - fprintf(script, "echo %sIf you have used ALTER TABLE to modify the statistics target for%s\n", - ECHO_QUOTE, ECHO_QUOTE); - fprintf(script, "echo %sany tables, you might want to remove them and restore them after%s\n", - ECHO_QUOTE, ECHO_QUOTE); - fprintf(script, "echo %srunning this script because they will delay fast statistics generation.%s\n", - ECHO_QUOTE, ECHO_QUOTE); - fprintf(script, "echo%s\n\n", ECHO_BLANK); - - fprintf(script, "echo %sIf you would like default statistics as quickly as possible, cancel%s\n", - ECHO_QUOTE, ECHO_QUOTE); - fprintf(script, "echo %sthis script and run:%s\n", - ECHO_QUOTE, ECHO_QUOTE); - fprintf(script, "echo %s \"%s/vacuumdb\" %s--all %s%s\n", ECHO_QUOTE, - new_cluster.bindir, user_specification, - /* Did we copy the free space files? */ - (GET_MAJOR_VERSION(old_cluster.major_version) >= 804) ? - "--analyze-only" : "--analyze", ECHO_QUOTE); - fprintf(script, "echo%s\n\n", ECHO_BLANK); - - fprintf(script, "\"%s/vacuumdb\" %s--all --analyze-in-stages\n", - new_cluster.bindir, user_specification); - /* Did we copy the free space files? */ - if (GET_MAJOR_VERSION(old_cluster.major_version) < 804) - fprintf(script, "\"%s/vacuumdb\" %s--all\n", new_cluster.bindir, - user_specification); - - fprintf(script, "echo%s\n\n", ECHO_BLANK); - fprintf(script, "echo %sDone%s\n", - ECHO_QUOTE, ECHO_QUOTE); - - fclose(script); - -#ifndef WIN32 - if (chmod(*analyze_script_file_name, S_IRWXU) != 0) - pg_fatal("Could not add execute permission to file \"%s\": %s\n", - *analyze_script_file_name, getErrorText(errno)); -#endif - - if (os_info.user_specified) - pg_free(user_specification); - - check_ok(); -} - - -/* - * create_script_for_old_cluster_deletion() - * - * This is particularly useful for tablespace deletion. - */ -void -create_script_for_old_cluster_deletion(char **deletion_script_file_name) -{ - FILE *script = NULL; - int tblnum; - char old_cluster_pgdata[MAXPGPATH]; - - *deletion_script_file_name = psprintf("%sdelete_old_cluster.%s", - SCRIPT_PREFIX, SCRIPT_EXT); - - /* - * Some users (oddly) create tablespaces inside the cluster data - * directory. We can't create a proper old cluster delete script in that - * case. - */ - strlcpy(old_cluster_pgdata, old_cluster.pgdata, MAXPGPATH); - canonicalize_path(old_cluster_pgdata); - for (tblnum = 0; tblnum < os_info.num_old_tablespaces; tblnum++) - { - char old_tablespace_dir[MAXPGPATH]; - - strlcpy(old_tablespace_dir, os_info.old_tablespaces[tblnum], MAXPGPATH); - canonicalize_path(old_tablespace_dir); - if (path_is_prefix_of_path(old_cluster_pgdata, old_tablespace_dir)) - { - /* Unlink file in case it is left over from a previous run. */ - unlink(*deletion_script_file_name); - pg_free(*deletion_script_file_name); - *deletion_script_file_name = NULL; - return; - } - } - - prep_status("Creating script to delete old cluster"); - - if ((script = fopen_priv(*deletion_script_file_name, "w")) == NULL) - pg_fatal("Could not open file \"%s\": %s\n", - *deletion_script_file_name, getErrorText(errno)); - -#ifndef WIN32 - /* add shebang header */ - fprintf(script, "#!/bin/sh\n\n"); -#endif - - /* delete old cluster's default tablespace */ - fprintf(script, RMDIR_CMD " \"%s\"\n", fix_path_separator(old_cluster.pgdata)); - - /* delete old cluster's alternate tablespaces */ - for (tblnum = 0; tblnum < os_info.num_old_tablespaces; tblnum++) - { - /* - * Do the old cluster's per-database directories share a directory - * with a new version-specific tablespace? - */ - if (strlen(old_cluster.tablespace_suffix) == 0) - { - /* delete per-database directories */ - int dbnum; - - fprintf(script, "\n"); - /* remove PG_VERSION? */ - if (GET_MAJOR_VERSION(old_cluster.major_version) <= 804) - fprintf(script, RM_CMD " %s%cPG_VERSION\n", - fix_path_separator(os_info.old_tablespaces[tblnum]), - PATH_SEPARATOR); - - for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++) - fprintf(script, RMDIR_CMD " \"%s%c%d\"\n", - fix_path_separator(os_info.old_tablespaces[tblnum]), - PATH_SEPARATOR, old_cluster.dbarr.dbs[dbnum].db_oid); - } - else - { - char *suffix_path = pg_strdup(old_cluster.tablespace_suffix); - - /* - * Simply delete the tablespace directory, which might be ".old" - * or a version-specific subdirectory. - */ - fprintf(script, RMDIR_CMD " \"%s%s\"\n", - fix_path_separator(os_info.old_tablespaces[tblnum]), - fix_path_separator(suffix_path)); - pfree(suffix_path); - } - } - - fclose(script); - -#ifndef WIN32 - if (chmod(*deletion_script_file_name, S_IRWXU) != 0) - pg_fatal("Could not add execute permission to file \"%s\": %s\n", - *deletion_script_file_name, getErrorText(errno)); -#endif - - check_ok(); -} - - -/* - * check_is_install_user() - * - * Check we are the install user, and that the new cluster - * has no other users. - */ -static void -check_is_install_user(ClusterInfo *cluster) -{ - PGresult *res; - PGconn *conn = connectToServer(cluster, "template1"); - - prep_status("Checking database user is the install user"); - - /* Can't use pg_authid because only superusers can view it. */ - res = executeQueryOrDie(conn, - "SELECT rolsuper, oid " - "FROM pg_catalog.pg_roles " - "WHERE rolname = current_user"); - - /* - * We only allow the install user in the new cluster (see comment below) - * and we preserve pg_authid.oid, so this must be the install user in - * the old cluster too. - */ - if (PQntuples(res) != 1 || - atooid(PQgetvalue(res, 0, 1)) != BOOTSTRAP_SUPERUSERID) - pg_fatal("database user \"%s\" is not the install user\n", - os_info.user); - - PQclear(res); - - res = executeQueryOrDie(conn, - "SELECT COUNT(*) " - "FROM pg_catalog.pg_roles "); - - if (PQntuples(res) != 1) - pg_fatal("could not determine the number of users\n"); - - /* - * We only allow the install user in the new cluster because other defined - * users might match users defined in the old cluster and generate an - * error during pg_dump restore. - */ - if (cluster == &new_cluster && atooid(PQgetvalue(res, 0, 0)) != 1) - pg_fatal("Only the install user can be defined in the new cluster.\n"); - - PQclear(res); - - PQfinish(conn); - - check_ok(); -} - - -/* - * check_for_prepared_transactions() - * - * Make sure there are no prepared transactions because the storage format - * might have changed. - */ -static void -check_for_prepared_transactions(ClusterInfo *cluster) -{ - PGresult *res; - PGconn *conn = connectToServer(cluster, "template1"); - - prep_status("Checking for prepared transactions"); - - res = executeQueryOrDie(conn, - "SELECT * " - "FROM pg_catalog.pg_prepared_xacts"); - - if (PQntuples(res) != 0) - pg_fatal("The %s cluster contains prepared transactions\n", - CLUSTER_NAME(cluster)); - - PQclear(res); - - PQfinish(conn); - - check_ok(); -} - - -/* - * check_for_isn_and_int8_passing_mismatch() - * - * contrib/isn relies on data type int8, and in 8.4 int8 can now be passed - * by value. The schema dumps the CREATE TYPE PASSEDBYVALUE setting so - * it must match for the old and new servers. - */ -static void -check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster) -{ - int dbnum; - FILE *script = NULL; - bool found = false; - char output_path[MAXPGPATH]; - - prep_status("Checking for contrib/isn with bigint-passing mismatch"); - - if (old_cluster.controldata.float8_pass_by_value == - new_cluster.controldata.float8_pass_by_value) - { - /* no mismatch */ - check_ok(); - return; - } - - snprintf(output_path, sizeof(output_path), - "contrib_isn_and_int8_pass_by_value.txt"); - - for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++) - { - PGresult *res; - bool db_used = false; - int ntups; - int rowno; - int i_nspname, - i_proname; - DbInfo *active_db = &cluster->dbarr.dbs[dbnum]; - PGconn *conn = connectToServer(cluster, active_db->db_name); - - /* Find any functions coming from contrib/isn */ - res = executeQueryOrDie(conn, - "SELECT n.nspname, p.proname " - "FROM pg_catalog.pg_proc p, " - " pg_catalog.pg_namespace n " - "WHERE p.pronamespace = n.oid AND " - " p.probin = '$libdir/isn'"); - - ntups = PQntuples(res); - i_nspname = PQfnumber(res, "nspname"); - i_proname = PQfnumber(res, "proname"); - for (rowno = 0; rowno < ntups; rowno++) - { - found = true; - if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL) - pg_fatal("Could not open file \"%s\": %s\n", - output_path, getErrorText(errno)); - if (!db_used) - { - fprintf(script, "Database: %s\n", active_db->db_name); - db_used = true; - } - fprintf(script, " %s.%s\n", - PQgetvalue(res, rowno, i_nspname), - PQgetvalue(res, rowno, i_proname)); - } - - PQclear(res); - - PQfinish(conn); - } - - if (script) - fclose(script); - - if (found) - { - pg_log(PG_REPORT, "fatal\n"); - pg_fatal("Your installation contains \"contrib/isn\" functions which rely on the\n" - "bigint data type. Your old and new clusters pass bigint values\n" - "differently so this cluster cannot currently be upgraded. You can\n" - "manually upgrade databases that use \"contrib/isn\" facilities and remove\n" - "\"contrib/isn\" from the old cluster and restart the upgrade. A list of\n" - "the problem functions is in the file:\n" - " %s\n\n", output_path); - } - else - check_ok(); -} - - -/* - * check_for_reg_data_type_usage() - * pg_upgrade only preserves these system values: - * pg_class.oid - * pg_type.oid - * pg_enum.oid - * - * Many of the reg* data types reference system catalog info that is - * not preserved, and hence these data types cannot be used in user - * tables upgraded by pg_upgrade. - */ -static void -check_for_reg_data_type_usage(ClusterInfo *cluster) -{ - int dbnum; - FILE *script = NULL; - bool found = false; - char output_path[MAXPGPATH]; - - prep_status("Checking for reg* system OID user data types"); - - snprintf(output_path, sizeof(output_path), "tables_using_reg.txt"); - - for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++) - { - PGresult *res; - bool db_used = false; - int ntups; - int rowno; - int i_nspname, - i_relname, - i_attname; - DbInfo *active_db = &cluster->dbarr.dbs[dbnum]; - PGconn *conn = connectToServer(cluster, active_db->db_name); - - /* - * While several relkinds don't store any data, e.g. views, they can - * be used to define data types of other columns, so we check all - * relkinds. - */ - res = executeQueryOrDie(conn, - "SELECT n.nspname, c.relname, a.attname " - "FROM pg_catalog.pg_class c, " - " pg_catalog.pg_namespace n, " - " pg_catalog.pg_attribute a " - "WHERE c.oid = a.attrelid AND " - " NOT a.attisdropped AND " - " a.atttypid IN ( " - " 'pg_catalog.regproc'::pg_catalog.regtype, " - " 'pg_catalog.regprocedure'::pg_catalog.regtype, " - " 'pg_catalog.regoper'::pg_catalog.regtype, " - " 'pg_catalog.regoperator'::pg_catalog.regtype, " - /* regclass.oid is preserved, so 'regclass' is OK */ - /* regtype.oid is preserved, so 'regtype' is OK */ - " 'pg_catalog.regconfig'::pg_catalog.regtype, " - " 'pg_catalog.regdictionary'::pg_catalog.regtype) AND " - " c.relnamespace = n.oid AND " - " n.nspname NOT IN ('pg_catalog', 'information_schema')"); - - ntups = PQntuples(res); - i_nspname = PQfnumber(res, "nspname"); - i_relname = PQfnumber(res, "relname"); - i_attname = PQfnumber(res, "attname"); - for (rowno = 0; rowno < ntups; rowno++) - { - found = true; - if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL) - pg_fatal("Could not open file \"%s\": %s\n", - output_path, getErrorText(errno)); - if (!db_used) - { - fprintf(script, "Database: %s\n", active_db->db_name); - db_used = true; - } - fprintf(script, " %s.%s.%s\n", - PQgetvalue(res, rowno, i_nspname), - PQgetvalue(res, rowno, i_relname), - PQgetvalue(res, rowno, i_attname)); - } - - PQclear(res); - - PQfinish(conn); - } - - if (script) - fclose(script); - - if (found) - { - pg_log(PG_REPORT, "fatal\n"); - pg_fatal("Your installation contains one of the reg* data types in user tables.\n" - "These data types reference system OIDs that are not preserved by\n" - "pg_upgrade, so this cluster cannot currently be upgraded. You can\n" - "remove the problem tables and restart the upgrade. A list of the problem\n" - "columns is in the file:\n" - " %s\n\n", output_path); - } - else - check_ok(); -} - - -/* - * check_for_jsonb_9_4_usage() - * - * JSONB changed its storage format during 9.4 beta, so check for it. - */ -static void -check_for_jsonb_9_4_usage(ClusterInfo *cluster) -{ - int dbnum; - FILE *script = NULL; - bool found = false; - char output_path[MAXPGPATH]; - - prep_status("Checking for JSONB user data types"); - - snprintf(output_path, sizeof(output_path), "tables_using_jsonb.txt"); - - for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++) - { - PGresult *res; - bool db_used = false; - int ntups; - int rowno; - int i_nspname, - i_relname, - i_attname; - DbInfo *active_db = &cluster->dbarr.dbs[dbnum]; - PGconn *conn = connectToServer(cluster, active_db->db_name); - - /* - * While several relkinds don't store any data, e.g. views, they can - * be used to define data types of other columns, so we check all - * relkinds. - */ - res = executeQueryOrDie(conn, - "SELECT n.nspname, c.relname, a.attname " - "FROM pg_catalog.pg_class c, " - " pg_catalog.pg_namespace n, " - " pg_catalog.pg_attribute a " - "WHERE c.oid = a.attrelid AND " - " NOT a.attisdropped AND " - " a.atttypid = 'pg_catalog.jsonb'::pg_catalog.regtype AND " - " c.relnamespace = n.oid AND " - /* exclude possible orphaned temp tables */ - " n.nspname !~ '^pg_temp_' AND " - " n.nspname NOT IN ('pg_catalog', 'information_schema')"); - - ntups = PQntuples(res); - i_nspname = PQfnumber(res, "nspname"); - i_relname = PQfnumber(res, "relname"); - i_attname = PQfnumber(res, "attname"); - for (rowno = 0; rowno < ntups; rowno++) - { - found = true; - if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL) - pg_fatal("Could not open file \"%s\": %s\n", - output_path, getErrorText(errno)); - if (!db_used) - { - fprintf(script, "Database: %s\n", active_db->db_name); - db_used = true; - } - fprintf(script, " %s.%s.%s\n", - PQgetvalue(res, rowno, i_nspname), - PQgetvalue(res, rowno, i_relname), - PQgetvalue(res, rowno, i_attname)); - } - - PQclear(res); - - PQfinish(conn); - } - - if (script) - fclose(script); - - if (found) - { - pg_log(PG_REPORT, "fatal\n"); - pg_fatal("Your installation contains one of the JSONB data types in user tables.\n" - "The internal format of JSONB changed during 9.4 beta so this cluster cannot currently\n" - "be upgraded. You can remove the problem tables and restart the upgrade. A list\n" - "of the problem columns is in the file:\n" - " %s\n\n", output_path); - } - else - check_ok(); -} - - -static void -get_bin_version(ClusterInfo *cluster) -{ - char cmd[MAXPGPATH], - cmd_output[MAX_STRING]; - FILE *output; - int pre_dot, - post_dot; - - snprintf(cmd, sizeof(cmd), "\"%s/pg_ctl\" --version", cluster->bindir); - - if ((output = popen(cmd, "r")) == NULL || - fgets(cmd_output, sizeof(cmd_output), output) == NULL) - pg_fatal("Could not get pg_ctl version data using %s: %s\n", - cmd, getErrorText(errno)); - - pclose(output); - - /* Remove trailing newline */ - if (strchr(cmd_output, '\n') != NULL) - *strchr(cmd_output, '\n') = '\0'; - - if (sscanf(cmd_output, "%*s %*s %d.%d", &pre_dot, &post_dot) != 2) - pg_fatal("could not get version from %s\n", cmd); - - cluster->bin_version = (pre_dot * 100 + post_dot) * 100; -} - - -/* - * get_canonical_locale_name - * - * Send the locale name to the system, and hope we get back a canonical - * version. This should match the backend's check_locale() function. - */ -static char * -get_canonical_locale_name(int category, const char *locale) -{ - char *save; - char *res; - - /* get the current setting, so we can restore it. */ - save = setlocale(category, NULL); - if (!save) - pg_fatal("failed to get the current locale\n"); - - /* 'save' may be pointing at a modifiable scratch variable, so copy it. */ - save = pg_strdup(save); - - /* set the locale with setlocale, to see if it accepts it. */ - res = setlocale(category, locale); - - if (!res) - pg_fatal("failed to get system locale name for \"%s\"\n", locale); - - res = pg_strdup(res); - - /* restore old value. */ - if (!setlocale(category, save)) - pg_fatal("failed to restore old locale \"%s\"\n", save); - - pg_free(save); - - return res; -} |