4 * information support functions
6 * Copyright (c) 2010, PostgreSQL Global Development Group
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
);
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
47 * NOTE: Its the Caller's responsibility to free the returned array.
50 gen_db_file_maps(migratorContext
*ctx
, DbInfo
*old_db
, DbInfo
*new_db
,
51 int *nmaps
, const char *old_pgdata
, const char *new_pgdata
)
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
];
65 /* toast tables are handled by their parent */
66 if (strcmp(newrel
->nspname
, "pg_toast") == 0)
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
,
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)
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",
90 snprintf(new_name
, sizeof(new_name
), "pg_toast_%u",
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
,
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
115 snprintf(old_name
, sizeof(old_name
), "%s_index", old_toast
->relname
);
116 snprintf(new_name
, sizeof(new_name
), "pg_toast_%u_index",
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
);
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
);
151 * fills a file node map structure and returns it in "map".
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
)
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
);
181 * relation belongs to some tablespace, hence copy its physical
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
);
193 print_maps(migratorContext
*ctx
, FileNameMap
*maps
, int n
, const char *dbName
)
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");
214 * Scans pg_database system catalog and returns (in dbinfs_arr) all user
218 get_db_infos(migratorContext
*ctx
, DbInfoArr
*dbinfs_arr
, Cluster whichCluster
)
220 PGconn
*conn
= connectToServer(ctx
, "template1", whichCluster
);
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
));
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.
268 get_db_and_rel_infos(migratorContext
*ctx
, DbInfoArr
*db_arr
, Cluster whichCluster
)
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
);
279 dbarr_print(ctx
, db_arr
, whichCluster
);
286 * gets the relinfos for all the user tables of the database refered
289 * NOTE: we assume that relations/entities with oids greater than
290 * FirstNormalObjectId belongs to the user
293 get_rel_infos(migratorContext
*ctx
, const DbInfo
*dbinfo
,
294 RelInfoArr
*relarr
, Cluster whichCluster
)
296 PGconn
*conn
= connectToServer(ctx
, dbinfo
->db_name
, whichCluster
);
302 char *nspname
= NULL
;
303 char *relname
= NULL
;
304 int i_spclocation
= -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') "
332 " n.nspname = 'pg_catalog' "
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, "
339 "ORDER BY n.nspname, c.relname;",
341 /* see the comment at the top of old_8_3_create_sequence_script() */
342 (GET_MAJOR_VERSION(ctx
->old
.major_version
) <= 803) ?
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
));
384 relarr
->rels
= relinfos
;
385 relarr
->nrels
= num_rels
;
392 * Returns the pointer to the DbInfo structure
395 dbarr_lookup_db(DbInfoArr
*db_arr
, const char *db_name
)
399 if (!db_arr
|| !db_name
)
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
];
413 * relarr_lookup_rel()
415 * Searches "relname" in rel_arr. Returns the *real* pointer to the
419 relarr_lookup_rel(migratorContext
*ctx
, RelInfoArr
*rel_arr
,
420 const char *nspname
, const char *relname
,
421 Cluster whichCluster
)
425 if (!rel_arr
|| !relname
)
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
));
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
448 relarr_lookup_reloid(migratorContext
*ctx
, RelInfoArr
*rel_arr
, Oid oid
,
449 Cluster whichCluster
)
453 if (!rel_arr
|| !oid
)
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
));
468 relarr_free(RelInfoArr
*rel_arr
)
470 pg_free(rel_arr
->rels
);
476 dbarr_free(DbInfoArr
*db_arr
)
480 for (dbnum
= 0; dbnum
< db_arr
->ndbs
; dbnum
++)
481 relarr_free(&db_arr
->dbs
[dbnum
].rel_arr
);
487 dbarr_print(migratorContext
*ctx
, DbInfoArr
*arr
, Cluster whichCluster
)
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");
503 relarr_print(migratorContext
*ctx
, RelInfoArr
*arr
)
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
);