Merge branch 'master' of git://git.postgresql.org/git/postgresql
[pgsql-fdw.git] / contrib / pg_upgrade / info.c
blobcedfdad5211da34532e62fa39c696baa6f3d85c8
1 /*
2 * info.c
4 * information support functions
6 * Copyright (c) 2010, PostgreSQL Global Development Group
7 * $PostgreSQL$
8 */
10 #include "pg_upgrade.h"
12 #include "access/transam.h"
15 static void get_db_infos(migratorContext *ctx, DbInfoArr *dbinfos,
16 Cluster whichCluster);
17 static void dbarr_print(migratorContext *ctx, DbInfoArr *arr,
18 Cluster whichCluster);
19 static void relarr_print(migratorContext *ctx, RelInfoArr *arr);
20 static void get_rel_infos(migratorContext *ctx, const DbInfo *dbinfo,
21 RelInfoArr *relarr, Cluster whichCluster);
22 static void relarr_free(RelInfoArr *rel_arr);
23 static void map_rel(migratorContext *ctx, const RelInfo *oldrel,
24 const RelInfo *newrel, const DbInfo *old_db,
25 const DbInfo *new_db, const char *olddata,
26 const char *newdata, FileNameMap *map);
27 static void map_rel_by_id(migratorContext *ctx, Oid oldid, Oid newid,
28 const char *old_nspname, const char *old_relname,
29 const char *new_nspname, const char *new_relname,
30 const char *old_tablespace, const DbInfo *old_db,
31 const DbInfo *new_db, const char *olddata,
32 const char *newdata, FileNameMap *map);
33 static RelInfo *relarr_lookup_reloid(migratorContext *ctx,
34 RelInfoArr *rel_arr, Oid oid, Cluster whichCluster);
35 static RelInfo *relarr_lookup_rel(migratorContext *ctx, RelInfoArr *rel_arr,
36 const char *nspname, const char *relname,
37 Cluster whichCluster);
41 * gen_db_file_maps()
43 * generates database mappings for "old_db" and "new_db". Returns a malloc'ed
44 * array of mappings. nmaps is a return parameter which refers to the number
45 * mappings.
47 * NOTE: Its the Caller's responsibility to free the returned array.
49 FileNameMap *
50 gen_db_file_maps(migratorContext *ctx, DbInfo *old_db, DbInfo *new_db,
51 int *nmaps, const char *old_pgdata, const char *new_pgdata)
53 FileNameMap *maps;
54 int relnum;
55 int num_maps = 0;
57 maps = (FileNameMap *) pg_malloc(ctx, sizeof(FileNameMap) *
58 new_db->rel_arr.nrels);
60 for (relnum = 0; relnum < new_db->rel_arr.nrels; relnum++)
62 RelInfo *newrel = &new_db->rel_arr.rels[relnum];
63 RelInfo *oldrel;
65 /* toast tables are handled by their parent */
66 if (strcmp(newrel->nspname, "pg_toast") == 0)
67 continue;
69 oldrel = relarr_lookup_rel(ctx, &(old_db->rel_arr), newrel->nspname,
70 newrel->relname, CLUSTER_OLD);
72 map_rel(ctx, oldrel, newrel, old_db, new_db, old_pgdata, new_pgdata,
73 maps + num_maps);
74 num_maps++;
77 * so much for the mapping of this relation. Now we need a mapping for
78 * its corresponding toast relation if any.
80 if (oldrel->toastrelid > 0)
82 RelInfo *new_toast;
83 RelInfo *old_toast;
84 char new_name[MAXPGPATH];
85 char old_name[MAXPGPATH];
87 /* construct the new and old relnames for the toast relation */
88 snprintf(old_name, sizeof(old_name), "pg_toast_%u",
89 oldrel->reloid);
90 snprintf(new_name, sizeof(new_name), "pg_toast_%u",
91 newrel->reloid);
93 /* look them up in their respective arrays */
94 old_toast = relarr_lookup_reloid(ctx, &old_db->rel_arr,
95 oldrel->toastrelid, CLUSTER_OLD);
96 new_toast = relarr_lookup_rel(ctx, &new_db->rel_arr,
97 "pg_toast", new_name, CLUSTER_NEW);
99 /* finally create a mapping for them */
100 map_rel(ctx, old_toast, new_toast, old_db, new_db, old_pgdata, new_pgdata,
101 maps + num_maps);
102 num_maps++;
105 * also need to provide a mapping for the index of this toast
106 * relation. The procedure is similar to what we did above for
107 * toast relation itself, the only difference being that the
108 * relnames need to be appended with _index.
112 * construct the new and old relnames for the toast index
113 * relations
115 snprintf(old_name, sizeof(old_name), "%s_index", old_toast->relname);
116 snprintf(new_name, sizeof(new_name), "pg_toast_%u_index",
117 newrel->reloid);
119 /* look them up in their respective arrays */
120 old_toast = relarr_lookup_rel(ctx, &old_db->rel_arr,
121 "pg_toast", old_name, CLUSTER_OLD);
122 new_toast = relarr_lookup_rel(ctx, &new_db->rel_arr,
123 "pg_toast", new_name, CLUSTER_NEW);
125 /* finally create a mapping for them */
126 map_rel(ctx, old_toast, new_toast, old_db, new_db, old_pgdata,
127 new_pgdata, maps + num_maps);
128 num_maps++;
132 *nmaps = num_maps;
133 return maps;
137 static void
138 map_rel(migratorContext *ctx, const RelInfo *oldrel, const RelInfo *newrel,
139 const DbInfo *old_db, const DbInfo *new_db, const char *olddata,
140 const char *newdata, FileNameMap *map)
142 map_rel_by_id(ctx, oldrel->relfilenode, newrel->relfilenode, oldrel->nspname,
143 oldrel->relname, newrel->nspname, newrel->relname, oldrel->tablespace, old_db,
144 new_db, olddata, newdata, map);
149 * map_rel_by_id()
151 * fills a file node map structure and returns it in "map".
153 static void
154 map_rel_by_id(migratorContext *ctx, Oid oldid, Oid newid,
155 const char *old_nspname, const char *old_relname,
156 const char *new_nspname, const char *new_relname,
157 const char *old_tablespace, const DbInfo *old_db,
158 const DbInfo *new_db, const char *olddata,
159 const char *newdata, FileNameMap *map)
161 map->new = newid;
162 map->old = oldid;
164 snprintf(map->old_nspname, sizeof(map->old_nspname), "%s", old_nspname);
165 snprintf(map->old_relname, sizeof(map->old_relname), "%s", old_relname);
166 snprintf(map->new_nspname, sizeof(map->new_nspname), "%s", new_nspname);
167 snprintf(map->new_relname, sizeof(map->new_relname), "%s", new_relname);
169 if (strlen(old_tablespace) == 0)
172 * relation belongs to the default tablespace, hence relfiles would
173 * exist in the data directories.
175 snprintf(map->old_file, sizeof(map->old_file), "%s/base/%u", olddata, old_db->db_oid);
176 snprintf(map->new_file, sizeof(map->new_file), "%s/base/%u", newdata, new_db->db_oid);
178 else
181 * relation belongs to some tablespace, hence copy its physical
182 * location
184 snprintf(map->old_file, sizeof(map->old_file), "%s%s/%u", old_tablespace,
185 ctx->old.tablespace_suffix, old_db->db_oid);
186 snprintf(map->new_file, sizeof(map->new_file), "%s%s/%u", old_tablespace,
187 ctx->new.tablespace_suffix, new_db->db_oid);
192 void
193 print_maps(migratorContext *ctx, FileNameMap *maps, int n, const char *dbName)
195 if (ctx->debug)
197 int mapnum;
199 pg_log(ctx, PG_DEBUG, "mappings for db %s:\n", dbName);
201 for (mapnum = 0; mapnum < n; mapnum++)
202 pg_log(ctx, PG_DEBUG, "%s.%s:%u ==> %s.%s:%u\n",
203 maps[mapnum].old_nspname, maps[mapnum].old_relname, maps[mapnum].old,
204 maps[mapnum].new_nspname, maps[mapnum].new_relname, maps[mapnum].new);
206 pg_log(ctx, PG_DEBUG, "\n\n");
212 * get_db_infos()
214 * Scans pg_database system catalog and returns (in dbinfs_arr) all user
215 * databases.
217 static void
218 get_db_infos(migratorContext *ctx, DbInfoArr *dbinfs_arr, Cluster whichCluster)
220 PGconn *conn = connectToServer(ctx, "template1", whichCluster);
221 PGresult *res;
222 int ntups;
223 int tupnum;
224 DbInfo *dbinfos;
225 int i_datname;
226 int i_oid;
227 int i_spclocation;
229 res = executeQueryOrDie(ctx, conn,
230 "SELECT d.oid, d.datname, t.spclocation "
231 "FROM pg_catalog.pg_database d "
232 " LEFT OUTER JOIN pg_catalog.pg_tablespace t "
233 " ON d.dattablespace = t.oid "
234 "WHERE d.datallowconn = true");
236 i_datname = PQfnumber(res, "datname");
237 i_oid = PQfnumber(res, "oid");
238 i_spclocation = PQfnumber(res, "spclocation");
240 ntups = PQntuples(res);
241 dbinfos = (DbInfo *) pg_malloc(ctx, sizeof(DbInfo) * ntups);
243 for (tupnum = 0; tupnum < ntups; tupnum++)
245 dbinfos[tupnum].db_oid = atol(PQgetvalue(res, tupnum, i_oid));
247 snprintf(dbinfos[tupnum].db_name, sizeof(dbinfos[tupnum].db_name), "%s",
248 PQgetvalue(res, tupnum, i_datname));
249 snprintf(dbinfos[tupnum].db_tblspace, sizeof(dbinfos[tupnum].db_tblspace), "%s",
250 PQgetvalue(res, tupnum, i_spclocation));
252 PQclear(res);
254 PQfinish(conn);
256 dbinfs_arr->dbs = dbinfos;
257 dbinfs_arr->ndbs = ntups;
262 * get_db_and_rel_infos()
264 * higher level routine to generate dbinfos for the database running
265 * on the given "port". Assumes that server is already running.
267 void
268 get_db_and_rel_infos(migratorContext *ctx, DbInfoArr *db_arr, Cluster whichCluster)
270 int dbnum;
272 get_db_infos(ctx, db_arr, whichCluster);
274 for (dbnum = 0; dbnum < db_arr->ndbs; dbnum++)
275 get_rel_infos(ctx, &db_arr->dbs[dbnum],
276 &(db_arr->dbs[dbnum].rel_arr), whichCluster);
278 if (ctx->debug)
279 dbarr_print(ctx, db_arr, whichCluster);
284 * get_rel_infos()
286 * gets the relinfos for all the user tables of the database refered
287 * by "db".
289 * NOTE: we assume that relations/entities with oids greater than
290 * FirstNormalObjectId belongs to the user
292 static void
293 get_rel_infos(migratorContext *ctx, const DbInfo *dbinfo,
294 RelInfoArr *relarr, Cluster whichCluster)
296 PGconn *conn = connectToServer(ctx, dbinfo->db_name, whichCluster);
297 PGresult *res;
298 RelInfo *relinfos;
299 int ntups;
300 int relnum;
301 int num_rels = 0;
302 char *nspname = NULL;
303 char *relname = NULL;
304 int i_spclocation = -1;
305 int i_nspname = -1;
306 int i_relname = -1;
307 int i_oid = -1;
308 int i_relfilenode = -1;
309 int i_reltoastrelid = -1;
310 char query[QUERY_ALLOC];
313 * pg_largeobject contains user data that does not appear the pg_dumpall
314 * --schema-only output, so we have to migrate that system table heap and
315 * index. Ideally we could just get the relfilenode from template1 but
316 * pg_largeobject_loid_pn_index's relfilenode can change if the table was
317 * reindexed so we get the relfilenode for each database and migrate it as
318 * a normal user table.
321 snprintf(query, sizeof(query),
322 "SELECT DISTINCT c.oid, n.nspname, c.relname, "
323 " c.relfilenode, c.reltoastrelid, t.spclocation "
324 "FROM pg_catalog.pg_class c JOIN "
325 " pg_catalog.pg_namespace n "
326 " ON c.relnamespace = n.oid "
327 " LEFT OUTER JOIN pg_catalog.pg_tablespace t "
328 " ON c.reltablespace = t.oid "
329 "WHERE (( n.nspname NOT IN ('pg_catalog', 'information_schema') "
330 " AND c.oid >= %u "
331 " ) OR ( "
332 " n.nspname = 'pg_catalog' "
333 " AND relname IN "
334 " ('pg_largeobject', 'pg_largeobject_loid_pn_index') )) "
335 " AND relkind IN ('r','t', 'i'%s)"
336 "GROUP BY c.oid, n.nspname, c.relname, c.relfilenode,"
337 " c.reltoastrelid, t.spclocation, "
338 " n.nspname "
339 "ORDER BY n.nspname, c.relname;",
340 FirstNormalObjectId,
341 /* see the comment at the top of old_8_3_create_sequence_script() */
342 (GET_MAJOR_VERSION(ctx->old.major_version) <= 803) ?
343 "" : ", 'S'");
345 res = executeQueryOrDie(ctx, conn, query);
347 ntups = PQntuples(res);
349 relinfos = (RelInfo *) pg_malloc(ctx, sizeof(RelInfo) * ntups);
351 i_oid = PQfnumber(res, "oid");
352 i_nspname = PQfnumber(res, "nspname");
353 i_relname = PQfnumber(res, "relname");
354 i_relfilenode = PQfnumber(res, "relfilenode");
355 i_reltoastrelid = PQfnumber(res, "reltoastrelid");
356 i_spclocation = PQfnumber(res, "spclocation");
358 for (relnum = 0; relnum < ntups; relnum++)
360 RelInfo *curr = &relinfos[num_rels++];
361 const char *tblspace;
363 curr->reloid = atol(PQgetvalue(res, relnum, i_oid));
365 nspname = PQgetvalue(res, relnum, i_nspname);
366 strlcpy(curr->nspname, nspname, sizeof(curr->nspname));
368 relname = PQgetvalue(res, relnum, i_relname);
369 strlcpy(curr->relname, relname, sizeof(curr->relname));
371 curr->relfilenode = atol(PQgetvalue(res, relnum, i_relfilenode));
372 curr->toastrelid = atol(PQgetvalue(res, relnum, i_reltoastrelid));
374 tblspace = PQgetvalue(res, relnum, i_spclocation);
375 /* if no table tablespace, use the database tablespace */
376 if (strlen(tblspace) == 0)
377 tblspace = dbinfo->db_tblspace;
378 strlcpy(curr->tablespace, tblspace, sizeof(curr->tablespace));
380 PQclear(res);
382 PQfinish(conn);
384 relarr->rels = relinfos;
385 relarr->nrels = num_rels;
390 * dbarr_lookup_db()
392 * Returns the pointer to the DbInfo structure
394 DbInfo *
395 dbarr_lookup_db(DbInfoArr *db_arr, const char *db_name)
397 int dbnum;
399 if (!db_arr || !db_name)
400 return NULL;
402 for (dbnum = 0; dbnum < db_arr->ndbs; dbnum++)
404 if (strcmp(db_arr->dbs[dbnum].db_name, db_name) == 0)
405 return &db_arr->dbs[dbnum];
408 return NULL;
413 * relarr_lookup_rel()
415 * Searches "relname" in rel_arr. Returns the *real* pointer to the
416 * RelInfo structure.
418 static RelInfo *
419 relarr_lookup_rel(migratorContext *ctx, RelInfoArr *rel_arr,
420 const char *nspname, const char *relname,
421 Cluster whichCluster)
423 int relnum;
425 if (!rel_arr || !relname)
426 return NULL;
428 for (relnum = 0; relnum < rel_arr->nrels; relnum++)
430 if (strcmp(rel_arr->rels[relnum].nspname, nspname) == 0 &&
431 strcmp(rel_arr->rels[relnum].relname, relname) == 0)
432 return &rel_arr->rels[relnum];
434 pg_log(ctx, PG_FATAL, "Could not find %s.%s in %s cluster\n",
435 nspname, relname, CLUSTERNAME(whichCluster));
436 return NULL;
441 * relarr_lookup_reloid()
443 * Returns a pointer to the RelInfo structure for the
444 * given oid or NULL if the desired entry cannot be
445 * found.
447 static RelInfo *
448 relarr_lookup_reloid(migratorContext *ctx, RelInfoArr *rel_arr, Oid oid,
449 Cluster whichCluster)
451 int relnum;
453 if (!rel_arr || !oid)
454 return NULL;
456 for (relnum = 0; relnum < rel_arr->nrels; relnum++)
458 if (rel_arr->rels[relnum].reloid == oid)
459 return &rel_arr->rels[relnum];
461 pg_log(ctx, PG_FATAL, "Could not find %d in %s cluster\n",
462 oid, CLUSTERNAME(whichCluster));
463 return NULL;
467 static void
468 relarr_free(RelInfoArr *rel_arr)
470 pg_free(rel_arr->rels);
471 rel_arr->nrels = 0;
475 void
476 dbarr_free(DbInfoArr *db_arr)
478 int dbnum;
480 for (dbnum = 0; dbnum < db_arr->ndbs; dbnum++)
481 relarr_free(&db_arr->dbs[dbnum].rel_arr);
482 db_arr->ndbs = 0;
486 static void
487 dbarr_print(migratorContext *ctx, DbInfoArr *arr, Cluster whichCluster)
489 int dbnum;
491 pg_log(ctx, PG_DEBUG, "%s databases\n", CLUSTERNAME(whichCluster));
493 for (dbnum = 0; dbnum < arr->ndbs; dbnum++)
495 pg_log(ctx, PG_DEBUG, "Database: %s\n", arr->dbs[dbnum].db_name);
496 relarr_print(ctx, &arr->dbs[dbnum].rel_arr);
497 pg_log(ctx, PG_DEBUG, "\n\n");
502 static void
503 relarr_print(migratorContext *ctx, RelInfoArr *arr)
505 int relnum;
507 for (relnum = 0; relnum < arr->nrels; relnum++)
508 pg_log(ctx, PG_DEBUG, "relname: %s.%s: reloid: %u reltblspace: %s\n",
509 arr->rels[relnum].nspname, arr->rels[relnum].relname,
510 arr->rels[relnum].reloid, arr->rels[relnum].tablespace);