/* * page.c * * per-page conversion operations * * Copyright (c) 2010-2014, PostgreSQL Global Development Group * contrib/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