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