PostgreSQL Source Code  git master
version.c
Go to the documentation of this file.
1 /*
2  * version.c
3  *
4  * Postgres-version-specific routines
5  *
6  * Copyright (c) 2010-2017, 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.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  */
24 void
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  */
110 void
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  */
207 void
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 ("
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  */
296 void
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 */
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 }
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:10406
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
Definition: fe-exec.c:3118
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:128
void old_9_6_check_for_unknown_data_type_usage(ClusterInfo *cluster)
Definition: version.c:208
#define RELKIND_MATVIEW
Definition: pg_class.h:165
PGresult * executeQueryOrDie(PGconn *conn, const char *fmt,...) pg_attribute_printf(2
void PQfinish(PGconn *conn)
Definition: fe-connect.c:3629
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
void pg_fatal(const char *fmt,...)
Definition: logging.c:83
int PQntuples(const PGresult *res)
Definition: fe-exec.c:2724
FILE * fopen_priv(const char *path, const char *mode)
Definition: file.c:321
#define RELKIND_COMPOSITE_TYPE
Definition: pg_class.h:166
void appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname)
Definition: string_utils.c:596
PGconn * conn
Definition: streamutil.c:46
#define MAXPGPATH
void prep_status(const char *fmt,...) pg_attribute_printf(1
void cluster(ClusterStmt *stmt, bool isTopLevel)
Definition: cluster.c:106
void pg_log(eLogType type, const char *fmt,...)
Definition: logging.c:69
static void check_ok(void)
Definition: initdb.c:2051
PGconn * connectToServer(ClusterInfo *cluster, const char *db_name)
Definition: server.c:27
#define CppAsString2(x)
Definition: c.h:206
int PQfnumber(const PGresult *res, const char *field_name)
Definition: fe-exec.c:2832
void PQclear(PGresult *res)
Definition: fe-exec.c:671
DbInfoArr dbarr
Definition: pg_upgrade.h:265
const char * strerror(int errnum)
Definition: strerror.c:19
void report_status(eLogType type, const char *fmt,...) pg_attribute_printf(2
char * db_name
Definition: pg_upgrade.h:187
void new_9_0_populate_pg_largeobject_metadata(ClusterInfo *cluster, bool check_mode)
Definition: version.c:25
DbInfo * dbs
Definition: pg_upgrade.h:198
#define RELKIND_RELATION
Definition: pg_class.h:160
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:89
void old_9_3_check_for_line_data_type_usage(ClusterInfo *cluster)
Definition: version.c:111
void old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
Definition: version.c:297