diff options
Diffstat (limited to 'src')
44 files changed, 1231 insertions, 1172 deletions
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y index ee624697776..2f64414aee2 100644 --- a/src/backend/bootstrap/bootparse.y +++ b/src/backend/bootstrap/bootparse.y @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.57 2003/05/27 17:49:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.58 2003/05/28 16:03:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -273,7 +273,7 @@ boot_index_param: { IndexElem *n = makeNode(IndexElem); n->name = LexIDStr($1); - n->funcname = n->args = NIL; /* no func indexes */ + n->expr = NULL; n->opclass = makeList1(makeString(LexIDStr($2))); $$ = n; } diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index c903afaa003..8539537f519 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.158 2003/05/27 17:49:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.159 2003/05/28 16:03:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1169,6 +1169,10 @@ index_register(Oid heap, newind->il_info = (IndexInfo *) palloc(sizeof(IndexInfo)); memcpy(newind->il_info, indexInfo, sizeof(IndexInfo)); + /* expressions will likely be null, but may as well copy it */ + newind->il_info->ii_Expressions = (List *) + copyObject(indexInfo->ii_Expressions); + newind->il_info->ii_ExpressionsState = NIL; /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 37282aab7f3..d57f645b281 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.24 2003/05/27 17:49:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.25 2003/05/28 16:03:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -851,6 +851,91 @@ recordDependencyOnExpr(const ObjectAddress *depender, } /* + * recordDependencyOnSingleRelExpr - find expression dependencies + * + * As above, but only one relation is expected to be referenced (with + * varno = 1 and varlevelsup = 0). Pass the relation OID instead of a + * range table. An additional frammish is that dependencies on that + * relation (or its component columns) will be marked with 'self_behavior', + * whereas 'behavior' is used for everything else. + */ +void +recordDependencyOnSingleRelExpr(const ObjectAddress *depender, + Node *expr, Oid relId, + DependencyType behavior, + DependencyType self_behavior) +{ + find_expr_references_context context; + RangeTblEntry rte; + + init_object_addresses(&context.addrs); + + /* We gin up a rather bogus rangetable list to handle Vars */ + MemSet(&rte, 0, sizeof(rte)); + rte.type = T_RangeTblEntry; + rte.rtekind = RTE_RELATION; + rte.relid = relId; + + context.rtables = makeList1(makeList1(&rte)); + + /* Scan the expression tree for referenceable objects */ + find_expr_references_walker(expr, &context); + + /* Remove any duplicates */ + eliminate_duplicate_dependencies(&context.addrs); + + /* Separate self-dependencies if necessary */ + if (behavior != self_behavior && context.addrs.numrefs > 0) + { + ObjectAddresses self_addrs; + ObjectAddress *outobj; + int oldref, + outrefs; + + init_object_addresses(&self_addrs); + + outobj = context.addrs.refs; + outrefs = 0; + for (oldref = 0; oldref < context.addrs.numrefs; oldref++) + { + ObjectAddress *thisobj = context.addrs.refs + oldref; + + if (thisobj->classId == RelOid_pg_class && + thisobj->objectId == relId) + { + /* Move this ref into self_addrs */ + add_object_address(OCLASS_CLASS, relId, thisobj->objectSubId, + &self_addrs); + } + else + { + /* Keep it in context.addrs */ + outobj->classId = thisobj->classId; + outobj->objectId = thisobj->objectId; + outobj->objectSubId = thisobj->objectSubId; + outobj++; + outrefs++; + } + } + context.addrs.numrefs = outrefs; + + /* Record the self-dependencies */ + recordMultipleDependencies(depender, + self_addrs.refs, self_addrs.numrefs, + self_behavior); + + term_object_addresses(&self_addrs); + } + + /* Record the external dependencies */ + recordMultipleDependencies(depender, + context.addrs.refs, context.addrs.numrefs, + behavior); + + term_object_addresses(&context.addrs); +} + +/* * Recursively search an expression tree for object references. * * Note: we avoid creating references to columns of tables that participate diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 4c9c98e3856..fe57ab7bad3 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.244 2003/05/12 00:17:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.245 2003/05/28 16:03:55 tgl Exp $ * * * INTERFACE ROUTINES @@ -1885,41 +1885,21 @@ RemoveStatistics(Relation rel, AttrNumber attnum) static void RelationTruncateIndexes(Oid heapId) { - Relation indexRelation; - ScanKeyData entry; - SysScanDesc scan; - HeapTuple indexTuple; - - /* Scan pg_index to find indexes on specified heap */ - indexRelation = heap_openr(IndexRelationName, AccessShareLock); - ScanKeyEntryInitialize(&entry, 0, - Anum_pg_index_indrelid, - F_OIDEQ, - ObjectIdGetDatum(heapId)); - scan = systable_beginscan(indexRelation, IndexIndrelidIndex, true, - SnapshotNow, 1, &entry); - - while (HeapTupleIsValid(indexTuple = systable_getnext(scan))) - { - Form_pg_index indexform = (Form_pg_index) GETSTRUCT(indexTuple); - Oid indexId; - IndexInfo *indexInfo; - Relation heapRelation, - currentIndex; + Relation heapRelation; + List *indlist; - /* - * For each index, fetch info needed for index_build - */ - indexId = indexform->indexrelid; - indexInfo = BuildIndexInfo(indexform); + /* + * Open the heap rel. We need grab no lock because we assume + * heap_truncate is holding an exclusive lock on the heap rel. + */ + heapRelation = heap_open(heapId, NoLock); - /* - * We have to re-open the heap rel each time through this loop - * because index_build will close it again. We need grab no lock, - * however, because we assume heap_truncate is holding an - * exclusive lock on the heap rel. - */ - heapRelation = heap_open(heapId, NoLock); + /* Ask the relcache to produce a list of the indexes of the rel */ + foreach(indlist, RelationGetIndexList(heapRelation)) + { + Oid indexId = lfirsto(indlist); + Relation currentIndex; + IndexInfo *indexInfo; /* Open the index relation */ currentIndex = index_open(indexId); @@ -1927,6 +1907,9 @@ RelationTruncateIndexes(Oid heapId) /* Obtain exclusive lock on it, just to be sure */ LockRelation(currentIndex, AccessExclusiveLock); + /* Fetch info needed for index_build */ + indexInfo = BuildIndexInfo(currentIndex); + /* * Drop any buffers associated with this index. If they're dirty, * they're just dropped without bothering to flush to disk. @@ -1943,13 +1926,14 @@ RelationTruncateIndexes(Oid heapId) /* * index_build will close both the heap and index relations (but - * not give up the locks we hold on them). + * not give up the locks we hold on them). We're done with this + * index, but we must re-open the heap rel. */ + heapRelation = heap_open(heapId, NoLock); } - /* Complete the scan and close pg_index */ - systable_endscan(scan); - heap_close(indexRelation, AccessShareLock); + /* Finish by closing the heap rel again */ + heap_close(heapRelation, NoLock); } /* diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 3533b24ca78..1399f960f71 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.209 2003/02/19 04:06:28 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.210 2003/05/28 16:03:56 tgl Exp $ * * * INTERFACE ROUTINES @@ -42,6 +42,7 @@ #include "miscadmin.h" #include "optimizer/clauses.h" #include "optimizer/prep.h" +#include "parser/parse_expr.h" #include "parser/parse_func.h" #include "storage/sinval.h" #include "storage/smgr.h" @@ -63,11 +64,9 @@ ((natts) * AVG_ATTR_SIZE + MAXALIGN(sizeof(HeapTupleHeaderData)))) /* non-export function prototypes */ -static TupleDesc BuildFuncTupleDesc(Oid funcOid, - Oid *classObjectId); static TupleDesc ConstructTupleDescriptor(Relation heapRelation, - int numatts, AttrNumber *attNums, - Oid *classObjectId); + IndexInfo *indexInfo, + Oid *classObjectId); static void UpdateRelationRelation(Relation indexRelation); static void InitializeAttributeOids(Relation indexRelation, int numatts, Oid indexoid); @@ -98,95 +97,18 @@ IsReindexProcessing(void) return reindexing; } -static TupleDesc -BuildFuncTupleDesc(Oid funcOid, - Oid *classObjectId) -{ - TupleDesc funcTupDesc; - HeapTuple tuple; - Oid keyType; - Oid retType; - Form_pg_type typeTup; - - /* - * Allocate and zero a tuple descriptor for a one-column tuple. - */ - funcTupDesc = CreateTemplateTupleDesc(1, false); - funcTupDesc->attrs[0] = (Form_pg_attribute) palloc0(ATTRIBUTE_TUPLE_SIZE); - - /* - * Lookup the function to get its name and return type. - */ - tuple = SearchSysCache(PROCOID, - ObjectIdGetDatum(funcOid), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "Function %u does not exist", funcOid); - retType = ((Form_pg_proc) GETSTRUCT(tuple))->prorettype; - - /* - * make the attributes name the same as the functions - */ - namestrcpy(&funcTupDesc->attrs[0]->attname, - NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname)); - - ReleaseSysCache(tuple); - - /* - * Check the opclass to see if it provides a keytype (overriding the - * function result type). - */ - tuple = SearchSysCache(CLAOID, - ObjectIdGetDatum(classObjectId[0]), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "Opclass %u does not exist", classObjectId[0]); - keyType = ((Form_pg_opclass) GETSTRUCT(tuple))->opckeytype; - ReleaseSysCache(tuple); - - if (!OidIsValid(keyType)) - keyType = retType; - - /* - * Lookup the key type in pg_type for the type length etc. - */ - tuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(keyType), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "Type %u does not exist", keyType); - typeTup = (Form_pg_type) GETSTRUCT(tuple); - - /* - * Assign some of the attributes values. Leave the rest as 0. - */ - funcTupDesc->attrs[0]->attnum = 1; - funcTupDesc->attrs[0]->atttypid = keyType; - funcTupDesc->attrs[0]->attlen = typeTup->typlen; - funcTupDesc->attrs[0]->attbyval = typeTup->typbyval; - funcTupDesc->attrs[0]->attstorage = typeTup->typstorage; - funcTupDesc->attrs[0]->attalign = typeTup->typalign; - funcTupDesc->attrs[0]->attcacheoff = -1; - funcTupDesc->attrs[0]->atttypmod = -1; - funcTupDesc->attrs[0]->attislocal = true; - - ReleaseSysCache(tuple); - - return funcTupDesc; -} - -/* ---------------------------------------------------------------- +/* * ConstructTupleDescriptor * - * Build an index tuple descriptor for a new index (plain not functional) - * ---------------------------------------------------------------- + * Build an index tuple descriptor for a new index */ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, - int numatts, - AttrNumber *attNums, + IndexInfo *indexInfo, Oid *classObjectId) { + int numatts = indexInfo->ii_NumIndexAttrs; + List *indexprs = indexInfo->ii_Expressions; TupleDesc heapTupDesc; TupleDesc indexTupDesc; int natts; /* #atts in heap rel --- for error checks */ @@ -198,69 +120,109 @@ ConstructTupleDescriptor(Relation heapRelation, /* * allocate the new tuple descriptor */ - indexTupDesc = CreateTemplateTupleDesc(numatts, false); - /* ---------------- - * for each attribute we are indexing, obtain its attribute - * tuple form from either the static table of system attribute - * tuple forms or the relation tuple descriptor - * ---------------- + /* + * For simple index columns, we copy the pg_attribute row from the + * parent relation and modify it as necessary. For expressions we + * have to cons up a pg_attribute row the hard way. */ for (i = 0; i < numatts; i++) { - AttrNumber atnum; /* attributeNumber[attributeOffset] */ - Form_pg_attribute from; + AttrNumber atnum = indexInfo->ii_KeyAttrNumbers[i]; Form_pg_attribute to; HeapTuple tuple; + Form_pg_type typeTup; Oid keyType; - /* - * get the attribute number and make sure it's valid; determine - * which attribute descriptor to copy - */ - atnum = attNums[i]; + indexTupDesc->attrs[i] = to = + (Form_pg_attribute) palloc0(ATTRIBUTE_TUPLE_SIZE); - if (!AttrNumberIsForUserDefinedAttr(atnum)) + if (atnum != 0) { + /* Simple index column */ + Form_pg_attribute from; + + if (atnum < 0) + { + /* + * here we are indexing on a system attribute (-1...-n) + */ + from = SystemAttributeDefinition(atnum, + heapRelation->rd_rel->relhasoids); + } + else + { + /* + * here we are indexing on a normal attribute (1...n) + */ + if (atnum > natts) + elog(ERROR, "cannot create index: column %d does not exist", + atnum); + from = heapTupDesc->attrs[AttrNumberGetAttrOffset(atnum)]; + } + + /* + * now that we've determined the "from", let's copy the tuple desc + * data... + */ + memcpy(to, from, ATTRIBUTE_TUPLE_SIZE); + /* - * here we are indexing on a system attribute (-1...-n) + * Fix the stuff that should not be the same as the underlying + * attr */ - from = SystemAttributeDefinition(atnum, - heapRelation->rd_rel->relhasoids); + to->attnum = i + 1; + + to->attstattarget = 0; + to->attcacheoff = -1; + to->attnotnull = false; + to->atthasdef = false; + to->attislocal = true; + to->attinhcount = 0; } else { + /* Expressional index */ + Node *indexkey; + + if (indexprs == NIL) + elog(ERROR, "too few entries in indexprs list"); + indexkey = (Node *) lfirst(indexprs); + indexprs = lnext(indexprs); + /* - * here we are indexing on a normal attribute (1...n) + * Make the attribute's name "pg_expresssion_nnn" (maybe think + * of something better later) */ - if (atnum > natts) - elog(ERROR, "cannot create index: column %d does not exist", - atnum); + sprintf(NameStr(to->attname), "pg_expression_%d", i + 1); - from = heapTupDesc->attrs[AttrNumberGetAttrOffset(atnum)]; - } - - /* - * now that we've determined the "from", let's copy the tuple desc - * data... - */ - indexTupDesc->attrs[i] = to = - (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE); - memcpy(to, from, ATTRIBUTE_TUPLE_SIZE); + /* + * Lookup the expression type in pg_type for the type length etc. + */ + keyType = exprType(indexkey); + tuple = SearchSysCache(TYPEOID, + ObjectIdGetDatum(keyType), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "Type %u does not exist", keyType); + typeTup = (Form_pg_type) GETSTRUCT(tuple); - /* - * Fix the stuff that should not be the same as the underlying - * attr - */ - to->attnum = i + 1; + /* + * Assign some of the attributes values. Leave the rest as 0. + */ + to->attnum = i + 1; + to->atttypid = keyType; + to->attlen = typeTup->typlen; + to->attbyval = typeTup->typbyval; + to->attstorage = typeTup->typstorage; + to->attalign = typeTup->typalign; + to->attcacheoff = -1; + to->atttypmod = -1; + to->attislocal = true; - to->attstattarget = 0; - to->attcacheoff = -1; - to->attnotnull = false; - to->atthasdef = false; - to->attislocal = true; - to->attinhcount = 0; + ReleaseSysCache(tuple); + } /* * We do not yet have the correct relation OID for the index, so @@ -284,8 +246,6 @@ ConstructTupleDescriptor(Relation heapRelation, if (OidIsValid(keyType) && keyType != to->atttypid) { /* index value and heap value have different types */ - Form_pg_type typeTup; - tuple = SearchSysCache(TYPEOID, ObjectIdGetDatum(keyType), 0, 0, 0); @@ -421,6 +381,7 @@ UpdateIndexRelation(Oid indexoid, { int16 indkey[INDEX_MAX_KEYS]; Oid indclass[INDEX_MAX_KEYS]; + Datum exprsDatum; Datum predDatum; Datum values[Natts_pg_index]; char nulls[Natts_pg_index]; @@ -430,20 +391,32 @@ UpdateIndexRelation(Oid indexoid, /* * Copy the index key and opclass info into zero-filled vectors - * - * (zero filling is essential 'cause we don't store the number of - * index columns explicitly in pg_index, which is pretty grotty...) */ MemSet(indkey, 0, sizeof(indkey)); - for (i = 0; i < indexInfo->ii_NumKeyAttrs; i++) - indkey[i] = indexInfo->ii_KeyAttrNumbers[i]; - MemSet(indclass, 0, sizeof(indclass)); for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) + { + indkey[i] = indexInfo->ii_KeyAttrNumbers[i]; indclass[i] = classOids[i]; + } + + /* + * Convert the index expressions (if any) to a text datum + */ + if (indexInfo->ii_Expressions != NIL) + { + char *exprsString; + + exprsString = nodeToString(indexInfo->ii_Expressions); + exprsDatum = DirectFunctionCall1(textin, + CStringGetDatum(exprsString)); + pfree(exprsString); + } + else + exprsDatum = (Datum) 0; /* - * Convert the index-predicate (if any) to a text datum + * Convert the index predicate (if any) to a text datum */ if (indexInfo->ii_Predicate != NIL) { @@ -455,8 +428,7 @@ UpdateIndexRelation(Oid indexoid, pfree(predString); } else - predDatum = DirectFunctionCall1(textin, - CStringGetDatum("")); + predDatum = (Datum) 0; /* * open the system catalog index relation @@ -470,14 +442,18 @@ UpdateIndexRelation(Oid indexoid, values[Anum_pg_index_indexrelid - 1] = ObjectIdGetDatum(indexoid); values[Anum_pg_index_indrelid - 1] = ObjectIdGetDatum(heapoid); - values[Anum_pg_index_indproc - 1] = ObjectIdGetDatum(indexInfo->ii_FuncOid); values[Anum_pg_index_indkey - 1] = PointerGetDatum(indkey); values[Anum_pg_index_indclass - 1] = PointerGetDatum(indclass); - values[Anum_pg_index_indisclustered - 1] = BoolGetDatum(false); + values[Anum_pg_index_indnatts - 1] = Int16GetDatum(indexInfo->ii_NumIndexAttrs); values[Anum_pg_index_indisunique - 1] = BoolGetDatum(indexInfo->ii_Unique); values[Anum_pg_index_indisprimary - 1] = BoolGetDatum(primary); - values[Anum_pg_index_indreference - 1] = ObjectIdGetDatum(InvalidOid); + values[Anum_pg_index_indisclustered - 1] = BoolGetDatum(false); + values[Anum_pg_index_indexprs - 1] = exprsDatum; + if (exprsDatum == (Datum) 0) + nulls[Anum_pg_index_indexprs - 1] = 'n'; values[Anum_pg_index_indpred - 1] = predDatum; + if (predDatum == (Datum) 0) + nulls[Anum_pg_index_indpred - 1] = 'n'; tuple = heap_formtuple(RelationGetDescr(pg_index), values, nulls); @@ -538,8 +514,7 @@ index_create(Oid heapRelationId, /* * check parameters */ - if (indexInfo->ii_NumIndexAttrs < 1 || - indexInfo->ii_NumKeyAttrs < 1) + if (indexInfo->ii_NumIndexAttrs < 1) elog(ERROR, "must index at least one column"); if (!allow_system_table_mods && @@ -564,16 +539,9 @@ index_create(Oid heapRelationId, /* * construct tuple descriptor for index tuples */ - if (OidIsValid(indexInfo->ii_FuncOid)) - indexTupDesc = BuildFuncTupleDesc(indexInfo->ii_FuncOid, - classObjectId); - else - indexTupDesc = ConstructTupleDescriptor(heapRelation, - indexInfo->ii_NumKeyAttrs, - indexInfo->ii_KeyAttrNumbers, - classObjectId); - - indexTupDesc->tdhasoid = false; + indexTupDesc = ConstructTupleDescriptor(heapRelation, + indexInfo, + classObjectId); /* * create the index relation's relcache entry and physical disk file. @@ -675,6 +643,10 @@ index_create(Oid heapRelationId, constraintType = 0; /* keep compiler quiet */ } + /* Shouldn't have any expressions */ + if (indexInfo->ii_Expressions) + elog(ERROR, "constraints can't have index expressions"); + conOid = CreateConstraintEntry(indexRelationName, namespaceId, constraintType, @@ -682,7 +654,7 @@ index_create(Oid heapRelationId, false, /* isDeferred */ heapRelationId, indexInfo->ii_KeyAttrNumbers, - indexInfo->ii_NumKeyAttrs, + indexInfo->ii_NumIndexAttrs, InvalidOid, /* no domain */ InvalidOid, /* no foreign key */ NULL, @@ -703,13 +675,17 @@ index_create(Oid heapRelationId, } else { - for (i = 0; i < indexInfo->ii_NumKeyAttrs; i++) + /* Create auto dependencies on simply-referenced columns */ + for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) { - referenced.classId = RelOid_pg_class; - referenced.objectId = heapRelationId; - referenced.objectSubId = indexInfo->ii_KeyAttrNumbers[i]; - - recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + if (indexInfo->ii_KeyAttrNumbers[i] != 0) + { + referenced.classId = RelOid_pg_class; + referenced.objectId = heapRelationId; + referenced.objectSubId = indexInfo->ii_KeyAttrNumbers[i]; + + recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); + } } } @@ -723,14 +699,24 @@ index_create(Oid heapRelationId, recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } - /* Store the dependency on the function (if appropriate) */ - if (OidIsValid(indexInfo->ii_FuncOid)) + /* Store dependencies on anything mentioned in index expressions */ + if (indexInfo->ii_Expressions) { - referenced.classId = RelOid_pg_proc; - referenced.objectId = indexInfo->ii_FuncOid; - referenced.objectSubId = 0; + recordDependencyOnSingleRelExpr(&myself, + (Node *) indexInfo->ii_Expressions, + heapRelationId, + DEPENDENCY_NORMAL, + DEPENDENCY_AUTO); + } - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + /* Store dependencies on anything mentioned in predicate */ + if (indexInfo->ii_Predicate) + { + recordDependencyOnSingleRelExpr(&myself, + (Node *) indexInfo->ii_Predicate, + heapRelationId, + DEPENDENCY_NORMAL, + DEPENDENCY_AUTO); } } @@ -864,7 +850,7 @@ index_drop(Oid indexId) /* ---------------- * BuildIndexInfo - * Construct an IndexInfo record given the index's pg_index tuple + * Construct an IndexInfo record for an open index * * IndexInfo stores the information about the index that's needed by * FormIndexDatum, which is used for both index_build() and later insertion @@ -873,62 +859,31 @@ index_drop(Oid indexId) * ---------------- */ IndexInfo * -BuildIndexInfo(Form_pg_index indexStruct) +BuildIndexInfo(Relation index) { IndexInfo *ii = makeNode(IndexInfo); + Form_pg_index indexStruct = index->rd_index; int i; int numKeys; - /* - * count the number of keys, and copy them into the IndexInfo - */ - numKeys = 0; - for (i = 0; i < INDEX_MAX_KEYS && - indexStruct->indkey[i] != InvalidAttrNumber; i++) - { + /* check the number of keys, and copy attr numbers into the IndexInfo */ + numKeys = indexStruct->indnatts; + if (numKeys < 1 || numKeys > INDEX_MAX_KEYS) + elog(ERROR, "invalid indnatts %d for index %u", + numKeys, RelationGetRelid(index)); + ii->ii_NumIndexAttrs = numKeys; + for (i = 0; i < numKeys; i++) ii->ii_KeyAttrNumbers[i] = indexStruct->indkey[i]; - numKeys++; - } - ii->ii_NumKeyAttrs = numKeys; - /* - * Handle functional index. - * - * If we have a functional index then the number of attributes defined in - * the index must be 1 (the function's single return value). Otherwise - * it's same as number of keys. - */ - ii->ii_FuncOid = indexStruct->indproc; + /* fetch any expressions needed for expressional indexes */ + ii->ii_Expressions = RelationGetIndexExpressions(index); + ii->ii_ExpressionsState = NIL; - if (OidIsValid(indexStruct->indproc)) - { - ii->ii_NumIndexAttrs = 1; - /* Do a lookup on the function, too */ - fmgr_info(indexStruct->indproc, &ii->ii_FuncInfo); - } - else - ii->ii_NumIndexAttrs = numKeys; - - /* - * If partial index, convert predicate into expression nodetree - */ - if (VARSIZE(&indexStruct->indpred) > VARHDRSZ) - { - char *predString; - - predString = DatumGetCString(DirectFunctionCall1(textout, - PointerGetDatum(&indexStruct->indpred))); - ii->ii_Predicate = stringToNode(predString); - ii->ii_PredicateState = NIL; - pfree(predString); - } - else - { - ii->ii_Predicate = NIL; - ii->ii_PredicateState = NIL; - } + /* fetch index predicate if any */ + ii->ii_Predicate = RelationGetIndexPredicate(index); + ii->ii_PredicateState = NIL; - /* Other info */ + /* other info */ ii->ii_Unique = indexStruct->indisunique; return ii; @@ -941,10 +896,14 @@ BuildIndexInfo(Form_pg_index indexStruct) * indexInfo Info about the index * heapTuple Heap tuple for which we must prepare an index entry * heapDescriptor tupledesc for heap tuple - * resultCxt Temporary memory context for any palloc'd datums created + * estate executor state for evaluating any index expressions * datum Array of index Datums (output area) * nullv Array of is-null indicators (output area) * + * When there are no index expressions, estate may be NULL. Otherwise it + * must be supplied, *and* the ecxt_scantuple slot of its per-tuple expr + * context must point to the heap tuple passed in. + * * For largely historical reasons, we don't actually call index_formtuple() * here, we just prepare its input arrays datum[] and nullv[]. * ---------------- @@ -953,69 +912,58 @@ void FormIndexDatum(IndexInfo *indexInfo, HeapTuple heapTuple, TupleDesc heapDescriptor, - MemoryContext resultCxt, + EState *estate, Datum *datum, char *nullv) { - MemoryContext oldContext; + List *indexprs; int i; - Datum iDatum; - bool isNull; - - oldContext = MemoryContextSwitchTo(resultCxt); - if (OidIsValid(indexInfo->ii_FuncOid)) + if (indexInfo->ii_Expressions != NIL && + indexInfo->ii_ExpressionsState == NIL) { - /* - * Functional index --- compute the single index attribute - */ - FunctionCallInfoData fcinfo; - bool anynull = false; + /* First time through, set up expression evaluation state */ + indexInfo->ii_ExpressionsState = (List *) + ExecPrepareExpr((Expr *) indexInfo->ii_Expressions, + estate); + /* Check caller has set up context correctly */ + Assert(GetPerTupleExprContext(estate)->ecxt_scantuple->val == heapTuple); + } + indexprs = indexInfo->ii_ExpressionsState; - MemSet(&fcinfo, 0, sizeof(fcinfo)); - fcinfo.flinfo = &indexInfo->ii_FuncInfo; - fcinfo.nargs = indexInfo->ii_NumKeyAttrs; + for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) + { + int keycol = indexInfo->ii_KeyAttrNumbers[i]; + Datum iDatum; + bool isNull; - for (i = 0; i < indexInfo->ii_NumKeyAttrs; i++) - { - fcinfo.arg[i] = heap_getattr(heapTuple, - indexInfo->ii_KeyAttrNumbers[i], - heapDescriptor, - &fcinfo.argnull[i]); - anynull |= fcinfo.argnull[i]; - } - if (indexInfo->ii_FuncInfo.fn_strict && anynull) + if (keycol != 0) { - /* force a null result for strict function */ - iDatum = (Datum) 0; - isNull = true; + /* + * Plain index column; get the value we need directly from the + * heap tuple. + */ + iDatum = heap_getattr(heapTuple, keycol, heapDescriptor, &isNull); } else { - iDatum = FunctionCallInvoke(&fcinfo); - isNull = fcinfo.isnull; - } - datum[0] = iDatum; - nullv[0] = (isNull) ? 'n' : ' '; - } - else - { - /* - * Plain index --- for each attribute we need from the heap tuple, - * get the attribute and stick it into the datum and nullv arrays. - */ - for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) - { - iDatum = heap_getattr(heapTuple, - indexInfo->ii_KeyAttrNumbers[i], - heapDescriptor, - &isNull); - datum[i] = iDatum; - nullv[i] = (isNull) ? 'n' : ' '; + /* + * Index expression --- need to evaluate it. + */ + if (indexprs == NIL) + elog(ERROR, "wrong number of index expressions"); + iDatum = ExecEvalExprSwitchContext((ExprState *) lfirst(indexprs), + GetPerTupleExprContext(estate), + &isNull, + NULL); + indexprs = lnext(indexprs); } + datum[i] = iDatum; + nullv[i] = (isNull) ? 'n' : ' '; } - MemoryContextSwitchTo(oldContext); + if (indexprs != NIL) + elog(ERROR, "wrong number of index expressions"); } @@ -1501,7 +1449,7 @@ IndexBuildHeapScan(Relation heapRelation, heapDescriptor = RelationGetDescr(heapRelation); /* - * Need an EState for evaluation of functional-index functions + * Need an EState for evaluation of index expressions * and partial-index predicates. */ estate = CreateExecutorState(); @@ -1510,9 +1458,9 @@ IndexBuildHeapScan(Relation heapRelation, /* * If this is a predicate (partial) index, we will need to evaluate * the predicate using ExecQual, which requires the current tuple to - * be in a slot of a TupleTable. + * be in a slot of a TupleTable. Likewise if there are any expressions. */ - if (indexInfo->ii_Predicate != NIL) + if (indexInfo->ii_Predicate != NIL || indexInfo->ii_Expressions != NIL) { tupleTable = ExecCreateTupleTable(1); slot = ExecAllocTableSlot(tupleTable); @@ -1521,11 +1469,7 @@ IndexBuildHeapScan(Relation heapRelation, /* Arrange for econtext's scan tuple to be the tuple under test */ econtext->ecxt_scantuple = slot; - /* - * Set up execution state for predicate. Note: we mustn't attempt to - * cache this in the indexInfo, since we're building it in a transient - * EState. - */ + /* Set up execution state for predicate. */ predicate = (List *) ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, estate); @@ -1676,12 +1620,12 @@ IndexBuildHeapScan(Relation heapRelation, /* * For the current heap tuple, extract all the attributes we use * in this index, and note which are null. This also performs - * evaluation of the function, if this is a functional index. + * evaluation of any expressions needed. */ FormIndexDatum(indexInfo, heapTuple, heapDescriptor, - econtext->ecxt_per_tuple_memory, + estate, attdata, nulls); @@ -1703,6 +1647,10 @@ IndexBuildHeapScan(Relation heapRelation, FreeExecutorState(estate); + /* These may have been pointing to the now-gone estate */ + indexInfo->ii_ExpressionsState = NIL; + indexInfo->ii_PredicateState = NIL; + return reltuples; } @@ -1807,7 +1755,7 @@ reindex_index(Oid indexId, bool force, bool inplace) } /* Fetch info needed for index_build */ - indexInfo = BuildIndexInfo(iRel->rd_index); + indexInfo = BuildIndexInfo(iRel); if (inplace) { diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c index 27d66b4b2e8..45a60b0e856 100644 --- a/src/backend/catalog/indexing.c +++ b/src/backend/catalog/indexing.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.102 2002/09/04 20:31:14 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.103 2003/05/28 16:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -30,11 +30,9 @@ * In the current implementation, we share code for opening/closing the * indexes with execUtils.c. But we do not use ExecInsertIndexTuples, * because we don't want to create an EState. This implies that we - * do not support partial indexes on system catalogs. Nor do we handle - * functional indexes very well (the code will work, but will leak memory - * intraquery, because the index function is called in the per-query context - * that we are invoked in). This could be fixed with localized changes here - * if we wanted to pay the extra overhead of building an EState. + * do not support partial or expressional indexes on system catalogs. + * This could be fixed with localized changes here if we wanted to pay + * the extra overhead of building an EState. */ CatalogIndexState CatalogOpenIndexes(Relation heapRel) @@ -99,7 +97,11 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) indexInfo = indexInfoArray[i]; - /* Partial indexes on system catalogs are not supported */ + /* + * Expressional and partial indexes on system catalogs are not + * supported + */ + Assert(indexInfo->ii_Expressions == NIL); Assert(indexInfo->ii_Predicate == NIL); /* @@ -109,7 +111,7 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) FormIndexDatum(indexInfo, heapTuple, heapDescriptor, - CurrentMemoryContext, + NULL, /* no expression eval to do */ datum, nullv); diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index e724034969c..3822059dd35 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.12 2002/12/12 20:35:11 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.13 2003/05/28 16:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -250,18 +250,11 @@ CreateConstraintEntry(const char *constraintName, { /* * Register dependencies from constraint to objects mentioned in - * CHECK expression. We gin up a rather bogus rangetable list to - * handle any Vars in the constraint. + * CHECK expression. */ - RangeTblEntry rte; - - MemSet(&rte, 0, sizeof(rte)); - rte.type = T_RangeTblEntry; - rte.rtekind = RTE_RELATION; - rte.relid = relId; - - recordDependencyOnExpr(&conobject, conExpr, makeList1(&rte), - DEPENDENCY_NORMAL); + recordDependencyOnSingleRelExpr(&conobject, conExpr, relId, + DEPENDENCY_NORMAL, + DEPENDENCY_NORMAL); } return conOid; diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 09ef0ac598e..87062782456 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.109 2003/05/14 03:26:01 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.110 2003/05/28 16:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -214,7 +214,7 @@ cluster(ClusterStmt *stmt) /* Start a new transaction for each relation. */ StartTransactionCommand(); - SetQuerySnapshot(); /* might be needed for functional index */ + SetQuerySnapshot(); /* might be needed for functions in indexes */ cluster_rel(rvtc, true); CommitTransactionCommand(); } @@ -320,7 +320,7 @@ cluster_rel(RelToCluster *rvtc, bool recheck) * seqscan pass over the table to copy the missing rows, but that seems * expensive and tedious. */ - if (VARSIZE(&OldIndex->rd_index->indpred) > VARHDRSZ) /* partial? */ + if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred)) elog(ERROR, "CLUSTER: cannot cluster on partial index"); if (!OldIndex->rd_am->amindexnulls) { @@ -332,14 +332,24 @@ cluster_rel(RelToCluster *rvtc, bool recheck) * at the first column; multicolumn-capable AMs are *required* to * index nulls in columns after the first. */ - if (OidIsValid(OldIndex->rd_index->indproc)) - elog(ERROR, "CLUSTER: cannot cluster on functional index when index access method does not handle nulls"); colno = OldIndex->rd_index->indkey[0]; - if (colno > 0) /* system columns are non-null */ + if (colno > 0) + { + /* ordinary user attribute */ if (!OldHeap->rd_att->attrs[colno - 1]->attnotnull) elog(ERROR, "CLUSTER: cannot cluster when index access method does not handle nulls" "\n\tYou may be able to work around this by marking column \"%s\" NOT NULL", NameStr(OldHeap->rd_att->attrs[colno - 1]->attname)); + } + else if (colno < 0) + { + /* system column --- okay, always non-null */ + } + else + { + /* index expression, lose... */ + elog(ERROR, "CLUSTER: cannot cluster on expressional index when index access method does not handle nulls"); + } } /* @@ -557,43 +567,24 @@ get_indexattr_list(Relation OldHeap, Oid OldIndex) foreach(indlist, RelationGetIndexList(OldHeap)) { Oid indexOID = lfirsto(indlist); - HeapTuple indexTuple; - HeapTuple classTuple; - Form_pg_index indexForm; - Form_pg_class classForm; + Relation oldIndex; IndexAttrs *attrs; - indexTuple = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(indexOID), - 0, 0, 0); - if (!HeapTupleIsValid(indexTuple)) - elog(ERROR, "Cache lookup failed for index %u", indexOID); - indexForm = (Form_pg_index) GETSTRUCT(indexTuple); - Assert(indexForm->indexrelid == indexOID); + oldIndex = index_open(indexOID); attrs = (IndexAttrs *) palloc(sizeof(IndexAttrs)); attrs->indexOID = indexOID; - attrs->indexInfo = BuildIndexInfo(indexForm); + attrs->indexName = pstrdup(NameStr(oldIndex->rd_rel->relname)); + attrs->accessMethodOID = oldIndex->rd_rel->relam; + attrs->indexInfo = BuildIndexInfo(oldIndex); attrs->classOID = (Oid *) palloc(sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs); - memcpy(attrs->classOID, indexForm->indclass, + memcpy(attrs->classOID, oldIndex->rd_index->indclass, sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs); /* We adjust the isclustered attribute to correct new state */ attrs->isclustered = (indexOID == OldIndex); - /* Name and access method of each index come from pg_class */ - classTuple = SearchSysCache(RELOID, - ObjectIdGetDatum(indexOID), - 0, 0, 0); - if (!HeapTupleIsValid(classTuple)) - elog(ERROR, "Cache lookup failed for index %u", indexOID); - classForm = (Form_pg_class) GETSTRUCT(classTuple); - - attrs->indexName = pstrdup(NameStr(classForm->relname)); - attrs->accessMethodOID = classForm->relam; - - ReleaseSysCache(classTuple); - ReleaseSysCache(indexTuple); + index_close(oldIndex); /* * Cons the gathered data into the list. We do not care about diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 6a371587368..4186a145795 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.99 2003/05/14 03:26:01 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.100 2003/05/28 16:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,6 +32,7 @@ #include "optimizer/prep.h" #include "parser/parsetree.h" #include "parser/parse_coerce.h" +#include "parser/parse_expr.h" #include "parser/parse_func.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -39,19 +40,13 @@ #include "utils/syscache.h" -#define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->funcname != NIL) - /* non-export function prototypes */ -static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid); -static void FuncIndexArgs(IndexInfo *indexInfo, Oid *classOidP, - IndexElem *funcIndex, - Oid relId, - char *accessMethodName, Oid accessMethodId); -static void NormIndexAttrs(IndexInfo *indexInfo, Oid *classOidP, +static void CheckPredicate(List *predList); +static void ComputeIndexAttrs(IndexInfo *indexInfo, Oid *classOidP, List *attList, Oid relId, char *accessMethodName, Oid accessMethodId); -static Oid GetAttrOpClass(IndexElem *attribute, Oid attrType, +static Oid GetIndexOpClass(List *opclass, Oid attrType, char *accessMethodName, Oid accessMethodId); static Oid GetDefaultOpClass(Oid attrType, Oid accessMethodId); @@ -59,8 +54,8 @@ static Oid GetDefaultOpClass(Oid attrType, Oid accessMethodId); * DefineIndex * Creates a new index. * - * 'attributeList' is a list of IndexElem specifying either a functional - * index or a list of attributes to index on. + * 'attributeList' is a list of IndexElem specifying columns and expressions + * to index on. * 'predicate' is the qual specified in the where clause. * 'rangetable' is needed to interpret the predicate. */ @@ -156,6 +151,16 @@ DefineIndex(RangeVar *heapRelation, ReleaseSysCache(tuple); /* + * If a range table was created then check that only the base rel is + * mentioned. + */ + if (rangetable != NIL) + { + if (length(rangetable) != 1 || getrelid(1, rangetable) != relationId) + elog(ERROR, "index expressions and predicates may refer only to the base relation"); + } + + /* * Convert the partial-index predicate from parsetree form to an * implicit-AND qual expression, for easier evaluation at runtime. * While we are at it, we reduce it to a canonical (CNF or DNF) form @@ -164,14 +169,14 @@ DefineIndex(RangeVar *heapRelation, if (predicate) { cnfPred = canonicalize_qual((Expr *) copyObject(predicate), true); - CheckPredicate(cnfPred, rangetable, relationId); + CheckPredicate(cnfPred); } /* * Check that all of the attributes in a primary key are marked * as not null, otherwise attempt to ALTER TABLE .. SET NOT NULL */ - if (primary && !IsFuncIndex(attributeList)) + if (primary) { List *keys; @@ -180,6 +185,9 @@ DefineIndex(RangeVar *heapRelation, IndexElem *key = (IndexElem *) lfirst(keys); HeapTuple atttuple; + if (!key->name) + elog(ERROR, "primary keys cannot be expressions"); + /* System attributes are never null, so no problem */ if (SystemAttributeByName(key->name, rel->rd_rel->relhasoids)) continue; @@ -216,43 +224,16 @@ DefineIndex(RangeVar *heapRelation, * structure */ indexInfo = makeNode(IndexInfo); + indexInfo->ii_NumIndexAttrs = numberOfAttributes; + indexInfo->ii_Expressions = NIL; /* for now */ + indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = cnfPred; indexInfo->ii_PredicateState = NIL; - indexInfo->ii_FuncOid = InvalidOid; indexInfo->ii_Unique = unique; - if (IsFuncIndex(attributeList)) - { - IndexElem *funcIndex = (IndexElem *) lfirst(attributeList); - int nargs; - - /* Parser should have given us only one list item, but check */ - if (numberOfAttributes != 1) - elog(ERROR, "Functional index can only have one attribute"); - - nargs = length(funcIndex->args); - if (nargs > INDEX_MAX_KEYS) - elog(ERROR, "Index function can take at most %d arguments", - INDEX_MAX_KEYS); - - indexInfo->ii_NumIndexAttrs = 1; - indexInfo->ii_NumKeyAttrs = nargs; - - classObjectId = (Oid *) palloc(sizeof(Oid)); - - FuncIndexArgs(indexInfo, classObjectId, funcIndex, + classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid)); + ComputeIndexAttrs(indexInfo, classObjectId, attributeList, relationId, accessMethodName, accessMethodId); - } - else - { - indexInfo->ii_NumIndexAttrs = numberOfAttributes; - indexInfo->ii_NumKeyAttrs = numberOfAttributes; - - classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid)); - - NormIndexAttrs(indexInfo, classObjectId, attributeList, - relationId, accessMethodName, accessMethodId); - } index_create(relationId, indexRelationName, indexInfo, accessMethodId, classObjectId, @@ -271,8 +252,7 @@ DefineIndex(RangeVar *heapRelation, /* * CheckPredicate - * Checks that the given list of partial-index predicates refer - * (via the given range table) only to the given base relation oid. + * Checks that the given list of partial-index predicates is valid. * * This used to also constrain the form of the predicate to forms that * indxpath.c could do something with. However, that seems overly @@ -281,14 +261,9 @@ DefineIndex(RangeVar *heapRelation, * any evaluatable predicate will work. So accept any predicate here * (except ones requiring a plan), and let indxpath.c fend for itself. */ - static void -CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid) +CheckPredicate(List *predList) { - if (length(rangeTable) != 1 || getrelid(1, rangeTable) != baseRelOid) - elog(ERROR, - "Partial-index predicates may refer only to the base relation"); - /* * We don't currently support generation of an actual query plan for a * predicate, only simple scalar expressions; hence these @@ -301,119 +276,19 @@ CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid) /* * A predicate using mutable functions is probably wrong, for the same - * reasons that we don't allow a functional index to use one. + * reasons that we don't allow an index expression to use one. */ if (contain_mutable_functions((Node *) predList)) elog(ERROR, "Functions in index predicate must be marked IMMUTABLE"); } - static void -FuncIndexArgs(IndexInfo *indexInfo, - Oid *classOidP, - IndexElem *funcIndex, - Oid relId, - char *accessMethodName, - Oid accessMethodId) -{ - Oid argTypes[FUNC_MAX_ARGS]; - List *arglist; - int nargs = 0; - int i; - FuncDetailCode fdresult; - Oid funcid; - Oid rettype; - bool retset; - Oid *true_typeids; - - /* - * process the function arguments, which are a list of T_String - * (someday ought to allow more general expressions?) - * - * Note caller already checked that list is not too long. - */ - MemSet(argTypes, 0, sizeof(argTypes)); - - foreach(arglist, funcIndex->args) - { - char *arg = strVal(lfirst(arglist)); - HeapTuple tuple; - Form_pg_attribute att; - - tuple = SearchSysCacheAttName(relId, arg); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "DefineIndex: column \"%s\" named in key does not exist", arg); - att = (Form_pg_attribute) GETSTRUCT(tuple); - indexInfo->ii_KeyAttrNumbers[nargs] = att->attnum; - argTypes[nargs] = att->atttypid; - ReleaseSysCache(tuple); - nargs++; - } - - /* - * Lookup the function procedure to get its OID and result type. - * - * We rely on parse_func.c to find the correct function in the possible - * presence of binary-compatible types. However, parse_func may do - * too much: it will accept a function that requires run-time coercion - * of input types, and the executor is not currently set up to support - * that. So, check to make sure that the selected function has - * exact-match or binary-compatible input types. - */ - fdresult = func_get_detail(funcIndex->funcname, funcIndex->args, - nargs, argTypes, - &funcid, &rettype, &retset, - &true_typeids); - if (fdresult != FUNCDETAIL_NORMAL) - { - if (fdresult == FUNCDETAIL_AGGREGATE) - elog(ERROR, "DefineIndex: functional index may not use an aggregate function"); - else if (fdresult == FUNCDETAIL_COERCION) - elog(ERROR, "DefineIndex: functional index must use a real function, not a type coercion" - "\n\tTry specifying the index opclass you want to use, instead"); - else - func_error("DefineIndex", funcIndex->funcname, nargs, argTypes, - NULL); - } - - if (retset) - elog(ERROR, "DefineIndex: cannot index on a function returning a set"); - - for (i = 0; i < nargs; i++) - { - if (!IsBinaryCoercible(argTypes[i], true_typeids[i])) - func_error("DefineIndex", funcIndex->funcname, nargs, true_typeids, - "Index function must be binary-compatible with table datatype"); - } - - /* - * Require that the function be marked immutable. Using a mutable - * function for a functional index is highly questionable, since if - * you aren't going to get the same result for the same data every - * time, it's not clear what the index entries mean at all. - */ - if (func_volatile(funcid) != PROVOLATILE_IMMUTABLE) - elog(ERROR, "DefineIndex: index function must be marked IMMUTABLE"); - - /* Process opclass, using func return type as default type */ - - classOidP[0] = GetAttrOpClass(funcIndex, rettype, - accessMethodName, accessMethodId); - - /* OK, return results */ - - indexInfo->ii_FuncOid = funcid; - /* Need to do the fmgr function lookup now, too */ - fmgr_info(funcid, &indexInfo->ii_FuncInfo); -} - -static void -NormIndexAttrs(IndexInfo *indexInfo, - Oid *classOidP, - List *attList, /* list of IndexElem's */ - Oid relId, - char *accessMethodName, - Oid accessMethodId) +ComputeIndexAttrs(IndexInfo *indexInfo, + Oid *classOidP, + List *attList, /* list of IndexElem's */ + Oid relId, + char *accessMethodName, + Oid accessMethodId) { List *rest; int attn = 0; @@ -424,31 +299,75 @@ NormIndexAttrs(IndexInfo *indexInfo, foreach(rest, attList) { IndexElem *attribute = (IndexElem *) lfirst(rest); - HeapTuple atttuple; - Form_pg_attribute attform; - - if (attribute->name == NULL) - elog(ERROR, "missing attribute for define index"); - - atttuple = SearchSysCacheAttName(relId, attribute->name); - if (!HeapTupleIsValid(atttuple)) - elog(ERROR, "DefineIndex: attribute \"%s\" not found", - attribute->name); - attform = (Form_pg_attribute) GETSTRUCT(atttuple); + Oid atttype; - indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum; + if (attribute->name != NULL) + { + /* Simple index attribute */ + HeapTuple atttuple; + Form_pg_attribute attform; + + Assert(attribute->expr == NULL); + atttuple = SearchSysCacheAttName(relId, attribute->name); + if (!HeapTupleIsValid(atttuple)) + elog(ERROR, "DefineIndex: attribute \"%s\" not found", + attribute->name); + attform = (Form_pg_attribute) GETSTRUCT(atttuple); + indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum; + atttype = attform->atttypid; + ReleaseSysCache(atttuple); + } + else if (attribute->expr && IsA(attribute->expr, Var)) + { + /* Tricky tricky, he wrote (column) ... treat as simple attr */ + Var *var = (Var *) attribute->expr; - classOidP[attn] = GetAttrOpClass(attribute, attform->atttypid, - accessMethodName, accessMethodId); + indexInfo->ii_KeyAttrNumbers[attn] = var->varattno; + atttype = get_atttype(relId, var->varattno); + } + else + { + /* Index expression */ + Assert(attribute->expr != NULL); + indexInfo->ii_KeyAttrNumbers[attn] = 0; /* marks expression */ + indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions, + attribute->expr); + atttype = exprType(attribute->expr); + + /* + * We don't currently support generation of an actual query plan + * for an index expression, only simple scalar expressions; + * hence these restrictions. + */ + if (contain_subplans(attribute->expr)) + elog(ERROR, "Cannot use subselect in index expression"); + if (contain_agg_clause(attribute->expr)) + elog(ERROR, "Cannot use aggregate in index expression"); + + /* + * A expression using mutable functions is probably wrong, + * since if you aren't going to get the same result for the same + * data every time, it's not clear what the index entries mean at + * all. + */ + if (contain_mutable_functions(attribute->expr)) + elog(ERROR, "Functions in index expression must be marked IMMUTABLE"); + } - ReleaseSysCache(atttuple); + classOidP[attn] = GetIndexOpClass(attribute->opclass, + atttype, + accessMethodName, + accessMethodId); attn++; } } +/* + * Resolve possibly-defaulted operator class specification + */ static Oid -GetAttrOpClass(IndexElem *attribute, Oid attrType, - char *accessMethodName, Oid accessMethodId) +GetIndexOpClass(List *opclass, Oid attrType, + char *accessMethodName, Oid accessMethodId) { char *schemaname; char *opcname; @@ -456,7 +375,32 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType, Oid opClassId, opInputType; - if (attribute->opclass == NIL) + /* + * Release 7.0 removed network_ops, timespan_ops, and + * datetime_ops, so we ignore those opclass names + * so the default *_ops is used. This can be + * removed in some later release. bjm 2000/02/07 + * + * Release 7.1 removes lztext_ops, so suppress that too + * for a while. tgl 2000/07/30 + * + * Release 7.2 renames timestamp_ops to timestamptz_ops, + * so suppress that too for awhile. I'm starting to + * think we need a better approach. tgl 2000/10/01 + */ + if (length(opclass) == 1) + { + char *claname = strVal(lfirst(opclass)); + + if (strcmp(claname, "network_ops") == 0 || + strcmp(claname, "timespan_ops") == 0 || + strcmp(claname, "datetime_ops") == 0 || + strcmp(claname, "lztext_ops") == 0 || + strcmp(claname, "timestamp_ops") == 0) + opclass = NIL; + } + + if (opclass == NIL) { /* no operator class specified, so find the default */ opClassId = GetDefaultOpClass(attrType, accessMethodId); @@ -473,7 +417,7 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType, */ /* deconstruct the name list */ - DeconstructQualifiedName(attribute->opclass, &schemaname, &opcname); + DeconstructQualifiedName(opclass, &schemaname, &opcname); if (schemaname) { @@ -501,7 +445,7 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType, if (!HeapTupleIsValid(tuple)) elog(ERROR, "DefineIndex: operator class \"%s\" not supported by access method \"%s\"", - NameListToString(attribute->opclass), accessMethodName); + NameListToString(opclass), accessMethodName); /* * Verify that the index operator class accepts this datatype. Note @@ -512,7 +456,7 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType, if (!IsBinaryCoercible(attrType, opInputType)) elog(ERROR, "operator class \"%s\" does not accept data type %s", - NameListToString(attribute->opclass), format_type_be(attrType)); + NameListToString(opclass), format_type_be(attrType)); ReleaseSysCache(tuple); @@ -773,7 +717,7 @@ ReindexDatabase(const char *dbname, bool force, bool all) for (i = 0; i < relcnt; i++) { StartTransactionCommand(); - SetQuerySnapshot(); /* might be needed for functional index */ + SetQuerySnapshot(); /* might be needed for functions in indexes */ if (reindex_relation(relids[i], force)) elog(NOTICE, "relation %u was reindexed", relids[i]); CommitTransactionCommand(); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 5f60ab9cf01..349fb8f3917 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.72 2003/04/29 22:13:08 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.73 2003/05/28 16:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1103,6 +1103,7 @@ renameatt(Oid myrelid, Relation attrelation; HeapTuple atttup; Form_pg_attribute attform; + int attnum; List *indexoidlist; List *indexoidscan; @@ -1178,7 +1179,8 @@ renameatt(Oid myrelid, oldattname); attform = (Form_pg_attribute) GETSTRUCT(atttup); - if (attform->attnum < 0) + attnum = attform->attnum; + if (attnum < 0) elog(ERROR, "renameatt: system attribute \"%s\" may not be renamed", oldattname); @@ -1217,43 +1219,48 @@ renameatt(Oid myrelid, { Oid indexoid = lfirsto(indexoidscan); HeapTuple indextup; + Form_pg_index indexform; + int i; /* - * First check to see if index is a functional index. If so, its - * column name is a function name and shouldn't be renamed here. + * Scan through index columns to see if there's any simple index + * entries for this attribute. We ignore expressional entries. */ indextup = SearchSysCache(INDEXRELID, ObjectIdGetDatum(indexoid), 0, 0, 0); if (!HeapTupleIsValid(indextup)) elog(ERROR, "renameatt: can't find index id %u", indexoid); - if (OidIsValid(((Form_pg_index) GETSTRUCT(indextup))->indproc)) - { - ReleaseSysCache(indextup); - continue; - } - ReleaseSysCache(indextup); + indexform = (Form_pg_index) GETSTRUCT(indextup); - /* - * Okay, look to see if any column name of the index matches the - * old attribute name. - */ - atttup = SearchSysCacheCopyAttName(indexoid, oldattname); - if (!HeapTupleIsValid(atttup)) - continue; /* Nope, so ignore it */ + for (i = 0; i < indexform->indnatts; i++) + { + if (attnum != indexform->indkey[i]) + continue; + /* + * Found one, rename it. + */ + atttup = SearchSysCacheCopy(ATTNUM, + ObjectIdGetDatum(indexoid), + Int16GetDatum(i + 1), + 0, 0); + if (!HeapTupleIsValid(atttup)) + continue; /* should we raise an error? */ + /* + * Update the (copied) attribute tuple. + */ + namestrcpy(&(((Form_pg_attribute) GETSTRUCT(atttup))->attname), + newattname); - /* - * Update the (copied) attribute tuple. - */ - namestrcpy(&(((Form_pg_attribute) GETSTRUCT(atttup))->attname), - newattname); + simple_heap_update(attrelation, &atttup->t_self, atttup); - simple_heap_update(attrelation, &atttup->t_self, atttup); + /* keep system catalog indexes current */ + CatalogUpdateIndexes(attrelation, atttup); - /* keep system catalog indexes current */ - CatalogUpdateIndexes(attrelation, atttup); + heap_freetuple(atttup); + } - heap_freetuple(atttup); + ReleaseSysCache(indextup); } freeList(indexoidlist); @@ -1986,8 +1993,7 @@ AlterTableAlterColumnDropNotNull(Oid myrelid, bool recurse, * Loop over each attribute in the primary key and see if it * matches the to-be-altered attribute */ - for (i = 0; i < INDEX_MAX_KEYS && - indexStruct->indkey[i] != InvalidAttrNumber; i++) + for (i = 0; i < indexStruct->indnatts; i++) { if (indexStruct->indkey[i] == attnum) elog(ERROR, "ALTER TABLE: Attribute \"%s\" is in a primary key", colName); @@ -3185,9 +3191,10 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, /* * Now build the list of PK attributes from the indkey definition + * (we assume a primary key cannot have expressional elements) */ *attnamelist = NIL; - for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++) + for (i = 0; i < indexStruct->indnatts; i++) { int pkattno = indexStruct->indkey[i]; @@ -3241,26 +3248,40 @@ transformFkeyCheckAttrs(Relation pkrel, indexStruct = (Form_pg_index) GETSTRUCT(indexTuple); /* - * Must be unique, not a functional index, and not a partial index + * Must have the right number of columns; must be unique and not a + * partial index; forget it if there are any expressions, too */ - if (indexStruct->indisunique && - indexStruct->indproc == InvalidOid && - VARSIZE(&indexStruct->indpred) <= VARHDRSZ) + if (indexStruct->indnatts == numattrs && + indexStruct->indisunique && + heap_attisnull(indexTuple, Anum_pg_index_indpred) && + heap_attisnull(indexTuple, Anum_pg_index_indexprs)) { - for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++) - ; - if (i == numattrs) + /* + * The given attnum list may match the index columns in any + * order. Check that each list is a subset of the other. + */ + for (i = 0; i < numattrs; i++) + { + found = false; + for (j = 0; j < numattrs; j++) + { + if (attnums[i] == indexStruct->indkey[j]) + { + found = true; + break; + } + } + if (!found) + break; + } + if (found) { - /* - * The given attnum list may match the index columns in any - * order. Check that each list is a subset of the other. - */ for (i = 0; i < numattrs; i++) { found = false; for (j = 0; j < numattrs; j++) { - if (attnums[i] == indexStruct->indkey[j]) + if (attnums[j] == indexStruct->indkey[i]) { found = true; break; @@ -3269,23 +3290,6 @@ transformFkeyCheckAttrs(Relation pkrel, if (!found) break; } - if (found) - { - for (i = 0; i < numattrs; i++) - { - found = false; - for (j = 0; j < numattrs; j++) - { - if (attnums[j] == indexStruct->indkey[i]) - { - found = true; - break; - } - } - if (!found) - break; - } - } } } ReleaseSysCache(indexTuple); @@ -4020,12 +4024,12 @@ AlterTableCreateToastTable(Oid relOid, bool silent) indexInfo = makeNode(IndexInfo); indexInfo->ii_NumIndexAttrs = 2; - indexInfo->ii_NumKeyAttrs = 2; indexInfo->ii_KeyAttrNumbers[0] = 1; indexInfo->ii_KeyAttrNumbers[1] = 2; + indexInfo->ii_Expressions = NIL; + indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; indexInfo->ii_PredicateState = NIL; - indexInfo->ii_FuncOid = InvalidOid; indexInfo->ii_Unique = true; classObjectId[0] = OID_BTREE_OPS_OID; diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index b5a8f7f3a17..93701b611ed 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -13,7 +13,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.254 2003/05/27 17:49:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.255 2003/05/28 16:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -304,7 +304,7 @@ vacuum(VacuumStmt *vacstmt) if (vacstmt->vacuum) { StartTransactionCommand(); - SetQuerySnapshot(); /* might be needed for functional index */ + SetQuerySnapshot(); /* might be needed for functions in indexes */ } else old_context = MemoryContextSwitchTo(anl_context); @@ -728,7 +728,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind) /* Begin a transaction for vacuuming this relation */ StartTransactionCommand(); - SetQuerySnapshot(); /* might be needed for functional index */ + SetQuerySnapshot(); /* might be needed for functions in indexes */ /* * Check for user-requested abort. Note we want this to be inside a @@ -3028,7 +3028,10 @@ vac_is_partial_index(Relation indrel) return true; /* Otherwise, look to see if there's a partial-index predicate */ - return (VARSIZE(&indrel->rd_index->indpred) > VARHDRSZ); + if (!heap_attisnull(indrel->rd_indextuple, Anum_pg_index_indpred)) + return true; + + return false; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index c06d951f91e..ca4ff192f44 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.99 2003/05/05 17:57:47 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.100 2003/05/28 16:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -706,10 +706,8 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo) if (!indexDesc->rd_am->amconcurrent) LockRelation(indexDesc, AccessExclusiveLock); - /* - * extract index key information from the index's pg_index tuple - */ - ii = BuildIndexInfo(indexDesc->rd_index); + /* extract index key information from the index's pg_index info */ + ii = BuildIndexInfo(indexDesc); relationDescs[i] = indexDesc; indexInfoArray[i] = ii; @@ -797,7 +795,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* * We will use the EState's per-tuple context for evaluating - * predicates and functional-index functions (creating it if it's not + * predicates and index expressions (creating it if it's not * already there). */ econtext = GetPerTupleExprContext(estate); @@ -844,11 +842,12 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* * FormIndexDatum fills in its datum and null parameters with * attribute information taken from the given heap tuple. + * It also computes any expressions needed. */ FormIndexDatum(indexInfo, heapTuple, heapDescriptor, - econtext->ecxt_per_tuple_memory, + estate, datum, nullv); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 1dc79a4dc40..4a5f858d7b0 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 - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.250 2003/05/06 00:20:32 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.251 2003/05/28 16:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1421,8 +1421,7 @@ _copyIndexElem(IndexElem *from) IndexElem *newnode = makeNode(IndexElem); COPY_STRING_FIELD(name); - COPY_NODE_FIELD(funcname); - COPY_NODE_FIELD(args); + COPY_NODE_FIELD(expr); COPY_NODE_FIELD(opclass); return newnode; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 6b9cac028b9..ff5400f6ee8 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 - * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.193 2003/05/06 00:20:32 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.194 2003/05/28 16:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1466,8 +1466,7 @@ static bool _equalIndexElem(IndexElem *a, IndexElem *b) { COMPARE_STRING_FIELD(name); - COMPARE_NODE_FIELD(funcname); - COMPARE_NODE_FIELD(args); + COMPARE_NODE_FIELD(expr); COMPARE_NODE_FIELD(opclass); return true; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 377c6bd2297..a1b238c93cf 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.205 2003/05/06 00:20:32 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.206 2003/05/28 16:03:56 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -1181,8 +1181,7 @@ _outIndexElem(StringInfo str, IndexElem *node) WRITE_NODE_TYPE("INDEXELEM"); WRITE_STRING_FIELD(name); - WRITE_NODE_FIELD(funcname); - WRITE_NODE_FIELD(args); + WRITE_NODE_FIELD(expr); WRITE_NODE_FIELD(opclass); } diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 454e9c92605..9319f8c1474 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.141 2003/05/27 17:49:46 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.142 2003/05/28 16:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -43,12 +43,8 @@ /* * DoneMatchingIndexKeys() - MACRO - * - * Formerly this looked at indexkeys, but that's the wrong thing for a - * functional index. */ -#define DoneMatchingIndexKeys(indexkeys, classes) \ - (classes[0] == InvalidOid) +#define DoneMatchingIndexKeys(classes) (classes[0] == InvalidOid) #define is_indexable_operator(clause,opclass,indexkey_on_left) \ (indexable_operator(clause,opclass,indexkey_on_left) != InvalidOid) @@ -67,14 +63,14 @@ static List *group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index, Relids outer_relids, bool isouterjoin); -static bool match_clause_to_indexkey(RelOptInfo *rel, IndexOptInfo *index, - int indexkey, Oid opclass, Expr *clause); -static bool match_join_clause_to_indexkey(RelOptInfo *rel, IndexOptInfo *index, - int indexkey, Oid opclass, Expr *clause); +static bool match_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index, + int indexcol, Oid opclass, Expr *clause); +static bool match_join_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index, + int indexcol, Oid opclass, Expr *clause); static Oid indexable_operator(Expr *clause, Oid opclass, bool indexkey_on_left); static bool pred_test(List *predicate_list, List *restrictinfo_list, - List *joininfo_list, int relvarno); + List *joininfo_list); static bool pred_test_restrict_list(Expr *predicate, List *restrictinfo_list); static bool pred_test_recurse_clause(Expr *predicate, Node *clause); static bool pred_test_recurse_pred(Expr *predicate, Node *clause); @@ -83,10 +79,8 @@ static Relids indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index); static Path *make_innerjoin_index_path(Query *root, RelOptInfo *rel, IndexOptInfo *index, List *clausegroups); -static bool match_index_to_operand(int indexkey, Node *operand, +static bool match_index_to_operand(Node *operand, int indexcol, RelOptInfo *rel, IndexOptInfo *index); -static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, - IndexOptInfo *index); static bool match_special_index_operator(Expr *clause, Oid opclass, bool indexkey_on_left); static List *expand_indexqual_condition(Expr *clause, Oid opclass); @@ -149,8 +143,7 @@ create_index_paths(Query *root, RelOptInfo *rel) * predicate test. */ if (index->indpred != NIL) - if (!pred_test(index->indpred, restrictinfo_list, joininfo_list, - rel->relid)) + if (!pred_test(index->indpred, restrictinfo_list, joininfo_list)) continue; /* @@ -371,7 +364,6 @@ match_or_subclause_to_indexkey(RelOptInfo *rel, IndexOptInfo *index, Expr *clause) { - int indexkey = index->indexkeys[0]; Oid opclass = index->classlist[0]; if (and_clause((Node *) clause)) @@ -380,14 +372,14 @@ match_or_subclause_to_indexkey(RelOptInfo *rel, foreach(item, ((BoolExpr *) clause)->args) { - if (match_clause_to_indexkey(rel, index, indexkey, opclass, + if (match_clause_to_indexcol(rel, index, 0, opclass, lfirst(item))) return true; } return false; } else - return match_clause_to_indexkey(rel, index, indexkey, opclass, + return match_clause_to_indexcol(rel, index, 0, opclass, clause); } @@ -426,7 +418,7 @@ extract_or_indexqual_conditions(RelOptInfo *rel, Expr *orsubclause) { List *quals = NIL; - int *indexkeys = index->indexkeys; + int indexcol = 0; Oid *classes = index->classlist; /* @@ -437,7 +429,6 @@ extract_or_indexqual_conditions(RelOptInfo *rel, */ do { - int curIndxKey = indexkeys[0]; Oid curClass = classes[0]; List *clausegroup = NIL; List *item; @@ -448,16 +439,16 @@ extract_or_indexqual_conditions(RelOptInfo *rel, { Expr *subsubclause = (Expr *) lfirst(item); - if (match_clause_to_indexkey(rel, index, - curIndxKey, curClass, + if (match_clause_to_indexcol(rel, index, + indexcol, curClass, subsubclause)) clausegroup = nconc(clausegroup, expand_indexqual_condition(subsubclause, curClass)); } } - else if (match_clause_to_indexkey(rel, index, - curIndxKey, curClass, + else if (match_clause_to_indexcol(rel, index, + indexcol, curClass, orsubclause)) clausegroup = expand_indexqual_condition(orsubclause, curClass); @@ -471,8 +462,8 @@ extract_or_indexqual_conditions(RelOptInfo *rel, { RestrictInfo *rinfo = (RestrictInfo *) lfirst(item); - if (match_clause_to_indexkey(rel, index, - curIndxKey, curClass, + if (match_clause_to_indexcol(rel, index, + indexcol, curClass, rinfo->clause)) clausegroup = nconc(clausegroup, expand_indexqual_condition(rinfo->clause, @@ -489,10 +480,10 @@ extract_or_indexqual_conditions(RelOptInfo *rel, quals = nconc(quals, clausegroup); - indexkeys++; + indexcol++; classes++; - } while (!DoneMatchingIndexKeys(indexkeys, classes)); + } while (!DoneMatchingIndexKeys(classes)); if (quals == NIL) elog(ERROR, "extract_or_indexqual_conditions: no matching clause"); @@ -531,7 +522,7 @@ group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index) { List *clausegroup_list = NIL; List *restrictinfo_list = rel->baserestrictinfo; - int *indexkeys = index->indexkeys; + int indexcol = 0; Oid *classes = index->classlist; if (restrictinfo_list == NIL) @@ -539,7 +530,6 @@ group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index) do { - int curIndxKey = indexkeys[0]; Oid curClass = classes[0]; List *clausegroup = NIL; List *i; @@ -548,9 +538,9 @@ group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(i); - if (match_clause_to_indexkey(rel, + if (match_clause_to_indexcol(rel, index, - curIndxKey, + indexcol, curClass, rinfo->clause)) clausegroup = lappend(clausegroup, rinfo); @@ -565,10 +555,10 @@ group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index) clausegroup_list = lappend(clausegroup_list, clausegroup); - indexkeys++; + indexcol++; classes++; - } while (!DoneMatchingIndexKeys(indexkeys, classes)); + } while (!DoneMatchingIndexKeys(classes)); return clausegroup_list; } @@ -592,12 +582,11 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index, { List *clausegroup_list = NIL; bool jfound = false; - int *indexkeys = index->indexkeys; + int indexcol = 0; Oid *classes = index->classlist; do { - int curIndxKey = indexkeys[0]; Oid curClass = classes[0]; List *clausegroup = NIL; List *i; @@ -619,9 +608,9 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index, if (isouterjoin && rinfo->ispusheddown) continue; - if (match_join_clause_to_indexkey(rel, + if (match_join_clause_to_indexcol(rel, index, - curIndxKey, + indexcol, curClass, rinfo->clause)) { @@ -640,9 +629,9 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index, if (isouterjoin && rinfo->ispusheddown) continue; - if (match_clause_to_indexkey(rel, + if (match_clause_to_indexcol(rel, index, - curIndxKey, + indexcol, curClass, rinfo->clause)) clausegroup = lappend(clausegroup, rinfo); @@ -657,10 +646,10 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index, clausegroup_list = lappend(clausegroup_list, clausegroup); - indexkeys++; + indexcol++; classes++; - } while (!DoneMatchingIndexKeys(indexkeys, classes)); + } while (!DoneMatchingIndexKeys(classes)); /* if no join clause was matched then forget it, per comments above */ if (!jfound) @@ -674,15 +663,15 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index, /* - * match_clause_to_indexkey() - * Determines whether a restriction clause matches a key of an index. + * match_clause_to_indexcol() + * Determines whether a restriction clause matches a column of an index. * * To match, the clause: * * (1) must be in the form (indexkey op const) or (const op indexkey); * and * (2) must contain an operator which is in the same class as the index - * operator for this key, or is a "special" operator as recognized + * operator for this column, or is a "special" operator as recognized * by match_special_index_operator(). * * Presently, the executor can only deal with indexquals that have the @@ -693,7 +682,7 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index, * * 'rel' is the relation of interest. * 'index' is an index on 'rel'. - * 'indexkey' is a key of 'index'. + * 'indexcol' is a column number of 'index' (counting from 0). * 'opclass' is the corresponding operator class. * 'clause' is the clause to be tested. * @@ -703,9 +692,9 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index, * responsibility of higher-level routines to cope with those. */ static bool -match_clause_to_indexkey(RelOptInfo *rel, +match_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index, - int indexkey, + int indexcol, Oid opclass, Expr *clause) { @@ -725,7 +714,7 @@ match_clause_to_indexkey(RelOptInfo *rel, * (indexkey operator constant) or (constant operator indexkey). * Anything that is a "pseudo constant" expression will do. */ - if (match_index_to_operand(indexkey, leftop, rel, index) && + if (match_index_to_operand(leftop, indexcol, rel, index) && is_pseudo_constant_clause(rightop)) { if (is_indexable_operator(clause, opclass, true)) @@ -740,7 +729,7 @@ match_clause_to_indexkey(RelOptInfo *rel, return false; } - if (match_index_to_operand(indexkey, rightop, rel, index) && + if (match_index_to_operand(rightop, indexcol, rel, index) && is_pseudo_constant_clause(leftop)) { if (is_indexable_operator(clause, opclass, false)) @@ -759,8 +748,8 @@ match_clause_to_indexkey(RelOptInfo *rel, } /* - * match_join_clause_to_indexkey() - * Determines whether a join clause matches a key of an index. + * match_join_clause_to_indexcol() + * Determines whether a join clause matches a column of an index. * * To match, the clause: * @@ -768,7 +757,7 @@ match_clause_to_indexkey(RelOptInfo *rel, * where others is an expression involving only vars of the other * relation(s); and * (2) must contain an operator which is in the same class as the index - * operator for this key, or is a "special" operator as recognized + * operator for this column, or is a "special" operator as recognized * by match_special_index_operator(). * * As above, we must be able to commute the clause to put the indexkey @@ -781,7 +770,7 @@ match_clause_to_indexkey(RelOptInfo *rel, * * 'rel' is the relation of interest. * 'index' is an index on 'rel'. - * 'indexkey' is a key of 'index'. + * 'indexcol' is a column number of 'index' (counting from 0). * 'opclass' is the corresponding operator class. * 'clause' is the clause to be tested. * @@ -791,9 +780,9 @@ match_clause_to_indexkey(RelOptInfo *rel, * responsibility of higher-level routines to cope with those. */ static bool -match_join_clause_to_indexkey(RelOptInfo *rel, +match_join_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index, - int indexkey, + int indexcol, Oid opclass, Expr *clause) { @@ -814,7 +803,7 @@ match_join_clause_to_indexkey(RelOptInfo *rel, * expression that uses none of the indexed relation's vars and * contains no volatile functions. */ - if (match_index_to_operand(indexkey, leftop, rel, index)) + if (match_index_to_operand(leftop, indexcol, rel, index)) { Relids othervarnos = pull_varnos(rightop); bool isIndexable; @@ -827,7 +816,7 @@ match_join_clause_to_indexkey(RelOptInfo *rel, return isIndexable; } - if (match_index_to_operand(indexkey, rightop, rel, index)) + if (match_index_to_operand(rightop, indexcol, rel, index)) { Relids othervarnos = pull_varnos(leftop); bool isIndexable; @@ -895,8 +884,7 @@ indexable_operator(Expr *clause, Oid opclass, bool indexkey_on_left) * to CNF format). --Nels, Jan '93 */ static bool -pred_test(List *predicate_list, List *restrictinfo_list, List *joininfo_list, - int relvarno) +pred_test(List *predicate_list, List *restrictinfo_list, List *joininfo_list) { List *pred; @@ -919,18 +907,6 @@ pred_test(List *predicate_list, List *restrictinfo_list, List *joininfo_list, return false; /* no restriction clauses: the test must * fail */ - /* - * The predicate as stored in the index definition will use varno 1 - * for its Vars referencing the indexed relation. If the indexed - * relation isn't varno 1 in the query, we must adjust the predicate - * to make the Vars match, else equal() won't work. - */ - if (relvarno != 1) - { - predicate_list = copyObject(predicate_list); - ChangeVarNodes((Node *) predicate_list, 1, relvarno, 0); - } - foreach(pred, predicate_list) { /* @@ -1320,17 +1296,16 @@ indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(j); Expr *clause = rinfo->clause; - int *indexkeys = index->indexkeys; + int indexcol = 0; Oid *classes = index->classlist; do { - int curIndxKey = indexkeys[0]; Oid curClass = classes[0]; - if (match_join_clause_to_indexkey(rel, + if (match_join_clause_to_indexcol(rel, index, - curIndxKey, + indexcol, curClass, clause)) { @@ -1338,10 +1313,10 @@ indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index) break; } - indexkeys++; + indexcol++; classes++; - } while (!DoneMatchingIndexKeys(indexkeys, classes)); + } while (!DoneMatchingIndexKeys(classes)); if (match_found) break; @@ -1611,16 +1586,22 @@ make_innerjoin_index_path(Query *root, * match_index_to_operand() * Generalized test for a match between an index's key * and the operand on one side of a restriction or join clause. - * Now check for functional indices as well. + * + * operand: the nodetree to be compared to the index + * indexcol: the column number of the index (counting from 0) + * rel: the parent relation + * index: the index of interest */ static bool -match_index_to_operand(int indexkey, - Node *operand, +match_index_to_operand(Node *operand, + int indexcol, RelOptInfo *rel, IndexOptInfo *index) { + int indkey; + /* - * Ignore any RelabelType node above the indexkey. This is needed to + * Ignore any RelabelType node above the operand. This is needed to * be able to apply indexscanning in binary-compatible-operator cases. * Note: we can assume there is at most one RelabelType node; * eval_const_expressions() will have simplified if more than one. @@ -1628,77 +1609,52 @@ match_index_to_operand(int indexkey, if (operand && IsA(operand, RelabelType)) operand = (Node *) ((RelabelType *) operand)->arg; - if (index->indproc == InvalidOid) + indkey = index->indexkeys[indexcol]; + if (indkey != 0) { /* - * Simple index. + * Simple index column; operand must be a matching Var. */ if (operand && IsA(operand, Var) && rel->relid == ((Var *) operand)->varno && - indexkey == ((Var *) operand)->varattno) + indkey == ((Var *) operand)->varattno) return true; - else - return false; } - - /* - * Functional index. - */ - return function_index_operand((Expr *) operand, rel, index); -} - -static bool -function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index) -{ - FuncExpr *function; - List *funcargs; - int *indexKeys = index->indexkeys; - List *arg; - int i; - - /* - * sanity check, make sure we know what we're dealing with here. - */ - if (funcOpnd == NULL || !IsA(funcOpnd, FuncExpr) || - indexKeys == NULL) - return false; - - function = (FuncExpr *) funcOpnd; - funcargs = function->args; - - if (function->funcid != index->indproc) - return false; - - /*---------- - * Check that the arguments correspond to the same arguments used to - * create the functional index. To do this we must check that - * 1. they refer to the right relation. - * 2. the args have the right attr. numbers in the right order. - * We must ignore RelabelType nodes above the argument Vars in order - * to recognize binary-compatible-function cases correctly. - *---------- - */ - i = 0; - foreach(arg, funcargs) + else { - Var *var = (Var *) lfirst(arg); + /* + * Index expression; find the correct expression. (This search could + * be avoided, at the cost of complicating all the callers of this + * routine; doesn't seem worth it.) + */ + List *indexprs; + int i; + Node *indexkey; - if (var && IsA(var, RelabelType)) - var = (Var *) ((RelabelType *) var)->arg; - if (var == NULL || !IsA(var, Var)) - return false; - if (indexKeys[i] == 0) - return false; - if (var->varno != rel->relid || var->varattno != indexKeys[i]) - return false; + indexprs = index->indexprs; + for (i = 0; i < indexcol; i++) + { + if (index->indexkeys[i] == 0) + { + if (indexprs == NIL) + elog(ERROR, "wrong number of index expressions"); + indexprs = lnext(indexprs); + } + } + if (indexprs == NIL) + elog(ERROR, "wrong number of index expressions"); + indexkey = (Node *) lfirst(indexprs); + /* + * Does it match the operand? Again, strip any relabeling. + */ + if (indexkey && IsA(indexkey, RelabelType)) + indexkey = (Node *) ((RelabelType *) indexkey)->arg; - i++; + if (equal(indexkey, operand)) + return true; } - if (indexKeys[i] != 0) - return false; /* not enough arguments */ - - return true; + return false; } /**************************************************************************** @@ -1728,7 +1684,7 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index) * * Two routines are provided here, match_special_index_operator() and * expand_indexqual_conditions(). match_special_index_operator() is - * just an auxiliary function for match_clause_to_indexkey(); after + * just an auxiliary function for match_clause_to_indexcol(); after * the latter fails to recognize a restriction opclause's operator * as a member of an index's opclass, it asks match_special_index_operator() * whether the clause should be considered an indexqual anyway. @@ -1915,7 +1871,6 @@ List * expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups) { List *resultquals = NIL; - int *indexkeys = index->indexkeys; Oid *classes = index->classlist; if (clausegroups == NIL) @@ -1937,11 +1892,9 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups) clausegroups = lnext(clausegroups); - indexkeys++; classes++; - } while (clausegroups != NIL && - !DoneMatchingIndexKeys(indexkeys, classes)); + } while (clausegroups != NIL && !DoneMatchingIndexKeys(classes)); Assert(clausegroups == NIL); /* else more groups than indexkeys... */ diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index cf48b7c47bd..f8e4906c13b 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -11,7 +11,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.48 2003/05/02 19:48:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.49 2003/05/28 16:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -636,73 +636,50 @@ build_index_pathkeys(Query *root, List *retval = NIL; int *indexkeys = index->indexkeys; Oid *ordering = index->ordering; - PathKeyItem *item; - Oid sortop; - - if (!indexkeys || indexkeys[0] == 0 || - !ordering || ordering[0] == InvalidOid) - return NIL; /* unordered index? */ + List *indexprs = index->indexprs; - if (index->indproc) + while (*ordering != InvalidOid) { - /* Functional index: build a representation of the function call */ - Expr *funcnode; - List *funcargs = NIL; + PathKeyItem *item; + Oid sortop; + Node *indexkey; + List *cpathkey; sortop = *ordering; if (ScanDirectionIsBackward(scandir)) { sortop = get_commutator(sortop); if (sortop == InvalidOid) - return NIL; /* oops, no reverse sort operator? */ + break; /* oops, no reverse sort operator? */ } - while (*indexkeys != 0) + if (*indexkeys != 0) { - funcargs = lappend(funcargs, - find_indexkey_var(root, rel, *indexkeys)); - indexkeys++; + /* simple index column */ + indexkey = (Node *) find_indexkey_var(root, rel, *indexkeys); } - - funcnode = make_funcclause(index->indproc, - get_func_rettype(index->indproc), - false, /* cannot be a set */ - COERCE_DONTCARE, /* to match any user expr */ - funcargs); - - /* Make a one-sublist pathkeys list for the function expression */ - item = makePathKeyItem((Node *) funcnode, sortop); - retval = makeList1(make_canonical_pathkey(root, item)); - } - else - { - /* Normal non-functional index */ - while (*indexkeys != 0 && *ordering != InvalidOid) + else { - Var *relvar = find_indexkey_var(root, rel, *indexkeys); - List *cpathkey; + /* expression --- assume we need not copy it */ + if (indexprs == NIL) + elog(ERROR, "wrong number of index expressions"); + indexkey = (Node *) lfirst(indexprs); + indexprs = lnext(indexprs); + } - sortop = *ordering; - if (ScanDirectionIsBackward(scandir)) - { - sortop = get_commutator(sortop); - if (sortop == InvalidOid) - break; /* oops, no reverse sort operator? */ - } + /* OK, make a sublist for this sort key */ + item = makePathKeyItem(indexkey, sortop); + cpathkey = make_canonical_pathkey(root, item); - /* OK, make a sublist for this sort key */ - item = makePathKeyItem((Node *) relvar, sortop); - cpathkey = make_canonical_pathkey(root, item); + /* + * Eliminate redundant ordering info; could happen if query is + * such that index keys are equijoined... + */ + if (!ptrMember(cpathkey, retval)) + retval = lappend(retval, cpathkey); - /* - * Eliminate redundant ordering info; could happen if query is - * such that index keys are equijoined... - */ - if (!ptrMember(cpathkey, retval)) - retval = lappend(retval, cpathkey); - indexkeys++; - ordering++; - } + indexkeys++; + ordering++; } return retval; diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index f50eb792811..0d55dd7f8c3 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.142 2003/05/12 00:17:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.143 2003/05/28 16:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1198,60 +1198,74 @@ fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index, Oid *opclass) { /* - * Remove any binary-compatible relabeling of the indexkey - */ - if (IsA(node, RelabelType)) - node = (Node *) ((RelabelType *) node)->arg; - - /* * We represent index keys by Var nodes having the varno of the base * table but varattno equal to the index's attribute number (index * column position). This is a bit hokey ... would be cleaner to use * a special-purpose node type that could not be mistaken for a * regular Var. But it will do for now. */ - if (IsA(node, Var)) + Var *result; + int pos; + List *indexprs; + + /* + * Remove any binary-compatible relabeling of the indexkey + */ + if (IsA(node, RelabelType)) + node = (Node *) ((RelabelType *) node)->arg; + + if (IsA(node, Var) && + ((Var *) node)->varno == baserelid) { - /* If it's a var, find which index key position it occupies */ - Assert(index->indproc == InvalidOid); + /* Try to match against simple index columns */ + int varatt = ((Var *) node)->varattno; - if (((Var *) node)->varno == baserelid) + if (varatt != 0) { - int varatt = ((Var *) node)->varattno; - int pos; - - for (pos = 0; pos < index->nkeys; pos++) + for (pos = 0; pos < index->ncolumns; pos++) { if (index->indexkeys[pos] == varatt) { - Node *newnode = copyObject(node); - - ((Var *) newnode)->varattno = pos + 1; + result = (Var *) copyObject(node); + result->varattno = pos + 1; /* return the correct opclass, too */ *opclass = index->classlist[pos]; - return newnode; + return (Node *) result; } } } - - /* - * Oops, this Var isn't an indexkey! - */ - elog(ERROR, "fix_indxqual_operand: var is not index attribute"); } - /* - * Else, it must be a func expression matching a functional index. - * Since we currently only support single-column functional indexes, - * the returned varattno must be 1. - */ - Assert(index->indproc != InvalidOid); - Assert(is_funcclause(node)); /* not a very thorough check, but easy */ - - /* classlist[0] is the only class of a functional index */ - *opclass = index->classlist[0]; + /* Try to match against index expressions */ + indexprs = index->indexprs; + for (pos = 0; pos < index->ncolumns; pos++) + { + if (index->indexkeys[pos] == 0) + { + Node *indexkey; + + if (indexprs == NIL) + elog(ERROR, "too few entries in indexprs list"); + indexkey = (Node *) lfirst(indexprs); + if (indexkey && IsA(indexkey, RelabelType)) + indexkey = (Node *) ((RelabelType *) indexkey)->arg; + if (equal(node, indexkey)) + { + /* Found a match */ + result = makeVar(baserelid, pos + 1, + exprType(lfirst(indexprs)), -1, + 0); + /* return the correct opclass, too */ + *opclass = index->classlist[pos]; + return (Node *) result; + } + indexprs = lnext(indexprs); + } + } - return (Node *) makeVar(baserelid, 1, exprType(node), -1, 0); + /* Ooops... */ + elog(ERROR, "fix_indxqual_operand: node is not index attribute"); + return NULL; /* keep compiler quiet */ } /* diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index c0ffd939cbe..4d51e7ffbc0 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.136 2003/04/29 22:13:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.137 2003/05/28 16:03:56 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -133,28 +133,6 @@ get_rightop(Expr *clause) } /***************************************************************************** - * FUNCTION clause functions - *****************************************************************************/ - -/* - * make_funcclause - * Creates a function clause given its function info and argument list. - */ -Expr * -make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset, - CoercionForm funcformat, List *funcargs) -{ - FuncExpr *expr = makeNode(FuncExpr); - - expr->funcid = funcid; - expr->funcresulttype = funcresulttype; - expr->funcretset = funcretset; - expr->funcformat = funcformat; - expr->args = funcargs; - return (Expr *) expr; -} - -/***************************************************************************** * NOT clause functions *****************************************************************************/ diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 9ec638a5a43..36223be93fd 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.82 2003/05/12 00:17:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.83 2003/05/28 16:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,6 +27,7 @@ #include "optimizer/clauses.h" #include "optimizer/plancat.h" #include "parser/parsetree.h" +#include "rewrite/rewriteManip.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" @@ -116,78 +117,67 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel) Relation indexRelation; Form_pg_index index; IndexOptInfo *info; + int ncolumns; int i; int16 amorderstrategy; /* Extract info from the relation descriptor for the index */ indexRelation = index_open(indexoid); + index = indexRelation->rd_index; info = makeNode(IndexOptInfo); - /* - * Need to make these arrays large enough to be sure there is room - * for a terminating 0 at the end of each one. - */ - info->classlist = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1)); - info->indexkeys = (int *) palloc(sizeof(int) * (INDEX_MAX_KEYS + 1)); - info->ordering = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1)); - - /* Extract info from the pg_index tuple */ - index = indexRelation->rd_index; info->indexoid = index->indexrelid; - info->indproc = index->indproc; /* functional index ?? */ - if (VARSIZE(&index->indpred) > VARHDRSZ) /* partial index ?? */ - { - char *predString; + info->ncolumns = ncolumns = index->indnatts; - predString = DatumGetCString(DirectFunctionCall1(textout, - PointerGetDatum(&index->indpred))); - info->indpred = (List *) stringToNode(predString); - pfree(predString); - } - else - info->indpred = NIL; - info->unique = index->indisunique; + /* + * Need to make classlist and ordering arrays large enough to put + * a terminating 0 at the end of each one. + */ + info->indexkeys = (int *) palloc(sizeof(int) * ncolumns); + info->classlist = (Oid *) palloc0(sizeof(Oid) * (ncolumns + 1)); + info->ordering = (Oid *) palloc0(sizeof(Oid) * (ncolumns + 1)); - for (i = 0; i < INDEX_MAX_KEYS; i++) + for (i = 0; i < ncolumns; i++) { - if (index->indclass[i] == (Oid) 0) - break; info->classlist[i] = index->indclass[i]; - } - info->classlist[i] = (Oid) 0; - info->ncolumns = i; - - for (i = 0; i < INDEX_MAX_KEYS; i++) - { - if (index->indkey[i] == 0) - break; info->indexkeys[i] = index->indkey[i]; } - info->indexkeys[i] = 0; - info->nkeys = i; info->relam = indexRelation->rd_rel->relam; info->pages = indexRelation->rd_rel->relpages; info->tuples = indexRelation->rd_rel->reltuples; info->amcostestimate = index_cost_estimator(indexRelation); - amorderstrategy = indexRelation->rd_am->amorderstrategy; /* * Fetch the ordering operators associated with the index, if any. */ - MemSet(info->ordering, 0, sizeof(Oid) * (INDEX_MAX_KEYS + 1)); + amorderstrategy = indexRelation->rd_am->amorderstrategy; if (amorderstrategy != 0) { int oprindex = amorderstrategy - 1; - for (i = 0; i < info->ncolumns; i++) + for (i = 0; i < ncolumns; i++) { info->ordering[i] = indexRelation->rd_operator[oprindex]; oprindex += indexRelation->rd_am->amstrategies; } } + /* + * Fetch the index expressions and predicate, if any. We must + * modify the copies we obtain from the relcache to have the + * correct varno for the parent relation, so that they match up + * correctly against qual clauses. + */ + info->indexprs = RelationGetIndexExpressions(indexRelation); + info->indpred = RelationGetIndexPredicate(indexRelation); + if (info->indexprs && varno != 1) + ChangeVarNodes((Node *) info->indexprs, 1, varno, 0); + if (info->indpred && varno != 1) + ChangeVarNodes((Node *) info->indpred, 1, varno, 0); + info->unique = index->indisunique; + /* initialize cached join info to empty */ info->outer_relids = NULL; info->inner_paths = NIL; @@ -372,15 +362,15 @@ has_unique_index(RelOptInfo *rel, AttrNumber attno) IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist); /* - * Note: ignore functional and partial indexes, since they don't - * allow us to conclude that all attr values are distinct. Also, a - * multicolumn unique index doesn't allow us to conclude that just - * the specified attr is unique. + * Note: ignore partial indexes, since they don't allow us to conclude + * that all attr values are distinct. We don't take any interest in + * expressional indexes either. Also, a multicolumn unique index + * doesn't allow us to conclude that just the specified attr is + * unique. */ if (index->unique && - index->nkeys == 1 && + index->ncolumns == 1 && index->indexkeys[0] == attno && - index->indproc == InvalidOid && index->indpred == NIL) return true; } diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index b7fc22c46be..79b36caad75 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.271 2003/05/06 00:20:32 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.272 2003/05/28 16:03:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1311,8 +1311,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) /* OK, add it to the index definition */ iparam = makeNode(IndexElem); iparam->name = pstrdup(key); - iparam->funcname = NIL; - iparam->args = NIL; + iparam->expr = NULL; iparam->opclass = NIL; index->indexParams = lappend(index->indexParams, iparam); } @@ -1386,11 +1385,13 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) if (index->idxname == NULL && index->indexParams != NIL) { - iparam = lfirst(index->indexParams); + iparam = (IndexElem *) lfirst(index->indexParams); + /* we should never see an expression item here */ + Assert(iparam->expr == NULL); index->idxname = CreateIndexName(cxt->relation->relname, - iparam->name ? iparam->name : - strVal(llast(iparam->funcname)), - "key", cxt->alist); + iparam->name, + "key", + cxt->alist); } if (index->idxname == NULL) /* should not happen */ elog(ERROR, "%s: failed to make implicit index name", @@ -1454,7 +1455,8 @@ static Query * transformIndexStmt(ParseState *pstate, IndexStmt *stmt) { Query *qry; - RangeTblEntry *rte; + RangeTblEntry *rte = NULL; + List *l; qry = makeNode(Query); qry->commandType = CMD_UTILITY; @@ -1477,6 +1479,32 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt) stmt->whereClause = transformWhereClause(pstate, stmt->whereClause); } + /* take care of any index expressions */ + foreach(l, stmt->indexParams) + { + IndexElem *ielem = (IndexElem *) lfirst(l); + + if (ielem->expr) + { + /* Set up rtable as for predicate, see notes above */ + if (rte == NULL) + { + rte = addRangeTableEntry(pstate, stmt->relation, NULL, + false, true); + /* no to join list, yes to namespace */ + addRTEtoQuery(pstate, rte, false, true); + } + ielem->expr = transformExpr(pstate, ielem->expr); + /* + * We check only that the result type is legitimate; this is + * for consistency with what transformWhereClause() checks for + * the predicate. DefineIndex() will make more checks. + */ + if (expression_returns_set(ielem->expr)) + elog(ERROR, "index expression may not return a set"); + } + } + qry->hasSubLinks = pstate->p_hasSubLinks; stmt->rangetable = pstate->p_rtable; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 0a7ae361f49..681738253bb 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.414 2003/05/15 16:35:28 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.415 2003/05/28 16:03:57 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -210,7 +210,7 @@ static void doNegateFloat(Value *v); oper_argtypes RuleActionList RuleActionMulti opt_column_list columnList opt_name_list sort_clause opt_sort_clause sortby_list index_params - index_list name_list from_clause from_list opt_array_bounds + name_list from_clause from_list opt_array_bounds qualified_name_list any_name any_name_list any_operator expr_list dotted_name attrs target_list update_target_list insert_column_list @@ -276,7 +276,7 @@ static void doNegateFloat(Value *v); %type <columnref> columnref %type <alias> alias_clause %type <sortgroupby> sortby -%type <ielem> index_elem func_index +%type <ielem> index_elem %type <node> table_ref %type <jexpr> joined_table %type <range> relation_expr @@ -408,7 +408,7 @@ static void doNegateFloat(Value *v); %token UNIONJOIN /* Special keywords, not in the query language - see the "lex" file */ -%token <str> IDENT FCONST SCONST NCONST BCONST XCONST Op +%token <str> IDENT FCONST SCONST BCONST XCONST Op %token <ival> ICONST PARAM /* precedence: lowest to highest */ @@ -2932,7 +2932,7 @@ function_with_argtypes: * * QUERY: * create index <indexname> on <relname> - * [ using <access> ] "(" (<col> with <op>)+ ")" + * [ using <access> ] "(" ( <col> [ using <opclass> ] )+ ")" * [ where <predicate> ] * *****************************************************************************/ @@ -2958,70 +2958,48 @@ index_opt_unique: access_method_clause: USING access_method { $$ = $2; } - /* If btree changes as our default, update pg_get_indexdef() */ | /*EMPTY*/ { $$ = DEFAULT_INDEX_TYPE; } ; -index_params: - index_list { $$ = $1; } - | func_index { $$ = makeList1($1); } +index_params: index_elem { $$ = makeList1($1); } + | index_params ',' index_elem { $$ = lappend($1, $3); } ; -index_list: index_elem { $$ = makeList1($1); } - | index_list ',' index_elem { $$ = lappend($1, $3); } - ; - -func_index: func_name '(' name_list ')' opt_class +/* + * Index attributes can be either simple column references, or arbitrary + * expressions in parens. For backwards-compatibility reasons, we allow + * an expression that's just a function call to be written without parens. + */ +index_elem: attr_name opt_class + { + $$ = makeNode(IndexElem); + $$->name = $1; + $$->expr = NULL; + $$->opclass = $2; + } + | func_name '(' expr_list ')' opt_class { + FuncCall *n = makeNode(FuncCall); + n->funcname = $1; + n->args = $3; + n->agg_star = FALSE; + n->agg_distinct = FALSE; + $$ = makeNode(IndexElem); $$->name = NULL; - $$->funcname = $1; - $$->args = $3; + $$->expr = (Node *)n; $$->opclass = $5; } - ; - -index_elem: attr_name opt_class + | '(' a_expr ')' opt_class { $$ = makeNode(IndexElem); - $$->name = $1; - $$->funcname = NIL; - $$->args = NIL; - $$->opclass = $2; + $$->name = NULL; + $$->expr = $2; + $$->opclass = $4; } ; -opt_class: any_name - { - /* - * Release 7.0 removed network_ops, timespan_ops, and - * datetime_ops, so we suppress it from being passed to - * the parser so the default *_ops is used. This can be - * removed in some later release. bjm 2000/02/07 - * - * Release 7.1 removes lztext_ops, so suppress that too - * for a while. tgl 2000/07/30 - * - * Release 7.2 renames timestamp_ops to timestamptz_ops, - * so suppress that too for awhile. I'm starting to - * think we need a better approach. tgl 2000/10/01 - */ - if (length($1) == 1) - { - char *claname = strVal(lfirst($1)); - - if (strcmp(claname, "network_ops") != 0 && - strcmp(claname, "timespan_ops") != 0 && - strcmp(claname, "datetime_ops") != 0 && - strcmp(claname, "lztext_ops") != 0 && - strcmp(claname, "timestamp_ops") != 0) - $$ = $1; - else - $$ = NIL; - } - else - $$ = $1; - } +opt_class: any_name { $$ = $1; } | USING any_name { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } ; diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 7a66a9095e4..1cad3ec31a3 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.65 2003/05/27 17:49:46 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.66 2003/05/28 16:03:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -713,8 +713,9 @@ PortalRunUtility(Portal portal, Query *query, * without freezing a snapshot. By extension we allow SHOW * not to set a snapshot. The other stmts listed are just * efficiency hacks. Beware of listing anything that can - * modify the database --- if, say, it has to update a - * functional index, then it had better have a snapshot. + * modify the database --- if, say, it has to update an + * index with expressions that invoke user-defined functions, + * then it had better have a snapshot. */ if (! (IsA(utilityStmt, TransactionStmt) || IsA(utilityStmt, LockStmt) || diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index e41d3245818..1040ff3e238 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -3,7 +3,7 @@ * back to source text * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.140 2003/05/20 20:35:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.141 2003/05/28 16:03:59 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -535,12 +535,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS) Form_pg_index idxrec; Form_pg_class idxrelrec; Form_pg_am amrec; + List *indexprs; + List *context; Oid indrelid; int len; int keyno; - Oid keycoltypes[INDEX_MAX_KEYS]; + Oid keycoltype; StringInfoData buf; - StringInfoData keybuf; + char *str; char *sep; /* @@ -577,6 +579,30 @@ pg_get_indexdef(PG_FUNCTION_ARGS) amrec = (Form_pg_am) GETSTRUCT(ht_am); /* + * Get the index expressions, if any. (NOTE: we do not use the relcache + * versions of the expressions and predicate, because we want to display + * non-const-folded expressions.) + */ + if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs)) + { + Datum exprsDatum; + bool isnull; + char *exprsString; + + exprsDatum = SysCacheGetAttr(INDEXRELID, ht_idx, + Anum_pg_index_indexprs, &isnull); + Assert(!isnull); + exprsString = DatumGetCString(DirectFunctionCall1(textout, + exprsDatum)); + indexprs = (List *) stringToNode(exprsString); + pfree(exprsString); + } + else + indexprs = NIL; + + context = deparse_context_for(get_rel_name(indrelid), indrelid); + + /* * Start the index definition. Note that the index's name should * never be schema-qualified, but the indexed rel's name may be. */ @@ -588,76 +614,72 @@ pg_get_indexdef(PG_FUNCTION_ARGS) quote_identifier(NameStr(amrec->amname))); /* - * Collect the indexed attributes in keybuf + * Report the indexed attributes */ - initStringInfo(&keybuf); sep = ""; - for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++) + for (keyno = 0; keyno < idxrec->indnatts; keyno++) { AttrNumber attnum = idxrec->indkey[keyno]; - char *attname; - - if (attnum == InvalidAttrNumber) - break; - - attname = get_relid_attribute_name(indrelid, attnum); - keycoltypes[keyno] = get_atttype(indrelid, attnum); - appendStringInfo(&keybuf, sep); + appendStringInfo(&buf, sep); sep = ", "; - /* - * Add the indexed field name - */ - appendStringInfo(&keybuf, "%s", quote_identifier(attname)); + if (attnum != 0) + { + /* Simple index column */ + char *attname; - /* - * If not a functional index, add the operator class name - */ - if (idxrec->indproc == InvalidOid) - get_opclass_name(idxrec->indclass[keyno], - keycoltypes[keyno], - &keybuf); - } + attname = get_relid_attribute_name(indrelid, attnum); + appendStringInfo(&buf, "%s", quote_identifier(attname)); + keycoltype = get_atttype(indrelid, attnum); + } + else + { + /* expressional index */ + Node *indexkey; + + if (indexprs == NIL) + elog(ERROR, "too few entries in indexprs list"); + indexkey = (Node *) lfirst(indexprs); + indexprs = lnext(indexprs); + /* Deparse */ + str = deparse_expression(indexkey, context, false, false); + /* Need parens if it's not a bare function call */ + if (indexkey && IsA(indexkey, FuncExpr) && + ((FuncExpr *) indexkey)->funcformat == COERCE_EXPLICIT_CALL) + appendStringInfo(&buf, "%s", str); + else + appendStringInfo(&buf, "(%s)", str); + keycoltype = exprType(indexkey); + } - if (idxrec->indproc != InvalidOid) - { /* - * For functional index say 'func (attrs) opclass' + * Add the operator class name */ - appendStringInfo(&buf, "%s(%s)", - generate_function_name(idxrec->indproc, - keyno, keycoltypes), - keybuf.data); - get_opclass_name(idxrec->indclass[0], - get_func_rettype(idxrec->indproc), + get_opclass_name(idxrec->indclass[keyno], keycoltype, &buf); } - else - { - /* - * Otherwise say 'attr opclass [, ...]' - */ - appendStringInfo(&buf, "%s", keybuf.data); - } appendStringInfoChar(&buf, ')'); /* * If it's a partial index, decompile and append the predicate */ - if (VARSIZE(&idxrec->indpred) > VARHDRSZ) + if (!heap_attisnull(ht_idx, Anum_pg_index_indpred)) { Node *node; - List *context; - char *exprstr; - char *str; + Datum predDatum; + bool isnull; + char *predString; - /* Convert TEXT object to C string */ - exprstr = DatumGetCString(DirectFunctionCall1(textout, - PointerGetDatum(&idxrec->indpred))); - /* Convert expression to node tree */ - node = (Node *) stringToNode(exprstr); + /* Convert text string to node tree */ + predDatum = SysCacheGetAttr(INDEXRELID, ht_idx, + Anum_pg_index_indpred, &isnull); + Assert(!isnull); + predString = DatumGetCString(DirectFunctionCall1(textout, + predDatum)); + node = (Node *) stringToNode(predString); + pfree(predString); /* * If top level is a List, assume it is an implicit-AND structure, @@ -667,7 +689,6 @@ pg_get_indexdef(PG_FUNCTION_ARGS) if (node && IsA(node, List)) node = (Node *) make_ands_explicit((List *) node); /* Deparse */ - context = deparse_context_for(get_rel_name(indrelid), indrelid); str = deparse_expression(node, context, false, false); appendStringInfo(&buf, " WHERE %s", str); } @@ -681,7 +702,6 @@ pg_get_indexdef(PG_FUNCTION_ARGS) memcpy(VARDATA(indexdef), buf.data, buf.len); pfree(buf.data); - pfree(keybuf.data); ReleaseSysCache(ht_idx); ReleaseSysCache(ht_idxrel); diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 77ef33b8783..724503e9c4c 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.138 2003/05/26 00:11:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.139 2003/05/28 16:03:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -3905,14 +3905,13 @@ btcostestimate(PG_FUNCTION_ARGS) indexSelectivity, indexCorrelation); /* - * If it's a functional index, leave the default zero-correlation - * estimate in place. If not, and if we can get an estimate for the - * first variable's ordering correlation C from pg_statistic, estimate + * If the first column is a simple variable, and we can get an estimate + * for its ordering correlation C from pg_statistic, estimate * the index correlation as C / number-of-columns. (The idea here is * that multiple columns dilute the importance of the first column's * ordering, but don't negate it entirely.) */ - if (index->indproc == InvalidOid) + if (index->indexkeys[0] != 0) { Oid relid; HeapTuple tuple; @@ -3942,8 +3941,7 @@ btcostestimate(PG_FUNCTION_ARGS) Assert(nnumbers == 1); varCorrelation = numbers[0]; - for (nKeys = 1; index->indexkeys[nKeys] != 0; nKeys++) - /* skip */ ; + nKeys = index->ncolumns; *indexCorrelation = varCorrelation / nKeys; diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 6ab9427dfdb..14899e2968b 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.184 2003/02/09 06:56:28 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.185 2003/05/28 16:03:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -52,6 +52,8 @@ #include "catalog/pg_type.h" #include "commands/trigger.h" #include "miscadmin.h" +#include "optimizer/clauses.h" +#include "optimizer/planmain.h" #include "storage/smgr.h" #include "utils/builtins.h" #include "utils/catcache.h" @@ -939,10 +941,9 @@ void RelationInitIndexAccessInfo(Relation relation) { HeapTuple tuple; - Size iformsize; - Form_pg_index iform; Form_pg_am aform; MemoryContext indexcxt; + MemoryContext oldcontext; IndexStrategy strategy; Oid *operator; RegProcedure *support; @@ -952,8 +953,9 @@ RelationInitIndexAccessInfo(Relation relation) uint16 amsupport; /* - * Make a copy of the pg_index entry for the index. Note that this is - * a variable-length tuple. + * Make a copy of the pg_index entry for the index. Since pg_index + * contains variable-length and possibly-null fields, we have to do + * this honestly rather than just treating it as a Form_pg_index struct. */ tuple = SearchSysCache(INDEXRELID, ObjectIdGetDatum(RelationGetRelid(relation)), @@ -961,11 +963,11 @@ RelationInitIndexAccessInfo(Relation relation) if (!HeapTupleIsValid(tuple)) elog(ERROR, "RelationInitIndexAccessInfo: no pg_index entry for index %u", RelationGetRelid(relation)); - iformsize = tuple->t_len - tuple->t_data->t_hoff; - iform = (Form_pg_index) MemoryContextAlloc(CacheMemoryContext, iformsize); - memcpy(iform, GETSTRUCT(tuple), iformsize); + oldcontext = MemoryContextSwitchTo(CacheMemoryContext); + relation->rd_indextuple = heap_copytuple(tuple); + relation->rd_index = (Form_pg_index) GETSTRUCT(relation->rd_indextuple); + MemoryContextSwitchTo(oldcontext); ReleaseSysCache(tuple); - relation->rd_index = iform; /* * Make a copy of the pg_am entry for the index's access method @@ -982,6 +984,9 @@ RelationInitIndexAccessInfo(Relation relation) relation->rd_am = aform; natts = relation->rd_rel->relnatts; + if (natts != relation->rd_index->indnatts) + elog(ERROR, "RelationInitIndexAccessInfo: relnatts disagrees with indnatts for index %u", + RelationGetRelid(relation)); amstrategies = aform->amstrategies; amsupport = aform->amsupport; @@ -1047,9 +1052,15 @@ RelationInitIndexAccessInfo(Relation relation) * Fill the strategy map and the support RegProcedure arrays. * (supportinfo is left as zeroes, and is filled on-the-fly when used) */ - IndexSupportInitialize(iform, + IndexSupportInitialize(relation->rd_index, strategy, operator, support, amstrategies, amsupport, natts); + + /* + * expressions and predicate cache will be filled later + */ + relation->rd_indexprs = NIL; + relation->rd_indpred = NIL; } /* @@ -1087,8 +1098,7 @@ IndexSupportInitialize(Form_pg_index iform, { OpClassCacheEnt *opcentry; - if (iform->indkey[attIndex] == InvalidAttrNumber || - !OidIsValid(iform->indclass[attIndex])) + if (!OidIsValid(iform->indclass[attIndex])) elog(ERROR, "IndexSupportInitialize: bogus pg_index tuple"); /* look up the info for this opclass, using a cache */ @@ -1729,8 +1739,8 @@ RelationClearRelation(Relation relation, bool rebuild) * with, we can only get rid of these fields: */ FreeTriggerDesc(relation->trigdesc); - if (relation->rd_index) - pfree(relation->rd_index); + if (relation->rd_indextuple) + pfree(relation->rd_indextuple); if (relation->rd_am) pfree(relation->rd_am); if (relation->rd_rel) @@ -2659,6 +2669,136 @@ insert_ordered_oid(List *list, Oid datum) return list; } +/* + * RelationGetIndexExpressions -- get the index expressions for an index + * + * We cache the result of transforming pg_index.indexprs into a node tree. + * If the rel is not an index or has no expressional columns, we return NIL. + * Otherwise, the returned tree is copied into the caller's memory context. + * (We don't want to return a pointer to the relcache copy, since it could + * disappear due to relcache invalidation.) + */ +List * +RelationGetIndexExpressions(Relation relation) +{ + List *result; + Datum exprsDatum; + bool isnull; + char *exprsString; + MemoryContext oldcxt; + + /* Quick exit if we already computed the result. */ + if (relation->rd_indexprs) + return (List *) copyObject(relation->rd_indexprs); + + /* Quick exit if there is nothing to do. */ + if (relation->rd_indextuple == NULL || + heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs)) + return NIL; + + /* + * We build the tree we intend to return in the caller's context. + * After successfully completing the work, we copy it into the relcache + * entry. This avoids problems if we get some sort of + * error partway through. + * + * We make use of the syscache's copy of pg_index's tupledesc + * to access the non-fixed fields of the tuple. We assume that + * the syscache will be initialized before any access of a + * partial index could occur. (This would probably fail if we + * were to allow partial indexes on system catalogs.) + */ + exprsDatum = SysCacheGetAttr(INDEXRELID, relation->rd_indextuple, + Anum_pg_index_indexprs, &isnull); + Assert(!isnull); + exprsString = DatumGetCString(DirectFunctionCall1(textout, exprsDatum)); + result = (List *) stringToNode(exprsString); + pfree(exprsString); + + /* + * Run the expressions through eval_const_expressions. This is not just + * an optimization, but is necessary, because the planner will be + * comparing them to const-folded qual clauses, and may fail to detect + * valid matches without this. + */ + result = (List *) eval_const_expressions((Node *) result); + + /* May as well fix opfuncids too */ + fix_opfuncids((Node *) result); + + /* Now save a copy of the completed tree in the relcache entry. */ + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + relation->rd_indexprs = (List *) copyObject(result); + MemoryContextSwitchTo(oldcxt); + + return result; +} + +/* + * RelationGetIndexPredicate -- get the index predicate for an index + * + * We cache the result of transforming pg_index.indpred into a node tree. + * If the rel is not an index or has no predicate, we return NIL. + * Otherwise, the returned tree is copied into the caller's memory context. + * (We don't want to return a pointer to the relcache copy, since it could + * disappear due to relcache invalidation.) + */ +List * +RelationGetIndexPredicate(Relation relation) +{ + List *result; + Datum predDatum; + bool isnull; + char *predString; + MemoryContext oldcxt; + + /* Quick exit if we already computed the result. */ + if (relation->rd_indpred) + return (List *) copyObject(relation->rd_indpred); + + /* Quick exit if there is nothing to do. */ + if (relation->rd_indextuple == NULL || + heap_attisnull(relation->rd_indextuple, Anum_pg_index_indpred)) + return NIL; + + /* + * We build the tree we intend to return in the caller's context. + * After successfully completing the work, we copy it into the relcache + * entry. This avoids problems if we get some sort of + * error partway through. + * + * We make use of the syscache's copy of pg_index's tupledesc + * to access the non-fixed fields of the tuple. We assume that + * the syscache will be initialized before any access of a + * partial index could occur. (This would probably fail if we + * were to allow partial indexes on system catalogs.) + */ + predDatum = SysCacheGetAttr(INDEXRELID, relation->rd_indextuple, + Anum_pg_index_indpred, &isnull); + Assert(!isnull); + predString = DatumGetCString(DirectFunctionCall1(textout, predDatum)); + result = (List *) stringToNode(predString); + pfree(predString); + + /* + * Run the expression through eval_const_expressions. This is not just + * an optimization, but is necessary, because the planner will be + * comparing it to const-folded qual clauses, and may fail to detect + * valid matches without this. + */ + result = (List *) eval_const_expressions((Node *) result); + + /* May as well fix opfuncids too */ + fix_opfuncids((Node *) result); + + /* Now save a copy of the completed tree in the relcache entry. */ + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + relation->rd_indpred = (List *) copyObject(result); + MemoryContextSwitchTo(oldcxt); + + return result; +} + /* * load_relcache_init_file, write_relcache_init_file @@ -2829,14 +2969,19 @@ load_relcache_init_file(void) if (rel->rd_isnailed) nailed_indexes++; - /* next, read the pg_index tuple form */ + /* next, read the pg_index tuple */ if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len)) goto read_failed; - rel->rd_index = (Form_pg_index) palloc(len); - if ((nread = fread(rel->rd_index, 1, len, fp)) != len) + rel->rd_indextuple = (HeapTuple) palloc(len); + if ((nread = fread(rel->rd_indextuple, 1, len, fp)) != len) goto read_failed; + /* Fix up internal pointers in the tuple -- see heap_copytuple */ + rel->rd_indextuple->t_datamcxt = CurrentMemoryContext; + rel->rd_indextuple->t_data = (HeapTupleHeader) ((char *) rel->rd_indextuple + HEAPTUPLESIZE); + rel->rd_index = (Form_pg_index) GETSTRUCT(rel->rd_indextuple); + /* next, read the access method tuple form */ if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len)) goto read_failed; @@ -2904,6 +3049,7 @@ load_relcache_init_file(void) nailed_rels++; Assert(rel->rd_index == NULL); + Assert(rel->rd_indextuple == NULL); Assert(rel->rd_am == NULL); Assert(rel->rd_indexcxt == NULL); Assert(rel->rd_istrat == NULL); @@ -2917,11 +3063,13 @@ load_relcache_init_file(void) * format is complex and subject to change). They must be rebuilt * if needed by RelationCacheInitializePhase2. This is not * expected to be a big performance hit since few system catalogs - * have such. + * have such. Ditto for index expressions and predicates. */ rel->rd_rules = NULL; rel->rd_rulescxt = NULL; rel->trigdesc = NULL; + rel->rd_indexprs = NIL; + rel->rd_indpred = NIL; /* * Reset transient-state fields in the relcache entry @@ -3076,26 +3224,15 @@ write_relcache_init_file(void) if (rel->rd_rel->relkind == RELKIND_INDEX) { Form_pg_am am = rel->rd_am; - HeapTuple tuple; - /* - * We need to write the index tuple form, but this is a bit - * tricky since it's a variable-length struct. Rather than - * hoping to intuit the length, fetch the pg_index tuple - * afresh using the syscache, and write that. - */ - tuple = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(RelationGetRelid(rel)), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "write_relcache_init_file: no pg_index entry for index %u", - RelationGetRelid(rel)); - len = tuple->t_len - tuple->t_data->t_hoff; + /* write the pg_index tuple */ + /* we assume this was created by heap_copytuple! */ + len = HEAPTUPLESIZE + rel->rd_indextuple->t_len; if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len)) - elog(FATAL, "cannot write init file -- index tuple form length"); - if (fwrite(GETSTRUCT(tuple), 1, len, fp) != len) - elog(FATAL, "cannot write init file -- index tuple form"); - ReleaseSysCache(tuple); + elog(FATAL, "cannot write init file -- index tuple length"); + + if (fwrite(rel->rd_indextuple, 1, len, fp) != len) + elog(FATAL, "cannot write init file -- index tuple"); /* next, write the access method tuple form */ len = sizeof(FormData_pg_am); diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 4aeac0c44e6..a20cc1a4207 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -3,7 +3,7 @@ * * Copyright 2000-2002 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.76 2003/03/27 16:57:39 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.77 2003/05/28 16:03:59 tgl Exp $ */ #include "postgres_fe.h" #include "describe.h" @@ -711,10 +711,7 @@ describeOneTableDetails(const char *schemaname, headers[cols] = NULL; /* Get column info (index requires additional checks) */ - if (tableinfo.relkind == 'i') - printfPQExpBuffer(&buf, "SELECT\n CASE i.indproc WHEN ('-'::pg_catalog.regproc) THEN a.attname\n ELSE SUBSTR(pg_catalog.pg_get_indexdef(attrelid),\n POSITION('(' in pg_catalog.pg_get_indexdef(attrelid)))\n END,"); - else - printfPQExpBuffer(&buf, "SELECT a.attname,"); + printfPQExpBuffer(&buf, "SELECT a.attname,"); appendPQExpBuffer(&buf, "\n pg_catalog.format_type(a.atttypid, a.atttypmod)," "\n (SELECT substring(d.adsrc for 128) FROM pg_catalog.pg_attrdef d" "\n WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)," diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 2c28ffcabc5..443af8e8d6a 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catversion.h,v 1.196 2003/05/26 00:11:27 tgl Exp $ + * $Id: catversion.h,v 1.197 2003/05/28 16:03:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200305241 +#define CATALOG_VERSION_NO 200305271 #endif diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index 7a17ce92873..512c17a43cf 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: dependency.h,v 1.7 2003/03/06 22:54:49 tgl Exp $ + * $Id: dependency.h,v 1.8 2003/05/28 16:03:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -91,6 +91,11 @@ extern void recordDependencyOnExpr(const ObjectAddress *depender, Node *expr, List *rtable, DependencyType behavior); +extern void recordDependencyOnSingleRelExpr(const ObjectAddress *depender, + Node *expr, Oid relId, + DependencyType behavior, + DependencyType self_behavior); + /* in pg_depend.c */ extern void recordDependencyOn(const ObjectAddress *depender, diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index eb4e5f8814b..5833a92e2e2 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: index.h,v 1.50 2002/09/23 00:42:48 tgl Exp $ + * $Id: index.h,v 1.51 2003/05/28 16:04:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -18,6 +18,7 @@ #include "catalog/pg_index.h" #include "nodes/execnodes.h" + #define DEFAULT_INDEX_TYPE "btree" /* Typedef for callback function for IndexBuildHeapScan */ @@ -40,12 +41,12 @@ extern Oid index_create(Oid heapRelationId, extern void index_drop(Oid indexId); -extern IndexInfo *BuildIndexInfo(Form_pg_index indexStruct); +extern IndexInfo *BuildIndexInfo(Relation index); extern void FormIndexDatum(IndexInfo *indexInfo, HeapTuple heapTuple, TupleDesc heapDescriptor, - MemoryContext resultCxt, + EState *estate, Datum *datum, char *nullv); diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h index ecdce77f17d..2984b862aa5 100644 --- a/src/include/catalog/pg_index.h +++ b/src/include/catalog/pg_index.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_index.h,v 1.30 2003/03/10 22:28:19 tgl Exp $ + * $Id: pg_index.h,v 1.31 2003/05/28 16:04:00 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -35,18 +35,19 @@ CATALOG(pg_index) BKI_WITHOUT_OIDS { Oid indexrelid; /* OID of the index */ Oid indrelid; /* OID of the relation it indexes */ - regproc indproc; /* OID of function for functional index */ - int2vector indkey; /* column numbers of indexed attributes */ + int2vector indkey; /* column numbers of indexed cols, or 0 */ oidvector indclass; /* opclass identifiers */ - bool indisclustered; /* is this the index last clustered by? */ + int2 indnatts; /* number of columns in index */ bool indisunique; /* is this a unique index? */ bool indisprimary; /* is this index for primary key? */ - Oid indreference; /* oid of index of referenced relation (ie - * - this index for foreign key) */ + bool indisclustered; /* is this the index last clustered by? */ - /* VARIABLE LENGTH FIELD: */ + /* VARIABLE LENGTH FIELDS: */ + text indexprs; /* expression trees for index attributes + * that are not simple column references; + * one for each zero entry in indkey[] */ text indpred; /* expression tree for predicate, if a - * partial index */ + * partial index; else NULL */ } FormData_pg_index; /* ---------------- @@ -63,13 +64,13 @@ typedef FormData_pg_index *Form_pg_index; #define Natts_pg_index 10 #define Anum_pg_index_indexrelid 1 #define Anum_pg_index_indrelid 2 -#define Anum_pg_index_indproc 3 -#define Anum_pg_index_indkey 4 -#define Anum_pg_index_indclass 5 -#define Anum_pg_index_indisclustered 6 -#define Anum_pg_index_indisunique 7 -#define Anum_pg_index_indisprimary 8 -#define Anum_pg_index_indreference 9 +#define Anum_pg_index_indkey 3 +#define Anum_pg_index_indclass 4 +#define Anum_pg_index_indnatts 5 +#define Anum_pg_index_indisunique 6 +#define Anum_pg_index_indisprimary 7 +#define Anum_pg_index_indisclustered 8 +#define Anum_pg_index_indexprs 9 #define Anum_pg_index_indpred 10 #endif /* PG_INDEX_H */ diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h index 809cde3da02..f9de8fa28c8 100644 --- a/src/include/catalog/pg_opclass.h +++ b/src/include/catalog/pg_opclass.h @@ -8,7 +8,7 @@ * <opcamid, opcname> --- that is, there is a row for each valid combination * of opclass name and index access method type. This row specifies the * expected input data type for the opclass (the type of the heap column, - * or the function output type in the case of a functional index). Note + * or the expression output type in the case of an index expression). Note * that types binary-coercible to the specified type will be accepted too. * * For a given <opcamid, opcintype> pair, there can be at most one row that @@ -26,7 +26,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_opclass.h,v 1.49 2003/05/26 00:11:27 tgl Exp $ + * $Id: pg_opclass.h,v 1.50 2003/05/28 16:04:00 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 17e27969f53..68a3bb9b1cd 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: execnodes.h,v 1.97 2003/04/08 23:20:04 tgl Exp $ + * $Id: execnodes.h,v 1.98 2003/05/28 16:04:00 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,19 +27,17 @@ /* ---------------- * IndexInfo information * - * this class holds the information needed to construct new index + * this struct holds the information needed to construct new index * entries for a particular index. Used for both index_build and * retail creation of index entries. * * NumIndexAttrs number of columns in this index - * (1 if a func. index, else same as NumKeyAttrs) - * NumKeyAttrs number of key attributes for this index - * (ie, number of attrs from underlying relation) * KeyAttrNumbers underlying-rel attribute numbers used as keys + * (zeroes indicate expressions) + * Expressions expr trees for expression entries, or NIL if none + * ExpressionsState exec state for expressions, or NIL if none * Predicate partial-index predicate, or NIL if none * PredicateState exec state for predicate, or NIL if none - * FuncOid OID of function, or InvalidOid if not f. index - * FuncInfo fmgr lookup data for function, if FuncOid valid * Unique is it a unique index? * ---------------- */ @@ -47,12 +45,11 @@ typedef struct IndexInfo { NodeTag type; int ii_NumIndexAttrs; - int ii_NumKeyAttrs; AttrNumber ii_KeyAttrNumbers[INDEX_MAX_KEYS]; + List *ii_Expressions; /* list of Expr */ + List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ List *ii_PredicateState; /* list of ExprState */ - Oid ii_FuncOid; - FmgrInfo ii_FuncInfo; bool ii_Unique; } IndexInfo; diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 4ad35b683a7..25d719dd8fc 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.237 2003/05/02 20:54:36 tgl Exp $ + * $Id: parsenodes.h,v 1.238 2003/05/28 16:04:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -353,17 +353,15 @@ typedef struct ColumnDef /* * IndexElem - index parameters (used in CREATE INDEX) * - * For a plain index, each 'name' is an attribute name in the heap relation; - * 'funcname' and 'args' are NIL. For a functional index, only one IndexElem - * is allowed. It has name = NULL, funcname = name of function and args = - * list of attribute names that are the function's arguments. + * For a plain index attribute, 'name' is the name of the table column to + * index, and 'expr' is NULL. For an index expression, 'name' is NULL and + * 'expr' is the expression tree. */ typedef struct IndexElem { NodeTag type; char *name; /* name of attribute to index, or NULL */ - List *funcname; /* qualified name of function */ - List *args; /* list of names of function arguments */ + Node *expr; /* expression to index, or NULL */ List *opclass; /* name of desired opclass; NIL = default */ } IndexElem; @@ -1271,8 +1269,8 @@ typedef struct IndexStmt char *accessMethod; /* name of access method (eg. btree) */ List *indexParams; /* a list of IndexElem */ Node *whereClause; /* qualification (partial-index predicate) */ - List *rangetable; /* range table for qual, filled in by - * transformStmt() */ + List *rangetable; /* range table for qual and/or expressions, + * filled in by transformStmt() */ bool unique; /* is index unique? */ bool primary; /* is index on primary key? */ bool isconstraint; /* is it from a CONSTRAINT clause? */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 03240d64153..4f770b7ca10 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: relation.h,v 1.79 2003/02/08 20:20:55 tgl Exp $ + * $Id: relation.h,v 1.80 2003/05/28 16:04:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -227,15 +227,16 @@ typedef struct RelOptInfo * and indexes, but that created confusion without actually doing anything * useful. So now we have a separate IndexOptInfo struct for indexes. * - * ncolumns and nkeys are the same except for a functional index, - * wherein ncolumns is 1 (the single function output) while nkeys - * is the number of table columns passed to the function. classlist[] - * and ordering[] have ncolumns entries, while indexkeys[] has nkeys - * entries. + * classlist[], indexkeys[], and ordering[] have ncolumns entries. + * Zeroes in the indexkeys[] array indicate index columns that are + * expressions; there is one element in indexprs for each such column. * - * Note: for historical reasons, the arrays classlist, indexkeys and - * ordering have an extra entry that is always zero. Some code scans - * until it sees a zero rather than looking at ncolumns or nkeys. + * Note: for historical reasons, the classlist and ordering arrays have + * an extra entry that is always zero. Some code scans until it sees a + * zero entry, rather than looking at ncolumns. + * + * The indexprs and indpred expressions have been run through + * eval_const_expressions() for ease of matching to WHERE clauses. */ typedef struct IndexOptInfo @@ -250,15 +251,14 @@ typedef struct IndexOptInfo /* index descriptor information */ int ncolumns; /* number of columns in index */ - int nkeys; /* number of keys used by index */ Oid *classlist; /* OIDs of operator classes for columns */ - int *indexkeys; /* column numbers of index's keys */ + int *indexkeys; /* column numbers of index's keys, or 0 */ Oid *ordering; /* OIDs of sort operators for each column */ Oid relam; /* OID of the access method (in pg_am) */ RegProcedure amcostestimate; /* OID of the access method's cost fcn */ - Oid indproc; /* OID of func if functional index, else 0 */ + List *indexprs; /* expressions for non-simple index columns */ List *indpred; /* predicate if a partial index, else NIL */ bool unique; /* true if a unique index */ @@ -289,9 +289,8 @@ typedef struct PathKeyItem /* * key typically points to a Var node, ie a relation attribute, but it - * can also point to a FuncExpr clause representing the value indexed by a - * functional index. Someday we might allow arbitrary expressions as - * path keys, so don't assume more than you must. + * can also point to an arbitrary expression representing the value + * indexed by an index expression. */ } PathKeyItem; diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index 5a38d6ef2ad..77dd3765951 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: clauses.h,v 1.62 2003/02/04 00:50:01 tgl Exp $ + * $Id: clauses.h,v 1.63 2003/05/28 16:04:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,9 +28,6 @@ extern Expr *make_opclause(Oid opno, Oid opresulttype, bool opretset, extern Node *get_leftop(Expr *clause); extern Node *get_rightop(Expr *clause); -extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset, - CoercionForm funcformat, List *funcargs); - extern bool not_clause(Node *clause); extern Expr *make_notclause(Expr *notclause); extern Expr *get_notclausearg(Expr *notclause); diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index a9ff7325c1e..fd5e0c56b76 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: rel.h,v 1.64 2002/11/23 03:59:09 momjian Exp $ + * $Id: rel.h,v 1.65 2003/05/28 16:04:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -132,6 +132,8 @@ typedef struct RelationData /* These are non-NULL only for an index relation: */ Form_pg_index rd_index; /* pg_index tuple describing this index */ + struct HeapTupleData *rd_indextuple; /* all of pg_index tuple */ + /* "struct HeapTupleData *" avoids need to include htup.h here */ Form_pg_am rd_am; /* pg_am tuple for index's AM */ /* index access support info (used only for an index relation) */ @@ -142,6 +144,8 @@ typedef struct RelationData struct FmgrInfo *rd_supportinfo; /* lookup info for support * procedures */ /* "struct FmgrInfo" avoids need to include fmgr.h here */ + List *rd_indexprs; /* index expression trees, if any */ + List *rd_indpred; /* index predicate tree, if any */ /* statistics collection area */ PgStat_Info pgstat_info; diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index c0495284912..f28d23a6268 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: relcache.h,v 1.34 2002/08/06 02:36:35 tgl Exp $ + * $Id: relcache.h,v 1.35 2003/05/28 16:04:02 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,6 +32,8 @@ extern void RelationClose(Relation relation); * Routines to compute/retrieve additional cached information */ extern List *RelationGetIndexList(Relation relation); +extern List *RelationGetIndexExpressions(Relation relation); +extern List *RelationGetIndexPredicate(Relation relation); extern void RelationInitIndexAccessInfo(Relation relation); diff --git a/src/interfaces/python/tutorial/syscat.py b/src/interfaces/python/tutorial/syscat.py index 1ab1d5840c2..46d6d46ae07 100755 --- a/src/interfaces/python/tutorial/syscat.py +++ b/src/interfaces/python/tutorial/syscat.py @@ -36,8 +36,8 @@ def list_simple_ind(pgcnx): ic.relname AS index_name, a.attname FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a WHERE i.indrelid = bc.oid AND i.indexrelid = bc.oid - AND i.indkey[0] = a.attnum AND a.attrelid = bc.oid - AND i.indproc = '0'::oid AND a.attisdropped = 'f' + AND i.indkey[0] = a.attnum AND i.indnatts = 1 + AND a.attrelid = bc.oid AND a.attisdropped = 'f' ORDER BY class_name, index_name, attname""") return result diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index 243198d79f2..d82ed8c4e89 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -81,3 +81,17 @@ INSERT INTO func_index_heap VALUES('ABCD', 'EF'); ERROR: Cannot insert a duplicate key into unique index func_index_index -- but this shouldn't: INSERT INTO func_index_heap VALUES('QWERTY'); +-- +-- Same test, expressional index +-- +DROP TABLE func_index_heap; +CREATE TABLE func_index_heap (f1 text, f2 text); +CREATE UNIQUE INDEX func_index_index on func_index_heap ((f1 || f2) text_ops); +INSERT INTO func_index_heap VALUES('ABC','DEF'); +INSERT INTO func_index_heap VALUES('AB','CDEFG'); +INSERT INTO func_index_heap VALUES('QWE','RTY'); +-- this should fail because of unique index: +INSERT INTO func_index_heap VALUES('ABCD', 'EF'); +ERROR: Cannot insert a duplicate key into unique index func_index_index +-- but this shouldn't: +INSERT INTO func_index_heap VALUES('QWERTY'); diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index 2dd74613d99..87e1943e5a2 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -75,7 +75,7 @@ FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace WHERE relhasoids AND ((nspname ~ '^pg_') IS NOT FALSE) AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE indrelid = c.oid - AND indkey[0] = -2 AND indkey[1] = 0 AND indisunique); + AND indkey[0] = -2 AND indnatts = 1 AND indisunique); relname | nspname ---------+--------- (0 rows) diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql index fe49d4ec2e2..383a7a1a9f9 100644 --- a/src/test/regress/sql/create_index.sql +++ b/src/test/regress/sql/create_index.sql @@ -104,3 +104,19 @@ INSERT INTO func_index_heap VALUES('QWE','RTY'); INSERT INTO func_index_heap VALUES('ABCD', 'EF'); -- but this shouldn't: INSERT INTO func_index_heap VALUES('QWERTY'); + + +-- +-- Same test, expressional index +-- +DROP TABLE func_index_heap; +CREATE TABLE func_index_heap (f1 text, f2 text); +CREATE UNIQUE INDEX func_index_index on func_index_heap ((f1 || f2) text_ops); + +INSERT INTO func_index_heap VALUES('ABC','DEF'); +INSERT INTO func_index_heap VALUES('AB','CDEFG'); +INSERT INTO func_index_heap VALUES('QWE','RTY'); +-- this should fail because of unique index: +INSERT INTO func_index_heap VALUES('ABCD', 'EF'); +-- but this shouldn't: +INSERT INTO func_index_heap VALUES('QWERTY'); diff --git a/src/test/regress/sql/sanity_check.sql b/src/test/regress/sql/sanity_check.sql index 50b1a2659b2..108d4f78124 100644 --- a/src/test/regress/sql/sanity_check.sql +++ b/src/test/regress/sql/sanity_check.sql @@ -21,4 +21,4 @@ FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace WHERE relhasoids AND ((nspname ~ '^pg_') IS NOT FALSE) AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE indrelid = c.oid - AND indkey[0] = -2 AND indkey[1] = 0 AND indisunique); + AND indkey[0] = -2 AND indnatts = 1 AND indisunique); diff --git a/src/tutorial/syscat.source b/src/tutorial/syscat.source index 4ef6d513bf4..ab5e26c94b5 100644 --- a/src/tutorial/syscat.source +++ b/src/tutorial/syscat.source @@ -7,7 +7,7 @@ -- Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group -- Portions Copyright (c) 1994, Regents of the University of California -- --- $Id: syscat.source,v 1.7 2002/06/20 20:29:54 momjian Exp $ +-- $Id: syscat.source,v 1.8 2003/05/28 16:04:02 tgl Exp $ -- --------------------------------------------------------------------------- @@ -31,8 +31,8 @@ SELECT relname -- --- lists all simple indices (ie. those that are not defined over a function --- of several attributes) +-- lists all simple indices (ie. those that are defined over one simple +-- column reference) -- SELECT bc.relname AS class_name, ic.relname AS index_name, @@ -44,8 +44,8 @@ SELECT bc.relname AS class_name, WHERE i.indrelid = bc.oid and i.indexrelid = ic.oid and i.indkey[0] = a.attnum + and i.indnatts = 1 and a.attrelid = bc.oid - and i.indproc = '0'::oid -- no functional indices ORDER BY class_name, index_name, attname; |