@@ -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 ;
115115bool 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
122119for (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 ;
124124PGresult * res ;
125125bool db_used = false;
126126int ntups ;
127127int rowno ;
128128int i_nspname ,
129129i_relname ,
130130i_attname ;
131- DbInfo * active_db = & cluster -> dbarr .dbs [dbnum ];
132- PGconn * conn = connectToServer (cluster ,active_db -> db_name );
133131
134132/*
135- * Thepg_catalog.line typemay be wrapped in a domain or composite
136- *type , orboth (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 typeof interest might be wrapped in a domain, array,
134+ *composite , orrange, 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- "FROMpg_catalog.pg_class c, "
161- "pg_catalog.pg_namespace n, "
162- "pg_catalog.pg_attribute a "
163- "WHEREc.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+ "FROMpg_catalog.pg_class c, "
176+ "pg_catalog.pg_namespace n, "
177+ "pg_catalog.pg_attribute a "
178+ "WHEREc.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
176194ntups = PQntuples (res );
177195i_nspname = PQfnumber (res ,"nspname" );
@@ -185,7 +203,7 @@ old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster)
185203strerror (errno ));
186204if (!db_used )
187205{
188- fprintf (script ,"Database : %s\n" ,active_db -> db_name );
206+ fprintf (script ,"In database : %s\n" ,active_db -> db_name );
189207db_used = true;
190208}
191209fprintf (script ," %s.%s.%s\n" ,
@@ -196,13 +214,36 @@ old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster)
196214
197215PQclear (res );
198216
217+ termPQExpBuffer (& querybuf );
218+
199219PQfinish (conn );
200220}
201221
202222if (script )
203223fclose (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{
207248pg_log (PG_REPORT ,"fatal\n" );
208249pg_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 */
236274void
237275old_9_6_check_for_unknown_data_type_usage (ClusterInfo * cluster )
238276{
239- int dbnum ;
240- FILE * script = NULL ;
241- bool found = false;
242277char output_path [MAXPGPATH ];
243278
244279prep_status ("Checking for invalid \"unknown\" user columns" );
245280
246281snprintf (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- "FROMpg_catalog.pg_class c, "
287- "pg_catalog.pg_namespace n, "
288- "pg_catalog.pg_attribute a "
289- "WHEREc.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{
332285pg_log (PG_REPORT ,"fatal\n" );
333286pg_fatal ("Your installation contains the \"unknown\" data type in user tables. This\n"