4 * Postgres-version-specific routines
6 * Copyright (c) 2010, PostgreSQL Global Development Group
7 * $PostgreSQL: pgsql/contrib/pg_upgrade/version_old_8_3.c,v 1.6 2010/07/03 16:33:14 momjian Exp $
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(migratorContext *ctx, Cluster whichCluster)
24 ClusterInfo *active_cluster = (whichCluster == CLUSTER_OLD) ?
25 &ctx->old : &ctx->new;
29 char output_path[MAXPGPATH];
31 prep_status(ctx, "Checking for invalid 'name' user columns");
33 snprintf(output_path, sizeof(output_path), "%s/tables_using_name.txt",
36 for (dbnum = 0; dbnum < active_cluster->dbarr.ndbs; dbnum++)
45 DbInfo *active_db = &active_cluster->dbarr.dbs[dbnum];
46 PGconn *conn = connectToServer(ctx, active_db->db_name, whichCluster);
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(ctx, 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 " n.nspname != 'pg_catalog' AND "
65 " n.nspname != 'information_schema'");
67 ntups = PQntuples(res);
68 i_nspname = PQfnumber(res, "nspname");
69 i_relname = PQfnumber(res, "relname");
70 i_attname = PQfnumber(res, "attname");
71 for (rowno = 0; rowno < ntups; rowno++)
74 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
75 pg_log(ctx, PG_FATAL, "Could not create necessary file: %s\n", output_path);
78 fprintf(script, "Database: %s\n", active_db->db_name);
81 fprintf(script, " %s.%s.%s\n",
82 PQgetvalue(res, rowno, i_nspname),
83 PQgetvalue(res, rowno, i_relname),
84 PQgetvalue(res, rowno, i_attname));
95 pg_log(ctx, PG_REPORT, "fatal\n");
97 "| Your installation uses 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 migration.\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 migration of such fields is impossible.
117 old_8_3_check_for_tsquery_usage(migratorContext *ctx, Cluster whichCluster)
119 ClusterInfo *active_cluster = (whichCluster == CLUSTER_OLD) ?
120 &ctx->old : &ctx->new;
124 char output_path[MAXPGPATH];
126 prep_status(ctx, "Checking for tsquery user columns");
128 snprintf(output_path, sizeof(output_path), "%s/tables_using_tsquery.txt",
131 for (dbnum = 0; dbnum < active_cluster->dbarr.ndbs; dbnum++)
134 bool db_used = false;
140 DbInfo *active_db = &active_cluster->dbarr.dbs[dbnum];
141 PGconn *conn = connectToServer(ctx, active_db->db_name, whichCluster);
143 /* Find any user-defined tsquery columns */
144 res = executeQueryOrDie(ctx, conn,
145 "SELECT n.nspname, c.relname, a.attname "
146 "FROM pg_catalog.pg_class c, "
147 " pg_catalog.pg_namespace n, "
148 " pg_catalog.pg_attribute a "
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 " n.nspname != 'pg_catalog' AND "
155 " n.nspname != 'information_schema'");
157 ntups = PQntuples(res);
158 i_nspname = PQfnumber(res, "nspname");
159 i_relname = PQfnumber(res, "relname");
160 i_attname = PQfnumber(res, "attname");
161 for (rowno = 0; rowno < ntups; rowno++)
164 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
165 pg_log(ctx, PG_FATAL, "Could not create necessary file: %s\n", output_path);
168 fprintf(script, "Database: %s\n", active_db->db_name);
171 fprintf(script, " %s.%s.%s\n",
172 PQgetvalue(res, rowno, i_nspname),
173 PQgetvalue(res, rowno, i_relname),
174 PQgetvalue(res, rowno, i_attname));
185 pg_log(ctx, PG_REPORT, "fatal\n");
186 pg_log(ctx, PG_FATAL,
187 "| Your installation uses 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 migration. A list of the\n"
192 "| problem columns is in the file:\n"
193 "| \t%s\n\n", output_path);
201 * old_8_3_check_for_isn_and_int8_passing_mismatch()
203 * /contrib/isn relies on data type int8, and in 8.4 int8 is now passed
204 * by value. The schema dumps the CREATE TYPE PASSEDBYVALUE setting so
205 * it must match for the old and new servers.
208 old_8_3_check_for_isn_and_int8_passing_mismatch(migratorContext *ctx, Cluster whichCluster)
210 ClusterInfo *active_cluster = (whichCluster == CLUSTER_OLD) ?
211 &ctx->old : &ctx->new;
215 char output_path[MAXPGPATH];
217 prep_status(ctx, "Checking for /contrib/isn with bigint-passing mismatch");
219 if (ctx->old.controldata.float8_pass_by_value ==
220 ctx->new.controldata.float8_pass_by_value)
227 snprintf(output_path, sizeof(output_path), "%s/contrib_isn_and_int8_pass_by_value.txt",
230 for (dbnum = 0; dbnum < active_cluster->dbarr.ndbs; dbnum++)
233 bool db_used = false;
238 DbInfo *active_db = &active_cluster->dbarr.dbs[dbnum];
239 PGconn *conn = connectToServer(ctx, active_db->db_name, whichCluster);
241 /* Find any functions coming from contrib/isn */
242 res = executeQueryOrDie(ctx, conn,
243 "SELECT n.nspname, p.proname "
244 "FROM pg_catalog.pg_proc p, "
245 " pg_catalog.pg_namespace n "
246 "WHERE p.pronamespace = n.oid AND "
247 " p.probin = '$libdir/isn'");
249 ntups = PQntuples(res);
250 i_nspname = PQfnumber(res, "nspname");
251 i_proname = PQfnumber(res, "proname");
252 for (rowno = 0; rowno < ntups; rowno++)
255 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
256 pg_log(ctx, PG_FATAL, "Could not create necessary file: %s\n", output_path);
259 fprintf(script, "Database: %s\n", active_db->db_name);
262 fprintf(script, " %s.%s\n",
263 PQgetvalue(res, rowno, i_nspname),
264 PQgetvalue(res, rowno, i_proname));
275 pg_log(ctx, PG_REPORT, "fatal\n");
276 pg_log(ctx, PG_FATAL,
277 "| Your installation uses \"/contrib/isn\" functions\n"
278 "| which rely on the bigint data type. Your old and\n"
279 "| new clusters pass bigint values differently so this\n"
280 "| cluster cannot currently be upgraded. You can\n"
281 "| manually migrate data that use \"/contrib/isn\"\n"
282 "| facilities and remove \"/contrib/isn\" from the\n"
283 "| old cluster and restart the migration. A list\n"
284 "| of the problem functions is in the file:\n"
285 "| \t%s\n\n", output_path);
293 * old_8_3_rebuild_tsvector_tables()
295 * 8.3 sorts lexemes by its length and if lengths are the same then it uses
296 * alphabetic order; 8.4 sorts lexemes in lexicographical order, e.g.
298 * => SELECT 'c bb aaa'::tsvector;
301 * 'aaa' 'bb' 'c' -- 8.4
302 * 'c' 'bb' 'aaa' -- 8.3
305 old_8_3_rebuild_tsvector_tables(migratorContext *ctx, bool check_mode,
306 Cluster whichCluster)
308 ClusterInfo *active_cluster = (whichCluster == CLUSTER_OLD) ?
309 &ctx->old : &ctx->new;
313 char output_path[MAXPGPATH];
315 prep_status(ctx, "Checking for tsvector user columns");
317 snprintf(output_path, sizeof(output_path), "%s/rebuild_tsvector_tables.sql",
320 for (dbnum = 0; dbnum < active_cluster->dbarr.ndbs; dbnum++)
323 bool db_used = false;
324 char old_nspname[NAMEDATALEN] = "",
325 old_relname[NAMEDATALEN] = "";
331 DbInfo *active_db = &active_cluster->dbarr.dbs[dbnum];
332 PGconn *conn = connectToServer(ctx, active_db->db_name, whichCluster);
334 /* Find any user-defined tsvector columns */
335 res = executeQueryOrDie(ctx, conn,
336 "SELECT n.nspname, c.relname, a.attname "
337 "FROM pg_catalog.pg_class c, "
338 " pg_catalog.pg_namespace n, "
339 " pg_catalog.pg_attribute a "
340 "WHERE c.relkind = 'r' AND "
341 " c.oid = a.attrelid AND "
342 " NOT a.attisdropped AND "
343 " a.atttypid = 'pg_catalog.tsvector'::pg_catalog.regtype AND "
344 " c.relnamespace = n.oid AND "
345 " n.nspname != 'pg_catalog' AND "
346 " n.nspname != 'information_schema'");
349 * This macro is used below to avoid reindexing indexes already rebuilt
350 * because of tsvector columns.
352 #define SKIP_TSVECTOR_TABLES \
353 "i.indrelid NOT IN ( " \
354 "SELECT DISTINCT c.oid " \
355 "FROM pg_catalog.pg_class c, " \
356 " pg_catalog.pg_namespace n, " \
357 " pg_catalog.pg_attribute a " \
358 "WHERE c.relkind = 'r' AND " \
359 " c.oid = a.attrelid AND " \
360 " NOT a.attisdropped AND " \
361 " a.atttypid = 'pg_catalog.tsvector'::pg_catalog.regtype AND " \
362 " c.relnamespace = n.oid AND " \
363 " n.nspname != 'pg_catalog' AND " \
364 " n.nspname != 'information_schema') "
366 ntups = PQntuples(res);
367 i_nspname = PQfnumber(res, "nspname");
368 i_relname = PQfnumber(res, "relname");
369 i_attname = PQfnumber(res, "attname");
370 for (rowno = 0; rowno < ntups; rowno++)
375 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
376 pg_log(ctx, PG_FATAL, "Could not create necessary file: %s\n", output_path);
379 fprintf(script, "\\connect %s\n\n",
380 quote_identifier(ctx, active_db->db_name));
384 /* Rebuild all tsvector collumns with one ALTER TABLE command */
385 if (strcmp(PQgetvalue(res, rowno, i_nspname), old_nspname) != 0 ||
386 strcmp(PQgetvalue(res, rowno, i_relname), old_relname) != 0)
388 if (strlen(old_nspname) != 0 || strlen(old_relname) != 0)
389 fprintf(script, ";\n\n");
390 fprintf(script, "ALTER TABLE %s.%s\n",
391 quote_identifier(ctx, PQgetvalue(res, rowno, i_nspname)),
392 quote_identifier(ctx, PQgetvalue(res, rowno, i_relname)));
395 fprintf(script, ",\n");
396 strlcpy(old_nspname, PQgetvalue(res, rowno, i_nspname), sizeof(old_nspname));
397 strlcpy(old_relname, PQgetvalue(res, rowno, i_relname), sizeof(old_relname));
399 fprintf(script, "ALTER COLUMN %s "
400 /* This could have been a custom conversion function call. */
401 "TYPE pg_catalog.tsvector USING %s::pg_catalog.text::pg_catalog.tsvector",
402 quote_identifier(ctx, PQgetvalue(res, rowno, i_attname)),
403 quote_identifier(ctx, PQgetvalue(res, rowno, i_attname)));
406 if (strlen(old_nspname) != 0 || strlen(old_relname) != 0)
407 fprintf(script, ";\n\n");
411 /* XXX Mark tables as not accessable somehow */
420 report_status(ctx, PG_WARNING, "warning");
422 pg_log(ctx, PG_WARNING, "\n"
423 "| Your installation contains tsvector columns.\n"
424 "| The tsvector internal storage format changed\n"
425 "| between your old and new clusters so the tables\n"
426 "| must be rebuilt. After migration, you will be\n"
427 "| given instructions.\n\n");
429 pg_log(ctx, PG_WARNING, "\n"
430 "| Your installation contains tsvector columns.\n"
431 "| The tsvector internal storage format changed\n"
432 "| between your old and new clusters so the tables\n"
433 "| must be rebuilt. The file:\n"
435 "| when executed by psql by the database super-user\n"
436 "| will rebuild all tables with tsvector columns.\n\n",
445 * old_8_3_invalidate_hash_gin_indexes()
447 * Hash, Gin, and GiST index binary format has changes from 8.3->8.4
450 old_8_3_invalidate_hash_gin_indexes(migratorContext *ctx, bool check_mode,
451 Cluster whichCluster)
453 ClusterInfo *active_cluster = (whichCluster == CLUSTER_OLD) ?
454 &ctx->old : &ctx->new;
458 char output_path[MAXPGPATH];
460 prep_status(ctx, "Checking for hash and gin indexes");
462 snprintf(output_path, sizeof(output_path), "%s/reindex_hash_and_gin.sql",
465 for (dbnum = 0; dbnum < active_cluster->dbarr.ndbs; dbnum++)
468 bool db_used = false;
473 DbInfo *active_db = &active_cluster->dbarr.dbs[dbnum];
474 PGconn *conn = connectToServer(ctx, active_db->db_name, whichCluster);
476 /* find hash and gin indexes */
477 res = executeQueryOrDie(ctx, conn,
478 "SELECT n.nspname, c.relname "
479 "FROM pg_catalog.pg_class c, "
480 " pg_catalog.pg_index i, "
481 " pg_catalog.pg_am a, "
482 " pg_catalog.pg_namespace n "
483 "WHERE i.indexrelid = c.oid AND "
484 " c.relam = a.oid AND "
485 " c.relnamespace = n.oid AND "
486 " a.amname IN ('hash', 'gin') AND "
487 SKIP_TSVECTOR_TABLES);
489 ntups = PQntuples(res);
490 i_nspname = PQfnumber(res, "nspname");
491 i_relname = PQfnumber(res, "relname");
492 for (rowno = 0; rowno < ntups; rowno++)
497 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
498 pg_log(ctx, PG_FATAL, "Could not create necessary file: %s\n", output_path);
501 fprintf(script, "\\connect %s\n",
502 quote_identifier(ctx, active_db->db_name));
505 fprintf(script, "REINDEX INDEX %s.%s;\n",
506 quote_identifier(ctx, PQgetvalue(res, rowno, i_nspname)),
507 quote_identifier(ctx, PQgetvalue(res, rowno, i_relname)));
513 if (!check_mode && found)
514 /* mark hash and gin indexes as invalid */
515 PQclear(executeQueryOrDie(ctx, conn,
516 "UPDATE pg_catalog.pg_index i "
517 "SET indisvalid = false "
518 "FROM pg_catalog.pg_class c, "
519 " pg_catalog.pg_am a, "
520 " pg_catalog.pg_namespace n "
521 "WHERE i.indexrelid = c.oid AND "
522 " c.relam = a.oid AND "
523 " c.relnamespace = n.oid AND "
524 " a.amname IN ('hash', 'gin')"));
533 report_status(ctx, PG_WARNING, "warning");
535 pg_log(ctx, PG_WARNING, "\n"
536 "| Your installation contains hash and/or gin\n"
537 "| indexes. These indexes have different\n"
538 "| internal formats between your old and new\n"
539 "| clusters so they must be reindexed with the\n"
540 "| REINDEX command. After migration, you will\n"
541 "| be given REINDEX instructions.\n\n");
543 pg_log(ctx, PG_WARNING, "\n"
544 "| Your installation contains hash and/or gin\n"
545 "| indexes. These indexes have different internal\n"
546 "| formats between your old and new clusters so\n"
547 "| they must be reindexed with the REINDEX command.\n"
550 "| when executed by psql by the database super-user\n"
551 "| will recreate all invalid indexes; until then,\n"
552 "| none of these indexes will be used.\n\n",
561 * old_8_3_invalidate_bpchar_pattern_ops_indexes()
563 * 8.4 bpchar_pattern_ops no longer sorts based on trailing spaces
566 old_8_3_invalidate_bpchar_pattern_ops_indexes(migratorContext *ctx, bool check_mode,
567 Cluster whichCluster)
569 ClusterInfo *active_cluster = (whichCluster == CLUSTER_OLD) ?
570 &ctx->old : &ctx->new;
574 char output_path[MAXPGPATH];
576 prep_status(ctx, "Checking for bpchar_pattern_ops indexes");
578 snprintf(output_path, sizeof(output_path), "%s/reindex_bpchar_ops.sql",
581 for (dbnum = 0; dbnum < active_cluster->dbarr.ndbs; dbnum++)
584 bool db_used = false;
589 DbInfo *active_db = &active_cluster->dbarr.dbs[dbnum];
590 PGconn *conn = connectToServer(ctx, active_db->db_name, whichCluster);
592 /* find bpchar_pattern_ops indexes */
595 * Do only non-hash, non-gin indexees; we already invalidated them
596 * above; no need to reindex twice
598 res = executeQueryOrDie(ctx, conn,
599 "SELECT n.nspname, c.relname "
600 "FROM pg_catalog.pg_index i, "
601 " pg_catalog.pg_class c, "
602 " pg_catalog.pg_namespace n "
603 "WHERE indexrelid = c.oid AND "
604 " c.relnamespace = n.oid AND "
607 " FROM pg_catalog.pg_opclass o, "
608 " pg_catalog.pg_am a"
609 " WHERE a.amname NOT IN ('hash', 'gin') AND "
610 " a.oid = o.opcmethod AND "
611 " o.opcname = 'bpchar_pattern_ops') "
612 " = ANY (i.indclass) AND "
613 SKIP_TSVECTOR_TABLES);
615 ntups = PQntuples(res);
616 i_nspname = PQfnumber(res, "nspname");
617 i_relname = PQfnumber(res, "relname");
618 for (rowno = 0; rowno < ntups; rowno++)
623 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
624 pg_log(ctx, PG_FATAL, "Could not create necessary file: %s\n", output_path);
627 fprintf(script, "\\connect %s\n",
628 quote_identifier(ctx, active_db->db_name));
631 fprintf(script, "REINDEX INDEX %s.%s;\n",
632 quote_identifier(ctx, PQgetvalue(res, rowno, i_nspname)),
633 quote_identifier(ctx, PQgetvalue(res, rowno, i_relname)));
639 if (!check_mode && found)
640 /* mark bpchar_pattern_ops indexes as invalid */
641 PQclear(executeQueryOrDie(ctx, conn,
642 "UPDATE pg_catalog.pg_index i "
643 "SET indisvalid = false "
644 "FROM pg_catalog.pg_class c, "
645 " pg_catalog.pg_namespace n "
646 "WHERE indexrelid = c.oid AND "
647 " c.relnamespace = n.oid AND "
650 " FROM pg_catalog.pg_opclass o, "
651 " pg_catalog.pg_am a"
652 " WHERE a.amname NOT IN ('hash', 'gin') AND "
653 " a.oid = o.opcmethod AND "
654 " o.opcname = 'bpchar_pattern_ops') "
655 " = ANY (i.indclass)"));
664 report_status(ctx, PG_WARNING, "warning");
666 pg_log(ctx, PG_WARNING, "\n"
667 "| Your installation contains indexes using\n"
668 "| \"bpchar_pattern_ops\". These indexes have\n"
669 "| different internal formats between your old and\n"
670 "| new clusters so they must be reindexed with the\n"
671 "| REINDEX command. After migration, you will be\n"
672 "| given REINDEX instructions.\n\n");
674 pg_log(ctx, PG_WARNING, "\n"
675 "| Your installation contains indexes using\n"
676 "| \"bpchar_pattern_ops\". These indexes have\n"
677 "| different internal formats between your old and\n"
678 "| new clusters so they must be reindexed with the\n"
679 "| REINDEX command. The file:\n"
681 "| when executed by psql by the database super-user\n"
682 "| will recreate all invalid indexes; until then,\n"
683 "| none of these indexes will be used.\n\n",
692 * old_8_3_create_sequence_script()
694 * 8.4 added the column "start_value" to all sequences. For this reason,
695 * we don't transfer sequence files but instead use the CREATE SEQUENCE
696 * command from the schema dump, and use setval() to restore the sequence
697 * value and 'is_called' from the old database. This is safe to run
698 * by pg_upgrade because sequence files are not transfered from the old
699 * server, even in link mode.
702 old_8_3_create_sequence_script(migratorContext *ctx, Cluster whichCluster)
704 ClusterInfo *active_cluster = (whichCluster == CLUSTER_OLD) ?
705 &ctx->old : &ctx->new;
709 char *output_path = pg_malloc(ctx, MAXPGPATH);
711 snprintf(output_path, MAXPGPATH, "%s/adjust_sequences.sql", ctx->cwd);
713 prep_status(ctx, "Creating script to adjust sequences");
715 for (dbnum = 0; dbnum < active_cluster->dbarr.ndbs; dbnum++)
718 bool db_used = false;
723 DbInfo *active_db = &active_cluster->dbarr.dbs[dbnum];
724 PGconn *conn = connectToServer(ctx, active_db->db_name, whichCluster);
726 /* Find any sequences */
727 res = executeQueryOrDie(ctx, conn,
728 "SELECT n.nspname, c.relname "
729 "FROM pg_catalog.pg_class c, "
730 " pg_catalog.pg_namespace n "
731 "WHERE c.relkind = 'S' AND "
732 " c.relnamespace = n.oid AND "
733 " n.nspname != 'pg_catalog' AND "
734 " n.nspname != 'information_schema'");
736 ntups = PQntuples(res);
737 i_nspname = PQfnumber(res, "nspname");
738 i_relname = PQfnumber(res, "relname");
739 for (rowno = 0; rowno < ntups; rowno++)
744 const char *nspname = PQgetvalue(res, rowno, i_nspname);
745 const char *relname = PQgetvalue(res, rowno, i_relname);
749 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
750 pg_log(ctx, PG_FATAL, "Could not create necessary file: %s\n", output_path);
753 fprintf(script, "\\connect %s\n\n",
754 quote_identifier(ctx, active_db->db_name));
758 /* Find the desired sequence */
759 seq_res = executeQueryOrDie(ctx, conn,
760 "SELECT s.last_value, s.is_called "
762 quote_identifier(ctx, nspname),
763 quote_identifier(ctx, relname));
765 assert(PQntuples(seq_res) == 1);
766 i_last_value = PQfnumber(seq_res, "last_value");
767 i_is_called = PQfnumber(seq_res, "is_called");
769 fprintf(script, "SELECT setval('%s.%s', %s, '%s');\n",
770 quote_identifier(ctx, nspname), quote_identifier(ctx, relname),
771 PQgetvalue(seq_res, 0, i_last_value), PQgetvalue(seq_res, 0, i_is_called));
775 fprintf(script, "\n");
790 pg_free(output_path);