LCOV - code coverage report
Current view: top level - src/bin/pg_upgrade - relfilenumber.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 161 222 72.5 %
Date: 2025-08-31 10:17:55 Functions: 11 13 84.6 %
Legend: Lines: hit not hit

          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
      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          16 : sync_queue_sync_all(void)
      61             : {
      62          16 :     if (!sync_queue_inited)
      63          16 :         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          16 : sync_queue_destroy(void)
      88             : {
      89          16 :     if (!sync_queue_inited)
      90          16 :         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          16 : transfer_all_new_tablespaces(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr,
     109             :                              char *old_pgdata, char *new_pgdata)
     110             : {
     111          16 :     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          10 :         case TRANSFER_MODE_COPY:
     117          10 :             prep_status_progress("Copying user relation files");
     118          10 :             break;
     119           2 :         case TRANSFER_MODE_COPY_FILE_RANGE:
     120           2 :             prep_status_progress("Copying user relation files with copy_file_range");
     121           2 :             break;
     122           2 :         case TRANSFER_MODE_LINK:
     123           2 :             prep_status_progress("Linking user relation files");
     124           2 :             break;
     125           2 :         case TRANSFER_MODE_SWAP:
     126           2 :             prep_status_progress("Swapping data directories");
     127           2 :             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          16 :     if (user_opts.jobs <= 1)
     138          16 :         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          16 :     end_progress_output();
     161          16 :     check_ok();
     162          16 : }
     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          16 : 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          16 :     for (old_dbnum = new_dbnum = 0;
     181          72 :          old_dbnum < old_db_arr->ndbs;
     182          56 :          old_dbnum++, new_dbnum++)
     183             :     {
     184          56 :         DbInfo     *old_db = &old_db_arr->dbs[old_dbnum],
     185          56 :                    *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          56 :         for (; new_dbnum < new_db_arr->ndbs; new_dbnum++)
     195             :         {
     196          56 :             new_db = &new_db_arr->dbs[new_dbnum];
     197          56 :             if (strcmp(old_db->db_name, new_db->db_name) == 0)
     198          56 :                 break;
     199             :         }
     200             : 
     201          56 :         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          56 :         mappings = gen_db_file_maps(old_db, new_db, &n_maps, old_pgdata,
     206             :                                     new_pgdata);
     207          56 :         if (n_maps)
     208             :         {
     209          56 :             transfer_single_new_db(mappings, n_maps, old_tablespace, new_tablespace);
     210             :         }
     211             :         /* We allocate something even for n_maps == 0 */
     212          56 :         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          16 :     sync_queue_sync_all();
     220          16 :     sync_queue_destroy();
     221          16 : }
     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          16 : 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          16 :     if (strcmp(old_tablespace, old_cluster.pgdata) == 0)
     252           8 :         old_tblspc_suffix = "/base";
     253             :     else
     254           8 :         old_tblspc_suffix = old_cluster.tablespace_suffix;
     255             : 
     256          16 :     if (strcmp(new_tablespace, new_cluster.pgdata) == 0)
     257           8 :         new_tblspc_suffix = "/base";
     258             :     else
     259           8 :         new_tblspc_suffix = new_cluster.tablespace_suffix;
     260             : 
     261             :     /* Old and new cluster paths. */
     262          16 :     snprintf(old_tblspc, sizeof(old_tblspc), "%s%s", old_tablespace, old_tblspc_suffix);
     263          16 :     snprintf(new_tblspc, sizeof(new_tblspc), "%s%s", new_tablespace, new_tblspc_suffix);
     264          16 :     snprintf(old_db_dir, sizeof(old_db_dir), "%s/%u", old_tblspc, db_oid);
     265          16 :     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          16 :     snprintf(moved_tblspc, sizeof(moved_tblspc), "%s/moved_for_upgrade", old_tblspc);
     272          16 :     snprintf(old_catalog_dir, MAXPGPATH, "%s/%u_old_catalogs", moved_tblspc, db_oid);
     273          16 :     snprintf(moved_db_dir, MAXPGPATH, "%s/%u", moved_tblspc, db_oid);
     274             : 
     275             :     /* Check that the database directory exists in the given tablespace. */
     276          16 :     if (stat(old_db_dir, &st) != 0)
     277             :     {
     278           6 :         if (errno != ENOENT)
     279           0 :             pg_fatal("could not stat file \"%s\": %m", old_db_dir);
     280           6 :         return false;
     281             :     }
     282             : 
     283             :     /* Create directory for stuff that is moved aside. */
     284          10 :     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          10 :     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          10 :     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          10 :     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          10 :     return true;
     300             : }
     301             : 
     302             : /*
     303             :  * FileNameMapCmp()
     304             :  *
     305             :  * qsort() comparator for FileNameMap that sorts by RelFileNumber.
     306             :  */
     307             : static int
     308        9722 : FileNameMapCmp(const void *a, const void *b)
     309             : {
     310        9722 :     const FileNameMap *map1 = (const FileNameMap *) a;
     311        9722 :     const FileNameMap *map2 = (const FileNameMap *) b;
     312             : 
     313        9722 :     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        4836 : parse_relfilenumber(const char *filename)
     325             : {
     326             :     char       *endp;
     327             :     unsigned long n;
     328             : 
     329        4836 :     if (filename[0] < '1' || filename[0] > '9')
     330          48 :         return InvalidRelFileNumber;
     331             : 
     332        4788 :     errno = 0;
     333        4788 :     n = strtoul(filename, &endp, 10);
     334        4788 :     if (errno || filename == endp || n <= 0 || n > PG_UINT32_MAX)
     335           0 :         return InvalidRelFileNumber;
     336             : 
     337        4788 :     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          10 : 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          10 :     dir = opendir(new_db_dir);
     366          10 :     if (dir == NULL)
     367           0 :         pg_fatal("could not open directory \"%s\": %m", new_db_dir);
     368        2448 :     while (errno = 0, (de = readdir(dir)) != NULL)
     369             :     {
     370        2438 :         snprintf(path, sizeof(path), "%s/%s", new_db_dir, de->d_name);
     371        2438 :         if (get_dirent_type(path, de, false, PG_LOG_ERROR) != PGFILETYPE_REG)
     372          20 :             continue;
     373             : 
     374        2418 :         rfn = parse_relfilenumber(de->d_name);
     375        2418 :         if (RelFileNumberIsValid(rfn))
     376             :         {
     377        2394 :             FileNameMap key = {.relfilenumber = rfn};
     378             : 
     379        2394 :             if (bsearch(&key, maps, size, sizeof(FileNameMap), FileNameMapCmp))
     380          26 :                 continue;
     381             :         }
     382             : 
     383        2392 :         snprintf(dest, sizeof(dest), "%s/%s", old_catalog_dir, de->d_name);
     384        2392 :         if (rename(path, dest) != 0)
     385           0 :             pg_fatal("could not rename file \"%s\" to \"%s\": %m", path, dest);
     386             :     }
     387          10 :     if (errno)
     388           0 :         pg_fatal("could not read directory \"%s\": %m", new_db_dir);
     389          10 :     (void) closedir(dir);
     390             : 
     391             :     /* Move the new catalog files into place. */
     392          10 :     dir = opendir(moved_db_dir);
     393          10 :     if (dir == NULL)
     394           0 :         pg_fatal("could not open directory \"%s\": %m", moved_db_dir);
     395        2448 :     while (errno = 0, (de = readdir(dir)) != NULL)
     396             :     {
     397        2438 :         snprintf(path, sizeof(path), "%s/%s", moved_db_dir, de->d_name);
     398        2438 :         if (get_dirent_type(path, de, false, PG_LOG_ERROR) != PGFILETYPE_REG)
     399          20 :             continue;
     400             : 
     401        2418 :         rfn = parse_relfilenumber(de->d_name);
     402        2418 :         if (RelFileNumberIsValid(rfn))
     403             :         {
     404        2394 :             FileNameMap key = {.relfilenumber = rfn};
     405             : 
     406        2394 :             if (bsearch(&key, maps, size, sizeof(FileNameMap), FileNameMapCmp))
     407          26 :                 continue;
     408             :         }
     409             : 
     410        2392 :         snprintf(dest, sizeof(dest), "%s/%s", new_db_dir, de->d_name);
     411        2392 :         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        2392 :         if (user_opts.do_sync)
     423           0 :             sync_queue_push(dest);
     424             :     }
     425          10 :     if (errno)
     426           0 :         pg_fatal("could not read directory \"%s\": %m", moved_db_dir);
     427          10 :     (void) closedir(dir);
     428             : 
     429             :     /* Ensure the directory entries are persisted to disk. */
     430          10 :     if (fsync_fname(new_db_dir, true) != 0)
     431           0 :         pg_fatal("could not synchronize directory \"%s\": %m", new_db_dir);
     432          10 :     if (fsync_parent_path(new_db_dir) != 0)
     433           0 :         pg_fatal("could not synchronize parent directory of \"%s\": %m", new_db_dir);
     434          10 : }
     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           8 : 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           8 :     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           8 :     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           8 :         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           6 :             swap_catalog_files(maps, size,
     475             :                                old_catalog_dir, new_db_dir, moved_db_dir);
     476             : 
     477          16 :         for (int tblnum = 0; tblnum < old_cluster.num_tablespaces; tblnum++)
     478             :         {
     479           8 :             if (prepare_for_swap(old_cluster.tablespaces[tblnum],
     480           8 :                                  new_cluster.tablespaces[tblnum],
     481             :                                  maps[0].db_oid,
     482             :                                  old_catalog_dir, new_db_dir, moved_db_dir))
     483           4 :                 swap_catalog_files(maps, size,
     484             :                                    old_catalog_dir, new_db_dir, moved_db_dir);
     485             :         }
     486             :     }
     487           8 : }
     488             : 
     489             : /*
     490             :  * transfer_single_new_db()
     491             :  *
     492             :  * create links for mappings stored in "maps" array.
     493             :  */
     494             : static void
     495          56 : transfer_single_new_db(FileNameMap *maps, int size,
     496             :                        char *old_tablespace, char *new_tablespace)
     497             : {
     498             :     int         mapnum;
     499          56 :     bool        vm_must_add_frozenbit = false;
     500             : 
     501             :     /*
     502             :      * Do we need to rewrite visibilitymap?
     503             :      */
     504          56 :     if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
     505           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 */
     509          56 :     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             :         Assert(!vm_must_add_frozenbit);
     517             : 
     518           8 :         do_swap(maps, size, old_tablespace, new_tablespace);
     519           8 :         return;
     520             :     }
     521             : 
     522        3064 :     for (mapnum = 0; mapnum < size; mapnum++)
     523             :     {
     524        3016 :         if (old_tablespace == NULL ||
     525           0 :             strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
     526             :         {
     527             :             /* transfer primary file */
     528        3016 :             transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
     529             : 
     530             :             /*
     531             :              * Copy/link any fsm and vm files, if they exist
     532             :              */
     533        3016 :             transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
     534        3016 :             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
     548        9048 : 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             :      */
     561       12654 :     for (segno = 0;; segno++)
     562             :     {
     563       12654 :         if (segno == 0)
     564        9048 :             extent_suffix[0] = '\0';
     565             :         else
     566        3606 :             snprintf(extent_suffix, sizeof(extent_suffix), ".%d", segno);
     567             : 
     568       12654 :         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       12654 :         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? */
     584       12654 :         if (type_suffix[0] != '\0' || segno != 0)
     585             :         {
     586             :             /* Did file open fail? */
     587        9638 :             if (stat(old_file, &statbuf) != 0)
     588             :             {
     589             :                 /* File does not exist?  That's OK, just return */
     590        9046 :                 if (errno == ENOENT)
     591        9046 :                     return;
     592             :                 else
     593           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 */
     598         592 :             if (statbuf.st_size == 0)
     599           2 :                 return;
     600             :         }
     601             : 
     602        3606 :         unlink(new_file);
     603             : 
     604             :         /* Copying files might take some time, so give feedback. */
     605        3606 :         pg_log(PG_STATUS, "%s", old_file);
     606             : 
     607        3606 :         if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
     608             :         {
     609             :             /* Need to rewrite visibility map format */
     610           0 :             pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
     611             :                    old_file, new_file);
     612           0 :             rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
     613             :         }
     614             :         else
     615        3606 :             switch (user_opts.transfer_mode)
     616             :             {
     617           0 :                 case TRANSFER_MODE_CLONE:
     618           0 :                     pg_log(PG_VERBOSE, "cloning \"%s\" to \"%s\"",
     619             :                            old_file, new_file);
     620           0 :                     cloneFile(old_file, new_file, map->nspname, map->relname);
     621           0 :                     break;
     622        3558 :                 case TRANSFER_MODE_COPY:
     623        3558 :                     pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\"",
     624             :                            old_file, new_file);
     625        3558 :                     copyFile(old_file, new_file, map->nspname, map->relname);
     626        3558 :                     break;
     627          24 :                 case TRANSFER_MODE_COPY_FILE_RANGE:
     628          24 :                     pg_log(PG_VERBOSE, "copying \"%s\" to \"%s\" with copy_file_range",
     629             :                            old_file, new_file);
     630          24 :                     copyFileByRange(old_file, new_file, map->nspname, map->relname);
     631          24 :                     break;
     632          24 :                 case TRANSFER_MODE_LINK:
     633          24 :                     pg_log(PG_VERBOSE, "linking \"%s\" to \"%s\"",
     634             :                            old_file, new_file);
     635          24 :                     linkFile(old_file, new_file, map->nspname, map->relname);
     636          24 :                     break;
     637           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             : }

Generated by: LCOV version 1.16