Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit bbcfee0

Browse files
committed
Fix some more omissions in pg_upgrade's tests for non-upgradable types.
Commits 29aeda6 et al closed up some oversights involving not checking for non-upgradable types within container types, such as arrays and ranges. However, I only looked at version.c, failing to notice that there were substantially-equivalent tests in check.c. (The division of responsibility between those files is less than clear...) In addition, because genbki.pl does not guarantee that auto-generated rowtype OIDs will hold still across versions, we need to consider that the composite type associated with a system catalog or view is non-upgradable. It seems unlikely that someone would have a user column declared that way, but if they did, trying to read it in another PG version would likely draw "no such pg_type OID" failures, thanks to the type OID embedded in composite Datums. To support the composite and reg*-type cases, extend the recursive query that does the search to allow any base query that returns a column of pg_type OIDs, rather than limiting it to exactly one starting type. As before, back-patch to all supported branches. Discussion: https://postgr.es/m/2798740.1619622555@sss.pgh.pa.us
1 parent 896cedc commit bbcfee0

File tree

3 files changed

+133
-149
lines changed

3 files changed

+133
-149
lines changed

src/bin/pg_upgrade/check.c

Lines changed: 84 additions & 139 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_tables_with_oids(ClusterInfo *cluster);
26+
static void check_for_composite_data_type_usage(ClusterInfo *cluster);
2627
static void check_for_reg_data_type_usage(ClusterInfo *cluster);
2728
static void check_for_jsonb_9_4_usage(ClusterInfo *cluster);
2829
static void check_for_pg_role_prefix(ClusterInfo *cluster);
@@ -98,6 +99,7 @@ check_and_dump_old_cluster(bool live_check)
9899
check_is_install_user(&old_cluster);
99100
check_proper_datallowconn(&old_cluster);
100101
check_for_prepared_transactions(&old_cluster);
102+
check_for_composite_data_type_usage(&old_cluster);
101103
check_for_reg_data_type_usage(&old_cluster);
102104
check_for_isn_and_int8_passing_mismatch(&old_cluster);
103105

@@ -1000,6 +1002,63 @@ check_for_tables_with_oids(ClusterInfo *cluster)
10001002
}
10011003

10021004

