1/*
2 * version.c
3 *
4 * Postgres-version-specific routines
5 *
6 * Copyright (c) 2010-2019, PostgreSQL Global Development Group
7 * src/bin/pg_upgrade/version.c
8 */
9
10#include "postgres_fe.h"
11
12#include "pg_upgrade.h"
13
14#include "catalog/pg_class_d.h"
15#include "fe_utils/string_utils.h"
16
17
18
19/*
20 * new_9_0_populate_pg_largeobject_metadata()
21 * new >= 9.0, old <= 8.4
22 * 9.0 has a new pg_largeobject permission table
23 */
24void
25new_9_0_populate_pg_largeobject_metadata(ClusterInfo *cluster, bool check_mode)
26{
27 int dbnum;
28 FILE *script = NULL;
29 bool found = false;
30 char output_path[MAXPGPATH];
31
32 prep_status("Checking for large objects");
33
34 snprintf(output_path, sizeof(output_path), "pg_largeobject.sql");
35
36 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
37 {
38 PGresult *res;
39 int i_count;
40 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
41 PGconn *conn = connectToServer(cluster, active_db->db_name);
42
43 /* find if there are any large objects */
44 res = executeQueryOrDie(conn,
45 "SELECT count(*) "
46 "FROM pg_catalog.pg_largeobject ");
47
48 i_count = PQfnumber(res, "count");
49 if (atoi(PQgetvalue(res, 0, i_count)) != 0)
50 {
51 found = true;
52 if (!check_mode)
53 {
54 PQExpBufferData connectbuf;
55
56 if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
57 pg_fatal("could not open file \"%s\": %s\n", output_path,
58 strerror(errno));
59
60 initPQExpBuffer(&connectbuf);
61 appendPsqlMetaConnect(&connectbuf, active_db->db_name);
62 fputs(connectbuf.data, script);
63 termPQExpBuffer(&connectbuf);
64
65 fprintf(script,
66 "SELECT pg_catalog.lo_create(t.loid)\n"
67 "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) AS t;\n");
68 }
69 }
70
71 PQclear(res);
72 PQfinish(conn);
73 }
74
75 if (script)
76 fclose(script);
77
78 if (found)
79 {
80 report_status(PG_WARNING, "warning");
81 if (check_mode)
82 pg_log(PG_WARNING, "\n"
83 "Your installation contains large objects. The new database has an\n"
84 "additional large object permission table. After upgrading, you will be\n"
85 "given a command to populate the pg_largeobject_metadata table with\n"
86 "default permissions.\n\n");
87 else
88 pg_log(PG_WARNING, "\n"
89 "Your installation contains large objects. The new database has an\n"
90 "additional large object permission table, so default permissions must be\n"
91 "defined for all large objects. The file\n"
92 " %s\n"
93 "when executed by psql by the database superuser will set the default\n"
94 "permissions.\n\n",
95 output_path);
96 }
97 else
98 check_ok();
99}
100
101
102/*
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.
109 */
110void
111old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster)
112{
113 int dbnum;
114 FILE *script = NULL;
115 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");
121
122 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
123 {
124 PGresult *res;
125 bool db_used = false;
126 int ntups;
127 int rowno;
128 int i_nspname,
129 i_relname,
130 i_attname;
131 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
132 PGconn *conn = connectToServer(cluster, active_db->db_name);
133
134 res = executeQueryOrDie(conn,
135 "SELECT n.nspname, c.relname, a.attname "
136 "FROM pg_catalog.pg_class c, "
137 " pg_catalog.pg_namespace n, "
138 " pg_catalog.pg_attribute a "
139 "WHERE c.oid = a.attrelid AND "
140 " NOT a.attisdropped AND "
141 " a.atttypid = 'pg_catalog.line'::pg_catalog.regtype AND "
142 " c.relnamespace = n.oid AND "
143 /* exclude possible orphaned temp tables */
144 " n.nspname !~ '^pg_temp_' AND "
145 " n.nspname !~ '^pg_toast_temp_' AND "
146 " n.nspname NOT IN ('pg_catalog', 'information_schema')");
147
148 ntups = PQntuples(res);
149 i_nspname = PQfnumber(res, "nspname");
150 i_relname = PQfnumber(res, "relname");
151 i_attname = PQfnumber(res, "attname");
152 for (rowno = 0; rowno < ntups; rowno++)
153 {
154 found = true;
155 if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
156 pg_fatal("could not open file \"%s\": %s\n", output_path,
157 strerror(errno));
158 if (!db_used)
159 {
160 fprintf(script, "Database: %s\n", active_db->db_name);
161 db_used = true;
162 }
163 fprintf(script, " %s.%s.%s\n",
164 PQgetvalue(res, rowno, i_nspname),
165 PQgetvalue(res, rowno, i_relname),
166 PQgetvalue(res, rowno, i_attname));
167 }
168
169 PQclear(res);
170
171 PQfinish(conn);
172 }
173
174 if (script)
175 fclose(script);
176
177 if (found)
178 {
179 pg_log(PG_REPORT, "fatal\n");
180 pg_fatal("Your installation contains the \"line\" data type in user tables. This\n"
181 "data type changed its internal and input/output format between your old\n"
182 "and new clusters so this cluster cannot currently be upgraded. You can\n"
183 "remove the problem tables and restart the upgrade. A list of the problem\n"
184 "columns is in the file:\n"
185 " %s\n\n", output_path);
186 }
187 else
188 check_ok();
189}
190
191
192/*
193 * old_9_6_check_for_unknown_data_type_usage()
194 * 9.6 -> 10
195 * It's no longer allowed to create tables or views with "unknown"-type
196 * columns. We do not complain about views with such columns, because
197 * they should get silently converted to "text" columns during the DDL
198 * dump and reload; it seems unlikely to be worth making users do that
199 * by hand. However, if there's a table with such a column, the DDL
200 * reload will fail, so we should pre-detect that rather than failing
201 * mid-upgrade. Worse, if there's a matview with such a column, the
202 * DDL reload will silently change it to "text" which won't match the
203 * on-disk storage (which is like "cstring"). So we *must* reject that.
204 * Also check composite types, in case they are used for table columns.
205 * We needn't check indexes, because "unknown" has no opclasses.
206 */
207void
208old_9_6_check_for_unknown_data_type_usage(ClusterInfo *cluster)
209{
210 int dbnum;
211 FILE *script = NULL;
212 bool found = false;
213 char output_path[MAXPGPATH];
214
215 prep_status("Checking for invalid \"unknown\" user columns");
216
217 snprintf(output_path, sizeof(output_path), "tables_using_unknown.txt");
218
219 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
220 {
221 PGresult *res;
222 bool db_used = false;
223 int ntups;
224 int rowno;
225 int i_nspname,
226 i_relname,
227 i_attname;
228 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
229 PGconn *conn = connectToServer(cluster, active_db->db_name);
230
231 res = executeQueryOrDie(conn,
232 "SELECT n.nspname, c.relname, a.attname "
233 "FROM pg_catalog.pg_class c, "
234 " pg_catalog.pg_namespace n, "
235 " pg_catalog.pg_attribute a "
236 "WHERE c.oid = a.attrelid AND "
237 " NOT a.attisdropped AND "
238 " a.atttypid = 'pg_catalog.unknown'::pg_catalog.regtype AND "
239 " c.relkind IN ("
240 CppAsString2(RELKIND_RELATION) ", "
241 CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
242 CppAsString2(RELKIND_MATVIEW) ") AND "
243 " c.relnamespace = n.oid AND "
244 /* exclude possible orphaned temp tables */
245 " n.nspname !~ '^pg_temp_' AND "
246 " n.nspname !~ '^pg_toast_temp_' AND "
247 " n.nspname NOT IN ('pg_catalog', 'information_schema')");
248
249 ntups = PQntuples(res);
250 i_nspname = PQfnumber(res, "nspname");
251 i_relname = PQfnumber(res, "relname");
252 i_attname = PQfnumber(res, "attname");
253 for (rowno = 0; rowno < ntups; rowno++)
254 {
255 found = true;
256 if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
257 pg_fatal("could not open file \"%s\": %s\n", output_path,
258 strerror(errno));
259 if (!db_used)
260 {
261 fprintf(script, "Database: %s\n", active_db->db_name);
262 db_used = true;
263 }
264 fprintf(script, " %s.%s.%s\n",
265 PQgetvalue(res, rowno, i_nspname),
266 PQgetvalue(res, rowno, i_relname),
267 PQgetvalue(res, rowno, i_attname));
268 }
269
270 PQclear(res);
271
272 PQfinish(conn);
273 }
274
275 if (script)
276 fclose(script);
277
278 if (found)
279 {
280 pg_log(PG_REPORT, "fatal\n");
281 pg_fatal("Your installation contains the \"unknown\" data type in user tables. This\n"
282 "data type is no longer allowed in tables, so this cluster cannot currently\n"
283 "be upgraded. You can remove the problem tables and restart the upgrade.\n"
284 "A list of the problem columns is in the file:\n"
285 " %s\n\n", output_path);
286 }
287 else
288 check_ok();
289}
290
291/*
292 * old_9_6_invalidate_hash_indexes()
293 * 9.6 -> 10
294 * Hash index binary format has changed from 9.6->10.0
295 */
296void
297old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
298{
299 int dbnum;
300 FILE *script = NULL;
301 bool found = false;
302 char *output_path = "reindex_hash.sql";
303
304 prep_status("Checking for hash indexes");
305
306 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
307 {
308 PGresult *res;
309 bool db_used = false;
310 int ntups;
311 int rowno;
312 int i_nspname,
313 i_relname;
314 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
315 PGconn *conn = connectToServer(cluster, active_db->db_name);
316
317 /* find hash indexes */
318 res = executeQueryOrDie(conn,
319 "SELECT n.nspname, c.relname "
320 "FROM pg_catalog.pg_class c, "
321 " pg_catalog.pg_index i, "
322 " pg_catalog.pg_am a, "
323 " pg_catalog.pg_namespace n "
324 "WHERE i.indexrelid = c.oid AND "
325 " c.relam = a.oid AND "
326 " c.relnamespace = n.oid AND "
327 " a.amname = 'hash'"
328 );
329
330 ntups = PQntuples(res);
331 i_nspname = PQfnumber(res, "nspname");
332 i_relname = PQfnumber(res, "relname");
333 for (rowno = 0; rowno < ntups; rowno++)
334 {
335 found = true;
336 if (!check_mode)
337 {
338 if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
339 pg_fatal("could not open file \"%s\": %s\n", output_path,
340 strerror(errno));
341 if (!db_used)
342 {
343 PQExpBufferData connectbuf;
344
345 initPQExpBuffer(&connectbuf);
346 appendPsqlMetaConnect(&connectbuf, active_db->db_name);
347 fputs(connectbuf.data, script);
348 termPQExpBuffer(&connectbuf);
349 db_used = true;
350 }
351 fprintf(script, "REINDEX INDEX %s.%s;\n",
352 quote_identifier(PQgetvalue(res, rowno, i_nspname)),
353 quote_identifier(PQgetvalue(res, rowno, i_relname)));
354 }
355 }
356
357 PQclear(res);
358
359 if (!check_mode && db_used)
360 {
361 /* mark hash indexes as invalid */
362 PQclear(executeQueryOrDie(conn,
363 "UPDATE pg_catalog.pg_index i "
364 "SET indisvalid = false "
365 "FROM pg_catalog.pg_class c, "
366 " pg_catalog.pg_am a, "
367 " pg_catalog.pg_namespace n "
368 "WHERE i.indexrelid = c.oid AND "
369 " c.relam = a.oid AND "
370 " c.relnamespace = n.oid AND "
371 " a.amname = 'hash'"));
372 }
373
374 PQfinish(conn);
375 }
376
377 if (script)
378 fclose(script);
379
380 if (found)
381 {
382 report_status(PG_WARNING, "warning");
383 if (check_mode)
384 pg_log(PG_WARNING, "\n"
385 "Your installation contains hash indexes. These indexes have different\n"
386 "internal formats between your old and new clusters, so they must be\n"
387 "reindexed with the REINDEX command. After upgrading, you will be given\n"
388 "REINDEX instructions.\n\n");
389 else
390 pg_log(PG_WARNING, "\n"
391 "Your installation contains hash indexes. These indexes have different\n"
392 "internal formats between your old and new clusters, so they must be\n"
393 "reindexed with the REINDEX command. The file\n"
394 " %s\n"
395 "when executed by psql by the database superuser will recreate all invalid\n"
396 "indexes; until then, none of these indexes will be used.\n\n",
397 output_path);
398 }
399 else
400 check_ok();
401}
402