@@ -24,6 +24,7 @@ static void check_proper_datallowconn(ClusterInfo *cluster);
2424static void check_for_prepared_transactions (ClusterInfo * cluster );
2525static void check_for_isn_and_int8_passing_mismatch (ClusterInfo * cluster );
2626static void check_for_user_defined_postfix_ops (ClusterInfo * cluster );
27+ static void check_for_incompatible_polymorphics (ClusterInfo * cluster );
2728static void check_for_tables_with_oids (ClusterInfo * cluster );
2829static void check_for_composite_data_type_usage (ClusterInfo * cluster );
2930static 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