1005+
/*
1006+
* check_for_composite_data_type_usage()
1007+
* Check for system-defined composite types used in user tables.
1008+
*
1009+
* The OIDs of rowtypes of system catalogs and information_schema views
1010+
* can change across major versions; unlike user-defined types, we have
1011+
* no mechanism for forcing them to be the same in the new cluster.
1012+
* Hence, if any user table uses one, that's problematic for pg_upgrade.
1013+
*/
1014+
static void
1015+
check_for_composite_data_type_usage(ClusterInfo *cluster)
1016+
{
1017+
bool found;
1018+
Oid firstUserOid;
1019+
char output_path[MAXPGPATH];
1020+
char *base_query;
1021+
1022+
prep_status("Checking for system-defined composite types in user tables");
1023+
1024+
snprintf(output_path, sizeof(output_path), "tables_using_composite.txt");
1025+
1026+
/*
1027+
* Look for composite types that were made during initdb *or* belong to
1028+
* information_schema; that's important in case information_schema was
1029+
* dropped and reloaded.
1030+
*
1031+
* The cutoff OID here should match the source cluster's value of
1032+
* FirstNormalObjectId. We hardcode it rather than using that C #define
1033+
* because, if that #define is ever changed, our own version's value is
1034+
* NOT what to use. Eventually we may need a test on the source cluster's
1035+
* version to select the correct value.
1036+
*/
1037+
firstUserOid = 16384;
1038+
1039+
base_query = psprintf("SELECT t.oid FROM pg_catalog.pg_type t "
1040+
"LEFT JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid "
1041+
" WHERE typtype = 'c' AND (t.oid < %u OR nspname = 'information_schema')",
1042+
firstUserOid);
1043+
1044+
found = check_for_data_types_usage(cluster, base_query, output_path);
1045+
1046+
free(base_query);
1047+
1048+
if (found)
1049+
{
1050+
pg_log(PG_REPORT, "fatal\n");
1051+
pg_fatal("Your installation contains system-defined composite type(s) in user tables.\n"
1052+
"These type OIDs are not stable across PostgreSQL versions,\n"
1053+
"so this cluster cannot currently be upgraded. You can\n"
1054+
"drop the problem columns and restart the upgrade.\n"
1055+
"A list of the problem columns is in the file:\n"
1056+
" %s\n\n", output_path);
1057+
}
1058+
else
1059+
check_ok();
1060+
}
1061+
10031062
/*
10041063
* check_for_reg_data_type_usage()
10051064
* pg_upgrade only preserves these system values:
@@ -1014,88 +1073,36 @@ check_for_tables_with_oids(ClusterInfo *cluster)
10141073
static void
10151074
check_for_reg_data_type_usage(ClusterInfo *cluster)
10161075
{
1017-
int dbnum;
1018-
FILE *script = NULL;
1019-
bool found = false;
1076+
bool found;
10201077
char output_path[MAXPGPATH];
10211078

10221079
prep_status("Checking for reg* data types in user tables");
10231080

10241081
snprintf(output_path, sizeof(output_path), "tables_using_reg.txt");
10251082

1026-
for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
1027-
{
1028-
PGresult *res;
1029-
bool db_used = false;
1030-
int ntups;
1031-
int rowno;
1032-
int i_nspname,
1033-
i_relname,
1034-
i_attname;
1035-
DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
1036-
PGconn *conn = connectToServer(cluster, active_db->db_name);
1037-
1038-
/*
1039-
* While several relkinds don't store any data, e.g. views, they can
1040-
* be used to define data types of other columns, so we check all
1041-
* relkinds.
1042-
*/
1043-
res = executeQueryOrDie(conn,
1044-
"SELECT n.nspname, c.relname, a.attname "
1045-
"FROM pg_catalog.pg_class c, "
1046-
" pg_catalog.pg_namespace n, "
1047-
" pg_catalog.pg_attribute a, "
1048-
" pg_catalog.pg_type t "
1049-
"WHERE c.oid = a.attrelid AND "
1050-
" NOT a.attisdropped AND "
1051-
" a.atttypid = t.oid AND "
1052-
" t.typnamespace = "
1053-
" (SELECT oid FROM pg_namespace "
1054-
" WHERE nspname = 'pg_catalog') AND"
1055-
" t.typname IN ( "
1056-
/* regclass.oid is preserved, so 'regclass' is OK */
1057-
" 'regcollation', "
1058-
" 'regconfig', "
1059-
" 'regdictionary', "
1060-
" 'regnamespace', "
1061-
" 'regoper', "
1062-
" 'regoperator', "
1063-
" 'regproc', "
1064-
" 'regprocedure' "
1065-
/* regrole.oid is preserved, so 'regrole' is OK */
1066-
/* regtype.oid is preserved, so 'regtype' is OK */
1067-
" ) AND "
1068-
" c.relnamespace = n.oid AND "
1069-
" n.nspname NOT IN ('pg_catalog', 'information_schema')");
1070-
1071-
ntups = PQntuples(res);
1072-
i_nspname = PQfnumber(res, "nspname");
1073-
i_relname = PQfnumber(res, "relname");
1074-
i_attname = PQfnumber(res, "attname");
1075-
for (rowno = 0; rowno < ntups; rowno++)
1076-
{
1077-
found = true;
1078-
if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
1079-
pg_fatal("could not open file \"%s\": %s\n",
1080-
output_path, strerror(errno));
1081-
if (!db_used)
1082-
{
1083-
fprintf(script, "In database: %s\n", active_db->db_name);
1084-
db_used = true;
1085-
}
1086-
fprintf(script, " %s.%s.%s\n",
1087-
PQgetvalue(res, rowno, i_nspname),
1088-
PQgetvalue(res, rowno, i_relname),
1089-
PQgetvalue(res, rowno, i_attname));
1090-
}
1091-
1092-
PQclear(res);
1093-
1094-
PQfinish(conn);
1095-
}
1096-
1097-
if (script)
1098-
fclose(script);
1083+
/*
1084+
* Note: older servers will not have all of these reg* types, so we have
1085+
* to write the query like this rather than depending on casts to regtype.
1086+
*/
1087+
found = check_for_data_types_usage(cluster,
1088+
"SELECT oid FROM pg_catalog.pg_type t "
1089+
"WHERE t.typnamespace = "
1090+
" (SELECT oid FROM pg_catalog.pg_namespace "
1091+
" WHERE nspname = 'pg_catalog') "
1092+
" AND t.typname IN ( "
1093+
/* pg_class.oid is preserved, so 'regclass' is OK */
1094+
" 'regcollation', "
1095+
" 'regconfig', "
1096+
" 'regdictionary', "
1097+
" 'regnamespace', "
1098+
" 'regoper', "
1099+
" 'regoperator', "
1100+
" 'regproc', "
1101+
" 'regprocedure' "
1102+
/* pg_authid.oid is preserved, so 'regrole' is OK */
1103+
/* pg_type.oid is (mostly) preserved, so 'regtype' is OK */
1104+
" )",
1105+
output_path);
10991106

