mirror of
https://github.com/postgres/postgres.git
synced 2025-11-19 13:42:17 +03:00
Partial indexes work again, courtesy of Martijn van Oosterhout.
Note: I didn't force an initdb, figuring that one today was enough. However, there is a new function in pg_proc.h, and pg_dump won't be able to dump partial indexes until you add that function.
This commit is contained in:
@@ -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.111 2001/07/15 22:48:16 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.112 2001/07/16 05:06:57 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1100,8 +1100,9 @@ index_register(char *heap,
|
||||
newind->il_info = (IndexInfo *) palloc(sizeof(IndexInfo));
|
||||
|
||||
memcpy(newind->il_info, indexInfo, sizeof(IndexInfo));
|
||||
/* predicate will likely be null anyway, but may as well copy it */
|
||||
newind->il_info->ii_Predicate = copyObject(indexInfo->ii_Predicate);
|
||||
/* predicate will likely be null, but may as well copy it */
|
||||
newind->il_info->ii_Predicate = (List *)
|
||||
copyObject(indexInfo->ii_Predicate);
|
||||
|
||||
newind->il_next = ILHead;
|
||||
ILHead = newind;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.156 2001/07/15 22:48:17 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.157 2001/07/16 05:06:57 tgl Exp $
|
||||
*
|
||||
*
|
||||
* INTERFACE ROUTINES
|
||||
@@ -510,7 +510,7 @@ UpdateIndexRelation(Oid indexoid,
|
||||
* allocate a Form_pg_index big enough to hold the index-predicate (if
|
||||
* any) in string form
|
||||
*/
|
||||
if (indexInfo->ii_Predicate != NULL)
|
||||
if (indexInfo->ii_Predicate != NIL)
|
||||
{
|
||||
predString = nodeToString(indexInfo->ii_Predicate);
|
||||
predText = DatumGetTextP(DirectFunctionCall1(textin,
|
||||
@@ -586,87 +586,6 @@ UpdateIndexRelation(Oid indexoid,
|
||||
heap_freetuple(tuple);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* UpdateIndexPredicate
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
UpdateIndexPredicate(Oid indexoid, Node *oldPred, Node *predicate)
|
||||
{
|
||||
Node *newPred;
|
||||
char *predString;
|
||||
text *predText;
|
||||
Relation pg_index;
|
||||
HeapTuple tuple;
|
||||
HeapTuple newtup;
|
||||
int i;
|
||||
Datum values[Natts_pg_index];
|
||||
char nulls[Natts_pg_index];
|
||||
char replace[Natts_pg_index];
|
||||
|
||||
/*
|
||||
* Construct newPred as a CNF expression equivalent to the OR of the
|
||||
* original partial-index predicate ("oldPred") and the extension
|
||||
* predicate ("predicate").
|
||||
*
|
||||
* This should really try to process the result to change things like
|
||||
* "a>2 OR a>1" to simply "a>1", but for now all it does is make sure
|
||||
* that if the extension predicate is NULL (i.e., it is being extended
|
||||
* to be a complete index), then newPred will be NULL - in effect,
|
||||
* changing "a>2 OR TRUE" to "TRUE". --Nels, Jan '93
|
||||
*/
|
||||
newPred = NULL;
|
||||
if (predicate != NULL)
|
||||
{
|
||||
newPred = (Node *) make_orclause(lcons(make_andclause((List *) predicate),
|
||||
lcons(make_andclause((List *) oldPred),
|
||||
NIL)));
|
||||
newPred = (Node *) cnfify((Expr *) newPred, true);
|
||||
}
|
||||
|
||||
/* translate the index-predicate to string form */
|
||||
if (newPred != NULL)
|
||||
{
|
||||
predString = nodeToString(newPred);
|
||||
predText = DatumGetTextP(DirectFunctionCall1(textin,
|
||||
CStringGetDatum(predString)));
|
||||
pfree(predString);
|
||||
}
|
||||
else
|
||||
predText = DatumGetTextP(DirectFunctionCall1(textin,
|
||||
CStringGetDatum("")));
|
||||
|
||||
/* open the index system catalog relation */
|
||||
pg_index = heap_openr(IndexRelationName, RowExclusiveLock);
|
||||
|
||||
tuple = SearchSysCache(INDEXRELID,
|
||||
ObjectIdGetDatum(indexoid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "UpdateIndexPredicate: cache lookup failed for index %u",
|
||||
indexoid);
|
||||
|
||||
for (i = 0; i < Natts_pg_index; i++)
|
||||
{
|
||||
nulls[i] = heap_attisnull(tuple, i + 1) ? 'n' : ' ';
|
||||
replace[i] = ' ';
|
||||
values[i] = (Datum) NULL;
|
||||
}
|
||||
|
||||
replace[Anum_pg_index_indpred - 1] = 'r';
|
||||
values[Anum_pg_index_indpred - 1] = PointerGetDatum(predText);
|
||||
|
||||
newtup = heap_modifytuple(tuple, pg_index, values, nulls, replace);
|
||||
|
||||
simple_heap_update(pg_index, &newtup->t_self, newtup);
|
||||
|
||||
heap_freetuple(newtup);
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
heap_close(pg_index, RowExclusiveLock);
|
||||
pfree(predText);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* InitIndexStrategy
|
||||
*
|
||||
@@ -1084,7 +1003,7 @@ BuildIndexInfo(HeapTuple indexTuple)
|
||||
pfree(predString);
|
||||
}
|
||||
else
|
||||
ii->ii_Predicate = NULL;
|
||||
ii->ii_Predicate = NIL;
|
||||
|
||||
/* Other info */
|
||||
ii->ii_Unique = indexStruct->indisunique;
|
||||
@@ -1684,7 +1603,7 @@ IndexBuildHeapScan(Relation heapRelation,
|
||||
Datum attdata[INDEX_MAX_KEYS];
|
||||
char nulls[INDEX_MAX_KEYS];
|
||||
double reltuples;
|
||||
Node *predicate = indexInfo->ii_Predicate;
|
||||
List *predicate = indexInfo->ii_Predicate;
|
||||
TupleTable tupleTable;
|
||||
TupleTableSlot *slot;
|
||||
ExprContext *econtext;
|
||||
@@ -1708,7 +1627,7 @@ IndexBuildHeapScan(Relation heapRelation,
|
||||
* We construct the ExprContext anyway since we need a per-tuple
|
||||
* temporary memory context for function evaluation -- tgl July 00
|
||||
*/
|
||||
if (predicate != NULL)
|
||||
if (predicate != NIL)
|
||||
{
|
||||
tupleTable = ExecCreateTupleTable(1);
|
||||
slot = ExecAllocTableSlot(tupleTable);
|
||||
@@ -1831,12 +1750,12 @@ IndexBuildHeapScan(Relation heapRelation,
|
||||
* VACUUM doesn't complain about tuple count mismatch for partial
|
||||
* indexes.
|
||||
*/
|
||||
if (predicate != NULL)
|
||||
if (predicate != NIL)
|
||||
{
|
||||
if (! tupleIsAlive)
|
||||
continue;
|
||||
ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
|
||||
if (!ExecQual((List *) predicate, econtext, false))
|
||||
if (!ExecQual(predicate, econtext, false))
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1865,7 +1784,7 @@ IndexBuildHeapScan(Relation heapRelation,
|
||||
|
||||
heap_endscan(scan);
|
||||
|
||||
if (predicate != NULL)
|
||||
if (predicate != NIL)
|
||||
ExecDropTupleTable(tupleTable, true);
|
||||
FreeExprContext(econtext);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.135 2001/07/15 22:48:17 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.136 2001/07/16 05:06:57 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* The PerformAddAttribute() code, like most of the relation
|
||||
@@ -1882,7 +1882,7 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
|
||||
indexInfo->ii_NumIndexAttrs = 1;
|
||||
indexInfo->ii_NumKeyAttrs = 1;
|
||||
indexInfo->ii_KeyAttrNumbers[0] = 1;
|
||||
indexInfo->ii_Predicate = NULL;
|
||||
indexInfo->ii_Predicate = NIL;
|
||||
indexInfo->ii_FuncOid = InvalidOid;
|
||||
indexInfo->ii_Unique = false;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.51 2001/07/15 22:48:17 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.52 2001/07/16 05:06:57 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -45,8 +45,6 @@
|
||||
|
||||
/* non-export function prototypes */
|
||||
static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid);
|
||||
static void CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid);
|
||||
static void CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid);
|
||||
static void FuncIndexArgs(IndexInfo *indexInfo, Oid *classOidP,
|
||||
IndexElem *funcIndex,
|
||||
Oid relId,
|
||||
@@ -144,12 +142,8 @@ DefineIndex(char *heapRelationName,
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the partial-index predicate from parsetree form to plan
|
||||
* form, so it can be readily evaluated during index creation. Note:
|
||||
* "predicate" comes in as a list containing (1) the predicate itself
|
||||
* (a where_clause), and (2) a corresponding range table.
|
||||
*
|
||||
* [(1) is 'predicate' and (2) is 'rangetable' now. - ay 10/94]
|
||||
* Convert the partial-index predicate from parsetree form to
|
||||
* an implicit-AND qual expression, for easier evaluation at runtime.
|
||||
*/
|
||||
if (predicate != NULL && rangetable != NIL)
|
||||
{
|
||||
@@ -166,7 +160,7 @@ DefineIndex(char *heapRelationName,
|
||||
* structure
|
||||
*/
|
||||
indexInfo = makeNode(IndexInfo);
|
||||
indexInfo->ii_Predicate = (Node *) cnfPred;
|
||||
indexInfo->ii_Predicate = cnfPred;
|
||||
indexInfo->ii_FuncOid = InvalidOid;
|
||||
indexInfo->ii_Unique = unique;
|
||||
|
||||
@@ -218,155 +212,30 @@ DefineIndex(char *heapRelationName,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ExtendIndex
|
||||
* Extends a partial index.
|
||||
*/
|
||||
void
|
||||
ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable)
|
||||
{
|
||||
Relation heapRelation;
|
||||
Relation indexRelation;
|
||||
Oid accessMethodId,
|
||||
indexId,
|
||||
relationId;
|
||||
HeapTuple tuple;
|
||||
Form_pg_index index;
|
||||
List *cnfPred = NIL;
|
||||
IndexInfo *indexInfo;
|
||||
Node *oldPred;
|
||||
|
||||
/*
|
||||
* Get index's relation id and access method id from pg_class
|
||||
*/
|
||||
tuple = SearchSysCache(RELNAME,
|
||||
PointerGetDatum(indexRelationName),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "ExtendIndex: index \"%s\" not found",
|
||||
indexRelationName);
|
||||
indexId = tuple->t_data->t_oid;
|
||||
accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam;
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/*
|
||||
* Extract info from the pg_index tuple for the index
|
||||
*/
|
||||
tuple = SearchSysCache(INDEXRELID,
|
||||
ObjectIdGetDatum(indexId),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "ExtendIndex: relation \"%s\" is not an index",
|
||||
indexRelationName);
|
||||
index = (Form_pg_index) GETSTRUCT(tuple);
|
||||
Assert(index->indexrelid == indexId);
|
||||
relationId = index->indrelid;
|
||||
indexInfo = BuildIndexInfo(tuple);
|
||||
oldPred = indexInfo->ii_Predicate;
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
if (oldPred == NULL)
|
||||
elog(ERROR, "ExtendIndex: \"%s\" is not a partial index",
|
||||
indexRelationName);
|
||||
|
||||
/*
|
||||
* Convert the extension predicate from parsetree form to plan form,
|
||||
* so it can be readily evaluated during index creation. Note:
|
||||
* "predicate" comes in two parts (1) the predicate expression itself,
|
||||
* and (2) a corresponding range table.
|
||||
*
|
||||
* XXX I think this code is broken --- index_build expects a single
|
||||
* expression not a list --- tgl Jul 00
|
||||
*/
|
||||
if (rangetable != NIL)
|
||||
{
|
||||
cnfPred = cnfify((Expr *) copyObject(predicate), true);
|
||||
fix_opids((Node *) cnfPred);
|
||||
CheckPredicate(cnfPred, rangetable, relationId);
|
||||
}
|
||||
|
||||
/* pass new predicate to index_build */
|
||||
indexInfo->ii_Predicate = (Node *) cnfPred;
|
||||
|
||||
/* Open heap and index rels, and get suitable locks */
|
||||
heapRelation = heap_open(relationId, ShareLock);
|
||||
indexRelation = index_open(indexId);
|
||||
|
||||
/* Obtain exclusive lock on it, just to be sure */
|
||||
LockRelation(indexRelation, AccessExclusiveLock);
|
||||
|
||||
InitIndexStrategy(indexInfo->ii_NumIndexAttrs,
|
||||
indexRelation, accessMethodId);
|
||||
|
||||
/*
|
||||
* XXX currently BROKEN: if we want to support EXTEND INDEX, oldPred
|
||||
* needs to be passed through to IndexBuildHeapScan. We could do this
|
||||
* without help from the index AMs if we added an oldPred field to the
|
||||
* IndexInfo struct. Currently I'm expecting that EXTEND INDEX will
|
||||
* get removed, so I'm not going to do that --- tgl 7/14/01
|
||||
*/
|
||||
|
||||
index_build(heapRelation, indexRelation, indexInfo);
|
||||
|
||||
/* heap and index rels are closed as a side-effect of index_build */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* CheckPredicate
|
||||
* Checks that the given list of partial-index predicates refer
|
||||
* (via the given range table) only to the given base relation oid,
|
||||
* and that they're in a form the planner can handle, i.e.,
|
||||
* boolean combinations of "ATTR OP CONST" (yes, for now, the ATTR
|
||||
* has to be on the left).
|
||||
* (via the given range table) only to the given base relation oid.
|
||||
*
|
||||
* This used to also constrain the form of the predicate to forms that
|
||||
* indxpath.c could do something with. However, that seems overly
|
||||
* restrictive. One useful application of partial indexes is to apply
|
||||
* a UNIQUE constraint across a subset of a table, and in that scenario
|
||||
* 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)
|
||||
{
|
||||
List *item;
|
||||
|
||||
foreach(item, predList)
|
||||
CheckPredExpr(lfirst(item), rangeTable, baseRelOid);
|
||||
}
|
||||
|
||||
static void
|
||||
CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid)
|
||||
{
|
||||
List *clauses = NIL,
|
||||
*clause;
|
||||
|
||||
if (is_opclause(predicate))
|
||||
{
|
||||
CheckPredClause((Expr *) predicate, rangeTable, baseRelOid);
|
||||
return;
|
||||
}
|
||||
else if (or_clause(predicate) || and_clause(predicate))
|
||||
clauses = ((Expr *) predicate)->args;
|
||||
else
|
||||
elog(ERROR, "Unsupported partial-index predicate expression type");
|
||||
|
||||
foreach(clause, clauses)
|
||||
CheckPredExpr(lfirst(clause), rangeTable, baseRelOid);
|
||||
}
|
||||
|
||||
static void
|
||||
CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid)
|
||||
{
|
||||
Var *pred_var;
|
||||
Const *pred_const;
|
||||
|
||||
pred_var = (Var *) get_leftop(predicate);
|
||||
pred_const = (Const *) get_rightop(predicate);
|
||||
|
||||
if (!IsA(predicate->oper, Oper) ||
|
||||
!IsA(pred_var, Var) ||
|
||||
!IsA(pred_const, Const))
|
||||
elog(ERROR, "Unsupported partial-index predicate clause type");
|
||||
|
||||
if (getrelid(pred_var->varno, rangeTable) != baseRelOid)
|
||||
if (length(rangeTable) != 1 || getrelid(1, rangeTable) != baseRelOid)
|
||||
elog(ERROR,
|
||||
"Partial-index predicates may refer only to the base relation");
|
||||
"Partial-index predicates may refer only to the base relation");
|
||||
|
||||
if (contain_subplans((Node *) predList))
|
||||
elog(ERROR, "Cannot use subselect in index predicate");
|
||||
if (contain_agg_clause((Node *) predList))
|
||||
elog(ERROR, "Cannot use aggregate in index predicate");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.76 2001/07/15 22:48:17 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.77 2001/07/16 05:06:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -641,7 +641,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
|
||||
for (i = 0; i < numIndices; i++)
|
||||
{
|
||||
IndexInfo *indexInfo;
|
||||
Node *predicate;
|
||||
List *predicate;
|
||||
InsertIndexResult result;
|
||||
|
||||
if (relationDescs[i] == NULL)
|
||||
@@ -649,10 +649,10 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
|
||||
|
||||
indexInfo = indexInfoArray[i];
|
||||
predicate = indexInfo->ii_Predicate;
|
||||
if (predicate != NULL)
|
||||
if (predicate != NIL)
|
||||
{
|
||||
/* Skip this index-update if the predicate isn't satisfied */
|
||||
if (!ExecQual((List *) predicate, econtext, false))
|
||||
if (!ExecQual(predicate, econtext, false))
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.108 2001/06/25 21:11:43 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.109 2001/07/16 05:06:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -195,8 +195,13 @@ create_index_paths(Query *root, RelOptInfo *rel)
|
||||
* 4. Generate an indexscan path if there are relevant restriction
|
||||
* clauses OR the index ordering is potentially useful for later
|
||||
* merging or final output ordering.
|
||||
*
|
||||
* If there is a predicate, consider it anyway since the index
|
||||
* predicate has already been found to match the query.
|
||||
*/
|
||||
if (restrictclauses != NIL || useful_pathkeys != NIL)
|
||||
if (restrictclauses != NIL ||
|
||||
useful_pathkeys != NIL ||
|
||||
index->indpred != NIL)
|
||||
add_path(rel, (Path *)
|
||||
create_index_path(root, rel, index,
|
||||
restrictclauses,
|
||||
@@ -974,18 +979,18 @@ pred_test(List *predicate_list, List *restrictinfo_list, List *joininfo_list)
|
||||
* clauses (those in restrictinfo_list). --Nels, Dec '92
|
||||
*/
|
||||
|
||||
if (predicate_list == NULL)
|
||||
if (predicate_list == NIL)
|
||||
return true; /* no predicate: the index is usable */
|
||||
if (restrictinfo_list == NULL)
|
||||
if (restrictinfo_list == NIL)
|
||||
return false; /* no restriction clauses: the test must
|
||||
* fail */
|
||||
|
||||
foreach(pred, predicate_list)
|
||||
{
|
||||
|
||||
/*
|
||||
* if any clause is not implied, the whole predicate is not
|
||||
* implied
|
||||
* implied. Note that checking for sub-ANDs here is redundant
|
||||
* if the predicate has been cnfify()-ed.
|
||||
*/
|
||||
if (and_clause(lfirst(pred)))
|
||||
{
|
||||
@@ -1011,15 +1016,16 @@ pred_test(List *predicate_list, List *restrictinfo_list, List *joininfo_list)
|
||||
static bool
|
||||
one_pred_test(Expr *predicate, List *restrictinfo_list)
|
||||
{
|
||||
RestrictInfo *restrictinfo;
|
||||
List *item;
|
||||
|
||||
Assert(predicate != NULL);
|
||||
foreach(item, restrictinfo_list)
|
||||
{
|
||||
restrictinfo = (RestrictInfo *) lfirst(item);
|
||||
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(item);
|
||||
|
||||
/* if any clause implies the predicate, return true */
|
||||
if (one_pred_clause_expr_test(predicate, (Node *) restrictinfo->clause))
|
||||
if (one_pred_clause_expr_test(predicate,
|
||||
(Node *) restrictinfo->clause))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1055,7 +1061,6 @@ one_pred_clause_expr_test(Expr *predicate, Node *clause)
|
||||
items = ((Expr *) clause)->args;
|
||||
foreach(item, items)
|
||||
{
|
||||
|
||||
/*
|
||||
* if any AND item implies the predicate, the whole clause
|
||||
* does
|
||||
@@ -1102,7 +1107,6 @@ one_pred_clause_test(Expr *predicate, Node *clause)
|
||||
items = predicate->args;
|
||||
foreach(item, items)
|
||||
{
|
||||
|
||||
/*
|
||||
* if any item is not implied, the whole predicate is not
|
||||
* implied
|
||||
@@ -1177,26 +1181,30 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
|
||||
test_strategy;
|
||||
Oper *test_oper;
|
||||
Expr *test_expr;
|
||||
bool test_result,
|
||||
isNull;
|
||||
Datum test_result;
|
||||
bool isNull;
|
||||
Relation relation;
|
||||
HeapScanDesc scan;
|
||||
HeapTuple tuple;
|
||||
ScanKeyData entry[3];
|
||||
Form_pg_amop aform;
|
||||
ExprContext *econtext;
|
||||
|
||||
/* Check the basic form; for now, only allow the simplest case */
|
||||
/* Note caller already verified is_opclause(predicate) */
|
||||
if (!is_opclause(clause))
|
||||
return false;
|
||||
|
||||
pred_var = (Var *) get_leftop(predicate);
|
||||
pred_const = (Const *) get_rightop(predicate);
|
||||
clause_var = (Var *) get_leftop((Expr *) clause);
|
||||
clause_const = (Const *) get_rightop((Expr *) clause);
|
||||
|
||||
/* Check the basic form; for now, only allow the simplest case */
|
||||
if (!is_opclause(clause) ||
|
||||
!IsA(clause_var, Var) ||
|
||||
if (!IsA(clause_var, Var) ||
|
||||
clause_const == NULL ||
|
||||
!IsA(clause_const, Const) ||
|
||||
!IsA(predicate->oper, Oper) ||
|
||||
!IsA(pred_var, Var) ||
|
||||
pred_const == NULL ||
|
||||
!IsA(pred_const, Const))
|
||||
return false;
|
||||
|
||||
@@ -1211,10 +1219,15 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
|
||||
pred_op = ((Oper *) ((Expr *) predicate)->oper)->opno;
|
||||
clause_op = ((Oper *) ((Expr *) clause)->oper)->opno;
|
||||
|
||||
|
||||
/*
|
||||
* 1. Find a "btree" strategy number for the pred_op
|
||||
*
|
||||
* XXX consider using syscache lookups for these searches. Right
|
||||
* now we don't have caches that match all of the search conditions,
|
||||
* but reconsider it after upcoming restructuring of pg_opclass.
|
||||
*/
|
||||
relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
|
||||
|
||||
ScanKeyEntryInitialize(&entry[0], 0,
|
||||
Anum_pg_amop_amopid,
|
||||
F_OIDEQ,
|
||||
@@ -1225,8 +1238,6 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(pred_op));
|
||||
|
||||
relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
|
||||
|
||||
/*
|
||||
* The following assumes that any given operator will only be in a
|
||||
* single btree operator class. This is true at least for all the
|
||||
@@ -1254,7 +1265,6 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
|
||||
|
||||
heap_endscan(scan);
|
||||
|
||||
|
||||
/*
|
||||
* 2. From the same opclass, find a strategy num for the clause_op
|
||||
*/
|
||||
@@ -1281,13 +1291,12 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
|
||||
|
||||
/* Get the restriction clause operator's strategy number (1 to 5) */
|
||||
clause_strategy = (StrategyNumber) aform->amopstrategy;
|
||||
heap_endscan(scan);
|
||||
|
||||
heap_endscan(scan);
|
||||
|
||||
/*
|
||||
* 3. Look up the "test" strategy number in the implication table
|
||||
*/
|
||||
|
||||
test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1];
|
||||
if (test_strategy == 0)
|
||||
{
|
||||
@@ -1298,7 +1307,6 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
|
||||
/*
|
||||
* 4. From the same opclass, find the operator for the test strategy
|
||||
*/
|
||||
|
||||
ScanKeyEntryInitialize(&entry[2], 0,
|
||||
Anum_pg_amop_amopstrategy,
|
||||
F_INT2EQ,
|
||||
@@ -1329,19 +1337,20 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
|
||||
InvalidOid, /* opid */
|
||||
BOOLOID); /* opresulttype */
|
||||
replace_opid(test_oper);
|
||||
|
||||
test_expr = make_opclause(test_oper,
|
||||
copyObject(clause_const),
|
||||
copyObject(pred_const));
|
||||
(Var *) clause_const,
|
||||
(Var *) pred_const);
|
||||
|
||||
test_result = ExecEvalExpr((Node *) test_expr, NULL, &isNull, NULL);
|
||||
econtext = MakeExprContext(NULL, TransactionCommandContext);
|
||||
test_result = ExecEvalExpr((Node *) test_expr, econtext, &isNull, NULL);
|
||||
FreeExprContext(econtext);
|
||||
|
||||
if (isNull)
|
||||
{
|
||||
elog(DEBUG, "clause_pred_clause_test: null test result");
|
||||
return false;
|
||||
}
|
||||
return test_result;
|
||||
return DatumGetBool(test_result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.74 2001/06/05 05:26:04 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.75 2001/07/16 05:06:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -362,6 +362,13 @@ create_index_path(Query *root,
|
||||
pathnode->alljoinquals = false;
|
||||
pathnode->rows = rel->rows;
|
||||
|
||||
/*
|
||||
* Not sure if this is necessary, but it should help if the
|
||||
* statistics are too far off
|
||||
*/
|
||||
if (index->indpred && index->tuples < pathnode->rows)
|
||||
pathnode->rows = index->tuples;
|
||||
|
||||
cost_index(&pathnode->path, root, rel, index, indexquals, false);
|
||||
|
||||
return pathnode;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.192 2001/07/04 17:36:54 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.193 2001/07/16 05:06:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -45,7 +45,6 @@ static Query *transformStmt(ParseState *pstate, Node *stmt);
|
||||
static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
|
||||
static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
|
||||
static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
|
||||
static Query *transformExtendStmt(ParseState *pstate, ExtendStmt *stmt);
|
||||
static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt);
|
||||
static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
|
||||
static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
|
||||
@@ -148,10 +147,6 @@ transformStmt(ParseState *pstate, Node *parseTree)
|
||||
result = transformIndexStmt(pstate, (IndexStmt *) parseTree);
|
||||
break;
|
||||
|
||||
case T_ExtendStmt:
|
||||
result = transformExtendStmt(pstate, (ExtendStmt *) parseTree);
|
||||
break;
|
||||
|
||||
case T_RuleStmt:
|
||||
result = transformRuleStmt(pstate, (RuleStmt *) parseTree);
|
||||
break;
|
||||
@@ -1630,15 +1625,30 @@ static Query *
|
||||
transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
|
||||
{
|
||||
Query *qry;
|
||||
RangeTblEntry *rte;
|
||||
|
||||
qry = makeNode(Query);
|
||||
qry->commandType = CMD_UTILITY;
|
||||
|
||||
/* take care of the where clause */
|
||||
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
|
||||
if (stmt->whereClause)
|
||||
{
|
||||
/*
|
||||
* Put the parent table into the rtable so that the WHERE clause can
|
||||
* refer to its fields without qualification. Note that this only
|
||||
* works if the parent table already exists --- so we can't easily
|
||||
* support predicates on indexes created implicitly by CREATE TABLE.
|
||||
* Fortunately, that's not necessary.
|
||||
*/
|
||||
rte = addRangeTableEntry(pstate, stmt->relname, NULL, false, true);
|
||||
|
||||
/* no to join list, yes to namespace */
|
||||
addRTEtoQuery(pstate, rte, false, true);
|
||||
|
||||
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
|
||||
}
|
||||
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
|
||||
stmt->rangetable = pstate->p_rtable;
|
||||
|
||||
qry->utilityStmt = (Node *) stmt;
|
||||
@@ -1646,30 +1656,6 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
|
||||
return qry;
|
||||
}
|
||||
|
||||
/*
|
||||
* transformExtendStmt -
|
||||
* transform the qualifications of the Extend Index Statement
|
||||
*
|
||||
*/
|
||||
static Query *
|
||||
transformExtendStmt(ParseState *pstate, ExtendStmt *stmt)
|
||||
{
|
||||
Query *qry;
|
||||
|
||||
qry = makeNode(Query);
|
||||
qry->commandType = CMD_UTILITY;
|
||||
|
||||
/* take care of the where clause */
|
||||
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
|
||||
|
||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||
|
||||
stmt->rangetable = pstate->p_rtable;
|
||||
|
||||
qry->utilityStmt = (Node *) stmt;
|
||||
return qry;
|
||||
}
|
||||
|
||||
/*
|
||||
* transformRuleStmt -
|
||||
* transform a Create Rule Statement. The actions is a list of parse
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.236 2001/07/12 18:02:59 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.237 2001/07/16 05:06:58 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -135,7 +135,7 @@ static void doNegateFloat(Value *v);
|
||||
CreateSchemaStmt, CreateSeqStmt, CreateStmt, CreateTrigStmt,
|
||||
CreateUserStmt, CreatedbStmt, CursorStmt, DefineStmt, DeleteStmt,
|
||||
DropGroupStmt, DropPLangStmt, DropSchemaStmt, DropStmt, DropTrigStmt,
|
||||
DropUserStmt, DropdbStmt, ExplainStmt, ExtendStmt, FetchStmt,
|
||||
DropUserStmt, DropdbStmt, ExplainStmt, FetchStmt,
|
||||
GrantStmt, IndexStmt, InsertStmt, ListenStmt, LoadStmt, LockStmt,
|
||||
NotifyStmt, OptimizableStmt, ProcedureStmt, ReindexStmt,
|
||||
RemoveAggrStmt, RemoveFuncStmt, RemoveOperStmt,
|
||||
@@ -345,7 +345,7 @@ static void doNegateFloat(Value *v);
|
||||
BACKWARD, BEFORE, BINARY, BIT,
|
||||
CACHE, CHECKPOINT, CLUSTER, COMMENT, COPY, CREATEDB, CREATEUSER, CYCLE,
|
||||
DATABASE, DELIMITERS, DO,
|
||||
EACH, ENCODING, EXCLUSIVE, EXPLAIN, EXTEND,
|
||||
EACH, ENCODING, EXCLUSIVE, EXPLAIN,
|
||||
FORCE, FORWARD, FUNCTION, HANDLER,
|
||||
ILIKE, INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL,
|
||||
LANCOMPILER, LIMIT, LISTEN, LOAD, LOCATION, LOCK_P,
|
||||
@@ -450,7 +450,6 @@ stmt : AlterSchemaStmt
|
||||
| DropPLangStmt
|
||||
| DropTrigStmt
|
||||
| DropUserStmt
|
||||
| ExtendStmt
|
||||
| ExplainStmt
|
||||
| FetchStmt
|
||||
| GrantStmt
|
||||
@@ -2392,14 +2391,14 @@ RevokeStmt: REVOKE privileges ON opt_table relation_name_list FROM grantee_list
|
||||
*
|
||||
* QUERY:
|
||||
* create index <indexname> on <relname>
|
||||
* using <access> "(" (<col> with <op>)+ ")" [with
|
||||
* <target_list>]
|
||||
* [ using <access> ] "(" (<col> with <op>)+ ")"
|
||||
* [ with <parameters> ]
|
||||
* [ where <predicate> ]
|
||||
*
|
||||
* [where <qual>] is not supported anymore
|
||||
*****************************************************************************/
|
||||
|
||||
IndexStmt: CREATE index_opt_unique INDEX index_name ON relation_name
|
||||
access_method_clause '(' index_params ')' opt_with
|
||||
access_method_clause '(' index_params ')' opt_with where_clause
|
||||
{
|
||||
IndexStmt *n = makeNode(IndexStmt);
|
||||
n->unique = $2;
|
||||
@@ -2408,7 +2407,7 @@ IndexStmt: CREATE index_opt_unique INDEX index_name ON relation_name
|
||||
n->accessMethod = $7;
|
||||
n->indexParams = $9;
|
||||
n->withClause = $11;
|
||||
n->whereClause = NULL;
|
||||
n->whereClause = $12;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
@@ -2471,22 +2470,6 @@ opt_class: class
|
||||
;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* QUERY:
|
||||
* extend index <indexname> [where <qual>]
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
ExtendStmt: EXTEND INDEX index_name where_clause
|
||||
{
|
||||
ExtendStmt *n = makeNode(ExtendStmt);
|
||||
n->idxname = $3;
|
||||
n->whereClause = $4;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* QUERY:
|
||||
@@ -5775,7 +5758,6 @@ ColLabel: ColId { $$ = $1; }
|
||||
| EXCEPT { $$ = "except"; }
|
||||
| EXISTS { $$ = "exists"; }
|
||||
| EXPLAIN { $$ = "explain"; }
|
||||
| EXTEND { $$ = "extend"; }
|
||||
| EXTRACT { $$ = "extract"; }
|
||||
| FALSE_P { $$ = "false"; }
|
||||
| FLOAT { $$ = "float"; }
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.93 2001/06/19 22:39:11 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.94 2001/07/16 05:06:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -109,7 +109,6 @@ static ScanKeyword ScanKeywords[] = {
|
||||
{"execute", EXECUTE},
|
||||
{"exists", EXISTS},
|
||||
{"explain", EXPLAIN},
|
||||
{"extend", EXTEND},
|
||||
{"extract", EXTRACT},
|
||||
{"false", FALSE_P},
|
||||
{"fetch", FETCH},
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.114 2001/06/18 16:13:21 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.115 2001/07/16 05:06:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -567,18 +567,6 @@ ProcessUtility(Node *parsetree,
|
||||
DefineSequence((CreateSeqStmt *) parsetree);
|
||||
break;
|
||||
|
||||
case T_ExtendStmt:
|
||||
{
|
||||
ExtendStmt *stmt = (ExtendStmt *) parsetree;
|
||||
|
||||
set_ps_display(commandTag = "EXTEND");
|
||||
|
||||
ExtendIndex(stmt->idxname, /* index name */
|
||||
(Expr *) stmt->whereClause, /* where */
|
||||
stmt->rangetable);
|
||||
}
|
||||
break;
|
||||
|
||||
case T_RemoveAggrStmt:
|
||||
{
|
||||
RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* back to source text
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.79 2001/07/10 00:02:02 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.80 2001/07/16 05:06:59 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "catalog/heap.h"
|
||||
#include "catalog/pg_index.h"
|
||||
#include "catalog/pg_operator.h"
|
||||
#include "catalog/pg_shadow.h"
|
||||
@@ -554,6 +555,64 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* get_expr - Decompile an expression tree
|
||||
*
|
||||
* Input: an expression tree in nodeToString form, and a relation OID
|
||||
*
|
||||
* Output: reverse-listed expression
|
||||
*
|
||||
* Currently, the expression can only refer to a single relation, namely
|
||||
* the one specified by the second parameter. This is sufficient for
|
||||
* partial indexes, column default expressions, etc.
|
||||
* ----------
|
||||
*/
|
||||
Datum
|
||||
pg_get_expr(PG_FUNCTION_ARGS)
|
||||
{
|
||||
text *expr = PG_GETARG_TEXT_P(0);
|
||||
Oid relid = PG_GETARG_OID(1);
|
||||
text *result;
|
||||
Node *node;
|
||||
List *context;
|
||||
char *exprstr;
|
||||
char *relname;
|
||||
char *str;
|
||||
|
||||
/* Get the name for the relation */
|
||||
relname = get_rel_name(relid);
|
||||
if (relname == NULL)
|
||||
PG_RETURN_NULL(); /* should we raise an error? */
|
||||
|
||||
/* Convert input TEXT object to C string */
|
||||
exprstr = DatumGetCString(DirectFunctionCall1(textout,
|
||||
PointerGetDatum(expr)));
|
||||
|
||||
/* Convert expression to node tree */
|
||||
node = (Node *) stringToNode(exprstr);
|
||||
|
||||
/*
|
||||
* If top level is a List, assume it is an implicit-AND structure,
|
||||
* and convert to explicit AND. This is needed for partial index
|
||||
* predicates.
|
||||
*/
|
||||
if (node && IsA(node, List))
|
||||
{
|
||||
node = (Node *) make_ands_explicit((List *) node);
|
||||
}
|
||||
|
||||
/* Deparse */
|
||||
context = deparse_context_for(relname, relid);
|
||||
str = deparse_expression(node, context, false);
|
||||
|
||||
/* Pass the result back as TEXT */
|
||||
result = DatumGetTextP(DirectFunctionCall1(textin,
|
||||
CStringGetDatum(str)));
|
||||
|
||||
PG_RETURN_TEXT_P(result);
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* get_userbyid - Get a user name by usesysid and
|
||||
* fallback to 'unknown (UID=n)'
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.94 2001/06/25 21:11:44 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.95 2001/07/16 05:06:59 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -86,6 +86,7 @@
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/plancat.h"
|
||||
#include "optimizer/prep.h"
|
||||
#include "parser/parse_func.h"
|
||||
#include "parser/parse_oper.h"
|
||||
#include "parser/parsetree.h"
|
||||
@@ -2950,24 +2951,63 @@ genericcostestimate(Query *root, RelOptInfo *rel,
|
||||
{
|
||||
double numIndexTuples;
|
||||
double numIndexPages;
|
||||
|
||||
/* Estimate the fraction of main-table tuples that will be visited */
|
||||
*indexSelectivity = clauselist_selectivity(root, indexQuals,
|
||||
lfirsti(rel->relids));
|
||||
|
||||
/* Estimate the number of index tuples that will be visited */
|
||||
numIndexTuples = *indexSelectivity * index->tuples;
|
||||
|
||||
/* Estimate the number of index pages that will be retrieved */
|
||||
numIndexPages = *indexSelectivity * index->pages;
|
||||
List *selectivityQuals = indexQuals;
|
||||
|
||||
/*
|
||||
* Always estimate at least one tuple and page are touched, even when
|
||||
* If the index is partial, AND the index predicate with the explicitly
|
||||
* given indexquals to produce a more accurate idea of the index
|
||||
* restriction. This may produce redundant clauses, which we hope that
|
||||
* cnfify and clauselist_selectivity will deal with intelligently.
|
||||
*
|
||||
* Note that index->indpred and indexQuals are both in implicit-AND
|
||||
* form to start with, which we have to make explicit to hand to
|
||||
* canonicalize_qual, and then we get back implicit-AND form again.
|
||||
*/
|
||||
if (index->indpred != NIL)
|
||||
{
|
||||
Expr *andedQuals;
|
||||
|
||||
andedQuals = make_ands_explicit(nconc(listCopy(index->indpred),
|
||||
indexQuals));
|
||||
selectivityQuals = canonicalize_qual(andedQuals, true);
|
||||
}
|
||||
|
||||
/* Estimate the fraction of main-table tuples that will be visited */
|
||||
*indexSelectivity = clauselist_selectivity(root, selectivityQuals,
|
||||
lfirsti(rel->relids));
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
numIndexTuples = *indexSelectivity * rel->tuples;
|
||||
|
||||
if (numIndexTuples > index->tuples)
|
||||
numIndexTuples = index->tuples;
|
||||
|
||||
/*
|
||||
* Always estimate at least one tuple is touched, even when
|
||||
* indexSelectivity estimate is tiny.
|
||||
*/
|
||||
if (numIndexTuples < 1.0)
|
||||
numIndexTuples = 1.0;
|
||||
if (numIndexPages < 1.0)
|
||||
|
||||
/*
|
||||
* Estimate the number of index pages that will be retrieved.
|
||||
*
|
||||
* For all currently-supported index types, the first page of the index
|
||||
* is a metadata page, and we should figure on fetching that plus a
|
||||
* pro-rated fraction of the remaining pages.
|
||||
*/
|
||||
if (index->pages > 1 && index->tuples > 0)
|
||||
{
|
||||
numIndexPages = (numIndexTuples / index->tuples) * (index->pages - 1);
|
||||
numIndexPages += 1; /* count the metapage too */
|
||||
numIndexPages = ceil(numIndexPages);
|
||||
}
|
||||
else
|
||||
numIndexPages = 1.0;
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user