Age Owner Branch data TLA 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
5436 bruce@momjian.us 19 :CBC 15 : init_tablespaces(void)
20 : : {
21 : 15 : get_tablespace_paths();
22 : :
5362 23 : 15 : set_tablespace_directory_suffix(&old_cluster);
24 : 15 : set_tablespace_directory_suffix(&new_cluster);
25 : :
38 nathan@postgresql.or 26 [ + + ]:GNC 15 : if (old_cluster.num_tablespaces > 0 &&
2999 tgl@sss.pgh.pa.us 27 [ + - ]:GBC 5 : strcmp(old_cluster.tablespace_suffix, new_cluster.tablespace_suffix) == 0)
28 : : {
38 nathan@postgresql.or 29 [ + + ]:GNC 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)
38 nathan@postgresql.or 36 :UNC 0 : pg_fatal("Cannot upgrade to/from the same system catalog version when\n"
37 : : "using tablespaces.");
38 : : }
39 : : }
5596 bruce@momjian.us 40 :CBC 15 : }
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
5436 50 : 15 : get_tablespace_paths(void)
51 : : {
5362 52 : 15 : PGconn *conn = connectToServer(&old_cluster, "template1");
53 : : PGresult *res;
54 : : int tblnum;
55 : : int i_spclocation;
56 : : char query[QUERY_ALLOC];
57 : :
5022 magnus@hagander.net 58 : 15 : 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 : 15 : res = executeQueryOrDie(conn, "%s", query);
65 : :
38 nathan@postgresql.or 66 :GNC 15 : old_cluster.num_tablespaces = PQntuples(res);
67 : 15 : new_cluster.num_tablespaces = PQntuples(res);
68 : :
69 [ + + ]: 15 : if (PQntuples(res) != 0)
70 : : {
71 : 5 : old_cluster.tablespaces =
72 : 5 : (char **) pg_malloc(old_cluster.num_tablespaces * sizeof(char *));
73 : 5 : new_cluster.tablespaces =
74 : 5 : (char **) pg_malloc(new_cluster.num_tablespaces * sizeof(char *));
75 : : }
76 : : else
77 : : {
78 : 10 : old_cluster.tablespaces = NULL;
79 : 10 : new_cluster.tablespaces = NULL;
80 : : }
81 : :
5596 bruce@momjian.us 82 :CBC 15 : i_spclocation = PQfnumber(res, "spclocation");
83 : :
38 nathan@postgresql.or 84 [ + + ]:GNC 20 : 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 : : {
38 nathan@postgresql.or 100 :UNC 0 : old_cluster.tablespaces[tblnum] = pg_strdup(spcloc);
101 : 0 : new_cluster.tablespaces[tblnum] = old_cluster.tablespaces[tblnum];
102 : : }
103 : : else
104 : : {
38 nathan@postgresql.or 105 :GNC 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 : : {
4160 bruce@momjian.us 120 [ # # ]:UBC 0 : if (errno == ENOENT)
121 : 0 : report_status(PG_FATAL,
122 : : "tablespace directory \"%s\" does not exist",
38 nathan@postgresql.or 123 :UNC 0 : old_cluster.tablespaces[tblnum]);
124 : : else
4160 bruce@momjian.us 125 :UBC 0 : report_status(PG_FATAL,
126 : : "could not stat tablespace directory \"%s\": %m",
38 nathan@postgresql.or 127 :UNC 0 : old_cluster.tablespaces[tblnum]);
128 : : }
4160 bruce@momjian.us 129 [ - + ]:GBC 5 : if (!S_ISDIR(statBuf.st_mode))
4141 bruce@momjian.us 130 :UBC 0 : report_status(PG_FATAL,
131 : : "tablespace path \"%s\" is not a directory",
38 nathan@postgresql.or 132 :UNC 0 : old_cluster.tablespaces[tblnum]);
133 : : }
134 : :
5596 bruce@momjian.us 135 :CBC 15 : PQclear(res);
136 : :
137 : 15 : PQfinish(conn);
138 : 15 : }
139 : :
140 : :
141 : : static void
5362 142 : 30 : 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. */
1362 tgl@sss.pgh.pa.us 147 : 60 : cluster->tablespace_suffix = psprintf("/PG_%s_%d",
148 : 30 : cluster->major_version_str,
149 : : cluster->controldata.cat_ver);
5596 bruce@momjian.us 150 : 30 : }
|