summaryrefslogtreecommitdiff
path: root/src/backend/commands
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands')
-rw-r--r--src/backend/commands/indexcmds.c149
-rw-r--r--src/backend/commands/tablecmds.c82
2 files changed, 99 insertions, 132 deletions
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 7a6a4c33adc..94ed4370023 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -69,7 +69,6 @@ static void ComputeIndexAttrs(IndexInfo *indexInfo,
static Oid GetIndexOpClass(List *opclass, Oid attrType,
char *accessMethodName, Oid accessMethodId);
static char *ChooseIndexNameAddition(List *colnames);
-static bool relationHasPrimaryKey(Relation rel);
/*
@@ -321,92 +320,6 @@ DefineIndex(RangeVar *heapRelation,
CheckPredicate(predicate);
/*
- * Extra checks when creating a PRIMARY KEY index.
- */
- if (primary)
- {
- List *cmds;
- ListCell *keys;
-
- /*
- * If ALTER TABLE, check that there isn't already a PRIMARY KEY. In
- * CREATE TABLE, we have faith that the parser rejected multiple pkey
- * clauses; and CREATE INDEX doesn't have a way to say PRIMARY KEY, so
- * it's no problem either.
- */
- if (is_alter_table &&
- relationHasPrimaryKey(rel))
- {
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
- errmsg("multiple primary keys for table \"%s\" are not allowed",
- RelationGetRelationName(rel))));
- }
-
- /*
- * Check that all of the attributes in a primary key are marked as not
- * null, otherwise attempt to ALTER TABLE .. SET NOT NULL
- */
- cmds = NIL;
- foreach(keys, attributeList)
- {
- IndexElem *key = (IndexElem *) lfirst(keys);
- HeapTuple atttuple;
-
- if (!key->name)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("primary keys cannot be expressions")));
-
- /* System attributes are never null, so no problem */
- if (SystemAttributeByName(key->name, rel->rd_rel->relhasoids))
- continue;
-
- atttuple = SearchSysCacheAttName(relationId, key->name);
- if (HeapTupleIsValid(atttuple))
- {
- if (!((Form_pg_attribute) GETSTRUCT(atttuple))->attnotnull)
- {
- /* Add a subcommand to make this one NOT NULL */
- AlterTableCmd *cmd = makeNode(AlterTableCmd);
-
- cmd->subtype = AT_SetNotNull;
- cmd->name = key->name;
-
- cmds = lappend(cmds, cmd);
- }
- ReleaseSysCache(atttuple);
- }
- else
- {
- /*
- * This shouldn't happen during CREATE TABLE, but can happen
- * during ALTER TABLE. Keep message in sync with
- * transformIndexConstraints() in parser/parse_utilcmd.c.
- */
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("column \"%s\" named in key does not exist",
- key->name)));
- }
- }
-
- /*
- * XXX: Shouldn't the ALTER TABLE .. SET NOT NULL cascade to child
- * tables? Currently, since the PRIMARY KEY itself doesn't cascade,
- * we don't cascade the notnull constraint(s) either; but this is
- * pretty debatable.
- *
- * XXX: possible future improvement: when being called from ALTER
- * TABLE, it would be more efficient to merge this with the outer
- * ALTER TABLE, so as to avoid two scans. But that seems to
- * complicate DefineIndex's API unduly.
- */
- if (cmds)
- AlterTableInternal(relationId, cmds, false);
- }
-
- /*
* Parse AM-specific options, convert to text array form, validate.
*/
reloptions = transformRelOptions((Datum) 0, options, NULL, NULL, false, false);
@@ -440,6 +353,12 @@ DefineIndex(RangeVar *heapRelation,
amcanorder, isconstraint);
/*
+ * Extra checks when creating a PRIMARY KEY index.
+ */
+ if (primary)
+ index_check_primary_key(rel, indexInfo, is_alter_table);
+
+ /*
* Report index creation if appropriate (delay this till after most of the
* error checks)
*/
@@ -466,17 +385,12 @@ DefineIndex(RangeVar *heapRelation,
indexRelationName, RelationGetRelationName(rel))));
}
- /* save lockrelid and locktag for below, then close rel */
- heaprelid = rel->rd_lockInfo.lockRelId;
- SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId);
- heap_close(rel, NoLock);
-
/*
* Make the catalog entries for the index, including constraints. Then, if
* not skip_build || concurrent, actually build the index.
*/
indexRelationId =
- index_create(relationId, indexRelationName, indexRelationId,
+ index_create(rel, indexRelationName, indexRelationId,
indexInfo, indexColNames,
accessMethodId, tablespaceId, classObjectId,
coloptions, reloptions, primary,
@@ -486,7 +400,16 @@ DefineIndex(RangeVar *heapRelation,
concurrent);
if (!concurrent)
- return; /* We're done, in the standard case */
+ {
+ /* Close the heap and we're done, in the non-concurrent case */
+ heap_close(rel, NoLock);
+ return;
+ }
+
+ /* save lockrelid and locktag for below, then close rel */
+ heaprelid = rel->rd_lockInfo.lockRelId;
+ SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId);
+ heap_close(rel, NoLock);
/*
* For a concurrent build, it's important to make the catalog entries
@@ -1532,44 +1455,6 @@ ChooseIndexColumnNames(List *indexElems)
}
/*
- * relationHasPrimaryKey -
- *
- * See whether an existing relation has a primary key.
- */
-static bool
-relationHasPrimaryKey(Relation rel)
-{
- bool result = false;
- List *indexoidlist;
- ListCell *indexoidscan;
-
- /*
- * Get the list of index OIDs for the table from the relcache, and look up
- * each one in the pg_index syscache until we find one marked primary key
- * (hopefully there isn't more than one such).
- */
- indexoidlist = RelationGetIndexList(rel);
-
- foreach(indexoidscan, indexoidlist)
- {
- Oid indexoid = lfirst_oid(indexoidscan);
- HeapTuple indexTuple;
-
- indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
- if (!HeapTupleIsValid(indexTuple)) /* should not happen */
- elog(ERROR, "cache lookup failed for index %u", indexoid);
- result = ((Form_pg_index) GETSTRUCT(indexTuple))->indisprimary;
- ReleaseSysCache(indexTuple);
- if (result)
- break;
- }
-
- list_free(indexoidlist);
-
- return result;
-}
-
-/*
* ReindexIndex
* Recreate a specific index.
*/
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index a9bb8351bc1..6726ca97331 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -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,
@@ -5010,6 +5022,76 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
}
/*
+ * 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
*/
static void