mirror of
https://github.com/postgres/postgres.git
synced 2025-04-27 22:56:53 +03:00
Fix some more omissions in pg_upgrade's tests for non-upgradable types.
Commits 29aeda6e4 et al closed up some oversights involving not checking for non-upgradable types within container types, such as arrays and ranges. However, I only looked at version.c, failing to notice that there were substantially-equivalent tests in check.c. (The division of responsibility between those files is less than clear...) In addition, because genbki.pl does not guarantee that auto-generated rowtype OIDs will hold still across versions, we need to consider that the composite type associated with a system catalog or view is non-upgradable. It seems unlikely that someone would have a user column declared that way, but if they did, trying to read it in another PG version would likely draw "no such pg_type OID" failures, thanks to the type OID embedded in composite Datums. To support the composite and reg*-type cases, extend the recursive query that does the search to allow any base query that returns a column of pg_type OIDs, rather than limiting it to exactly one starting type. As before, back-patch to all supported branches. Discussion: https://postgr.es/m/2798740.1619622555@sss.pgh.pa.us
This commit is contained in:
parent
896cedc7d5
commit
bbcfee0e56
@ -23,6 +23,7 @@ static void check_proper_datallowconn(ClusterInfo *cluster);
|
|||||||
static void check_for_prepared_transactions(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_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
|
||||||
static void check_for_tables_with_oids(ClusterInfo *cluster);
|
static void check_for_tables_with_oids(ClusterInfo *cluster);
|
||||||
|
static void check_for_composite_data_type_usage(ClusterInfo *cluster);
|
||||||
static void check_for_reg_data_type_usage(ClusterInfo *cluster);
|
static void check_for_reg_data_type_usage(ClusterInfo *cluster);
|
||||||
static void check_for_jsonb_9_4_usage(ClusterInfo *cluster);
|
static void check_for_jsonb_9_4_usage(ClusterInfo *cluster);
|
||||||
static void check_for_pg_role_prefix(ClusterInfo *cluster);
|
static void check_for_pg_role_prefix(ClusterInfo *cluster);
|
||||||
@ -98,6 +99,7 @@ check_and_dump_old_cluster(bool live_check)
|
|||||||
check_is_install_user(&old_cluster);
|
check_is_install_user(&old_cluster);
|
||||||
check_proper_datallowconn(&old_cluster);
|
check_proper_datallowconn(&old_cluster);
|
||||||
check_for_prepared_transactions(&old_cluster);
|
check_for_prepared_transactions(&old_cluster);
|
||||||
|
check_for_composite_data_type_usage(&old_cluster);
|
||||||
check_for_reg_data_type_usage(&old_cluster);
|
check_for_reg_data_type_usage(&old_cluster);
|
||||||
check_for_isn_and_int8_passing_mismatch(&old_cluster);
|
check_for_isn_and_int8_passing_mismatch(&old_cluster);
|
||||||
|
|
||||||
@ -1000,6 +1002,63 @@ check_for_tables_with_oids(ClusterInfo *cluster)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check_for_composite_data_type_usage()
|
||||||
|
* Check for system-defined composite types used in user tables.
|
||||||
|
*
|
||||||
|
* The OIDs of rowtypes of system catalogs and information_schema views
|
||||||
|
* can change across major versions; unlike user-defined types, we have
|
||||||
|
* no mechanism for forcing them to be the same in the new cluster.
|
||||||
|
* Hence, if any user table uses one, that's problematic for pg_upgrade.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
check_for_composite_data_type_usage(ClusterInfo *cluster)
|
||||||
|
{
|
||||||
|
bool found;
|
||||||
|
Oid firstUserOid;
|
||||||
|
char output_path[MAXPGPATH];
|
||||||
|
char *base_query;
|
||||||
|
|
||||||
|
prep_status("Checking for system-defined composite types in user tables");
|
||||||
|
|
||||||
|
snprintf(output_path, sizeof(output_path), "tables_using_composite.txt");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look for composite types that were made during initdb *or* belong to
|
||||||
|
* information_schema; that's important in case information_schema was
|
||||||
|
* dropped and reloaded.
|
||||||
|
*
|
||||||
|
* The cutoff OID here should match the source cluster's value of
|
||||||
|
* FirstNormalObjectId. We hardcode it rather than using that C #define
|
||||||
|
* because, if that #define is ever changed, our own version's value is
|
||||||
|
* NOT what to use. Eventually we may need a test on the source cluster's
|
||||||
|
* version to select the correct value.
|
||||||
|
*/
|
||||||
|
firstUserOid = 16384;
|
||||||
|
|
||||||
|
base_query = psprintf("SELECT t.oid FROM pg_catalog.pg_type t "
|
||||||
|
"LEFT JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid "
|
||||||
|
" WHERE typtype = 'c' AND (t.oid < %u OR nspname = 'information_schema')",
|
||||||
|
firstUserOid);
|
||||||
|
|
||||||
|
found = check_for_data_types_usage(cluster, base_query, output_path);
|
||||||
|
|
||||||
|
free(base_query);
|
||||||
|
|
||||||
|
if (found)
|
||||||
|
{
|
||||||
|
pg_log(PG_REPORT, "fatal\n");
|
||||||
|
pg_fatal("Your installation contains system-defined composite type(s) in user tables.\n"
|
||||||
|
"These type OIDs are not stable across PostgreSQL versions,\n"
|
||||||
|
"so this cluster cannot currently be upgraded. You can\n"
|
||||||
|
"drop the problem columns and restart the upgrade.\n"
|
||||||
|
"A list of the problem columns is in the file:\n"
|
||||||
|
" %s\n\n", output_path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
check_ok();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* check_for_reg_data_type_usage()
|
* check_for_reg_data_type_usage()
|
||||||
* pg_upgrade only preserves these system values:
|
* pg_upgrade only preserves these system values:
|
||||||
@ -1014,46 +1073,24 @@ check_for_tables_with_oids(ClusterInfo *cluster)
|
|||||||
static void
|
static void
|
||||||
check_for_reg_data_type_usage(ClusterInfo *cluster)
|
check_for_reg_data_type_usage(ClusterInfo *cluster)
|
||||||
{
|
{
|
||||||
int dbnum;
|
bool found;
|
||||||
FILE *script = NULL;
|
|
||||||
bool found = false;
|
|
||||||
char output_path[MAXPGPATH];
|
char output_path[MAXPGPATH];
|
||||||
|
|
||||||
prep_status("Checking for reg* data types in user tables");
|
prep_status("Checking for reg* data types in user tables");
|
||||||
|
|
||||||
snprintf(output_path, sizeof(output_path), "tables_using_reg.txt");
|
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
|
* Note: older servers will not have all of these reg* types, so we have
|
||||||
* be used to define data types of other columns, so we check all
|
* to write the query like this rather than depending on casts to regtype.
|
||||||
* relkinds.
|
|
||||||
*/
|
*/
|
||||||
res = executeQueryOrDie(conn,
|
found = check_for_data_types_usage(cluster,
|
||||||
"SELECT n.nspname, c.relname, a.attname "
|
"SELECT oid FROM pg_catalog.pg_type t "
|
||||||
"FROM pg_catalog.pg_class c, "
|
"WHERE t.typnamespace = "
|
||||||
" pg_catalog.pg_namespace n, "
|
" (SELECT oid FROM pg_catalog.pg_namespace "
|
||||||
" pg_catalog.pg_attribute a, "
|
" WHERE nspname = 'pg_catalog') "
|
||||||
" pg_catalog.pg_type t "
|
" AND t.typname IN ( "
|
||||||
"WHERE c.oid = a.attrelid AND "
|
/* pg_class.oid is preserved, so 'regclass' is OK */
|
||||||
" NOT a.attisdropped AND "
|
|
||||||
" a.atttypid = t.oid AND "
|
|
||||||
" t.typnamespace = "
|
|
||||||
" (SELECT oid FROM pg_namespace "
|
|
||||||
" WHERE nspname = 'pg_catalog') AND"
|
|
||||||
" t.typname IN ( "
|
|
||||||
/* regclass.oid is preserved, so 'regclass' is OK */
|
|
||||||
" 'regcollation', "
|
" 'regcollation', "
|
||||||
" 'regconfig', "
|
" 'regconfig', "
|
||||||
" 'regdictionary', "
|
" 'regdictionary', "
|
||||||
@ -1062,40 +1099,10 @@ check_for_reg_data_type_usage(ClusterInfo *cluster)
|
|||||||
" 'regoperator', "
|
" 'regoperator', "
|
||||||
" 'regproc', "
|
" 'regproc', "
|
||||||
" 'regprocedure' "
|
" 'regprocedure' "
|
||||||
/* regrole.oid is preserved, so 'regrole' is OK */
|
/* pg_authid.oid is preserved, so 'regrole' is OK */
|
||||||
/* regtype.oid is preserved, so 'regtype' is OK */
|
/* pg_type.oid is (mostly) preserved, so 'regtype' is OK */
|
||||||
" ) AND "
|
" )",
|
||||||
" c.relnamespace = n.oid AND "
|
output_path);
|
||||||
" 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, strerror(errno));
|
|
||||||
if (!db_used)
|
|
||||||
{
|
|
||||||
fprintf(script, "In 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)
|
if (found)
|
||||||
{
|
{
|
||||||
@ -1120,75 +1127,13 @@ check_for_reg_data_type_usage(ClusterInfo *cluster)
|
|||||||
static void
|
static void
|
||||||
check_for_jsonb_9_4_usage(ClusterInfo *cluster)
|
check_for_jsonb_9_4_usage(ClusterInfo *cluster)
|
||||||
{
|
{
|
||||||
int dbnum;
|
|
||||||
FILE *script = NULL;
|
|
||||||
bool found = false;
|
|
||||||
char output_path[MAXPGPATH];
|
char output_path[MAXPGPATH];
|
||||||
|
|
||||||
prep_status("Checking for incompatible \"jsonb\" data type");
|
prep_status("Checking for incompatible \"jsonb\" data type");
|
||||||
|
|
||||||
snprintf(output_path, sizeof(output_path), "tables_using_jsonb.txt");
|
snprintf(output_path, sizeof(output_path), "tables_using_jsonb.txt");
|
||||||
|
|
||||||
for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
|
if (check_for_data_type_usage(cluster, "pg_catalog.jsonb", output_path))
|
||||||
{
|
|
||||||
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, strerror(errno));
|
|
||||||
if (!db_used)
|
|
||||||
{
|
|
||||||
fprintf(script, "In 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_log(PG_REPORT, "fatal\n");
|
||||||
pg_fatal("Your installation contains the \"jsonb\" data type in user tables.\n"
|
pg_fatal("Your installation contains the \"jsonb\" data type in user tables.\n"
|
||||||
|
@ -442,6 +442,12 @@ void pg_putenv(const char *var, const char *val);
|
|||||||
|
|
||||||
/* version.c */
|
/* version.c */
|
||||||
|
|
||||||
|
bool check_for_data_types_usage(ClusterInfo *cluster,
|
||||||
|
const char *base_query,
|
||||||
|
const char *output_path);
|
||||||
|
bool check_for_data_type_usage(ClusterInfo *cluster,
|
||||||
|
const char *typename,
|
||||||
|
const char *output_path);
|
||||||
void new_9_0_populate_pg_largeobject_metadata(ClusterInfo *cluster,
|
void new_9_0_populate_pg_largeobject_metadata(ClusterInfo *cluster,
|
||||||
bool check_mode);
|
bool check_mode);
|
||||||
void old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster);
|
void old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster);
|
||||||
|
@ -97,17 +97,22 @@ new_9_0_populate_pg_largeobject_metadata(ClusterInfo *cluster, bool check_mode)
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* check_for_data_type_usage
|
* check_for_data_types_usage()
|
||||||
* Detect whether there are any stored columns depending on the given type
|
* Detect whether there are any stored columns depending on given type(s)
|
||||||
*
|
*
|
||||||
* If so, write a report to the given file name, and return true.
|
* If so, write a report to the given file name, and return true.
|
||||||
*
|
*
|
||||||
* We check for the type in tables, matviews, and indexes, but not views;
|
* base_query should be a SELECT yielding a single column named "oid",
|
||||||
|
* containing the pg_type OIDs of one or more types that are known to have
|
||||||
|
* inconsistent on-disk representations across server versions.
|
||||||
|
*
|
||||||
|
* We check for the type(s) in tables, matviews, and indexes, but not views;
|
||||||
* there's no storage involved in a view.
|
* there's no storage involved in a view.
|
||||||
*/
|
*/
|
||||||
static bool
|
bool
|
||||||
check_for_data_type_usage(ClusterInfo *cluster, const char *typename,
|
check_for_data_types_usage(ClusterInfo *cluster,
|
||||||
char *output_path)
|
const char *base_query,
|
||||||
|
const char *output_path)
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
FILE *script = NULL;
|
FILE *script = NULL;
|
||||||
@ -127,7 +132,7 @@ check_for_data_type_usage(ClusterInfo *cluster, const char *typename,
|
|||||||
i_attname;
|
i_attname;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The type of interest might be wrapped in a domain, array,
|
* The type(s) of interest might be wrapped in a domain, array,
|
||||||
* composite, or range, and these container types can be nested (to
|
* composite, or range, and these container types can be nested (to
|
||||||
* varying extents depending on server version, but that's not of
|
* varying extents depending on server version, but that's not of
|
||||||
* concern here). To handle all these cases we need a recursive CTE.
|
* concern here). To handle all these cases we need a recursive CTE.
|
||||||
@ -135,8 +140,8 @@ check_for_data_type_usage(ClusterInfo *cluster, const char *typename,
|
|||||||
initPQExpBuffer(&querybuf);
|
initPQExpBuffer(&querybuf);
|
||||||
appendPQExpBuffer(&querybuf,
|
appendPQExpBuffer(&querybuf,
|
||||||
"WITH RECURSIVE oids AS ( "
|
"WITH RECURSIVE oids AS ( "
|
||||||
/* the target type itself */
|
/* start with the type(s) returned by base_query */
|
||||||
" SELECT '%s'::pg_catalog.regtype AS oid "
|
" %s "
|
||||||
" UNION ALL "
|
" UNION ALL "
|
||||||
" SELECT * FROM ( "
|
" SELECT * FROM ( "
|
||||||
/* inner WITH because we can only reference the CTE once */
|
/* inner WITH because we can only reference the CTE once */
|
||||||
@ -154,7 +159,7 @@ check_for_data_type_usage(ClusterInfo *cluster, const char *typename,
|
|||||||
" c.oid = a.attrelid AND "
|
" c.oid = a.attrelid AND "
|
||||||
" NOT a.attisdropped AND "
|
" NOT a.attisdropped AND "
|
||||||
" a.atttypid = x.oid ",
|
" a.atttypid = x.oid ",
|
||||||
typename);
|
base_query);
|
||||||
|
|
||||||
/* Ranges were introduced in 9.2 */
|
/* Ranges were introduced in 9.2 */
|
||||||
if (GET_MAJOR_VERSION(cluster->major_version) >= 902)
|
if (GET_MAJOR_VERSION(cluster->major_version) >= 902)
|
||||||
@ -222,6 +227,34 @@ check_for_data_type_usage(ClusterInfo *cluster, const char *typename,
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check_for_data_type_usage()
|
||||||
|
* Detect whether there are any stored columns depending on the given type
|
||||||
|
*
|
||||||
|
* If so, write a report to the given file name, and return true.
|
||||||
|
*
|
||||||
|
* typename should be a fully qualified type name. This is just a
|
||||||
|
* trivial wrapper around check_for_data_types_usage() to convert a
|
||||||
|
* type name into a base query.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
check_for_data_type_usage(ClusterInfo *cluster,
|
||||||
|
const char *typename,
|
||||||
|
const char *output_path)
|
||||||
|
{
|
||||||
|
bool found;
|
||||||
|
char *base_query;
|
||||||
|
|
||||||
|
base_query = psprintf("SELECT '%s'::pg_catalog.regtype AS oid",
|
||||||
|
typename);
|
||||||
|
|
||||||
|
found = check_for_data_types_usage(cluster, base_query, output_path);
|
||||||
|
|
||||||
|
free(base_query);
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* old_9_3_check_for_line_data_type_usage()
|
* old_9_3_check_for_line_data_type_usage()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user