4 * Postgres-version-specific routines
6 * Copyright (c) 2010-2011, PostgreSQL Global Development Group
7 * contrib/pg_upgrade/version_old_8_3.c
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), "%s/tables_using_name.txt",
36 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
45 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
46 PGconn *conn = connectToServer(cluster, active_db->db_name);
49 * With a smaller alignment in 8.4, 'name' cannot be used in a
50 * non-pg_catalog table, except as the first column. (We could tighten
51 * that condition with enough analysis, but it seems not worth the
54 res = executeQueryOrDie(conn,
55 "SELECT n.nspname, c.relname, a.attname "
56 "FROM pg_catalog.pg_class c, "
57 " pg_catalog.pg_namespace n, "
58 " pg_catalog.pg_attribute a "
59 "WHERE c.oid = a.attrelid AND "
61 " NOT a.attisdropped AND "
62 " a.atttypid = 'pg_catalog.name'::pg_catalog.regtype AND "
63 " c.relnamespace = n.oid AND "
64 /* exclude possible orphaned temp tables */
65 " n.nspname !~ '^pg_temp_' AND "
66 " n.nspname !~ '^pg_toast_temp_' AND "
67 " n.nspname NOT IN ('pg_catalog', 'information_schema')");
69 ntups = PQntuples(res);
70 i_nspname = PQfnumber(res, "nspname");
71 i_relname = PQfnumber(res, "relname");
72 i_attname = PQfnumber(res, "attname");
73 for (rowno = 0; rowno < ntups; rowno++)
76 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
77 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
80 fprintf(script, "Database: %s\n", active_db->db_name);
83 fprintf(script, " %s.%s.%s\n",
84 PQgetvalue(res, rowno, i_nspname),
85 PQgetvalue(res, rowno, i_relname),
86 PQgetvalue(res, rowno, i_attname));
99 pg_log(PG_REPORT, "fatal\n");
101 "Your installation contains the \"name\" data type in user tables. This\n"
102 "data type changed its internal alignment between your old and new\n"
103 "clusters so this cluster cannot currently be upgraded. You can remove\n"
104 "the problem tables and restart the upgrade. A list of the problem\n"
105 "columns is in the file:\n"
106 " %s\n\n", output_path);
114 * old_8_3_check_for_tsquery_usage()
116 * A new 'prefix' field was added to the 'tsquery' data type in 8.4
117 * so upgrading of such fields is impossible.
120 old_8_3_check_for_tsquery_usage(ClusterInfo *cluster)
125 char output_path[MAXPGPATH];
127 prep_status("Checking for tsquery user columns");
129 snprintf(output_path, sizeof(output_path), "%s/tables_using_tsquery.txt",
132 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
135 bool db_used = false;
141 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
142 PGconn *conn = connectToServer(cluster, active_db->db_name);
144 /* Find any user-defined tsquery columns */
145 res = executeQueryOrDie(conn,
146 "SELECT n.nspname, c.relname, a.attname "
147 "FROM pg_catalog.pg_class c, "
148 " pg_catalog.pg_namespace n, "
149 " pg_catalog.pg_attribute a "
150 "WHERE c.relkind = 'r' AND "
151 " c.oid = a.attrelid AND "
152 " NOT a.attisdropped AND "
153 " a.atttypid = 'pg_catalog.tsquery'::pg_catalog.regtype AND "
154 " c.relnamespace = n.oid AND "
155 /* exclude possible orphaned temp tables */
156 " n.nspname !~ '^pg_temp_' AND "
157 " n.nspname !~ '^pg_toast_temp_' AND "
158 " n.nspname NOT IN ('pg_catalog', 'information_schema')");
160 ntups = PQntuples(res);
161 i_nspname = PQfnumber(res, "nspname");
162 i_relname = PQfnumber(res, "relname");
163 i_attname = PQfnumber(res, "attname");
164 for (rowno = 0; rowno < ntups; rowno++)
167 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
168 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
171 fprintf(script, "Database: %s\n", active_db->db_name);
174 fprintf(script, " %s.%s.%s\n",
175 PQgetvalue(res, rowno, i_nspname),
176 PQgetvalue(res, rowno, i_relname),
177 PQgetvalue(res, rowno, i_attname));
190 pg_log(PG_REPORT, "fatal\n");
192 "Your installation contains the \"tsquery\" data type. This data type\n"
193 "added a new internal field between your old and new clusters so this\n"
194 "cluster cannot currently be upgraded. You can remove the problem\n"
195 "columns and restart the upgrade. A list of the problem columns is in the\n"
197 " %s\n\n", output_path);
205 * old_8_3_check_ltree_usage()
207 * The internal ltree structure was changed in 8.4 so upgrading is impossible.
210 old_8_3_check_ltree_usage(ClusterInfo *cluster)
215 char output_path[MAXPGPATH];
217 prep_status("Checking for contrib/ltree");
219 snprintf(output_path, sizeof(output_path), "%s/contrib_ltree.txt",
222 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
225 bool db_used = false;
230 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
231 PGconn *conn = connectToServer(cluster, active_db->db_name);
233 /* Find any functions coming from contrib/ltree */
234 res = executeQueryOrDie(conn,
235 "SELECT n.nspname, p.proname "
236 "FROM pg_catalog.pg_proc p, "
237 " pg_catalog.pg_namespace n "
238 "WHERE p.pronamespace = n.oid AND "
239 " p.probin = '$libdir/ltree'");
241 ntups = PQntuples(res);
242 i_nspname = PQfnumber(res, "nspname");
243 i_proname = PQfnumber(res, "proname");
244 for (rowno = 0; rowno < ntups; rowno++)
247 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
248 pg_log(PG_FATAL, "Could not open file \"%s\": %s\n",
249 output_path, getErrorText(errno));
252 fprintf(script, "Database: %s\n", active_db->db_name);
255 fprintf(script, " %s.%s\n",
256 PQgetvalue(res, rowno, i_nspname),
257 PQgetvalue(res, rowno, i_proname));
270 pg_log(PG_REPORT, "fatal\n");
272 "Your installation contains the \"ltree\" data type. This data type\n"
273 "changed its internal storage format between your old and new clusters so this\n"
274 "cluster cannot currently be upgraded. You can manually upgrade databases\n"
275 "that use \"contrib/ltree\" facilities and remove \"contrib/ltree\" from the old\n"
276 "cluster and restart the upgrade. A list of the problem functions is in the\n"
278 " %s\n\n", output_path);
286 * old_8_3_rebuild_tsvector_tables()
288 * 8.3 sorts lexemes by its length and if lengths are the same then it uses
289 * alphabetic order; 8.4 sorts lexemes in lexicographical order, e.g.
291 * => SELECT 'c bb aaa'::tsvector;
294 * 'aaa' 'bb' 'c' -- 8.4
295 * 'c' 'bb' 'aaa' -- 8.3
298 old_8_3_rebuild_tsvector_tables(ClusterInfo *cluster, bool check_mode)
303 char output_path[MAXPGPATH];
305 prep_status("Checking for tsvector user columns");
307 snprintf(output_path, sizeof(output_path), "%s/rebuild_tsvector_tables.sql",
310 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
313 bool db_used = false;
314 char nspname[NAMEDATALEN] = "",
315 relname[NAMEDATALEN] = "";
321 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
322 PGconn *conn = connectToServer(cluster, active_db->db_name);
324 /* Find any user-defined tsvector columns */
325 res = executeQueryOrDie(conn,
326 "SELECT n.nspname, c.relname, a.attname "
327 "FROM pg_catalog.pg_class c, "
328 " pg_catalog.pg_namespace n, "
329 " pg_catalog.pg_attribute a "
330 "WHERE c.relkind = 'r' AND "
331 " c.oid = a.attrelid AND "
332 " NOT a.attisdropped AND "
333 " a.atttypid = 'pg_catalog.tsvector'::pg_catalog.regtype AND "
334 " c.relnamespace = n.oid AND "
335 /* exclude possible orphaned temp tables */
336 " n.nspname !~ '^pg_temp_' AND "
337 " n.nspname !~ '^pg_toast_temp_' AND "
338 " n.nspname NOT IN ('pg_catalog', 'information_schema')");
341 * This macro is used below to avoid reindexing indexes already rebuilt
342 * because of tsvector columns.
344 #define SKIP_TSVECTOR_TABLES \
345 "i.indrelid NOT IN ( " \
346 "SELECT DISTINCT c.oid " \
347 "FROM pg_catalog.pg_class c, " \
348 " pg_catalog.pg_namespace n, " \
349 " pg_catalog.pg_attribute a " \
350 "WHERE c.relkind = 'r' AND " \
351 " c.oid = a.attrelid AND " \
352 " NOT a.attisdropped AND " \
353 " a.atttypid = 'pg_catalog.tsvector'::pg_catalog.regtype AND " \
354 " c.relnamespace = n.oid AND " \
355 " n.nspname !~ '^pg_' AND " \
356 " n.nspname != 'information_schema') "
358 ntups = PQntuples(res);
359 i_nspname = PQfnumber(res, "nspname");
360 i_relname = PQfnumber(res, "relname");
361 i_attname = PQfnumber(res, "attname");
362 for (rowno = 0; rowno < ntups; rowno++)
367 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
368 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
371 fprintf(script, "\\connect %s\n\n",
372 quote_identifier(active_db->db_name));
376 /* Rebuild all tsvector collumns with one ALTER TABLE command */
377 if (strcmp(PQgetvalue(res, rowno, i_nspname), nspname) != 0 ||
378 strcmp(PQgetvalue(res, rowno, i_relname), relname) != 0)
380 if (strlen(nspname) != 0 || strlen(relname) != 0)
381 fprintf(script, ";\n\n");
382 fprintf(script, "ALTER TABLE %s.%s\n",
383 quote_identifier(PQgetvalue(res, rowno, i_nspname)),
384 quote_identifier(PQgetvalue(res, rowno, i_relname)));
387 fprintf(script, ",\n");
388 strlcpy(nspname, PQgetvalue(res, rowno, i_nspname), sizeof(nspname));
389 strlcpy(relname, PQgetvalue(res, rowno, i_relname), sizeof(relname));
391 fprintf(script, "ALTER COLUMN %s "
392 /* This could have been a custom conversion function call. */
393 "TYPE pg_catalog.tsvector USING %s::pg_catalog.text::pg_catalog.tsvector",
394 quote_identifier(PQgetvalue(res, rowno, i_attname)),
395 quote_identifier(PQgetvalue(res, rowno, i_attname)));
398 if (strlen(nspname) != 0 || strlen(relname) != 0)
399 fprintf(script, ";\n\n");
403 /* XXX Mark tables as not accessable somehow */
413 report_status(PG_WARNING, "warning");
415 pg_log(PG_WARNING, "\n"
416 "Your installation contains tsvector columns. The tsvector internal\n"
417 "storage format changed between your old and new clusters so the tables\n"
418 "must be rebuilt. After upgrading, you will be given instructions.\n\n");
420 pg_log(PG_WARNING, "\n"
421 "Your installation contains tsvector columns. The tsvector internal\n"
422 "storage format changed between your old and new clusters so the tables\n"
423 "must be rebuilt. The file:\n"
425 "when executed by psql by the database superuser will rebuild all tables\n"
426 "with tsvector columns.\n\n",
435 * old_8_3_invalidate_hash_gin_indexes()
437 * Hash, Gin, and GiST index binary format has changes from 8.3->8.4
440 old_8_3_invalidate_hash_gin_indexes(ClusterInfo *cluster, bool check_mode)
445 char output_path[MAXPGPATH];
447 prep_status("Checking for hash and GIN indexes");
449 snprintf(output_path, sizeof(output_path), "%s/reindex_hash_and_gin.sql",
452 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
455 bool db_used = false;
460 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
461 PGconn *conn = connectToServer(cluster, active_db->db_name);
463 /* find hash and gin indexes */
464 res = executeQueryOrDie(conn,
465 "SELECT n.nspname, c.relname "
466 "FROM pg_catalog.pg_class c, "
467 " pg_catalog.pg_index i, "
468 " pg_catalog.pg_am a, "
469 " pg_catalog.pg_namespace n "
470 "WHERE i.indexrelid = c.oid AND "
471 " c.relam = a.oid AND "
472 " c.relnamespace = n.oid AND "
473 " a.amname IN ('hash', 'gin') AND "
474 SKIP_TSVECTOR_TABLES);
476 ntups = PQntuples(res);
477 i_nspname = PQfnumber(res, "nspname");
478 i_relname = PQfnumber(res, "relname");
479 for (rowno = 0; rowno < ntups; rowno++)
484 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
485 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
488 fprintf(script, "\\connect %s\n",
489 quote_identifier(active_db->db_name));
492 fprintf(script, "REINDEX INDEX %s.%s;\n",
493 quote_identifier(PQgetvalue(res, rowno, i_nspname)),
494 quote_identifier(PQgetvalue(res, rowno, i_relname)));
500 if (!check_mode && found)
501 /* mark hash and gin indexes as invalid */
502 PQclear(executeQueryOrDie(conn,
503 "UPDATE pg_catalog.pg_index i "
504 "SET indisvalid = false "
505 "FROM pg_catalog.pg_class c, "
506 " pg_catalog.pg_am a, "
507 " pg_catalog.pg_namespace n "
508 "WHERE i.indexrelid = c.oid AND "
509 " c.relam = a.oid AND "
510 " c.relnamespace = n.oid AND "
511 " a.amname IN ('hash', 'gin')"));
521 report_status(PG_WARNING, "warning");
523 pg_log(PG_WARNING, "\n"
524 "Your installation contains hash and/or GIN indexes. These indexes have\n"
525 "different internal formats between your old and new clusters, so they\n"
526 "must be reindexed with the REINDEX command. After upgrading, you will\n"
527 "be given REINDEX instructions.\n\n");
529 pg_log(PG_WARNING, "\n"
530 "Your installation contains hash and/or GIN indexes. These indexes have\n"
531 "different internal formats between your old and new clusters, so they\n"
532 "must be reindexed with the REINDEX command. The file:\n"
534 "when executed by psql by the database superuser will recreate all invalid\n"
535 "indexes; until then, none of these indexes will be used.\n\n",
544 * old_8_3_invalidate_bpchar_pattern_ops_indexes()
546 * 8.4 bpchar_pattern_ops no longer sorts based on trailing spaces
549 old_8_3_invalidate_bpchar_pattern_ops_indexes(ClusterInfo *cluster,
555 char output_path[MAXPGPATH];
557 prep_status("Checking for bpchar_pattern_ops indexes");
559 snprintf(output_path, sizeof(output_path), "%s/reindex_bpchar_ops.sql",
562 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
565 bool db_used = false;
570 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
571 PGconn *conn = connectToServer(cluster, active_db->db_name);
573 /* find bpchar_pattern_ops indexes */
576 * Do only non-hash, non-gin indexees; we already invalidated them
577 * above; no need to reindex twice
579 res = executeQueryOrDie(conn,
580 "SELECT n.nspname, c.relname "
581 "FROM pg_catalog.pg_index i, "
582 " pg_catalog.pg_class c, "
583 " pg_catalog.pg_namespace n "
584 "WHERE indexrelid = c.oid AND "
585 " c.relnamespace = n.oid AND "
588 " FROM pg_catalog.pg_opclass o, "
589 " pg_catalog.pg_am a"
590 " WHERE a.amname NOT IN ('hash', 'gin') AND "
591 " a.oid = o.opcmethod AND "
592 " o.opcname = 'bpchar_pattern_ops') "
593 " = ANY (i.indclass) AND "
594 SKIP_TSVECTOR_TABLES);
596 ntups = PQntuples(res);
597 i_nspname = PQfnumber(res, "nspname");
598 i_relname = PQfnumber(res, "relname");
599 for (rowno = 0; rowno < ntups; rowno++)
604 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
605 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
608 fprintf(script, "\\connect %s\n",
609 quote_identifier(active_db->db_name));
612 fprintf(script, "REINDEX INDEX %s.%s;\n",
613 quote_identifier(PQgetvalue(res, rowno, i_nspname)),
614 quote_identifier(PQgetvalue(res, rowno, i_relname)));
620 if (!check_mode && found)
621 /* mark bpchar_pattern_ops indexes as invalid */
622 PQclear(executeQueryOrDie(conn,
623 "UPDATE pg_catalog.pg_index i "
624 "SET indisvalid = false "
625 "FROM pg_catalog.pg_class c, "
626 " pg_catalog.pg_namespace n "
627 "WHERE indexrelid = c.oid AND "
628 " c.relnamespace = n.oid AND "
631 " FROM pg_catalog.pg_opclass o, "
632 " pg_catalog.pg_am a"
633 " WHERE a.amname NOT IN ('hash', 'gin') AND "
634 " a.oid = o.opcmethod AND "
635 " o.opcname = 'bpchar_pattern_ops') "
636 " = ANY (i.indclass)"));
646 report_status(PG_WARNING, "warning");
648 pg_log(PG_WARNING, "\n"
649 "Your installation contains indexes using \"bpchar_pattern_ops\". These\n"
650 "indexes have different internal formats between your old and new clusters\n"
651 "so they must be reindexed with the REINDEX command. After upgrading, you\n"
652 "will be given REINDEX instructions.\n\n");
654 pg_log(PG_WARNING, "\n"
655 "Your installation contains indexes using \"bpchar_pattern_ops\". These\n"
656 "indexes have different internal formats between your old and new clusters\n"
657 "so they must be reindexed with the REINDEX command. The file:\n"
659 "when executed by psql by the database superuser will recreate all invalid\n"
660 "indexes; until then, none of these indexes will be used.\n\n",
669 * old_8_3_create_sequence_script()
671 * 8.4 added the column "start_value" to all sequences. For this reason,
672 * we don't transfer sequence files but instead use the CREATE SEQUENCE
673 * command from the schema dump, and use setval() to restore the sequence
674 * value and 'is_called' from the old database. This is safe to run
675 * by pg_upgrade because sequence files are not transfered from the old
676 * server, even in link mode.
679 old_8_3_create_sequence_script(ClusterInfo *cluster)
684 char *output_path = pg_malloc(MAXPGPATH);
686 snprintf(output_path, MAXPGPATH, "%s/adjust_sequences.sql", os_info.cwd);
688 prep_status("Creating script to adjust sequences");
690 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
693 bool db_used = false;
698 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
699 PGconn *conn = connectToServer(cluster, active_db->db_name);
701 /* Find any sequences */
702 res = executeQueryOrDie(conn,
703 "SELECT n.nspname, c.relname "
704 "FROM pg_catalog.pg_class c, "
705 " pg_catalog.pg_namespace n "
706 "WHERE c.relkind = 'S' AND "
707 " c.relnamespace = n.oid AND "
708 /* exclude possible orphaned temp tables */
709 " n.nspname !~ '^pg_temp_' AND "
710 " n.nspname !~ '^pg_toast_temp_' AND "
711 " n.nspname NOT IN ('pg_catalog', 'information_schema')");
713 ntups = PQntuples(res);
714 i_nspname = PQfnumber(res, "nspname");
715 i_relname = PQfnumber(res, "relname");
716 for (rowno = 0; rowno < ntups; rowno++)
721 const char *nspname = PQgetvalue(res, rowno, i_nspname);
722 const char *relname = PQgetvalue(res, rowno, i_relname);
726 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
727 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
730 fprintf(script, "\\connect %s\n\n",
731 quote_identifier(active_db->db_name));
735 /* Find the desired sequence */
736 seq_res = executeQueryOrDie(conn,
737 "SELECT s.last_value, s.is_called "
739 quote_identifier(nspname),
740 quote_identifier(relname));
742 assert(PQntuples(seq_res) == 1);
743 i_last_value = PQfnumber(seq_res, "last_value");
744 i_is_called = PQfnumber(seq_res, "is_called");
746 fprintf(script, "SELECT setval('%s.%s', %s, '%s');\n",
747 quote_identifier(nspname), quote_identifier(relname),
748 PQgetvalue(seq_res, 0, i_last_value), PQgetvalue(seq_res, 0, i_is_called));
752 fprintf(script, "\n");
768 pg_free(output_path);