Skip to content

Commit c443e3c

Browse files
committed
Handle arrays and ranges in pg_upgrade's test for non-upgradable types.
pg_upgrade needs to check whether certain non-upgradable data types appear anywhere on-disk in the source cluster. It knew that it has to check for these types being contained inside domains and composite types; but it somehow overlooked that they could be contained in arrays and ranges, too. Extend the existing recursive-containment query to handle those cases. We probably should have noticed this oversight while working on commit 0ccfc28 and follow-ups, but we failed to :-(. The whole thing's possibly a bit overdesigned, since we don't really expect that any of these types will appear on disk; but if we're going to the effort of doing a recursive search then it's silly not to cover all the possibilities. While at it, refactor so that we have only one copy of the search logic, not three-and-counting. Also, to keep the branches looking more alike, back-patch the output wording change of commit 1634d36. Back-patch to all supported branches. Discussion: https://postgr.es/m/31473.1573412838@sss.pgh.pa.us
1 parent 004ae0a commit c443e3c

File tree

1 file changed

+99
-146
lines changed

1 file changed

+99
-146
lines changed

src/bin/pg_upgrade/version.c

Lines changed: 99 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -100,78 +100,96 @@ new_9_0_populate_pg_largeobject_metadata(ClusterInfo *cluster, bool check_mode)
100100

101101

102102
/*
103-
* old_9_3_check_for_line_data_type_usage()
104-
* 9.3 -> 9.4
105-
* Fully implement the 'line' data type in 9.4, which previously returned
106-
* "not enabled" by default and was only functionally enabled with a
107-
* compile-time switch; 9.4 "line" has different binary and text
108-
* representation formats; checks tables and indexes.
103+
* check_for_data_type_usage
104+
* Detect whether there are any stored columns depending on the given type
105+
*
106+
* If so, write a report to the given file name, and return true.
107+
*
108+
* We check for the type in tables, matviews, and indexes, but not views;
109+
* there's no storage involved in a view.
109110
*/
110-
void
111-
old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster)
111+
static bool
112+
check_for_data_type_usage(ClusterInfo *cluster, const char *typename,
113+
char *output_path)
112114
{
113-
int dbnum;
114-
FILE *script = NULL;
115115
bool found = false;
116-
char output_path[MAXPGPATH];
117-
118-
prep_status("Checking for incompatible \"line\" data type");
119-
120-
snprintf(output_path, sizeof(output_path), "tables_using_line.txt");
116+
FILE *script = NULL;
117+
int dbnum;
121118

122119
for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
123120
{
121+
DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
122+
PGconn *conn = connectToServer(cluster, active_db->db_name);
123+
PQExpBufferData querybuf;
124124
PGresult *res;
125125
bool db_used = false;
126126
int ntups;
127127
int rowno;
128128
int i_nspname,
129129
i_relname,
130130
i_attname;
131-
DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
132-
PGconn *conn = connectToServer(cluster, active_db->db_name);
133131

134132
/*
135-
* The pg_catalog.line type may be wrapped in a domain or composite
136-
* type, or both (9.3 did not allow domains on composite types, but
137-
* there may be multi-level composite type). To detect these cases
138-
* we need a recursive CTE.
133+
* The type of interest might be wrapped in a domain, array,
134+
* composite, or range, and these container types can be nested (to
135+
* varying extents depending on server version, but that's not of
136+
* concern here). To handle all these cases we need a recursive CTE.
139137
*/
140-
res = executeQueryOrDie(conn,
141-
"WITH RECURSIVE oids AS ( "
142-
/* the pg_catalog.line type itself */
143-
" SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid "
144-
" UNION ALL "
145-
" SELECT * FROM ( "
146-
/* domains on the type */
147-
" WITH x AS (SELECT oid FROM oids) "
148-
" SELECT t.oid FROM pg_catalog.pg_type t, x WHERE typbasetype = x.oid AND typtype = 'd' "
149-
" UNION "
150-
/* composite types containing the type */
151-
" SELECT t.oid FROM pg_catalog.pg_type t, pg_catalog.pg_class c, pg_catalog.pg_attribute a, x "
152-
" WHERE t.typtype = 'c' AND "
153-
" t.oid = c.reltype AND "
154-
" c.oid = a.attrelid AND "
155-
" NOT a.attisdropped AND "
156-
" a.atttypid = x.oid "
157-
" ) foo "
158-
") "
159-
"SELECT n.nspname, c.relname, a.attname "
160-
"FROM pg_catalog.pg_class c, "
161-
" pg_catalog.pg_namespace n, "
162-
" pg_catalog.pg_attribute a "
163-
"WHERE c.oid = a.attrelid AND "
164-
" NOT a.attisdropped AND "
165-
" a.atttypid IN (SELECT oid FROM oids) AND "
166-
" c.relkind IN ("
167-
CppAsString2(RELKIND_RELATION) ", "
168-
CppAsString2(RELKIND_MATVIEW) ", "
169-
CppAsString2(RELKIND_INDEX) ") AND "
170-
" c.relnamespace = n.oid AND "
138+
initPQExpBuffer(&querybuf);
139+
appendPQExpBuffer(&querybuf,
140+
"WITH RECURSIVE oids AS ( "
141+
/* the target type itself */
142+
" SELECT '%s'::pg_catalog.regtype AS oid "
143+
" UNION ALL "
144+
" SELECT * FROM ( "
145+
/* inner WITH because we can only reference the CTE once */
146+
" WITH x AS (SELECT oid FROM oids) "
147+
/* domains on any type selected so far */
148+
" SELECT t.oid FROM pg_catalog.pg_type t, x WHERE typbasetype = x.oid AND typtype = 'd' "
149+
" UNION ALL "
150+
/* arrays over any type selected so far */
151+
" SELECT t.oid FROM pg_catalog.pg_type t, x WHERE typelem = x.oid AND typtype = 'b' "
152+
" UNION ALL "
153+
/* composite types containing any type selected so far */
154+
" SELECT t.oid FROM pg_catalog.pg_type t, pg_catalog.pg_class c, pg_catalog.pg_attribute a, x "
155+
" WHERE t.typtype = 'c' AND "
156+
" t.oid = c.reltype AND "
157+
" c.oid = a.attrelid AND "
158+
" NOT a.attisdropped AND "
159+
" a.atttypid = x.oid ",
160+
typename);
161+
162+
/* Ranges came in in 9.2 */
163+
if (GET_MAJOR_VERSION(cluster->major_version) >= 902)
164+
appendPQExpBuffer(&querybuf,
165+
" UNION ALL "
166+
/* ranges containing any type selected so far */
167+
" SELECT t.oid FROM pg_catalog.pg_type t, pg_catalog.pg_range r, x "
168+
" WHERE t.typtype = 'r' AND r.rngtypid = t.oid AND r.rngsubtype = x.oid");
169+
170+
appendPQExpBuffer(&querybuf,
171+
" ) foo "
172+
") "
173+
/* now look for stored columns of any such type */
174+
"SELECT n.nspname, c.relname, a.attname "
175+
"FROM pg_catalog.pg_class c, "
176+
" pg_catalog.pg_namespace n, "
177+
" pg_catalog.pg_attribute a "
178+
"WHERE c.oid = a.attrelid AND "
179+
" NOT a.attisdropped AND "
180+
" a.atttypid IN (SELECT oid FROM oids) AND "
181+
" c.relkind IN ("
182+
CppAsString2(RELKIND_RELATION) ", "
183+
CppAsString2(RELKIND_MATVIEW) ", "
184+
CppAsString2(RELKIND_INDEX) ") AND "
185+
" c.relnamespace = n.oid AND "
171186
/* exclude possible orphaned temp tables */
172-
" n.nspname !~ '^pg_temp_' AND "
173-
" n.nspname !~ '^pg_toast_temp_' AND "
174-
" n.nspname NOT IN ('pg_catalog', 'information_schema')");
187+
" n.nspname !~ '^pg_temp_' AND "
188+
" n.nspname !~ '^pg_toast_temp_' AND "
189+
/* exclude system catalogs, too */
190+
" n.nspname NOT IN ('pg_catalog', 'information_schema')");
191+
192+
res = executeQueryOrDie(conn, "%s", querybuf.data);
175193

176194
ntups = PQntuples(res);
177195
i_nspname = PQfnumber(res, "nspname");
@@ -185,7 +203,7 @@ old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster)
185203
strerror(errno));
186204
if (!db_used)
187205
{
188-
fprintf(script, "Database: %s\n", active_db->db_name);
206+
fprintf(script, "In database: %s\n", active_db->db_name);
189207
db_used = true;
190208
}
191209
fprintf(script, " %s.%s.%s\n",
@@ -196,13 +214,36 @@ old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster)
196214

