@@ -24,6 +24,7 @@ static void check_for_prepared_transactions(ClusterInfo *cluster);
2424static void check_for_isn_and_int8_passing_mismatch (ClusterInfo * cluster );
2525static void check_for_user_defined_postfix_ops (ClusterInfo * cluster );
2626static void check_for_tables_with_oids (ClusterInfo * cluster );
27+ static void check_for_composite_data_type_usage (ClusterInfo * cluster );
2728static void check_for_reg_data_type_usage (ClusterInfo * cluster );
2829static void check_for_jsonb_9_4_usage (ClusterInfo * cluster );
2930static void check_for_pg_role_prefix (ClusterInfo * cluster );
@@ -100,6 +101,7 @@ check_and_dump_old_cluster(bool live_check)
100101 check_is_install_user (& old_cluster );
101102 check_proper_datallowconn (& old_cluster );
102103 check_for_prepared_transactions (& old_cluster );
104+ check_for_composite_data_type_usage (& old_cluster );
103105 check_for_reg_data_type_usage (& old_cluster );
104106 check_for_isn_and_int8_passing_mismatch (& old_cluster );
105107
@@ -1043,6 +1045,63 @@ check_for_tables_with_oids(ClusterInfo *cluster)
10431045}
10441046
10451047
1048+ /*
1049+ * check_for_composite_data_type_usage()
1050+ * Check for system-defined composite types used in user tables.
1051+ *
1052+ * The OIDs of rowtypes of system catalogs and information_schema views
1053+ * can change across major versions; unlike user-defined types, we have
1054+ * no mechanism for forcing them to be the same in the new cluster.
1055+ * Hence, if any user table uses one, that's problematic for pg_upgrade.
1056+ */
1057+ static void
1058+ check_for_composite_data_type_usage (ClusterInfo * cluster )
1059+ {
1060+ bool found ;
1061+ Oid firstUserOid ;
1062+ char output_path [MAXPGPATH ];
1063+ char * base_query ;
1064+
1065+ prep_status ("Checking for system-defined composite types in user tables" );
1066+
1067+ snprintf (output_path , sizeof (output_path ), "tables_using_composite.txt" );
1068+
1069+ /*
1070+ * Look for composite types that were made during initdb *or* belong to
1071+ * information_schema; that's important in case information_schema was
1072+ * dropped and reloaded.
1073+ *
1074+ * The cutoff OID here should match the source cluster's value of
1075+ * FirstNormalObjectId. We hardcode it rather than using that C #define
1076+ * because, if that #define is ever changed, our own version's value is
1077+ * NOT what to use. Eventually we may need a test on the source cluster's
1078+ * version to select the correct value.
1079+ */
1080+ firstUserOid = 16384 ;
1081+
1082+ base_query = psprintf ("SELECT t.oid FROM pg_catalog.pg_type t "
1083+ "LEFT JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid "
1084+ " WHERE typtype = 'c' AND (t.oid < %u OR nspname = 'information_schema')" ,
1085+ firstUserOid );
1086+
1087+ found = check_for_data_types_usage (cluster , base_query , output_path );
1088+
1089+ free (base_query );
1090+
1091+ if (found )
1092+ {
1093+ pg_log (PG_REPORT , "fatal\n" );
1094+ pg_fatal ("Your installation contains system-defined composite type(s) in user tables.\n"
1095+ "These type OIDs are not stable across PostgreSQL versions,\n"
1096+ "so this cluster cannot currently be upgraded. You can\n"
1097+ "drop the problem columns and restart the upgrade.\n"
1098+ "A list of the problem columns is in the file:\n"
1099+ " %s\n\n" , output_path );
1100+ }
1101+ else
1102+ check_ok ();
1103+ }
1104+
10461105/*
10471106 * check_for_reg_data_type_usage()
10481107 * pg_upgrade only preserves these system values:
@@ -1057,88 +1116,36 @@ check_for_tables_with_oids(ClusterInfo *cluster)
10571116static void
10581117check_for_reg_data_type_usage (ClusterInfo * cluster )
10591118{
1060- int dbnum ;
1061- FILE * script = NULL ;
1062- bool found = false;
1119+ bool found ;
10631120 char output_path [MAXPGPATH ];
10641121
10651122 prep_status ("Checking for reg* data types in user tables" );
10661123
10671124 snprintf (output_path , sizeof (output_path ), "tables_using_reg.txt" );
10681125
1069- for (dbnum = 0 ; dbnum < cluster -> dbarr .ndbs ; dbnum ++ )
1070- {
1071- PGresult * res ;
1072- bool db_used = false;
1073- int ntups ;
1074- int rowno ;
1075- int i_nspname ,
1076- i_relname ,
1077- i_attname ;
1078- DbInfo * active_db = & cluster -> dbarr .dbs [dbnum ];
1079- PGconn * conn = connectToServer (cluster , active_db -> db_name );
1080-
1081- /*
1082- * While several relkinds don't store any data, e.g. views, they can
1083- * be used to define data types of other columns, so we check all
1084- * relkinds.
1085- */
1086- res = executeQueryOrDie (conn ,
1087- "SELECT n.nspname, c.relname, a.attname "
1088- "FROM pg_catalog.pg_class c, "
1089- " pg_catalog.pg_namespace n, "
1090- " pg_catalog.pg_attribute a, "
1091- " pg_catalog.pg_type t "
1092- "WHERE c.oid = a.attrelid AND "
1093- " NOT a.attisdropped AND "
1094- " a.atttypid = t.oid AND "
1095- " t.typnamespace = "
1096- " (SELECT oid FROM pg_namespace "
1097- " WHERE nspname = 'pg_catalog') AND"
1098- " t.typname IN ( "
1099- /* regclass.oid is preserved, so 'regclass' is OK */
1100- " 'regcollation', "
1101- " 'regconfig', "
1102- " 'regdictionary', "
1103- " 'regnamespace', "
1104- " 'regoper', "
1105- " 'regoperator', "
1106- " 'regproc', "
1107- " 'regprocedure' "
1108- /* regrole.oid is preserved, so 'regrole' is OK */
1109- /* regtype.oid is preserved, so 'regtype' is OK */
1110- " ) AND "
1111- " c.relnamespace = n.oid AND "
1112- " n.nspname NOT IN ('pg_catalog', 'information_schema')" );
1113-
1114- ntups = PQntuples (res );
1115- i_nspname = PQfnumber (res , "nspname" );
1116- i_relname = PQfnumber (res , "relname" );
1117- i_attname = PQfnumber (res , "attname" );
1118- for (rowno = 0 ; rowno < ntups ; rowno ++ )
1119- {
1120- found = true;
1121- if (script == NULL && (script = fopen_priv (output_path , "w" )) == NULL )
1122- pg_fatal ("could not open file \"%s\": %s\n" ,
1123- output_path , strerror (errno ));
1124- if (!db_used )
1125- {
1126- fprintf (script , "In database: %s\n" , active_db -> db_name );
1127- db_used = true;
1128- }
1129- fprintf (script , " %s.%s.%s\n" ,
1130- PQgetvalue (res , rowno , i_nspname ),
1131- PQgetvalue (res , rowno , i_relname ),
1132- PQgetvalue (res , rowno , i_attname ));
1133- }
1134-
1135- PQclear (res );
1136-
1137- PQfinish (conn );
1138- }
1139-
1140- if (script )
1141- fclose (script );
1126+ /*
1127+ * Note: older servers will not have all of these reg* types, so we have
1128+ * to write the query like this rather than depending on casts to regtype.
1129+ */
1130+ found = check_for_data_types_usage (cluster ,
1131+ "SELECT oid FROM pg_catalog.pg_type t "
1132+ "WHERE t.typnamespace = "
1133+ " (SELECT oid FROM pg_catalog.pg_namespace "
1134+ " WHERE nspname = 'pg_catalog') "
1135+ " AND t.typname IN ( "
1136+ /* pg_class.oid is preserved, so 'regclass' is OK */
1137+ " 'regcollation', "
1138+ " 'regconfig', "
1139+ " 'regdictionary', "
1140+ " 'regnamespace', "
1141+ " 'regoper', "
1142+ " 'regoperator', "
1143+ " 'regproc', "
1144+ " 'regprocedure' "
1145+ /* pg_authid.oid is preserved, so 'regrole' is OK */
1146+ /* pg_type.oid is (mostly) preserved, so 'regtype' is OK */
1147+ " )" ,
1148+ output_path );
11421149
11431150 if (found )
11441151 {
@@ -1163,75 +1170,13 @@ check_for_reg_data_type_usage(ClusterInfo *cluster)
11631170static void
11641171check_for_jsonb_9_4_usage (ClusterInfo * cluster )
11651172{
1166- int dbnum ;
1167- FILE * script = NULL ;
1168- bool found = false;
11691173 char output_path [MAXPGPATH ];
11701174
11711175 prep_status ("Checking for incompatible \"jsonb\" data type" );
11721176
11731177 snprintf (output_path , sizeof (output_path ), "tables_using_jsonb.txt" );
11741178
1175- for (dbnum = 0 ; dbnum < cluster -> dbarr .ndbs ; dbnum ++ )
1176- {
1177- PGresult * res ;
1178- bool db_used = false;
1179- int ntups ;
1180- int rowno ;
1181- int i_nspname ,
1182- i_relname ,
1183- i_attname ;
1184- DbInfo * active_db = & cluster -> dbarr .dbs [dbnum ];
1185- PGconn * conn = connectToServer (cluster , active_db -> db_name );
1186-
1187- /*
1188- * While several relkinds don't store any data, e.g. views, they can
1189- * be used to define data types of other columns, so we check all
1190- * relkinds.
1191- */
1192- res = executeQueryOrDie (conn ,
1193- "SELECT n.nspname, c.relname, a.attname "
1194- "FROM pg_catalog.pg_class c, "
1195- " pg_catalog.pg_namespace n, "
1196- " pg_catalog.pg_attribute a "
1197- "WHERE c.oid = a.attrelid AND "
1198- " NOT a.attisdropped AND "
1199- " a.atttypid = 'pg_catalog.jsonb'::pg_catalog.regtype AND "
1200- " c.relnamespace = n.oid AND "
1201- /* exclude possible orphaned temp tables */
1202- " n.nspname !~ '^pg_temp_' AND "
1203- " n.nspname NOT IN ('pg_catalog', 'information_schema')" );
1204-
1205- ntups = PQntuples (res );
1206- i_nspname = PQfnumber (res , "nspname" );
1207- i_relname = PQfnumber (res , "relname" );
1208- i_attname = PQfnumber (res , "attname" );
1209- for (rowno = 0 ; rowno < ntups ; rowno ++ )
1210- {
1211- found = true;
1212- if (script == NULL && (script = fopen_priv (output_path , "w" )) == NULL )
1213- pg_fatal ("could not open file \"%s\": %s\n" ,
1214- output_path , strerror (errno ));
1215- if (!db_used )
1216- {
1217- fprintf (script , "In database: %s\n" , active_db -> db_name );
1218- db_used = true;
1219- }
1220- fprintf (script , " %s.%s.%s\n" ,
1221- PQgetvalue (res , rowno , i_nspname ),
1222- PQgetvalue (res , rowno , i_relname ),
1223- PQgetvalue (res , rowno , i_attname ));
1224- }
1225-
1226- PQclear (res );
1227-
1228- PQfinish (conn );
1229- }
1230-
1231- if (script )
1232- fclose (script );
1233-
1234- if (found )
1179+ if (check_for_data_type_usage (cluster , "pg_catalog.jsonb" , output_path ))
12351180 {
12361181 pg_log (PG_REPORT , "fatal\n" );
12371182 pg_fatal ("Your installation contains the \"jsonb\" data type in user tables.\n"
0 commit comments