mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-24 01:29:19 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			176 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *	page.c
 | |
|  *
 | |
|  *	per-page conversion operations
 | |
|  *
 | |
|  *	Copyright (c) 2010, PostgreSQL Global Development Group
 | |
|  *	$PostgreSQL: pgsql/contrib/pg_upgrade/page.c,v 1.5 2010/07/03 16:33:14 momjian Exp $
 | |
|  */
 | |
| 
 | |
| #include "pg_upgrade.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
 |