2010-05-12 10:19:11 +08:00
|
|
|
/*
|
|
|
|
* page.c
|
|
|
|
*
|
|
|
|
* per-page conversion operations
|
2010-07-03 22:23:14 +08:00
|
|
|
*
|
2013-01-02 06:15:01 +08:00
|
|
|
* Copyright (c) 2010-2013, PostgreSQL Global Development Group
|
2010-09-21 04:08:53 +08:00
|
|
|
* contrib/pg_upgrade/page.c
|
2010-05-12 10:19:11 +08:00
|
|
|
*/
|
|
|
|
|
2011-08-27 09:16:24 +08:00
|
|
|
#include "postgres.h"
|
|
|
|
|
2010-05-12 10:19:11 +08:00
|
|
|
#include "pg_upgrade.h"
|
|
|
|
|
|
|
|
#include "storage/bufpage.h"
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef PAGE_CONVERSION
|
|
|
|
|
|
|
|
|
2013-01-03 10:20:13 +08:00
|
|
|
static void getPageVersion(
|
2010-05-12 10:19:11 +08:00
|
|
|
uint16 *version, const char *pathName);
|
2010-10-20 05:38:16 +08:00
|
|
|
static pageCnvCtx *loadConverterPlugin(
|
2010-05-12 10:19:11 +08:00
|
|
|
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.
|
|
|
|
*/
|
2013-01-03 10:20:13 +08:00
|
|
|
pageCnvCtx *
|
|
|
|
setupPageConverter(void)
|
2010-05-12 10:19:11 +08:00
|
|
|
{
|
|
|
|
uint16 oldPageVersion;
|
|
|
|
uint16 newPageVersion;
|
|
|
|
pageCnvCtx *converter;
|
|
|
|
const char *msg;
|
|
|
|
char dstName[MAXPGPATH];
|
|
|
|
char srcName[MAXPGPATH];
|
|
|
|
|
2010-10-20 05:38:16 +08:00
|
|
|
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);
|
2010-05-12 10:19:11 +08:00
|
|
|
|
2013-01-03 10:20:13 +08:00
|
|
|
getPageVersion(&oldPageVersion, srcName);
|
|
|
|
getPageVersion(&newPageVersion, dstName);
|
2010-05-12 10:19:11 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the old cluster and new cluster use the same page layouts, then we
|
|
|
|
* don't need a page converter.
|
|
|
|
*/
|
2013-01-03 10:20:13 +08:00
|
|
|
if (newPageVersion != oldPageVersion)
|
2010-05-12 10:19:11 +08:00
|
|
|
{
|
2013-01-03 10:20:13 +08:00
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
pg_log(PG_FATAL, "could not find plugin to convert from old page layout to new page layout\n");
|
|
|
|
|
|
|
|
return converter;
|
2010-05-12 10:19:11 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
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).
|
|
|
|
*/
|
2013-01-03 10:20:13 +08:00
|
|
|
static void
|
2010-10-20 05:38:16 +08:00
|
|
|
getPageVersion(uint16 *version, const char *pathName)
|
2010-05-12 10:19:11 +08:00
|
|
|
{
|
|
|
|
int relfd;
|
|
|
|
PageHeaderData page;
|
|
|
|
ssize_t bytesRead;
|
|
|
|
|
|
|
|
if ((relfd = open(pathName, O_RDONLY, 0)) < 0)
|
2013-01-03 10:20:13 +08:00
|
|
|
pg_log(PG_FATAL, "could not open relation %s\n", pathName);
|
2010-05-12 10:19:11 +08:00
|
|
|
|
|
|
|
if ((bytesRead = read(relfd, &page, sizeof(page))) != sizeof(page))
|
2013-01-03 10:20:13 +08:00
|
|
|
pg_log(PG_FATAL, "could not read page header of %s\n", pathName);
|
2010-05-12 10:19:11 +08:00
|
|
|
|
|
|
|
*version = PageGetPageLayoutVersion(&page);
|
|
|
|
|
|
|
|
close(relfd);
|
|
|
|
|
2013-01-03 10:20:13 +08:00
|
|
|
return;
|
2010-05-12 10:19:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 *
|
2010-10-20 05:38:16 +08:00
|
|
|
loadConverterPlugin(uint16 newPageVersion, uint16 oldPageVersion)
|
2010-05-12 10:19:11 +08:00
|
|
|
{
|
|
|
|
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
|
|
|
|
{
|
2010-10-20 05:38:16 +08:00
|
|
|
pageCnvCtx *result = (pageCnvCtx *) pg_malloc(sizeof(*result));
|
2010-05-12 10:19:11 +08:00
|
|
|
|
|
|
|
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
|