/*-------------------------------------------------------------------------
 *
 * rename.c
 *	  renameatt() and renamerel() reside here.
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.33 1999/09/18 19:06:40 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/heapam.h"
#include "catalog/catname.h"
#include "utils/syscache.h"
#include "catalog/heap.h"
#include "catalog/indexing.h"
#include "catalog/catalog.h"
#include "commands/rename.h"
#include "miscadmin.h"
#include "optimizer/prep.h"
#include "utils/acl.h"

/*
 *		renameatt		- changes the name of a attribute in a relation
 *
 *		Attname attribute is changed in attribute catalog.
 *		No record of the previous attname is kept (correct?).
 *
 *		get proper relrelation from relation catalog (if not arg)
 *		scan attribute catalog
 *				for name conflict (within rel)
 *				for original attribute (if not arg)
 *		modify attname in attribute tuple
 *		insert modified attribute in attribute catalog
 *		delete original attribute from attribute catalog
 *
 *		XXX Renaming an indexed attribute must (eventually) also change
 *				the attribute name in the associated indexes.
 */
void
renameatt(char *relname,
		  char *oldattname,
		  char *newattname,
		  char *userName,
		  int recurse)
{
	Relation	targetrelation;
	Relation	attrelation;
	HeapTuple	reltup,
				oldatttup,
				newatttup;
	Relation	irelations[Num_pg_attr_indices];
	Oid			relid;

	/*
	 * permissions checking.  this would normally be done in utility.c,
	 * but this particular routine is recursive.
	 *
	 * normally, only the owner of a class can change its schema.
	 */
	if (!allowSystemTableMods && IsSystemRelationName(relname))
		elog(ERROR, "renameatt: class \"%s\" is a system catalog",
			 relname);
#ifndef NO_SECURITY
	if (!IsBootstrapProcessingMode() &&
		!pg_ownercheck(userName, relname, RELNAME))
		elog(ERROR, "renameatt: you do not own class \"%s\"",
			 relname);
#endif

	/*
	 * Grab an exclusive lock on the target table, which we will NOT release
	 * until end of transaction.
	 */
	targetrelation = heap_openr(relname, AccessExclusiveLock);
	relid = RelationGetRelid(targetrelation);
	heap_close(targetrelation, NoLock);	/* close rel but keep lock! */

	/*
	 * if the 'recurse' flag is set then we are supposed to rename this
	 * attribute in all classes that inherit from 'relname' (as well as in
	 * 'relname').
	 *
	 * any permissions or problems with duplicate attributes will cause the
	 * whole transaction to abort, which is what we want -- all or
	 * nothing.
	 */
	if (recurse)
	{
		List	   *child,
				   *children;

		/* this routine is actually in the planner */
		children = find_all_inheritors(lconsi(relid, NIL), NIL);

		/*
		 * find_all_inheritors does the recursive search of the
		 * inheritance hierarchy, so all we have to do is process all of
		 * the relids in the list that it returns.
		 */
		foreach(child, children)
		{
			Oid			childrelid;
			char		childname[NAMEDATALEN];

			childrelid = lfirsti(child);
			if (childrelid == relid)
				continue;
			reltup = SearchSysCacheTuple(RELOID,
										 ObjectIdGetDatum(childrelid),
										 0, 0, 0);
			if (!HeapTupleIsValid(reltup))
			{
				elog(ERROR, "renameatt: can't find catalog entry for inheriting class with oid %u",
					 childrelid);
			}
			/* make copy of cache value, could disappear in call */
			StrNCpy(childname,
					((Form_pg_class) GETSTRUCT(reltup))->relname.data,
					NAMEDATALEN);
			/* note we need not recurse again! */
			renameatt(childname, oldattname, newattname, userName, 0);
		}
	}

	attrelation = heap_openr(AttributeRelationName, RowExclusiveLock);

	oldatttup = SearchSysCacheTupleCopy(ATTNAME,
										ObjectIdGetDatum(relid),
										PointerGetDatum(oldattname),
										0, 0);
	if (!HeapTupleIsValid(oldatttup))
		elog(ERROR, "renameatt: attribute \"%s\" nonexistent", oldattname);

	if (((Form_pg_attribute) GETSTRUCT(oldatttup))->attnum < 0)
		elog(ERROR, "renameatt: system attribute \"%s\" not renamed", oldattname);

	newatttup = SearchSysCacheTuple(ATTNAME,
									ObjectIdGetDatum(relid),
									PointerGetDatum(newattname),
									0, 0);
	/* should not already exist */
	if (HeapTupleIsValid(newatttup))
	{
		pfree(oldatttup);
		elog(ERROR, "renameatt: attribute \"%s\" exists", newattname);
	}

	StrNCpy((((Form_pg_attribute) (GETSTRUCT(oldatttup)))->attname.data),
			newattname, NAMEDATALEN);

	heap_replace(attrelation, &oldatttup->t_self, oldatttup, NULL);

	/* keep system catalog indices current */
	CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations);
	CatalogIndexInsert(irelations, Num_pg_attr_indices, attrelation, oldatttup);
	CatalogCloseIndices(Num_pg_attr_indices, irelations);

	pfree(oldatttup);
	heap_close(attrelation, RowExclusiveLock);
}

