-<!-- $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>
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>
<!--
-$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
-->
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
<!--
-$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
-->
<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>
</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>
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
+}
+
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
+}
+
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
case OBJECT_INDEX:
case OBJECT_COLUMN:
case OBJECT_TRIGGER:
+ case OBJECT_CONSTRAINT:
{
Oid relid;
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;
}
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 */ ;
}
*
*
* 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
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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
case OBJECT_SCHEMA:
tag = "ALTER SCHEMA";
break;
+ case OBJECT_CONSTRAINT:
case OBJECT_COLUMN:
case OBJECT_TABLE:
tag = "ALTER TABLE";
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
* 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
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 */
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);
-- 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;
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);
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;