mirror of
https://github.com/postgres/postgres.git
synced 2025-06-16 06:01:02 +03:00
Implement ALTER TABLE ADD UNIQUE/PRIMARY KEY USING INDEX.
This feature allows a unique or pkey constraint to be created using an already-existing unique index. While the constraint isn't very functionally different from the bare index, it's nice to be able to do that for documentation purposes. The main advantage over just issuing a plain ALTER TABLE ADD UNIQUE/PRIMARY KEY is that the index can be created with CREATE INDEX CONCURRENTLY, so that there is not a long interval where the table is locked against updates. On the way, refactor some of the code in DefineIndex() and index_create() so that we don't have to pass through those functions in order to create the index constraint's catalog entries. Also, in parse_utilcmd.c, pass around the ParseState pointer in struct CreateStmtContext to save on notation, and add error location pointers to some error reports that didn't have one before. Gurjeet Singh, reviewed by Steve Singer and Tom Lane
This commit is contained in:
@ -319,6 +319,8 @@ static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
|
||||
static void ATExecAddConstraint(List **wqueue,
|
||||
AlteredTableInfo *tab, Relation rel,
|
||||
Constraint *newConstraint, bool recurse, LOCKMODE lockmode);
|
||||
static void ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
|
||||
IndexStmt *stmt, LOCKMODE lockmode);
|
||||
static void ATAddCheckConstraint(List **wqueue,
|
||||
AlteredTableInfo *tab, Relation rel,
|
||||
Constraint *constr,
|
||||
@ -2594,6 +2596,7 @@ AlterTableGetLockLevel(List *cmds)
|
||||
case AT_DisableTrigAll:
|
||||
case AT_DisableTrigUser:
|
||||
case AT_AddIndex: /* from ADD CONSTRAINT */
|
||||
case AT_AddIndexConstraint:
|
||||
cmd_lockmode = ShareRowExclusiveLock;
|
||||
break;
|
||||
|
||||
@ -2811,6 +2814,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
||||
cmd->subtype = AT_AddConstraintRecurse;
|
||||
pass = AT_PASS_ADD_CONSTR;
|
||||
break;
|
||||
case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
|
||||
ATSimplePermissions(rel, ATT_TABLE);
|
||||
/* This command never recurses */
|
||||
/* No command-specific prep needed */
|
||||
pass = AT_PASS_ADD_CONSTR;
|
||||
break;
|
||||
case AT_DropConstraint: /* DROP CONSTRAINT */
|
||||
ATSimplePermissions(rel, ATT_TABLE);
|
||||
/* Recursion occurs during execution phase */
|
||||
@ -3042,6 +3051,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
||||
ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
|
||||
true, lockmode);
|
||||
break;
|
||||
case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
|
||||
ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def, lockmode);
|
||||
break;
|
||||
case AT_DropConstraint: /* DROP CONSTRAINT */
|
||||
ATExecDropConstraint(rel, cmd->name, cmd->behavior,
|
||||
false, false,
|
||||
@ -5009,6 +5021,76 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
|
||||
false);
|
||||
}
|
||||
|
||||
/*
|
||||
* ALTER TABLE ADD CONSTRAINT USING INDEX
|
||||
*/
|
||||
static void
|
||||
ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
|
||||
IndexStmt *stmt, LOCKMODE lockmode)
|
||||
{
|
||||
Oid index_oid = stmt->indexOid;
|
||||
Relation indexRel;
|
||||
char *indexName;
|
||||
IndexInfo *indexInfo;
|
||||
char *constraintName;
|
||||
char constraintType;
|
||||
|
||||
Assert(IsA(stmt, IndexStmt));
|
||||
Assert(OidIsValid(index_oid));
|
||||
Assert(stmt->isconstraint);
|
||||
|
||||
indexRel = index_open(index_oid, AccessShareLock);
|
||||
|
||||
indexName = pstrdup(RelationGetRelationName(indexRel));
|
||||
|
||||
indexInfo = BuildIndexInfo(indexRel);
|
||||
|
||||
/* this should have been checked at parse time */
|
||||
if (!indexInfo->ii_Unique)
|
||||
elog(ERROR, "index \"%s\" is not unique", indexName);
|
||||
|
||||
/*
|
||||
* Determine name to assign to constraint. We require a constraint to
|
||||
* have the same name as the underlying index; therefore, use the index's
|
||||
* existing name as the default constraint name, and if the user explicitly
|
||||
* gives some other name for the constraint, rename the index to match.
|
||||
*/
|
||||
constraintName = stmt->idxname;
|
||||
if (constraintName == NULL)
|
||||
constraintName = indexName;
|
||||
else if (strcmp(constraintName, indexName) != 0)
|
||||
{
|
||||
ereport(NOTICE,
|
||||
(errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
|
||||
indexName, constraintName)));
|
||||
RenameRelation(index_oid, constraintName, OBJECT_INDEX);
|
||||
}
|
||||
|
||||
/* Extra checks needed if making primary key */
|
||||
if (stmt->primary)
|
||||
index_check_primary_key(rel, indexInfo, true);
|
||||
|
||||
/* Note we currently don't support EXCLUSION constraints here */
|
||||
if (stmt->primary)
|
||||
constraintType = CONSTRAINT_PRIMARY;
|
||||
else
|
||||
constraintType = CONSTRAINT_UNIQUE;
|
||||
|
||||
/* Create the catalog entries for the constraint */
|
||||
index_constraint_create(rel,
|
||||
index_oid,
|
||||
indexInfo,
|
||||
constraintName,
|
||||
constraintType,
|
||||
stmt->deferrable,
|
||||
stmt->initdeferred,
|
||||
stmt->primary,
|
||||
true,
|
||||
allowSystemTableMods);
|
||||
|
||||
index_close(indexRel, NoLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* ALTER TABLE ADD CONSTRAINT
|
||||
*/
|
||||
|
Reference in New Issue
Block a user