Skip to content
Snippets Groups Projects
Select Git revision
  • benchmark-tools
  • postgres-lambda
  • master default
  • REL9_4_25
  • REL9_5_20
  • REL9_6_16
  • REL_10_11
  • REL_11_6
  • REL_12_1
  • REL_12_0
  • REL_12_RC1
  • REL_12_BETA4
  • REL9_4_24
  • REL9_5_19
  • REL9_6_15
  • REL_10_10
  • REL_11_5
  • REL_12_BETA3
  • REL9_4_23
  • REL9_5_18
  • REL9_6_14
  • REL_10_9
  • REL_11_4
23 results

rename.c

Blame
  • rename.c 10.45 KiB
    /*-------------------------------------------------------------------------
     *
     * rename.c
     *	  renameatt() and renamerel() reside here.
     *
     * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
     * Portions Copyright (c) 1994, Regents of the University of California
     *
     *
     * IDENTIFICATION
     *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.46 2000/06/20 06:41:13 tgl Exp $
     *
     *-------------------------------------------------------------------------
     */
    #include "postgres.h"
    
    #include <errno.h>
    
    #include "access/heapam.h"
    #include "catalog/catname.h"
    #include "catalog/pg_type.h"
    #include "catalog/heap.h"
    #include "catalog/indexing.h"
    #include "catalog/catalog.h"
    #include "commands/rename.h"
    #include "miscadmin.h"
    #include "storage/smgr.h"
    #include "optimizer/prep.h"
    #include "utils/acl.h"
    #include "utils/relcache.h"
    #include "utils/syscache.h"
    #include "utils/temprel.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;
    	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(relid);
    
    		/*
    		 * 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 = lfirsti(child);
    			char		childname[NAMEDATALEN];
    
    			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,
    					NameStr(((Form_pg_class) GETSTRUCT(reltup))->relname),
    					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))
    	{
    		heap_freetuple(oldatttup);
    		elog(ERROR, "renameatt: attribute \"%s\" exists", newattname);
    	}
    
    	StrNCpy(NameStr(((Form_pg_attribute) GETSTRUCT(oldatttup))->attname),
    			newattname, NAMEDATALEN);
    
    	heap_update(attrelation, &oldatttup->t_self, oldatttup, NULL);
    
    	/* keep system catalog indices current */
    	{
    		Relation	irelations[Num_pg_attr_indices];
    
    		CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations);
    		CatalogIndexInsert(irelations, Num_pg_attr_indices, attrelation, oldatttup);
    		CatalogCloseIndices(Num_pg_attr_indices, irelations);
    	}
    
    	heap_freetuple(oldatttup);
    	heap_close(attrelation, RowExclusiveLock);
    }
    
    /*
     *		renamerel		- change the name of a relation
     */
    void
    renamerel(const char *oldrelname, const char *newrelname)
    {
    	int			i;
    	Relation	targetrelation;
    	Relation	relrelation;	/* for RELATION relation */
    	HeapTuple	oldreltup;
    	Oid			reloid;
    	char		relkind;
    	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);
    
    	/*
    	 * Check for renaming a temp table, which only requires altering
    	 * the temp-table mapping, not the physical table.
    	 */
    	if (rename_temp_relation(oldrelname, newrelname))
    		return;					/* all done... */
    
    	/*
    	 * Instead of using heap_openr(), do it the hard way, so that we
    	 * can rename indexes as well as regular relations.
    	 */
    	targetrelation = RelationNameGetRelation(oldrelname);
    
    	if (!RelationIsValid(targetrelation))
    		elog(ERROR, "Relation '%s' does not exist", oldrelname);
    
    	/*
    	 * Grab an exclusive lock on the target table, which we will NOT
    	 * release until end of transaction.
    	 */
    	LockRelation(targetrelation, AccessExclusiveLock);
    
    	/* ----------------
    	 *	RENAME TABLE within a transaction block is dangerous, because
    	 *	if the transaction is later rolled back we have no way to
    	 *	undo the rename of the relation's physical file.  For now, allow it
    	 *	but emit a warning message.
    	 *	Someday we might want to consider postponing the physical rename
    	 *	until transaction commit, but that's a lot of work...
    	 *	The only case that actually works right is for relations created
    	 *	in the current transaction, since the post-abort state would be that
    	 *	they don't exist anyway.  So, no warning in that case.
    	 * ----------------
    	 */
    	if (IsTransactionBlock() && !targetrelation->rd_myxactonly)
    		elog(NOTICE, "Caution: RENAME TABLE cannot be rolled back, so don't abort now");
    
    	reloid = RelationGetRelid(targetrelation);
    	relkind = targetrelation->rd_rel->relkind;
    
    	/*
    	 * Flush all blocks of the relation out of the buffer pool.  We need
    	 * this because the blocks are marked with the relation's name as well
    	 * as OID. If some backend tries to write a dirty buffer with
    	 * mdblindwrt after we've renamed the physical file, we'll be in big
    	 * trouble.
    	 *
    	 * Since we hold the exclusive lock on the relation, we don't have to
    	 * worry about more blocks being read in while we finish the rename.
    	 */
    	if (FlushRelationBuffers(targetrelation, (BlockNumber) 0) < 0)
    		elog(ERROR, "renamerel: unable to flush relation from buffer pool");
    
    	/*
    	 * Make sure smgr and lower levels close the relation's files. (Next
    	 * access to rel will reopen them.)
    	 *
    	 * Note: we rely on shared cache invalidation message to make other
    	 * backends close and re-open the files.
    	 */
    	smgrclose(DEFAULT_SMGR, targetrelation);
    
    	/*
    	 * Close rel, but keep exclusive lock!
    	 */
    	heap_close(targetrelation, NoLock);
    
    	/*
    	 * Flush the relcache entry (easier than trying to change it at exactly
    	 * the right instant).  It'll get rebuilt on next access to relation.
    	 *
    	 * XXX What if relation is myxactonly?
    	 */
    	targetrelation = NULL;		/* make sure I don't touch it again */
    	RelationIdInvalidateRelationCacheByRelationId(reloid);
    
    	/*
    	 * Find relation's pg_class tuple, and make sure newrelname isn't in
    	 * use.
    	 */
    	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);
    
    	/*
    	 * Update pg_class tuple with new relname.
    	 */
    	StrNCpy(NameStr(((Form_pg_class) GETSTRUCT(oldreltup))->relname),
    			newrelname, NAMEDATALEN);
    
    	heap_update(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, NoLock);
    
    	/*
    	 * Also rename the associated type, if any.
    	 */
    	if (relkind != RELKIND_INDEX)
    		TypeRename(oldrelname, newrelname);
    
    	/*
    	 * Perform physical rename of files.  If this fails, we haven't yet
    	 * done anything irreversible.  NOTE that this MUST be the last step;
    	 * an error occurring afterwards would leave the relation hosed!
    	 *
    	 * XXX smgr.c ought to provide an interface for this; doing it directly
    	 * is bletcherous.
    	 */
    	strcpy(oldpath, relpath(oldrelname));
    	strcpy(newpath, relpath(newrelname));
    	if (rename(oldpath, newpath) < 0)
    		elog(ERROR, "renamerel: unable to rename %s to %s: %m",
    			 oldpath, newpath);
    
    	/* 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)
    		{
    			/* expected case is that there's not another segment file */
    			if (errno == ENOENT)
    				break;
    			/* otherwise we're up the creek... */
    			elog(ERROR, "renamerel: unable to rename %s to %s: %m",
    				 toldpath, tnewpath);
    		}
    	}
    }