11001107
if (found)
11011108
{
@@ -1120,75 +1127,13 @@ check_for_reg_data_type_usage(ClusterInfo *cluster)
11201127
static void
11211128
check_for_jsonb_9_4_usage(ClusterInfo *cluster)
11221129
{
1123-
int dbnum;
1124-
FILE *script = NULL;
1125-
bool found = false;
11261130
char output_path[MAXPGPATH];
11271131

11281132
prep_status("Checking for incompatible \"jsonb\" data type");
11291133

11301134
snprintf(output_path, sizeof(output_path), "tables_using_jsonb.txt");
11311135

1132-
for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
1133-
{
1134-
PGresult *res;
1135-
bool db_used = false;
1136-
int ntups;
1137-
int rowno;
1138-
int i_nspname,
1139-
i_relname,
1140-
i_attname;
1141-
DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
1142-
PGconn *conn = connectToServer(cluster, active_db->db_name);
1143-
1144-
/*
1145-
* While several relkinds don't store any data, e.g. views, they can
1146-
* be used to define data types of other columns, so we check all
1147-
* relkinds.
1148-
*/
1149-
res = executeQueryOrDie(conn,
1150-
"SELECT n.nspname, c.relname, a.attname "
1151-
"FROM pg_catalog.pg_class c, "
1152-
" pg_catalog.pg_namespace n, "
1153-
" pg_catalog.pg_attribute a "
1154-
"WHERE c.oid = a.attrelid AND "
1155-
" NOT a.attisdropped AND "
1156-
" a.atttypid = 'pg_catalog.jsonb'::pg_catalog.regtype AND "
1157-
" c.relnamespace = n.oid AND "
1158-
/* exclude possible orphaned temp tables */
1159-
" n.nspname !~ '^pg_temp_' AND "
1160-
" n.nspname NOT IN ('pg_catalog', 'information_schema')");
1161-
1162-
ntups = PQntuples(res);
1163-
i_nspname = PQfnumber(res, "nspname");
1164-
i_relname = PQfnumber(res, "relname");
1165-
i_attname = PQfnumber(res, "attname");
1166-
for (rowno = 0; rowno < ntups; rowno++)
1167-
{
1168-
found = true;
1169-
if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
1170-
pg_fatal("could not open file \"%s\": %s\n",
1171-
output_path, strerror(errno));
1172-
if (!db_used)
1173-
{
1174-
fprintf(script, "In database: %s\n", active_db->db_name);
1175-
db_used = true;
1176-
}
1177-
fprintf(script, " %s.%s.%s\n",
1178-
PQgetvalue(res, rowno, i_nspname),
1179-
PQgetvalue(res, rowno, i_relname),
1180-
PQgetvalue(res, rowno, i_attname));
1181-
}
1182-
1183-
PQclear(res);
1184-
1185-
PQfinish(conn);
1186-
}
1187-
1188-
if (script)
1189-
fclose(script);
1190-
1191-
if (found)
1136+
if (check_for_data_type_usage(cluster, "pg_catalog.jsonb", output_path))
11921137
{
11931138
pg_log(PG_REPORT, "fatal\n");
11941139
pg_fatal("Your installation contains the \"jsonb\" data type in user tables.\n"

src/bin/pg_upgrade/pg_upgrade.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,12 @@ void pg_putenv(const char *var, const char *val);
442442

443443
/* version.c */
444444

445+
bool check_for_data_types_usage(ClusterInfo *cluster,
446+
const char *base_query,
447+
const char *output_path);
448+
bool check_for_data_type_usage(ClusterInfo *cluster,
449+
const char *typename,
450+
const char *output_path);
445451
void new_9_0_populate_pg_largeobject_metadata(ClusterInfo *cluster,
446452
bool check_mode);
447453
void old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster);

src/bin/pg_upgrade/version.c

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,22 @@ new_9_0_populate_pg_largeobject_metadata(ClusterInfo *cluster, bool check_mode)
9797

9898

9999
/*
100-
* check_for_data_type_usage
101-
* Detect whether there are any stored columns depending on the given type
100+
* check_for_data_types_usage()
101+
* Detect whether there are any stored columns depending on given type(s)
102102
*
103103
* If so, write a report to the given file name, and return true.
104104
*
105-
* We check for the type in tables, matviews, and indexes, but not views;
105+
* base_query should be a SELECT yielding a single column named "oid",
106+
* containing the pg_type OIDs of one or more types that are known to have
107+
* inconsistent on-disk representations across server versions.
108+
*
109+
* We check for the type(s) in tables, matviews, and indexes, but not views;
106110
* there's no storage involved in a view.
107111
*/
108-
static bool
109-
check_for_data_type_usage(ClusterInfo *cluster, const char *typename,
110-
char *output_path)
112+
bool
113+
check_for_data_types_usage(ClusterInfo *cluster,
114+
const char *base_query,
115+
const char *output_path)
111116
{
112117
bool found = false;
113118
FILE *script = NULL;
@@ -127,16 +132,16 @@ check_for_data_type_usage(ClusterInfo *cluster, const char *typename,
127132
i_attname;
128133

129134
/*
130-
* The type of interest might be wrapped in a domain, array,
135+
* The type(s) of interest might be wrapped in a domain, array,
131136
* composite, or range, and these container types can be nested (to
132137
* varying extents depending on server version, but that's not of
133138
* concern here). To handle all these cases we need a recursive CTE.
134139
*/
135140
initPQExpBuffer(&querybuf);
136141
appendPQExpBuffer(&querybuf,
137142
"WITH RECURSIVE oids AS ( "
138-
/* the target type itself */
139-
" SELECT '%s'::pg_catalog.regtype AS oid "
143+
/* start with the type(s) returned by base_query */
144+
" %s "
140145
" UNION ALL "
141146
" SELECT * FROM ( "
142147
/* inner WITH because we can only reference the CTE once */
@@ -154,7 +159,7 @@ check_for_data_type_usage(ClusterInfo *cluster, const char *typename,
154159
" c.oid = a.attrelid AND "
155160
" NOT a.attisdropped AND "
156161
" a.atttypid = x.oid ",
157-
typename);
162+
base_query);
158163

159164
/* Ranges were introduced in 9.2 */
160165
if (GET_MAJOR_VERSION(cluster->major_version) >= 902)
@@ -222,6 +227,34 @@ check_for_data_type_usage(ClusterInfo *cluster, const char *typename,
222227
return found;
223228
}
224229

230+
/*
231+
* check_for_data_type_usage()
232+
* Detect whether there are any stored columns depending on the given type
233+
*
234+
* If so, write a report to the given file name, and return true.
235+
*
236+
* typename should be a fully qualified type name. This is just a
237+
* trivial wrapper around check_for_data_types_usage() to convert a
238+
* type name into a base query.
239+
*/
240+
bool
241+
check_for_data_type_usage(ClusterInfo *cluster,
242+
const char *typename,
243+
const char *output_path)
244+
{
245+
bool found;
246+
char *base_query;
247+
248+
base_query = psprintf("SELECT '%s'::pg_catalog.regtype AS oid",
249+
typename);
250+
251+
found = check_for_data_types_usage(cluster, base_query, output_path);
252+
253+
free(base_query);
254+
255+
return found;
256+
}
257+
225258

226259
/*
227260
* old_9_3_check_for_line_data_type_usage()

0 commit comments

Comments
 (0)