4 * Postgres-version-specific routines
6 * Copyright (c) 2010, 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(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 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 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 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 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_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(migratorContext *ctx, bool check_mode,
214 Cluster whichCluster)
216 ClusterInfo *active_cluster = (whichCluster == CLUSTER_OLD) ?
217 &ctx->old : &ctx->new;
221 char output_path[MAXPGPATH];
223 prep_status(ctx, "Checking for tsvector user columns");
225 snprintf(output_path, sizeof(output_path), "%s/rebuild_tsvector_tables.sql",
228 for (dbnum = 0; dbnum < active_cluster->dbarr.ndbs; dbnum++)
231 bool db_used = false;
232 char old_nspname[NAMEDATALEN] = "",
233 old_relname[NAMEDATALEN] = "";
239 DbInfo *active_db = &active_cluster->dbarr.dbs[dbnum];
240 PGconn *conn = connectToServer(ctx, active_db->db_name, whichCluster);
242 /* Find any user-defined tsvector columns */
243 res = executeQueryOrDie(ctx, conn,
244 "SELECT n.nspname, c.relname, a.attname "
245 "FROM pg_catalog.pg_class c, "
246 " pg_catalog.pg_namespace n, "
247 " pg_catalog.pg_attribute a "
248 "WHERE c.relkind = 'r' AND "
249 " c.oid = a.attrelid AND "
250 " NOT a.attisdropped AND "
251 " a.atttypid = 'pg_catalog.tsvector'::pg_catalog.regtype AND "
252 " c.relnamespace = n.oid AND "
253 " n.nspname != 'pg_catalog' AND "
254 " n.nspname != 'information_schema'");
257 * This macro is used below to avoid reindexing indexes already rebuilt
258 * because of tsvector columns.
260 #define SKIP_TSVECTOR_TABLES \
261 "i.indrelid NOT IN ( " \
262 "SELECT DISTINCT c.oid " \
263 "FROM pg_catalog.pg_class c, " \
264 " pg_catalog.pg_namespace n, " \
265 " pg_catalog.pg_attribute a " \
266 "WHERE c.relkind = 'r' AND " \
267 " c.oid = a.attrelid AND " \
268 " NOT a.attisdropped AND " \
269 " a.atttypid = 'pg_catalog.tsvector'::pg_catalog.regtype AND " \
270 " c.relnamespace = n.oid AND " \
271 " n.nspname != 'pg_catalog' AND " \
272 " n.nspname != 'information_schema') "
274 ntups = PQntuples(res);
275 i_nspname = PQfnumber(res, "nspname");
276 i_relname = PQfnumber(res, "relname");
277 i_attname = PQfnumber(res, "attname");
278 for (rowno = 0; rowno < ntups; rowno++)
283 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
284 pg_log(ctx, PG_FATAL, "Could not create necessary file: %s\n", output_path);
287 fprintf(script, "\\connect %s\n\n",
288 quote_identifier(ctx, active_db->db_name));
292 /* Rebuild all tsvector collumns with one ALTER TABLE command */
293 if (strcmp(PQgetvalue(res, rowno, i_nspname), old_nspname) != 0 ||
294 strcmp(PQgetvalue(res, rowno, i_relname), old_relname) != 0)
296 if (strlen(old_nspname) != 0 || strlen(old_relname) != 0)
297 fprintf(script, ";\n\n");
298 fprintf(script, "ALTER TABLE %s.%s\n",
299 quote_identifier(ctx, PQgetvalue(res, rowno, i_nspname)),
300 quote_identifier(ctx, PQgetvalue(res, rowno, i_relname)));
303 fprintf(script, ",\n");
304 strlcpy(old_nspname, PQgetvalue(res, rowno, i_nspname), sizeof(old_nspname));
305 strlcpy(old_relname, PQgetvalue(res, rowno, i_relname), sizeof(old_relname));
307 fprintf(script, "ALTER COLUMN %s "
308 /* This could have been a custom conversion function call. */
309 "TYPE pg_catalog.tsvector USING %s::pg_catalog.text::pg_catalog.tsvector",
310 quote_identifier(ctx, PQgetvalue(res, rowno, i_attname)),
311 quote_identifier(ctx, PQgetvalue(res, rowno, i_attname)));
314 if (strlen(old_nspname) != 0 || strlen(old_relname) != 0)
315 fprintf(script, ";\n\n");
319 /* XXX Mark tables as not accessable somehow */
328 report_status(ctx, PG_WARNING, "warning");
330 pg_log(ctx, PG_WARNING, "\n"
331 "| Your installation contains tsvector columns.\n"
332 "| The tsvector internal storage format changed\n"
333 "| between your old and new clusters so the tables\n"
334 "| must be rebuilt. After migration, you will be\n"
335 "| given instructions.\n\n");
337 pg_log(ctx, PG_WARNING, "\n"
338 "| Your installation contains tsvector columns.\n"
339 "| The tsvector internal storage format changed\n"
340 "| between your old and new clusters so the tables\n"
341 "| must be rebuilt. The file:\n"
343 "| when executed by psql by the database super-user\n"
344 "| will rebuild all tables with tsvector columns.\n\n",
353 * old_8_3_invalidate_hash_gin_indexes()
355 * Hash, Gin, and GiST index binary format has changes from 8.3->8.4
358 old_8_3_invalidate_hash_gin_indexes(migratorContext *ctx, bool check_mode,
359 Cluster whichCluster)
361 ClusterInfo *active_cluster = (whichCluster == CLUSTER_OLD) ?
362 &ctx->old : &ctx->new;
366 char output_path[MAXPGPATH];
368 prep_status(ctx, "Checking for hash and gin indexes");
370 snprintf(output_path, sizeof(output_path), "%s/reindex_hash_and_gin.sql",
373 for (dbnum = 0; dbnum < active_cluster->dbarr.ndbs; dbnum++)
376 bool db_used = false;
381 DbInfo *active_db = &active_cluster->dbarr.dbs[dbnum];
382 PGconn *conn = connectToServer(ctx, active_db->db_name, whichCluster);
384 /* find hash and gin indexes */
385 res = executeQueryOrDie(ctx, conn,
386 "SELECT n.nspname, c.relname "
387 "FROM pg_catalog.pg_class c, "
388 " pg_catalog.pg_index i, "
389 " pg_catalog.pg_am a, "
390 " pg_catalog.pg_namespace n "
391 "WHERE i.indexrelid = c.oid AND "
392 " c.relam = a.oid AND "
393 " c.relnamespace = n.oid AND "
394 " a.amname IN ('hash', 'gin') AND "
395 SKIP_TSVECTOR_TABLES);
397 ntups = PQntuples(res);
398 i_nspname = PQfnumber(res, "nspname");
399 i_relname = PQfnumber(res, "relname");
400 for (rowno = 0; rowno < ntups; rowno++)
405 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
406 pg_log(ctx, PG_FATAL, "Could not create necessary file: %s\n", output_path);
409 fprintf(script, "\\connect %s\n",
410 quote_identifier(ctx, active_db->db_name));
413 fprintf(script, "REINDEX INDEX %s.%s;\n",
414 quote_identifier(ctx, PQgetvalue(res, rowno, i_nspname)),
415 quote_identifier(ctx, PQgetvalue(res, rowno, i_relname)));
421 if (!check_mode && found)
422 /* mark hash and gin indexes as invalid */
423 PQclear(executeQueryOrDie(ctx, conn,
424 "UPDATE pg_catalog.pg_index i "
425 "SET indisvalid = false "
426 "FROM pg_catalog.pg_class c, "
427 " pg_catalog.pg_am a, "
428 " pg_catalog.pg_namespace n "
429 "WHERE i.indexrelid = c.oid AND "
430 " c.relam = a.oid AND "
431 " c.relnamespace = n.oid AND "
432 " a.amname IN ('hash', 'gin')"));
441 report_status(ctx, PG_WARNING, "warning");
443 pg_log(ctx, PG_WARNING, "\n"
444 "| Your installation contains hash and/or gin\n"
445 "| indexes. These indexes have different\n"
446 "| internal formats between your old and new\n"
447 "| clusters so they must be reindexed with the\n"
448 "| REINDEX command. After migration, you will\n"
449 "| be given REINDEX instructions.\n\n");
451 pg_log(ctx, PG_WARNING, "\n"
452 "| Your installation contains hash and/or gin\n"
453 "| indexes. These indexes have different internal\n"
454 "| formats between your old and new clusters so\n"
455 "| they must be reindexed with the REINDEX command.\n"
458 "| when executed by psql by the database super-user\n"
459 "| will recreate all invalid indexes; until then,\n"
460 "| none of these indexes will be used.\n\n",
469 * old_8_3_invalidate_bpchar_pattern_ops_indexes()
471 * 8.4 bpchar_pattern_ops no longer sorts based on trailing spaces
474 old_8_3_invalidate_bpchar_pattern_ops_indexes(migratorContext *ctx, bool check_mode,
475 Cluster whichCluster)
477 ClusterInfo *active_cluster = (whichCluster == CLUSTER_OLD) ?
478 &ctx->old : &ctx->new;
482 char output_path[MAXPGPATH];
484 prep_status(ctx, "Checking for bpchar_pattern_ops indexes");
486 snprintf(output_path, sizeof(output_path), "%s/reindex_bpchar_ops.sql",
489 for (dbnum = 0; dbnum < active_cluster->dbarr.ndbs; dbnum++)
492 bool db_used = false;
497 DbInfo *active_db = &active_cluster->dbarr.dbs[dbnum];
498 PGconn *conn = connectToServer(ctx, active_db->db_name, whichCluster);
500 /* find bpchar_pattern_ops indexes */
503 * Do only non-hash, non-gin indexees; we already invalidated them
504 * above; no need to reindex twice
506 res = executeQueryOrDie(ctx, conn,
507 "SELECT n.nspname, c.relname "
508 "FROM pg_catalog.pg_index i, "
509 " pg_catalog.pg_class c, "
510 " pg_catalog.pg_namespace n "
511 "WHERE indexrelid = c.oid AND "
512 " c.relnamespace = n.oid AND "
515 " FROM pg_catalog.pg_opclass o, "
516 " pg_catalog.pg_am a"
517 " WHERE a.amname NOT IN ('hash', 'gin') AND "
518 " a.oid = o.opcmethod AND "
519 " o.opcname = 'bpchar_pattern_ops') "
520 " = ANY (i.indclass) AND "
521 SKIP_TSVECTOR_TABLES);
523 ntups = PQntuples(res);
524 i_nspname = PQfnumber(res, "nspname");
525 i_relname = PQfnumber(res, "relname");
526 for (rowno = 0; rowno < ntups; rowno++)
531 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
532 pg_log(ctx, PG_FATAL, "Could not create necessary file: %s\n", output_path);
535 fprintf(script, "\\connect %s\n",
536 quote_identifier(ctx, active_db->db_name));
539 fprintf(script, "REINDEX INDEX %s.%s;\n",
540 quote_identifier(ctx, PQgetvalue(res, rowno, i_nspname)),
541 quote_identifier(ctx, PQgetvalue(res, rowno, i_relname)));
547 if (!check_mode && found)
548 /* mark bpchar_pattern_ops indexes as invalid */
549 PQclear(executeQueryOrDie(ctx, conn,
550 "UPDATE pg_catalog.pg_index i "
551 "SET indisvalid = false "
552 "FROM pg_catalog.pg_class c, "
553 " pg_catalog.pg_namespace n "
554 "WHERE indexrelid = c.oid AND "
555 " c.relnamespace = n.oid AND "
558 " FROM pg_catalog.pg_opclass o, "
559 " pg_catalog.pg_am a"
560 " WHERE a.amname NOT IN ('hash', 'gin') AND "
561 " a.oid = o.opcmethod AND "
562 " o.opcname = 'bpchar_pattern_ops') "
563 " = ANY (i.indclass)"));
572 report_status(ctx, PG_WARNING, "warning");
574 pg_log(ctx, PG_WARNING, "\n"
575 "| Your installation contains indexes using\n"
576 "| \"bpchar_pattern_ops\". These indexes have\n"
577 "| different internal formats between your old and\n"
578 "| new clusters so they must be reindexed with the\n"
579 "| REINDEX command. After migration, you will be\n"
580 "| given REINDEX instructions.\n\n");
582 pg_log(ctx, PG_WARNING, "\n"
583 "| Your installation contains indexes using\n"
584 "| \"bpchar_pattern_ops\". These indexes have\n"
585 "| different internal formats between your old and\n"
586 "| new clusters so they must be reindexed with the\n"
587 "| REINDEX command. The file:\n"
589 "| when executed by psql by the database super-user\n"
590 "| will recreate all invalid indexes; until then,\n"
591 "| none of these indexes will be used.\n\n",
600 * old_8_3_create_sequence_script()
602 * 8.4 added the column "start_value" to all sequences. For this reason,
603 * we don't transfer sequence files but instead use the CREATE SEQUENCE
604 * command from the schema dump, and use setval() to restore the sequence
605 * value and 'is_called' from the old database. This is safe to run
606 * by pg_upgrade because sequence files are not transfered from the old
607 * server, even in link mode.
610 old_8_3_create_sequence_script(migratorContext *ctx, Cluster whichCluster)
612 ClusterInfo *active_cluster = (whichCluster == CLUSTER_OLD) ?
613 &ctx->old : &ctx->new;
617 char *output_path = pg_malloc(ctx, MAXPGPATH);
619 snprintf(output_path, MAXPGPATH, "%s/adjust_sequences.sql", ctx->cwd);
621 prep_status(ctx, "Creating script to adjust sequences");
623 for (dbnum = 0; dbnum < active_cluster->dbarr.ndbs; dbnum++)
626 bool db_used = false;
631 DbInfo *active_db = &active_cluster->dbarr.dbs[dbnum];
632 PGconn *conn = connectToServer(ctx, active_db->db_name, whichCluster);
634 /* Find any sequences */
635 res = executeQueryOrDie(ctx, conn,
636 "SELECT n.nspname, c.relname "
637 "FROM pg_catalog.pg_class c, "
638 " pg_catalog.pg_namespace n "
639 "WHERE c.relkind = 'S' AND "
640 " c.relnamespace = n.oid AND "
641 " n.nspname != 'pg_catalog' AND "
642 " n.nspname != 'information_schema'");
644 ntups = PQntuples(res);
645 i_nspname = PQfnumber(res, "nspname");
646 i_relname = PQfnumber(res, "relname");
647 for (rowno = 0; rowno < ntups; rowno++)
652 const char *nspname = PQgetvalue(res, rowno, i_nspname);
653 const char *relname = PQgetvalue(res, rowno, i_relname);
657 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
658 pg_log(ctx, PG_FATAL, "Could not create necessary file: %s\n", output_path);
661 fprintf(script, "\\connect %s\n\n",
662 quote_identifier(ctx, active_db->db_name));
666 /* Find the desired sequence */
667 seq_res = executeQueryOrDie(ctx, conn,
668 "SELECT s.last_value, s.is_called "
670 quote_identifier(ctx, nspname),
671 quote_identifier(ctx, relname));
673 assert(PQntuples(seq_res) == 1);
674 i_last_value = PQfnumber(seq_res, "last_value");
675 i_is_called = PQfnumber(seq_res, "is_called");
677 fprintf(script, "SELECT setval('%s.%s', %s, '%s');\n",
678 quote_identifier(ctx, nspname), quote_identifier(ctx, relname),
679 PQgetvalue(seq_res, 0, i_last_value), PQgetvalue(seq_res, 0, i_is_called));
683 fprintf(script, "\n");
698 pg_free(output_path);