Skip to content

Commit 175e60a

Browse files
committed
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
1 parent 7c1f426 commit 175e60a

File tree

1 file changed

+134
-0
lines changed

1 file changed

+134
-0
lines changed

src/bin/pg_upgrade/check.c

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ static void check_proper_datallowconn(ClusterInfo *cluster);
2323
static void check_for_prepared_transactions(ClusterInfo *cluster);
2424
static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
2525
static void check_for_user_defined_postfix_ops(ClusterInfo *cluster);
26+
static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
2627
static void check_for_tables_with_oids(ClusterInfo *cluster);
2728
static void check_for_composite_data_type_usage(ClusterInfo *cluster);
2829
static void check_for_reg_data_type_usage(ClusterInfo *cluster);
@@ -121,6 +122,13 @@ check_and_dump_old_cluster(bool live_check)
121122
if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1300)
122123
check_for_user_defined_postfix_ops(&old_cluster);
123124

125+
/*
126+
* PG 14 changed polymorphic functions from anyarray to
127+
* anycompatiblearray.
128+
*/
129+
if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1300)
130+
check_for_incompatible_polymorphics(&old_cluster);
131+
124132
/*
125133
* Pre-PG 12 allowed tables to be declared WITH OIDS, which is not
126134
* supported anymore. Verify there are none, iff applicable.
@@ -970,6 +978,132 @@ check_for_user_defined_postfix_ops(ClusterInfo *cluster)
970978
check_ok();
971979
}
972980

981+
/*
982+
* check_for_incompatible_polymorphics()
983+
*
984+
* Make sure nothing is using old polymorphic functions with
985+
* anyarray/anyelement rather than the new anycompatible variants.
986+
*/
987+
static void
988+
check_for_incompatible_polymorphics(ClusterInfo *cluster)
989+
{
990+
PGresult *res;
991+
FILE *script = NULL;
992+
char output_path[MAXPGPATH];
993+
PQExpBufferData old_polymorphics;
994+
995+
prep_status("Checking for incompatible polymorphic functions");
996+
997+
snprintf(output_path, sizeof(output_path),
998+
"incompatible_polymorphics.txt");
999+
1000+
/* The set of problematic functions varies a bit in different versions */
1001+
initPQExpBuffer(&old_polymorphics);
1002+
1003+
appendPQExpBufferStr(&old_polymorphics,
1004+
"'array_append(anyarray,anyelement)'"
1005+
", 'array_cat(anyarray,anyarray)'"
1006+
", 'array_prepend(anyelement,anyarray)'");
1007+
1008+
if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
1009+
appendPQExpBufferStr(&old_polymorphics,
1010+
", 'array_remove(anyarray,anyelement)'"
1011+
", 'array_replace(anyarray,anyelement,anyelement)'");
1012+
1013+
if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
1014+
appendPQExpBufferStr(&old_polymorphics,
1015+
", 'array_position(anyarray,anyelement)'"
1016+
", 'array_position(anyarray,anyelement,integer)'"
1017+
", 'array_positions(anyarray,anyelement)'"
1018+
", 'width_bucket(anyelement,anyarray)'");
1019+
1020+
for (int dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
1021+
{
1022+
bool db_used = false;
1023+
DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
1024+
PGconn *conn = connectToServer(cluster, active_db->db_name);
1025+
int ntups;
1026+
int i_objkind,
1027+
i_objname;
1028+
1029+
/*
1030+
* The query below hardcodes FirstNormalObjectId as 16384 rather than
1031+
* interpolating that C #define into the query because, if that
1032+
* #define is ever changed, the cutoff we want to use is the value
1033+
* used by pre-version 14 servers, not that of some future version.
1034+
*/
1035+
res = executeQueryOrDie(conn,
1036+
/* Aggregate transition functions */
1037+
"SELECT 'aggregate' AS objkind, p.oid::regprocedure::text AS objname "
1038+
"FROM pg_proc AS p "
1039+
"JOIN pg_aggregate AS a ON a.aggfnoid=p.oid "
1040+
"JOIN pg_proc AS transfn ON transfn.oid=a.aggtransfn "
1041+
"WHERE p.oid >= 16384 "
1042+
"AND a.aggtransfn = ANY(ARRAY[%s]::regprocedure[]) "
1043+
1044+
/* Aggregate final functions */
1045+
"UNION ALL "
1046+
"SELECT 'aggregate' AS objkind, p.oid::regprocedure::text AS objname "
1047+
"FROM pg_proc AS p "
1048+
"JOIN pg_aggregate AS a ON a.aggfnoid=p.oid "
1049+
"JOIN pg_proc AS finalfn ON finalfn.oid=a.aggfinalfn "
1050+
"WHERE p.oid >= 16384 "
1051+
"AND a.aggfinalfn = ANY(ARRAY[%s]::regprocedure[]) "
1052+
1053+
/* Operators */
1054+
"UNION ALL "
1055+
"SELECT 'operator' AS objkind, op.oid::regoperator::text AS objname "
1056+
"FROM pg_operator AS op "
1057+
"WHERE op.oid >= 16384 "
1058+
"AND oprcode = ANY(ARRAY[%s]::regprocedure[]);",
1059+
old_polymorphics.data,
1060+
old_polymorphics.data,
1061+
old_polymorphics.data);
1062+
1063+
ntups = PQntuples(res);
1064+
1065+
i_objkind = PQfnumber(res, "objkind");
1066+
i_objname = PQfnumber(res, "objname");
1067+
1068+
for (int rowno = 0; rowno < ntups; rowno++)
1069+
{
1070+
if (script == NULL &&
1071+
(script = fopen_priv(output_path, "w")) == NULL)
1072+
pg_fatal("could not open file \"%s\": %s\n",
1073+
output_path, strerror(errno));
1074+
if (!db_used)
1075+
{
1076+
fprintf(script, "In database: %s\n", active_db->db_name);
1077+
db_used = true;
1078+
}
1079+
1080+
fprintf(script, " %s: %s\n",
1081+
PQgetvalue(res, rowno, i_objkind),
1082+
PQgetvalue(res, rowno, i_objname));
1083+
}
1084+
1085+
PQclear(res);
1086+
PQfinish(conn);
1087+
}
1088+
1089+
if (script)
1090+
{
1091+
fclose(script);
1092+
pg_log(PG_REPORT, "fatal\n");
1093+
pg_fatal("Your installation contains user-defined objects that refer to internal\n"
1094+
"polymorphic functions with arguments of type 'anyarray' or 'anyelement'.\n"
1095+
"These user-defined objects must be dropped before upgrading and restored\n"
1096+
"afterwards, changing them to refer to the new corresponding functions with\n"
1097+
"arguments of type 'anycompatiblearray' and 'anycompatible'.\n"
1098+
"A list of the problematic objects is in the file:\n"
1099+
" %s\n\n", output_path);
1100+
}
1101+
else
1102+
check_ok();
1103+
1104+
termPQExpBuffer(&old_polymorphics);
1105+
}
1106+
9731107
/*
9741108
* Verify that no tables are declared WITH OIDS.
9751109
*/

0 commit comments

Comments
 (0)