/*
 *		renamerel		- change the name of a relation
 *
 *		Relname attribute is changed in relation catalog.
 *		No record of the previous relname is kept (correct?).
 *
 *		scan relation catalog
 *				for name conflict
 *				for original relation (if not arg)
 *		modify relname in relation tuple
 *		insert modified relation in relation catalog
 *		delete original relation from relation catalog
 *
 *		XXX Will currently lose track of a relation if it is unable to
 *				properly replace the new relation tuple.
 */
void
renamerel(char *oldrelname, char *newrelname)
{
	int			i;
	Relation	targetrelation;
	Relation	relrelation;	/* for RELATION relation */
	HeapTuple	oldreltup;
	char		oldpath[MAXPGPATH],
				newpath[MAXPGPATH],
				toldpath[MAXPGPATH + 10],
				tnewpath[MAXPGPATH + 10];
	Relation	irelations[Num_pg_class_indices];

	if (!allowSystemTableMods && IsSystemRelationName(oldrelname))
		elog(ERROR, "renamerel: system relation \"%s\" not renamed",
			 oldrelname);

	if (!allowSystemTableMods && IsSystemRelationName(newrelname))
		elog(ERROR, "renamerel: Illegal class name: \"%s\" -- pg_ is reserved for system catalogs",
			 newrelname);

	/*
	 * Grab an exclusive lock on the target table, which we will NOT release
	 * until end of transaction.
	 */
	targetrelation = heap_openr(oldrelname, AccessExclusiveLock);
	heap_close(targetrelation, NoLock);	/* close rel but keep lock! */

	relrelation = heap_openr(RelationRelationName, RowExclusiveLock);

	oldreltup = SearchSysCacheTupleCopy(RELNAME,
										PointerGetDatum(oldrelname),
										0, 0, 0);
	if (!HeapTupleIsValid(oldreltup))
		elog(ERROR, "renamerel: relation \"%s\" does not exist", oldrelname);

	if (RelnameFindRelid(newrelname) != InvalidOid)
		elog(ERROR, "renamerel: relation \"%s\" exists", newrelname);

	/*
	 * XXX need to close relation and flush dirty buffers here!
	 */

	/* rename the path first, so if this fails the rename's not done */
	strcpy(oldpath, relpath(oldrelname));
	strcpy(newpath, relpath(newrelname));
	if (rename(oldpath, newpath) < 0)
		elog(ERROR, "renamerel: unable to rename file: %s", oldpath);

	/* rename additional segments of relation, too */
	for (i = 1;; i++)
	{
		sprintf(toldpath, "%s.%d", oldpath, i);
		sprintf(tnewpath, "%s.%d", newpath, i);
		if (rename(toldpath, tnewpath) < 0)
			break;
	}

	StrNCpy((((Form_pg_class) GETSTRUCT(oldreltup))->relname.data),
			newrelname, NAMEDATALEN);

	/* insert fixed rel tuple */
	heap_replace(relrelation, &oldreltup->t_self, oldreltup, NULL);

	/* keep the system catalog indices current */
	CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, irelations);
	CatalogIndexInsert(irelations, Num_pg_class_indices, relrelation, oldreltup);
	CatalogCloseIndices(Num_pg_class_indices, irelations);

	heap_close(relrelation, RowExclusiveLock);
}
