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
|
|
|
*/
|
|
|
|
|
Create libpgcommon, and move pg_malloc et al to it
libpgcommon is a new static library to allow sharing code among the
various frontend programs and backend; this lets us eliminate duplicate
implementations of common routines. We avoid libpgport, because that's
intended as a place for porting issues; per discussion, it seems better
to keep them separate.
The first use case, and the only implemented by this patch, is pg_malloc
and friends, which many frontend programs were already using.
At the same time, we can use this to provide palloc emulation functions
for the frontend; this way, some palloc-using files in the backend can
also be used by the frontend cleanly. To do this, we change palloc() in
the backend to be a function instead of a macro on top of
MemoryContextAlloc(). This was previously believed to cause loss of
performance, but this implementation has been tweaked by Tom and Andres
so that on modern compilers it provides a slight improvement over the
previous one.
This lets us clean up some places that were already with
localized hacks.
Most of the pg_malloc/palloc changes in this patch were authored by
Andres Freund. Zoltán Böszörményi also independently provided a form of
that. libpgcommon infrastructure was authored by Álvaro.
2013-02-12 21:33:40 +08:00
|
|
|
#include "postgres_fe.h"
|
2011-08-27 09:16:24 +08:00
|
|
|
|
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
|
|
|
/*
|
2013-05-30 04:58:43 +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.
|
2013-01-03 10:20:13 +08:00
|
|
|
*/
|
2013-05-30 04:58:43 +08:00
|
|
|
|
2013-01-03 10:20:13 +08:00
|
|
|
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
|