diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2011-01-25 15:42:03 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2011-01-25 15:43:05 -0500 |
commit | 88452d5ba6b3e8ad49133ac1a660ce0725710f8c (patch) | |
tree | 75762e7ac491b55eff6357e4b432fddce4169ab2 /src/backend/commands/indexcmds.c | |
parent | 966d4f52c258aba8187d61fdc1e10b80d560f07c (diff) | |
download | postgresql-88452d5ba6b3e8ad49133ac1a660ce0725710f8c.tar.gz postgresql-88452d5ba6b3e8ad49133ac1a660ce0725710f8c.zip |
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
Diffstat (limited to 'src/backend/commands/indexcmds.c')
-rw-r--r-- | src/backend/commands/indexcmds.c | 149 |
1 files changed, 17 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. */ |