1
0
mirror of https://github.com/postgres/postgres.git synced 2025-04-22 23:02:54 +03:00
Vadim B. Mikheev 2c7de17b07 New file naming. Database OID is used as "tablespace" id and
relation OID is used as file node on creation but may be changed later
if required. Regression Tests Approved (c) -:)))
2000-10-16 14:52:28 +00:00

346 lines
10 KiB
C

/*-------------------------------------------------------------------------
*
* 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.48 2000/10/16 14:52:03 vadim 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,
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(GetUserId(), 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, 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);
#ifdef OLD_FILE_NAMING
/*
* 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);
}
}
#endif
}