Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * relfilenumber.c
3 : : *
4 : : * relfilenumber functions
5 : : *
6 : : * Copyright (c) 2010-2025, PostgreSQL Global Development Group
7 : : * src/bin/pg_upgrade/relfilenumber.c
8 : : */
9 : :
10 : : #include "postgres_fe.h"
11 : :
12 : : #include <sys/stat.h>
13 : :
14 : : #include "common/file_perm.h"
15 : : #include "common/file_utils.h"
16 : : #include "common/int.h"
17 : : #include "common/logging.h"
18 : : #include "pg_upgrade.h"
19 : :
20 : : static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
21 : : static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
22 : :
23 : : /*
24 : : * The following set of sync_queue_* functions are used for --swap to reduce
25 : : * the amount of time spent synchronizing the swapped catalog files. When a
26 : : * file is added to the queue, we also alert the file system that we'd like it
27 : : * to be persisted to disk in the near future (if that operation is supported
28 : : * by the current platform). Once the queue is full, all of the files are
29 : : * synchronized to disk. This strategy should generally be much faster than
30 : : * simply calling fsync() on the files right away.
31 : : *
32 : : * The general usage pattern should be something like:
33 : : *
34 : : * for (int i = 0; i < num_files; i++)
35 : : * sync_queue_push(files[i]);
36 : : *
37 : : * // be sure to sync any remaining files in the queue
38 : : * sync_queue_sync_all();
39 : : * sync_queue_destroy();
40 : : */
41 : :
42 : : #define SYNC_QUEUE_MAX_LEN (1024)
43 : :
44 : : static char *sync_queue[SYNC_QUEUE_MAX_LEN];
45 : : static bool sync_queue_inited;
46 : : static int sync_queue_len;
47 : :
48 : : static inline void
165 nathan@postgresql.or 49 :UBC 0 : sync_queue_init(void)
50 : : {
51 [ # # ]: 0 : if (sync_queue_inited)
52 : 0 : return;
53 : :
54 : 0 : sync_queue_inited = true;
55 [ # # ]: 0 : for (int i = 0; i < SYNC_QUEUE_MAX_LEN; i++)
56 : 0 : sync_queue[i] = palloc(MAXPGPATH);
57 : : }
58 : :
59 : : static inline void
165 nathan@postgresql.or 60 :CBC 8 : sync_queue_sync_all(void)
61 : : {
62 [ + - ]: 8 : if (!sync_queue_inited)
63 : 8 : return;
64 : :
165 nathan@postgresql.or 65 [ # # ]:UBC 0 : for (int i = 0; i < sync_queue_len; i++)
66 : : {
67 [ # # ]: 0 : if (fsync_fname(sync_queue[i], false) != 0)
68 : 0 : pg_fatal("could not synchronize file \"%s\": %m", sync_queue[i]);
69 : : }
70 : :
71 : 0 : sync_queue_len = 0;
72 : : }
73 : :
74 : : static inline void
75 : 0 : sync_queue_push(const char *fname)
76 : : {
77 : 0 : sync_queue_init();
78 : :
79 : 0 : pre_sync_fname(fname, false);
80 : :
81 : 0 : strncpy(sync_queue[sync_queue_len++], fname, MAXPGPATH);
82 [ # # ]: 0 : if (sync_queue_len >= SYNC_QUEUE_MAX_LEN)
83 : 0 : sync_queue_sync_all();
84 : 0 : }
85 : :
86 : : static inline void
165 nathan@postgresql.or 87 :CBC 8 : sync_queue_destroy(void)
88 : : {
89 [ + - ]: 8 : if (!sync_queue_inited)
90 : 8 : return;
91 : :
165 nathan@postgresql.or 92 :UBC 0 : sync_queue_inited = false;
93 : 0 : sync_queue_len = 0;
94 [ # # ]: 0 : for (int i = 0; i < SYNC_QUEUE_MAX_LEN; i++)
95 : : {
96 : 0 : pfree(sync_queue[i]);
97 : 0 : sync_queue[i] = NULL;
98 : : }
99 : : }
100 : :
101 : : /*
102 : : * transfer_all_new_tablespaces()
103 : : *
104 : : * Responsible for upgrading all database. invokes routines to generate mappings and then
105 : : * physically link the databases.
106 : : */
107 : : void
4623 bruce@momjian.us 108 :CBC 8 : transfer_all_new_tablespaces(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
109 : : char *old_pgdata, char *new_pgdata)
110 : : {
2495 peter_e@gmx.net 111 [ - + + + : 8 : switch (user_opts.transfer_mode)
+ - ]
112 : : {
2495 peter_e@gmx.net 113 :UBC 0 : case TRANSFER_MODE_CLONE:
1293 andres@anarazel.de 114 : 0 : prep_status_progress("Cloning user relation files");
2495 peter_e@gmx.net 115 : 0 : break;
2495 peter_e@gmx.net 116 :CBC 5 : case TRANSFER_MODE_COPY:
1293 andres@anarazel.de 117 : 5 : prep_status_progress("Copying user relation files");
2495 peter_e@gmx.net 118 : 5 : break;
549 tmunro@postgresql.or 119 : 1 : case TRANSFER_MODE_COPY_FILE_RANGE:
120 : 1 : prep_status_progress("Copying user relation files with copy_file_range");
121 : 1 : break;
2495 peter_e@gmx.net 122 : 1 : case TRANSFER_MODE_LINK:
1293 andres@anarazel.de 123 : 1 : prep_status_progress("Linking user relation files");
2495 peter_e@gmx.net 124 : 1 : break;
165 nathan@postgresql.or 125 : 1 : case TRANSFER_MODE_SWAP:
126 : 1 : prep_status_progress("Swapping data directories");
127 : 1 : break;
128 : : }
129 : :
130 : : /*
131 : : * Transferring files by tablespace is tricky because a single database
132 : : * can use multiple tablespaces. For non-parallel mode, we just pass a
133 : : * NULL tablespace path, which matches all tablespaces. In parallel mode,
134 : : * we pass the default tablespace and all user-created tablespaces and let
135 : : * those operations happen in parallel.
136 : : */
4623 bruce@momjian.us 137 [ + - ]: 8 : if (user_opts.jobs <= 1)
138 : 8 : parallel_transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata,
139 : : new_pgdata, NULL, NULL);
140 : : else
141 : : {
142 : : int tblnum;
143 : :
144 : : /* transfer default tablespace */
4623 bruce@momjian.us 145 :UBC 0 : parallel_transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata,
146 : : new_pgdata, old_pgdata, new_pgdata);
147 : :
38 nathan@postgresql.or 148 [ # # ]:UNC 0 : for (tblnum = 0; tblnum < old_cluster.num_tablespaces; tblnum++)
4480 sfrost@snowman.net 149 :UBC 0 : parallel_transfer_all_new_dbs(old_db_arr,
150 : : new_db_arr,
151 : : old_pgdata,
152 : : new_pgdata,
38 nathan@postgresql.or 153 :UNC 0 : old_cluster.tablespaces[tblnum],
154 : 0 : new_cluster.tablespaces[tblnum]);
155 : : /* reap all children */
4623 bruce@momjian.us 156 [ # # ]:UBC 0 : while (reap_child(true) == true)
157 : : ;
158 : : }
159 : :
4623 bruce@momjian.us 160 :CBC 8 : end_progress_output();
161 : 8 : check_ok();
162 : 8 : }
163 : :
164 : :
165 : : /*
166 : : * transfer_all_new_dbs()
167 : : *
168 : : * Responsible for upgrading all database. invokes routines to generate mappings and then
169 : : * physically link the databases.
170 : : */
171 : : void
172 : 8 : transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
173 : : char *old_pgdata, char *new_pgdata,
174 : : char *old_tablespace, char *new_tablespace)
175 : : {
176 : : int old_dbnum,
177 : : new_dbnum;
178 : :
179 : : /* Scan the old cluster databases and transfer their files */
5058 180 : 8 : for (old_dbnum = new_dbnum = 0;
5056 181 [ + + ]: 36 : old_dbnum < old_db_arr->ndbs;
5058 182 : 28 : old_dbnum++, new_dbnum++)
183 : : {
4836 184 : 28 : DbInfo *old_db = &old_db_arr->dbs[old_dbnum],
185 : 28 : *new_db = NULL;
186 : : FileNameMap *mappings;
187 : : int n_maps;
188 : :
189 : : /*
190 : : * Advance past any databases that exist in the new cluster but not in
191 : : * the old, e.g. "postgres". (The user might have removed the
192 : : * 'postgres' database from the old cluster.)
193 : : */
5056 194 [ + - ]: 28 : for (; new_dbnum < new_db_arr->ndbs; new_dbnum++)
195 : : {
196 : 28 : new_db = &new_db_arr->dbs[new_dbnum];
197 [ + - ]: 28 : if (strcmp(old_db->db_name, new_db->db_name) == 0)
198 : 28 : break;
199 : : }
200 : :
201 [ - + ]: 28 : if (new_dbnum >= new_db_arr->ndbs)
1152 tgl@sss.pgh.pa.us 202 :UBC 0 : pg_fatal("old database \"%s\" not found in the new cluster",
203 : : old_db->db_name);
204 : :
5436 bruce@momjian.us 205 :CBC 28 : mappings = gen_db_file_maps(old_db, new_db, &n_maps, old_pgdata,
206 : : new_pgdata);
5596 207 [ + - ]: 28 : if (n_maps)
208 : : {
38 nathan@postgresql.or 209 :GNC 28 : transfer_single_new_db(mappings, n_maps, old_tablespace, new_tablespace);
210 : : }
211 : : /* We allocate something even for n_maps == 0 */
3893 bruce@momjian.us 212 :CBC 28 : pg_free(mappings);
213 : : }
214 : :
215 : : /*
216 : : * Make sure anything pending synchronization in swap mode is fully
217 : : * persisted to disk. This is a no-op for other transfer modes.
218 : : */
165 nathan@postgresql.or 219 : 8 : sync_queue_sync_all();
220 : 8 : sync_queue_destroy();
221 : 8 : }
222 : :
223 : : /*
224 : : * prepare_for_swap()
225 : : *
226 : : * This function moves the database directory from the old cluster to the new
227 : : * cluster in preparation for moving the pg_restore-generated catalog files
228 : : * into place. Returns false if the database with the given OID does not have
229 : : * a directory in the given tablespace, otherwise returns true.
230 : : *
231 : : * This function will return paths in the following variables, which the caller
232 : : * must ensure are sized to MAXPGPATH bytes:
233 : : *
234 : : * old_catalog_dir: The directory for the old cluster's catalog files.
235 : : * new_db_dir: The new cluster's database directory for db_oid.
236 : : * moved_db_dir: Destination for the pg_restore-generated database directory.
237 : : */
238 : : static bool
38 nathan@postgresql.or 239 :GNC 8 : prepare_for_swap(const char *old_tablespace, const char *new_tablespace,
240 : : Oid db_oid, char *old_catalog_dir, char *new_db_dir,
241 : : char *moved_db_dir)
242 : : {
243 : : const char *old_tblspc_suffix;
244 : : const char *new_tblspc_suffix;
245 : : char old_tblspc[MAXPGPATH];
246 : : char new_tblspc[MAXPGPATH];
247 : : char moved_tblspc[MAXPGPATH];
248 : : char old_db_dir[MAXPGPATH];
249 : : struct stat st;
250 : :
165 nathan@postgresql.or 251 [ + + ]:CBC 8 : if (strcmp(old_tablespace, old_cluster.pgdata) == 0)
252 : 4 : old_tblspc_suffix = "/base";
253 : : else
38 nathan@postgresql.or 254 :GNC 4 : old_tblspc_suffix = old_cluster.tablespace_suffix;
255 : :
256 [ + + ]: 8 : if (strcmp(new_tablespace, new_cluster.pgdata) == 0)
257 : 4 : new_tblspc_suffix = "/base";
258 : : else
165 nathan@postgresql.or 259 :GBC 4 : new_tblspc_suffix = new_cluster.tablespace_suffix;
260 : :
261 : : /* Old and new cluster paths. */
165 nathan@postgresql.or 262 :CBC 8 : snprintf(old_tblspc, sizeof(old_tblspc), "%s%s", old_tablespace, old_tblspc_suffix);
263 : 8 : snprintf(new_tblspc, sizeof(new_tblspc), "%s%s", new_tablespace, new_tblspc_suffix);
264 : 8 : snprintf(old_db_dir, sizeof(old_db_dir), "%s/%u", old_tblspc, db_oid);
265 : 8 : snprintf(new_db_dir, MAXPGPATH, "%s/%u", new_tblspc, db_oid);
266 : :
267 : : /*
268 : : * Paths for "moved aside" stuff. We intentionally put these in the old
269 : : * cluster so that the delete_old_cluster.{sh,bat} script handles them.
270 : : */
271 : 8 : snprintf(moved_tblspc, sizeof(moved_tblspc), "%s/moved_for_upgrade", old_tblspc);
272 : 8 : snprintf(old_catalog_dir, MAXPGPATH, "%s/%u_old_catalogs", moved_tblspc, db_oid);
273 : 8 : snprintf(moved_db_dir, MAXPGPATH, "%s/%u", moved_tblspc, db_oid);
274 : :
275 : : /* Check that the database directory exists in the given tablespace. */
276 [ + + ]: 8 : if (stat(old_db_dir, &st) != 0)
277 : : {
165 nathan@postgresql.or 278 [ - + ]:GBC 3 : if (errno != ENOENT)
165 nathan@postgresql.or 279 :UBC 0 : pg_fatal("could not stat file \"%s\": %m", old_db_dir);
165 nathan@postgresql.or 280 :GBC 3 : return false;
281 : : }
282 : :
283 : : /* Create directory for stuff that is moved aside. */
165 nathan@postgresql.or 284 [ - + - - ]:CBC 5 : if (pg_mkdir_p(moved_tblspc, pg_dir_create_mode) != 0 && errno != EEXIST)
82 peter@eisentraut.org 285 :UBC 0 : pg_fatal("could not create directory \"%s\": %m", moved_tblspc);
286 : :
287 : : /* Create directory for old catalog files. */
165 nathan@postgresql.or 288 [ - + ]:CBC 5 : if (pg_mkdir_p(old_catalog_dir, pg_dir_create_mode) != 0)
82 peter@eisentraut.org 289 :UBC 0 : pg_fatal("could not create directory \"%s\": %m", old_catalog_dir);
290 : :
291 : : /* Move the new cluster's database directory aside. */
165 nathan@postgresql.or 292 [ - + ]:CBC 5 : if (rename(new_db_dir, moved_db_dir) != 0)
82 peter@eisentraut.org 293 :UBC 0 : pg_fatal("could not rename directory \"%s\" to \"%s\": %m", new_db_dir, moved_db_dir);
294 : :
295 : : /* Move the old cluster's database directory into place. */
165 nathan@postgresql.or 296 [ - + ]:CBC 5 : if (rename(old_db_dir, new_db_dir) != 0)
82 peter@eisentraut.org 297 :UBC 0 : pg_fatal("could not rename directory \"%s\" to \"%s\": %m", old_db_dir, new_db_dir);
298 : :
165 nathan@postgresql.or 299 :CBC 5 : return true;
300 : : }
301 : :
302 : : /*
303 : : * FileNameMapCmp()
304 : : *
305 : : * qsort() comparator for FileNameMap that sorts by RelFileNumber.
306 : : */
307 : : static int
308 : 4861 : FileNameMapCmp(const void *a, const void *b)
309 : : {
310 : 4861 : const FileNameMap *map1 = (const FileNameMap *) a;
311 : 4861 : const FileNameMap *map2 = (const FileNameMap *) b;
312 : :
313 : 4861 : return pg_cmp_u32(map1->relfilenumber, map2->relfilenumber);
314 : : }
315 : :
316 : : /*
317 : : * parse_relfilenumber()
318 : : *
319 : : * Attempt to parse the RelFileNumber of the given file name. If we can't,
320 : : * return InvalidRelFileNumber. Note that this code snippet is lifted from
321 : : * parse_filename_for_nontemp_relation().
322 : : */
323 : : static RelFileNumber
324 : 2418 : parse_relfilenumber(const char *filename)
325 : : {
326 : : char *endp;
327 : : unsigned long n;
328 : :
329 [ + - + + ]: 2418 : if (filename[0] < '1' || filename[0] > '9')
330 : 24 : return InvalidRelFileNumber;
331 : :
332 : 2394 : errno = 0;
333 : 2394 : n = strtoul(filename, &endp, 10);
334 [ + - + - : 2394 : if (errno || filename == endp || n <= 0 || n > PG_UINT32_MAX)
+ - - + ]
165 nathan@postgresql.or 335 :UBC 0 : return InvalidRelFileNumber;
336 : :
165 nathan@postgresql.or 337 :CBC 2394 : return (RelFileNumber) n;
338 : : }
339 : :
340 : : /*
341 : : * swap_catalog_files()
342 : : *
343 : : * Moves the old catalog files aside, and moves the new catalog files into
344 : : * place. prepare_for_swap() should have already been called (and returned
345 : : * true) for the tablespace/database being transferred.
346 : : *
347 : : * The arguments for the following parameters should be the corresponding
348 : : * variables returned by prepare_for_swap():
349 : : *
350 : : * old_catalog_dir: The directory for the old cluster's catalog files.
351 : : * new_db_dir: New cluster's database directory (for DB being transferred).
352 : : * moved_db_dir: Moved-aside pg_restore-generated database directory.
353 : : */
354 : : static void
355 : 5 : swap_catalog_files(FileNameMap *maps, int size, const char *old_catalog_dir,
356 : : const char *new_db_dir, const char *moved_db_dir)
357 : : {
358 : : DIR *dir;
359 : : struct dirent *de;
360 : : char path[MAXPGPATH];
361 : : char dest[MAXPGPATH];
362 : : RelFileNumber rfn;
363 : :
364 : : /* Move the old catalog files aside. */
365 : 5 : dir = opendir(new_db_dir);
366 [ - + ]: 5 : if (dir == NULL)
165 nathan@postgresql.or 367 :UBC 0 : pg_fatal("could not open directory \"%s\": %m", new_db_dir);
165 nathan@postgresql.or 368 [ + + ]:CBC 1224 : while (errno = 0, (de = readdir(dir)) != NULL)
369 : : {
370 : 1219 : snprintf(path, sizeof(path), "%s/%s", new_db_dir, de->d_name);
371 [ + + ]: 1219 : if (get_dirent_type(path, de, false, PG_LOG_ERROR) != PGFILETYPE_REG)
372 : 10 : continue;
373 : :
374 : 1209 : rfn = parse_relfilenumber(de->d_name);
375 [ + + ]: 1209 : if (RelFileNumberIsValid(rfn))
376 : : {
377 : 1197 : FileNameMap key = {.relfilenumber = rfn};
378 : :
379 [ + + ]: 1197 : if (bsearch(&key, maps, size, sizeof(FileNameMap), FileNameMapCmp))
380 : 13 : continue;
381 : : }
382 : :
383 : 1196 : snprintf(dest, sizeof(dest), "%s/%s", old_catalog_dir, de->d_name);
384 [ - + ]: 1196 : if (rename(path, dest) != 0)
82 peter@eisentraut.org 385 :UBC 0 : pg_fatal("could not rename file \"%s\" to \"%s\": %m", path, dest);
386 : : }
165 nathan@postgresql.or 387 [ - + ]:CBC 5 : if (errno)
165 nathan@postgresql.or 388 :UBC 0 : pg_fatal("could not read directory \"%s\": %m", new_db_dir);
165 nathan@postgresql.or 389 :CBC 5 : (void) closedir(dir);
390 : :
391 : : /* Move the new catalog files into place. */
392 : 5 : dir = opendir(moved_db_dir);
393 [ - + ]: 5 : if (dir == NULL)
165 nathan@postgresql.or 394 :UBC 0 : pg_fatal("could not open directory \"%s\": %m", moved_db_dir);
165 nathan@postgresql.or 395 [ + + ]:CBC 1224 : while (errno = 0, (de = readdir(dir)) != NULL)
396 : : {
397 : 1219 : snprintf(path, sizeof(path), "%s/%s", moved_db_dir, de->d_name);
398 [ + + ]: 1219 : if (get_dirent_type(path, de, false, PG_LOG_ERROR) != PGFILETYPE_REG)
399 : 10 : continue;
400 : :
401 : 1209 : rfn = parse_relfilenumber(de->d_name);
402 [ + + ]: 1209 : if (RelFileNumberIsValid(rfn))
403 : : {
404 : 1197 : FileNameMap key = {.relfilenumber = rfn};
405 : :
406 [ + + ]: 1197 : if (bsearch(&key, maps, size, sizeof(FileNameMap), FileNameMapCmp))
407 : 13 : continue;
408 : : }
409 : :
410 : 1196 : snprintf(dest, sizeof(dest), "%s/%s", new_db_dir, de->d_name);
411 [ - + ]: 1196 : if (rename(path, dest) != 0)
82 peter@eisentraut.org 412 :UBC 0 : pg_fatal("could not rename file \"%s\" to \"%s\": %m", path, dest);
413 : :
414 : : /*
415 : : * We don't fsync() the database files in the file synchronization
416 : : * stage of pg_upgrade in swap mode, so we need to synchronize them
417 : : * ourselves. We only do this for the catalog files because they were
418 : : * created during pg_restore with fsync=off. We assume that the user
419 : : * data files were properly persisted to disk when the user last shut
420 : : * it down.
421 : : */
165 nathan@postgresql.or 422 [ - + ]:CBC 1196 : if (user_opts.do_sync)
165 nathan@postgresql.or 423 :UBC 0 : sync_queue_push(dest);
424 : : }
165 nathan@postgresql.or 425 [ - + ]:CBC 5 : if (errno)
165 nathan@postgresql.or 426 :UBC 0 : pg_fatal("could not read directory \"%s\": %m", moved_db_dir);
165 nathan@postgresql.or 427 :CBC 5 : (void) closedir(dir);
428 : :
429 : : /* Ensure the directory entries are persisted to disk. */
430 [ - + ]: 5 : if (fsync_fname(new_db_dir, true) != 0)
165 nathan@postgresql.or 431 :UBC 0 : pg_fatal("could not synchronize directory \"%s\": %m", new_db_dir);
165 nathan@postgresql.or 432 [ - + ]:CBC 5 : if (fsync_parent_path(new_db_dir) != 0)
165 nathan@postgresql.or 433 :UBC 0 : pg_fatal("could not synchronize parent directory of \"%s\": %m", new_db_dir);
165 nathan@postgresql.or 434 :CBC 5 : }
435 : :
436 : : /*
437 : : * do_swap()
438 : : *
439 : : * Perform the required steps for --swap for a single database. In short this
440 : : * moves the old cluster's database directory into the new cluster and then
441 : : * replaces any files for system catalogs with the ones that were generated
442 : : * during pg_restore.
443 : : */
444 : : static void
38 nathan@postgresql.or 445 :GNC 4 : do_swap(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace)
446 : : {
447 : : char old_catalog_dir[MAXPGPATH];
448 : : char new_db_dir[MAXPGPATH];
449 : : char moved_db_dir[MAXPGPATH];
450 : :
451 : : /*
452 : : * We perform many lookups on maps by relfilenumber in swap mode, so make
453 : : * sure it's sorted by relfilenumber. maps should already be sorted by
454 : : * OID, so in general this shouldn't have much work to do.
455 : : */
165 nathan@postgresql.or 456 :CBC 4 : qsort(maps, size, sizeof(FileNameMap), FileNameMapCmp);
457 : :
458 : : /*
459 : : * If an old tablespace is given, we only need to process that one. If no
460 : : * old tablespace is specified, we need to process all the tablespaces on
461 : : * the system.
462 : : */
463 [ - + ]: 4 : if (old_tablespace)
464 : : {
38 nathan@postgresql.or 465 [ # # ]:UNC 0 : if (prepare_for_swap(old_tablespace, new_tablespace, maps[0].db_oid,
466 : : old_catalog_dir, new_db_dir, moved_db_dir))
165 nathan@postgresql.or 467 :UBC 0 : swap_catalog_files(maps, size,
468 : : old_catalog_dir, new_db_dir, moved_db_dir);
469 : : }
470 : : else
471 : : {
38 nathan@postgresql.or 472 [ + + ]:GNC 4 : if (prepare_for_swap(old_cluster.pgdata, new_cluster.pgdata, maps[0].db_oid,
473 : : old_catalog_dir, new_db_dir, moved_db_dir))
165 nathan@postgresql.or 474 :CBC 3 : swap_catalog_files(maps, size,
475 : : old_catalog_dir, new_db_dir, moved_db_dir);
476 : :
38 nathan@postgresql.or 477 [ + + ]:GNC 8 : for (int tblnum = 0; tblnum < old_cluster.num_tablespaces; tblnum++)
478 : : {
479 [ + + ]: 4 : if (prepare_for_swap(old_cluster.tablespaces[tblnum],
480 : 4 : new_cluster.tablespaces[tblnum],
481 : : maps[0].db_oid,
482 : : old_catalog_dir, new_db_dir, moved_db_dir))
165 nathan@postgresql.or 483 :GBC 2 : swap_catalog_files(maps, size,
484 : : old_catalog_dir, new_db_dir, moved_db_dir);
485 : : }
486 : : }
5596 bruce@momjian.us 487 :CBC 4 : }
488 : :
489 : : /*
490 : : * transfer_single_new_db()
491 : : *
492 : : * create links for mappings stored in "maps" array.
493 : : */
494 : : static void
38 nathan@postgresql.or 495 :GNC 28 : transfer_single_new_db(FileNameMap *maps, int size,
496 : : char *old_tablespace, char *new_tablespace)
497 : : {
498 : : int mapnum;
3466 rhaas@postgresql.org 499 :CBC 28 : bool vm_must_add_frozenbit = false;
500 : :
501 : : /*
502 : : * Do we need to rewrite visibilitymap?
503 : : */
504 [ - + ]: 28 : if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
3466 rhaas@postgresql.org 505 [ # # ]:UBC 0 : new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
506 : 0 : vm_must_add_frozenbit = true;
507 : :
508 : : /* --swap has its own subroutine */
165 nathan@postgresql.or 509 [ + + ]:CBC 28 : if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
510 : : {
511 : : /*
512 : : * We don't support --swap to upgrade from versions that require
513 : : * rewriting the visibility map. We should've failed already if
514 : : * someone tries to do that.
515 : : */
516 [ - + ]: 4 : Assert(!vm_must_add_frozenbit);
517 : :
38 nathan@postgresql.or 518 :GNC 4 : do_swap(maps, size, old_tablespace, new_tablespace);
165 nathan@postgresql.or 519 :CBC 4 : return;
520 : : }
521 : :
5596 bruce@momjian.us 522 [ + + ]: 1532 : for (mapnum = 0; mapnum < size; mapnum++)
523 : : {
4623 524 [ - + ]: 1508 : if (old_tablespace == NULL ||
4623 bruce@momjian.us 525 [ # # ]:UBC 0 : strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
526 : : {
527 : : /* transfer primary file */
3466 rhaas@postgresql.org 528 :CBC 1508 : transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
529 : :
530 : : /*
531 : : * Copy/link any fsm and vm files, if they exist
532 : : */
1796 bruce@momjian.us 533 : 1508 : transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
1362 tgl@sss.pgh.pa.us 534 : 1508 : transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
535 : : }
536 : : }
537 : : }
538 : :
539 : :
540 : : /*
541 : : * transfer_relfile()
542 : : *
543 : : * Copy or link file from old cluster to new one. If vm_must_add_frozenbit
544 : : * is true, visibility map forks are converted and rewritten, even in link
545 : : * mode.
546 : : */
547 : : static void
3466 rhaas@postgresql.org 548 : 4524 : transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
549 : : {
550 : : char old_file[MAXPGPATH];
551 : : char new_file[MAXPGPATH];
552 : : int segno;
553 : : char extent_suffix[65];
554 : : struct stat statbuf;
555 : :
556 : : /*
557 : : * Now copy/link any related segments as well. Remember, PG breaks large
558 : : * files into 1GB segments, the first segment has no extension, subsequent
559 : : * segments are named relfilenumber.1, relfilenumber.2, relfilenumber.3.
560 : : */
4679 bruce@momjian.us 561 : 6345 : for (segno = 0;; segno++)
562 : : {
563 [ + + ]: 6345 : if (segno == 0)
564 : 4524 : extent_suffix[0] = '\0';
565 : : else
566 : 1821 : snprintf(extent_suffix, sizeof(extent_suffix), ".%d", segno);
567 : :
1074 rhaas@postgresql.org 568 : 6345 : snprintf(old_file, sizeof(old_file), "%s%s/%u/%u%s%s",
569 : : map->old_tablespace,
570 : : map->old_tablespace_suffix,
571 : : map->db_oid,
572 : : map->relfilenumber,
573 : : type_suffix,
574 : : extent_suffix);
575 : 6345 : snprintf(new_file, sizeof(new_file), "%s%s/%u/%u%s%s",
576 : : map->new_tablespace,
577 : : map->new_tablespace_suffix,
578 : : map->db_oid,
579 : : map->relfilenumber,
580 : : type_suffix,
581 : : extent_suffix);
582 : :
583 : : /* Is it an extent, fsm, or vm file? */
4679 bruce@momjian.us 584 [ + + + + ]: 6345 : if (type_suffix[0] != '\0' || segno != 0)
585 : : {
586 : : /* Did file open fail? */
3466 rhaas@postgresql.org 587 [ + + ]: 4837 : if (stat(old_file, &statbuf) != 0)
588 : : {
589 : : /* File does not exist? That's OK, just return */
4679 bruce@momjian.us 590 [ + - ]: 4523 : if (errno == ENOENT)
591 : 4523 : return;
592 : : else
543 michael@paquier.xyz 593 :UBC 0 : pg_fatal("error while checking for file existence \"%s.%s\" (\"%s\" to \"%s\"): %m",
594 : : map->nspname, map->relname, old_file, new_file);
595 : : }
596 : :
597 : : /* If file is empty, just return */
3466 rhaas@postgresql.org 598 [ + + ]:CBC 314 : if (statbuf.st_size == 0)
599 : 1 : return;
600 : : }
601 : :
4679 bruce@momjian.us 602 : 1821 : unlink(new_file);
603 : :
604 : : /* Copying files might take some time, so give feedback. */
4656 605 : 1821 : pg_log(PG_STATUS, "%s", old_file);
606 : :
3263 tgl@sss.pgh.pa.us 607 [ - + - - ]: 1821 : if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
608 : : {
609 : : /* Need to rewrite visibility map format */
1152 tgl@sss.pgh.pa.us 610 :UBC 0 : pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
611 : : old_file, new_file);
3263 612 : 0 : rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
613 : : }
614 : : else
2495 peter_e@gmx.net 615 [ - + + + :CBC 1821 : switch (user_opts.transfer_mode)
- - ]
616 : : {
2495 peter_e@gmx.net 617 :UBC 0 : case TRANSFER_MODE_CLONE:
1152 tgl@sss.pgh.pa.us 618 : 0 : pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
619 : : old_file, new_file);
2495 peter_e@gmx.net 620 : 0 : cloneFile(old_file, new_file, map->nspname, map->relname);
621 : 0 : break;
2495 peter_e@gmx.net 622 :CBC 1797 : case TRANSFER_MODE_COPY:
1152 tgl@sss.pgh.pa.us 623 : 1797 : pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
624 : : old_file, new_file);
2495 peter_e@gmx.net 625 : 1797 : copyFile(old_file, new_file, map->nspname, map->relname);
626 : 1797 : break;
549 tmunro@postgresql.or 627 : 12 : case TRANSFER_MODE_COPY_FILE_RANGE:
628 : 12 : pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
629 : : old_file, new_file);
630 : 12 : copyFileByRange(old_file, new_file, map->nspname, map->relname);
631 : 12 : break;
2495 peter_e@gmx.net 632 : 12 : case TRANSFER_MODE_LINK:
1152 tgl@sss.pgh.pa.us 633 : 12 : pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
634 : : old_file, new_file);
2495 peter_e@gmx.net 635 : 12 : linkFile(old_file, new_file, map->nspname, map->relname);
165 nathan@postgresql.or 636 : 12 : break;
165 nathan@postgresql.or 637 :UBC 0 : case TRANSFER_MODE_SWAP:
638 : : /* swap mode is handled in its own code path */
639 : 0 : pg_fatal("should never happen");
640 : : break;
641 : : }
642 : : }
643 : : }
|