text: /*
   *	page.c
   *
   *	per-page conversion operations
   *
   *	Copyright (c) 2010-2011, PostgreSQL Global Development Group
   *	contrib/pg_upgrade/page.c
   */
  
  #include "pg_upgrade.h"
  
  #include "storage/bufpage.h"
  
  
  #ifdef PAGE_CONVERSION
  
  
  static const char *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.
   *
   *	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(pageCnvCtx **result)
  {
  	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);
  
  	if ((msg = getPageVersion(&oldPageVersion, srcName)) != NULL)
  		return msg;
  
  	if ((msg = getPageVersion(&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(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(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))
  	{
  		close(relfd);
  		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(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
  