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 *
40 GIC 6 : gen_db_file_maps(DbInfo *old_db, DbInfo *new_db,
41 ECB : int *nmaps,
42 : const char *old_pgdata, const char *new_pgdata)
43 : {
44 : FileNameMap *maps;
45 : int old_relnum,
46 : new_relnum;
47 GIC 6 : int num_maps = 0;
48 CBC 6 : bool all_matched = true;
49 ECB :
50 : /* There will certainly not be more mappings than there are old rels */
51 GIC 6 : maps = (FileNameMap *) pg_malloc(sizeof(FileNameMap) *
52 CBC 6 : old_db->rel_arr.nrels);
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 : */
59 GIC 6 : old_relnum = new_relnum = 0;
60 CBC 1264 : while (old_relnum < old_db->rel_arr.nrels ||
61 6 : new_relnum < new_db->rel_arr.nrels)
62 ECB : {
63 GIC 2516 : RelInfo *old_rel = (old_relnum < old_db->rel_arr.nrels) ?
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;
67 ECB :
68 : /* handle running off one array before the other */
69 GIC 1258 : if (!new_rel)
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 : */
75 UIC 0 : report_unmatched_relation(old_rel, old_db, false);
76 UBC 0 : all_matched = false;
77 0 : old_relnum++;
78 0 : continue;
79 EUB : }
80 GIC 1258 : if (!old_rel)
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 : */
88 UIC 0 : if (strcmp(new_rel->nspname, "pg_toast") != 0)
89 EUB : {
90 UIC 0 : report_unmatched_relation(new_rel, new_db, true);
91 UBC 0 : all_matched = false;
92 EUB : }
93 UIC 0 : new_relnum++;
94 UBC 0 : continue;
95 EUB : }
96 :
97 : /* check for mismatched OID */
98 GIC 1258 : if (old_rel->reloid < new_rel->reloid)
99 ECB : {
100 : /* old_rel is unmatched, see comment above */
101 UIC 0 : report_unmatched_relation(old_rel, old_db, false);
102 UBC 0 : all_matched = false;
103 0 : old_relnum++;
104 0 : continue;
105 EUB : }
106 GIC 1258 : else if (old_rel->reloid > new_rel->reloid)
107 ECB : {
108 : /* new_rel is unmatched, see comment above */
109 UIC 0 : if (strcmp(new_rel->nspname, "pg_toast") != 0)
110 EUB : {
111 UIC 0 : report_unmatched_relation(new_rel, new_db, true);
112 UBC 0 : all_matched = false;
113 EUB : }
114 UIC 0 : new_relnum++;
115 UBC 0 : continue;
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 : */
123 GIC 1258 : if (strcmp(old_rel->nspname, new_rel->nspname) != 0 ||
124 CBC 1258 : strcmp(old_rel->relname, new_rel->relname) != 0)
125 ECB : {
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;
132 UBC 0 : old_relnum++;
133 0 : new_relnum++;
134 0 : continue;
135 EUB : }
136 :
137 : /* OK, create a mapping entry */
138 GIC 1258 : create_rel_filename_map(old_pgdata, new_pgdata, old_db, new_db,
139 CBC 1258 : old_rel, new_rel, maps + num_maps);
140 1258 : num_maps++;
141 1258 : old_relnum++;
142 1258 : new_relnum++;
143 ECB : }
144 :
145 GIC 6 : if (!all_matched)
146 UNC 0 : pg_fatal("Failed to match up old and new tables in database \"%s\"",
147 EUB : old_db->db_name);
148 :
149 GIC 6 : *nmaps = num_maps;
150 CBC 6 : return maps;
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
160 GIC 1258 : create_rel_filename_map(const char *old_data, const char *new_data,
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. */
166 GIC 1258 : if (strlen(old_rel->tablespace) == 0)
167 ECB : {
168 : /*
169 : * relation belongs to the default tablespace, hence relfiles should
170 : * exist in the data directories.
171 : */
172 GIC 1258 : map->old_tablespace = old_data;
173 CBC 1258 : map->old_tablespace_suffix = "/base";
174 ECB : }
175 : else
176 : {
177 : /* relation belongs to a tablespace, so use the tablespace location */
178 UIC 0 : map->old_tablespace = old_rel->tablespace;
179 UBC 0 : map->old_tablespace_suffix = old_cluster.tablespace_suffix;
180 EUB : }
181 :
182 : /* Do the same for new tablespaces */
183 GIC 1258 : if (strlen(new_rel->tablespace) == 0)
184 ECB : {
185 GIC 1258 : map->new_tablespace = new_data;
186 CBC 1258 : map->new_tablespace_suffix = "/base";
187 ECB : }
188 : else
189 : {
190 UIC 0 : map->new_tablespace = new_rel->tablespace;
191 UBC 0 : map->new_tablespace_suffix = new_cluster.tablespace_suffix;
192 EUB : }
193 :
194 : /* DB oid and relfilenumbers are preserved between old and new cluster */
195 GIC 1258 : map->db_oid = old_db->db_oid;
196 GNC 1258 : map->relfilenumber = old_rel->relfilenumber;
197 ECB :
198 : /* used only for logging and error reporting, old/new are identical */
199 GIC 1258 : map->nspname = old_rel->nspname;
200 CBC 1258 : map->relname = old_rel->relname;
201 1258 : }
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
209 UIC 0 : report_unmatched_relation(const RelInfo *rel, const DbInfo *db, bool is_new_db)
210 EUB : {
211 UIC 0 : Oid reloid = rel->reloid; /* we might change rel below */
212 EUB : char reldesc[1000];
213 : int i;
214 :
215 UIC 0 : snprintf(reldesc, sizeof(reldesc), "\"%s.%s\"",
216 UBC 0 : rel->nspname, rel->relname);
217 0 : if (rel->indtable)
218 EUB : {
219 UIC 0 : for (i = 0; i < db->rel_arr.nrels; i++)
220 EUB : {
221 UIC 0 : const RelInfo *hrel = &db->rel_arr.rels[i];
222 EUB :
223 UIC 0 : if (hrel->reloid == rel->indtable)
224 EUB : {
225 UIC 0 : snprintf(reldesc + strlen(reldesc),
226 UBC 0 : sizeof(reldesc) - strlen(reldesc),
227 0 : _(" which is an index on \"%s.%s\""),
228 0 : hrel->nspname, hrel->relname);
229 EUB : /* Shift attention to index's table for toast check */
230 UIC 0 : rel = hrel;
231 UBC 0 : break;
232 EUB : }
233 : }
234 UIC 0 : if (i >= db->rel_arr.nrels)
235 UBC 0 : snprintf(reldesc + strlen(reldesc),
236 0 : sizeof(reldesc) - strlen(reldesc),
237 0 : _(" which is an index on OID %u"), rel->indtable);
238 EUB : }
239 UIC 0 : if (rel->toastheap)
240 EUB : {
241 UIC 0 : for (i = 0; i < db->rel_arr.nrels; i++)
242 EUB : {
243 UIC 0 : const RelInfo *brel = &db->rel_arr.rels[i];
244 EUB :
245 UIC 0 : if (brel->reloid == rel->toastheap)
246 EUB : {
247 UIC 0 : snprintf(reldesc + strlen(reldesc),
248 UBC 0 : sizeof(reldesc) - strlen(reldesc),
249 0 : _(" which is the TOAST table for \"%s.%s\""),
250 0 : brel->nspname, brel->relname);
251 0 : break;
252 EUB : }
253 : }
254 UIC 0 : if (i >= db->rel_arr.nrels)
255 UBC 0 : snprintf(reldesc + strlen(reldesc),
256 0 : sizeof(reldesc) - strlen(reldesc),
257 0 : _(" which is the TOAST table for OID %u"), rel->toastheap);
258 EUB : }
259 :
260 UIC 0 : if (is_new_db)
261 UNC 0 : pg_log(PG_WARNING, "No match found in old cluster for new relation with OID %u in database \"%s\": %s",
262 UBC 0 : reloid, db->db_name, reldesc);
263 EUB : else
264 UNC 0 : pg_log(PG_WARNING, "No match found in new cluster for old relation with OID %u in database \"%s\": %s",
265 UBC 0 : reloid, db->db_name, reldesc);
266 0 : }
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
275 GIC 5 : get_db_and_rel_infos(ClusterInfo *cluster)
276 ECB : {
277 : int dbnum;
278 :
279 GIC 5 : if (cluster->dbarr.dbs != NULL)
280 CBC 1 : free_db_and_rel_infos(&cluster->dbarr);
281 ECB :
282 GNC 5 : get_template0_info(cluster);
283 GIC 5 : get_db_infos(cluster);
284 ECB :
285 CBC 27 : for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
286 GIC 22 : get_rel_infos(cluster, &cluster->dbarr.dbs[dbnum]);
287 ECB :
288 CBC 5 : if (cluster == &old_cluster)
289 GNC 2 : pg_log(PG_VERBOSE, "\nsource databases:");
290 ECB : else
291 GNC 3 : pg_log(PG_VERBOSE, "\ntarget databases:");
292 :
293 CBC 5 : if (log_opts.verbose)
294 UIC 0 : print_db_infos(&cluster->dbarr);
295 CBC 5 : }
296 EUB :
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
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
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 :
328 GNC 5 : if (PQntuples(dbres) != 1)
329 UNC 0 : pg_fatal("template0 not found");
330 :
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
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 :
374 GIC 5 : snprintf(query, sizeof(query),
375 EUB : "SELECT d.oid, d.datname, d.encoding, d.datcollate, d.datctype, ");
376 GIC 5 : if (GET_MAJOR_VERSION(cluster->major_version) < 1500)
377 UIC 0 : snprintf(query + strlen(query), sizeof(query) - strlen(query),
378 : "'c' AS datlocprovider, NULL AS daticulocale, ");
379 : else
380 GIC 5 : snprintf(query + strlen(query), sizeof(query) - strlen(query),
381 : "datlocprovider, daticulocale, ");
382 CBC 5 : snprintf(query + strlen(query), sizeof(query) - strlen(query),
383 EUB : "pg_catalog.pg_tablespace_location(t.oid) AS spclocation "
384 : "FROM pg_catalog.pg_database d "
385 ECB : " LEFT OUTER JOIN pg_catalog.pg_tablespace t "
386 : " ON d.dattablespace = t.oid "
387 : "WHERE d.datallowconn = true "
388 : "ORDER BY 1");
389 :
390 CBC 5 : res = executeQueryOrDie(conn, "%s", query);
391 ECB :
392 GIC 5 : i_oid = PQfnumber(res, "oid");
393 CBC 5 : i_datname = PQfnumber(res, "datname");
394 GIC 5 : i_spclocation = PQfnumber(res, "spclocation");
395 ECB :
396 GIC 5 : ntups = PQntuples(res);
397 CBC 5 : dbinfos = (DbInfo *) pg_malloc(sizeof(DbInfo) * ntups);
398 :
399 27 : for (tupnum = 0; tupnum < ntups; tupnum++)
400 ECB : {
401 CBC 22 : dbinfos[tupnum].db_oid = atooid(PQgetvalue(res, tupnum, i_oid));
402 GIC 22 : dbinfos[tupnum].db_name = pg_strdup(PQgetvalue(res, tupnum, i_datname));
403 CBC 22 : snprintf(dbinfos[tupnum].db_tablespace, sizeof(dbinfos[tupnum].db_tablespace), "%s",
404 : PQgetvalue(res, tupnum, i_spclocation));
405 ECB : }
406 GIC 5 : PQclear(res);
407 :
408 5 : PQfinish(conn);
409 :
410 5 : cluster->dbarr.dbs = dbinfos;
411 5 : cluster->dbarr.ndbs = ntups;
412 5 : }
413 :
414 :
415 ECB : /*
416 : * get_rel_infos()
417 : *
418 EUB : * gets the relinfos for all the user tables and indexes of the database
419 : * referred to by "dbinfo".
420 : *
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.
423 : */
424 : static void
425 GIC 22 : get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo)
426 : {
427 22 : PGconn *conn = connectToServer(cluster,
428 22 : dbinfo->db_name);
429 : PGresult *res;
430 : RelInfo *relinfos;
431 ECB : int ntups;
432 : int relnum;
433 CBC 22 : int num_rels = 0;
434 22 : char *nspname = NULL;
435 22 : char *relname = NULL;
436 GIC 22 : char *tablespace = NULL;
437 ECB : int i_spclocation,
438 : i_nspname,
439 : i_relname,
440 : i_reloid,
441 : i_indtable,
442 : i_toastheap,
443 : i_relfilenumber,
444 : i_reltablespace;
445 : char query[QUERY_ALLOC];
446 GIC 22 : char *last_namespace = NULL,
447 CBC 22 : *last_tablespace = NULL;
448 :
449 22 : query[0] = '\0'; /* initialize query string to empty */
450 :
451 ECB : /*
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 : */
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 "
466 ECB : " WHERE relkind IN (" CppAsString2(RELKIND_RELATION) ", "
467 : CppAsString2(RELKIND_MATVIEW) ") AND "
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 "
475 : " relname IN ('pg_largeobject') ))), ",
476 : FirstNormalObjectId);
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 : */
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 "
487 ECB : " ON regular_heap.reloid = c.oid "
488 : " WHERE c.reltoastrelid != 0), ");
489 :
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 : */
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 "
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 : */
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 "
524 ECB : " ON c.reltablespace = t.oid "
525 : "ORDER BY 1;");
526 :
527 GIC 22 : res = executeQueryOrDie(conn, "%s", query);
528 :
529 22 : ntups = PQntuples(res);
530 :
531 22 : relinfos = (RelInfo *) pg_malloc(sizeof(RelInfo) * ntups);
532 :
533 22 : i_reloid = PQfnumber(res, "reloid");
534 22 : i_indtable = PQfnumber(res, "indtable");
535 22 : i_toastheap = PQfnumber(res, "toastheap");
536 22 : i_nspname = PQfnumber(res, "nspname");
537 CBC 22 : i_relname = PQfnumber(res, "relname");
538 GNC 22 : i_relfilenumber = PQfnumber(res, "relfilenode");
539 GIC 22 : i_reltablespace = PQfnumber(res, "reltablespace");
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 :
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 :
550 3782 : nspname = PQgetvalue(res, relnum, i_nspname);
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? */
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 :
567 3782 : relname = PQgetvalue(res, relnum, i_relname);
568 CBC 3782 : curr->relname = pg_strdup(relname);
569 :
570 GNC 3782 : curr->relfilenumber = atooid(PQgetvalue(res, relnum, i_relfilenumber));
571 GIC 3782 : curr->tblsp_alloc = false;
572 ECB :
573 : /* Is the tablespace oid non-default? */
574 CBC 3782 : if (atooid(PQgetvalue(res, relnum, i_reltablespace)) != 0)
575 ECB : {
576 : /*
577 : * The tablespace location might be "", meaning the cluster
578 : * default location, i.e. pg_default or pg_global.
579 : */
580 LBC 0 : tablespace = PQgetvalue(res, relnum, i_spclocation);
581 ECB :
582 : /* Can we reuse the previous string allocation? */
583 LBC 0 : if (last_tablespace && strcmp(tablespace, last_tablespace) == 0)
584 UIC 0 : curr->tablespace = last_tablespace;
585 ECB : else
586 : {
587 LBC 0 : last_tablespace = curr->tablespace = pg_strdup(tablespace);
588 0 : curr->tblsp_alloc = true;
589 ECB : }
590 : }
591 : else
592 : /* A zero reltablespace oid indicates the database tablespace. */
593 GIC 3782 : curr->tablespace = dbinfo->db_tablespace;
594 : }
595 22 : PQclear(res);
596 :
597 22 : PQfinish(conn);
598 :
599 22 : dbinfo->rel_arr.rels = relinfos;
600 CBC 22 : dbinfo->rel_arr.nrels = num_rels;
601 22 : }
602 :
603 :
604 ECB : static void
605 CBC 1 : free_db_and_rel_infos(DbInfoArr *db_arr)
606 : {
607 : int dbnum;
608 ECB :
609 CBC 3 : for (dbnum = 0; dbnum < db_arr->ndbs; dbnum++)
610 : {
611 2 : free_rel_infos(&db_arr->dbs[dbnum].rel_arr);
612 2 : pg_free(db_arr->dbs[dbnum].db_name);
613 : }
614 GIC 1 : pg_free(db_arr->dbs);
615 CBC 1 : db_arr->dbs = NULL;
616 GIC 1 : db_arr->ndbs = 0;
617 1 : }
618 :
619 :
620 : static void
621 GBC 2 : free_rel_infos(RelInfoArr *rel_arr)
622 : {
623 : int relnum;
624 EUB :
625 GBC 6 : for (relnum = 0; relnum < rel_arr->nrels; relnum++)
626 : {
627 GIC 4 : if (rel_arr->rels[relnum].nsp_alloc)
628 GBC 2 : pg_free(rel_arr->rels[relnum].nspname);
629 4 : pg_free(rel_arr->rels[relnum].relname);
630 GIC 4 : if (rel_arr->rels[relnum].tblsp_alloc)
631 UIC 0 : pg_free(rel_arr->rels[relnum].tablespace);
632 : }
633 GIC 2 : pg_free(rel_arr->rels);
634 CBC 2 : rel_arr->nrels = 0;
635 GIC 2 : }
636 ECB :
637 :
638 : static void
639 UIC 0 : print_db_infos(DbInfoArr *db_arr)
640 ECB : {
641 : int dbnum;
642 :
643 UIC 0 : for (dbnum = 0; dbnum < db_arr->ndbs; dbnum++)
644 : {
645 UNC 0 : pg_log(PG_VERBOSE, "Database: %s", db_arr->dbs[dbnum].db_name);
646 LBC 0 : print_rel_infos(&db_arr->dbs[dbnum].rel_arr);
647 : }
648 UIC 0 : }
649 ECB :
650 :
651 : static void
652 LBC 0 : print_rel_infos(RelInfoArr *rel_arr)
653 : {
654 ECB : int relnum;
655 :
656 LBC 0 : for (relnum = 0; relnum < rel_arr->nrels; relnum++)
657 UNC 0 : pg_log(PG_VERBOSE, "relname: %s.%s: reloid: %u reltblspace: %s",
658 UIC 0 : rel_arr->rels[relnum].nspname,
659 0 : rel_arr->rels[relnum].relname,
660 0 : rel_arr->rels[relnum].reloid,
661 LBC 0 : rel_arr->rels[relnum].tablespace);
662 UIC 0 : }
|