--- a/src/bin/pg_upgrade/check.c +++ b/src/bin/pg_upgrade/check.c @@ -33,6 +33,8 @@ static void check_for_gist_inet_ops(ClusterInfo *cluster); static void check_for_nonstandard_string_settings(ClusterInfo *cluster); static void check_for_uuid_bytea_casts(ClusterInfo *cluster); +static void collect_stored_setting_pairs(ClusterInfo *cluster); +static void check_for_stored_setting_values(void); static void check_for_new_tablespace_dir(void); static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster); static void check_for_unicode_update(ClusterInfo *cluster); @@ -708,6 +710,8 @@ if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800) check_for_uuid_bytea_casts(&old_cluster); + collect_stored_setting_pairs(&old_cluster); + /* * While not a check option, we do this now because this is the only time * the old server is running. @@ -726,6 +730,7 @@ get_db_rel_and_slot_infos(&new_cluster); check_new_cluster_is_empty(); + check_for_stored_setting_values(); check_loadable_libraries(); @@ -2032,6 +2037,199 @@ } else check_ok(); +} + +typedef struct +{ + char *object; + char *name; + char *value; +} StoredSettingPair; + +static StoredSettingPair *stored_setting_pairs = NULL; +static int n_stored_setting_pairs = 0; + +static void +add_stored_setting_pair(const char *object, const char *setting) +{ + const char *eq = strchr(setting, '='); + + if (eq == NULL) + return; + + stored_setting_pairs = pg_realloc(stored_setting_pairs, + (n_stored_setting_pairs + 1) * sizeof(StoredSettingPair)); + stored_setting_pairs[n_stored_setting_pairs].object = pg_strdup(object); + stored_setting_pairs[n_stored_setting_pairs].name = + psprintf("%.*s", (int) (eq - setting), setting); + stored_setting_pairs[n_stored_setting_pairs].value = pg_strdup(eq + 1); + n_stored_setting_pairs++; +} + +static void +process_stored_setting_pairs(DbInfo *dbinfo, PGresult *res, void *arg) +{ + int ntups = PQntuples(res); + int i_proc = PQfnumber(res, "proc"); + int i_setting = PQfnumber(res, "setting"); + + for (int rowno = 0; rowno < ntups; rowno++) + add_stored_setting_pair(psprintf("function %s", + PQgetvalue(res, rowno, i_proc)), + PQgetvalue(res, rowno, i_setting)); +} + +static void +collect_stored_setting_pairs(ClusterInfo *cluster) +{ + UpgradeTask *task; + PGconn *conn; + PGresult *res; + + conn = connectToServer(cluster, "template1"); + res = executeQueryOrDie(conn, + "SELECT d.datname, r.rolname, s.setting " + "FROM (SELECT setdatabase, setrole, " + " pg_catalog.unnest(setconfig) AS setting " + " FROM pg_catalog.pg_db_role_setting " + " WHERE setdatabase <> 0 OR setrole <> 0) s " + " LEFT JOIN pg_catalog.pg_database d " + " ON d.oid = s.setdatabase " + " LEFT JOIN pg_catalog.pg_roles r " + " ON r.oid = s.setrole"); + + for (int rowno = 0; rowno < PQntuples(res); rowno++) + { + const char *datname = PQgetvalue(res, rowno, 0); + const char *rolname = PQgetvalue(res, rowno, 1); + char *object; + + if (PQgetisnull(res, rowno, 1)) + object = psprintf("database %s", datname); + else if (PQgetisnull(res, rowno, 0)) + object = psprintf("role %s", rolname); + else + object = psprintf("role %s in database %s", rolname, datname); + + add_stored_setting_pair(object, PQgetvalue(res, rowno, 2)); + } + + PQclear(res); + PQfinish(conn); + + task = upgrade_task_create(); + upgrade_task_add_step(task, + "SELECT p.oid::pg_catalog.regprocedure AS proc, p.setting " + "FROM (SELECT oid, pg_catalog.unnest(proconfig) AS setting " + " FROM pg_catalog.pg_proc " + " WHERE proconfig IS NOT NULL) p", + process_stored_setting_pairs, true, NULL); + upgrade_task_run(task, cluster); + upgrade_task_free(task); +} + +static void +check_for_stored_setting_values(void) +{ + PGconn *conn; + PGresult *res; + PQExpBufferData query; + FILE *script = NULL; + char path[MAXPGPATH]; + char **names = NULL; + int n_names = 0; + + if (n_stored_setting_pairs == 0) + return; + + prep_status("Checking for stored settings the new cluster rejects"); + + snprintf(path, sizeof(path), "%s/%s", + log_opts.basedir, "rejected_settings.txt"); + + conn = connectToServer(&new_cluster, "template1"); + + res = executeQueryOrDie(conn, + "SELECT name FROM pg_catalog.pg_settings " + "WHERE context IN ('user', 'superuser')"); + names = pg_malloc(PQntuples(res) * sizeof(char *)); + for (int rowno = 0; rowno < PQntuples(res); rowno++) + names[n_names++] = pg_strdup(PQgetvalue(res, rowno, 0)); + PQclear(res); + + initPQExpBuffer(&query); + + for (int i = 0; i < n_stored_setting_pairs; i++) + { + bool seen = false; + bool known = false; + + for (int j = 0; j < i && !seen; j++) + seen = (strcmp(stored_setting_pairs[j].name, + stored_setting_pairs[i].name) == 0 && + strcmp(stored_setting_pairs[j].value, + stored_setting_pairs[i].value) == 0); + if (seen) + continue; + + if (strcmp(stored_setting_pairs[i].name, + "standard_conforming_strings") == 0) + continue; + + for (int j = 0; j < n_names && !known; j++) + known = (strcmp(names[j], stored_setting_pairs[i].name) == 0); + if (!known) + continue; + + resetPQExpBuffer(&query); + appendPQExpBufferStr(&query, "SELECT pg_catalog.set_config("); + appendStringLiteralConn(&query, stored_setting_pairs[i].name, conn); + appendPQExpBufferStr(&query, ", "); + appendStringLiteralConn(&query, stored_setting_pairs[i].value, conn); + appendPQExpBufferStr(&query, ", true)"); + res = PQexec(conn, query.data); + + if (PQresultStatus(res) != PGRES_TUPLES_OK) + for (int k = 0; k < n_stored_setting_pairs; k++) + { + if (strcmp(stored_setting_pairs[k].name, + stored_setting_pairs[i].name) != 0 || + strcmp(stored_setting_pairs[k].value, + stored_setting_pairs[i].value) != 0) + continue; + if (script == NULL && + (script = fopen_priv(path, "w")) == NULL) + pg_fatal("could not open file \"%s\": %m", path); + fprintf(script, "%s: %s = %s\n", + stored_setting_pairs[k].object, + stored_setting_pairs[i].name, + stored_setting_pairs[i].value); + } + + PQclear(res); + } + + termPQExpBuffer(&query); + PQfinish(conn); + + for (int i = 0; i < n_names; i++) + pg_free(names[i]); + pg_free(names); + + if (script) + { + fclose(script); + pg_log(PG_REPORT, "fatal"); + pg_fatal("Your installation contains roles, databases, or functions that store\n" + "a value the new cluster does not accept for a setting.\n" + "The upgrade would fail restoring them.\n" + "Change those settings to a value the new cluster accepts and restart\n" + "the upgrade.\n" + "A list of the settings is in the file:\n" + " %s", path); + } + else + check_ok(); } /*