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 open file \"%s\": %s\n", output_path, getErrorText(errno));
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 user tables. This\n"
98 "data type changed its internal alignment between your old and new\n"
99 "clusters so this cluster cannot currently be upgraded. You can remove\n"
100 "the problem tables and restart the upgrade. A list of the problem\n"
101 "columns is in the file:\n"
102 " %s\n\n", output_path);
110 * old_8_3_check_for_tsquery_usage()
112 * A new 'prefix' field was added to the 'tsquery' data type in 8.4
113 * so upgrading of such fields is impossible.
116 old_8_3_check_for_tsquery_usage(ClusterInfo *cluster)
121 char output_path[MAXPGPATH];
123 prep_status("Checking for tsquery user columns");
125 snprintf(output_path, sizeof(output_path), "%s/tables_using_tsquery.txt",
128 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
131 bool db_used = false;
137 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
138 PGconn *conn = connectToServer(cluster, active_db->db_name);
140 /* Find any user-defined tsquery columns */
141 res = executeQueryOrDie(conn,
142 "SELECT n.nspname, c.relname, a.attname "
143 "FROM pg_catalog.pg_class c, "
144 " pg_catalog.pg_namespace n, "
145 " pg_catalog.pg_attribute a "
146 "WHERE c.relkind = 'r' AND "
147 " c.oid = a.attrelid AND "
148 " NOT a.attisdropped AND "
149 " a.atttypid = 'pg_catalog.tsquery'::pg_catalog.regtype AND "
150 " c.relnamespace = n.oid AND "
151 " n.nspname != 'pg_catalog' AND "
152 " n.nspname != 'information_schema'");
154 ntups = PQntuples(res);
155 i_nspname = PQfnumber(res, "nspname");
156 i_relname = PQfnumber(res, "relname");
157 i_attname = PQfnumber(res, "attname");
158 for (rowno = 0; rowno < ntups; rowno++)
161 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
162 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
165 fprintf(script, "Database: %s\n", active_db->db_name);
168 fprintf(script, " %s.%s.%s\n",
169 PQgetvalue(res, rowno, i_nspname),
170 PQgetvalue(res, rowno, i_relname),
171 PQgetvalue(res, rowno, i_attname));
184 pg_log(PG_REPORT, "fatal\n");
186 "Your installation contains the \"tsquery\" data type. This data type\n"
187 "added a new internal field between your old and new clusters so this\n"
188 "cluster cannot currently be upgraded. You can remove the problem\n"
189 "columns and restart the upgrade. A list of the problem columns is in the\n"
191 " %s\n\n", output_path);
199 * old_8_3_rebuild_tsvector_tables()
201 * 8.3 sorts lexemes by its length and if lengths are the same then it uses
202 * alphabetic order; 8.4 sorts lexemes in lexicographical order, e.g.
204 * => SELECT 'c bb aaa'::tsvector;
207 * 'aaa' 'bb' 'c' -- 8.4
208 * 'c' 'bb' 'aaa' -- 8.3
211 old_8_3_rebuild_tsvector_tables(ClusterInfo *cluster, bool check_mode)
216 char output_path[MAXPGPATH];
218 prep_status("Checking for tsvector user columns");
220 snprintf(output_path, sizeof(output_path), "%s/rebuild_tsvector_tables.sql",
223 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
226 bool db_used = false;
227 char nspname[NAMEDATALEN] = "",
228 relname[NAMEDATALEN] = "";
234 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
235 PGconn *conn = connectToServer(cluster, active_db->db_name);
237 /* Find any user-defined tsvector columns */
238 res = executeQueryOrDie(conn,
239 "SELECT n.nspname, c.relname, a.attname "
240 "FROM pg_catalog.pg_class c, "
241 " pg_catalog.pg_namespace n, "
242 " pg_catalog.pg_attribute a "
243 "WHERE c.relkind = 'r' AND "
244 " c.oid = a.attrelid AND "
245 " NOT a.attisdropped AND "
246 " a.atttypid = 'pg_catalog.tsvector'::pg_catalog.regtype AND "
247 " c.relnamespace = n.oid AND "
248 " n.nspname != 'pg_catalog' AND "
249 " n.nspname != 'information_schema'");
252 * This macro is used below to avoid reindexing indexes already rebuilt
253 * because of tsvector columns.
255 #define SKIP_TSVECTOR_TABLES \
256 "i.indrelid NOT IN ( " \
257 "SELECT DISTINCT c.oid " \
258 "FROM pg_catalog.pg_class c, " \
259 " pg_catalog.pg_namespace n, " \
260 " pg_catalog.pg_attribute a " \
261 "WHERE c.relkind = 'r' AND " \
262 " c.oid = a.attrelid AND " \
263 " NOT a.attisdropped AND " \
264 " a.atttypid = 'pg_catalog.tsvector'::pg_catalog.regtype AND " \
265 " c.relnamespace = n.oid AND " \
266 " n.nspname != 'pg_catalog' AND " \
267 " n.nspname != 'information_schema') "
269 ntups = PQntuples(res);
270 i_nspname = PQfnumber(res, "nspname");
271 i_relname = PQfnumber(res, "relname");
272 i_attname = PQfnumber(res, "attname");
273 for (rowno = 0; rowno < ntups; rowno++)
278 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
279 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
282 fprintf(script, "\\connect %s\n\n",
283 quote_identifier(active_db->db_name));
287 /* Rebuild all tsvector collumns with one ALTER TABLE command */
288 if (strcmp(PQgetvalue(res, rowno, i_nspname), nspname) != 0 ||
289 strcmp(PQgetvalue(res, rowno, i_relname), relname) != 0)
291 if (strlen(nspname) != 0 || strlen(relname) != 0)
292 fprintf(script, ";\n\n");
293 fprintf(script, "ALTER TABLE %s.%s\n",
294 quote_identifier(PQgetvalue(res, rowno, i_nspname)),
295 quote_identifier(PQgetvalue(res, rowno, i_relname)));
298 fprintf(script, ",\n");
299 strlcpy(nspname, PQgetvalue(res, rowno, i_nspname), sizeof(nspname));
300 strlcpy(relname, PQgetvalue(res, rowno, i_relname), sizeof(relname));
302 fprintf(script, "ALTER COLUMN %s "
303 /* This could have been a custom conversion function call. */
304 "TYPE pg_catalog.tsvector USING %s::pg_catalog.text::pg_catalog.tsvector",
305 quote_identifier(PQgetvalue(res, rowno, i_attname)),
306 quote_identifier(PQgetvalue(res, rowno, i_attname)));
309 if (strlen(nspname) != 0 || strlen(relname) != 0)
310 fprintf(script, ";\n\n");
314 /* XXX Mark tables as not accessable somehow */
324 report_status(PG_WARNING, "warning");
326 pg_log(PG_WARNING, "\n"
327 "Your installation contains tsvector columns. The tsvector internal\n"
328 "storage format changed between your old and new clusters so the tables\n"
329 "must be rebuilt. After upgrading, you will be given instructions.\n\n");
331 pg_log(PG_WARNING, "\n"
332 "Your installation contains tsvector columns. The tsvector internal\n"
333 "storage format changed between your old and new clusters so the tables\n"
334 "must be rebuilt. The file:\n"
336 "when executed by psql by the database superuser will rebuild all tables\n"
337 "with tsvector columns.\n\n",
346 * old_8_3_invalidate_hash_gin_indexes()
348 * Hash, Gin, and GiST index binary format has changes from 8.3->8.4
351 old_8_3_invalidate_hash_gin_indexes(ClusterInfo *cluster, bool check_mode)
356 char output_path[MAXPGPATH];
358 prep_status("Checking for hash and GIN indexes");
360 snprintf(output_path, sizeof(output_path), "%s/reindex_hash_and_gin.sql",
363 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
366 bool db_used = false;
371 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
372 PGconn *conn = connectToServer(cluster, active_db->db_name);
374 /* find hash and gin indexes */
375 res = executeQueryOrDie(conn,
376 "SELECT n.nspname, c.relname "
377 "FROM pg_catalog.pg_class c, "
378 " pg_catalog.pg_index i, "
379 " pg_catalog.pg_am a, "
380 " pg_catalog.pg_namespace n "
381 "WHERE i.indexrelid = c.oid AND "
382 " c.relam = a.oid AND "
383 " c.relnamespace = n.oid AND "
384 " a.amname IN ('hash', 'gin') AND "
385 SKIP_TSVECTOR_TABLES);
387 ntups = PQntuples(res);
388 i_nspname = PQfnumber(res, "nspname");
389 i_relname = PQfnumber(res, "relname");
390 for (rowno = 0; rowno < ntups; rowno++)
395 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
396 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
399 fprintf(script, "\\connect %s\n",
400 quote_identifier(active_db->db_name));
403 fprintf(script, "REINDEX INDEX %s.%s;\n",
404 quote_identifier(PQgetvalue(res, rowno, i_nspname)),
405 quote_identifier(PQgetvalue(res, rowno, i_relname)));
411 if (!check_mode && found)
412 /* mark hash and gin indexes as invalid */
413 PQclear(executeQueryOrDie(conn,
414 "UPDATE pg_catalog.pg_index i "
415 "SET indisvalid = false "
416 "FROM pg_catalog.pg_class c, "
417 " pg_catalog.pg_am a, "
418 " pg_catalog.pg_namespace n "
419 "WHERE i.indexrelid = c.oid AND "
420 " c.relam = a.oid AND "
421 " c.relnamespace = n.oid AND "
422 " a.amname IN ('hash', 'gin')"));
432 report_status(PG_WARNING, "warning");
434 pg_log(PG_WARNING, "\n"
435 "Your installation contains hash and/or GIN indexes. These indexes have\n"
436 "different internal formats between your old and new clusters, so they\n"
437 "must be reindexed with the REINDEX command. After upgrading, you will\n"
438 "be given REINDEX instructions.\n\n");
440 pg_log(PG_WARNING, "\n"
441 "Your installation contains hash and/or GIN indexes. These indexes have\n"
442 "different internal formats between your old and new clusters, so they\n"
443 "must be reindexed with the REINDEX command. The file:\n"
445 "when executed by psql by the database superuser will recreate all invalid\n"
446 "indexes; until then, none of these indexes will be used.\n\n",
455 * old_8_3_invalidate_bpchar_pattern_ops_indexes()
457 * 8.4 bpchar_pattern_ops no longer sorts based on trailing spaces
460 old_8_3_invalidate_bpchar_pattern_ops_indexes(ClusterInfo *cluster,
466 char output_path[MAXPGPATH];
468 prep_status("Checking for bpchar_pattern_ops indexes");
470 snprintf(output_path, sizeof(output_path), "%s/reindex_bpchar_ops.sql",
473 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
476 bool db_used = false;
481 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
482 PGconn *conn = connectToServer(cluster, active_db->db_name);
484 /* find bpchar_pattern_ops indexes */
487 * Do only non-hash, non-gin indexees; we already invalidated them
488 * above; no need to reindex twice
490 res = executeQueryOrDie(conn,
491 "SELECT n.nspname, c.relname "
492 "FROM pg_catalog.pg_index i, "
493 " pg_catalog.pg_class c, "
494 " pg_catalog.pg_namespace n "
495 "WHERE indexrelid = c.oid AND "
496 " c.relnamespace = n.oid AND "
499 " FROM pg_catalog.pg_opclass o, "
500 " pg_catalog.pg_am a"
501 " WHERE a.amname NOT IN ('hash', 'gin') AND "
502 " a.oid = o.opcmethod AND "
503 " o.opcname = 'bpchar_pattern_ops') "
504 " = ANY (i.indclass) AND "
505 SKIP_TSVECTOR_TABLES);
507 ntups = PQntuples(res);
508 i_nspname = PQfnumber(res, "nspname");
509 i_relname = PQfnumber(res, "relname");
510 for (rowno = 0; rowno < ntups; rowno++)
515 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
516 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
519 fprintf(script, "\\connect %s\n",
520 quote_identifier(active_db->db_name));
523 fprintf(script, "REINDEX INDEX %s.%s;\n",
524 quote_identifier(PQgetvalue(res, rowno, i_nspname)),
525 quote_identifier(PQgetvalue(res, rowno, i_relname)));
531 if (!check_mode && found)
532 /* mark bpchar_pattern_ops indexes as invalid */
533 PQclear(executeQueryOrDie(conn,
534 "UPDATE pg_catalog.pg_index i "
535 "SET indisvalid = false "
536 "FROM pg_catalog.pg_class c, "
537 " pg_catalog.pg_namespace n "
538 "WHERE indexrelid = c.oid AND "
539 " c.relnamespace = n.oid AND "
542 " FROM pg_catalog.pg_opclass o, "
543 " pg_catalog.pg_am a"
544 " WHERE a.amname NOT IN ('hash', 'gin') AND "
545 " a.oid = o.opcmethod AND "
546 " o.opcname = 'bpchar_pattern_ops') "
547 " = ANY (i.indclass)"));
557 report_status(PG_WARNING, "warning");
559 pg_log(PG_WARNING, "\n"
560 "Your installation contains indexes using \"bpchar_pattern_ops\". These\n"
561 "indexes have different internal formats between your old and new clusters\n"
562 "so they must be reindexed with the REINDEX command. After upgrading, you\n"
563 "will be given REINDEX instructions.\n\n");
565 pg_log(PG_WARNING, "\n"
566 "Your installation contains indexes using \"bpchar_pattern_ops\". These\n"
567 "indexes have different internal formats between your old and new clusters\n"
568 "so they must be reindexed with the REINDEX command. The file:\n"
570 "when executed by psql by the database superuser will recreate all invalid\n"
571 "indexes; until then, none of these indexes will be used.\n\n",
580 * old_8_3_create_sequence_script()
582 * 8.4 added the column "start_value" to all sequences. For this reason,
583 * we don't transfer sequence files but instead use the CREATE SEQUENCE
584 * command from the schema dump, and use setval() to restore the sequence
585 * value and 'is_called' from the old database. This is safe to run
586 * by pg_upgrade because sequence files are not transfered from the old
587 * server, even in link mode.
590 old_8_3_create_sequence_script(ClusterInfo *cluster)
595 char *output_path = pg_malloc(MAXPGPATH);
597 snprintf(output_path, MAXPGPATH, "%s/adjust_sequences.sql", os_info.cwd);
599 prep_status("Creating script to adjust sequences");
601 for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
604 bool db_used = false;
609 DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
610 PGconn *conn = connectToServer(cluster, active_db->db_name);
612 /* Find any sequences */
613 res = executeQueryOrDie(conn,
614 "SELECT n.nspname, c.relname "
615 "FROM pg_catalog.pg_class c, "
616 " pg_catalog.pg_namespace n "
617 "WHERE c.relkind = 'S' AND "
618 " c.relnamespace = n.oid AND "
619 " n.nspname != 'pg_catalog' AND "
620 " n.nspname != 'information_schema'");
622 ntups = PQntuples(res);
623 i_nspname = PQfnumber(res, "nspname");
624 i_relname = PQfnumber(res, "relname");
625 for (rowno = 0; rowno < ntups; rowno++)
630 const char *nspname = PQgetvalue(res, rowno, i_nspname);
631 const char *relname = PQgetvalue(res, rowno, i_relname);
635 if (script == NULL && (script = fopen(output_path, "w")) == NULL)
636 pg_log(PG_FATAL, "could not open file \"%s\": %s\n", output_path, getErrorText(errno));
639 fprintf(script, "\\connect %s\n\n",
640 quote_identifier(active_db->db_name));
644 /* Find the desired sequence */
645 seq_res = executeQueryOrDie(conn,
646 "SELECT s.last_value, s.is_called "
648 quote_identifier(nspname),
649 quote_identifier(relname));
651 assert(PQntuples(seq_res) == 1);
652 i_last_value = PQfnumber(seq_res, "last_value");
653 i_is_called = PQfnumber(seq_res, "is_called");
655 fprintf(script, "SELECT setval('%s.%s', %s, '%s');\n",
656 quote_identifier(nspname), quote_identifier(relname),
657 PQgetvalue(seq_res, 0, i_last_value), PQgetvalue(seq_res, 0, i_is_called));
661 fprintf(script, "\n");
677 pg_free(output_path);