diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index 7e4afd70bd520d797067c948d386b4bab0a92b96..8b3331702c5dce5ed47ae05a8c702bf1ee4394b5 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.6 2007/11/15 21:14:31 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.7 2007/12/01 23:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,6 +18,7 @@ #include "access/reloptions.h" #include "catalog/pg_type.h" #include "commands/defrem.h" +#include "nodes/makefuncs.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/rel.h" @@ -149,6 +150,50 @@ transformRelOptions(Datum oldOptions, List *defList, } +/* + * Convert the text-array format of reloptions into a List of DefElem. + * This is the inverse of transformRelOptions(). + */ +List * +untransformRelOptions(Datum options) +{ + List *result = NIL; + ArrayType *array; + Datum *optiondatums; + int noptions; + int i; + + /* Nothing to do if no options */ + if (options == (Datum) 0) + return result; + + array = DatumGetArrayTypeP(options); + + Assert(ARR_ELEMTYPE(array) == TEXTOID); + + deconstruct_array(array, TEXTOID, -1, false, 'i', + &optiondatums, NULL, &noptions); + + for (i = 0; i < noptions; i++) + { + char *s; + char *p; + Node *val = NULL; + + s = DatumGetCString(DirectFunctionCall1(textout, optiondatums[i])); + p = strchr(s, '='); + if (p) + { + *p++ = '\0'; + val = (Node *) makeString(pstrdup(p)); + } + result = lappend(result, makeDefElem(pstrdup(s), val)); + } + + return result; +} + + /* * Interpret reloptions that are given in text-array format. * diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y index 3fd29b7e97168894adf42eab40ba47fc28e37242..bd640804c461788e19780aeeabf66664d10deb01 100644 --- a/src/backend/bootstrap/bootparse.y +++ b/src/backend/bootstrap/bootparse.y @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.89 2007/07/17 05:02:00 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.90 2007/12/01 23:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -252,7 +252,7 @@ Boot_DeclareIndexStmt: LexIDStr($8), NULL, $10, - NULL, NIL, NULL, + NULL, NIL, false, false, false, false, false, true, false, false); do_end(); @@ -270,7 +270,7 @@ Boot_DeclareUniqueIndexStmt: LexIDStr($9), NULL, $11, - NULL, NIL, NULL, + NULL, NIL, true, false, false, false, false, true, false, false); do_end(); diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index 3574ab1ba3a3ed3e36153e82e73f4590d5b0d8a7..a35b376cc10db94a051031dff4b88f2b1eb42088 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.24 2007/01/05 22:19:25 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.25 2007/12/01 23:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,9 +18,11 @@ #include "access/heapam.h" #include "catalog/dependency.h" #include "catalog/indexing.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_depend.h" #include "miscadmin.h" #include "utils/fmgroids.h" +#include "utils/lsyscache.h" static bool isObjectPinned(const ObjectAddress *object, Relation rel); @@ -260,6 +262,62 @@ changeDependencyFor(Oid classId, Oid objectId, return count; } +/* + * isObjectPinned() + * + * Test if an object is required for basic database functionality. + * Caller must already have opened pg_depend. + * + * The passed subId, if any, is ignored; we assume that only whole objects + * are pinned (and that this implies pinning their components). + */ +static bool +isObjectPinned(const ObjectAddress *object, Relation rel) +{ + bool ret = false; + SysScanDesc scan; + HeapTuple tup; + ScanKeyData key[2]; + + ScanKeyInit(&key[0], + Anum_pg_depend_refclassid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->classId)); + + ScanKeyInit(&key[1], + Anum_pg_depend_refobjid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(object->objectId)); + + scan = systable_beginscan(rel, DependReferenceIndexId, true, + SnapshotNow, 2, key); + + /* + * Since we won't generate additional pg_depend entries for pinned + * objects, there can be at most one entry referencing a pinned object. + * Hence, it's sufficient to look at the first returned tuple; we don't + * need to loop. + */ + tup = systable_getnext(scan); + if (HeapTupleIsValid(tup)) + { + Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup); + + if (foundDep->deptype == DEPENDENCY_PIN) + ret = true; + } + + systable_endscan(scan); + + return ret; +} + + +/* + * Various special-purpose lookups and manipulations of pg_depend. + */ + + /* * Detect whether a sequence is marked as "owned" by a column * @@ -359,52 +417,120 @@ markSequenceUnowned(Oid seqId) heap_close(depRel, RowExclusiveLock); } + /* - * isObjectPinned() - * - * Test if an object is required for basic database functionality. - * Caller must already have opened pg_depend. + * get_constraint_index + * Given the OID of a unique or primary-key constraint, return the + * OID of the underlying unique index. * - * The passed subId, if any, is ignored; we assume that only whole objects - * are pinned (and that this implies pinning their components). + * Return InvalidOid if the index couldn't be found; this suggests the + * given OID is bogus, but we leave it to caller to decide what to do. */ -static bool -isObjectPinned(const ObjectAddress *object, Relation rel) +Oid +get_constraint_index(Oid constraintId) { - bool ret = false; + Oid indexId = InvalidOid; + Relation depRel; + ScanKeyData key[3]; SysScanDesc scan; HeapTuple tup; - ScanKeyData key[2]; + + /* Search the dependency table for the dependent index */ + depRel = heap_open(DependRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_depend_refclassid, BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(object->classId)); - + ObjectIdGetDatum(ConstraintRelationId)); ScanKeyInit(&key[1], Anum_pg_depend_refobjid, BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(object->objectId)); + ObjectIdGetDatum(constraintId)); + ScanKeyInit(&key[2], + Anum_pg_depend_refobjsubid, + BTEqualStrategyNumber, F_INT4EQ, + Int32GetDatum(0)); - scan = systable_beginscan(rel, DependReferenceIndexId, true, - SnapshotNow, 2, key); + scan = systable_beginscan(depRel, DependReferenceIndexId, true, + SnapshotNow, 3, key); - /* - * Since we won't generate additional pg_depend entries for pinned - * objects, there can be at most one entry referencing a pinned object. - * Hence, it's sufficient to look at the first returned tuple; we don't - * need to loop. - */ - tup = systable_getnext(scan); - if (HeapTupleIsValid(tup)) + while (HeapTupleIsValid(tup = systable_getnext(scan))) { - Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup); + Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup); - if (foundDep->deptype == DEPENDENCY_PIN) - ret = true; + /* + * We assume any internal dependency of an index on the constraint + * must be what we are looking for. (The relkind test is just + * paranoia; there shouldn't be any such dependencies otherwise.) + */ + if (deprec->classid == RelationRelationId && + deprec->objsubid == 0 && + deprec->deptype == DEPENDENCY_INTERNAL && + get_rel_relkind(deprec->objid) == RELKIND_INDEX) + { + indexId = deprec->objid; + break; + } } systable_endscan(scan); + heap_close(depRel, AccessShareLock); - return ret; + return indexId; +} + +/* + * get_index_constraint + * Given the OID of an index, return the OID of the owning unique or + * primary-key constraint, or InvalidOid if no such constraint. + */ +Oid +get_index_constraint(Oid indexId) +{ + Oid constraintId = InvalidOid; + Relation depRel; + ScanKeyData key[3]; + SysScanDesc scan; + HeapTuple tup; + + /* Search the dependency table for the index */ + depRel = heap_open(DependRelationId, AccessShareLock); + + ScanKeyInit(&key[0], + Anum_pg_depend_classid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(RelationRelationId)); + ScanKeyInit(&key[1], + Anum_pg_depend_objid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(indexId)); + ScanKeyInit(&key[2], + Anum_pg_depend_objsubid, + BTEqualStrategyNumber, F_INT4EQ, + Int32GetDatum(0)); + + scan = systable_beginscan(depRel, DependDependerIndexId, true, + SnapshotNow, 3, key); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup); + + /* + * We assume any internal dependency on a constraint + * must be what we are looking for. + */ + if (deprec->refclassid == ConstraintRelationId && + deprec->refobjsubid == 0 && + deprec->deptype == DEPENDENCY_INTERNAL) + { + constraintId = deprec->refobjid; + break; + } + } + + systable_endscan(scan); + heap_close(depRel, AccessShareLock); + + return constraintId; } diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index dc53546a05f3b2243750c724a958251359c4d3c6..1ae51246e795c770607cea92241621d907b23c1d 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.167 2007/11/15 21:14:33 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.168 2007/12/01 23:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -80,8 +80,6 @@ static bool relationHasPrimaryKey(Relation rel); * to index on. * 'predicate': the partial-index condition, or NULL if none. * 'options': reloptions from WITH (in list-of-DefElem form). - * 'src_options': reloptions from the source index, if this is a cloned - * index produced by CREATE TABLE LIKE ... INCLUDING INDEXES * 'unique': make the index enforce uniqueness. * 'primary': mark the index as a primary key in the catalogs. * 'isconstraint': index is for a PRIMARY KEY or UNIQUE constraint, @@ -103,7 +101,6 @@ DefineIndex(RangeVar *heapRelation, List *attributeList, Expr *predicate, List *options, - char *src_options, bool unique, bool primary, bool isconstraint, @@ -396,16 +393,9 @@ DefineIndex(RangeVar *heapRelation, } /* - * Parse AM-specific options, convert to text array form, validate. The - * src_options introduced due to using indexes via the "CREATE LIKE - * INCLUDING INDEXES" statement also need to be merged here + * Parse AM-specific options, convert to text array form, validate. */ - if (src_options) - reloptions = unflatten_reloptions(src_options); - else - reloptions = (Datum) 0; - - reloptions = transformRelOptions(reloptions, options, false, false); + reloptions = transformRelOptions((Datum) 0, options, false, false); (void) index_reloptions(amoptions, reloptions, true); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 285bc23496708da17f739d94dc021c8c051f415b..2310c821d03b7d710f798f182660fc82425798e7 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.236 2007/11/15 21:14:33 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.237 2007/12/01 23:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -3795,7 +3795,6 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel, stmt->indexParams, /* parameters */ (Expr *) stmt->whereClause, stmt->options, - stmt->src_options, stmt->unique, stmt->primary, stmt->isconstraint, diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 17de2ff3b8807932799e199fd67b2a92ba2b423f..ebee72f80c7897bb7822b3e68df86a571b8f5247 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.385 2007/11/15 22:25:15 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.386 2007/12/01 23:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2195,7 +2195,6 @@ _copyIndexStmt(IndexStmt *from) COPY_STRING_FIELD(tableSpace); COPY_NODE_FIELD(indexParams); COPY_NODE_FIELD(options); - COPY_STRING_FIELD(src_options); COPY_NODE_FIELD(whereClause); COPY_SCALAR_FIELD(unique); COPY_SCALAR_FIELD(primary); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 115436237196a956db39f45ae1a05574b73500f3..ebca3370dfabed93e8043d3a1727cb86ac617b07 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.315 2007/11/15 22:25:15 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.316 2007/12/01 23:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1046,7 +1046,6 @@ _equalIndexStmt(IndexStmt *a, IndexStmt *b) COMPARE_STRING_FIELD(tableSpace); COMPARE_NODE_FIELD(indexParams); COMPARE_NODE_FIELD(options); - COMPARE_STRING_FIELD(src_options); COMPARE_NODE_FIELD(whereClause); COMPARE_SCALAR_FIELD(unique); COMPARE_SCALAR_FIELD(primary); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 7d3952c86bd5e501f8a9b21ec9eedba650922f7c..ee56af2bc84faed7848bb15bdcc4b535b80d0435 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.318 2007/11/15 22:25:15 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.319 2007/12/01 23:44:44 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -1546,7 +1546,6 @@ _outIndexStmt(StringInfo str, IndexStmt *node) WRITE_STRING_FIELD(tableSpace); WRITE_NODE_FIELD(indexParams); WRITE_NODE_FIELD(options); - WRITE_STRING_FIELD(src_options); WRITE_NODE_FIELD(whereClause); WRITE_BOOL_FIELD(unique); WRITE_BOOL_FIELD(primary); diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 2ff6f9274d717ba946debe202e35ef303da40449..a53f01c32abe797d7c1f582da4614df6f09de69b 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -19,7 +19,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.6 2007/11/15 21:14:37 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.7 2007/12/01 23:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,6 +28,8 @@ #include "access/genam.h" #include "access/heapam.h" +#include "access/reloptions.h" +#include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/namespace.h" @@ -675,13 +677,15 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, } } + /* + * Likewise, copy indexes if requested + */ if (including_indexes && relation->rd_rel->relhasindex) { - AttrNumber *attmap; + AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns); List *parent_indexes; ListCell *l; - attmap = varattnos_map_schema(tupleDesc, cxt->columns); parent_indexes = RelationGetIndexList(relation); foreach(l, parent_indexes) @@ -693,14 +697,12 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, parent_index = index_open(parent_index_oid, AccessShareLock); /* Build CREATE INDEX statement to recreate the parent_index */ - index_stmt = generateClonedIndexStmt(cxt, parent_index, - attmap); + index_stmt = generateClonedIndexStmt(cxt, parent_index, attmap); - /* Add the new IndexStmt to the create context */ + /* Save it in the inh_indexes list for the time being */ cxt->inh_indexes = lappend(cxt->inh_indexes, index_stmt); - /* Keep our lock on the index till xact commit */ - index_close(parent_index, NoLock); + index_close(parent_index, AccessShareLock); } } @@ -713,54 +715,62 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, } /* - * Generate an IndexStmt entry using information from an already - * existing index "source_idx". - * - * Note: Much of this functionality is cribbed from pg_get_indexdef. + * Generate an IndexStmt node using information from an already existing index + * "source_idx". Attribute numbers should be adjusted according to attmap. */ static IndexStmt * generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx, AttrNumber *attmap) { - HeapTuple ht_idx; + Oid source_relid = RelationGetRelid(source_idx); HeapTuple ht_idxrel; - HeapTuple ht_am; - Form_pg_index idxrec; + HeapTuple ht_idx; Form_pg_class idxrelrec; + Form_pg_index idxrec; Form_pg_am amrec; - List *indexprs = NIL; + oidvector *indclass; + IndexStmt *index; + List *indexprs; ListCell *indexpr_item; Oid indrelid; - Oid source_relid; int keyno; Oid keycoltype; - Datum indclassDatum; - Datum indoptionDatum; + Datum datum; bool isnull; - oidvector *indclass; - int2vector *indoption; - IndexStmt *index; - Datum reloptions; - source_relid = RelationGetRelid(source_idx); + /* + * Fetch pg_class tuple of source index. We can't use the copy in the + * relcache entry because it doesn't include optional fields. + */ + ht_idxrel = SearchSysCache(RELOID, + ObjectIdGetDatum(source_relid), + 0, 0, 0); + if (!HeapTupleIsValid(ht_idxrel)) + elog(ERROR, "cache lookup failed for relation %u", source_relid); + idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel); - /* Fetch pg_index tuple for source index */ - ht_idx = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(source_relid), - 0, 0, 0); - if (!HeapTupleIsValid(ht_idx)) - elog(ERROR, "cache lookup failed for index %u", source_relid); + /* Fetch pg_index tuple for source index from relcache entry */ + ht_idx = source_idx->rd_indextuple; idxrec = (Form_pg_index) GETSTRUCT(ht_idx); - - Assert(source_relid == idxrec->indexrelid); indrelid = idxrec->indrelid; + /* Fetch pg_am tuple for source index from relcache entry */ + amrec = source_idx->rd_am; + + /* Must get indclass the hard way, since it's not stored in relcache */ + datum = SysCacheGetAttr(INDEXRELID, ht_idx, + Anum_pg_index_indclass, &isnull); + Assert(!isnull); + indclass = (oidvector *) DatumGetPointer(datum); + + /* Begin building the IndexStmt */ index = makeNode(IndexStmt); + index->relation = cxt->relation; + index->accessMethod = pstrdup(NameStr(amrec->amname)); + index->tableSpace = get_tablespace_name(source_idx->rd_node.spcNode); index->unique = idxrec->indisunique; - index->concurrent = false; index->primary = idxrec->indisprimary; - index->relation = cxt->relation; - index->isconstraint = false; + index->concurrent = false; /* * We don't try to preserve the name of the source index; instead, just @@ -768,65 +778,40 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx, */ index->idxname = NULL; - /* Must get indclass and indoption the hard way */ - indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx, - Anum_pg_index_indclass, &isnull); - Assert(!isnull); - indclass = (oidvector *) DatumGetPointer(indclassDatum); - indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx, - Anum_pg_index_indoption, &isnull); - Assert(!isnull); - indoption = (int2vector *) DatumGetPointer(indoptionDatum); - - /* Fetch pg_class tuple of source index */ - ht_idxrel = SearchSysCache(RELOID, - ObjectIdGetDatum(source_relid), - 0, 0, 0); - if (!HeapTupleIsValid(ht_idxrel)) - elog(ERROR, "cache lookup failed for relation %u", source_relid); - /* - * Store the reloptions for later use by this new index + * If the index is marked PRIMARY, it's certainly from a constraint; + * else, if it's not marked UNIQUE, it certainly isn't; else, we have + * to search pg_depend to see if there's an associated unique constraint. */ - reloptions = SysCacheGetAttr(RELOID, ht_idxrel, - Anum_pg_class_reloptions, &isnull); - if (!isnull) - index->src_options = flatten_reloptions(source_relid); - - idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel); - - /* Fetch pg_am tuple for the index's access method */ - ht_am = SearchSysCache(AMOID, - ObjectIdGetDatum(idxrelrec->relam), - 0, 0, 0); - if (!HeapTupleIsValid(ht_am)) - elog(ERROR, "cache lookup failed for access method %u", - idxrelrec->relam); - amrec = (Form_pg_am) GETSTRUCT(ht_am); - index->accessMethod = pstrdup(NameStr(amrec->amname)); + if (index->primary) + index->isconstraint = true; + else if (!index->unique) + index->isconstraint = false; + else + index->isconstraint = OidIsValid(get_index_constraint(source_relid)); /* Get the index expressions, if any */ - if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs)) + datum = SysCacheGetAttr(INDEXRELID, ht_idx, + Anum_pg_index_indexprs, &isnull); + if (!isnull) { - Datum exprsDatum; - bool isnull; char *exprsString; - exprsDatum = SysCacheGetAttr(INDEXRELID, ht_idx, - Anum_pg_index_indexprs, &isnull); - exprsString = DatumGetCString(DirectFunctionCall1(textout, - exprsDatum)); - Assert(!isnull); + exprsString = DatumGetCString(DirectFunctionCall1(textout, datum)); indexprs = (List *) stringToNode(exprsString); } + else + indexprs = NIL; - indexpr_item = list_head(indexprs); + /* Build the list of IndexElem */ + index->indexParams = NIL; + indexpr_item = list_head(indexprs); for (keyno = 0; keyno < idxrec->indnatts; keyno++) { IndexElem *iparam; AttrNumber attnum = idxrec->indkey.values[keyno]; - int16 opt = indoption->values[keyno]; + int16 opt = source_idx->rd_indoption[keyno]; iparam = makeNode(IndexElem); @@ -849,11 +834,14 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx, if (indexpr_item == NULL) elog(ERROR, "too few entries in indexprs list"); indexkey = (Node *) lfirst(indexpr_item); + indexpr_item = lnext(indexpr_item); + + /* OK to modify indexkey since we are working on a private copy */ change_varattnos_of_a_node(indexkey, attmap); + iparam->name = NULL; iparam->expr = indexkey; - indexpr_item = lnext(indexpr_item); keycoltype = exprType(indexkey); } @@ -866,40 +854,50 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx, /* Adjust options if necessary */ if (amrec->amcanorder) { - /* If it supports sort ordering, report DESC and NULLS opts */ + /* + * If it supports sort ordering, copy DESC and NULLS opts. + * Don't set non-default settings unnecessarily, though, + * so as to improve the chance of recognizing equivalence + * to constraint indexes. + */ if (opt & INDOPTION_DESC) + { iparam->ordering = SORTBY_DESC; - if (opt & INDOPTION_NULLS_FIRST) - iparam->nulls_ordering = SORTBY_NULLS_FIRST; + if ((opt & INDOPTION_NULLS_FIRST) == 0) + iparam->nulls_ordering = SORTBY_NULLS_LAST; + } + else + { + if (opt & INDOPTION_NULLS_FIRST) + iparam->nulls_ordering = SORTBY_NULLS_FIRST; + } } index->indexParams = lappend(index->indexParams, iparam); } - /* Use the same tablespace as the source index */ - index->tableSpace = get_tablespace_name(source_idx->rd_node.spcNode); + /* Copy reloptions if any */ + datum = SysCacheGetAttr(RELOID, ht_idxrel, + Anum_pg_class_reloptions, &isnull); + if (!isnull) + index->options = untransformRelOptions(datum); /* If it's a partial index, decompile and append the predicate */ - if (!heap_attisnull(ht_idx, Anum_pg_index_indpred)) + datum = SysCacheGetAttr(INDEXRELID, ht_idx, + Anum_pg_index_indpred, &isnull); + if (!isnull) { - Datum pred_datum; - bool isnull; char *pred_str; /* Convert text string to node tree */ - pred_datum = SysCacheGetAttr(INDEXRELID, ht_idx, - Anum_pg_index_indpred, &isnull); - Assert(!isnull); - pred_str = DatumGetCString(DirectFunctionCall1(textout, - pred_datum)); + pred_str = DatumGetCString(DirectFunctionCall1(textout, datum)); index->whereClause = (Node *) stringToNode(pred_str); + /* Adjust attribute numbers */ change_varattnos_of_a_node(index->whereClause, attmap); } /* Clean up */ - ReleaseSysCache(ht_idx); ReleaseSysCache(ht_idxrel); - ReleaseSysCache(ht_am); return index; } @@ -924,11 +922,11 @@ get_opclass(Oid opclass, Oid actual_datatype) elog(ERROR, "cache lookup failed for opclass %u", opclass); opc_rec = (Form_pg_opclass) GETSTRUCT(ht_opc); - if (!OidIsValid(actual_datatype) || - GetDefaultOpClass(actual_datatype, opc_rec->opcmethod) != opclass) + if (GetDefaultOpClass(actual_datatype, opc_rec->opcmethod) != opclass) { + /* For simplicity, we always schema-qualify the name */ char *nsp_name = get_namespace_name(opc_rec->opcnamespace); - char *opc_name = NameStr(opc_rec->opcname); + char *opc_name = pstrdup(NameStr(opc_rec->opcname)); result = list_make2(makeString(nsp_name), makeString(opc_name)); } @@ -940,8 +938,8 @@ get_opclass(Oid opclass, Oid actual_datatype) /* * transformIndexConstraints - * Handle UNIQUE and PRIMARY KEY constraints, which create - * indexes. We also merge index definitions arising from + * Handle UNIQUE and PRIMARY KEY constraints, which create indexes. + * We also merge in any index definitions arising from * LIKE ... INCLUDING INDEXES. */ static void @@ -960,7 +958,30 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) { Constraint *constraint = (Constraint *) lfirst(lc); + Assert(IsA(constraint, Constraint)); + Assert(constraint->contype == CONSTR_PRIMARY || + constraint->contype == CONSTR_UNIQUE); + index = transformIndexConstraint(constraint, cxt); + + indexlist = lappend(indexlist, index); + } + + /* Add in any indexes defined by LIKE ... INCLUDING INDEXES */ + foreach(lc, cxt->inh_indexes) + { + index = (IndexStmt *) lfirst(lc); + + if (index->primary) + { + if (cxt->pkey != NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("multiple primary keys for table \"%s\" are not allowed", + cxt->relation->relname))); + cxt->pkey = index; + } + indexlist = lappend(indexlist, index); } @@ -995,8 +1016,11 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) { IndexStmt *priorindex = lfirst(k); - if (equal(index->indexParams, priorindex->indexParams)) + if (equal(index->indexParams, priorindex->indexParams) && + equal(index->whereClause, priorindex->whereClause) && + strcmp(index->accessMethod, priorindex->accessMethod) == 0) { + priorindex->unique |= index->unique; /* * If the prior index is as yet unnamed, and this one is * named, then transfer the name to the prior index. This @@ -1013,27 +1037,13 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) if (keep) cxt->alist = lappend(cxt->alist, index); } - - /* Copy indexes defined by LIKE ... INCLUDING INDEXES */ - foreach(lc, cxt->inh_indexes) - { - index = (IndexStmt *) lfirst(lc); - - if (index->primary) - { - if (cxt->pkey) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("multiple primary keys for table \"%s\" are not allowed", - cxt->relation->relname))); - - cxt->pkey = index; - } - - cxt->alist = lappend(cxt->alist, index); - } } +/* + * transformIndexConstraint + * Transform one UNIQUE or PRIMARY KEY constraint for + * transformIndexConstraints. + */ static IndexStmt * transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) { @@ -1041,13 +1051,10 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) ListCell *keys; IndexElem *iparam; - Assert(constraint->contype == CONSTR_PRIMARY || - constraint->contype == CONSTR_UNIQUE); - index = makeNode(IndexStmt); + index->unique = true; index->primary = (constraint->contype == CONSTR_PRIMARY); - if (index->primary) { if (cxt->pkey != NULL) @@ -1771,7 +1778,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) /* * transformIndexConstraints wants cxt.alist to contain only index - * statements, so transfer anything we already have into save_alist. + * statements, so transfer anything we already have into save_alist * immediately. */ save_alist = cxt.alist; diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 9a1e877820dd42e3a0c92bb9332826d8c03ad3bb..bcbfe251b7f07e19a251c5720ebf9af7ebab8304 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.287 2007/11/15 21:14:38 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.288 2007/12/01 23:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -924,7 +924,6 @@ ProcessUtility(Node *parsetree, stmt->indexParams, /* parameters */ (Expr *) stmt->whereClause, stmt->options, - stmt->src_options, stmt->unique, stmt->primary, stmt->isconstraint, diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 168da20aa23d0d998774b3eb4752f91ad0108841..8e4d5c90cd3e76f5bc3f306dc01d249033fc9316 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.265 2007/11/15 21:14:39 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.266 2007/12/01 23:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -133,7 +133,6 @@ static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, int prettyFlags); static char *pg_get_expr_worker(text *expr, Oid relid, char *relname, int prettyFlags); -static Oid get_constraint_index(Oid constraintId); static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, int prettyFlags); static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, @@ -195,6 +194,7 @@ static char *generate_relation_name(Oid relid); static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes); static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2); static text *string_to_text(char *str); +static char *flatten_reloptions(Oid relid); #define only_marker(rte) ((rte)->inh ? "" : "ONLY ") @@ -1384,68 +1384,6 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS) } -/* - * get_constraint_index - * Given the OID of a unique or primary-key constraint, return the - * OID of the underlying unique index. - * - * Return InvalidOid if the index couldn't be found; this suggests the - * given OID is bogus, but we leave it to caller to decide what to do. - */ -static Oid -get_constraint_index(Oid constraintId) -{ - Oid indexId = InvalidOid; - Relation depRel; - ScanKeyData key[3]; - SysScanDesc scan; - HeapTuple tup; - - /* Search the dependency table for the dependent index */ - depRel = heap_open(DependRelationId, AccessShareLock); - - ScanKeyInit(&key[0], - Anum_pg_depend_refclassid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(ConstraintRelationId)); - ScanKeyInit(&key[1], - Anum_pg_depend_refobjid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(constraintId)); - ScanKeyInit(&key[2], - Anum_pg_depend_refobjsubid, - BTEqualStrategyNumber, F_INT4EQ, - Int32GetDatum(0)); - - scan = systable_beginscan(depRel, DependReferenceIndexId, true, - SnapshotNow, 3, key); - - while (HeapTupleIsValid(tup = systable_getnext(scan))) - { - Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup); - - /* - * We assume any internal dependency of an index on the constraint - * must be what we are looking for. (The relkind test is just - * paranoia; there shouldn't be any such dependencies otherwise.) - */ - if (deprec->classid == RelationRelationId && - deprec->objsubid == 0 && - deprec->deptype == DEPENDENCY_INTERNAL && - get_rel_relkind(deprec->objid) == RELKIND_INDEX) - { - indexId = deprec->objid; - break; - } - } - - systable_endscan(scan); - heap_close(depRel, AccessShareLock); - - return indexId; -} - - /* * deparse_expression - General utility for deparsing expressions * @@ -5507,7 +5445,7 @@ string_to_text(char *str) /* * Generate a C string representing a relation's reloptions, or NULL if none. */ -char * +static char * flatten_reloptions(Oid relid) { char *result = NULL; @@ -5543,32 +5481,3 @@ flatten_reloptions(Oid relid) return result; } - -/* - * Generate an Array Datum representing a relation's reloptions using - * a C string - */ -Datum -unflatten_reloptions(char *reloptstring) -{ - Datum result = (Datum) 0; - - if (reloptstring) - { - Datum sep, - relopts; - - /* - * We want to use text_to_array(reloptstring, ', ') --- but - * DirectFunctionCall2(text_to_array) does not work, because - * text_to_array() relies on fcinfo to be valid. So use - * OidFunctionCall2. - */ - sep = DirectFunctionCall1(textin, CStringGetDatum(", ")); - relopts = DirectFunctionCall1(textin, CStringGetDatum(reloptstring)); - - result = OidFunctionCall2(F_TEXT_TO_ARRAY, relopts, sep); - } - - return result; -} diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h index 1c5aa3526cbc891ff6a9d928eb6dc81a56cd1e6e..f6a7c42c19db17d2ba1af93282587f0bda9682b6 100644 --- a/src/include/access/reloptions.h +++ b/src/include/access/reloptions.h @@ -11,7 +11,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/reloptions.h,v 1.3 2007/01/05 22:19:51 momjian Exp $ + * $PostgreSQL: pgsql/src/include/access/reloptions.h,v 1.4 2007/12/01 23:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -23,6 +23,8 @@ extern Datum transformRelOptions(Datum oldOptions, List *defList, bool ignoreOids, bool isReset); +extern List *untransformRelOptions(Datum options); + extern void parseRelOptions(Datum options, int numkeywords, const char *const * keywords, char **values, bool validate); diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index 1b238d8d8a93ce4bb31bee8d77ad7d8d5d256f14..637a7a93712f7a3a3ed3e3c8a500153d15a64e3f 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.31 2007/11/15 21:14:42 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.32 2007/12/01 23:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -207,6 +207,10 @@ extern bool sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId); extern void markSequenceUnowned(Oid seqId); +extern Oid get_constraint_index(Oid constraintId); + +extern Oid get_index_constraint(Oid indexId); + /* in pg_shdepend.c */ extern void recordSharedDependencyOn(ObjectAddress *depender, diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 99621d3b26e38c3153a484f15970ceafd294ea43..152061239f503ed6b3acdbdaeef0975d669c9609 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.86 2007/11/15 22:25:17 momjian Exp $ + * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.87 2007/12/01 23:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -26,7 +26,6 @@ extern void DefineIndex(RangeVar *heapRelation, List *attributeList, Expr *predicate, List *options, - char *src_options, bool unique, bool primary, bool isconstraint, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 51d760ef564cdb13afe7befe9eaf23cdd912ec9b..d7055140a5fe20050a76c5ad10f7238da9557561 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.356 2007/11/15 22:25:17 momjian Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.357 2007/12/01 23:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1540,7 +1540,6 @@ typedef struct IndexStmt char *tableSpace; /* tablespace, or NULL to use parent's */ List *indexParams; /* a list of IndexElem */ List *options; /* options from WITH clause */ - char *src_options; /* relopts inherited from source index */ Node *whereClause; /* qualification (partial-index predicate) */ bool unique; /* is index unique? */ bool primary; /* is index on primary key? */ diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index cf9e03fcf3d3e1faa68e53e96e7d0dfc01778435..d4fb1262cdf9ec0cb6b2938805e7af09d31e2fcc 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.306 2007/11/15 21:14:45 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.307 2007/12/01 23:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -566,8 +566,6 @@ extern List *deparse_context_for_plan(Node *outer_plan, Node *inner_plan, extern const char *quote_identifier(const char *ident); extern char *quote_qualified_identifier(const char *namespace, const char *ident); -extern char *flatten_reloptions(Oid relid); -extern Datum unflatten_reloptions(char *reloptstring); /* tid.c */ extern Datum tidin(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index 40dfaeda902a0c75e0f51b8e66fc86c2190f5319..f81776fe80463dac11247b8cab439d3415f96030 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -634,6 +634,7 @@ SELECT * FROM inhg; /* Two records with three columns in order x=x, xx=text, y=y DROP TABLE inhg; CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* copies indexes */ +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "inhg_pkey" for table "inhg" INSERT INTO inhg VALUES (5, 10); INSERT INTO inhg VALUES (20, 10); -- should fail ERROR: duplicate key value violates unique constraint "inhg_pkey" @@ -647,6 +648,7 @@ CREATE UNIQUE INDEX inhz_xx_idx on inhz (xx) WHERE xx <> 'test'; /* Ok to create multiple unique indexes */ CREATE TABLE inhg (x text UNIQUE, LIKE inhz INCLUDING INDEXES); NOTICE: CREATE TABLE / UNIQUE will create implicit index "inhg_x_key" for table "inhg" +NOTICE: CREATE TABLE / UNIQUE will create implicit index "inhg_yy_key" for table "inhg" INSERT INTO inhg (xx, yy, x) VALUES ('test', 5, 10); INSERT INTO inhg (xx, yy, x) VALUES ('test', 10, 15); INSERT INTO inhg (xx, yy, x) VALUES ('foo', 10, 15); -- should fail