diff options
Diffstat (limited to 'contrib/pg_upgrade/page.c')
| -rw-r--r-- | contrib/pg_upgrade/page.c | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/contrib/pg_upgrade/page.c b/contrib/pg_upgrade/page.c new file mode 100644 index 00000000000..105198758bc --- /dev/null +++ b/contrib/pg_upgrade/page.c @@ -0,0 +1,173 @@ +/* + * page.c + * + * per-page conversion operations + */ + +#include "pg_upgrade.h" + +#include "dynloader.h" +#include "storage/bufpage.h" + + +#ifdef PAGE_CONVERSION + + +static const char *getPageVersion(migratorContext *ctx, + uint16 *version, const char *pathName); +static pageCnvCtx *loadConverterPlugin(migratorContext *ctx, + 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(migratorContext *ctx, pageCnvCtx **result) +{ + uint16 oldPageVersion; + uint16 newPageVersion; + pageCnvCtx *converter; + const char *msg; + char dstName[MAXPGPATH]; + char srcName[MAXPGPATH]; + + snprintf(dstName, sizeof(dstName), "%s/global/%u", ctx->new.pgdata, + ctx->new.pg_database_oid); + snprintf(srcName, sizeof(srcName), "%s/global/%u", ctx->old.pgdata, + ctx->old.pg_database_oid); + + if ((msg = getPageVersion(ctx, &oldPageVersion, srcName)) != NULL) + return msg; + + if ((msg = getPageVersion(ctx, &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(ctx, 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(migratorContext *ctx, 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)) + 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(migratorContext *ctx, 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(ctx, 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 |
