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