LCOV - code coverage report
Current view: top level - src/bin/pg_upgrade - tablespace.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 78.3 % 46 36
Test Date: 2026-07-16 11:17:59 Functions: 100.0 % 3 3
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 65.0 % 20 13

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  *  tablespace.c
       3                 :             :  *
       4                 :             :  *  tablespace functions
       5                 :             :  *
       6                 :             :  *  Copyright (c) 2010-2026, PostgreSQL Global Development Group
       7                 :             :  *  src/bin/pg_upgrade/tablespace.c
       8                 :             :  */
       9                 :             : 
      10                 :             : #include "postgres_fe.h"
      11                 :             : 
      12                 :             : #include "pg_upgrade.h"
      13                 :             : 
      14                 :             : static void get_tablespace_paths(void);
      15                 :             : static void set_tablespace_directory_suffix(ClusterInfo *cluster);
      16                 :             : 
      17                 :             : 
      18                 :             : void
      19                 :          17 : init_tablespaces(void)
      20                 :             : {
      21                 :          17 :     get_tablespace_paths();
      22                 :             : 
      23                 :          17 :     set_tablespace_directory_suffix(&old_cluster);
      24                 :          17 :     set_tablespace_directory_suffix(&new_cluster);
      25                 :             : 
      26         [ +  + ]:          17 :     if (old_cluster.num_tablespaces > 0 &&
      27         [ +  - ]:           5 :         strcmp(old_cluster.tablespace_suffix, new_cluster.tablespace_suffix) == 0)
      28                 :             :     {
      29         [ +  + ]:          10 :         for (int i = 0; i < old_cluster.num_tablespaces; i++)
      30                 :             :         {
      31                 :             :             /*
      32                 :             :              * In-place tablespaces are okay for same-version upgrades because
      33                 :             :              * their paths will differ between clusters.
      34                 :             :              */
      35         [ -  + ]:           5 :             if (strcmp(old_cluster.tablespaces[i], new_cluster.tablespaces[i]) == 0)
      36                 :           0 :                 pg_fatal("Cannot upgrade to/from the same system catalog version when\n"
      37                 :             :                          "using tablespaces.");
      38                 :             :         }
      39                 :             :     }
      40                 :          17 : }
      41                 :             : 
      42                 :             : 
      43                 :             : /*
      44                 :             :  * get_tablespace_paths()
      45                 :             :  *
      46                 :             :  * Scans pg_tablespace and returns a malloc'ed array of all tablespace
      47                 :             :  * paths. It's the caller's responsibility to free the array.
      48                 :             :  */
      49                 :             : static void
      50                 :          17 : get_tablespace_paths(void)
      51                 :             : {
      52                 :          17 :     PGconn     *conn = connectToServer(&old_cluster, "template1");
      53                 :             :     PGresult   *res;
      54                 :             :     int         tblnum;
      55                 :             :     int         i_spclocation;
      56                 :             :     char        query[QUERY_ALLOC];
      57                 :             : 
      58                 :          17 :     snprintf(query, sizeof(query),
      59                 :             :              "SELECT pg_catalog.pg_tablespace_location(oid) AS spclocation "
      60                 :             :              "FROM pg_catalog.pg_tablespace "
      61                 :             :              "WHERE    spcname != 'pg_default' AND "
      62                 :             :              "     spcname != 'pg_global'");
      63                 :             : 
      64                 :          17 :     res = executeQueryOrDie(conn, "%s", query);
      65                 :             : 
      66                 :          17 :     old_cluster.num_tablespaces = PQntuples(res);
      67                 :          17 :     new_cluster.num_tablespaces = PQntuples(res);
      68                 :             : 
      69         [ +  + ]:          17 :     if (PQntuples(res) != 0)
      70                 :             :     {
      71                 :           5 :         old_cluster.tablespaces =
      72                 :           5 :             pg_malloc_array(char *, old_cluster.num_tablespaces);
      73                 :           5 :         new_cluster.tablespaces =
      74                 :           5 :             pg_malloc_array(char *, new_cluster.num_tablespaces);
      75                 :             :     }
      76                 :             :     else
      77                 :             :     {
      78                 :          12 :         old_cluster.tablespaces = NULL;
      79                 :          12 :         new_cluster.tablespaces = NULL;
      80                 :             :     }
      81                 :             : 
      82                 :          17 :     i_spclocation = PQfnumber(res, "spclocation");
      83                 :             : 
      84         [ +  + ]:          22 :     for (tblnum = 0; tblnum < old_cluster.num_tablespaces; tblnum++)
      85                 :             :     {
      86                 :             :         struct stat statBuf;
      87                 :           5 :         char       *spcloc = PQgetvalue(res, tblnum, i_spclocation);
      88                 :             : 
      89                 :             :         /*
      90                 :             :          * For now, we do not expect non-in-place tablespaces to move during
      91                 :             :          * upgrade.  If that changes, it will likely become necessary to run
      92                 :             :          * the above query on the new cluster, too.
      93                 :             :          *
      94                 :             :          * pg_tablespace_location() returns absolute paths for non-in-place
      95                 :             :          * tablespaces and relative paths for in-place ones, so we use
      96                 :             :          * is_absolute_path() to distinguish between them.
      97                 :             :          */
      98         [ -  + ]:           5 :         if (is_absolute_path(PQgetvalue(res, tblnum, i_spclocation)))
      99                 :             :         {
     100                 :           0 :             old_cluster.tablespaces[tblnum] = pg_strdup(spcloc);
     101                 :           0 :             new_cluster.tablespaces[tblnum] = old_cluster.tablespaces[tblnum];
     102                 :             :         }
     103                 :             :         else
     104                 :             :         {
     105                 :           5 :             old_cluster.tablespaces[tblnum] = psprintf("%s/%s", old_cluster.pgdata, spcloc);
     106                 :           5 :             new_cluster.tablespaces[tblnum] = psprintf("%s/%s", new_cluster.pgdata, spcloc);
     107                 :             :         }
     108                 :             : 
     109                 :             :         /*
     110                 :             :          * Check that the tablespace path exists and is a directory.
     111                 :             :          * Effectively, this is checking only for tables/indexes in
     112                 :             :          * non-existent tablespace directories.  Databases located in
     113                 :             :          * non-existent tablespaces already throw a backend error.
     114                 :             :          * Non-existent tablespace directories can occur when a data directory
     115                 :             :          * that contains user tablespaces is moved as part of pg_upgrade
     116                 :             :          * preparation and the symbolic links are not updated.
     117                 :             :          */
     118         [ -  + ]:           5 :         if (stat(old_cluster.tablespaces[tblnum], &statBuf) != 0)
     119                 :             :         {
     120         [ #  # ]:           0 :             if (errno == ENOENT)
     121                 :           0 :                 report_status(PG_FATAL,
     122                 :             :                               "tablespace directory \"%s\" does not exist",
     123                 :           0 :                               old_cluster.tablespaces[tblnum]);
     124                 :             :             else
     125                 :           0 :                 report_status(PG_FATAL,
     126                 :             :                               "could not stat tablespace directory \"%s\": %m",
     127                 :           0 :                               old_cluster.tablespaces[tblnum]);
     128                 :             :         }
     129         [ -  + ]:           5 :         if (!S_ISDIR(statBuf.st_mode))
     130                 :           0 :             report_status(PG_FATAL,
     131                 :             :                           "tablespace path \"%s\" is not a directory",
     132                 :           0 :                           old_cluster.tablespaces[tblnum]);
     133                 :             :     }
     134                 :             : 
     135                 :          17 :     PQclear(res);
     136                 :             : 
     137                 :          17 :     PQfinish(conn);
     138                 :          17 : }
     139                 :             : 
     140                 :             : 
     141                 :             : static void
     142                 :          34 : set_tablespace_directory_suffix(ClusterInfo *cluster)
     143                 :             : {
     144                 :             :     /* This cluster has a version-specific subdirectory */
     145                 :             : 
     146                 :             :     /* The leading slash is needed to start a new directory. */
     147                 :          34 :     cluster->tablespace_suffix = psprintf("/PG_%s_%d",
     148                 :             :                                           cluster->major_version_str,
     149                 :             :                                           cluster->controldata.cat_ver);
     150                 :          34 : }
        

Generated by: LCOV version 2.0-1