4 * Postgres-version-specific routines
6 * Copyright (c) 2010-2013, PostgreSQL Global Development Group
7 * contrib/pg_upgrade/version_old_8_3.c
10 #include "postgres_fe.h"
12 #include "pg_upgrade.h"
14 #include "access/transam.h"
18 * old_8_3_check_for_name_data_type_usage()
20 * Alignment for the 'name' data type changed to 'char' in 8.4;
21 * checks tables and indexes.
24 old_8_3_check_for_name_data_type_usage(ClusterInfo *cluster)
29 char output_path[MAXPGPATH];
31 prep_status("Checking for invalid \"name\" user columns");
33 snprintf(output_path, sizeof(output_path), "tables_using_name.txt");
35 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
44 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
45 PGconn *conn = connectToServer(cluster, active_db->db_name);
48 * With a smaller alignment in 8.4, 'name' cannot be used in a
49 * non-pg_catalog table, except as the first column. (We could tighten
50 * that condition with enough analysis, but it seems not worth the
53 res = executeQueryOrDie(conn,
54 "SELECT n.nspname, c.relname, a.attname "
55 "FROM pg_catalog.pg_class c, "
56 " pg_catalog.pg_namespace n, "
57 " pg_catalog.pg_attribute a "
58 "WHERE c.oid = a.attrelid AND "
60 " NOT a.attisdropped AND "
61 " a.atttypid = 'pg_catalog.name'::pg_catalog.regtype AND "
62 " c.relnamespace = n.oid AND "
63 /* exclude possible orphaned temp tables */
64 " n.nspname !~ '^pg_temp_' AND "
65 " n.nspname !~ '^pg_toast_temp_' AND "
66 " n.nspname NOT IN ('pg_catalog', 'information_schema')");
68 ntups = PQntuples(res);
69 i_nspname = PQfnumber(res, "nspname");
70 i_relname = PQfnumber(res, "relname");
71 i_attname = PQfnumber(res, "attname");
72 for (rowno = 0; rowno < ntups; rowno++)
75 if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
76 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
79 fprintf(script, "Database: %s\n", active_db->db_name);
82 fprintf(script, " %s.%s.%s\n",
83 PQgetvalue(res, rowno, i_nspname),
84 PQgetvalue(res, rowno, i_relname),
85 PQgetvalue(res, rowno, i_attname));
98 pg_log(PG_REPORT, "fatal\n");
100 "Your installation contains the \"name\" data type in user tables. This\n"
101 "data type changed its internal alignment between your old and new\n"
102 "clusters so this cluster cannot currently be upgraded. You can remove\n"
103 "the problem tables and restart the upgrade. A list of the problem\n"
104 "columns is in the file:\n"
105 " %s\n\n", output_path);
113 * old_8_3_check_for_tsquery_usage()
115 * A new 'prefix' field was added to the 'tsquery' data type in 8.4
116 * so upgrading of such fields is impossible.
119 old_8_3_check_for_tsquery_usage(ClusterInfo *cluster)
124 char output_path[MAXPGPATH];
126 prep_status("Checking for tsquery user columns");
128 snprintf(output_path, sizeof(output_path), "tables_using_tsquery.txt");
130 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
133 bool db_used = false;
139 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
140 PGconn *conn = connectToServer(cluster, active_db->db_name);
142 /* Find any user-defined tsquery columns */
143 res = executeQueryOrDie(conn,
144 "SELECT n.nspname, c.relname, a.attname "
145 "FROM pg_catalog.pg_class c, "
146 " pg_catalog.pg_namespace n, "
147 " pg_catalog.pg_attribute a "
148 /* materialized views didn't exist in 8.3, so no need to check 'm' */
149 "WHERE c.relkind = 'r' AND "
150 " c.oid = a.attrelid AND "
151 " NOT a.attisdropped AND "
152 " a.atttypid = 'pg_catalog.tsquery'::pg_catalog.regtype AND "
153 " c.relnamespace = n.oid AND "
154 /* exclude possible orphaned temp tables */
155 " n.nspname !~ '^pg_temp_' AND "
156 " n.nspname !~ '^pg_toast_temp_' AND "
157 " n.nspname NOT IN ('pg_catalog', 'information_schema')");
159 ntups = PQntuples(res);
160 i_nspname = PQfnumber(res, "nspname");
161 i_relname = PQfnumber(res, "relname");
162 i_attname = PQfnumber(res, "attname");
163 for (rowno = 0; rowno < ntups; rowno++)
166 if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
167 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
170 fprintf(script, "Database: %s\n", active_db->db_name);
173 fprintf(script, " %s.%s.%s\n",
174 PQgetvalue(res, rowno, i_nspname),
175 PQgetvalue(res, rowno, i_relname),
176 PQgetvalue(res, rowno, i_attname));
189 pg_log(PG_REPORT, "fatal\n");
191 "Your installation contains the \"tsquery\" data type. This data type\n"
192 "added a new internal field between your old and new clusters so this\n"
193 "cluster cannot currently be upgraded. You can remove the problem\n"
194 "columns and restart the upgrade. A list of the problem columns is in the\n"
196 " %s\n\n", output_path);
204 * old_8_3_check_ltree_usage()
206 * The internal ltree structure was changed in 8.4 so upgrading is impossible.
209 old_8_3_check_ltree_usage(ClusterInfo *cluster)
214 char output_path[MAXPGPATH];
216 prep_status("Checking for contrib/ltree");
218 snprintf(output_path, sizeof(output_path), "contrib_ltree.txt");
220 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
223 bool db_used = false;
228 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
229 PGconn *conn = connectToServer(cluster, active_db->db_name);
231 /* Find any functions coming from contrib/ltree */
232 res = executeQueryOrDie(conn,
233 "SELECT n.nspname, p.proname "
234 "FROM pg_catalog.pg_proc p, "
235 " pg_catalog.pg_namespace n "
236 "WHERE p.pronamespace = n.oid AND "
237 " p.probin = '$libdir/ltree'");
239 ntups = PQntuples(res);
240 i_nspname = PQfnumber(res, "nspname");
241 i_proname = PQfnumber(res, "proname");
242 for (rowno = 0; rowno < ntups; rowno++)
245 if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
246 pg_log(PG_FATAL, "Could not open file \"%s\": %s\n",
247 output_path, getErrorText(errno));
250 fprintf(script, "Database: %s\n", active_db->db_name);
253 fprintf(script, " %s.%s\n",
254 PQgetvalue(res, rowno, i_nspname),
255 PQgetvalue(res, rowno, i_proname));
268 pg_log(PG_REPORT, "fatal\n");
270 "Your installation contains the \"ltree\" data type. This data type\n"
271 "changed its internal storage format between your old and new clusters so this\n"
272 "cluster cannot currently be upgraded. You can manually upgrade databases\n"
273 "that use \"contrib/ltree\" facilities and remove \"contrib/ltree\" from the old\n"
274 "cluster and restart the upgrade. A list of the problem functions is in the\n"
276 " %s\n\n", output_path);
284 * old_8_3_rebuild_tsvector_tables()
286 * 8.3 sorts lexemes by its length and if lengths are the same then it uses
287 * alphabetic order; 8.4 sorts lexemes in lexicographical order, e.g.
289 * => SELECT 'c bb aaa'::tsvector;
292 * 'aaa' 'bb' 'c' -- 8.4
293 * 'c' 'bb' 'aaa' -- 8.3
296 old_8_3_rebuild_tsvector_tables(ClusterInfo *cluster, bool check_mode)
301 char output_path[MAXPGPATH];
303 prep_status("Checking for tsvector user columns");
305 snprintf(output_path, sizeof(output_path), "rebuild_tsvector_tables.sql");
307 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
310 bool db_used = false;
311 char nspname[NAMEDATALEN] = "",
312 relname[NAMEDATALEN] = "";
318 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
319 PGconn *conn = connectToServer(cluster, active_db->db_name);
321 /* Find any user-defined tsvector columns */
322 res = executeQueryOrDie(conn,
323 "SELECT n.nspname, c.relname, a.attname "
324 "FROM pg_catalog.pg_class c, "
325 " pg_catalog.pg_namespace n, "
326 " pg_catalog.pg_attribute a "
327 /* materialized views didn't exist in 8.3, so no need to check 'm' */
328 "WHERE c.relkind = 'r' AND "
329 " c.oid = a.attrelid AND "
330 " NOT a.attisdropped AND "
331 " a.atttypid = 'pg_catalog.tsvector'::pg_catalog.regtype AND "
332 " c.relnamespace = n.oid AND "
333 /* exclude possible orphaned temp tables */
334 " n.nspname !~ '^pg_temp_' AND "
335 " n.nspname !~ '^pg_toast_temp_' AND "
336 " n.nspname NOT IN ('pg_catalog', 'information_schema')");
339 * This macro is used below to avoid reindexing indexes already rebuilt
340 * because of tsvector columns.
342 #define SKIP_TSVECTOR_TABLES \
343 "i.indrelid NOT IN ( " \
344 "SELECT DISTINCT c.oid " \
345 "FROM pg_catalog.pg_class c, " \
346 " pg_catalog.pg_namespace n, " \
347 " pg_catalog.pg_attribute a " \
348 /* materialized views didn't exist in 8.3, so no need to check 'm' */ \
349 "WHERE c.relkind = 'r' AND " \
350 " c.oid = a.attrelid AND " \
351 " NOT a.attisdropped AND " \
352 " a.atttypid = 'pg_catalog.tsvector'::pg_catalog.regtype AND " \
353 " c.relnamespace = n.oid AND " \
354 " n.nspname !~ '^pg_' AND " \
355 " n.nspname != 'information_schema') "
357 ntups = PQntuples(res);
358 i_nspname = PQfnumber(res, "nspname");
359 i_relname = PQfnumber(res, "relname");
360 i_attname = PQfnumber(res, "attname");
361 for (rowno = 0; rowno < ntups; rowno++)
366 if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
367 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
370 fprintf(script, "\\connect %s\n\n",
371 quote_identifier(active_db->db_name));
375 /* Rebuild all tsvector collumns with one ALTER TABLE command */
376 if (strcmp(PQgetvalue(res, rowno, i_nspname), nspname) != 0 ||
377 strcmp(PQgetvalue(res, rowno, i_relname), relname) != 0)
379 if (strlen(nspname) != 0 || strlen(relname) != 0)
380 fprintf(script, ";\n\n");
381 fprintf(script, "ALTER TABLE %s.%s\n",
382 quote_identifier(PQgetvalue(res, rowno, i_nspname)),
383 quote_identifier(PQgetvalue(res, rowno, i_relname)));
386 fprintf(script, ",\n");
387 strlcpy(nspname, PQgetvalue(res, rowno, i_nspname), sizeof(nspname));
388 strlcpy(relname, PQgetvalue(res, rowno, i_relname), sizeof(relname));
390 fprintf(script, "ALTER COLUMN %s "
391 /* This could have been a custom conversion function call. */
392 "TYPE pg_catalog.tsvector USING %s::pg_catalog.text::pg_catalog.tsvector",
393 quote_identifier(PQgetvalue(res, rowno, i_attname)),
394 quote_identifier(PQgetvalue(res, rowno, i_attname)));
397 if (strlen(nspname) != 0 || strlen(relname) != 0)
398 fprintf(script, ";\n\n");
402 /* XXX Mark tables as not accessible somehow */
412 report_status(PG_WARNING, "warning");
414 pg_log(PG_WARNING, "\n"
415 "Your installation contains tsvector columns. The tsvector internal\n"
416 "storage format changed between your old and new clusters so the tables\n"
417 "must be rebuilt. After upgrading, you will be given instructions.\n\n");
419 pg_log(PG_WARNING, "\n"
420 "Your installation contains tsvector columns. The tsvector internal\n"
421 "storage format changed between your old and new clusters so the tables\n"
422 "must be rebuilt. The file:\n"
424 "when executed by psql by the database superuser will rebuild all tables\n"
425 "with tsvector columns.\n\n",
434 * old_8_3_invalidate_hash_gin_indexes()
436 * Hash, Gin, and GiST index binary format has changes from 8.3->8.4
439 old_8_3_invalidate_hash_gin_indexes(ClusterInfo *cluster, bool check_mode)
444 char output_path[MAXPGPATH];
446 prep_status("Checking for hash and GIN indexes");
448 snprintf(output_path, sizeof(output_path), "reindex_hash_and_gin.sql");
450 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
453 bool db_used = false;
458 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
459 PGconn *conn = connectToServer(cluster, active_db->db_name);
461 /* find hash and gin indexes */
462 res = executeQueryOrDie(conn,
463 "SELECT n.nspname, c.relname "
464 "FROM pg_catalog.pg_class c, "
465 " pg_catalog.pg_index i, "
466 " pg_catalog.pg_am a, "
467 " pg_catalog.pg_namespace n "
468 "WHERE i.indexrelid = c.oid AND "
469 " c.relam = a.oid AND "
470 " c.relnamespace = n.oid AND "
471 " a.amname IN ('hash', 'gin') AND "
472 SKIP_TSVECTOR_TABLES);
474 ntups = PQntuples(res);
475 i_nspname = PQfnumber(res, "nspname");
476 i_relname = PQfnumber(res, "relname");
477 for (rowno = 0; rowno < ntups; rowno++)
482 if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
483 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
486 fprintf(script, "\\connect %s\n",
487 quote_identifier(active_db->db_name));
490 fprintf(script, "REINDEX INDEX %s.%s;\n",
491 quote_identifier(PQgetvalue(res, rowno, i_nspname)),
492 quote_identifier(PQgetvalue(res, rowno, i_relname)));
498 if (!check_mode && found)
499 /* mark hash and gin indexes as invalid */
500 PQclear(executeQueryOrDie(conn,
501 "UPDATE pg_catalog.pg_index i "
502 "SET indisvalid = false "
503 "FROM pg_catalog.pg_class c, "
504 " pg_catalog.pg_am a, "
505 " pg_catalog.pg_namespace n "
506 "WHERE i.indexrelid = c.oid AND "
507 " c.relam = a.oid AND "
508 " c.relnamespace = n.oid AND "
509 " a.amname IN ('hash', 'gin')"));
519 report_status(PG_WARNING, "warning");
521 pg_log(PG_WARNING, "\n"
522 "Your installation contains hash and/or GIN indexes. These indexes have\n"
523 "different internal formats between your old and new clusters, so they\n"
524 "must be reindexed with the REINDEX command. After upgrading, you will\n"
525 "be given REINDEX instructions.\n\n");
527 pg_log(PG_WARNING, "\n"
528 "Your installation contains hash and/or GIN indexes. These indexes have\n"
529 "different internal formats between your old and new clusters, so they\n"
530 "must be reindexed with the REINDEX command. The file:\n"
532 "when executed by psql by the database superuser will recreate all invalid\n"
533 "indexes; until then, none of these indexes will be used.\n\n",
542 * old_8_3_invalidate_bpchar_pattern_ops_indexes()
544 * 8.4 bpchar_pattern_ops no longer sorts based on trailing spaces
547 old_8_3_invalidate_bpchar_pattern_ops_indexes(ClusterInfo *cluster,
553 char output_path[MAXPGPATH];
555 prep_status("Checking for bpchar_pattern_ops indexes");
557 snprintf(output_path, sizeof(output_path), "reindex_bpchar_ops.sql");
559 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
562 bool db_used = false;
567 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
568 PGconn *conn = connectToServer(cluster, active_db->db_name);
570 /* find bpchar_pattern_ops indexes */
573 * Do only non-hash, non-gin indexees; we already invalidated them
574 * above; no need to reindex twice
576 res = executeQueryOrDie(conn,
577 "SELECT n.nspname, c.relname "
578 "FROM pg_catalog.pg_index i, "
579 " pg_catalog.pg_class c, "
580 " pg_catalog.pg_namespace n "
581 "WHERE indexrelid = c.oid AND "
582 " c.relnamespace = n.oid AND "
585 " FROM pg_catalog.pg_opclass o, "
586 " pg_catalog.pg_am a"
587 " WHERE a.amname NOT IN ('hash', 'gin') AND "
588 " a.oid = o.opcmethod AND "
589 " o.opcname = 'bpchar_pattern_ops') "
590 " = ANY (i.indclass) AND "
591 SKIP_TSVECTOR_TABLES);
593 ntups = PQntuples(res);
594 i_nspname = PQfnumber(res, "nspname");
595 i_relname = PQfnumber(res, "relname");
596 for (rowno = 0; rowno < ntups; rowno++)
601 if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
602 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
605 fprintf(script, "\\connect %s\n",
606 quote_identifier(active_db->db_name));
609 fprintf(script, "REINDEX INDEX %s.%s;\n",
610 quote_identifier(PQgetvalue(res, rowno, i_nspname)),
611 quote_identifier(PQgetvalue(res, rowno, i_relname)));
617 if (!check_mode && found)
618 /* mark bpchar_pattern_ops indexes as invalid */
619 PQclear(executeQueryOrDie(conn,
620 "UPDATE pg_catalog.pg_index i "
621 "SET indisvalid = false "
622 "FROM pg_catalog.pg_class c, "
623 " pg_catalog.pg_namespace n "
624 "WHERE indexrelid = c.oid AND "
625 " c.relnamespace = n.oid AND "
628 " FROM pg_catalog.pg_opclass o, "
629 " pg_catalog.pg_am a"
630 " WHERE a.amname NOT IN ('hash', 'gin') AND "
631 " a.oid = o.opcmethod AND "
632 " o.opcname = 'bpchar_pattern_ops') "
633 " = ANY (i.indclass)"));
643 report_status(PG_WARNING, "warning");
645 pg_log(PG_WARNING, "\n"
646 "Your installation contains indexes using \"bpchar_pattern_ops\". These\n"
647 "indexes have different internal formats between your old and new clusters\n"
648 "so they must be reindexed with the REINDEX command. After upgrading, you\n"
649 "will be given REINDEX instructions.\n\n");
651 pg_log(PG_WARNING, "\n"
652 "Your installation contains indexes using \"bpchar_pattern_ops\". These\n"
653 "indexes have different internal formats between your old and new clusters\n"
654 "so they must be reindexed with the REINDEX command. The file:\n"
656 "when executed by psql by the database superuser will recreate all invalid\n"
657 "indexes; until then, none of these indexes will be used.\n\n",
666 * old_8_3_create_sequence_script()
668 * 8.4 added the column "start_value" to all sequences. For this reason,
669 * we don't transfer sequence files but instead use the CREATE SEQUENCE
670 * command from the schema dump, and use setval() to restore the sequence
671 * value and 'is_called' from the old database. This is safe to run
672 * by pg_upgrade because sequence files are not transferred from the old
673 * server, even in link mode.
676 old_8_3_create_sequence_script(ClusterInfo *cluster)
681 char *output_path = pg_malloc(MAXPGPATH);
683 snprintf(output_path, MAXPGPATH, "adjust_sequences.sql");
685 prep_status("Creating script to adjust sequences");
687 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
690 bool db_used = false;
695 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
696 PGconn *conn = connectToServer(cluster, active_db->db_name);
698 /* Find any sequences */
699 res = executeQueryOrDie(conn,
700 "SELECT n.nspname, c.relname "
701 "FROM pg_catalog.pg_class c, "
702 " pg_catalog.pg_namespace n "
703 "WHERE c.relkind = 'S' AND "
704 " c.relnamespace = n.oid AND "
705 /* exclude possible orphaned temp tables */
706 " n.nspname !~ '^pg_temp_' AND "
707 " n.nspname !~ '^pg_toast_temp_' AND "
708 " n.nspname NOT IN ('pg_catalog', 'information_schema')");
710 ntups = PQntuples(res);
711 i_nspname = PQfnumber(res, "nspname");
712 i_relname = PQfnumber(res, "relname");
713 for (rowno = 0; rowno < ntups; rowno++)
718 const char *nspname = PQgetvalue(res, rowno, i_nspname);
719 const char *relname = PQgetvalue(res, rowno, i_relname);
723 if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
724 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
727 fprintf(script, "\\connect %s\n\n",
728 quote_identifier(active_db->db_name));
732 /* Find the desired sequence */
733 seq_res = executeQueryOrDie(conn,
734 "SELECT s.last_value, s.is_called "
736 quote_identifier(nspname),
737 quote_identifier(relname));
739 assert(PQntuples(seq_res) == 1);
740 i_last_value = PQfnumber(seq_res, "last_value");
741 i_is_called = PQfnumber(seq_res, "is_called");
743 fprintf(script, "SELECT setval('%s.%s', %s, '%s');\n",
744 quote_identifier(nspname), quote_identifier(relname),
745 PQgetvalue(seq_res, 0, i_last_value), PQgetvalue(seq_res, 0, i_is_called));
749 fprintf(script, "\n");
765 pg_free(output_path);