Skip to content

Commit 09878cd

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 8d9f963 commit 09878cd

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

src/bin/pg_upgrade/check.c

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ static void check_proper_datallowconn(ClusterInfo *cluster);
2424
static void check_for_prepared_transactions(ClusterInfo *cluster);
2525
static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
2626
static void check_for_user_defined_postfix_ops(ClusterInfo *cluster);
27+
static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
2728
static void check_for_tables_with_oids(ClusterInfo *cluster);
2829
static void check_for_composite_data_type_usage(ClusterInfo *cluster);
2930
static void check_for_reg_data_type_usage(ClusterInfo *cluster);
@@ -122,6 +123,13 @@ check_and_dump_old_cluster(bool live_check)
122123
if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1300)
123124
check_for_user_defined_postfix_ops(&old_cluster);
124125

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

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

0 commit comments

Comments
 (0)