diff options
Diffstat (limited to 'src/bin/pg_upgrade/page.c')
-rw-r--r-- | src/bin/pg_upgrade/page.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/src/bin/pg_upgrade/page.c b/src/bin/pg_upgrade/page.c new file mode 100644 index 00000000000..3f4c697a108 --- /dev/null +++ b/src/bin/pg_upgrade/page.c @@ -0,0 +1,164 @@ +/* + * page.c + * + * per-page conversion operations + * + * Copyright (c) 2010-2015, PostgreSQL Global Development Group + * src/bin/pg_upgrade/page.c + */ + +#include "postgres_fe.h" + +#include "pg_upgrade.h" + +#include "storage/bufpage.h" + + +#ifdef PAGE_CONVERSION + + +static void 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. + */ +pageCnvCtx * +setupPageConverter(void) +{ + 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); + + getPageVersion(&oldPageVersion, srcName); + getPageVersion(&newPageVersion, dstName); + + /* + * If the old cluster and new cluster use the same page layouts, then we + * don't need a page converter. + */ + if (newPageVersion != oldPageVersion) + { + /* + * 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_fatal("could not find plugin to convert from old page layout to new page layout\n"); + + return converter; + } + 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). + */ +static void +getPageVersion(uint16 *version, const char *pathName) +{ + int relfd; + PageHeaderData page; + ssize_t bytesRead; + + if ((relfd = open(pathName, O_RDONLY, 0)) < 0) + pg_fatal("could not open relation %s\n", pathName); + + if ((bytesRead = read(relfd, &page, sizeof(page))) != sizeof(page)) + pg_fatal("could not read page header of %s\n", pathName); + + *version = PageGetPageLayoutVersion(&page); + + close(relfd); + + return; +} + + +/* + * 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 |