summaryrefslogtreecommitdiff
path: root/contrib/pg_upgrade/page.c
blob: 22a587f4b1981e4fbdb9af8ca83e71188198a153 (plain)
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/*
 *	page.c
 *
 *	per-page conversion operations
 *
 *	Copyright (c) 2010-2011, PostgreSQL Global Development Group
 *	contrib/pg_upgrade/page.c
 */

#include "pg_upgrade.h"

#include "storage/bufpage.h"


#ifdef PAGE_CONVERSION


static const char *getPageVersion(
			   uint16 *version, const char *pathName);
static pageCnvCtx *loadConverterPlugin(
					uint16 newPageVersion, uint16 oldPageVersion);


/*
 * setupPageConverter()
 *
 *	This function determines the PageLayoutVersion of the old cluster and
 *	the PageLayoutVersion of the new cluster.  If the versions differ, this
 *	function loads a converter plugin and returns a pointer to a pageCnvCtx
 *	object (in *result) that knows how to convert pages from the old format
 *	to the new format.	If the versions are identical, this function just
 *	returns a NULL pageCnvCtx pointer to indicate that page-by-page conversion
 *	is not required.
 *
 *	If successful this function sets *result and returns NULL.	If an error
 *	occurs, this function returns an error message in the form of an null-terminated
 *	string.
 */
const char *
setupPageConverter(pageCnvCtx **result)
{
	uint16		oldPageVersion;
	uint16		newPageVersion;
	pageCnvCtx *converter;
	const char *msg;
	char		dstName[MAXPGPATH];
	char		srcName[MAXPGPATH];

	snprintf(dstName, sizeof(dstName), "%s/global/%u", new_cluster.pgdata,
			 new_cluster.pg_database_oid);
	snprintf(srcName, sizeof(srcName), "%s/global/%u", old_cluster.pgdata,
			 old_cluster.pg_database_oid);

	if ((msg = getPageVersion(&oldPageVersion, srcName)) != NULL)
		return msg;

	if ((msg = getPageVersion(&newPageVersion, dstName)) != NULL)
		return msg;

	/*
	 * If the old cluster and new cluster use the same page layouts, then we
	 * don't need a page converter.
	 */
	if (newPageVersion == oldPageVersion)
	{
		*result = NULL;
		return NULL;
	}

	/*
	 * The clusters use differing page layouts, see if we can find a plugin
	 * that knows how to convert from the old page layout to the new page
	 * layout.
	 */

	if ((converter = loadConverterPlugin(newPageVersion, oldPageVersion)) == NULL)
		return "can't find plugin to convert from old page layout to new page layout";
	else
	{
		*result = converter;
		return NULL;
	}
}


/*
 * getPageVersion()
 *
 *	Retrieves the PageLayoutVersion for the given relation.
 *
 *	Returns NULL on success (and stores the PageLayoutVersion at *version),
 *	if an error occurs, this function returns an error message (in the form
 *	of a null-terminated string).
 */
static const char *
getPageVersion(uint16 *version, const char *pathName)
{
	int			relfd;
	PageHeaderData page;
	ssize_t		bytesRead;

	if ((relfd = open(pathName, O_RDONLY, 0)) < 0)
		return "can't open relation";

	if ((bytesRead = read(relfd, &page, sizeof(page))) != sizeof(page))
	{
		close(relfd);
		return "can't read page header";
	}

	*version = PageGetPageLayoutVersion(&page);

	close(relfd);

	return NULL;
}


/*
 * loadConverterPlugin()
 *
 *	This function loads a page-converter plugin library and grabs a
 *	pointer to each of the (interesting) functions provided by that
 *	plugin.  The name of the plugin library is derived from the given
 *	newPageVersion and oldPageVersion.	If a plugin is found, this
 *	function returns a pointer to a pageCnvCtx object (which will contain
 *	a collection of plugin function pointers). If the required plugin
 *	is not found, this function returns NULL.
 */
static pageCnvCtx *
loadConverterPlugin(uint16 newPageVersion, uint16 oldPageVersion)
{
	char		pluginName[MAXPGPATH];
	void	   *plugin;

	/*
	 * Try to find a plugin that can convert pages of oldPageVersion into
	 * pages of newPageVersion.  For example, if we oldPageVersion = 3 and
	 * newPageVersion is 4, we search for a plugin named:
	 * plugins/convertLayout_3_to_4.dll
	 */

	/*
	 * FIXME: we are searching for plugins relative to the current directory,
	 * we should really search relative to our own executable instead.
	 */
	snprintf(pluginName, sizeof(pluginName), "./plugins/convertLayout_%d_to_%d%s",
			 oldPageVersion, newPageVersion, DLSUFFIX);

	if ((plugin = pg_dlopen(pluginName)) == NULL)
		return NULL;
	else
	{
		pageCnvCtx *result = (pageCnvCtx *) pg_malloc(sizeof(*result));

		result->old.PageVersion = oldPageVersion;
		result->new.PageVersion = newPageVersion;

		result->startup = (pluginStartup) pg_dlsym(plugin, "init");
		result->convertFile = (pluginConvertFile) pg_dlsym(plugin, "convertFile");
		result->convertPage = (pluginConvertPage) pg_dlsym(plugin, "convertPage");
		result->shutdown = (pluginShutdown) pg_dlsym(plugin, "fini");
		result->pluginData = NULL;

		/*
		 * If the plugin has exported an initializer, go ahead and invoke it.
		 */
		if (result->startup)
			result->startup(MIGRATOR_API_VERSION, &result->pluginVersion,
						newPageVersion, oldPageVersion, &result->pluginData);

		return result;
	}
}



#endif