1 /*
2  *	tablespace.c
3  *
4  *	tablespace functions
5  *
6  *	Copyright (c) 2010-2016, 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 #include <sys/types.h>
15 
16 static void get_tablespace_paths(void);
17 static void set_tablespace_directory_suffix(ClusterInfo *cluster);
18 
19 
20 void
init_tablespaces(void)21 init_tablespaces(void)
22 {
23 	get_tablespace_paths();
24 
25 	set_tablespace_directory_suffix(&old_cluster);
26 	set_tablespace_directory_suffix(&new_cluster);
27 
28 	if (os_info.num_old_tablespaces > 0 &&
29 	strcmp(old_cluster.tablespace_suffix, new_cluster.tablespace_suffix) == 0)
30 		pg_fatal("Cannot upgrade to/from the same system catalog version when\n"
31 				 "using tablespaces.\n");
32 }
33 
34 
35 /*
36  * get_tablespace_paths()
37  *
38  * Scans pg_tablespace and returns a malloc'ed array of all tablespace
39  * paths. It's the caller's responsibility to free the array.
40  */
41 static void
get_tablespace_paths(void)42 get_tablespace_paths(void)
43 {
44 	PGconn	   *conn = connectToServer(&old_cluster, "template1");
45 	PGresult   *res;
46 	int			tblnum;
47 	int			i_spclocation;
48 	char		query[QUERY_ALLOC];
49 
50 	snprintf(query, sizeof(query),
51 			 "SELECT	%s "
52 			 "FROM	pg_catalog.pg_tablespace "
53 			 "WHERE	spcname != 'pg_default' AND "
54 			 "		spcname != 'pg_global'",
55 	/* 9.2 removed the spclocation column */
56 			 (GET_MAJOR_VERSION(old_cluster.major_version) <= 901) ?
57 	"spclocation" : "pg_catalog.pg_tablespace_location(oid) AS spclocation");
58 
59 	res = executeQueryOrDie(conn, "%s", query);
60 
61 	if ((os_info.num_old_tablespaces = PQntuples(res)) != 0)
62 		os_info.old_tablespaces = (char **) pg_malloc(
63 							   os_info.num_old_tablespaces * sizeof(char *));
64 	else
65 		os_info.old_tablespaces = NULL;
66 
67 	i_spclocation = PQfnumber(res, "spclocation");
68 
69 	for (tblnum = 0; tblnum < os_info.num_old_tablespaces; tblnum++)
70 	{
71 		struct stat statBuf;
72 
73 		os_info.old_tablespaces[tblnum] = pg_strdup(
74 									 PQgetvalue(res, tblnum, i_spclocation));
75 
76 		/*
77 		 * Check that the tablespace path exists and is a directory.
78 		 * Effectively, this is checking only for tables/indexes in
79 		 * non-existent tablespace directories.  Databases located in
80 		 * non-existent tablespaces already throw a backend error.
81 		 * Non-existent tablespace directories can occur when a data directory
82 		 * that contains user tablespaces is moved as part of pg_upgrade
83 		 * preparation and the symbolic links are not updated.
84 		 */
85 		if (stat(os_info.old_tablespaces[tblnum], &statBuf) != 0)
86 		{
87 			if (errno == ENOENT)
88 				report_status(PG_FATAL,
89 							  "tablespace directory \"%s\" does not exist\n",
90 							  os_info.old_tablespaces[tblnum]);
91 			else
92 				report_status(PG_FATAL,
93 						  "could not stat tablespace directory \"%s\": %s\n",
94 						   os_info.old_tablespaces[tblnum], strerror(errno));
95 		}
96 		if (!S_ISDIR(statBuf.st_mode))
97 			report_status(PG_FATAL,
98 						  "tablespace path \"%s\" is not a directory\n",
99 						  os_info.old_tablespaces[tblnum]);
100 	}
101 
102 	PQclear(res);
103 
104 	PQfinish(conn);
105 
106 	return;
107 }
108 
109 
110 static void
set_tablespace_directory_suffix(ClusterInfo * cluster)111 set_tablespace_directory_suffix(ClusterInfo *cluster)
112 {
113 	if (GET_MAJOR_VERSION(cluster->major_version) <= 804)
114 		cluster->tablespace_suffix = pg_strdup("");
115 	else
116 	{
117 		/* This cluster has a version-specific subdirectory */
118 
119 		/* The leading slash is needed to start a new directory. */
120 		cluster->tablespace_suffix = psprintf("/PG_%s_%d",
121 											  cluster->major_version_str,
122 											  cluster->controldata.cat_ver);
123 	}
124 }
125