197215
PQclear(res);
198216

217+
termPQExpBuffer(&querybuf);
218+
199219
PQfinish(conn);
200220
}
201221

202222
if (script)
203223
fclose(script);
204224

205-
if (found)
225+
return found;
226+
}
227+
228+
229+
/*
230+
* old_9_3_check_for_line_data_type_usage()
231+
* 9.3 -> 9.4
232+
* Fully implement the 'line' data type in 9.4, which previously returned
233+
* "not enabled" by default and was only functionally enabled with a
234+
* compile-time switch; as of 9.4 "line" has a different on-disk
235+
* representation format.
236+
*/
237+
void
238+
old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster)
239+
{
240+
char output_path[MAXPGPATH];
241+
242+
prep_status("Checking for incompatible \"line\" data type");
243+
244+
snprintf(output_path, sizeof(output_path), "tables_using_line.txt");
245+
246+
if (check_for_data_type_usage(cluster, "pg_catalog.line", output_path))
206247
{
207248
pg_log(PG_REPORT, "fatal\n");
208249
pg_fatal("Your installation contains the \"line\" data type in user tables. This\n"
@@ -229,105 +270,17 @@ old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster)
229270
* mid-upgrade. Worse, if there's a matview with such a column, the
230271
* DDL reload will silently change it to "text" which won't match the
231272
* on-disk storage (which is like "cstring"). So we *must* reject that.
232-
* Also check composite types and domains on the "unknwown" type (even
233-
* combinations of both), in case they are used for table columns.
234-
* We needn't check indexes, because "unknown" has no opclasses.
235273
*/
236274
void
237275
old_9_6_check_for_unknown_data_type_usage(ClusterInfo *cluster)
238276
{
239-
int dbnum;
240-
FILE *script = NULL;
241-
bool found = false;
242277
char output_path[MAXPGPATH];
243278

244279
prep_status("Checking for invalid \"unknown\" user columns");
245280

246281
snprintf(output_path, sizeof(output_path), "tables_using_unknown.txt");
247282

248-
for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
249-
{
250-
PGresult *res;
251-
bool db_used = false;
252-
int ntups;
253-
int rowno;
254-
int i_nspname,
255-
i_relname,
256-
i_attname;
257-
DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
258-
PGconn *conn = connectToServer(cluster, active_db->db_name);
259-
260-
/*
261-
* The pg_catalog.unknown type may be wrapped in a domain or composite
262-
* type, or both (9.3 did not allow domains on composite types, but
263-
* there may be multi-level composite type). To detect these cases
264-
* we need a recursive CTE.
265-
*/
266-
res = executeQueryOrDie(conn,
267-
"WITH RECURSIVE oids AS ( "
268-
/* the pg_catalog.unknown type itself */
269-
" SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid "
270-
" UNION ALL "
271-
" SELECT * FROM ( "
272-
/* domains on the type */
273-
" WITH x AS (SELECT oid FROM oids) "
274-
" SELECT t.oid FROM pg_catalog.pg_type t, x WHERE typbasetype = x.oid AND typtype = 'd' "
275-
" UNION "
276-
/* composite types containing the type */
277-
" SELECT t.oid FROM pg_catalog.pg_type t, pg_catalog.pg_class c, pg_catalog.pg_attribute a, x "
278-
" WHERE t.typtype = 'c' AND "
279-
" t.oid = c.reltype AND "
280-
" c.oid = a.attrelid AND "
281-
" NOT a.attisdropped AND "
282-
" a.atttypid = x.oid "
283-
" ) foo "
284-
") "
285-
"SELECT n.nspname, c.relname, a.attname "
286-
"FROM pg_catalog.pg_class c, "
287-
" pg_catalog.pg_namespace n, "
288-
" pg_catalog.pg_attribute a "
289-
"WHERE c.oid = a.attrelid AND "
290-
" NOT a.attisdropped AND "
291-
" a.atttypid IN (SELECT oid FROM oids) AND "
292-
" c.relkind IN ("
293-
CppAsString2(RELKIND_RELATION) ", "
294-
CppAsString2(RELKIND_MATVIEW) ") AND "
295-
" c.relnamespace = n.oid AND "
296-
/* exclude possible orphaned temp tables */
297-
" n.nspname !~ '^pg_temp_' AND "
298-
" n.nspname !~ '^pg_toast_temp_' AND "
299-
" n.nspname NOT IN ('pg_catalog', 'information_schema')");
300-
301-
ntups = PQntuples(res);
302-
i_nspname = PQfnumber(res, "nspname");
303-
i_relname = PQfnumber(res, "relname");
304-
i_attname = PQfnumber(res, "attname");
305-
for (rowno = 0; rowno < ntups; rowno++)
306-
{
307-
found = true;
308-
if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
309-
pg_fatal("could not open file \"%s\": %s\n", output_path,
310-
strerror(errno));
311-
if (!db_used)
312-
{
313-
fprintf(script, "Database: %s\n", active_db->db_name);
314-
db_used = true;
315-
}
316-
fprintf(script, " %s.%s.%s\n",
317-
PQgetvalue(res, rowno, i_nspname),
318-
PQgetvalue(res, rowno, i_relname),
319-
PQgetvalue(res, rowno, i_attname));
320-
}
321-
322-
PQclear(res);
323-
324-
PQfinish(conn);
325-
}
326-
327-
if (script)
328-
fclose(script);
329-
330-
if (found)
283+
if (check_for_data_type_usage(cluster, "pg_catalog.unknown", output_path))
331284
{
332285
pg_log(PG_REPORT, "fatal\n");
333286
pg_fatal("Your installation contains the \"unknown\" data type in user tables. This\n"

0 commit comments

Comments
 (0)