Commit 916b2321 authored by Tom Lane's avatar Tom Lane

Clean up and document btree code for ordering keys. Neat stuff,

actually, but who could understand it with no comments?  Fix bug
while at it: _bt_orderkeys would try to invoke comparisons on
NULL inputs, given the right sort of redundant quals.
parent da1ad323
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.61 2000/07/21 06:42:32 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.62 2000/07/25 04:47:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -374,7 +374,7 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
BTItem btitem;
IndexTuple itup;
BTScanOpaque so;
Size keysok;
bool continuescan;
rel = scan->relation;
so = (BTScanOpaque) scan->opaque;
......@@ -396,16 +396,14 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum));
itup = &btitem->bti_itup;
if (_bt_checkkeys(scan, itup, &keysok))
if (_bt_checkkeys(scan, itup, dir, &continuescan))
{
/* tuple passes all scan key conditions, so return it */
Assert(keysok == so->numberOfKeys);
return FormRetrieveIndexResult(current, &(itup->t_tid));
}
/* This tuple doesn't pass, but there might be more that do */
} while (keysok >= so->numberOfFirstKeys ||
(keysok == ((Size) -1) && ScanDirectionIsBackward(dir)));
} while (continuescan);
/* No more items, so close down the current-item info */
ItemPointerSetInvalid(current);
......@@ -442,11 +440,10 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
RegProcedure proc;
int32 result;
BTScanOpaque so;
Size keysok;
bool strategyCheck;
ScanKey scankeys = 0;
bool continuescan;
ScanKey scankeys = NULL;
int keysCount = 0;
int *nKeyIs = 0;
int *nKeyIs = NULL;
int i,
j;
StrategyNumber strat_total;
......@@ -455,42 +452,50 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
so = (BTScanOpaque) scan->opaque;
/*
* Order the keys in the qualification and be sure that the scan
* exploits the tree order.
* Order the scan keys in our canonical fashion and eliminate any
* redundant keys.
*/
so->numberOfFirstKeys = 0; /* may be changed by _bt_orderkeys */
so->qual_ok = 1; /* may be changed by _bt_orderkeys */
scan->scanFromEnd = false;
strategyCheck = false;
if (so->numberOfKeys > 0)
{
_bt_orderkeys(rel, so);
if (so->qual_ok)
strategyCheck = true;
}
/*
* Quit now if _bt_orderkeys() discovered that the scan keys can
* never be satisfied (eg, x == 1 AND x > 2).
*/
if (! so->qual_ok)
return (RetrieveIndexResult) NULL;
/*
* Examine the scan keys to discover where we need to start the scan.
*/
scan->scanFromEnd = false;
strat_total = BTEqualStrategyNumber;
if (strategyCheck)
if (so->numberOfKeys > 0)
{
AttrNumber attno;
nKeyIs = (int *) palloc(so->numberOfKeys * sizeof(int));
for (i = 0; i < so->numberOfKeys; i++)
{
attno = so->keyData[i].sk_attno;
if (attno == keysCount)
AttrNumber attno = so->keyData[i].sk_attno;
/* ignore keys for already-determined attrs */
if (attno <= keysCount)
continue;
/* if we didn't find a boundary for the preceding attr, quit */
if (attno > keysCount + 1)
break;
strat = _bt_getstrat(rel, attno,
so->keyData[i].sk_procedure);
/*
* Can we use this key as a starting boundary for this attr?
*
* We can use multiple keys if they look like, say, = >= =
* but we have to stop after accepting a > or < boundary.
*/
if (strat == strat_total ||
strat == BTEqualStrategyNumber)
{
nKeyIs[keysCount++] = i;
continue;
}
if (ScanDirectionIsBackward(dir) &&
else if (ScanDirectionIsBackward(dir) &&
(strat == BTLessStrategyNumber ||
strat == BTLessEqualStrategyNumber))
{
......@@ -498,9 +503,8 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
strat_total = strat;
if (strat == BTLessStrategyNumber)
break;
continue;
}
if (ScanDirectionIsForward(dir) &&
else if (ScanDirectionIsForward(dir) &&
(strat == BTGreaterStrategyNumber ||
strat == BTGreaterEqualStrategyNumber))
{
......@@ -508,18 +512,14 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
strat_total = strat;
if (strat == BTGreaterStrategyNumber)
break;
continue;
}
}
if (!keysCount)
if (keysCount == 0)
scan->scanFromEnd = true;
}
else
scan->scanFromEnd = true;
if (so->qual_ok == 0)
return (RetrieveIndexResult) NULL;
/* if we just need to walk down one edge of the tree, do that */
if (scan->scanFromEnd)
{
......@@ -529,16 +529,14 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
}
/*
* Okay, we want something more complicated. What we'll do is use the
* first item in the scan key passed in (which has been correctly
* ordered to take advantage of index ordering) to position ourselves
* at the right place in the scan.
* We want to start the scan somewhere within the index. Set up a
* scankey we can use to search for the correct starting point.
*/
scankeys = (ScanKey) palloc(keysCount * sizeof(ScanKeyData));
for (i = 0; i < keysCount; i++)
{
j = nKeyIs[i];
/* _bt_orderkeys disallows it, but it's place to add some code latter */
/* _bt_orderkeys disallows it, but it's place to add some code later */
if (so->keyData[j].sk_flags & SK_ISNULL)
{
pfree(nKeyIs);
......@@ -578,6 +576,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
blkno = BufferGetBlockNumber(buf);
page = BufferGetPage(buf);
/* position to the precise item on the page */
offnum = _bt_binsrch(rel, buf, keysCount, scankeys);
ItemPointerSet(current, blkno, offnum);
......@@ -595,7 +594,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* call _bt_endpoint() to set up a scan starting at that index endpoint,
* as appropriate for the desired scan type.
*
* it's yet other place to add some code latter for is(not)null ...
* it's yet other place to add some code later for is(not)null ...
*----------
*/
......@@ -737,13 +736,12 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
itup = &btitem->bti_itup;
/* is the first item actually acceptable? */
if (_bt_checkkeys(scan, itup, &keysok))
if (_bt_checkkeys(scan, itup, dir, &continuescan))
{
/* yes, return it */
res = FormRetrieveIndexResult(current, &(itup->t_tid));
}
else if (keysok >= so->numberOfFirstKeys ||
(keysok == ((Size) -1) && ScanDirectionIsBackward(dir)))
else if (continuescan)
{
/* no, but there might be another one that is */
res = _bt_next(scan, dir);
......@@ -906,7 +904,7 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
IndexTuple itup;
BTScanOpaque so;
RetrieveIndexResult res;
Size keysok;
bool continuescan;
rel = scan->relation;
current = &(scan->currentItemData);
......@@ -1012,13 +1010,12 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
itup = &(btitem->bti_itup);
/* see if we picked a winner */
if (_bt_checkkeys(scan, itup, &keysok))
if (_bt_checkkeys(scan, itup, dir, &continuescan))
{
/* yes, return it */
res = FormRetrieveIndexResult(current, &(itup->t_tid));
}
else if (keysok >= so->numberOfFirstKeys ||
(keysok == ((Size) -1) && ScanDirectionIsBackward(dir)))
else if (continuescan)
{
/* no, but there might be another one that is */
res = _bt_next(scan, dir);
......
This diff is collapsed.
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: nbtree.h,v 1.39 2000/07/21 06:42:35 tgl Exp $
* $Id: nbtree.h,v 1.40 2000/07/25 04:47:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -47,13 +47,12 @@ typedef struct BTPageOpaqueData
typedef BTPageOpaqueData *BTPageOpaque;
/*
* ScanOpaqueData is used to remember which buffers we're currently
* examining in the scan. We keep these buffers locked and pinned
* and recorded in the opaque entry of the scan in order to avoid
* doing a ReadBuffer() for every tuple in the index. This avoids
* semop() calls, which are expensive.
* BTScanOpaqueData is used to remember which buffers we're currently
* examining in the scan. We keep these buffers pinned (but not locked,
* see nbtree.c) and recorded in the opaque entry of the scan to avoid
* doing a ReadBuffer() for every tuple in the index.
*
* And it's used to remember actual scankey info (we need in it
* And it's used to remember actual scankey info (we need it
* if some scankeys evaled at runtime).
*
* curHeapIptr & mrkHeapIptr are heap iptr-s from current/marked
......@@ -69,11 +68,12 @@ typedef struct BTScanOpaqueData
Buffer btso_mrkbuf;
ItemPointerData curHeapIptr;
ItemPointerData mrkHeapIptr;
uint16 qual_ok; /* 0 for quals like key == 1 && key > 2 */
uint16 numberOfKeys; /* number of keys */
uint16 numberOfFirstKeys; /* number of keys for 1st
* attribute */
ScanKey keyData; /* key descriptor */
/* these fields are set by _bt_orderkeys(), which see for more info: */
bool qual_ok; /* false if qual can never be satisfied */
uint16 numberOfKeys; /* number of scan keys */
uint16 numberOfRequiredKeys; /* number of keys that must be matched
* to continue the scan */
ScanKey keyData; /* array of scan keys */
} BTScanOpaqueData;
typedef BTScanOpaqueData *BTScanOpaque;
......@@ -276,7 +276,8 @@ extern ScanKey _bt_mkscankey_nodata(Relation rel);
extern void _bt_freeskey(ScanKey skey);
extern void _bt_freestack(BTStack stack);
extern void _bt_orderkeys(Relation relation, BTScanOpaque so);
extern bool _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple, Size *keysok);
extern bool _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple,
ScanDirection dir, bool *continuescan);
extern BTItem _bt_formitem(IndexTuple itup);
/*
......
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