Branch data Line data Source code
1 : : /*
2 : : * relfilenumber.c
3 : : *
4 : : * relfilenumber functions
5 : : *
6 : : * Copyright (c) 2010-2026, 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);
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
49 : 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
60 : 10 : sync_queue_sync_all(void)
61 : : {
62 [ + - ]: 10 : if (!sync_queue_inited)
63 : 10 : return;
64 : :
65 [ # # ]: 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
87 : 10 : sync_queue_destroy(void)
88 : : {
89 [ + - ]: 10 : if (!sync_queue_inited)
90 : 10 : return;
91 : :
92 : 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
108 : 10 : transfer_all_new_tablespaces(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
109 : : char *old_pgdata, char *new_pgdata)
110 : : {
111 [ - + + + : 10 : switch (user_opts.transfer_mode)
+ - ]
112 : : {
113 : 0 : case TRANSFER_MODE_CLONE:
114 : 0 : prep_status_progress("Cloning user relation files");
115 : 0 : break;
116 : 7 : case TRANSFER_MODE_COPY:
117 : 7 : prep_status_progress("Copying user relation files");
118 : 7 : break;
119 : 1 : case TRANSFER_MODE_COPY_FILE_RANGE:
120 : 1 : prep_status_progress("Copying user relation files with copy_file_range");
121 : 1 : break;
122 : 1 : case TRANSFER_MODE_LINK:
123 : 1 : prep_status_progress("Linking user relation files");
124 : 1 : break;
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 : : */
137 [ + - ]: 10 : if (user_opts.jobs <= 1)
138 : 10 : 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 */
145 : 0 : parallel_transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata,
146 : : new_pgdata, old_pgdata, new_pgdata);
147 : :
148 [ # # ]: 0 : for (tblnum = 0; tblnum < old_cluster.num_tablespaces; tblnum++)
149 : 0 : parallel_transfer_all_new_dbs(old_db_arr,
150 : : new_db_arr,
151 : : old_pgdata,
152 : : new_pgdata,
153 : 0 : old_cluster.tablespaces[tblnum],
154 : 0 : new_cluster.tablespaces[tblnum]);
155 : : /* reap all children */
156 [ # # ]: 0 : while (reap_child(true) == true)
157 : : ;
158 : : }
159 : :
160 : 10 : end_progress_output();
161 : 10 : check_ok();
162 : 10 : }
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 : 10 : 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 */
180 : 10 : for (old_dbnum = new_dbnum = 0;
181 [ + + ]: 42 : old_dbnum < old_db_arr->ndbs;
182 : 32 : old_dbnum++, new_dbnum++)
183 : : {
184 : 32 : DbInfo *old_db = &old_db_arr->dbs[old_dbnum],
185 : 32 : *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 : : */
194 [ + - ]: 32 : for (; new_dbnum < new_db_arr->ndbs; new_dbnum++)
195 : : {
196 : 32 : new_db = &new_db_arr->dbs[new_dbnum];
197 [ + - ]: 32 : if (strcmp(old_db->db_name, new_db->db_name) == 0)
198 : 32 : break;
199 : : }
200 : :
201 [ - + ]: 32 : if (new_dbnum >= new_db_arr->ndbs)
202 : 0 : pg_fatal("old database \"%s\" not found in the new cluster",
203 : : old_db->db_name);
204 : :
205 : 32 : mappings = gen_db_file_maps(old_db, new_db, &n_maps, old_pgdata,
206 : : new_pgdata);
207 [ + - ]: 32 : if (n_maps)
208 : : {
209 : 32 : transfer_single_new_db(mappings, n_maps, old_tablespace, new_tablespace);
210 : : }
211 : : /* We allocate something even for n_maps == 0 */
212 : 32 : 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 : : */
219 : 10 : sync_queue_sync_all();
220 : 10 : sync_queue_destroy();
221 : 10 : }
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
239 : 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 : :
251 [ + + ]: 8 : if (strcmp(old_tablespace, old_cluster.pgdata) == 0)
252 : 4 : old_tblspc_suffix = "/base";
253 : : else
254 : 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
259 : 4 : new_tblspc_suffix = new_cluster.tablespace_suffix;
260 : :
261 : : /* Old and new cluster paths. */
262 : 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 : : {
278 [ - + ]: 3 : if (errno != ENOENT)
279 : 0 : pg_fatal("could not stat file \"%s\": %m", old_db_dir);
280 : 3 : return false;
281 : : }
282 : :
283 : : /* Create directory for stuff that is moved aside. */
284 [ - + - - ]: 5 : if (pg_mkdir_p(moved_tblspc, pg_dir_create_mode) != 0 && errno != EEXIST)
285 : 0 : pg_fatal("could not create directory \"%s\": %m", moved_tblspc);
286 : :
287 : : /* Create directory for old catalog files. */
288 [ - + ]: 5 : if (pg_mkdir_p(old_catalog_dir, pg_dir_create_mode) != 0)
289 : 0 : pg_fatal("could not create directory \"%s\": %m", old_catalog_dir);
290 : :
291 : : /* Move the new cluster's database directory aside. */
292 [ - + ]: 5 : if (rename(new_db_dir, moved_db_dir) != 0)
293 : 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. */
296 [ - + ]: 5 : if (rename(old_db_dir, new_db_dir) != 0)
297 : 0 : pg_fatal("could not rename directory \"%s\" to \"%s\": %m", old_db_dir, new_db_dir);
298 : :
299 : 5 : return true;
300 : : }
301 : :
302 : : /*
303 : : * FileNameMapCmp()
304 : : *
305 : : * qsort() comparator for FileNameMap that sorts by RelFileNumber.
306 : : */
307 : : static int
308 : 7157 : FileNameMapCmp(const void *a, const void *b)
309 : : {
310 : 7157 : const FileNameMap *map1 = (const FileNameMap *) a;
311 : 7157 : const FileNameMap *map2 = (const FileNameMap *) b;
312 : :
313 : 7157 : 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 : 2578 : parse_relfilenumber(const char *filename)
325 : : {
326 : : char *endp;
327 : : unsigned long n;
328 : :
329 [ + - + + ]: 2578 : if (filename[0] < '1' || filename[0] > '9')
330 : 24 : return InvalidRelFileNumber;
331 : :
332 : 2554 : errno = 0;
333 : 2554 : n = strtoul(filename, &endp, 10);
334 [ + - + - : 2554 : if (errno || filename == endp || n <= 0 || n > PG_UINT32_MAX)
+ - - + ]
335 : 0 : return InvalidRelFileNumber;
336 : :
337 : 2554 : 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)
367 : 0 : pg_fatal("could not open directory \"%s\": %m", new_db_dir);
368 [ + + ]: 1304 : while (errno = 0, (de = readdir(dir)) != NULL)
369 : : {
370 : 1299 : snprintf(path, sizeof(path), "%s/%s", new_db_dir, de->d_name);
371 [ + + ]: 1299 : if (get_dirent_type(path, de, false, PG_LOG_ERROR) != PGFILETYPE_REG)
372 : 10 : continue;
373 : :
374 : 1289 : rfn = parse_relfilenumber(de->d_name);
375 [ + + ]: 1289 : if (RelFileNumberIsValid(rfn))
376 : : {
377 : 1277 : FileNameMap key = {.relfilenumber = rfn};
378 : :
379 [ + + ]: 1277 : if (bsearch(&key, maps, size, sizeof(FileNameMap), FileNameMapCmp))
380 : 21 : continue;
381 : : }
382 : :
383 : 1268 : snprintf(dest, sizeof(dest), "%s/%s", old_catalog_dir, de->d_name);
384 [ - + ]: 1268 : if (rename(path, dest) != 0)
385 : 0 : pg_fatal("could not rename file \"%s\" to \"%s\": %m", path, dest);
386 : : }
387 [ - + ]: 5 : if (errno)
388 : 0 : pg_fatal("could not read directory \"%s\": %m", new_db_dir);
389 : 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)
394 : 0 : pg_fatal("could not open directory \"%s\": %m", moved_db_dir);
395 [ + + ]: 1304 : while (errno = 0, (de = readdir(dir)) != NULL)
396 : : {
397 : 1299 : snprintf(path, sizeof(path), "%s/%s", moved_db_dir, de->d_name);
398 [ + + ]: 1299 : if (get_dirent_type(path, de, false, PG_LOG_ERROR) != PGFILETYPE_REG)
399 : 10 : continue;
400 : :
401 : 1289 : rfn = parse_relfilenumber(de->d_name);
402 [ + + ]: 1289 : if (RelFileNumberIsValid(rfn))
403 : : {
404 : 1277 : FileNameMap key = {.relfilenumber = rfn};
405 : :
406 [ + + ]: 1277 : if (bsearch(&key, maps, size, sizeof(FileNameMap), FileNameMapCmp))
407 : 21 : continue;
408 : : }
409 : :
410 : 1268 : snprintf(dest, sizeof(dest), "%s/%s", new_db_dir, de->d_name);
411 [ - + ]: 1268 : if (rename(path, dest) != 0)
412 : 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 : : */
422 [ - + ]: 1268 : if (user_opts.do_sync)
423 : 0 : sync_queue_push(dest);
424 : : }
425 [ - + ]: 5 : if (errno)
426 : 0 : pg_fatal("could not read directory \"%s\": %m", moved_db_dir);
427 : 5 : (void) closedir(dir);
428 : :
429 : : /* Ensure the directory entries are persisted to disk. */
430 [ - + ]: 5 : if (fsync_fname(new_db_dir, true) != 0)
431 : 0 : pg_fatal("could not synchronize directory \"%s\": %m", new_db_dir);
432 [ - + ]: 5 : if (fsync_parent_path(new_db_dir) != 0)
433 : 0 : pg_fatal("could not synchronize parent directory of \"%s\": %m", new_db_dir);
434 : 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
445 : 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 : : */
456 : 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 : : {
465 [ # # ]: 0 : if (prepare_for_swap(old_tablespace, new_tablespace, maps[0].db_oid,
466 : : old_catalog_dir, new_db_dir, moved_db_dir))
467 : 0 : swap_catalog_files(maps, size,
468 : : old_catalog_dir, new_db_dir, moved_db_dir);
469 : : }
470 : : else
471 : : {
472 [ + + ]: 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))
474 : 3 : swap_catalog_files(maps, size,
475 : : old_catalog_dir, new_db_dir, moved_db_dir);
476 : :
477 [ + + ]: 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))
483 : 2 : swap_catalog_files(maps, size,
484 : : old_catalog_dir, new_db_dir, moved_db_dir);
485 : : }
486 : : }
487 : 4 : }
488 : :
489 : : /*
490 : : * transfer_single_new_db()
491 : : *
492 : : * create links for mappings stored in "maps" array.
493 : : */
494 : : static void
495 : 32 : transfer_single_new_db(FileNameMap *maps, int size,
496 : : char *old_tablespace, char *new_tablespace)
497 : : {
498 : : int mapnum;
499 : :
500 : : /* --swap has its own subroutine */
501 [ + + ]: 32 : if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
502 : : {
503 : 4 : do_swap(maps, size, old_tablespace, new_tablespace);
504 : 4 : return;
505 : : }
506 : :
507 [ + + ]: 1729 : for (mapnum = 0; mapnum < size; mapnum++)
508 : : {
509 [ - + ]: 1701 : if (old_tablespace == NULL ||
510 [ # # ]: 0 : strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
511 : : {
512 : : /* transfer primary file */
513 : 1701 : transfer_relfile(&maps[mapnum], "");
514 : :
515 : : /*
516 : : * Copy/link any fsm and vm files, if they exist
517 : : */
518 : 1701 : transfer_relfile(&maps[mapnum], "_fsm");
519 : 1701 : transfer_relfile(&maps[mapnum], "_vm");
520 : : }
521 : : }
522 : : }
523 : :
524 : :
525 : : /*
526 : : * transfer_relfile()
527 : : *
528 : : * Copy or link file from old cluster to new one.
529 : : */
530 : : static void
531 : 5103 : transfer_relfile(FileNameMap *map, const char *type_suffix)
532 : : {
533 : : char old_file[MAXPGPATH];
534 : : char new_file[MAXPGPATH];
535 : : int segno;
536 : : char extent_suffix[65];
537 : : struct stat statbuf;
538 : :
539 : : /*
540 : : * Now copy/link any related segments as well. Remember, PG breaks large
541 : : * files into 1GB segments, the first segment has no extension, subsequent
542 : : * segments are named relfilenumber.1, relfilenumber.2, relfilenumber.3.
543 : : */
544 : 7153 : for (segno = 0;; segno++)
545 : : {
546 [ + + ]: 7153 : if (segno == 0)
547 : 5103 : extent_suffix[0] = '\0';
548 : : else
549 : 2050 : snprintf(extent_suffix, sizeof(extent_suffix), ".%d", segno);
550 : :
551 : 7153 : snprintf(old_file, sizeof(old_file), "%s%s/%u/%u%s%s",
552 : : map->old_tablespace,
553 : : map->old_tablespace_suffix,
554 : : map->db_oid,
555 : : map->relfilenumber,
556 : : type_suffix,
557 : : extent_suffix);
558 : 7153 : snprintf(new_file, sizeof(new_file), "%s%s/%u/%u%s%s",
559 : : map->new_tablespace,
560 : : map->new_tablespace_suffix,
561 : : map->db_oid,
562 : : map->relfilenumber,
563 : : type_suffix,
564 : : extent_suffix);
565 : :
566 : : /* Is it an extent, fsm, or vm file? */
567 [ + + + + ]: 7153 : if (type_suffix[0] != '\0' || segno != 0)
568 : : {
569 : : /* Did file open fail? */
570 [ + + ]: 5452 : if (stat(old_file, &statbuf) != 0)
571 : : {
572 : : /* File does not exist? That's OK, just return */
573 [ + - ]: 5103 : if (errno == ENOENT)
574 : 5103 : return;
575 : : else
576 : 0 : pg_fatal("error while checking for file existence \"%s.%s\" (\"%s\" to \"%s\"): %m",
577 : : map->nspname, map->relname, old_file, new_file);
578 : : }
579 : :
580 : : /* If file is empty, just return */
581 [ - + ]: 349 : if (statbuf.st_size == 0)
582 : 0 : return;
583 : : }
584 : :
585 : 2050 : unlink(new_file);
586 : :
587 : : /* Copying files might take some time, so give feedback. */
588 : 2050 : pg_log(PG_STATUS, "%s", old_file);
589 : :
590 [ - + + + : 2050 : switch (user_opts.transfer_mode)
- - ]
591 : : {
592 : 0 : case TRANSFER_MODE_CLONE:
593 : 0 : pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
594 : : old_file, new_file);
595 : 0 : cloneFile(old_file, new_file, map->nspname, map->relname);
596 : 0 : break;
597 : 2010 : case TRANSFER_MODE_COPY:
598 : 2010 : pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
599 : : old_file, new_file);
600 : 2010 : copyFile(old_file, new_file, map->nspname, map->relname);
601 : 2010 : break;
602 : 20 : case TRANSFER_MODE_COPY_FILE_RANGE:
603 : 20 : pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
604 : : old_file, new_file);
605 : 20 : copyFileByRange(old_file, new_file, map->nspname, map->relname);
606 : 20 : break;
607 : 20 : case TRANSFER_MODE_LINK:
608 : 20 : pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
609 : : old_file, new_file);
610 : 20 : linkFile(old_file, new_file, map->nspname, map->relname);
611 : 20 : break;
612 : 0 : case TRANSFER_MODE_SWAP:
613 : : /* swap mode is handled in its own code path */
614 : 0 : pg_fatal("should never happen");
615 : : break;
616 : : }
617 : : }
618 : : }
|