1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-20 15:22:23 +03:00

Allow ALTER TABLE ... ALTER CONSTRAINT ... RENAME

Joachim Wieland
This commit is contained in:
Bruce Momjian
2006-02-11 22:17:19 +00:00
parent 3fcb38f031
commit a02f6ce33b
12 changed files with 462 additions and 12 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.28 2005/11/22 18:17:08 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.29 2006/02/11 22:17:18 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -664,3 +664,191 @@ AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
heap_close(conRel, RowExclusiveLock);
}
/*
* RenameConstraint
* Rename a single constraint record
* conId: The OID of the constraint to rename
* newName: The new name of the constraint
* implicitRename: is this an implicit rename? If so, we will issue
* a notice about the implicit rename
* cmdName: the command that triggered the rename for the "implicitly
* renames" notice message
*/
void
RenameConstraint(Oid conId, const char* newName,
bool implicitRename, const char* cmdName)
{
Relation conRel;
ScanKeyData key[1];
SysScanDesc scan;
HeapTuple tup;
NameData newNameData;
Relation rel;
Oid relId;
Oid nspOid;
Form_pg_constraint conform;
/* before reading the tuple, lock the table it constraints in
* AccessExclusiveLock mode. Otherwise, if we read it before locking this
* table, the tuple might be changed by another transaction and our copy
* would be out of date
*/
relId = GetConstraintRelationId(conId);
if (!OidIsValid(relId))
{
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("constraint with OID %d does not exist", conId)));
}
rel = relation_open(relId, AccessExclusiveLock);
nspOid = get_rel_namespace(relId);
conRel = heap_open(ConstraintRelationId, RowExclusiveLock);
ScanKeyInit(&key[0],
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(conId));
scan = systable_beginscan(conRel, ConstraintOidIndexId, true,
SnapshotNow, 1, key);
if (!HeapTupleIsValid((tup = systable_getnext(scan))))
{
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("constraint with OID %d does not exist", conId)));
}
conform = (Form_pg_constraint) GETSTRUCT(tup);
if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
conform->conrelid,
get_rel_namespace(conform->conrelid),
newName))
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("constraint \"%s\" for relation \"%s\" already exists",
newName,
RelationGetRelationName(rel))));
}
tup = heap_copytuple(tup);
conform = (Form_pg_constraint) GETSTRUCT(tup);
if (implicitRename && cmdName)
{
ereport(NOTICE,
(errmsg("%s will implicitly rename constraint "
"\"%s\" to \"%s\" on table \"%s.%s\"",
cmdName,
NameStr(conform->conname),
newName,
get_namespace_name(nspOid),
RelationGetRelationName(rel))));
}
namestrcpy(&newNameData, newName);
conform->conname = newNameData;
simple_heap_update(conRel, &tup->t_self, tup);
CatalogUpdateIndexes(conRel, tup);
heap_freetuple(tup);
systable_endscan(scan);
heap_close(conRel, RowExclusiveLock);
/* close relation but hold lock until end of transaction */
relation_close(rel, NoLock);
}
/* GetRelationConstraintOid
*
* Get the contraint OID by the relation Id of the relation it constraints and
* this relations' name. We need this function in order to rename a constraint.
* This is done via "ALTER TABLE ... ALTER CONSTRAINT name" and the parser
* gives us the relation this constraint is defined on as well as the
* constraint's name.
*
* The function returns:
*
* - the unique OID of the constraint if the constraint could be found
* - the invalid OID if the constraint was not found
*
*/
Oid GetRelationConstraintOid(Oid relId, const char* name)
{
Relation conRel;
ScanKeyData key[1];
SysScanDesc scan;
HeapTuple tup;
Oid conId = InvalidOid;
/* we don't change data, so an AccessShareLock is enough */
conRel = heap_open(ConstraintRelationId, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_constraint_conrelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relId));
scan = systable_beginscan(conRel, ConstraintRelidIndexId, true,
SnapshotNow, 1, key);
while (HeapTupleIsValid((tup = systable_getnext(scan))))
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
if (pg_strcasecmp(name, NameStr(con->conname)) == 0)
{
conId = HeapTupleGetOid(tup);
Assert(OidIsValid(conId));
}
}
systable_endscan(scan);
heap_close(conRel, AccessShareLock);
return conId;
}
/* GetConstraintRelationId
*
* Gets the OID of the relation where the constraint is defined on or the
* invalid OID if the constraint cannot be found.
*/
Oid GetConstraintRelationId(Oid conId)
{
Relation conRel;
ScanKeyData key[1];
SysScanDesc scan;
HeapTuple tup;
Oid relId = InvalidOid;
/* we don't change data, so an AccessShareLock is enough */
conRel = heap_open(ConstraintRelationId, AccessShareLock);
ScanKeyInit(&key[0],
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(conId));
scan = systable_beginscan(conRel, ConstraintOidIndexId, true,
SnapshotNow, 1, key);
if (HeapTupleIsValid((tup = systable_getnext(scan))))
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
relId = con->conrelid;
Assert(OidIsValid(relId));
}
systable_endscan(scan);
heap_close(conRel, AccessShareLock);
return relId;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.17 2005/11/22 18:17:08 momjian Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.18 2006/02/11 22:17:18 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -361,3 +361,102 @@ isObjectPinned(const ObjectAddress *object, Relation rel)
return ret;
}
List* getReferencingOids(Oid refClassId, Oid refObjId, Oid refObjSubId,
Oid classId, DependencyType deptype)
{
ScanKeyData key[3];
SysScanDesc scan;
HeapTuple tup;
Relation depRel;
List *list = NIL;
depRel = heap_open(DependRelationId, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_depend_refclassid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(refClassId));
ScanKeyInit(&key[1],
Anum_pg_depend_refobjid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(refObjId));
ScanKeyInit(&key[2],
Anum_pg_depend_refobjsubid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(refObjSubId));
scan = systable_beginscan(depRel, DependReferenceIndexId, true,
SnapshotNow, 3, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
/* check if the class id is what we want */
if (depForm->classid != classId)
continue;
/* check if the DependencyType is what we want */
if (depForm->deptype != deptype)
continue;
/* if we are still here, we have found a match */
list = lcons_oid(depForm->objid, list);
break;
}
systable_endscan(scan);
heap_close(depRel, AccessShareLock);
return list;
}
List* getDependentOids(Oid classId, Oid objId,
Oid refClassId, DependencyType deptype)
{
ScanKeyData key[2];
SysScanDesc scan;
HeapTuple tup;
Relation depRel;
List *list = NIL;
depRel = heap_open(DependRelationId, AccessShareLock);
ScanKeyInit(&key[0],
Anum_pg_depend_classid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(classId));
ScanKeyInit(&key[1],
Anum_pg_depend_objid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(objId));
scan = systable_beginscan(depRel, DependDependerIndexId, true,
SnapshotNow, 2, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
/* check if the DependencyType is what we want */
if (depForm->deptype != deptype)
continue;
/* check if the referenced class id is what we want */
if (depForm->refclassid != refClassId)
continue;
/* if we are still here, we have found a match */
list = lcons_oid(depForm->refobjid, list);
break;
}
systable_endscan(scan);
heap_close(depRel, AccessShareLock);
return list;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.15 2005/10/15 02:49:14 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.16 2006/02/11 22:17:18 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -16,8 +16,10 @@
#include "access/htup.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/pg_class.h"
#include "catalog/pg_constraint.h"
#include "commands/alter.h"
#include "commands/conversioncmds.h"
#include "commands/dbcommands.h"
@ -88,6 +90,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_INDEX:
case OBJECT_COLUMN:
case OBJECT_TRIGGER:
case OBJECT_CONSTRAINT:
{
Oid relid;
@ -109,12 +112,38 @@ ExecRenameStmt(RenameStmt *stmt)
AclResult aclresult;
aclresult = pg_namespace_aclcheck(namespaceId,
GetUserId(),
ACL_CREATE);
GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceId));
/*
* Do NOT refer to stmt->renameType here because
* you can also rename an index with ALTER TABLE
*/
if (get_rel_relkind(relid) == RELKIND_INDEX)
{
/* see if we depend on a constraint */
List* depOids = getDependentOids(
RelationRelationId, relid,
ConstraintRelationId,
DEPENDENCY_INTERNAL);
/* there should only be one constraint */
Assert(list_length(depOids) <= 1);
if (list_length(depOids) == 1)
{
Oid conRelId = linitial_oid(depOids);
/*
* Apply the same name to the
* constraint and tell it that this
* is an implicit rename triggered
* by an "ALTER INDEX" command.
*/
RenameConstraint(conRelId,
stmt->newname, true, "ALTER INDEX");
}
}
renamerel(relid, stmt->newname);
break;
}
@ -130,6 +159,52 @@ ExecRenameStmt(RenameStmt *stmt)
stmt->subname, /* old att name */
stmt->newname); /* new att name */
break;
case OBJECT_CONSTRAINT:
/* XXX could do extra function renameconstr() - but I
* don't know where it should go */
/* renameconstr(relid,
stmt->subname,
stmt->newname); */
{
List *depRelOids;
ListCell *l;
Oid conId =
GetRelationConstraintOid(relid,
stmt->subname);
if (!OidIsValid(conId)) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("constraint with name \"%s\" "
"does not exist",
stmt->subname)));
}
RenameConstraint(conId, stmt->newname,
false, NULL);
depRelOids = getReferencingOids(
ConstraintRelationId, conId, 0,
RelationRelationId,
DEPENDENCY_INTERNAL);
foreach(l, depRelOids)
{
Oid depRelOid;
Oid nspOid;
depRelOid = lfirst_oid(l);
nspOid = get_rel_namespace(depRelOid);
if (get_rel_relkind(depRelOid) == RELKIND_INDEX)
{
ereport(NOTICE,
(errmsg("ALTER TABLE / CONSTRAINT will implicitly rename index "
"\"%s\" to \"%s\" on table \"%s.%s\"",
get_rel_name(depRelOid),
stmt->newname,
get_namespace_name(nspOid),
get_rel_name(relid))));
renamerel(depRelOid, stmt->newname);
}
}
}
break;
default:
/* can't happen */ ;
}

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.526 2006/02/04 19:06:46 adunstan Exp $
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.527 2006/02/11 22:17:18 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -4096,6 +4096,15 @@ RenameStmt: ALTER AGGREGATE func_name '(' aggr_argtype ')' RENAME TO name
n->newname = $8;
$$ = (Node *)n;
}
| ALTER TABLE relation_expr ALTER CONSTRAINT name RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);
n->renameType = OBJECT_CONSTRAINT;
n->relation = $3;
n->subname = $6;
n->newname = $9;
$$ = (Node *)n;
}
| ALTER TRIGGER name ON relation_expr RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.250 2005/11/29 01:25:49 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.251 2006/02/11 22:17:19 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -1406,6 +1406,7 @@ CreateCommandTag(Node *parsetree)
case OBJECT_SCHEMA:
tag = "ALTER SCHEMA";
break;
case OBJECT_CONSTRAINT:
case OBJECT_COLUMN:
case OBJECT_TABLE:
tag = "ALTER TABLE";

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.18 2005/11/21 12:49:32 alvherre Exp $
* $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.19 2006/02/11 22:17:19 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -179,6 +179,12 @@ extern long changeDependencyFor(Oid classId, Oid objectId,
extern bool objectIsInternalDependency(Oid classId, Oid objectId);
extern List* getDependentOids(Oid classId, Oid objId,
Oid refClassId, DependencyType deptype);
extern List* getReferencingOids(Oid refClassId, Oid refObjId, Oid refObjSubId,
Oid classId, DependencyType deptype);
/* in pg_shdepend.c */
extern void recordSharedDependencyOn(ObjectAddress *depender,

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.19 2005/11/22 18:17:30 momjian Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.20 2006/02/11 22:17:19 momjian Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -187,4 +187,10 @@ extern char *GetConstraintNameForTrigger(Oid triggerId);
extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
Oid newNspId, bool isType);
extern void RenameConstraint(Oid conId, const char* newName,
bool implicitRename, const char* cmdName);
extern Oid GetRelationConstraintOid(Oid relId, const char* name);
extern Oid GetConstraintRelationId(Oid conId);
#endif /* PG_CONSTRAINT_H */

View File

@ -159,6 +159,10 @@ CREATE TABLE tmp3 (a int, b int);
CREATE TABLE tmp4 (a int, b int, unique(a,b));
NOTICE: CREATE TABLE / UNIQUE will create implicit index "tmp4_a_key" for table "tmp4"
CREATE TABLE tmp5 (a int, b int);
-- creates implicit index tmp6_a_key
CREATE TABLE tmp6 (a int, b int, unique(a));
NOTICE: CREATE TABLE / UNIQUE will create implicit index "tmp6_a_key" for table "tmp6"
CREATE INDEX tmp6_b_key ON tmp6(b);
-- Insert rows into tmp2 (pktable)
INSERT INTO tmp2 values (1);
INSERT INTO tmp2 values (2);
@ -186,6 +190,22 @@ ALTER TABLE tmp3 add constraint tmpconstr foreign key (a) references tmp2 match
-- tmp4 is a,b
ALTER TABLE tmp5 add constraint tmpconstr foreign key(a) references tmp4(a) match full;
ERROR: there is no unique constraint matching given keys for referenced table "tmp4"
-- check if constraint and index name stay in sync if we rename one or the other
-- fail here
ALTER TABLE tmp6 ALTER CONSTRAINT tmp6_a_key RENAME TO tmp6_b_key;
NOTICE: ALTER TABLE / CONSTRAINT will implicitly rename index "tmp6_a_key" to "tmp6_b_key" on table "public.tmp6"
ERROR: relation "tmp6_b_key" already exists
-- succeed
ALTER TABLE tmp6 ALTER CONSTRAINT tmp6_a_key RENAME TO tmp6_c_key;
NOTICE: ALTER TABLE / CONSTRAINT will implicitly rename index "tmp6_a_key" to "tmp6_c_key" on table "public.tmp6"
-- Now rename the index (this fails)
ALTER INDEX tmp6_c_key RENAME TO tmp6_b_key;
NOTICE: ALTER INDEX will implicitly rename constraint "tmp6_c_key" to "tmp6_b_key" on table "public.tmp6"
ERROR: relation "tmp6_b_key" already exists
-- this succeeds and uses ALTER TABLE syntax to rename an INDEX
ALTER TABLE tmp6_c_key RENAME TO tmp6_a_key;
NOTICE: ALTER INDEX will implicitly rename constraint "tmp6_c_key" to "tmp6_a_key" on table "public.tmp6"
DROP TABLE tmp6;
DROP TABLE tmp5;
DROP TABLE tmp4;
DROP TABLE tmp3;

View File

@ -196,6 +196,10 @@ CREATE TABLE tmp4 (a int, b int, unique(a,b));
CREATE TABLE tmp5 (a int, b int);
-- creates implicit index tmp6_a_key
CREATE TABLE tmp6 (a int, b int, unique(a));
CREATE INDEX tmp6_b_key ON tmp6(b);
-- Insert rows into tmp2 (pktable)
INSERT INTO tmp2 values (1);
INSERT INTO tmp2 values (2);
@ -227,6 +231,21 @@ ALTER TABLE tmp3 add constraint tmpconstr foreign key (a) references tmp2 match
ALTER TABLE tmp5 add constraint tmpconstr foreign key(a) references tmp4(a) match full;
-- check if constraint and index name stay in sync if we rename one or the other
-- fail here
ALTER TABLE tmp6 ALTER CONSTRAINT tmp6_a_key RENAME TO tmp6_b_key;
-- succeed
ALTER TABLE tmp6 ALTER CONSTRAINT tmp6_a_key RENAME TO tmp6_c_key;
-- Now rename the index (this fails)
ALTER INDEX tmp6_c_key RENAME TO tmp6_b_key;
-- this succeeds and uses ALTER TABLE syntax to rename an INDEX
ALTER TABLE tmp6_c_key RENAME TO tmp6_a_key;
DROP TABLE tmp6;
DROP TABLE tmp5;
DROP TABLE tmp4;