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

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/ddl.sgml,v 1.52 2006/02/04 23:03:19 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/ddl.sgml,v 1.53 2006/02/11 22:17:18 momjian Exp $ -->
<chapter id="ddl">
<title>Data Definition</title>
@ -543,6 +543,10 @@ CREATE TABLE products (
price numeric
);
</programlisting>
Since <productname>PostgreSQL</productname> implements a UNIQUE constraint by
means of an index, the above command will also create an index with the same
name as the constraint. If you later on change the name of one of those, the
name of the corresponding object will be changed automatically as well.
</para>
<indexterm>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_index.sgml,v 1.6 2005/08/22 19:39:52 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_index.sgml,v 1.7 2006/02/11 22:17:18 momjian Exp $
PostgreSQL documentation
-->
@ -107,6 +107,15 @@ ALTER INDEX <replaceable class="PARAMETER">name</replaceable> SET TABLESPACE <re
of <command>ALTER TABLE</> that apply to indexes.
</para>
<para>
Indexes are also used internally by constraints, namely by UNIQUE and
PRIMARY KEY constraints. If you rename an index that is used internally by
a constraint of that type, this constraint will implicitly be renamed as
well. On the other hand, if you rename such a constraint, it will
implicitly rename its corresponding index such that both objects always
have the same name.
</para>
<para>
There was formerly an <command>ALTER INDEX OWNER</> variant, but
this is now ignored (with a warning). An index cannot have an owner

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.82 2005/12/08 21:35:36 momjian Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.83 2006/02/11 22:17:18 momjian Exp $
PostgreSQL documentation
-->
@ -24,6 +24,8 @@ ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ]
<replaceable class="PARAMETER">action</replaceable> [, ... ]
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ]
RENAME [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> TO <replaceable class="PARAMETER">new_column</replaceable>
ALTER TABLE [ ONLY ] <replaceable class="PARAMETER">name</replaceable> [ * ]
ALTER CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> RENAME TO <replaceable class="PARAMETER">new_constraint_name</replaceable>
ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
RENAME TO <replaceable class="PARAMETER">new_name</replaceable>
ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
@ -169,6 +171,18 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
</listitem>
</varlistentry>
<varlistentry>
<term><literal>ALTER CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> RENAME TO <replaceable class="PARAMETER">new_constraint_name</replaceable></literal></term>
<listitem>
<para>
This form renames a constraint that is defined on the table. Note that if
a constraint is using an index internally (<literal>UNIQUE</> or
<literal>PRIMARY KEY</> constraints), the corresponding index will be
renamed as well.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>ADD <replaceable class="PARAMETER">table_constraint</replaceable></literal></term>
<listitem>

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;