diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 544f9b758d..41d7a4e34d 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,6 +1,6 @@ @@ -358,6 +358,14 @@ Does the access method support multicolumn indexes? + + amoptionalkey + bool + + Does the access method support a scan without any constraint + for the first index column? + + amindexnulls bool diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml index cbc01bae8c..b5f3d33479 100644 --- a/doc/src/sgml/indexam.sgml +++ b/doc/src/sgml/indexam.sgml @@ -1,5 +1,5 @@ @@ -100,21 +100,30 @@ $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.5 2005/06/05 22:32:53 tgl Exp $ amconcurrent in . The amcanmulticol flag asserts that the access method supports multi-column indexes, while + amoptionalkey asserts that it allows scans + where no indexable restriction clause is given for the first index column. + When amcanmulticol is false, + amoptionalkey essentially says whether the + access method allows full-index scans without any restriction clause. + Access methods that support multiple index columns must + support scans that omit restrictions on any or all of the columns after + the first; however they are permitted to require some restriction to + appear for the first index column, and this is signaled by setting + amoptionalkey false. amindexnulls asserts that index entries are created for NULL key values. Since most indexable operators are strict and hence cannot return TRUE for NULL inputs, it is at first sight attractive to not store index entries for NULLs: they could never be returned by an index scan anyway. However, this - argument fails for a full-table index scan (one with no scan keys); - such a scan should include null rows. In practice this means that - indexes that support ordered scans (have amorderstrategy - nonzero) must index nulls, since the planner might decide to use such a - scan as a substitute for sorting. Such indexes must also be willing to - run a scan with no scan keys at all. Another restriction is that an index + argument fails when an index scan has no restriction clause for a given + index column. In practice this means that + indexes that have amoptionalkey true must + index nulls, since the planner might decide to use such an index + with no scan keys at all. A related restriction is that an index access method that supports multiple index columns must support indexing null values in columns after the first, because the planner - will assume the index can be used for queries on just the first - column(s). For example, consider an index on (a,b) and a query with + will assume the index can be used for queries that do not restrict + these columns. For example, consider an index on (a,b) and a query with WHERE a = 4. The system will assume the index can be used to scan for rows with a = 4, which is wrong if the index omits rows where b is null. diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 81c2114976..a445efc715 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.82 2005/05/27 23:31:20 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.83 2005/06/13 23:14:48 tgl Exp $ * * INTERFACE ROUTINES * index_open - open an index relation by relation OID @@ -25,7 +25,6 @@ * index_getmulti - get multiple tuples from a scan * index_bulk_delete - bulk deletion of index tuples * index_vacuum_cleanup - post-deletion cleanup of an index - * index_cost_estimator - fetch amcostestimate procedure OID * index_getprocid - get a support procedure OID * index_getprocinfo - get a support procedure's lookup info * @@ -718,27 +717,6 @@ index_vacuum_cleanup(Relation indexRelation, return result; } -/* ---------------- - * index_cost_estimator - * - * Fetch the amcostestimate procedure OID for an index. - * - * We could combine fetching and calling the procedure, - * as index_insert does for example; but that would require - * importing a bunch of planner/optimizer stuff into this file. - * ---------------- - */ -RegProcedure -index_cost_estimator(Relation indexRelation) -{ - FmgrInfo *procedure; - - RELATION_CHECKS; - GET_REL_PROCEDURE(amcostestimate); - - return procedure->fn_oid; -} - /* ---------------- * index_getprocid * diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c index 17b3b0dcdd..824d5ea70e 100644 --- a/src/backend/access/nbtree/nbtsearch.c +++ b/src/backend/access/nbtree/nbtsearch.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.91 2005/03/29 00:16:52 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.92 2005/06/13 23:14:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -594,15 +594,17 @@ _bt_first(IndexScanDesc scan, ScanDirection dir) } /* - * Done if that was the last attribute. + * Done if that was the last attribute, or if next key + * is not in sequence (implying no boundary key is available + * for the next attribute). */ - if (i >= so->numberOfKeys) + if (i >= so->numberOfKeys || + cur->sk_attno != curattr + 1) break; /* - * Reset for next attr, which should be in sequence. + * Reset for next attr. */ - Assert(cur->sk_attno == curattr + 1); curattr = cur->sk_attno; chosen = NULL; } diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c index 96a3f05a5d..9a5f8d7ac9 100644 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.62 2004/12/31 21:59:22 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.63 2005/06/13 23:14:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -190,7 +190,9 @@ _bt_formitem(IndexTuple itup) * matched to continue the scan. In general, numberOfRequiredKeys is equal * to the number of keys for leading attributes with "=" keys, plus the * key(s) for the first non "=" attribute, which can be seen to be correct - * by considering the above example. + * by considering the above example. Note in particular that if there are no + * keys for a given attribute, the keys for subsequent attributes can never + * be required; for instance "WHERE y = 4" requires a full-index scan. * * If possible, redundant keys are eliminated: we keep only the tightest * >/>= bound and the tightest keyData; cur = &inkeys[0]; /* we check that input keys are correctly ordered */ - if (cur->sk_attno != 1) - elog(ERROR, "key(s) for attribute 1 missed"); + if (cur->sk_attno < 1) + elog(ERROR, "btree index keys must be ordered by attribute"); /* We can short-circuit most of the work if there's just one key */ if (numberOfKeys == 1) @@ -270,7 +272,8 @@ _bt_preprocess_keys(IndexScanDesc scan) } memcpy(outkeys, inkeys, sizeof(ScanKeyData)); so->numberOfKeys = 1; - so->numberOfRequiredKeys = 1; + if (cur->sk_attno == 1) + so->numberOfRequiredKeys = 1; return; } @@ -324,8 +327,8 @@ _bt_preprocess_keys(IndexScanDesc scan) int priorNumberOfEqualCols = numberOfEqualCols; /* check input keys are correctly ordered */ - if (i < numberOfKeys && cur->sk_attno != attno + 1) - elog(ERROR, "key(s) for attribute %d missed", attno + 1); + if (i < numberOfKeys && cur->sk_attno < attno) + elog(ERROR, "btree index keys must be ordered by attribute"); /* * If = has been specified, no other key will be used. In case diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 9c1874d590..525304f5cb 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.183 2005/06/10 22:25:36 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.184 2005/06/13 23:14:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -87,7 +87,8 @@ static Const *string_to_const(const char *str, Oid datatype); * * To be considered for an index scan, an index must match one or more * restriction clauses or join clauses from the query's qual condition, - * or match the query's ORDER BY condition. + * or match the query's ORDER BY condition, or have a predicate that + * matches the query's qual condition. * * There are two basic kinds of index scans. A "plain" index scan uses * only restriction clauses (possibly none at all) in its indexqual, @@ -210,6 +211,7 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel) * 'clauses' is the current list of clauses (RestrictInfo nodes) * 'outer_clauses' is the list of additional upper-level clauses * 'istoplevel' is true if clauses are the rel's top-level restriction list + * (outer_clauses must be NIL when this is true) * 'isjoininner' is true if forming an inner indexscan (so some of the * given clauses are join clauses) * 'outer_relids' identifies the outer side of the join (pass NULL @@ -295,13 +297,12 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel, * selectivity of the predicate might alone make the index useful. * * Note: not all index AMs support scans with no restriction clauses. - * We assume here that the AM does so if and only if it supports - * ordered scans. (It would probably be better if there were a - * specific flag for this in pg_am, but there's not.) + * We can't generate a scan over an index with amoptionalkey = false + * unless there's at least one restriction clause. */ if (restrictclauses != NIL || - useful_pathkeys != NIL || - (index->indpred != NIL && index_is_ordered)) + (index->amoptionalkey && + (useful_pathkeys != NIL || index->indpred != NIL))) { ipath = create_index_path(root, index, restrictclauses, @@ -608,6 +609,11 @@ bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths) * group_clauses_by_indexkey * Find restriction clauses that can be used with an index. * + * Returns a list of sublists of RestrictInfo nodes for clauses that can be + * used with this index. Each sublist contains clauses that can be used + * with one index key (in no particular order); the top list is ordered by + * index key. (This is depended on by expand_indexqual_conditions().) + * * As explained in the comments for find_usable_indexes(), we can use * clauses from either of the given lists, but the result is required to * use at least one clause from the "current clauses" list. We return @@ -616,18 +622,14 @@ bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths) * outer_relids determines what Vars will be allowed on the other side * of a possible index qual; see match_clause_to_indexcol(). * - * Returns a list of sublists of RestrictInfo nodes for clauses that can be - * used with this index. Each sublist contains clauses that can be used - * with one index key (in no particular order); the top list is ordered by - * index key. (This is depended on by expand_indexqual_conditions().) + * If the index has amoptionalkey = false, we give up and return NIL when + * there are no restriction clauses matching the first index key. Otherwise, + * we return NIL if there are no restriction clauses matching any index key. + * A non-NIL result will have one (possibly empty) sublist for each index key. * - * Note that in a multi-key index, we stop if we find a key that cannot be - * used with any clause. For example, given an index on (A,B,C), we might - * return ((C1 C2) (C3 C4)) if we find that clauses C1 and C2 use column A, - * clauses C3 and C4 use column B, and no clauses use column C. But if - * no clauses match B we will return ((C1 C2)), whether or not there are - * clauses matching column C, because the executor couldn't use them anyway. - * Therefore, there are no empty sublists in the result. + * Example: given an index on (A,B,C), we would return ((C1 C2) () (C3 C4)) + * if we find that clauses C1 and C2 use column A, clauses C3 and C4 use + * column C, and no clauses use column B. */ List * group_clauses_by_indexkey(IndexOptInfo *index, @@ -680,11 +682,10 @@ group_clauses_by_indexkey(IndexOptInfo *index, } /* - * If no clauses match this key, we're done; we don't want to look - * at keys to its right. + * If no clauses match this key, check for amoptionalkey restriction. */ - if (clausegroup == NIL) - break; + if (clausegroup == NIL && !index->amoptionalkey && indexcol == 0) + return NIL; clausegroup_list = lappend(clausegroup_list, clausegroup); @@ -1581,11 +1582,9 @@ match_special_index_operator(Expr *clause, Oid opclass, * will know what to do with. * * The input list is ordered by index key, and so the output list is too. - * (The latter is not depended on by any part of the planner, so far as I can - * tell; but some parts of the executor do assume that the indexqual list - * ultimately delivered to the executor is so ordered. One such place is - * _bt_preprocess_keys() in the btree support. Perhaps that ought to be fixed - * someday --- tgl 7/00) + * (The latter is not depended on by any part of the core planner, I believe, + * but parts of the executor require it, and so do the amcostestimate + * functions.) */ List * expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups) diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index a8dcdae9a2..067e9f701c 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.111 2005/06/05 22:32:56 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.112 2005/06/13 23:14:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -161,7 +161,8 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel) } info->relam = indexRelation->rd_rel->relam; - info->amcostestimate = index_cost_estimator(indexRelation); + info->amcostestimate = indexRelation->rd_am->amcostestimate; + info->amoptionalkey = indexRelation->rd_am->amoptionalkey; /* * Fetch the ordering operators associated with the index, if diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 0b03f27c39..204d37bf41 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.181 2005/06/10 22:25:36 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.182 2005/06/13 23:14:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -4195,18 +4195,23 @@ string_to_bytea_const(const char *str, size_t str_len) * don't have any better idea about how to estimate. Index-type-specific * knowledge can be incorporated in the type-specific routines. * + * One bit of index-type-specific knowledge we can relatively easily use + * in genericcostestimate is the estimate of the number of index tuples + * visited. If numIndexTuples is not 0 then it is used as the estimate, + * otherwise we compute a generic estimate. + * *------------------------------------------------------------------------- */ static void genericcostestimate(PlannerInfo *root, IndexOptInfo *index, List *indexQuals, + double numIndexTuples, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, double *indexCorrelation) { - double numIndexTuples; double numIndexPages; QualCost index_qual_cost; double qual_op_cost; @@ -4254,20 +4259,20 @@ genericcostestimate(PlannerInfo *root, JOIN_INNER); /* - * Estimate the number of tuples that will be visited. We do it in - * this rather peculiar-looking way in order to get the right answer - * for partial indexes. We can bound the number of tuples by the - * index size, in any case. + * If caller didn't give us an estimate, estimate the number of index + * tuples that will be visited. We do it in this rather peculiar-looking + * way in order to get the right answer for partial indexes. */ - numIndexTuples = *indexSelectivity * index->rel->tuples; - - if (numIndexTuples > index->tuples) - numIndexTuples = index->tuples; + if (numIndexTuples <= 0.0) + numIndexTuples = *indexSelectivity * index->rel->tuples; /* - * Always estimate at least one tuple is touched, even when + * We can bound the number of tuples by the index size in any case. + * Also, always estimate at least one tuple is touched, even when * indexSelectivity estimate is tiny. */ + if (numIndexTuples > index->tuples) + numIndexTuples = index->tuples; if (numIndexTuples < 1.0) numIndexTuples = 1.0; @@ -4337,8 +4342,95 @@ btcostestimate(PG_FUNCTION_ARGS) Oid relid; AttrNumber colnum; HeapTuple tuple; + double numIndexTuples; + List *indexBoundQuals; + int indexcol; + bool eqQualHere; + ListCell *l; - genericcostestimate(root, index, indexQuals, + /* + * For a btree scan, only leading '=' quals plus inequality quals + * for the immediately next attribute contribute to index selectivity + * (these are the "boundary quals" that determine the starting and + * stopping points of the index scan). Additional quals can suppress + * visits to the heap, so it's OK to count them in indexSelectivity, + * but they should not count for estimating numIndexTuples. So we must + * examine the given indexQuals to find out which ones count as boundary + * quals. We rely on the knowledge that they are given in index column + * order. + */ + indexBoundQuals = NIL; + indexcol = 0; + eqQualHere = false; + foreach(l, indexQuals) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(l); + Expr *clause; + Oid clause_op; + int op_strategy; + + Assert(IsA(rinfo, RestrictInfo)); + clause = rinfo->clause; + Assert(IsA(clause, OpExpr)); + clause_op = ((OpExpr *) clause)->opno; + if (match_index_to_operand(get_leftop(clause), indexcol, index)) + { + /* clause_op is correct */ + } + else if (match_index_to_operand(get_rightop(clause), indexcol, index)) + { + /* Must flip operator to get the opclass member */ + clause_op = get_commutator(clause_op); + } + else + { + /* Must be past the end of quals for indexcol, try next */ + if (!eqQualHere) + break; /* done if no '=' qual for indexcol */ + indexcol++; + eqQualHere = false; + if (match_index_to_operand(get_leftop(clause), indexcol, index)) + { + /* clause_op is correct */ + } + else if (match_index_to_operand(get_rightop(clause), + indexcol, index)) + { + /* Must flip operator to get the opclass member */ + clause_op = get_commutator(clause_op); + } + else + { + /* No quals for new indexcol, so we are done */ + break; + } + } + op_strategy = get_op_opclass_strategy(clause_op, + index->classlist[indexcol]); + Assert(op_strategy != 0); /* not a member of opclass?? */ + if (op_strategy == BTEqualStrategyNumber) + eqQualHere = true; + indexBoundQuals = lappend(indexBoundQuals, rinfo); + } + + /* + * If index is unique and we found an '=' clause for each column, + * we can just assume numIndexTuples = 1 and skip the expensive + * clauselist_selectivity calculations. + */ + if (index->unique && indexcol == index->ncolumns - 1 && eqQualHere) + numIndexTuples = 1.0; + else + { + Selectivity btreeSelectivity; + + btreeSelectivity = clauselist_selectivity(root, indexBoundQuals, + index->rel->relid, + JOIN_INNER); + numIndexTuples = btreeSelectivity * index->rel->tuples; + } + + genericcostestimate(root, index, indexQuals, numIndexTuples, indexStartupCost, indexTotalCost, indexSelectivity, indexCorrelation); @@ -4414,7 +4506,7 @@ rtcostestimate(PG_FUNCTION_ARGS) Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5); double *indexCorrelation = (double *) PG_GETARG_POINTER(6); - genericcostestimate(root, index, indexQuals, + genericcostestimate(root, index, indexQuals, 0.0, indexStartupCost, indexTotalCost, indexSelectivity, indexCorrelation); @@ -4432,7 +4524,7 @@ hashcostestimate(PG_FUNCTION_ARGS) Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5); double *indexCorrelation = (double *) PG_GETARG_POINTER(6); - genericcostestimate(root, index, indexQuals, + genericcostestimate(root, index, indexQuals, 0.0, indexStartupCost, indexTotalCost, indexSelectivity, indexCorrelation); @@ -4450,7 +4542,7 @@ gistcostestimate(PG_FUNCTION_ARGS) Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5); double *indexCorrelation = (double *) PG_GETARG_POINTER(6); - genericcostestimate(root, index, indexQuals, + genericcostestimate(root, index, indexQuals, 0.0, indexStartupCost, indexTotalCost, indexSelectivity, indexCorrelation); diff --git a/src/include/access/genam.h b/src/include/access/genam.h index 3ad0a3d4b2..1ed42f8be6 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.51 2005/05/27 23:31:21 tgl Exp $ + * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.52 2005/06/13 23:14:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -101,7 +101,6 @@ extern IndexBulkDeleteResult *index_bulk_delete(Relation indexRelation, extern IndexBulkDeleteResult *index_vacuum_cleanup(Relation indexRelation, IndexVacuumCleanupInfo *info, IndexBulkDeleteResult *stats); -extern RegProcedure index_cost_estimator(Relation indexRelation); extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum); extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum, diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index ee8b3aee0a..969c0e8329 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.274 2005/06/13 02:26:50 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.275 2005/06/13 23:14:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200506121 +#define CATALOG_VERSION_NO 200506131 #endif diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h index bddfda993b..f473277b46 100644 --- a/src/include/catalog/pg_am.h +++ b/src/include/catalog/pg_am.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.33 2005/04/14 01:38:20 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.34 2005/06/13 23:14:49 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -48,6 +48,7 @@ CATALOG(pg_am,2601) * Zero if AM is not ordered. */ bool amcanunique; /* does AM support UNIQUE indexes? */ bool amcanmulticol; /* does AM support multi-column indexes? */ + bool amoptionalkey; /* can query omit key for the first column? */ bool amindexnulls; /* does AM support NULL index entries? */ bool amconcurrent; /* does AM support concurrent updates? */ regproc aminsert; /* "insert this tuple" function */ @@ -75,42 +76,43 @@ typedef FormData_pg_am *Form_pg_am; * compiler constants for pg_am * ---------------- */ -#define Natts_pg_am 20 +#define Natts_pg_am 21 #define Anum_pg_am_amname 1 #define Anum_pg_am_amstrategies 2 #define Anum_pg_am_amsupport 3 #define Anum_pg_am_amorderstrategy 4 #define Anum_pg_am_amcanunique 5 #define Anum_pg_am_amcanmulticol 6 -#define Anum_pg_am_amindexnulls 7 -#define Anum_pg_am_amconcurrent 8 -#define Anum_pg_am_aminsert 9 -#define Anum_pg_am_ambeginscan 10 -#define Anum_pg_am_amgettuple 11 -#define Anum_pg_am_amgetmulti 12 -#define Anum_pg_am_amrescan 13 -#define Anum_pg_am_amendscan 14 -#define Anum_pg_am_ammarkpos 15 -#define Anum_pg_am_amrestrpos 16 -#define Anum_pg_am_ambuild 17 -#define Anum_pg_am_ambulkdelete 18 -#define Anum_pg_am_amvacuumcleanup 19 -#define Anum_pg_am_amcostestimate 20 +#define Anum_pg_am_amoptionalkey 7 +#define Anum_pg_am_amindexnulls 8 +#define Anum_pg_am_amconcurrent 9 +#define Anum_pg_am_aminsert 10 +#define Anum_pg_am_ambeginscan 11 +#define Anum_pg_am_amgettuple 12 +#define Anum_pg_am_amgetmulti 13 +#define Anum_pg_am_amrescan 14 +#define Anum_pg_am_amendscan 15 +#define Anum_pg_am_ammarkpos 16 +#define Anum_pg_am_amrestrpos 17 +#define Anum_pg_am_ambuild 18 +#define Anum_pg_am_ambulkdelete 19 +#define Anum_pg_am_amvacuumcleanup 20 +#define Anum_pg_am_amcostestimate 21 /* ---------------- * initial contents of pg_am * ---------------- */ -DATA(insert OID = 402 ( rtree 8 3 0 f f f f rtinsert rtbeginscan rtgettuple rtgetmulti rtrescan rtendscan rtmarkpos rtrestrpos rtbuild rtbulkdelete - rtcostestimate )); +DATA(insert OID = 402 ( rtree 8 3 0 f f f f f rtinsert rtbeginscan rtgettuple rtgetmulti rtrescan rtendscan rtmarkpos rtrestrpos rtbuild rtbulkdelete - rtcostestimate )); DESCR("r-tree index access method"); -DATA(insert OID = 403 ( btree 5 1 1 t t t t btinsert btbeginscan btgettuple btgetmulti btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate )); +DATA(insert OID = 403 ( btree 5 1 1 t t t t t btinsert btbeginscan btgettuple btgetmulti btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate )); DESCR("b-tree index access method"); #define BTREE_AM_OID 403 -DATA(insert OID = 405 ( hash 1 1 0 f f f t hashinsert hashbeginscan hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete - hashcostestimate )); +DATA(insert OID = 405 ( hash 1 1 0 f f f f t hashinsert hashbeginscan hashgettuple hashgetmulti hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete - hashcostestimate )); DESCR("hash index access method"); #define HASH_AM_OID 405 -DATA(insert OID = 783 ( gist 100 7 0 f t f f gistinsert gistbeginscan gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete - gistcostestimate )); +DATA(insert OID = 783 ( gist 100 7 0 f t f f f gistinsert gistbeginscan gistgettuple gistgetmulti gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete - gistcostestimate )); DESCR("GiST index access method"); #define GIST_AM_OID 783 diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 687ef36784..0a2ca0e5f3 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.114 2005/06/10 03:32:25 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.115 2005/06/13 23:14:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -328,6 +328,7 @@ typedef struct IndexOptInfo bool predOK; /* true if predicate matches query */ bool unique; /* true if a unique index */ + bool amoptionalkey; /* can query omit key for the first column? */ } IndexOptInfo;