4 * Postgres-version-specific routines
6 * Copyright (c) 2010-2011, PostgreSQL Global Development Group
7 * contrib/pg_upgrade/version_old_8_3.c
10 #include "pg_upgrade.h"
12 #include "access/transam.h"
16 * old_8_3_check_for_name_data_type_usage()
18 * Alignment for the 'name' data type changed to 'char' in 8.4;
19 * checks tables and indexes.
22 old_8_3_check_for_name_data_type_usage(ClusterInfo *cluster)
27 char output_path[MAXPGPATH];
29 prep_status("Checking for invalid 'name' user columns");
31 snprintf(output_path, sizeof(output_path), "%s/tables_using_name.txt",
34 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
43 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
44 PGconn *conn = connectToServer(cluster, active_db->db_name);
47 * With a smaller alignment in 8.4, 'name' cannot be used in a
48 * non-pg_catalog table, except as the first column. (We could tighten
49 * that condition with enough analysis, but it seems not worth the
52 res = executeQueryOrDie(conn,
53 "SELECT n.nspname, c.relname, a.attname "
54 "FROM pg_catalog.pg_class c, "
55 " pg_catalog.pg_namespace n, "
56 " pg_catalog.pg_attribute a "
57 "WHERE c.oid = a.attrelid AND "
59 " NOT a.attisdropped AND "
60 " a.atttypid = 'pg_catalog.name'::pg_catalog.regtype AND "
61 " c.relnamespace = n.oid AND "
62 " n.nspname != 'pg_catalog' AND "
63 " n.nspname != 'information_schema'");
65 ntups = PQntuples(res);
66 i_nspname = PQfnumber(res, "nspname");
67 i_relname = PQfnumber(res, "relname");
68 i_attname = PQfnumber(res, "attname");
69 for (rowno = 0; rowno < ntups; rowno++)
72 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
73 pg_log(PG_FATAL, "Could not create necessary file: %s\n", output_path);
76 fprintf(script, "Database: %s\n", active_db->db_name);
79 fprintf(script, " %s.%s.%s\n",
80 PQgetvalue(res, rowno, i_nspname),
81 PQgetvalue(res, rowno, i_relname),
82 PQgetvalue(res, rowno, i_attname));
95 pg_log(PG_REPORT, "fatal\n");
97 "| Your installation contains the \"name\" data type in\n"
98 "| user tables. This data type changed its internal\n"
99 "| alignment between your old and new clusters so this\n"
100 "| cluster cannot currently be upgraded. You can\n"
101 "| remove the problem tables and restart the upgrade.\n"
102 "| A list of the problem columns is in the file:\n"
103 "| \t%s\n\n", output_path);
111 * old_8_3_check_for_tsquery_usage()
113 * A new 'prefix' field was added to the 'tsquery' data type in 8.4
114 * so upgrading of such fields is impossible.
117 old_8_3_check_for_tsquery_usage(ClusterInfo *cluster)
122 char output_path[MAXPGPATH];
124 prep_status("Checking for tsquery user columns");
126 snprintf(output_path, sizeof(output_path), "%s/tables_using_tsquery.txt",
129 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
132 bool db_used = false;
138 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
139 PGconn *conn = connectToServer(cluster, active_db->db_name);
141 /* Find any user-defined tsquery columns */
142 res = executeQueryOrDie(conn,
143 "SELECT n.nspname, c.relname, a.attname "
144 "FROM pg_catalog.pg_class c, "
145 " pg_catalog.pg_namespace n, "
146 " pg_catalog.pg_attribute a "
147 "WHERE c.relkind = 'r' AND "
148 " c.oid = a.attrelid AND "
149 " NOT a.attisdropped AND "
150 " a.atttypid = 'pg_catalog.tsquery'::pg_catalog.regtype AND "
151 " c.relnamespace = n.oid AND "
152 " n.nspname != 'pg_catalog' AND "
153 " n.nspname != 'information_schema'");
155 ntups = PQntuples(res);
156 i_nspname = PQfnumber(res, "nspname");
157 i_relname = PQfnumber(res, "relname");
158 i_attname = PQfnumber(res, "attname");
159 for (rowno = 0; rowno < ntups; rowno++)
162 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
163 pg_log(PG_FATAL, "Could not create necessary file: %s\n", output_path);
166 fprintf(script, "Database: %s\n", active_db->db_name);
169 fprintf(script, " %s.%s.%s\n",
170 PQgetvalue(res, rowno, i_nspname),
171 PQgetvalue(res, rowno, i_relname),
172 PQgetvalue(res, rowno, i_attname));
185 pg_log(PG_REPORT, "fatal\n");
187 "| Your installation contains the \"tsquery\" data type.\n"
188 "| This data type added a new internal field between\n"
189 "| your old and new clusters so this cluster cannot\n"
190 "| currently be upgraded. You can remove the problem\n"
191 "| columns and restart the upgrade. A list of the\n"
192 "| problem columns is in the file:\n"
193 "| \t%s\n\n", output_path);
201 * old_8_3_rebuild_tsvector_tables()
203 * 8.3 sorts lexemes by its length and if lengths are the same then it uses
204 * alphabetic order; 8.4 sorts lexemes in lexicographical order, e.g.
206 * => SELECT 'c bb aaa'::tsvector;
209 * 'aaa' 'bb' 'c' -- 8.4
210 * 'c' 'bb' 'aaa' -- 8.3
213 old_8_3_rebuild_tsvector_tables(ClusterInfo *cluster, bool check_mode)
218 char output_path[MAXPGPATH];
220 prep_status("Checking for tsvector user columns");
222 snprintf(output_path, sizeof(output_path), "%s/rebuild_tsvector_tables.sql",
225 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
228 bool db_used = false;
229 char nspname[NAMEDATALEN] = "",
230 relname[NAMEDATALEN] = "";
236 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
237 PGconn *conn = connectToServer(cluster, active_db->db_name);
239 /* Find any user-defined tsvector columns */
240 res = executeQueryOrDie(conn,
241 "SELECT n.nspname, c.relname, a.attname "
242 "FROM pg_catalog.pg_class c, "
243 " pg_catalog.pg_namespace n, "
244 " pg_catalog.pg_attribute a "
245 "WHERE c.relkind = 'r' AND "
246 " c.oid = a.attrelid AND "
247 " NOT a.attisdropped AND "
248 " a.atttypid = 'pg_catalog.tsvector'::pg_catalog.regtype AND "
249 " c.relnamespace = n.oid AND "
250 " n.nspname != 'pg_catalog' AND "
251 " n.nspname != 'information_schema'");
254 * This macro is used below to avoid reindexing indexes already rebuilt
255 * because of tsvector columns.
257 #define SKIP_TSVECTOR_TABLES \
258 "i.indrelid NOT IN ( " \
259 "SELECT DISTINCT c.oid " \
260 "FROM pg_catalog.pg_class c, " \
261 " pg_catalog.pg_namespace n, " \
262 " pg_catalog.pg_attribute a " \
263 "WHERE c.relkind = 'r' AND " \
264 " c.oid = a.attrelid AND " \
265 " NOT a.attisdropped AND " \
266 " a.atttypid = 'pg_catalog.tsvector'::pg_catalog.regtype AND " \
267 " c.relnamespace = n.oid AND " \
268 " n.nspname != 'pg_catalog' AND " \
269 " n.nspname != 'information_schema') "
271 ntups = PQntuples(res);
272 i_nspname = PQfnumber(res, "nspname");
273 i_relname = PQfnumber(res, "relname");
274 i_attname = PQfnumber(res, "attname");
275 for (rowno = 0; rowno < ntups; rowno++)
280 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
281 pg_log(PG_FATAL, "Could not create necessary file: %s\n", output_path);
284 fprintf(script, "\\connect %s\n\n",
285 quote_identifier(active_db->db_name));
289 /* Rebuild all tsvector collumns with one ALTER TABLE command */
290 if (strcmp(PQgetvalue(res, rowno, i_nspname), nspname) != 0 ||
291 strcmp(PQgetvalue(res, rowno, i_relname), relname) != 0)
293 if (strlen(nspname) != 0 || strlen(relname) != 0)
294 fprintf(script, ";\n\n");
295 fprintf(script, "ALTER TABLE %s.%s\n",
296 quote_identifier(PQgetvalue(res, rowno, i_nspname)),
297 quote_identifier(PQgetvalue(res, rowno, i_relname)));
300 fprintf(script, ",\n");
301 strlcpy(nspname, PQgetvalue(res, rowno, i_nspname), sizeof(nspname));
302 strlcpy(relname, PQgetvalue(res, rowno, i_relname), sizeof(relname));
304 fprintf(script, "ALTER COLUMN %s "
305 /* This could have been a custom conversion function call. */
306 "TYPE pg_catalog.tsvector USING %s::pg_catalog.text::pg_catalog.tsvector",
307 quote_identifier(PQgetvalue(res, rowno, i_attname)),
308 quote_identifier(PQgetvalue(res, rowno, i_attname)));
311 if (strlen(nspname) != 0 || strlen(relname) != 0)
312 fprintf(script, ";\n\n");
316 /* XXX Mark tables as not accessable somehow */
326 report_status(PG_WARNING, "warning");
328 pg_log(PG_WARNING, "\n"
329 "| Your installation contains tsvector columns.\n"
330 "| The tsvector internal storage format changed\n"
331 "| between your old and new clusters so the tables\n"
332 "| must be rebuilt. After upgrading, you will be\n"
333 "| given instructions.\n\n");
335 pg_log(PG_WARNING, "\n"
336 "| Your installation contains tsvector columns.\n"
337 "| The tsvector internal storage format changed\n"
338 "| between your old and new clusters so the tables\n"
339 "| must be rebuilt. The file:\n"
341 "| when executed by psql by the database super-user\n"
342 "| will rebuild all tables with tsvector columns.\n\n",
351 * old_8_3_invalidate_hash_gin_indexes()
353 * Hash, Gin, and GiST index binary format has changes from 8.3->8.4
356 old_8_3_invalidate_hash_gin_indexes(ClusterInfo *cluster, bool check_mode)
361 char output_path[MAXPGPATH];
363 prep_status("Checking for hash and gin indexes");
365 snprintf(output_path, sizeof(output_path), "%s/reindex_hash_and_gin.sql",
368 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
371 bool db_used = false;
376 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
377 PGconn *conn = connectToServer(cluster, active_db->db_name);
379 /* find hash and gin indexes */
380 res = executeQueryOrDie(conn,
381 "SELECT n.nspname, c.relname "
382 "FROM pg_catalog.pg_class c, "
383 " pg_catalog.pg_index i, "
384 " pg_catalog.pg_am a, "
385 " pg_catalog.pg_namespace n "
386 "WHERE i.indexrelid = c.oid AND "
387 " c.relam = a.oid AND "
388 " c.relnamespace = n.oid AND "
389 " a.amname IN ('hash', 'gin') AND "
390 SKIP_TSVECTOR_TABLES);
392 ntups = PQntuples(res);
393 i_nspname = PQfnumber(res, "nspname");
394 i_relname = PQfnumber(res, "relname");
395 for (rowno = 0; rowno < ntups; rowno++)
400 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
401 pg_log(PG_FATAL, "Could not create necessary file: %s\n", output_path);
404 fprintf(script, "\\connect %s\n",
405 quote_identifier(active_db->db_name));
408 fprintf(script, "REINDEX INDEX %s.%s;\n",
409 quote_identifier(PQgetvalue(res, rowno, i_nspname)),
410 quote_identifier(PQgetvalue(res, rowno, i_relname)));
416 if (!check_mode && found)
417 /* mark hash and gin indexes as invalid */
418 PQclear(executeQueryOrDie(conn,
419 "UPDATE pg_catalog.pg_index i "
420 "SET indisvalid = false "
421 "FROM pg_catalog.pg_class c, "
422 " pg_catalog.pg_am a, "
423 " pg_catalog.pg_namespace n "
424 "WHERE i.indexrelid = c.oid AND "
425 " c.relam = a.oid AND "
426 " c.relnamespace = n.oid AND "
427 " a.amname IN ('hash', 'gin')"));
437 report_status(PG_WARNING, "warning");
439 pg_log(PG_WARNING, "\n"
440 "| Your installation contains hash and/or gin\n"
441 "| indexes. These indexes have different\n"
442 "| internal formats between your old and new\n"
443 "| clusters so they must be reindexed with the\n"
444 "| REINDEX command. After upgrading, you will\n"
445 "| be given REINDEX instructions.\n\n");
447 pg_log(PG_WARNING, "\n"
448 "| Your installation contains hash and/or gin\n"
449 "| indexes. These indexes have different internal\n"
450 "| formats between your old and new clusters so\n"
451 "| they must be reindexed with the REINDEX command.\n"
454 "| when executed by psql by the database super-user\n"
455 "| will recreate all invalid indexes; until then,\n"
456 "| none of these indexes will be used.\n\n",
465 * old_8_3_invalidate_bpchar_pattern_ops_indexes()
467 * 8.4 bpchar_pattern_ops no longer sorts based on trailing spaces
470 old_8_3_invalidate_bpchar_pattern_ops_indexes(ClusterInfo *cluster,
476 char output_path[MAXPGPATH];
478 prep_status("Checking for bpchar_pattern_ops indexes");
480 snprintf(output_path, sizeof(output_path), "%s/reindex_bpchar_ops.sql",
483 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
486 bool db_used = false;
491 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
492 PGconn *conn = connectToServer(cluster, active_db->db_name);
494 /* find bpchar_pattern_ops indexes */
497 * Do only non-hash, non-gin indexees; we already invalidated them
498 * above; no need to reindex twice
500 res = executeQueryOrDie(conn,
501 "SELECT n.nspname, c.relname "
502 "FROM pg_catalog.pg_index i, "
503 " pg_catalog.pg_class c, "
504 " pg_catalog.pg_namespace n "
505 "WHERE indexrelid = c.oid AND "
506 " c.relnamespace = n.oid AND "
509 " FROM pg_catalog.pg_opclass o, "
510 " pg_catalog.pg_am a"
511 " WHERE a.amname NOT IN ('hash', 'gin') AND "
512 " a.oid = o.opcmethod AND "
513 " o.opcname = 'bpchar_pattern_ops') "
514 " = ANY (i.indclass) AND "
515 SKIP_TSVECTOR_TABLES);
517 ntups = PQntuples(res);
518 i_nspname = PQfnumber(res, "nspname");
519 i_relname = PQfnumber(res, "relname");
520 for (rowno = 0; rowno < ntups; rowno++)
525 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
526 pg_log(PG_FATAL, "Could not create necessary file: %s\n", output_path);
529 fprintf(script, "\\connect %s\n",
530 quote_identifier(active_db->db_name));
533 fprintf(script, "REINDEX INDEX %s.%s;\n",
534 quote_identifier(PQgetvalue(res, rowno, i_nspname)),
535 quote_identifier(PQgetvalue(res, rowno, i_relname)));
541 if (!check_mode && found)
542 /* mark bpchar_pattern_ops indexes as invalid */
543 PQclear(executeQueryOrDie(conn,
544 "UPDATE pg_catalog.pg_index i "
545 "SET indisvalid = false "
546 "FROM pg_catalog.pg_class c, "
547 " pg_catalog.pg_namespace n "
548 "WHERE indexrelid = c.oid AND "
549 " c.relnamespace = n.oid AND "
552 " FROM pg_catalog.pg_opclass o, "
553 " pg_catalog.pg_am a"
554 " WHERE a.amname NOT IN ('hash', 'gin') AND "
555 " a.oid = o.opcmethod AND "
556 " o.opcname = 'bpchar_pattern_ops') "
557 " = ANY (i.indclass)"));
567 report_status(PG_WARNING, "warning");
569 pg_log(PG_WARNING, "\n"
570 "| Your installation contains indexes using\n"
571 "| \"bpchar_pattern_ops\". These indexes have\n"
572 "| different internal formats between your old and\n"
573 "| new clusters so they must be reindexed with the\n"
574 "| REINDEX command. After upgrading, you will be\n"
575 "| given REINDEX instructions.\n\n");
577 pg_log(PG_WARNING, "\n"
578 "| Your installation contains indexes using\n"
579 "| \"bpchar_pattern_ops\". These indexes have\n"
580 "| different internal formats between your old and\n"
581 "| new clusters so they must be reindexed with the\n"
582 "| REINDEX command. The file:\n"
584 "| when executed by psql by the database super-user\n"
585 "| will recreate all invalid indexes; until then,\n"
586 "| none of these indexes will be used.\n\n",
595 * old_8_3_create_sequence_script()
597 * 8.4 added the column "start_value" to all sequences. For this reason,
598 * we don't transfer sequence files but instead use the CREATE SEQUENCE
599 * command from the schema dump, and use setval() to restore the sequence
600 * value and 'is_called' from the old database. This is safe to run
601 * by pg_upgrade because sequence files are not transfered from the old
602 * server, even in link mode.
605 old_8_3_create_sequence_script(ClusterInfo *cluster)
610 char *output_path = pg_malloc(MAXPGPATH);
612 snprintf(output_path, MAXPGPATH, "%s/adjust_sequences.sql", os_info.cwd);
614 prep_status("Creating script to adjust sequences");
616 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
619 bool db_used = false;
624 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
625 PGconn *conn = connectToServer(cluster, active_db->db_name);
627 /* Find any sequences */
628 res = executeQueryOrDie(conn,
629 "SELECT n.nspname, c.relname "
630 "FROM pg_catalog.pg_class c, "
631 " pg_catalog.pg_namespace n "
632 "WHERE c.relkind = 'S' AND "
633 " c.relnamespace = n.oid AND "
634 " n.nspname != 'pg_catalog' AND "
635 " n.nspname != 'information_schema'");
637 ntups = PQntuples(res);
638 i_nspname = PQfnumber(res, "nspname");
639 i_relname = PQfnumber(res, "relname");
640 for (rowno = 0; rowno < ntups; rowno++)
645 const char *nspname = PQgetvalue(res, rowno, i_nspname);
646 const char *relname = PQgetvalue(res, rowno, i_relname);
650 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
651 pg_log(PG_FATAL, "Could not create necessary file: %s\n", output_path);
654 fprintf(script, "\\connect %s\n\n",
655 quote_identifier(active_db->db_name));
659 /* Find the desired sequence */
660 seq_res = executeQueryOrDie(conn,
661 "SELECT s.last_value, s.is_called "
663 quote_identifier(nspname),
664 quote_identifier(relname));
666 assert(PQntuples(seq_res) == 1);
667 i_last_value = PQfnumber(seq_res, "last_value");
668 i_is_called = PQfnumber(seq_res, "is_called");
670 fprintf(script, "SELECT setval('%s.%s', %s, '%s');\n",
671 quote_identifier(nspname), quote_identifier(relname),
672 PQgetvalue(seq_res, 0, i_last_value), PQgetvalue(seq_res, 0, i_is_called));
676 fprintf(script, "\n");
692 pg_free(output_path);