]> granicus.if.org Git - postgresql/blob - contrib/pg_upgrade/relfilenode.c
a1e30b1f0ce9124f7f0ead5704177e49d0028076
[postgresql] / contrib / pg_upgrade / relfilenode.c
1 /*
2  *      relfilenode.c
3  *
4  *      relfilenode functions
5  *
6  *      Copyright (c) 2010-2012, PostgreSQL Global Development Group
7  *      contrib/pg_upgrade/relfilenode.c
8  */
9
10 #include "postgres.h"
11
12 #include "pg_upgrade.h"
13
14 #include "catalog/pg_class.h"
15 #include "access/transam.h"
16
17
18 static void transfer_single_new_db(pageCnvCtx *pageConverter,
19                                            FileNameMap *maps, int size);
20 static void transfer_relfile(pageCnvCtx *pageConverter,
21                                  const char *fromfile, const char *tofile,
22                                  const char *nspname, const char *relname);
23
24 /* used by scandir(), must be global */
25 char            scandir_file_pattern[MAXPGPATH];
26
27 /*
28  * transfer_all_new_dbs()
29  *
30  * Responsible for upgrading all database. invokes routines to generate mappings and then
31  * physically link the databases.
32  */
33 const char *
34 transfer_all_new_dbs(DbInfoArr *old_db_arr,
35                                    DbInfoArr *new_db_arr, char *old_pgdata, char *new_pgdata)
36 {
37         int                     old_dbnum, new_dbnum;
38         const char *msg = NULL;
39
40         prep_status("%s user relation files\n",
41                 user_opts.transfer_mode == TRANSFER_MODE_LINK ? "Linking" : "Copying");
42
43         /* Scan the old cluster databases and transfer their files */
44         for (old_dbnum = new_dbnum = 0;
45                  old_dbnum < old_db_arr->ndbs;
46                  old_dbnum++, new_dbnum++)
47         {
48                 DbInfo     *old_db = &old_db_arr->dbs[old_dbnum], *new_db = NULL;
49                 FileNameMap *mappings;
50                 int                     n_maps;
51                 pageCnvCtx *pageConverter = NULL;
52
53                 /*
54                  *      Advance past any databases that exist in the new cluster
55                  *      but not in the old, e.g. "postgres".  (The user might
56                  *      have removed the 'postgres' database from the old cluster.)
57                  */
58                 for (; new_dbnum < new_db_arr->ndbs; new_dbnum++)
59                 {
60                         new_db = &new_db_arr->dbs[new_dbnum];
61                         if (strcmp(old_db->db_name, new_db->db_name) == 0)
62                                 break;
63                 }
64
65                 if (new_dbnum >= new_db_arr->ndbs)
66                         pg_log(PG_FATAL, "old database \"%s\" not found in the new cluster\n",
67                                    old_db->db_name);
68
69                 n_maps = 0;
70                 mappings = gen_db_file_maps(old_db, new_db, &n_maps, old_pgdata,
71                                                                         new_pgdata);
72
73                 if (n_maps)
74                 {
75                         print_maps(mappings, n_maps, new_db->db_name);
76
77 #ifdef PAGE_CONVERSION
78                         msg = setupPageConverter(&pageConverter);
79 #endif
80                         transfer_single_new_db(pageConverter, mappings, n_maps);
81
82                         pg_free(mappings);
83                 }
84         }
85
86         prep_status(" ");                       /* in case nothing printed; pass a space so gcc
87                                                                  * doesn't complain about empty format
88                                                                  * string */
89         check_ok();
90
91         return msg;
92 }
93
94
95 /*
96  * get_pg_database_relfilenode()
97  *
98  *      Retrieves the relfilenode for a few system-catalog tables.      We need these
99  *      relfilenodes later in the upgrade process.
100  */
101 void
102 get_pg_database_relfilenode(ClusterInfo *cluster)
103 {
104         PGconn     *conn = connectToServer(cluster, "template1");
105         PGresult   *res;
106         int                     i_relfile;
107
108         res = executeQueryOrDie(conn,
109                                                         "SELECT c.relname, c.relfilenode "
110                                                         "FROM   pg_catalog.pg_class c, "
111                                                         "               pg_catalog.pg_namespace n "
112                                                         "WHERE  c.relnamespace = n.oid AND "
113                                                         "               n.nspname = 'pg_catalog' AND "
114                                                         "               c.relname = 'pg_database' "
115                                                         "ORDER BY c.relname");
116
117         i_relfile = PQfnumber(res, "relfilenode");
118         cluster->pg_database_oid = atooid(PQgetvalue(res, 0, i_relfile));
119
120         PQclear(res);
121         PQfinish(conn);
122 }
123
124
125 /*
126  * transfer_single_new_db()
127  *
128  * create links for mappings stored in "maps" array.
129  */
130 static void
131 transfer_single_new_db(pageCnvCtx *pageConverter,
132                                            FileNameMap *maps, int size)
133 {
134         char            old_dir[MAXPGPATH];
135         struct dirent **namelist = NULL;
136         int                     numFiles = 0;
137         int                     mapnum;
138         int                     fileno;
139         bool            vm_crashsafe_change = false;
140         
141         old_dir[0] = '\0';
142
143         /* Do not copy non-crashsafe vm files for binaries that assume crashsafety */
144         if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_CRASHSAFE_CAT_VER &&
145                 new_cluster.controldata.cat_ver >= VISIBILITY_MAP_CRASHSAFE_CAT_VER)
146                 vm_crashsafe_change = true;
147         
148         for (mapnum = 0; mapnum < size; mapnum++)
149         {
150                 char            old_file[MAXPGPATH];
151                 char            new_file[MAXPGPATH];
152
153                 /* Changed tablespaces?  Need a new directory scan? */
154                 if (strcmp(maps[mapnum].old_dir, old_dir) != 0)
155                 {
156                         if (numFiles > 0)
157                         {
158                                 for (fileno = 0; fileno < numFiles; fileno++)
159                                         pg_free(namelist[fileno]);
160                                 pg_free(namelist);
161                         }
162
163                         snprintf(old_dir, sizeof(old_dir), "%s", maps[mapnum].old_dir);
164                         numFiles = load_directory(old_dir, &namelist);
165                 }
166
167                 /* Copying files might take some time, so give feedback. */
168
169                 snprintf(old_file, sizeof(old_file), "%s/%u", maps[mapnum].old_dir,
170                                  maps[mapnum].old_relfilenode);
171                 snprintf(new_file, sizeof(new_file), "%s/%u", maps[mapnum].new_dir,
172                                  maps[mapnum].new_relfilenode);
173                 pg_log(PG_REPORT, OVERWRITE_MESSAGE, old_file);
174
175                 /*
176                  * Copy/link the relation file to the new cluster
177                  */
178                 unlink(new_file);
179                 transfer_relfile(pageConverter, old_file, new_file,
180                                                  maps[mapnum].nspname, maps[mapnum].relname);
181
182                 /* fsm/vm files added in PG 8.4 */
183                 if (GET_MAJOR_VERSION(old_cluster.major_version) >= 804)
184                 {
185                         /*
186                          * Copy/link any fsm and vm files, if they exist
187                          */
188                         snprintf(scandir_file_pattern, sizeof(scandir_file_pattern), "%u_",
189                                          maps[mapnum].old_relfilenode);
190
191                         for (fileno = 0; fileno < numFiles; fileno++)
192                         {
193                                 char *vm_offset = strstr(namelist[fileno]->d_name, "_vm");
194                                 bool is_vm_file = false;
195
196                                 /* Is a visibility map file? (name ends with _vm) */
197                                 if (vm_offset && strlen(vm_offset) == strlen("_vm"))
198                                         is_vm_file = true;
199
200                                 if (strncmp(namelist[fileno]->d_name, scandir_file_pattern,
201                                                         strlen(scandir_file_pattern)) == 0 &&
202                                         (!is_vm_file || !vm_crashsafe_change))
203                                 {
204                                         snprintf(old_file, sizeof(old_file), "%s/%s", maps[mapnum].old_dir,
205                                                          namelist[fileno]->d_name);
206                                         snprintf(new_file, sizeof(new_file), "%s/%u%s", maps[mapnum].new_dir,
207                                                          maps[mapnum].new_relfilenode, strchr(namelist[fileno]->d_name, '_'));
208
209                                         unlink(new_file);
210                                         transfer_relfile(pageConverter, old_file, new_file,
211                                                                  maps[mapnum].nspname, maps[mapnum].relname);
212                                 }
213                         }
214                 }
215
216                 /*
217                  * Now copy/link any related segments as well. Remember, PG breaks
218                  * large files into 1GB segments, the first segment has no extension,
219                  * subsequent segments are named relfilenode.1, relfilenode.2,
220                  * relfilenode.3, ...  'fsm' and 'vm' files use underscores so are not
221                  * copied.
222                  */
223                 snprintf(scandir_file_pattern, sizeof(scandir_file_pattern), "%u.",
224                                  maps[mapnum].old_relfilenode);
225
226                 for (fileno = 0; fileno < numFiles; fileno++)
227                 {
228                         if (strncmp(namelist[fileno]->d_name, scandir_file_pattern,
229                                                 strlen(scandir_file_pattern)) == 0)
230                         {
231                                 snprintf(old_file, sizeof(old_file), "%s/%s", maps[mapnum].old_dir,
232                                                  namelist[fileno]->d_name);
233                                 snprintf(new_file, sizeof(new_file), "%s/%u%s", maps[mapnum].new_dir,
234                                                  maps[mapnum].new_relfilenode, strchr(namelist[fileno]->d_name, '.'));
235
236                                 unlink(new_file);
237                                 transfer_relfile(pageConverter, old_file, new_file,
238                                                                  maps[mapnum].nspname, maps[mapnum].relname);
239                         }
240                 }
241         }
242
243
244         if (numFiles > 0)
245         {
246                 for (fileno = 0; fileno < numFiles; fileno++)
247                         pg_free(namelist[fileno]);
248                 pg_free(namelist);
249         }
250 }
251
252
253 /*
254  * transfer_relfile()
255  *
256  * Copy or link file from old cluster to new one.
257  */
258 static void
259 transfer_relfile(pageCnvCtx *pageConverter, const char *old_file,
260                           const char *new_file, const char *nspname, const char *relname)
261 {
262         const char *msg;
263
264         if ((user_opts.transfer_mode == TRANSFER_MODE_LINK) && (pageConverter != NULL))
265                 pg_log(PG_FATAL, "This upgrade requires page-by-page conversion, "
266                            "you must use copy mode instead of link mode.\n");
267
268         if (user_opts.transfer_mode == TRANSFER_MODE_COPY)
269         {
270                 pg_log(PG_INFO, "copying \"%s\" to \"%s\"\n", old_file, new_file);
271
272                 if ((msg = copyAndUpdateFile(pageConverter, old_file, new_file, true)) != NULL)
273                         pg_log(PG_FATAL, "error while copying relation \"%s.%s\" (\"%s\" to \"%s\"): %s\n",
274                                    nspname, relname, old_file, new_file, msg);
275         }
276         else
277         {
278                 pg_log(PG_INFO, "linking \"%s\" to \"%s\"\n", old_file, new_file);
279
280                 if ((msg = linkAndUpdateFile(pageConverter, old_file, new_file)) != NULL)
281                         pg_log(PG_FATAL,
282                                    "error while creating link for relation \"%s.%s\" (\"%s\" to \"%s\"): %s\n",
283                                    nspname, relname, old_file, new_file, msg);
284         }
285         return;
286 }