/*-------------------------------------------------------------------------
 * relpath.c
 *		Shared frontend/backend code to find out pathnames of relation files
 *
 * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/common/relpath.c
 *
 *-------------------------------------------------------------------------
 */
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif

#include "catalog/pg_tablespace.h"
#include "common/relpath.h"
#include "storage/backendid.h"

#define FORKNAMECHARS	4		/* max chars for a fork name */

/*
 * Lookup table of fork name by fork number.
 *
 * If you add a new entry, remember to update the errhint below, and the
 * documentation for pg_relation_size(). Also keep FORKNAMECHARS above
 * up-to-date.
 */
const char *forkNames[] = {
	"main",						/* MAIN_FORKNUM */
	"fsm",						/* FSM_FORKNUM */
	"vm",						/* VISIBILITYMAP_FORKNUM */
	"init"						/* INIT_FORKNUM */
};

/*
 * forkname_chars
 *		We use this to figure out whether a filename could be a relation
 *		fork (as opposed to an oddly named stray file that somehow ended
 *		up in the database directory).  If the passed string begins with
 *		a fork name (other than the main fork name), we return its length,
 *		and set *fork (if not NULL) to the fork number.  If not, we return 0.
 *
 * Note that the present coding assumes that there are no fork names which
 * are prefixes of other fork names.
 */
int
forkname_chars(const char *str, ForkNumber *fork)
{
	ForkNumber	forkNum;

	for (forkNum = 1; forkNum <= MAX_FORKNUM; forkNum++)
	{
		int			len = strlen(forkNames[forkNum]);

		if (strncmp(forkNames[forkNum], str, len) == 0)
		{
			if (fork)
				*fork = forkNum;
			return len;
		}
	}
	return 0;
}

/*
 * relpathbackend - construct path to a relation's file
 *
 * Result is a palloc'd string.
 */
char *
relpathbackend(RelFileNode rnode, BackendId backend, ForkNumber forknum)
{
	int			pathlen;
	char	   *path;

	if (rnode.spcNode == GLOBALTABLESPACE_OID)
	{
		/* Shared system relations live in {datadir}/global */
		Assert(rnode.dbNode == 0);
		Assert(backend == InvalidBackendId);
		pathlen = 7 + OIDCHARS + 1 + FORKNAMECHARS + 1;
		path = (char *) palloc(pathlen);
		if (forknum != MAIN_FORKNUM)
			snprintf(path, pathlen, "global/%u_%s",
					 rnode.relNode, forkNames[forknum]);
		else
			snprintf(path, pathlen, "global/%u", rnode.relNode);
	}
	else if (rnode.spcNode == DEFAULTTABLESPACE_OID)
	{
		/* The default tablespace is {datadir}/base */
		if (backend == InvalidBackendId)
		{
			pathlen = 5 + OIDCHARS + 1 + OIDCHARS + 1 + FORKNAMECHARS + 1;
			path = (char *) palloc(pathlen);
			if (forknum != MAIN_FORKNUM)
				snprintf(path, pathlen, "base/%u/%u_%s",
						 rnode.dbNode, rnode.relNode,
						 forkNames[forknum]);
			else
				snprintf(path, pathlen, "base/%u/%u",
						 rnode.dbNode, rnode.relNode);
		}
		else
		{
			/* OIDCHARS will suffice for an integer, too */
			pathlen = 5 + OIDCHARS + 2 + OIDCHARS + 1 + OIDCHARS + 1
				+ FORKNAMECHARS + 1;
			path = (char *) palloc(pathlen);
			if (forknum != MAIN_FORKNUM)
				snprintf(path, pathlen, "base/%u/t%d_%u_%s",
						 rnode.dbNode, backend, rnode.relNode,
						 forkNames[forknum]);
			else
				snprintf(path, pathlen, "base/%u/t%d_%u",
						 rnode.dbNode, backend, rnode.relNode);
		}
	}
	else
	{
		/* All other tablespaces are accessed via symlinks */
		if (backend == InvalidBackendId)
		{
			pathlen = 9 + 1 + OIDCHARS + 1
				+ strlen(TABLESPACE_VERSION_DIRECTORY) + 1 + OIDCHARS + 1
				+ OIDCHARS + 1 + FORKNAMECHARS + 1;
			path = (char *) palloc(pathlen);
			if (forknum != MAIN_FORKNUM)
				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/%u_%s",
						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
						 rnode.dbNode, rnode.relNode,
						 forkNames[forknum]);
			else
				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/%u",
						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
						 rnode.dbNode, rnode.relNode);
		}
		else
		{
			/* OIDCHARS will suffice for an integer, too */
			pathlen = 9 + 1 + OIDCHARS + 1
				+ strlen(TABLESPACE_VERSION_DIRECTORY) + 1 + OIDCHARS + 2
				+ OIDCHARS + 1 + OIDCHARS + 1 + FORKNAMECHARS + 1;
			path = (char *) palloc(pathlen);
			if (forknum != MAIN_FORKNUM)
				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/t%d_%u_%s",
						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
						 rnode.dbNode, backend, rnode.relNode,
						 forkNames[forknum]);
			else
				snprintf(path, pathlen, "pg_tblspc/%u/%s/%u/t%d_%u",
						 rnode.spcNode, TABLESPACE_VERSION_DIRECTORY,
						 rnode.dbNode, backend, rnode.relNode);
		}
	}
	return path;
}
