Line data Source code
1 : /* 2 : * tablespace.c 3 : * 4 : * tablespace functions 5 : * 6 : * Copyright (c) 2010-2025, 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 30 : init_tablespaces(void) 20 : { 21 30 : get_tablespace_paths(); 22 : 23 30 : set_tablespace_directory_suffix(&old_cluster); 24 30 : set_tablespace_directory_suffix(&new_cluster); 25 : 26 30 : if (old_cluster.num_tablespaces > 0 && 27 10 : strcmp(old_cluster.tablespace_suffix, new_cluster.tablespace_suffix) == 0) 28 : { 29 20 : 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 10 : 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 30 : } 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 30 : get_tablespace_paths(void) 51 : { 52 30 : PGconn *conn = connectToServer(&old_cluster, "template1"); 53 : PGresult *res; 54 : int tblnum; 55 : int i_spclocation; 56 : char query[QUERY_ALLOC]; 57 : 58 30 : 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 30 : res = executeQueryOrDie(conn, "%s", query); 65 : 66 30 : old_cluster.num_tablespaces = PQntuples(res); 67 30 : new_cluster.num_tablespaces = PQntuples(res); 68 : 69 30 : if (PQntuples(res) != 0) 70 : { 71 10 : old_cluster.tablespaces = 72 10 : (char **) pg_malloc(old_cluster.num_tablespaces * sizeof(char *)); 73 10 : new_cluster.tablespaces = 74 10 : (char **) pg_malloc(new_cluster.num_tablespaces * sizeof(char *)); 75 : } 76 : else 77 : { 78 20 : old_cluster.tablespaces = NULL; 79 20 : new_cluster.tablespaces = NULL; 80 : } 81 : 82 30 : i_spclocation = PQfnumber(res, "spclocation"); 83 : 84 40 : for (tblnum = 0; tblnum < old_cluster.num_tablespaces; tblnum++) 85 : { 86 : struct stat statBuf; 87 10 : 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 10 : 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 10 : old_cluster.tablespaces[tblnum] = psprintf("%s/%s", old_cluster.pgdata, spcloc); 106 10 : 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 10 : 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 10 : 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 30 : PQclear(res); 136 : 137 30 : PQfinish(conn); 138 30 : } 139 : 140 : 141 : static void 142 60 : 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 120 : cluster->tablespace_suffix = psprintf("/PG_%s_%d", 148 60 : cluster->major_version_str, 149 : cluster->controldata.cat_ver); 150 60 : }