summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/bootstrap/bootparse.y4
-rw-r--r--src/backend/bootstrap/bootstrap.c6
-rw-r--r--src/backend/catalog/dependency.c87
-rw-r--r--src/backend/catalog/heap.c60
-rw-r--r--src/backend/catalog/index.c500
-rw-r--r--src/backend/catalog/indexing.c18
-rw-r--r--src/backend/catalog/pg_constraint.c17
-rw-r--r--src/backend/commands/cluster.c55
-rw-r--r--src/backend/commands/indexcmds.c310
-rw-r--r--src/backend/commands/tablecmds.c124
-rw-r--r--src/backend/commands/vacuum.c11
-rw-r--r--src/backend/executor/execUtils.c13
-rw-r--r--src/backend/nodes/copyfuncs.c5
-rw-r--r--src/backend/nodes/equalfuncs.c5
-rw-r--r--src/backend/nodes/outfuncs.c5
-rw-r--r--src/backend/optimizer/path/indxpath.c247
-rw-r--r--src/backend/optimizer/path/pathkeys.c81
-rw-r--r--src/backend/optimizer/plan/createplan.c84
-rw-r--r--src/backend/optimizer/util/clauses.c24
-rw-r--r--src/backend/optimizer/util/plancat.c80
-rw-r--r--src/backend/parser/analyze.c44
-rw-r--r--src/backend/parser/gram.y86
-rw-r--r--src/backend/tcop/pquery.c7
-rw-r--r--src/backend/utils/adt/ruleutils.c126
-rw-r--r--src/backend/utils/adt/selfuncs.c12
-rw-r--r--src/backend/utils/cache/relcache.c209
-rw-r--r--src/bin/psql/describe.c7
-rw-r--r--src/include/catalog/catversion.h4
-rw-r--r--src/include/catalog/dependency.h7
-rw-r--r--src/include/catalog/index.h7
-rw-r--r--src/include/catalog/pg_index.h31
-rw-r--r--src/include/catalog/pg_opclass.h4
-rw-r--r--src/include/nodes/execnodes.h17
-rw-r--r--src/include/nodes/parsenodes.h16
-rw-r--r--src/include/nodes/relation.h29
-rw-r--r--src/include/optimizer/clauses.h5
-rw-r--r--src/include/utils/rel.h6
-rw-r--r--src/include/utils/relcache.h4
-rwxr-xr-xsrc/interfaces/python/tutorial/syscat.py4
-rw-r--r--src/test/regress/expected/create_index.out14
-rw-r--r--src/test/regress/expected/sanity_check.out2
-rw-r--r--src/test/regress/sql/create_index.sql16
-rw-r--r--src/test/regress/sql/sanity_check.sql2
-rw-r--r--src/tutorial/syscat.source8
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;