summaryrefslogtreecommitdiff
path: root/contrib/pg_upgrade/page.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/pg_upgrade/page.c')
-rw-r--r--contrib/pg_upgrade/page.c173
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