Age Owner TLA Line data Source code
1 : /*
2 : * info.c
3 : *
4 : * information support functions
5 : *
6 : * Copyright (c) 2010-2023, PostgreSQL Global Development Group
7 : * src/bin/pg_upgrade/info.c
8 : */
9 :
10 : #include "postgres_fe.h"
11 :
12 : #include "access/transam.h"
13 : #include "catalog/pg_class_d.h"
14 : #include "pg_upgrade.h"
15 :
16 : static void create_rel_filename_map(const char *old_data, const char *new_data,
17 : const DbInfo *old_db, const DbInfo *new_db,
18 : const RelInfo *old_rel, const RelInfo *new_rel,
19 : FileNameMap *map);
20 : static void report_unmatched_relation(const RelInfo *rel, const DbInfo *db,
21 : bool is_new_db);
22 : static void free_db_and_rel_infos(DbInfoArr *db_arr);
23 : static void get_template0_info(ClusterInfo *cluster);
24 : static void get_db_infos(ClusterInfo *cluster);
25 : static void get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo);
26 : static void free_rel_infos(RelInfoArr *rel_arr);
27 : static void print_db_infos(DbInfoArr *db_arr);
28 : static void print_rel_infos(RelInfoArr *rel_arr);
29 :
30 :
31 : /*
32 : * gen_db_file_maps()
33 : *
34 : * generates a database mapping from "old_db" to "new_db".
35 : *
36 : * Returns a malloc'ed array of mappings. The length of the array
37 : * is returned into *nmaps.
38 : */
39 : FileNameMap *
4555 bruce 40 GIC 6 : gen_db_file_maps(DbInfo *old_db, DbInfo *new_db,
2529 tgl 41 ECB : int *nmaps,
42 : const char *old_pgdata, const char *new_pgdata)
43 : {
44 : FileNameMap *maps;
45 : int old_relnum,
46 : new_relnum;
4715 bruce 47 GIC 6 : int num_maps = 0;
2529 tgl 48 CBC 6 : bool all_matched = true;
4715 bruce 49 ECB :
50 : /* There will certainly not be more mappings than there are old rels */
4555 bruce 51 GIC 6 : maps = (FileNameMap *) pg_malloc(sizeof(FileNameMap) *
4477 bruce 52 CBC 6 : old_db->rel_arr.nrels);
4715 bruce 53 ECB :
54 : /*
55 : * Each of the RelInfo arrays should be sorted by OID. Scan through them
56 : * and match them up. If we fail to match everything, we'll abort, but
57 : * first print as much info as we can about mismatches.
58 : */
2529 tgl 59 GIC 6 : old_relnum = new_relnum = 0;
2529 tgl 60 CBC 1264 : while (old_relnum < old_db->rel_arr.nrels ||
61 6 : new_relnum < new_db->rel_arr.nrels)
4715 bruce 62 ECB : {
2529 tgl 63 GIC 2516 : RelInfo *old_rel = (old_relnum < old_db->rel_arr.nrels) ?
2529 tgl 64 CBC 1258 : &old_db->rel_arr.rels[old_relnum] : NULL;
65 2516 : RelInfo *new_rel = (new_relnum < new_db->rel_arr.nrels) ?
66 1258 : &new_db->rel_arr.rels[new_relnum] : NULL;
3198 bruce 67 ECB :
68 : /* handle running off one array before the other */
2529 tgl 69 GIC 1258 : if (!new_rel)
3198 bruce 70 ECB : {
71 : /*
72 : * old_rel is unmatched. This should never happen, because we
73 : * force new rels to have TOAST tables if the old one did.
74 : */
2529 tgl 75 UIC 0 : report_unmatched_relation(old_rel, old_db, false);
2529 tgl 76 UBC 0 : all_matched = false;
77 0 : old_relnum++;
78 0 : continue;
2529 tgl 79 EUB : }
2529 tgl 80 GIC 1258 : if (!old_rel)
2529 tgl 81 ECB : {
82 : /*
83 : * new_rel is unmatched. This shouldn't really happen either, but
84 : * if it's a TOAST table, we can ignore it and continue
85 : * processing, assuming that the new server made a TOAST table
86 : * that wasn't needed.
87 : */
2529 tgl 88 UIC 0 : if (strcmp(new_rel->nspname, "pg_toast") != 0)
2529 tgl 89 EUB : {
2529 tgl 90 UIC 0 : report_unmatched_relation(new_rel, new_db, true);
2529 tgl 91 UBC 0 : all_matched = false;
2529 tgl 92 EUB : }
2529 tgl 93 UIC 0 : new_relnum++;
2529 tgl 94 UBC 0 : continue;
3198 bruce 95 EUB : }
96 :
97 : /* check for mismatched OID */
2529 tgl 98 GIC 1258 : if (old_rel->reloid < new_rel->reloid)
3198 bruce 99 ECB : {
100 : /* old_rel is unmatched, see comment above */
2529 tgl 101 UIC 0 : report_unmatched_relation(old_rel, old_db, false);
2529 tgl 102 UBC 0 : all_matched = false;
103 0 : old_relnum++;
104 0 : continue;
2529 tgl 105 EUB : }
2529 tgl 106 GIC 1258 : else if (old_rel->reloid > new_rel->reloid)
2529 tgl 107 ECB : {
108 : /* new_rel is unmatched, see comment above */
2529 tgl 109 UIC 0 : if (strcmp(new_rel->nspname, "pg_toast") != 0)
2529 tgl 110 EUB : {
2529 tgl 111 UIC 0 : report_unmatched_relation(new_rel, new_db, true);
2529 tgl 112 UBC 0 : all_matched = false;
2529 tgl 113 EUB : }
2529 tgl 114 UIC 0 : new_relnum++;
2529 tgl 115 UBC 0 : continue;
3198 bruce 116 EUB : }
117 :
118 : /*
119 : * Verify that rels of same OID have same name. The namespace name
120 : * should always match, but the relname might not match for TOAST
121 : * tables (and, therefore, their indexes).
122 : */
4417 bruce 123 GIC 1258 : if (strcmp(old_rel->nspname, new_rel->nspname) != 0 ||
481 tgl 124 CBC 1258 : strcmp(old_rel->relname, new_rel->relname) != 0)
2529 tgl 125 ECB : {
2529 tgl 126 UIC 0 : pg_log(PG_WARNING, "Relation names for OID %u in database \"%s\" do not match: "
127 : "old name \"%s.%s\", new name \"%s.%s\"",
128 : old_rel->reloid, old_db->db_name,
129 : old_rel->nspname, old_rel->relname,
130 : new_rel->nspname, new_rel->relname);
131 0 : all_matched = false;
2529 tgl 132 UBC 0 : old_relnum++;
133 0 : new_relnum++;
134 0 : continue;
2529 tgl 135 EUB : }
136 :
137 : /* OK, create a mapping entry */
4478 bruce 138 GIC 1258 : create_rel_filename_map(old_pgdata, new_pgdata, old_db, new_db,
4382 bruce 139 CBC 1258 : old_rel, new_rel, maps + num_maps);
4715 140 1258 : num_maps++;
3198 141 1258 : old_relnum++;
2529 tgl 142 1258 : new_relnum++;
4715 bruce 143 ECB : }
144 :
2529 tgl 145 GIC 6 : if (!all_matched)
271 tgl 146 UNC 0 : pg_fatal("Failed to match up old and new tables in database \"%s\"",
3260 bruce 147 EUB : old_db->db_name);
148 :
4715 bruce 149 GIC 6 : *nmaps = num_maps;
4715 bruce 150 CBC 6 : return maps;
4715 bruce 151 ECB : }
152 :
153 :
154 : /*
155 : * create_rel_filename_map()
156 : *
157 : * fills a file node map structure and returns it in "map".
158 : */
159 : static void
4478 bruce 160 GIC 1258 : create_rel_filename_map(const char *old_data, const char *new_data,
4382 bruce 161 ECB : const DbInfo *old_db, const DbInfo *new_db,
162 : const RelInfo *old_rel, const RelInfo *new_rel,
163 : FileNameMap *map)
164 : {
165 : /* In case old/new tablespaces don't match, do them separately. */
4478 bruce 166 GIC 1258 : if (strlen(old_rel->tablespace) == 0)
4715 bruce 167 ECB : {
168 : /*
169 : * relation belongs to the default tablespace, hence relfiles should
170 : * exist in the data directories.
171 : */
3343 bruce 172 GIC 1258 : map->old_tablespace = old_data;
3343 bruce 173 CBC 1258 : map->old_tablespace_suffix = "/base";
4715 bruce 174 ECB : }
175 : else
176 : {
177 : /* relation belongs to a tablespace, so use the tablespace location */
3343 bruce 178 UIC 0 : map->old_tablespace = old_rel->tablespace;
3343 bruce 179 UBC 0 : map->old_tablespace_suffix = old_cluster.tablespace_suffix;
2767 bruce 180 EUB : }
181 :
182 : /* Do the same for new tablespaces */
2767 bruce 183 GIC 1258 : if (strlen(new_rel->tablespace) == 0)
2767 bruce 184 ECB : {
2767 bruce 185 GIC 1258 : map->new_tablespace = new_data;
2767 bruce 186 CBC 1258 : map->new_tablespace_suffix = "/base";
2767 bruce 187 ECB : }
188 : else
189 : {
2767 bruce 190 UIC 0 : map->new_tablespace = new_rel->tablespace;
3343 bruce 191 UBC 0 : map->new_tablespace_suffix = new_cluster.tablespace_suffix;
4715 bruce 192 EUB : }
193 :
194 : /* DB oid and relfilenumbers are preserved between old and new cluster */
440 rhaas 195 GIC 1258 : map->db_oid = old_db->db_oid;
277 rhaas 196 GNC 1258 : map->relfilenumber = old_rel->relfilenumber;
4477 bruce 197 ECB :
198 : /* used only for logging and error reporting, old/new are identical */
3762 bruce 199 GIC 1258 : map->nspname = old_rel->nspname;
3762 bruce 200 CBC 1258 : map->relname = old_rel->relname;
4715 201 1258 : }
4715 bruce 202 ECB :
203 :
204 : /*
205 : * Complain about a relation we couldn't match to the other database,
206 : * identifying it as best we can.
207 : */
208 : static void
2529 tgl 209 UIC 0 : report_unmatched_relation(const RelInfo *rel, const DbInfo *db, bool is_new_db)
2529 tgl 210 EUB : {
2529 tgl 211 UIC 0 : Oid reloid = rel->reloid; /* we might change rel below */
2529 tgl 212 EUB : char reldesc[1000];
213 : int i;
214 :
2529 tgl 215 UIC 0 : snprintf(reldesc, sizeof(reldesc), "\"%s.%s\"",
2529 tgl 216 UBC 0 : rel->nspname, rel->relname);
217 0 : if (rel->indtable)
2529 tgl 218 EUB : {
2529 tgl 219 UIC 0 : for (i = 0; i < db->rel_arr.nrels; i++)
2529 tgl 220 EUB : {
2529 tgl 221 UIC 0 : const RelInfo *hrel = &db->rel_arr.rels[i];
2529 tgl 222 EUB :
2529 tgl 223 UIC 0 : if (hrel->reloid == rel->indtable)
2529 tgl 224 EUB : {
2529 tgl 225 UIC 0 : snprintf(reldesc + strlen(reldesc),
2529 tgl 226 UBC 0 : sizeof(reldesc) - strlen(reldesc),
2368 peter_e 227 0 : _(" which is an index on \"%s.%s\""),
2529 tgl 228 0 : hrel->nspname, hrel->relname);
2529 tgl 229 EUB : /* Shift attention to index's table for toast check */
2529 tgl 230 UIC 0 : rel = hrel;
2529 tgl 231 UBC 0 : break;
2529 tgl 232 EUB : }
233 : }
2529 tgl 234 UIC 0 : if (i >= db->rel_arr.nrels)
2529 tgl 235 UBC 0 : snprintf(reldesc + strlen(reldesc),
236 0 : sizeof(reldesc) - strlen(reldesc),
2368 peter_e 237 0 : _(" which is an index on OID %u"), rel->indtable);
2529 tgl 238 EUB : }
2529 tgl 239 UIC 0 : if (rel->toastheap)
2529 tgl 240 EUB : {
2529 tgl 241 UIC 0 : for (i = 0; i < db->rel_arr.nrels; i++)
2529 tgl 242 EUB : {
2529 tgl 243 UIC 0 : const RelInfo *brel = &db->rel_arr.rels[i];
2529 tgl 244 EUB :
2529 tgl 245 UIC 0 : if (brel->reloid == rel->toastheap)
2529 tgl 246 EUB : {
2529 tgl 247 UIC 0 : snprintf(reldesc + strlen(reldesc),
2529 tgl 248 UBC 0 : sizeof(reldesc) - strlen(reldesc),
2368 peter_e 249 0 : _(" which is the TOAST table for \"%s.%s\""),
2529 tgl 250 0 : brel->nspname, brel->relname);
251 0 : break;
2529 tgl 252 EUB : }
253 : }
2529 tgl 254 UIC 0 : if (i >= db->rel_arr.nrels)
2529 tgl 255 UBC 0 : snprintf(reldesc + strlen(reldesc),
256 0 : sizeof(reldesc) - strlen(reldesc),
2118 257 0 : _(" which is the TOAST table for OID %u"), rel->toastheap);
2529 tgl 258 EUB : }
259 :
2529 tgl 260 UIC 0 : if (is_new_db)
271 tgl 261 UNC 0 : pg_log(PG_WARNING, "No match found in old cluster for new relation with OID %u in database \"%s\": %s",
2529 tgl 262 UBC 0 : reloid, db->db_name, reldesc);
2529 tgl 263 EUB : else
271 tgl 264 UNC 0 : pg_log(PG_WARNING, "No match found in new cluster for old relation with OID %u in database \"%s\": %s",
2529 tgl 265 UBC 0 : reloid, db->db_name, reldesc);
266 0 : }
2529 tgl 267 EUB :
268 : /*
269 : * get_db_and_rel_infos()
270 : *
271 : * higher level routine to generate dbinfos for the database running
272 : * on the given "port". Assumes that server is already running.
273 : */
274 : void
4472 bruce 275 GIC 5 : get_db_and_rel_infos(ClusterInfo *cluster)
4472 bruce 276 ECB : {
277 : int dbnum;
278 :
4436 bruce 279 GIC 5 : if (cluster->dbarr.dbs != NULL)
4436 bruce 280 CBC 1 : free_db_and_rel_infos(&cluster->dbarr);
4436 bruce 281 ECB :
31 jdavis 282 GNC 5 : get_template0_info(cluster);
4472 bruce 283 GIC 5 : get_db_infos(cluster);
4472 bruce 284 ECB :
4472 bruce 285 CBC 27 : for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
4472 bruce 286 GIC 22 : get_rel_infos(cluster, &cluster->dbarr.dbs[dbnum]);
4472 bruce 287 ECB :
2095 alvherre 288 CBC 5 : if (cluster == &old_cluster)
271 tgl 289 GNC 2 : pg_log(PG_VERBOSE, "\nsource databases:");
2095 alvherre 290 ECB : else
271 tgl 291 GNC 3 : pg_log(PG_VERBOSE, "\ntarget databases:");
292 :
4045 bruce 293 CBC 5 : if (log_opts.verbose)
4472 bruce 294 UIC 0 : print_db_infos(&cluster->dbarr);
4472 bruce 295 CBC 5 : }
4472 bruce 296 EUB :
4472 bruce 297 ECB :
298 : /*
299 : * Get information about template0, which will be copied from the old cluster
300 : * to the new cluster.
301 : */
302 : static void
31 jdavis 303 GNC 5 : get_template0_info(ClusterInfo *cluster)
304 : {
305 5 : PGconn *conn = connectToServer(cluster, "template1");
306 : DbLocaleInfo *locale;
307 : PGresult *dbres;
308 : int i_datencoding;
309 : int i_datlocprovider;
310 : int i_datcollate;
311 : int i_datctype;
312 : int i_daticulocale;
313 :
314 5 : if (GET_MAJOR_VERSION(cluster->major_version) >= 1500)
315 5 : dbres = executeQueryOrDie(conn,
316 : "SELECT encoding, datlocprovider, "
317 : " datcollate, datctype, daticulocale "
318 : "FROM pg_catalog.pg_database "
319 : "WHERE datname='template0'");
320 : else
31 jdavis 321 UNC 0 : dbres = executeQueryOrDie(conn,
322 : "SELECT encoding, 'c' AS datlocprovider, "
323 : " datcollate, datctype, NULL AS daticulocale "
324 : "FROM pg_catalog.pg_database "
325 : "WHERE datname='template0'");
326 :
327 :
31 jdavis 328 GNC 5 : if (PQntuples(dbres) != 1)
31 jdavis 329 UNC 0 : pg_fatal("template0 not found");
330 :
31 jdavis 331 GNC 5 : locale = pg_malloc(sizeof(DbLocaleInfo));
332 :
333 5 : i_datencoding = PQfnumber(dbres, "encoding");
334 5 : i_datlocprovider = PQfnumber(dbres, "datlocprovider");
335 5 : i_datcollate = PQfnumber(dbres, "datcollate");
336 5 : i_datctype = PQfnumber(dbres, "datctype");
337 5 : i_daticulocale = PQfnumber(dbres, "daticulocale");
338 :
339 5 : locale->db_encoding = atoi(PQgetvalue(dbres, 0, i_datencoding));
340 5 : locale->db_collprovider = PQgetvalue(dbres, 0, i_datlocprovider)[0];
341 5 : locale->db_collate = pg_strdup(PQgetvalue(dbres, 0, i_datcollate));
342 5 : locale->db_ctype = pg_strdup(PQgetvalue(dbres, 0, i_datctype));
343 5 : if (PQgetisnull(dbres, 0, i_daticulocale))
344 2 : locale->db_iculocale = NULL;
345 : else
346 3 : locale->db_iculocale = pg_strdup(PQgetvalue(dbres, 0, i_daticulocale));
347 :
348 5 : cluster->template0 = locale;
349 :
350 5 : PQclear(dbres);
351 5 : PQfinish(conn);
352 5 : }
353 :
354 :
355 : /*
356 : * get_db_infos()
357 : *
358 : * Scans pg_database system catalog and populates all user
359 : * databases.
360 : */
361 : static void
4481 bruce 362 CBC 5 : get_db_infos(ClusterInfo *cluster)
363 : {
364 5 : PGconn *conn = connectToServer(cluster, "template1");
365 : PGresult *res;
366 : int ntups;
367 : int tupnum;
368 : DbInfo *dbinfos;
369 : int i_datname,
370 : i_oid,
371 : i_spclocation;
372 : char query[QUERY_ALLOC];
373 :
4141 magnus 374 GIC 5 : snprintf(query, sizeof(query),
388 peter 375 EUB : "SELECT d.oid, d.datname, d.encoding, d.datcollate, d.datctype, ");
230 peter 376 GIC 5 : if (GET_MAJOR_VERSION(cluster->major_version) < 1500)
388 peter 377 UIC 0 : snprintf(query + strlen(query), sizeof(query) - strlen(query),
378 : "'c' AS datlocprovider, NULL AS daticulocale, ");
379 : else
388 peter 380 GIC 5 : snprintf(query + strlen(query), sizeof(query) - strlen(query),
381 : "datlocprovider, daticulocale, ");
388 peter 382 CBC 5 : snprintf(query + strlen(query), sizeof(query) - strlen(query),
481 tgl 383 EUB : "pg_catalog.pg_tablespace_location(t.oid) AS spclocation "
384 : "FROM pg_catalog.pg_database d "
3955 bruce 385 ECB : " LEFT OUTER JOIN pg_catalog.pg_tablespace t "
386 : " ON d.dattablespace = t.oid "
387 : "WHERE d.datallowconn = true "
440 rhaas 388 : "ORDER BY 1");
4141 magnus 389 :
4141 magnus 390 CBC 5 : res = executeQueryOrDie(conn, "%s", query);
4660 bruce 391 ECB :
4715 bruce 392 GIC 5 : i_oid = PQfnumber(res, "oid");
4418 bruce 393 CBC 5 : i_datname = PQfnumber(res, "datname");
4715 bruce 394 GIC 5 : i_spclocation = PQfnumber(res, "spclocation");
4715 bruce 395 ECB :
4715 bruce 396 GIC 5 : ntups = PQntuples(res);
4555 bruce 397 CBC 5 : dbinfos = (DbInfo *) pg_malloc(sizeof(DbInfo) * ntups);
398 :
4715 399 27 : for (tupnum = 0; tupnum < ntups; tupnum++)
4715 bruce 400 ECB : {
4576 bruce 401 CBC 22 : dbinfos[tupnum].db_oid = atooid(PQgetvalue(res, tupnum, i_oid));
3762 bruce 402 GIC 22 : dbinfos[tupnum].db_name = pg_strdup(PQgetvalue(res, tupnum, i_datname));
3343 bruce 403 CBC 22 : snprintf(dbinfos[tupnum].db_tablespace, sizeof(dbinfos[tupnum].db_tablespace), "%s",
404 : PQgetvalue(res, tupnum, i_spclocation));
4715 bruce 405 ECB : }
4715 bruce 406 GIC 5 : PQclear(res);
407 :
408 5 : PQfinish(conn);
409 :
4481 410 5 : cluster->dbarr.dbs = dbinfos;
411 5 : cluster->dbarr.ndbs = ntups;
4715 412 5 : }
413 :
414 :
4715 bruce 415 ECB : /*
416 : * get_rel_infos()
417 : *
2529 tgl 418 EUB : * gets the relinfos for all the user tables and indexes of the database
419 : * referred to by "dbinfo".
420 : *
2529 tgl 421 ECB : * Note: the resulting RelInfo array is assumed to be sorted by OID.
422 : * This allows later processing to match up old and new databases efficiently.
4715 bruce 423 : */
424 : static void
4478 bruce 425 GIC 22 : get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo)
426 : {
4481 427 22 : PGconn *conn = connectToServer(cluster,
4478 428 22 : dbinfo->db_name);
429 : PGresult *res;
430 : RelInfo *relinfos;
4715 bruce 431 ECB : int ntups;
432 : int relnum;
4715 bruce 433 CBC 22 : int num_rels = 0;
434 22 : char *nspname = NULL;
435 22 : char *relname = NULL;
3343 bruce 436 GIC 22 : char *tablespace = NULL;
4382 bruce 437 ECB : int i_spclocation,
438 : i_nspname,
439 : i_relname,
2529 tgl 440 : i_reloid,
441 : i_indtable,
442 : i_toastheap,
443 : i_relfilenumber,
1433 akapila 444 : i_reltablespace;
445 : char query[QUERY_ALLOC];
3260 bruce 446 GIC 22 : char *last_namespace = NULL,
3260 bruce 447 CBC 22 : *last_tablespace = NULL;
448 :
2529 tgl 449 22 : query[0] = '\0'; /* initialize query string to empty */
450 :
4715 bruce 451 ECB : /*
96 michael 452 : * Create a CTE that collects OIDs of regular user tables and matviews,
453 : * but excluding toast tables and indexes. We assume that relations with
454 : * OIDs >= FirstNormalObjectId belong to the user. (That's probably
455 : * redundant with the namespace-name exclusions, but let's be safe.)
456 : *
457 : * pg_largeobject contains user data that does not appear in pg_dump
458 : * output, so we have to copy that system table. It's easiest to do that
459 : * by treating it as a user table.
460 : */
2529 tgl 461 GIC 22 : snprintf(query + strlen(query), sizeof(query) - strlen(query),
462 : "WITH regular_heap (reloid, indtable, toastheap) AS ( "
463 : " SELECT c.oid, 0::oid, 0::oid "
464 : " FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n "
465 : " ON c.relnamespace = n.oid "
2222 tgl 466 ECB : " WHERE relkind IN (" CppAsString2(RELKIND_RELATION) ", "
467 : CppAsString2(RELKIND_MATVIEW) ") AND "
2529 468 : /* exclude possible orphaned temp tables */
469 : " ((n.nspname !~ '^pg_temp_' AND "
470 : " n.nspname !~ '^pg_toast_temp_' AND "
471 : " n.nspname NOT IN ('pg_catalog', 'information_schema', "
472 : " 'binary_upgrade', 'pg_toast') AND "
473 : " c.oid >= %u::pg_catalog.oid) OR "
474 : " (n.nspname = 'pg_catalog' AND "
1595 andres 475 : " relname IN ('pg_largeobject') ))), ",
476 : FirstNormalObjectId);
4715 bruce 477 :
478 : /*
479 : * Add a CTE that collects OIDs of toast tables belonging to the tables
480 : * selected by the regular_heap CTE. (We have to do this separately
481 : * because the namespace-name rules above don't work for toast tables.)
482 : */
2529 tgl 483 GIC 22 : snprintf(query + strlen(query), sizeof(query) - strlen(query),
484 : " toast_heap (reloid, indtable, toastheap) AS ( "
485 : " SELECT c.reltoastrelid, 0::oid, c.oid "
486 : " FROM regular_heap JOIN pg_catalog.pg_class c "
2529 tgl 487 ECB : " ON regular_heap.reloid = c.oid "
488 : " WHERE c.reltoastrelid != 0), ");
489 :
2878 bruce 490 : /*
491 : * Add a CTE that collects OIDs of all valid indexes on the previously
492 : * selected tables. We can ignore invalid indexes since pg_dump does.
493 : * Testing indisready is necessary in 9.2, and harmless in earlier/later
494 : * versions.
495 : */
2529 tgl 496 GIC 22 : snprintf(query + strlen(query), sizeof(query) - strlen(query),
497 : " all_index (reloid, indtable, toastheap) AS ( "
498 : " SELECT indexrelid, indrelid, 0::oid "
499 : " FROM pg_catalog.pg_index "
500 : " WHERE indisvalid AND indisready "
501 : " AND indrelid IN "
2529 tgl 502 ECB : " (SELECT reloid FROM regular_heap "
503 : " UNION ALL "
504 : " SELECT reloid FROM toast_heap)) ");
505 :
506 : /*
507 : * And now we can write the query that retrieves the data we want for each
508 : * heap and index relation. Make sure result is sorted by OID.
509 : */
2529 tgl 510 GIC 22 : snprintf(query + strlen(query), sizeof(query) - strlen(query),
511 : "SELECT all_rels.*, n.nspname, c.relname, "
512 : " c.relfilenode, c.reltablespace, "
513 : " pg_catalog.pg_tablespace_location(t.oid) AS spclocation "
514 : "FROM (SELECT * FROM regular_heap "
515 : " UNION ALL "
516 : " SELECT * FROM toast_heap "
517 : " UNION ALL "
518 : " SELECT * FROM all_index) all_rels "
519 : " JOIN pg_catalog.pg_class c "
520 : " ON all_rels.reloid = c.oid "
521 : " JOIN pg_catalog.pg_namespace n "
522 : " ON c.relnamespace = n.oid "
523 : " LEFT OUTER JOIN pg_catalog.pg_tablespace t "
2529 tgl 524 ECB : " ON c.reltablespace = t.oid "
525 : "ORDER BY 1;");
526 :
4229 peter_e 527 GIC 22 : res = executeQueryOrDie(conn, "%s", query);
528 :
4715 bruce 529 22 : ntups = PQntuples(res);
530 :
4555 531 22 : relinfos = (RelInfo *) pg_malloc(sizeof(RelInfo) * ntups);
532 :
2529 tgl 533 22 : i_reloid = PQfnumber(res, "reloid");
534 22 : i_indtable = PQfnumber(res, "indtable");
535 22 : i_toastheap = PQfnumber(res, "toastheap");
4715 bruce 536 22 : i_nspname = PQfnumber(res, "nspname");
4715 bruce 537 CBC 22 : i_relname = PQfnumber(res, "relname");
277 rhaas 538 GNC 22 : i_relfilenumber = PQfnumber(res, "relfilenode");
4016 bruce 539 GIC 22 : i_reltablespace = PQfnumber(res, "reltablespace");
4715 540 22 : i_spclocation = PQfnumber(res, "spclocation");
541 :
542 3804 : for (relnum = 0; relnum < ntups; relnum++)
543 : {
544 3782 : RelInfo *curr = &relinfos[num_rels++];
545 :
2529 tgl 546 3782 : curr->reloid = atooid(PQgetvalue(res, relnum, i_reloid));
547 3782 : curr->indtable = atooid(PQgetvalue(res, relnum, i_indtable));
548 3782 : curr->toastheap = atooid(PQgetvalue(res, relnum, i_toastheap));
549 :
4715 bruce 550 3782 : nspname = PQgetvalue(res, relnum, i_nspname);
3343 bruce 551 CBC 3782 : curr->nsp_alloc = false;
552 :
553 : /*
554 : * Many of the namespace and tablespace strings are identical, so we
555 : * try to reuse the allocated string pointers where possible to reduce
556 : * memory consumption.
557 : */
558 : /* Can we reuse the previous string allocation? */
3343 bruce 559 GIC 3782 : if (last_namespace && strcmp(nspname, last_namespace) == 0)
560 2350 : curr->nspname = last_namespace;
561 : else
562 : {
563 1432 : last_namespace = curr->nspname = pg_strdup(nspname);
564 1432 : curr->nsp_alloc = true;
565 : }
566 :
4715 567 3782 : relname = PQgetvalue(res, relnum, i_relname);
3762 bruce 568 CBC 3782 : curr->relname = pg_strdup(relname);
569 :
193 rhaas 570 GNC 3782 : curr->relfilenumber = atooid(PQgetvalue(res, relnum, i_relfilenumber));
3343 bruce 571 GIC 3782 : curr->tblsp_alloc = false;
4715 bruce 572 ECB :
573 : /* Is the tablespace oid non-default? */
4016 bruce 574 CBC 3782 : if (atooid(PQgetvalue(res, relnum, i_reltablespace)) != 0)
3343 bruce 575 ECB : {
576 : /*
577 : * The tablespace location might be "", meaning the cluster
578 : * default location, i.e. pg_default or pg_global.
579 : */
3343 bruce 580 LBC 0 : tablespace = PQgetvalue(res, relnum, i_spclocation);
3343 bruce 581 ECB :
582 : /* Can we reuse the previous string allocation? */
3343 bruce 583 LBC 0 : if (last_tablespace && strcmp(tablespace, last_tablespace) == 0)
3343 bruce 584 UIC 0 : curr->tablespace = last_tablespace;
3343 bruce 585 ECB : else
586 : {
3343 bruce 587 LBC 0 : last_tablespace = curr->tablespace = pg_strdup(tablespace);
588 0 : curr->tblsp_alloc = true;
3343 bruce 589 ECB : }
590 : }
4016 591 : else
3343 592 : /* A zero reltablespace oid indicates the database tablespace. */
3343 bruce 593 GIC 3782 : curr->tablespace = dbinfo->db_tablespace;
594 : }
4715 595 22 : PQclear(res);
596 :
597 22 : PQfinish(conn);
598 :
4478 599 22 : dbinfo->rel_arr.rels = relinfos;
4478 bruce 600 CBC 22 : dbinfo->rel_arr.nrels = num_rels;
4715 601 22 : }
602 :
603 :
3799 bruce 604 ECB : static void
4472 bruce 605 CBC 1 : free_db_and_rel_infos(DbInfoArr *db_arr)
606 : {
607 : int dbnum;
4715 bruce 608 ECB :
4715 bruce 609 CBC 3 : for (dbnum = 0; dbnum < db_arr->ndbs; dbnum++)
610 : {
4472 611 2 : free_rel_infos(&db_arr->dbs[dbnum].rel_arr);
3762 612 2 : pg_free(db_arr->dbs[dbnum].db_name);
613 : }
4472 bruce 614 GIC 1 : pg_free(db_arr->dbs);
4436 bruce 615 CBC 1 : db_arr->dbs = NULL;
4715 bruce 616 GIC 1 : db_arr->ndbs = 0;
617 1 : }
618 :
619 :
620 : static void
4472 bruce 621 GBC 2 : free_rel_infos(RelInfoArr *rel_arr)
622 : {
623 : int relnum;
3762 bruce 624 EUB :
3762 bruce 625 GBC 6 : for (relnum = 0; relnum < rel_arr->nrels; relnum++)
626 : {
3343 bruce 627 GIC 4 : if (rel_arr->rels[relnum].nsp_alloc)
3343 bruce 628 GBC 2 : pg_free(rel_arr->rels[relnum].nspname);
3762 629 4 : pg_free(rel_arr->rels[relnum].relname);
3343 bruce 630 GIC 4 : if (rel_arr->rels[relnum].tblsp_alloc)
3343 bruce 631 UIC 0 : pg_free(rel_arr->rels[relnum].tablespace);
632 : }
4472 bruce 633 GIC 2 : pg_free(rel_arr->rels);
4472 bruce 634 CBC 2 : rel_arr->nrels = 0;
4472 bruce 635 GIC 2 : }
4715 bruce 636 ECB :
637 :
4472 638 : static void
4472 bruce 639 UIC 0 : print_db_infos(DbInfoArr *db_arr)
4472 bruce 640 ECB : {
641 : int dbnum;
642 :
4472 bruce 643 UIC 0 : for (dbnum = 0; dbnum < db_arr->ndbs; dbnum++)
644 : {
271 tgl 645 UNC 0 : pg_log(PG_VERBOSE, "Database: %s", db_arr->dbs[dbnum].db_name);
4472 bruce 646 LBC 0 : print_rel_infos(&db_arr->dbs[dbnum].rel_arr);
647 : }
4715 bruce 648 UIC 0 : }
4715 bruce 649 ECB :
650 :
651 : static void
3762 bruce 652 LBC 0 : print_rel_infos(RelInfoArr *rel_arr)
653 : {
4715 bruce 654 ECB : int relnum;
655 :
3762 bruce 656 LBC 0 : for (relnum = 0; relnum < rel_arr->nrels; relnum++)
271 tgl 657 UNC 0 : pg_log(PG_VERBOSE, "relname: %s.%s: reloid: %u reltblspace: %s",
3599 sfrost 658 UIC 0 : rel_arr->rels[relnum].nspname,
659 0 : rel_arr->rels[relnum].relname,
660 0 : rel_arr->rels[relnum].reloid,
3599 sfrost 661 LBC 0 : rel_arr->rels[relnum].tablespace);
4715 bruce 662 UIC 0 : }
|