Fix pg_upgrade to detect non-upgradable anyarray usages.

When we changed some built-in functions to use anycompatiblearray
instead of anyarray, we created a dump/restore hazard for user-defined
operators and aggregates relying on those functions: the user objects
have to be modified to change their signatures similarly.  This causes
pg_upgrade to fail partway through if the source installation contains
such objects.  We generally try to have pg_upgrade detect such hazards
and fail before it does anything exciting, so add logic to detect
this case too.

Back-patch to v14 where the change was made.

Justin Pryzby, reviewed by Andrey Borodin

Discussion: https://postgr.es/m/3383880.QJadu78ljV@vejsadalnx
This commit is contained in:
Tom Lane 2022-07-05 13:06:31 -04:00
parent 8d9f9634ef
commit 09878cdd48
1 changed files with 135 additions and 0 deletions

View File

@ -24,6 +24,7 @@ static void check_proper_datallowconn(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_user_defined_postfix_ops(ClusterInfo *cluster);
static void check_for_incompatible_polymorphics(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);
@ -122,6 +123,13 @@ check_and_dump_old_cluster(bool live_check)
if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1300)
check_for_user_defined_postfix_ops(&old_cluster);
/*
* PG 14 changed polymorphic functions from anyarray to
* anycompatiblearray.
*/
if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1300)
check_for_incompatible_polymorphics(&old_cluster);
/*
* Pre-PG 12 allowed tables to be declared WITH OIDS, which is not
* supported anymore. Verify there are none, iff applicable.
@ -999,6 +1007,133 @@ check_for_user_defined_postfix_ops(ClusterInfo *cluster)
check_ok();
}
/*
* check_for_incompatible_polymorphics()
*
* Make sure nothing is using old polymorphic functions with
* anyarray/anyelement rather than the new anycompatible variants.
*/
static void
check_for_incompatible_polymorphics(ClusterInfo *cluster)
{
PGresult *res;
FILE *script = NULL;
char output_path[MAXPGPATH];
PQExpBufferData old_polymorphics;
prep_status("Checking for incompatible polymorphic functions");
snprintf(output_path, sizeof(output_path), "%s/%s",
log_opts.basedir,
"incompatible_polymorphics.txt");
/* The set of problematic functions varies a bit in different versions */
initPQExpBuffer(&old_polymorphics);
appendPQExpBufferStr(&old_polymorphics,
"'array_append(anyarray,anyelement)'"
", 'array_cat(anyarray,anyarray)'"
", 'array_prepend(anyelement,anyarray)'");
if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
appendPQExpBufferStr(&old_polymorphics,
", 'array_remove(anyarray,anyelement)'"
", 'array_replace(anyarray,anyelement,anyelement)'");
if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
appendPQExpBufferStr(&old_polymorphics,
", 'array_position(anyarray,anyelement)'"
", 'array_position(anyarray,anyelement,integer)'"
", 'array_positions(anyarray,anyelement)'"
", 'width_bucket(anyelement,anyarray)'");
for (int dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
{
bool db_used = false;
DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
PGconn *conn = connectToServer(cluster, active_db->db_name);
int ntups;
int i_objkind,
i_objname;
/*
* The query below hardcodes FirstNormalObjectId as 16384 rather than
* interpolating that C #define into the query because, if that
* #define is ever changed, the cutoff we want to use is the value
* used by pre-version 14 servers, not that of some future version.
*/
res = executeQueryOrDie(conn,
/* Aggregate transition functions */
"SELECT 'aggregate' AS objkind, p.oid::regprocedure::text AS objname "
"FROM pg_proc AS p "
"JOIN pg_aggregate AS a ON a.aggfnoid=p.oid "
"JOIN pg_proc AS transfn ON transfn.oid=a.aggtransfn "
"WHERE p.oid >= 16384 "
"AND a.aggtransfn = ANY(ARRAY[%s]::regprocedure[]) "
/* Aggregate final functions */
"UNION ALL "
"SELECT 'aggregate' AS objkind, p.oid::regprocedure::text AS objname "
"FROM pg_proc AS p "
"JOIN pg_aggregate AS a ON a.aggfnoid=p.oid "
"JOIN pg_proc AS finalfn ON finalfn.oid=a.aggfinalfn "
"WHERE p.oid >= 16384 "
"AND a.aggfinalfn = ANY(ARRAY[%s]::regprocedure[]) "
/* Operators */
"UNION ALL "
"SELECT 'operator' AS objkind, op.oid::regoperator::text AS objname "
"FROM pg_operator AS op "
"WHERE op.oid >= 16384 "
"AND oprcode = ANY(ARRAY[%s]::regprocedure[]);",
old_polymorphics.data,
old_polymorphics.data,
old_polymorphics.data);
ntups = PQntuples(res);
i_objkind = PQfnumber(res, "objkind");
i_objname = PQfnumber(res, "objname");
for (int rowno = 0; rowno < ntups; rowno++)
{
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\n",
PQgetvalue(res, rowno, i_objkind),
PQgetvalue(res, rowno, i_objname));
}
PQclear(res);
PQfinish(conn);
}
if (script)
{
fclose(script);
pg_log(PG_REPORT, "fatal\n");
pg_fatal("Your installation contains user-defined objects that refer to internal\n"
"polymorphic functions with arguments of type 'anyarray' or 'anyelement'.\n"
"These user-defined objects must be dropped before upgrading and restored\n"
"afterwards, changing them to refer to the new corresponding functions with\n"
"arguments of type 'anycompatiblearray' and 'anycompatible'.\n"
"A list of the problematic objects is in the file:\n"
" %s\n\n", output_path);
}
else
check_ok();
termPQExpBuffer(&old_polymorphics);
}
/*
* Verify that no tables are declared WITH OIDS.
*/