]> granicus.if.org Git - postgresql/blob - contrib/pg_upgrade/info.c
72bc489d0e856eec37c3e05880ff9f29a29b8dea
[postgresql] / contrib / pg_upgrade / info.c
1 /*
2  *      info.c
3  *
4  *      information support functions
5  *
6  *      Copyright (c) 2010-2011, PostgreSQL Global Development Group
7  *      contrib/pg_upgrade/info.c
8  */
9
10 #include "pg_upgrade.h"
11
12 #include "access/transam.h"
13
14
15 static void create_rel_filename_map(const char *old_data, const char *new_data,
16                                                 const DbInfo *old_db, const DbInfo *new_db,
17                                                 const RelInfo *old_rel, const RelInfo *new_rel,
18                                                 FileNameMap *map);
19 static void get_db_infos(ClusterInfo *cluster);
20 static void get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo);
21 static void free_rel_infos(RelInfoArr *rel_arr);
22 static void print_db_infos(DbInfoArr *dbinfo);
23 static void print_rel_infos(RelInfoArr *arr);
24
25
26 /*
27  * gen_db_file_maps()
28  *
29  * generates database mappings for "old_db" and "new_db". Returns a malloc'ed
30  * array of mappings. nmaps is a return parameter which refers to the number
31  * mappings.
32  */
33 FileNameMap *
34 gen_db_file_maps(DbInfo *old_db, DbInfo *new_db,
35                                  int *nmaps, const char *old_pgdata, const char *new_pgdata)
36 {
37         FileNameMap *maps;
38         int                     relnum;
39         int                     num_maps = 0;
40
41         if (old_db->rel_arr.nrels != new_db->rel_arr.nrels)
42                 pg_log(PG_FATAL, "old and new databases \"%s\" have a different number of relations\n",
43                            old_db->db_name);
44
45         maps = (FileNameMap *) pg_malloc(sizeof(FileNameMap) *
46                                                                          old_db->rel_arr.nrels);
47
48         for (relnum = 0; relnum < old_db->rel_arr.nrels; relnum++)
49         {
50                 RelInfo    *old_rel = &old_db->rel_arr.rels[relnum];
51                 RelInfo    *new_rel = &new_db->rel_arr.rels[relnum];
52
53                 if (old_rel->reloid != new_rel->reloid)
54                         pg_log(PG_FATAL, "Mismatch of relation OID in database \"%s\": old OID %d, new OID %d\n",
55                                    old_db->db_name, old_rel->reloid, new_rel->reloid);
56
57                 /*
58                  * In pre-8.4, TOAST table names change during CLUSTER;  in >= 8.4
59                  * TOAST relation names always use heap table oids, hence we cannot
60                  * check relation names when upgrading from pre-8.4.
61                  */
62                 if (strcmp(old_rel->nspname, new_rel->nspname) != 0 ||
63                         ((GET_MAJOR_VERSION(old_cluster.major_version) >= 804 ||
64                           strcmp(old_rel->nspname, "pg_toast") != 0) &&
65                          strcmp(old_rel->relname, new_rel->relname) != 0))
66                         pg_log(PG_FATAL, "Mismatch of relation names in database \"%s\": "
67                                    "old name \"%s.%s\", new name \"%s.%s\"\n",
68                                    old_db->db_name, old_rel->nspname, old_rel->relname,
69                                    new_rel->nspname, new_rel->relname);
70
71                 create_rel_filename_map(old_pgdata, new_pgdata, old_db, new_db,
72                                                                 old_rel, new_rel, maps + num_maps);
73                 num_maps++;
74         }
75
76         *nmaps = num_maps;
77         return maps;
78 }
79
80
81 /*
82  * create_rel_filename_map()
83  *
84  * fills a file node map structure and returns it in "map".
85  */
86 static void
87 create_rel_filename_map(const char *old_data, const char *new_data,
88                                                 const DbInfo *old_db, const DbInfo *new_db,
89                                                 const RelInfo *old_rel, const RelInfo *new_rel,
90                                                 FileNameMap *map)
91 {
92         if (strlen(old_rel->tablespace) == 0)
93         {
94                 /*
95                  * relation belongs to the default tablespace, hence relfiles should
96                  * exist in the data directories.
97                  */
98                 snprintf(map->old_dir, sizeof(map->old_dir), "%s/base/%u", old_data,
99                                  old_db->db_oid);
100                 snprintf(map->new_dir, sizeof(map->new_dir), "%s/base/%u", new_data,
101                                  new_db->db_oid);
102         }
103         else
104         {
105                 /* relation belongs to a tablespace, so use the tablespace location */
106                 snprintf(map->old_dir, sizeof(map->old_dir), "%s%s/%u", old_rel->tablespace,
107                                  old_cluster.tablespace_suffix, old_db->db_oid);
108                 snprintf(map->new_dir, sizeof(map->new_dir), "%s%s/%u", new_rel->tablespace,
109                                  new_cluster.tablespace_suffix, new_db->db_oid);
110         }
111
112         /*
113          * old_relfilenode might differ from pg_class.oid (and hence
114          * new_relfilenode) because of CLUSTER, REINDEX, or VACUUM FULL.
115          */
116         map->old_relfilenode = old_rel->relfilenode;
117
118         /* new_relfilenode will match old and new pg_class.oid */
119         map->new_relfilenode = new_rel->relfilenode;
120
121         /* used only for logging and error reporing, old/new are identical */
122         snprintf(map->nspname, sizeof(map->nspname), "%s", old_rel->nspname);
123         snprintf(map->relname, sizeof(map->relname), "%s", old_rel->relname);
124 }
125
126
127 void
128 print_maps(FileNameMap *maps, int n_maps, const char *db_name)
129 {
130         if (log_opts.debug)
131         {
132                 int                     mapnum;
133
134                 pg_log(PG_DEBUG, "mappings for database \"%s\":\n", db_name);
135
136                 for (mapnum = 0; mapnum < n_maps; mapnum++)
137                         pg_log(PG_DEBUG, "%s.%s: %u to %u\n",
138                                    maps[mapnum].nspname, maps[mapnum].relname,
139                                    maps[mapnum].old_relfilenode,
140                                    maps[mapnum].new_relfilenode);
141
142                 pg_log(PG_DEBUG, "\n\n");
143         }
144 }
145
146
147 /*
148  * get_db_and_rel_infos()
149  *
150  * higher level routine to generate dbinfos for the database running
151  * on the given "port". Assumes that server is already running.
152  */
153 void
154 get_db_and_rel_infos(ClusterInfo *cluster)
155 {
156         int                     dbnum;
157
158         if (cluster->dbarr.dbs != NULL)
159                 free_db_and_rel_infos(&cluster->dbarr);
160
161         get_db_infos(cluster);
162
163         for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
164                 get_rel_infos(cluster, &cluster->dbarr.dbs[dbnum]);
165
166         if (log_opts.debug)
167         {
168                 pg_log(PG_DEBUG, "\n%s databases:\n", CLUSTER_NAME(cluster));
169                 print_db_infos(&cluster->dbarr);
170         }
171 }
172
173
174 /*
175  * get_db_infos()
176  *
177  * Scans pg_database system catalog and populates all user
178  * databases.
179  */
180 static void
181 get_db_infos(ClusterInfo *cluster)
182 {
183         PGconn     *conn = connectToServer(cluster, "template1");
184         PGresult   *res;
185         int                     ntups;
186         int                     tupnum;
187         DbInfo     *dbinfos;
188         int                     i_datname,
189                                 i_oid,
190                                 i_spclocation;
191
192         res = executeQueryOrDie(conn,
193                                                         "SELECT d.oid, d.datname, t.spclocation "
194                                                         "FROM pg_catalog.pg_database d "
195                                                         " LEFT OUTER JOIN pg_catalog.pg_tablespace t "
196                                                         " ON d.dattablespace = t.oid "
197                                                         "WHERE d.datallowconn = true "
198         /* we don't preserve pg_database.oid so we sort by name */
199                                                         "ORDER BY 2");
200
201         i_oid = PQfnumber(res, "oid");
202         i_datname = PQfnumber(res, "datname");
203         i_spclocation = PQfnumber(res, "spclocation");
204
205         ntups = PQntuples(res);
206         dbinfos = (DbInfo *) pg_malloc(sizeof(DbInfo) * ntups);
207
208         for (tupnum = 0; tupnum < ntups; tupnum++)
209         {
210                 dbinfos[tupnum].db_oid = atooid(PQgetvalue(res, tupnum, i_oid));
211                 snprintf(dbinfos[tupnum].db_name, sizeof(dbinfos[tupnum].db_name), "%s",
212                                  PQgetvalue(res, tupnum, i_datname));
213                 snprintf(dbinfos[tupnum].db_tblspace, sizeof(dbinfos[tupnum].db_tblspace), "%s",
214                                  PQgetvalue(res, tupnum, i_spclocation));
215         }
216         PQclear(res);
217
218         PQfinish(conn);
219
220         cluster->dbarr.dbs = dbinfos;
221         cluster->dbarr.ndbs = ntups;
222 }
223
224
225 /*
226  * get_rel_infos()
227  *
228  * gets the relinfos for all the user tables of the database refered
229  * by "db".
230  *
231  * NOTE: we assume that relations/entities with oids greater than
232  * FirstNormalObjectId belongs to the user
233  */
234 static void
235 get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo)
236 {
237         PGconn     *conn = connectToServer(cluster,
238                                                                            dbinfo->db_name);
239         PGresult   *res;
240         RelInfo    *relinfos;
241         int                     ntups;
242         int                     relnum;
243         int                     num_rels = 0;
244         char       *nspname = NULL;
245         char       *relname = NULL;
246         int                     i_spclocation,
247                                 i_nspname,
248                                 i_relname,
249                                 i_oid,
250                                 i_relfilenode;
251         char            query[QUERY_ALLOC];
252
253         /*
254          * pg_largeobject contains user data that does not appear in pg_dumpall
255          * --schema-only output, so we have to copy that system table heap and
256          * index.  We could grab the pg_largeobject oids from template1, but it is
257          * easy to treat it as a normal table. Order by oid so we can join old/new
258          * structures efficiently.
259          */
260
261         snprintf(query, sizeof(query),
262                          "SELECT c.oid, n.nspname, c.relname, "
263                          "      c.relfilenode, t.spclocation "
264                          "FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n "
265                          "         ON c.relnamespace = n.oid "
266                          "  LEFT OUTER JOIN pg_catalog.pg_tablespace t "
267                          "         ON c.reltablespace = t.oid "
268                          "WHERE relkind IN ('r','t', 'i'%s) AND "
269                          "  ((n.nspname NOT IN ('pg_catalog', 'information_schema', 'binary_upgrade') AND "
270                          "        c.oid >= %u) "
271                          "  OR (n.nspname = 'pg_catalog' AND "
272         "    relname IN ('pg_largeobject', 'pg_largeobject_loid_pn_index'%s) )) "
273         /* we preserve pg_class.oid so we sort by it to match old/new */
274                          "ORDER BY 1;",
275         /* see the comment at the top of old_8_3_create_sequence_script() */
276                          (GET_MAJOR_VERSION(old_cluster.major_version) <= 803) ?
277                          "" : ", 'S'",
278         /* this oid allows us to skip system toast tables */
279                          FirstNormalObjectId,
280         /* does pg_largeobject_metadata need to be migrated? */
281                          (GET_MAJOR_VERSION(old_cluster.major_version) <= 804) ?
282         "" : ", 'pg_largeobject_metadata', 'pg_largeobject_metadata_oid_index'");
283
284         res = executeQueryOrDie(conn, query);
285
286         ntups = PQntuples(res);
287
288         relinfos = (RelInfo *) pg_malloc(sizeof(RelInfo) * ntups);
289
290         i_oid = PQfnumber(res, "oid");
291         i_nspname = PQfnumber(res, "nspname");
292         i_relname = PQfnumber(res, "relname");
293         i_relfilenode = PQfnumber(res, "relfilenode");
294         i_spclocation = PQfnumber(res, "spclocation");
295
296         for (relnum = 0; relnum < ntups; relnum++)
297         {
298                 RelInfo    *curr = &relinfos[num_rels++];
299                 const char *tblspace;
300
301                 curr->reloid = atooid(PQgetvalue(res, relnum, i_oid));
302
303                 nspname = PQgetvalue(res, relnum, i_nspname);
304                 strlcpy(curr->nspname, nspname, sizeof(curr->nspname));
305
306                 relname = PQgetvalue(res, relnum, i_relname);
307                 strlcpy(curr->relname, relname, sizeof(curr->relname));
308
309                 curr->relfilenode = atooid(PQgetvalue(res, relnum, i_relfilenode));
310
311                 tblspace = PQgetvalue(res, relnum, i_spclocation);
312                 /* if no table tablespace, use the database tablespace */
313                 if (strlen(tblspace) == 0)
314                         tblspace = dbinfo->db_tblspace;
315                 strlcpy(curr->tablespace, tblspace, sizeof(curr->tablespace));
316         }
317         PQclear(res);
318
319         PQfinish(conn);
320
321         dbinfo->rel_arr.rels = relinfos;
322         dbinfo->rel_arr.nrels = num_rels;
323 }
324
325
326 void
327 free_db_and_rel_infos(DbInfoArr *db_arr)
328 {
329         int                     dbnum;
330
331         for (dbnum = 0; dbnum < db_arr->ndbs; dbnum++)
332                 free_rel_infos(&db_arr->dbs[dbnum].rel_arr);
333         pg_free(db_arr->dbs);
334         db_arr->dbs = NULL;
335         db_arr->ndbs = 0;
336 }
337
338
339 static void
340 free_rel_infos(RelInfoArr *rel_arr)
341 {
342         pg_free(rel_arr->rels);
343         rel_arr->nrels = 0;
344 }
345
346
347 static void
348 print_db_infos(DbInfoArr *db_arr)
349 {
350         int                     dbnum;
351
352         for (dbnum = 0; dbnum < db_arr->ndbs; dbnum++)
353         {
354                 pg_log(PG_DEBUG, "Database: %s\n", db_arr->dbs[dbnum].db_name);
355                 print_rel_infos(&db_arr->dbs[dbnum].rel_arr);
356                 pg_log(PG_DEBUG, "\n\n");
357         }
358 }
359
360
361 static void
362 print_rel_infos(RelInfoArr *arr)
363 {
364         int                     relnum;
365
366         for (relnum = 0; relnum < arr->nrels; relnum++)
367                 pg_log(PG_DEBUG, "relname: %s.%s: reloid: %u reltblspace: %s\n",
368                            arr->rels[relnum].nspname, arr->rels[relnum].relname,
369                            arr->rels[relnum].reloid, arr->rels[relnum].tablespace);
370 }