Commit 24558da1 authored by Tom Lane's avatar Tom Lane

Phase 2 of project to make index operator lossiness be determined at runtime

instead of plan time.  Extend the amgettuple API so that the index AM returns
a boolean indicating whether the indexquals need to be rechecked, and make
that rechecking happen in nodeIndexscan.c (currently the only place where
it's expected to be needed; other callers of index_getnext are just erroring
out for now).  For the moment, GIN and GIST have stub logic that just always
sets the recheck flag to TRUE --- I'm hoping to get Teodor to handle pushing
that control down to the opclass consistent() functions.  The planner no
longer pays any attention to amopreqcheck, and that catalog column will go
away in due course.
parent c22ed3d5
<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.24 2008/04/10 22:25:25 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.25 2008/04/13 19:18:13 tgl Exp $ -->
<chapter id="indexam"> <chapter id="indexam">
<title>Index Access Method Interface Definition</title> <title>Index Access Method Interface Definition</title>
...@@ -315,7 +315,15 @@ amgettuple (IndexScanDesc scan, ...@@ -315,7 +315,15 @@ amgettuple (IndexScanDesc scan,
TID is stored into the <literal>scan</> structure. Note that TID is stored into the <literal>scan</> structure. Note that
<quote>success</> means only that the index contains an entry that matches <quote>success</> means only that the index contains an entry that matches
the scan keys, not that the tuple necessarily still exists in the heap or the scan keys, not that the tuple necessarily still exists in the heap or
will pass the caller's snapshot test. will pass the caller's snapshot test. On success, <function>amgettuple</>
must also set <literal>scan-&gt;xs_recheck</> to TRUE or FALSE.
FALSE means it is certain that the index entry matches the scan keys.
TRUE means this is not certain, and the conditions represented by the
scan keys must be rechecked against the heap tuple after fetching it.
This provision supports <quote>lossy</> index operators.
Note that rechecking will extend only to the scan conditions; a partial
index predicate (if any) is never rechecked by <function>amgettuple</>
callers.
</para> </para>
<para> <para>
...@@ -327,6 +335,14 @@ amgetbitmap (IndexScanDesc scan, ...@@ -327,6 +335,14 @@ amgetbitmap (IndexScanDesc scan,
Fetch all tuples in the given scan and add them to the caller-supplied Fetch all tuples in the given scan and add them to the caller-supplied
TIDBitmap (that is, OR the set of tuple IDs into whatever set is already TIDBitmap (that is, OR the set of tuple IDs into whatever set is already
in the bitmap). The number of tuples fetched is returned. in the bitmap). The number of tuples fetched is returned.
While inserting tuple IDs into the bitmap, <function>amgetbitmap</> can
indicate that rechecking of the scan conditions is required for specific
tuple IDs. This is analogous to the <literal>xs_recheck</> output parameter
of <function>amgettuple</>. Note: in the current implementation, support
for this feature is conflated with support for lossy storage of the bitmap
itself, and therefore callers recheck both the scan conditions and the
partial index predicate (if any) for recheckable tuples. That might not
always be true, however.
<function>amgetbitmap</> and <function>amgetbitmap</> and
<function>amgettuple</> cannot be used in the same index scan; there <function>amgettuple</> cannot be used in the same index scan; there
are other restrictions too when using <function>amgetbitmap</>, as explained are other restrictions too when using <function>amgetbitmap</>, as explained
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginget.c,v 1.11 2008/04/10 22:25:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/gin/ginget.c,v 1.12 2008/04/13 19:18:13 tgl Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -428,11 +428,14 @@ keyGetItem(Relation index, GinState *ginstate, MemoryContext tempCtx, GinScanKey ...@@ -428,11 +428,14 @@ keyGetItem(Relation index, GinState *ginstate, MemoryContext tempCtx, GinScanKey
* returns true if found * returns true if found
*/ */
static bool static bool
scanGetItem(IndexScanDesc scan, ItemPointerData *item) scanGetItem(IndexScanDesc scan, ItemPointerData *item, bool *recheck)
{ {
uint32 i; uint32 i;
GinScanOpaque so = (GinScanOpaque) scan->opaque; GinScanOpaque so = (GinScanOpaque) scan->opaque;
/* XXX for the moment, treat all GIN operators as lossy */
*recheck = true;
ItemPointerSetMin(item); ItemPointerSetMin(item);
for (i = 0; i < so->nkeys; i++) for (i = 0; i < so->nkeys; i++)
{ {
...@@ -496,13 +499,14 @@ gingetbitmap(PG_FUNCTION_ARGS) ...@@ -496,13 +499,14 @@ gingetbitmap(PG_FUNCTION_ARGS)
for (;;) for (;;)
{ {
ItemPointerData iptr; ItemPointerData iptr;
bool recheck;
CHECK_FOR_INTERRUPTS(); CHECK_FOR_INTERRUPTS();
if (!scanGetItem(scan, &iptr)) if (!scanGetItem(scan, &iptr, &recheck))
break; break;
tbm_add_tuples(tbm, &iptr, 1, false); tbm_add_tuples(tbm, &iptr, 1, recheck);
ntids++; ntids++;
} }
...@@ -528,7 +532,7 @@ gingettuple(PG_FUNCTION_ARGS) ...@@ -528,7 +532,7 @@ gingettuple(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(false); PG_RETURN_BOOL(false);
startScan(scan); startScan(scan);
res = scanGetItem(scan, &scan->xs_ctup.t_self); res = scanGetItem(scan, &scan->xs_ctup.t_self, &scan->xs_recheck);
stopScan(scan); stopScan(scan);
PG_RETURN_BOOL(res); PG_RETURN_BOOL(res);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gistget.c,v 1.70 2008/04/10 22:25:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/gist/gistget.c,v 1.71 2008/04/13 19:18:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -23,9 +23,7 @@ ...@@ -23,9 +23,7 @@
static OffsetNumber gistfindnext(IndexScanDesc scan, OffsetNumber n, static OffsetNumber gistfindnext(IndexScanDesc scan, OffsetNumber n,
ScanDirection dir); ScanDirection dir);
static int64 gistnext(IndexScanDesc scan, ScanDirection dir, static int64 gistnext(IndexScanDesc scan, ScanDirection dir, TIDBitmap *tbm);
ItemPointer tid, TIDBitmap *tbm,
bool ignore_killed_tuples);
static bool gistindex_keytest(IndexTuple tuple, IndexScanDesc scan, static bool gistindex_keytest(IndexTuple tuple, IndexScanDesc scan,
OffsetNumber offset); OffsetNumber offset);
...@@ -100,7 +98,6 @@ gistgettuple(PG_FUNCTION_ARGS) ...@@ -100,7 +98,6 @@ gistgettuple(PG_FUNCTION_ARGS)
IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0); IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1); ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
GISTScanOpaque so; GISTScanOpaque so;
ItemPointerData tid;
bool res; bool res;
so = (GISTScanOpaque) scan->opaque; so = (GISTScanOpaque) scan->opaque;
...@@ -113,11 +110,9 @@ gistgettuple(PG_FUNCTION_ARGS) ...@@ -113,11 +110,9 @@ gistgettuple(PG_FUNCTION_ARGS)
killtuple(scan->indexRelation, so, &(so->curpos)); killtuple(scan->indexRelation, so, &(so->curpos));
/* /*
* Get the next tuple that matches the search key. If asked to skip killed * Get the next tuple that matches the search key.
* tuples, continue looping until we find a non-killed tuple that matches
* the search key.
*/ */
res = (gistnext(scan, dir, &tid, NULL, scan->ignore_killed_tuples) > 0) ? true : false; res = (gistnext(scan, dir, NULL) > 0);
PG_RETURN_BOOL(res); PG_RETURN_BOOL(res);
} }
...@@ -129,7 +124,7 @@ gistgetbitmap(PG_FUNCTION_ARGS) ...@@ -129,7 +124,7 @@ gistgetbitmap(PG_FUNCTION_ARGS)
TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1); TIDBitmap *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
int64 ntids; int64 ntids;
ntids = gistnext(scan, ForwardScanDirection, NULL, tbm, false); ntids = gistnext(scan, ForwardScanDirection, tbm);
PG_RETURN_INT64(ntids); PG_RETURN_INT64(ntids);
} }
...@@ -140,20 +135,23 @@ gistgetbitmap(PG_FUNCTION_ARGS) ...@@ -140,20 +135,23 @@ gistgetbitmap(PG_FUNCTION_ARGS)
* *
* This function is used by both gistgettuple and gistgetbitmap. When * This function is used by both gistgettuple and gistgetbitmap. When
* invoked from gistgettuple, tbm is null and the next matching tuple * invoked from gistgettuple, tbm is null and the next matching tuple
* is returned in *tid. When invoked from getbitmap, tid is null and * is returned in scan->xs_ctup.t_self. When invoked from getbitmap,
* all matching tuples are added to tbm. In both cases, the function * tbm is non-null and all matching tuples are added to tbm before
* result is the number of returned tuples. * returning. In both cases, the function result is the number of
* returned tuples.
*
* If scan specifies to skip killed tuples, continue looping until we find a
* non-killed tuple that matches the search key.
*/ */
static int64 static int64
gistnext(IndexScanDesc scan, ScanDirection dir, gistnext(IndexScanDesc scan, ScanDirection dir, TIDBitmap *tbm)
ItemPointer tid, TIDBitmap *tbm,
bool ignore_killed_tuples)
{ {
Page p; Page p;
OffsetNumber n; OffsetNumber n;
GISTScanOpaque so; GISTScanOpaque so;
GISTSearchStack *stk; GISTSearchStack *stk;
IndexTuple it; IndexTuple it;
bool recheck;
GISTPageOpaque opaque; GISTPageOpaque opaque;
bool resetoffset = false; bool resetoffset = false;
int64 ntids = 0; int64 ntids = 0;
...@@ -194,7 +192,7 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ...@@ -194,7 +192,7 @@ gistnext(IndexScanDesc scan, ScanDirection dir,
if (XLogRecPtrIsInvalid(so->stack->lsn) || !XLByteEQ(so->stack->lsn, PageGetLSN(p))) if (XLogRecPtrIsInvalid(so->stack->lsn) || !XLByteEQ(so->stack->lsn, PageGetLSN(p)))
{ {
/* page changed from last visit or visit first time , reset offset */ /* first visit or page changed from last visit, reset offset */
so->stack->lsn = PageGetLSN(p); so->stack->lsn = PageGetLSN(p);
resetoffset = true; resetoffset = true;
...@@ -259,6 +257,8 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ...@@ -259,6 +257,8 @@ gistnext(IndexScanDesc scan, ScanDirection dir,
for (;;) for (;;)
{ {
n = gistfindnext(scan, n, dir); n = gistfindnext(scan, n, dir);
/* XXX for the moment, treat all GIST operators as lossy */
recheck = true;
if (!OffsetNumberIsValid(n)) if (!OffsetNumberIsValid(n))
{ {
...@@ -298,15 +298,17 @@ gistnext(IndexScanDesc scan, ScanDirection dir, ...@@ -298,15 +298,17 @@ gistnext(IndexScanDesc scan, ScanDirection dir,
ItemPointerSet(&(so->curpos), ItemPointerSet(&(so->curpos),
BufferGetBlockNumber(so->curbuf), n); BufferGetBlockNumber(so->curbuf), n);
if (!(ignore_killed_tuples && ItemIdIsDead(PageGetItemId(p, n)))) if (!(scan->ignore_killed_tuples &&
ItemIdIsDead(PageGetItemId(p, n))))
{ {
it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n)); it = (IndexTuple) PageGetItem(p, PageGetItemId(p, n));
ntids++; ntids++;
if (tbm != NULL) if (tbm != NULL)
tbm_add_tuples(tbm, &it->t_tid, 1, false); tbm_add_tuples(tbm, &it->t_tid, 1, recheck);
else else
{ {
*tid = scan->xs_ctup.t_self = it->t_tid; scan->xs_ctup.t_self = it->t_tid;
scan->xs_recheck = recheck;
LockBuffer(so->curbuf, GIST_UNLOCK); LockBuffer(so->curbuf, GIST_UNLOCK);
return ntids; /* always 1 */ return ntids; /* always 1 */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.101 2008/04/10 22:25:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.102 2008/04/13 19:18:14 tgl Exp $
* *
* NOTES * NOTES
* This file contains only the public interface routines. * This file contains only the public interface routines.
...@@ -210,6 +210,9 @@ hashgettuple(PG_FUNCTION_ARGS) ...@@ -210,6 +210,9 @@ hashgettuple(PG_FUNCTION_ARGS)
OffsetNumber offnum; OffsetNumber offnum;
bool res; bool res;
/* Hash indexes are never lossy (at the moment anyway) */
scan->xs_recheck = false;
/* /*
* We hold pin but not lock on current buffer while outside the hash AM. * We hold pin but not lock on current buffer while outside the hash AM.
* Reacquire the read lock here. * Reacquire the read lock here.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.67 2008/04/12 23:14:21 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.68 2008/04/13 19:18:14 tgl Exp $
* *
* NOTES * NOTES
* many of the old access method routines have been turned into * many of the old access method routines have been turned into
...@@ -96,9 +96,9 @@ RelationGetIndexScan(Relation indexRelation, ...@@ -96,9 +96,9 @@ RelationGetIndexScan(Relation indexRelation,
ItemPointerSetInvalid(&scan->xs_ctup.t_self); ItemPointerSetInvalid(&scan->xs_ctup.t_self);
scan->xs_ctup.t_data = NULL; scan->xs_ctup.t_data = NULL;
scan->xs_cbuf = InvalidBuffer; scan->xs_cbuf = InvalidBuffer;
scan->xs_prev_xmax = InvalidTransactionId;
scan->xs_next_hot = InvalidOffsetNumber;
scan->xs_hot_dead = false; scan->xs_hot_dead = false;
scan->xs_next_hot = InvalidOffsetNumber;
scan->xs_prev_xmax = InvalidTransactionId;
/* /*
* Let the AM fill in the key and any opaque data it wants. * Let the AM fill in the key and any opaque data it wants.
...@@ -233,7 +233,18 @@ systable_getnext(SysScanDesc sysscan) ...@@ -233,7 +233,18 @@ systable_getnext(SysScanDesc sysscan)
HeapTuple htup; HeapTuple htup;
if (sysscan->irel) if (sysscan->irel)
{
htup = index_getnext(sysscan->iscan, ForwardScanDirection); htup = index_getnext(sysscan->iscan, ForwardScanDirection);
/*
* We currently don't need to support lossy index operators for
* any system catalog scan. It could be done here, using the
* scan keys to drive the operator calls, if we arranged to save
* the heap attnums during systable_beginscan(); this is practical
* because we still wouldn't need to support indexes on expressions.
*/
if (htup && sysscan->iscan->xs_recheck)
elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
}
else else
htup = heap_getnext(sysscan->scan, ForwardScanDirection); htup = heap_getnext(sysscan->scan, ForwardScanDirection);
...@@ -328,6 +339,9 @@ systable_getnext_ordered(SysScanDesc sysscan, ScanDirection direction) ...@@ -328,6 +339,9 @@ systable_getnext_ordered(SysScanDesc sysscan, ScanDirection direction)
Assert(sysscan->irel); Assert(sysscan->irel);
htup = index_getnext(sysscan->iscan, direction); htup = index_getnext(sysscan->iscan, direction);
/* See notes in systable_getnext */
if (htup && sysscan->iscan->xs_recheck)
elog(ERROR, "system catalog scans with lossy index conditions are not implemented");
return htup; return htup;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.106 2008/04/12 23:14:21 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.107 2008/04/13 19:18:14 tgl Exp $
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
* index_open - open an index relation by relation OID * index_open - open an index relation by relation OID
...@@ -402,6 +402,10 @@ index_restrpos(IndexScanDesc scan) ...@@ -402,6 +402,10 @@ index_restrpos(IndexScanDesc scan)
* snapshot, or NULL if no more matching tuples exist. On success, * snapshot, or NULL if no more matching tuples exist. On success,
* the buffer containing the heap tuple is pinned (the pin will be dropped * the buffer containing the heap tuple is pinned (the pin will be dropped
* at the next index_getnext or index_endscan). * at the next index_getnext or index_endscan).
*
* Note: caller must check scan->xs_recheck, and perform rechecking of the
* scan keys if required. We do not do that here because we don't have
* enough information to do it efficiently in the general case.
* ---------------- * ----------------
*/ */
HeapTuple HeapTuple
...@@ -455,6 +459,8 @@ index_getnext(IndexScanDesc scan, ScanDirection direction) ...@@ -455,6 +459,8 @@ index_getnext(IndexScanDesc scan, ScanDirection direction)
/* /*
* The AM's gettuple proc finds the next index entry matching the * The AM's gettuple proc finds the next index entry matching the
* scan keys, and puts the TID in xs_ctup.t_self (ie, *tid). * scan keys, and puts the TID in xs_ctup.t_self (ie, *tid).
* It should also set scan->xs_recheck, though we pay no
* attention to that here.
*/ */
found = DatumGetBool(FunctionCall2(procedure, found = DatumGetBool(FunctionCall2(procedure,
PointerGetDatum(scan), PointerGetDatum(scan),
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.157 2008/04/10 22:25:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.158 2008/04/13 19:18:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -240,6 +240,9 @@ btgettuple(PG_FUNCTION_ARGS) ...@@ -240,6 +240,9 @@ btgettuple(PG_FUNCTION_ARGS)
BTScanOpaque so = (BTScanOpaque) scan->opaque; BTScanOpaque so = (BTScanOpaque) scan->opaque;
bool res; bool res;
/* btree indexes are never lossy */
scan->xs_recheck = false;
/* /*
* If we've already initialized this scan, we can just advance it in the * If we've already initialized this scan, we can just advance it in the
* appropriate direction. If we haven't done so yet, we call a routine to * appropriate direction. If we haven't done so yet, we call a routine to
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.172 2008/03/26 21:10:37 alvherre Exp $ * $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.173 2008/04/13 19:18:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -776,6 +776,10 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex) ...@@ -776,6 +776,10 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
CHECK_FOR_INTERRUPTS(); CHECK_FOR_INTERRUPTS();
/* Since we used no scan keys, should never need to recheck */
if (scan->xs_recheck)
elog(ERROR, "CLUSTER does not support lossy index conditions");
LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE); LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
switch (HeapTupleSatisfiesVacuum(tuple->t_data, OldestXmin, switch (HeapTupleSatisfiesVacuum(tuple->t_data, OldestXmin,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.126 2008/03/18 03:54:52 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.127 2008/04/13 19:18:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -106,7 +106,7 @@ IndexNext(IndexScanState *node) ...@@ -106,7 +106,7 @@ IndexNext(IndexScanState *node)
/* /*
* ok, now that we have what we need, fetch the next tuple. * ok, now that we have what we need, fetch the next tuple.
*/ */
if ((tuple = index_getnext(scandesc, direction)) != NULL) while ((tuple = index_getnext(scandesc, direction)) != NULL)
{ {
/* /*
* Store the scanned tuple in the scan tuple slot of the scan state. * Store the scanned tuple in the scan tuple slot of the scan state.
...@@ -118,6 +118,18 @@ IndexNext(IndexScanState *node) ...@@ -118,6 +118,18 @@ IndexNext(IndexScanState *node)
scandesc->xs_cbuf, /* buffer containing tuple */ scandesc->xs_cbuf, /* buffer containing tuple */
false); /* don't pfree */ false); /* don't pfree */
/*
* If the index was lossy, we have to recheck the index quals using
* the real tuple.
*/
if (scandesc->xs_recheck)
{
econtext->ecxt_scantuple = slot;
ResetExprContext(econtext);
if (!ExecQual(node->indexqualorig, econtext, false))
continue; /* nope, so ask index for another one */
}
return slot; return slot;
} }
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.237 2008/01/01 19:45:50 momjian Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.238 2008/04/13 19:18:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -47,13 +47,12 @@ static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path); ...@@ -47,13 +47,12 @@ static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path);
static SeqScan *create_seqscan_plan(PlannerInfo *root, Path *best_path, static SeqScan *create_seqscan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses); List *tlist, List *scan_clauses);
static IndexScan *create_indexscan_plan(PlannerInfo *root, IndexPath *best_path, static IndexScan *create_indexscan_plan(PlannerInfo *root, IndexPath *best_path,
List *tlist, List *scan_clauses, List *tlist, List *scan_clauses);
List **nonlossy_clauses);
static BitmapHeapScan *create_bitmap_scan_plan(PlannerInfo *root, static BitmapHeapScan *create_bitmap_scan_plan(PlannerInfo *root,
BitmapHeapPath *best_path, BitmapHeapPath *best_path,
List *tlist, List *scan_clauses); List *tlist, List *scan_clauses);
static Plan *create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, static Plan *create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
List **qual, List **indexqual); List **qual);
static TidScan *create_tidscan_plan(PlannerInfo *root, TidPath *best_path, static TidScan *create_tidscan_plan(PlannerInfo *root, TidPath *best_path,
List *tlist, List *scan_clauses); List *tlist, List *scan_clauses);
static SubqueryScan *create_subqueryscan_plan(PlannerInfo *root, Path *best_path, static SubqueryScan *create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
...@@ -70,7 +69,6 @@ static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path, ...@@ -70,7 +69,6 @@ static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path,
Plan *outer_plan, Plan *inner_plan); Plan *outer_plan, Plan *inner_plan);
static void fix_indexqual_references(List *indexquals, IndexPath *index_path, static void fix_indexqual_references(List *indexquals, IndexPath *index_path,
List **fixed_indexquals, List **fixed_indexquals,
List **nonlossy_indexquals,
List **indexstrategy, List **indexstrategy,
List **indexsubtype); List **indexsubtype);
static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index,
...@@ -239,8 +237,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path) ...@@ -239,8 +237,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
plan = (Plan *) create_indexscan_plan(root, plan = (Plan *) create_indexscan_plan(root,
(IndexPath *) best_path, (IndexPath *) best_path,
tlist, tlist,
scan_clauses, scan_clauses);
NULL);
break; break;
case T_BitmapHeapScan: case T_BitmapHeapScan:
...@@ -840,16 +837,12 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path, ...@@ -840,16 +837,12 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path,
* The indexquals list of the path contains implicitly-ANDed qual conditions. * The indexquals list of the path contains implicitly-ANDed qual conditions.
* The list can be empty --- then no index restrictions will be applied during * The list can be empty --- then no index restrictions will be applied during
* the scan. * the scan.
*
* If nonlossy_clauses isn't NULL, *nonlossy_clauses receives a list of the
* nonlossy indexquals.
*/ */
static IndexScan * static IndexScan *
create_indexscan_plan(PlannerInfo *root, create_indexscan_plan(PlannerInfo *root,
IndexPath *best_path, IndexPath *best_path,
List *tlist, List *tlist,
List *scan_clauses, List *scan_clauses)
List **nonlossy_clauses)
{ {
List *indexquals = best_path->indexquals; List *indexquals = best_path->indexquals;
Index baserelid = best_path->path.parent->relid; Index baserelid = best_path->path.parent->relid;
...@@ -857,7 +850,6 @@ create_indexscan_plan(PlannerInfo *root, ...@@ -857,7 +850,6 @@ create_indexscan_plan(PlannerInfo *root,
List *qpqual; List *qpqual;
List *stripped_indexquals; List *stripped_indexquals;
List *fixed_indexquals; List *fixed_indexquals;
List *nonlossy_indexquals;
List *indexstrategy; List *indexstrategy;
List *indexsubtype; List *indexsubtype;
ListCell *l; ListCell *l;
...@@ -876,18 +868,13 @@ create_indexscan_plan(PlannerInfo *root, ...@@ -876,18 +868,13 @@ create_indexscan_plan(PlannerInfo *root,
/* /*
* The executor needs a copy with the indexkey on the left of each clause * The executor needs a copy with the indexkey on the left of each clause
* and with index attr numbers substituted for table ones. This pass also * and with index attr numbers substituted for table ones. This pass also
* gets strategy info and looks for "lossy" operators. * gets strategy info.
*/ */
fix_indexqual_references(indexquals, best_path, fix_indexqual_references(indexquals, best_path,
&fixed_indexquals, &fixed_indexquals,
&nonlossy_indexquals,
&indexstrategy, &indexstrategy,
&indexsubtype); &indexsubtype);
/* pass back nonlossy quals if caller wants 'em */
if (nonlossy_clauses)
*nonlossy_clauses = nonlossy_indexquals;
/* /*
* If this is an innerjoin scan, the indexclauses will contain join * If this is an innerjoin scan, the indexclauses will contain join
* clauses that are not present in scan_clauses (since the passed-in value * clauses that are not present in scan_clauses (since the passed-in value
...@@ -907,9 +894,8 @@ create_indexscan_plan(PlannerInfo *root, ...@@ -907,9 +894,8 @@ create_indexscan_plan(PlannerInfo *root,
* by the index. All the predicates in the indexquals will be checked * by the index. All the predicates in the indexquals will be checked
* (either by the index itself, or by nodeIndexscan.c), but if there are * (either by the index itself, or by nodeIndexscan.c), but if there are
* any "special" operators involved then they must be included in qpqual. * any "special" operators involved then they must be included in qpqual.
* Also, any lossy index operators must be rechecked in the qpqual. The * The upshot is that qpqual must contain scan_clauses minus whatever
* upshot is that qpqual must contain scan_clauses minus whatever appears * appears in indexquals.
* in nonlossy_indexquals.
* *
* In normal cases simple pointer equality checks will be enough to spot * In normal cases simple pointer equality checks will be enough to spot
* duplicate RestrictInfos, so we try that first. In some situations * duplicate RestrictInfos, so we try that first. In some situations
...@@ -932,13 +918,13 @@ create_indexscan_plan(PlannerInfo *root, ...@@ -932,13 +918,13 @@ create_indexscan_plan(PlannerInfo *root,
Assert(IsA(rinfo, RestrictInfo)); Assert(IsA(rinfo, RestrictInfo));
if (rinfo->pseudoconstant) if (rinfo->pseudoconstant)
continue; /* we may drop pseudoconstants here */ continue; /* we may drop pseudoconstants here */
if (list_member_ptr(nonlossy_indexquals, rinfo)) if (list_member_ptr(indexquals, rinfo))
continue; continue;
if (!contain_mutable_functions((Node *) rinfo->clause)) if (!contain_mutable_functions((Node *) rinfo->clause))
{ {
List *clausel = list_make1(rinfo->clause); List *clausel = list_make1(rinfo->clause);
if (predicate_implied_by(clausel, nonlossy_indexquals)) if (predicate_implied_by(clausel, indexquals))
continue; continue;
if (best_path->indexinfo->indpred) if (best_path->indexinfo->indpred)
{ {
...@@ -990,7 +976,6 @@ create_bitmap_scan_plan(PlannerInfo *root, ...@@ -990,7 +976,6 @@ create_bitmap_scan_plan(PlannerInfo *root,
Index baserelid = best_path->path.parent->relid; Index baserelid = best_path->path.parent->relid;
Plan *bitmapqualplan; Plan *bitmapqualplan;
List *bitmapqualorig; List *bitmapqualorig;
List *indexquals;
List *qpqual; List *qpqual;
ListCell *l; ListCell *l;
BitmapHeapScan *scan_plan; BitmapHeapScan *scan_plan;
...@@ -999,9 +984,9 @@ create_bitmap_scan_plan(PlannerInfo *root, ...@@ -999,9 +984,9 @@ create_bitmap_scan_plan(PlannerInfo *root,
Assert(baserelid > 0); Assert(baserelid > 0);
Assert(best_path->path.parent->rtekind == RTE_RELATION); Assert(best_path->path.parent->rtekind == RTE_RELATION);
/* Process the bitmapqual tree into a Plan tree and qual lists */ /* Process the bitmapqual tree into a Plan tree and qual list */
bitmapqualplan = create_bitmap_subplan(root, best_path->bitmapqual, bitmapqualplan = create_bitmap_subplan(root, best_path->bitmapqual,
&bitmapqualorig, &indexquals); &bitmapqualorig);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */ /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
scan_clauses = extract_actual_clauses(scan_clauses, false); scan_clauses = extract_actual_clauses(scan_clauses, false);
...@@ -1023,9 +1008,9 @@ create_bitmap_scan_plan(PlannerInfo *root, ...@@ -1023,9 +1008,9 @@ create_bitmap_scan_plan(PlannerInfo *root,
* The qpqual list must contain all restrictions not automatically handled * The qpqual list must contain all restrictions not automatically handled
* by the index. All the predicates in the indexquals will be checked * by the index. All the predicates in the indexquals will be checked
* (either by the index itself, or by nodeBitmapHeapscan.c), but if there * (either by the index itself, or by nodeBitmapHeapscan.c), but if there
* are any "special" or lossy operators involved then they must be added * are any "special" operators involved then they must be added to qpqual.
* to qpqual. The upshot is that qpqual must contain scan_clauses minus * The upshot is that qpqual must contain scan_clauses minus whatever
* whatever appears in indexquals. * appears in bitmapqualorig.
* *
* In normal cases simple equal() checks will be enough to spot duplicate * In normal cases simple equal() checks will be enough to spot duplicate
* clauses, so we try that first. In some situations (particularly with * clauses, so we try that first. In some situations (particularly with
...@@ -1037,22 +1022,22 @@ create_bitmap_scan_plan(PlannerInfo *root, ...@@ -1037,22 +1022,22 @@ create_bitmap_scan_plan(PlannerInfo *root,
* *
* Unlike create_indexscan_plan(), we need take no special thought here * Unlike create_indexscan_plan(), we need take no special thought here
* for partial index predicates; this is because the predicate conditions * for partial index predicates; this is because the predicate conditions
* are already listed in bitmapqualorig and indexquals. Bitmap scans have * are already listed in bitmapqualorig. Bitmap scans have to do it that
* to do it that way because predicate conditions need to be rechecked if * way because predicate conditions need to be rechecked if the scan's
* the scan becomes lossy. * bitmap becomes lossy.
*/ */
qpqual = NIL; qpqual = NIL;
foreach(l, scan_clauses) foreach(l, scan_clauses)
{ {
Node *clause = (Node *) lfirst(l); Node *clause = (Node *) lfirst(l);
if (list_member(indexquals, clause)) if (list_member(bitmapqualorig, clause))
continue; continue;
if (!contain_mutable_functions(clause)) if (!contain_mutable_functions(clause))
{ {
List *clausel = list_make1(clause); List *clausel = list_make1(clause);
if (predicate_implied_by(clausel, indexquals)) if (predicate_implied_by(clausel, bitmapqualorig))
continue; continue;
} }
qpqual = lappend(qpqual, clause); qpqual = lappend(qpqual, clause);
...@@ -1062,7 +1047,7 @@ create_bitmap_scan_plan(PlannerInfo *root, ...@@ -1062,7 +1047,7 @@ create_bitmap_scan_plan(PlannerInfo *root,
qpqual = order_qual_clauses(root, qpqual); qpqual = order_qual_clauses(root, qpqual);
/* /*
* When dealing with special or lossy operators, we will at this point * When dealing with special operators, we will at this point
* have duplicate clauses in qpqual and bitmapqualorig. We may as well * have duplicate clauses in qpqual and bitmapqualorig. We may as well
* drop 'em from bitmapqualorig, since there's no point in making the * drop 'em from bitmapqualorig, since there's no point in making the
* tests twice. * tests twice.
...@@ -1086,19 +1071,19 @@ create_bitmap_scan_plan(PlannerInfo *root, ...@@ -1086,19 +1071,19 @@ create_bitmap_scan_plan(PlannerInfo *root,
/* /*
* Given a bitmapqual tree, generate the Plan tree that implements it * Given a bitmapqual tree, generate the Plan tree that implements it
* *
* As byproducts, we also return in *qual and *indexqual the qual lists * As a byproduct, we also return in *qual a qual list (in implicit-AND
* (in implicit-AND form, without RestrictInfos) describing the original index * form, without RestrictInfos) describing the generated indexqual
* conditions and the generated indexqual conditions. The latter is made to * conditions, as needed for rechecking heap tuples in lossy cases.
* exclude lossy index operators. Both lists include partial-index predicates, * This list also includes partial-index predicates, because we have to
* because we have to recheck predicates as well as index conditions if the * recheck predicates as well as index conditions if the scan's bitmap
* bitmap scan becomes lossy. * becomes lossy.
* *
* Note: if you find yourself changing this, you probably need to change * Note: if you find yourself changing this, you probably need to change
* make_restrictinfo_from_bitmapqual too. * make_restrictinfo_from_bitmapqual too.
*/ */
static Plan * static Plan *
create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
List **qual, List **indexqual) List **qual)
{ {
Plan *plan; Plan *plan;
...@@ -1107,7 +1092,6 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, ...@@ -1107,7 +1092,6 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
BitmapAndPath *apath = (BitmapAndPath *) bitmapqual; BitmapAndPath *apath = (BitmapAndPath *) bitmapqual;
List *subplans = NIL; List *subplans = NIL;
List *subquals = NIL; List *subquals = NIL;
List *subindexquals = NIL;
ListCell *l; ListCell *l;
/* /*
...@@ -1121,13 +1105,11 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, ...@@ -1121,13 +1105,11 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
{ {
Plan *subplan; Plan *subplan;
List *subqual; List *subqual;
List *subindexqual;
subplan = create_bitmap_subplan(root, (Path *) lfirst(l), subplan = create_bitmap_subplan(root, (Path *) lfirst(l),
&subqual, &subindexqual); &subqual);
subplans = lappend(subplans, subplan); subplans = lappend(subplans, subplan);
subquals = list_concat_unique(subquals, subqual); subquals = list_concat_unique(subquals, subqual);
subindexquals = list_concat_unique(subindexquals, subindexqual);
} }
plan = (Plan *) make_bitmap_and(subplans); plan = (Plan *) make_bitmap_and(subplans);
plan->startup_cost = apath->path.startup_cost; plan->startup_cost = apath->path.startup_cost;
...@@ -1136,16 +1118,13 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, ...@@ -1136,16 +1118,13 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
clamp_row_est(apath->bitmapselectivity * apath->path.parent->tuples); clamp_row_est(apath->bitmapselectivity * apath->path.parent->tuples);
plan->plan_width = 0; /* meaningless */ plan->plan_width = 0; /* meaningless */
*qual = subquals; *qual = subquals;
*indexqual = subindexquals;
} }
else if (IsA(bitmapqual, BitmapOrPath)) else if (IsA(bitmapqual, BitmapOrPath))
{ {
BitmapOrPath *opath = (BitmapOrPath *) bitmapqual; BitmapOrPath *opath = (BitmapOrPath *) bitmapqual;
List *subplans = NIL; List *subplans = NIL;
List *subquals = NIL; List *subquals = NIL;
List *subindexquals = NIL;
bool const_true_subqual = false; bool const_true_subqual = false;
bool const_true_subindexqual = false;
ListCell *l; ListCell *l;
/* /*
...@@ -1161,21 +1140,15 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, ...@@ -1161,21 +1140,15 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
{ {
Plan *subplan; Plan *subplan;
List *subqual; List *subqual;
List *subindexqual;
subplan = create_bitmap_subplan(root, (Path *) lfirst(l), subplan = create_bitmap_subplan(root, (Path *) lfirst(l),
&subqual, &subindexqual); &subqual);
subplans = lappend(subplans, subplan); subplans = lappend(subplans, subplan);
if (subqual == NIL) if (subqual == NIL)
const_true_subqual = true; const_true_subqual = true;
else if (!const_true_subqual) else if (!const_true_subqual)
subquals = lappend(subquals, subquals = lappend(subquals,
make_ands_explicit(subqual)); make_ands_explicit(subqual));
if (subindexqual == NIL)
const_true_subindexqual = true;
else if (!const_true_subindexqual)
subindexquals = lappend(subindexquals,
make_ands_explicit(subindexqual));
} }
/* /*
...@@ -1207,23 +1180,15 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, ...@@ -1207,23 +1180,15 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
*qual = subquals; *qual = subquals;
else else
*qual = list_make1(make_orclause(subquals)); *qual = list_make1(make_orclause(subquals));
if (const_true_subindexqual)
*indexqual = NIL;
else if (list_length(subindexquals) <= 1)
*indexqual = subindexquals;
else
*indexqual = list_make1(make_orclause(subindexquals));
} }
else if (IsA(bitmapqual, IndexPath)) else if (IsA(bitmapqual, IndexPath))
{ {
IndexPath *ipath = (IndexPath *) bitmapqual; IndexPath *ipath = (IndexPath *) bitmapqual;
IndexScan *iscan; IndexScan *iscan;
List *nonlossy_clauses;
ListCell *l; ListCell *l;
/* Use the regular indexscan plan build machinery... */ /* Use the regular indexscan plan build machinery... */
iscan = create_indexscan_plan(root, ipath, NIL, NIL, iscan = create_indexscan_plan(root, ipath, NIL, NIL);
&nonlossy_clauses);
/* then convert to a bitmap indexscan */ /* then convert to a bitmap indexscan */
plan = (Plan *) make_bitmap_indexscan(iscan->scan.scanrelid, plan = (Plan *) make_bitmap_indexscan(iscan->scan.scanrelid,
iscan->indexid, iscan->indexid,
...@@ -1237,7 +1202,6 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, ...@@ -1237,7 +1202,6 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
clamp_row_est(ipath->indexselectivity * ipath->path.parent->tuples); clamp_row_est(ipath->indexselectivity * ipath->path.parent->tuples);
plan->plan_width = 0; /* meaningless */ plan->plan_width = 0; /* meaningless */
*qual = get_actual_clauses(ipath->indexclauses); *qual = get_actual_clauses(ipath->indexclauses);
*indexqual = get_actual_clauses(nonlossy_clauses);
foreach(l, ipath->indexinfo->indpred) foreach(l, ipath->indexinfo->indpred)
{ {
Expr *pred = (Expr *) lfirst(l); Expr *pred = (Expr *) lfirst(l);
...@@ -1249,10 +1213,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, ...@@ -1249,10 +1213,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
* generating redundant conditions. * generating redundant conditions.
*/ */
if (!predicate_implied_by(list_make1(pred), ipath->indexclauses)) if (!predicate_implied_by(list_make1(pred), ipath->indexclauses))
{
*qual = lappend(*qual, pred); *qual = lappend(*qual, pred);
*indexqual = lappend(*indexqual, pred);
}
} }
} }
else else
...@@ -1794,9 +1755,9 @@ create_hashjoin_plan(PlannerInfo *root, ...@@ -1794,9 +1755,9 @@ create_hashjoin_plan(PlannerInfo *root,
/* /*
* fix_indexqual_references * fix_indexqual_references
* Adjust indexqual clauses to the form the executor's indexqual * Adjust indexqual clauses to the form the executor's indexqual
* machinery needs, and check for recheckable (lossy) index conditions. * machinery needs.
* *
* We have five tasks here: * We have four tasks here:
* * Remove RestrictInfo nodes from the input clauses. * * Remove RestrictInfo nodes from the input clauses.
* * Index keys must be represented by Var nodes with varattno set to the * * Index keys must be represented by Var nodes with varattno set to the
* index's attribute number, not the attribute number in the original rel. * index's attribute number, not the attribute number in the original rel.
...@@ -1804,24 +1765,18 @@ create_hashjoin_plan(PlannerInfo *root, ...@@ -1804,24 +1765,18 @@ create_hashjoin_plan(PlannerInfo *root,
* left. * left.
* * We must construct lists of operator strategy numbers and subtypes * * We must construct lists of operator strategy numbers and subtypes
* for the top-level operators of each index clause. * for the top-level operators of each index clause.
* * We must detect any lossy index operators. The API is that we return
* a list of the input clauses whose operators are NOT lossy.
* *
* fixed_indexquals receives a modified copy of the indexquals list --- the * fixed_indexquals receives a modified copy of the indexquals list --- the
* original is not changed. Note also that the copy shares no substructure * original is not changed. Note also that the copy shares no substructure
* with the original; this is needed in case there is a subplan in it (we need * with the original; this is needed in case there is a subplan in it (we need
* two separate copies of the subplan tree, or things will go awry). * two separate copies of the subplan tree, or things will go awry).
* *
* nonlossy_indexquals receives a list of the original input clauses (with
* RestrictInfos) that contain non-lossy operators.
*
* indexstrategy receives an integer list of strategy numbers. * indexstrategy receives an integer list of strategy numbers.
* indexsubtype receives an OID list of strategy subtypes. * indexsubtype receives an OID list of strategy subtypes.
*/ */
static void static void
fix_indexqual_references(List *indexquals, IndexPath *index_path, fix_indexqual_references(List *indexquals, IndexPath *index_path,
List **fixed_indexquals, List **fixed_indexquals,
List **nonlossy_indexquals,
List **indexstrategy, List **indexstrategy,
List **indexsubtype) List **indexsubtype)
{ {
...@@ -1829,7 +1784,6 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path, ...@@ -1829,7 +1784,6 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
ListCell *l; ListCell *l;
*fixed_indexquals = NIL; *fixed_indexquals = NIL;
*nonlossy_indexquals = NIL;
*indexstrategy = NIL; *indexstrategy = NIL;
*indexsubtype = NIL; *indexsubtype = NIL;
...@@ -1837,7 +1791,7 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path, ...@@ -1837,7 +1791,7 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
* For each qual clause, commute if needed to put the indexkey operand on * For each qual clause, commute if needed to put the indexkey operand on
* the left, and then fix its varattno. (We do not need to change the * the left, and then fix its varattno. (We do not need to change the
* other side of the clause.) Then determine the operator's strategy * other side of the clause.) Then determine the operator's strategy
* number and subtype number, and check for lossy index behavior. * number and subtype number.
*/ */
foreach(l, indexquals) foreach(l, indexquals)
{ {
...@@ -1848,7 +1802,6 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path, ...@@ -1848,7 +1802,6 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
int stratno; int stratno;
Oid stratlefttype; Oid stratlefttype;
Oid stratrighttype; Oid stratrighttype;
bool recheck;
bool is_null_op = false; bool is_null_op = false;
Assert(IsA(rinfo, RestrictInfo)); Assert(IsA(rinfo, RestrictInfo));
...@@ -1961,8 +1914,6 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path, ...@@ -1961,8 +1914,6 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
/* IS NULL doesn't have a clause_op */ /* IS NULL doesn't have a clause_op */
stratno = InvalidStrategy; stratno = InvalidStrategy;
stratrighttype = InvalidOid; stratrighttype = InvalidOid;
/* We assume it's non-lossy ... might need more work someday */
recheck = false;
} }
else else
{ {
...@@ -1971,6 +1922,8 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path, ...@@ -1971,6 +1922,8 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
* to get its strategy number and the recheck indicator. This also * to get its strategy number and the recheck indicator. This also
* double-checks that we found an operator matching the index. * double-checks that we found an operator matching the index.
*/ */
bool recheck;
get_op_opfamily_properties(clause_op, opfamily, get_op_opfamily_properties(clause_op, opfamily,
&stratno, &stratno,
&stratlefttype, &stratlefttype,
...@@ -1980,10 +1933,6 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path, ...@@ -1980,10 +1933,6 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
*indexstrategy = lappend_int(*indexstrategy, stratno); *indexstrategy = lappend_int(*indexstrategy, stratno);
*indexsubtype = lappend_oid(*indexsubtype, stratrighttype); *indexsubtype = lappend_oid(*indexsubtype, stratrighttype);
/* If it's not lossy, add to nonlossy_indexquals */
if (!recheck)
*nonlossy_indexquals = lappend(*nonlossy_indexquals, rinfo);
} }
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/access/relscan.h,v 1.63 2008/04/12 23:14:21 tgl Exp $ * $PostgreSQL: pgsql/src/include/access/relscan.h,v 1.64 2008/04/13 19:18:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -76,15 +76,16 @@ typedef struct IndexScanDescData ...@@ -76,15 +76,16 @@ typedef struct IndexScanDescData
/* index access method's private state */ /* index access method's private state */
void *opaque; /* access-method-specific info */ void *opaque; /* access-method-specific info */
/* xs_ctup/xs_cbuf are valid after a successful index_getnext */ /* xs_ctup/xs_cbuf/xs_recheck are valid after a successful index_getnext */
HeapTupleData xs_ctup; /* current heap tuple, if any */ HeapTupleData xs_ctup; /* current heap tuple, if any */
Buffer xs_cbuf; /* current heap buffer in scan, if any */ Buffer xs_cbuf; /* current heap buffer in scan, if any */
/* NB: if xs_cbuf is not InvalidBuffer, we hold a pin on that buffer */ /* NB: if xs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
bool xs_recheck; /* T means scan keys must be rechecked */
/* state data for traversing HOT chains in index_getnext */ /* state data for traversing HOT chains in index_getnext */
TransactionId xs_prev_xmax; /* previous HOT chain member's XMAX, if any */
OffsetNumber xs_next_hot; /* next member of HOT chain, if any */
bool xs_hot_dead; /* T if all members of HOT chain are dead */ bool xs_hot_dead; /* T if all members of HOT chain are dead */
OffsetNumber xs_next_hot; /* next member of HOT chain, if any */
TransactionId xs_prev_xmax; /* previous HOT chain member's XMAX, if any */
} IndexScanDescData; } IndexScanDescData;
typedef IndexScanDescData *IndexScanDesc; typedef IndexScanDescData *IndexScanDesc;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment