Commit d1463050 authored by Marc G. Fournier's avatar Marc G. Fournier

Patches for Vadim's multikey indexing...

parent c3d637ac
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/common/Attic/indexvalid.c,v 1.13 1997/03/12 20:56:32 scrappy Exp $ * $Header: /cvsroot/pgsql/src/backend/access/common/Attic/indexvalid.c,v 1.14 1997/03/18 18:38:19 scrappy Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -48,7 +48,7 @@ index_keytest(IndexTuple tuple, ...@@ -48,7 +48,7 @@ index_keytest(IndexTuple tuple,
while (scanKeySize > 0) { while (scanKeySize > 0) {
datum = index_getattr(tuple, datum = index_getattr(tuple,
1, key[0].sk_attno,
tupdesc, tupdesc,
&isNull); &isNull);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.15 1997/02/22 10:04:14 vadim Exp $ * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.16 1997/03/18 18:38:35 scrappy Exp $
* *
* NOTES * NOTES
* This file contains only the public interface routines. * This file contains only the public interface routines.
...@@ -423,6 +423,7 @@ btrescan(IndexScanDesc scan, bool fromEnd, ScanKey scankey) ...@@ -423,6 +423,7 @@ btrescan(IndexScanDesc scan, bool fromEnd, ScanKey scankey)
/* reset the scan key */ /* reset the scan key */
so->numberOfKeys = scan->numberOfKeys; so->numberOfKeys = scan->numberOfKeys;
so->numberOfFirstKeys = 0;
so->qual_ok = 1; /* may be changed by _bt_orderkeys */ so->qual_ok = 1; /* may be changed by _bt_orderkeys */
if (scan->numberOfKeys > 0) { if (scan->numberOfKeys > 0) {
memmove(scan->keyData, memmove(scan->keyData,
...@@ -433,7 +434,9 @@ btrescan(IndexScanDesc scan, bool fromEnd, ScanKey scankey) ...@@ -433,7 +434,9 @@ btrescan(IndexScanDesc scan, bool fromEnd, ScanKey scankey)
so->numberOfKeys * sizeof(ScanKeyData)); so->numberOfKeys * sizeof(ScanKeyData));
/* order the keys in the qualification */ /* order the keys in the qualification */
if (so->numberOfKeys > 1) if (so->numberOfKeys > 1)
_bt_orderkeys(scan->relation, &so->numberOfKeys, so->keyData, &so->qual_ok); _bt_orderkeys(scan->relation, so);
else
so->numberOfFirstKeys = 1;
} }
/* finally, be sure that the scan exploits the tree order */ /* finally, be sure that the scan exploits the tree order */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.14 1997/02/18 17:13:48 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.15 1997/03/18 18:38:41 scrappy Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -562,7 +562,6 @@ _bt_next(IndexScanDesc scan, ScanDirection dir) ...@@ -562,7 +562,6 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
Page page; Page page;
OffsetNumber offnum; OffsetNumber offnum;
RetrieveIndexResult res; RetrieveIndexResult res;
BlockNumber blkno;
ItemPointer current; ItemPointer current;
BTItem btitem; BTItem btitem;
IndexTuple itup; IndexTuple itup;
...@@ -584,31 +583,35 @@ _bt_next(IndexScanDesc scan, ScanDirection dir) ...@@ -584,31 +583,35 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
/* we still have the buffer pinned and locked */ /* we still have the buffer pinned and locked */
buf = so->btso_curbuf; buf = so->btso_curbuf;
blkno = BufferGetBlockNumber(buf);
/* step one tuple in the appropriate direction */ do
if (!_bt_step(scan, &buf, dir)) {
return ((RetrieveIndexResult) NULL); /* step one tuple in the appropriate direction */
if (!_bt_step(scan, &buf, dir))
return ((RetrieveIndexResult) NULL);
/* by here, current is the tuple we want to return */ /* by here, current is the tuple we want to return */
offnum = ItemPointerGetOffsetNumber(current); offnum = ItemPointerGetOffsetNumber(current);
page = BufferGetPage(buf); page = BufferGetPage(buf);
btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum));
itup = &btitem->bti_itup; itup = &btitem->bti_itup;
if (_bt_checkqual(scan, itup)) { if (_bt_checkqual(scan, itup))
res = FormRetrieveIndexResult(current, &(itup->t_tid)); {
res = FormRetrieveIndexResult(current, &(itup->t_tid));
/* remember which buffer we have pinned and locked */ /* remember which buffer we have pinned and locked */
so->btso_curbuf = buf; so->btso_curbuf = buf;
} else { return (res);
ItemPointerSetInvalid(current); }
so->btso_curbuf = InvalidBuffer;
_bt_relbuf(rel, buf, BT_READ); } while ( _bt_checkforkeys (scan, itup, so->numberOfFirstKeys) );
res = (RetrieveIndexResult) NULL;
} ItemPointerSetInvalid(current);
so->btso_curbuf = InvalidBuffer;
_bt_relbuf(rel, buf, BT_READ);
return (res); return ((RetrieveIndexResult) NULL);
} }
/* /*
...@@ -660,13 +663,6 @@ _bt_first(IndexScanDesc scan, ScanDirection dir) ...@@ -660,13 +663,6 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* ordered to take advantage of index ordering) to position ourselves * ordered to take advantage of index ordering) to position ourselves
* at the right place in the scan. * at the right place in the scan.
*/ */
/*
* XXX -- The attribute number stored in the scan key is the attno
* in the heap relation. We need to transmogrify this into
* the index relation attno here. For the moment, we have
* hardwired attno == 1.
*/
proc = index_getprocid(rel, 1, BTORDER_PROC); proc = index_getprocid(rel, 1, BTORDER_PROC);
ScanKeyEntryInitialize(&skdata, so->keyData[0].sk_flags, 1, proc, ScanKeyEntryInitialize(&skdata, so->keyData[0].sk_flags, 1, proc,
so->keyData[0].sk_argument); so->keyData[0].sk_argument);
...@@ -802,12 +798,20 @@ _bt_first(IndexScanDesc scan, ScanDirection dir) ...@@ -802,12 +798,20 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum));
itup = &btitem->bti_itup; itup = &btitem->bti_itup;
if (_bt_checkqual(scan, itup)) { if ( _bt_checkqual(scan, itup) )
{
res = FormRetrieveIndexResult(current, &(itup->t_tid)); res = FormRetrieveIndexResult(current, &(itup->t_tid));
/* remember which buffer we have pinned */ /* remember which buffer we have pinned */
so->btso_curbuf = buf; so->btso_curbuf = buf;
} else { }
else if ( _bt_checkforkeys (scan, itup, so->numberOfFirstKeys) )
{
so->btso_curbuf = buf;
return (_bt_next (scan, dir));
}
else
{
ItemPointerSetInvalid(current); ItemPointerSetInvalid(current);
so->btso_curbuf = InvalidBuffer; so->btso_curbuf = InvalidBuffer;
_bt_relbuf(rel, buf, BT_READ); _bt_relbuf(rel, buf, BT_READ);
...@@ -1224,7 +1228,14 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir) ...@@ -1224,7 +1228,14 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
/* remember which buffer we have pinned */ /* remember which buffer we have pinned */
so->btso_curbuf = buf; so->btso_curbuf = buf;
} else { }
else if ( _bt_checkforkeys (scan, itup, so->numberOfFirstKeys) )
{
so->btso_curbuf = buf;
return (_bt_next (scan, dir));
}
else
{
_bt_relbuf(rel, buf, BT_READ); _bt_relbuf(rel, buf, BT_READ);
res = (RetrieveIndexResult) NULL; res = (RetrieveIndexResult) NULL;
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtutils.c,v 1.7 1996/11/05 10:35:38 scrappy Exp $ * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtutils.c,v 1.8 1997/03/18 18:38:46 scrappy Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -80,7 +80,7 @@ _bt_freestack(BTStack stack) ...@@ -80,7 +80,7 @@ _bt_freestack(BTStack stack)
* more than one qual clauses using this index. * more than one qual clauses using this index.
*/ */
void void
_bt_orderkeys(Relation relation, uint16 *numberOfKeys, ScanKey key, uint16 *qual_ok) _bt_orderkeys(Relation relation, BTScanOpaque so)
{ {
ScanKey xform; ScanKey xform;
ScanKeyData *cur; ScanKeyData *cur;
...@@ -89,42 +89,137 @@ _bt_orderkeys(Relation relation, uint16 *numberOfKeys, ScanKey key, uint16 *qual ...@@ -89,42 +89,137 @@ _bt_orderkeys(Relation relation, uint16 *numberOfKeys, ScanKey key, uint16 *qual
long test; long test;
int i, j; int i, j;
int init[BTMaxStrategyNumber+1]; int init[BTMaxStrategyNumber+1];
ScanKey key;
uint16 numberOfKeys, new_numberOfKeys = 0;
AttrNumber attno = 1;
/* haven't looked at any strategies yet */ numberOfKeys = so->numberOfKeys;
for (i = 0; i <= BTMaxStrategyNumber; i++) key = so->keyData;
init[i] = 0;
if ( numberOfKeys <= 1 )
return;
/* get space for the modified array of keys */ /* get space for the modified array of keys */
nbytes = BTMaxStrategyNumber * sizeof(ScanKeyData); nbytes = BTMaxStrategyNumber * sizeof(ScanKeyData);
xform = (ScanKey) palloc(nbytes); xform = (ScanKey) palloc(nbytes);
memset(xform, 0, nbytes);
cur = &key[0];
/* get the strategy map for this index/attribute pair */ if ( cur->sk_attno != 1 )
/* elog (WARN, "_bt_orderkeys: key(s) for attribute 1 missed");
* XXX
* When we support multiple keys in a single index, this is what memset(xform, 0, nbytes);
* we'll want to do. At present, the planner is hosed, so we
* hard-wire the attribute number below. Postgres only does single-
* key indices...
* map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation),
* BTMaxStrategyNumber,
* key->data[0].attributeNumber);
*/
map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation), map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation),
BTMaxStrategyNumber, BTMaxStrategyNumber,
1 /* XXX */ ); attno);
for (j = 0; j <= BTMaxStrategyNumber; j++)
init[j] = 0;
/* check each key passed in */ /* check each key passed in */
for (i = *numberOfKeys; --i >= 0; ) { for (i = 0; ; )
cur = &key[i]; {
for (j = BTMaxStrategyNumber; --j >= 0; ) { if ( i < numberOfKeys )
cur = &key[i];
if ( i == numberOfKeys || cur->sk_attno != attno )
{
if ( cur->sk_attno != attno + 1 && i < numberOfKeys )
{
elog (WARN, "_bt_orderkeys: key(s) for attribute %d missed", attno + 1);
}
/*
* If = has been specified, no other key will be used.
* In case of key < 2 && key == 1 and so on
* we have to set qual_ok to 0
*/
if (init[BTEqualStrategyNumber - 1])
{
ScanKeyData *eq, *chk;
eq = &xform[BTEqualStrategyNumber - 1];
for (j = BTMaxStrategyNumber; --j >= 0; )
{
if ( j == (BTEqualStrategyNumber - 1) || init[j] == 0 )
continue;
chk = &xform[j];
test = (long) fmgr(chk->sk_procedure, eq->sk_argument, chk->sk_argument);
if (!test)
so->qual_ok = 0;
}
init[BTLessStrategyNumber - 1] = 0;
init[BTLessEqualStrategyNumber - 1] = 0;
init[BTGreaterEqualStrategyNumber - 1] = 0;
init[BTGreaterStrategyNumber - 1] = 0;
}
/* only one of <, <= */
if (init[BTLessStrategyNumber - 1]
&& init[BTLessEqualStrategyNumber - 1])
{
ScanKeyData *lt, *le;
lt = &xform[BTLessStrategyNumber - 1];
le = &xform[BTLessEqualStrategyNumber - 1];
/*
* DO NOT use the cached function stuff here -- this is key
* ordering, happens only when the user expresses a hokey
* qualification, and gets executed only once, anyway. The
* transform maps are hard-coded, and can't be initialized
* in the correct way.
*/
test = (long) fmgr(le->sk_procedure, lt->sk_argument, le->sk_argument);
if (test)
init[BTLessEqualStrategyNumber - 1] = 0;
else
init[BTLessStrategyNumber - 1] = 0;
}
/* only one of >, >= */
if (init[BTGreaterStrategyNumber - 1]
&& init[BTGreaterEqualStrategyNumber - 1])
{
ScanKeyData *gt, *ge;
gt = &xform[BTGreaterStrategyNumber - 1];
ge = &xform[BTGreaterEqualStrategyNumber - 1];
/* see note above on function cache */
test = (long) fmgr(ge->sk_procedure, gt->sk_argument, ge->sk_argument);
if (test)
init[BTGreaterEqualStrategyNumber - 1] = 0;
else
init[BTGreaterStrategyNumber - 1] = 0;
}
/* okay, reorder and count */
for (j = BTMaxStrategyNumber; --j >= 0; )
if (init[j])
key[new_numberOfKeys++] = xform[j];
if ( attno == 1 )
so->numberOfFirstKeys = new_numberOfKeys;
if ( i == numberOfKeys )
break;
/* initialization for new attno */
attno = cur->sk_attno;
memset(xform, 0, nbytes);
map = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(relation),
BTMaxStrategyNumber,
attno);
/* haven't looked at any strategies yet */
for (j = 0; j <= BTMaxStrategyNumber; j++)
init[j] = 0;
}
for (j = BTMaxStrategyNumber; --j >= 0; )
{
if (cur->sk_procedure == map->entry[j].sk_procedure) if (cur->sk_procedure == map->entry[j].sk_procedure)
break; break;
} }
/* have we seen one of these before? */ /* have we seen one of these before? */
if (init[j]) { if (init[j])
{
/* yup, use the appropriate value */ /* yup, use the appropriate value */
test = test =
(long) FMGR_PTR2(cur->sk_func, cur->sk_procedure, (long) FMGR_PTR2(cur->sk_func, cur->sk_procedure,
...@@ -132,97 +227,18 @@ _bt_orderkeys(Relation relation, uint16 *numberOfKeys, ScanKey key, uint16 *qual ...@@ -132,97 +227,18 @@ _bt_orderkeys(Relation relation, uint16 *numberOfKeys, ScanKey key, uint16 *qual
if (test) if (test)
xform[j].sk_argument = cur->sk_argument; xform[j].sk_argument = cur->sk_argument;
else if ( j == (BTEqualStrategyNumber - 1) ) else if ( j == (BTEqualStrategyNumber - 1) )
*qual_ok = 0; /* key == a && key == b, but a != b */ so->qual_ok = 0; /* key == a && key == b, but a != b */
} else { } else
{
/* nope, use this value */ /* nope, use this value */
memmove(&xform[j], cur, sizeof(*cur)); memmove(&xform[j], cur, sizeof(*cur));
init[j] = 1; init[j] = 1;
} }
}
/* if = has been specified, no other key will be used */
/*
* XXX
* But in case of key < 2 && key == 1 and so on
* we have to set qual_ok to 0
*/
if (init[BTEqualStrategyNumber - 1]) {
ScanKeyData *eq, *chk;
eq = &xform[BTEqualStrategyNumber - 1];
for (j = BTMaxStrategyNumber; --j >= 0; )
{
if ( j == (BTEqualStrategyNumber - 1) || init[j] == 0 )
continue;
chk = &xform[j];
test = (long) fmgr(chk->sk_procedure, eq->sk_argument, chk->sk_argument);
if (!test) i++;
*qual_ok = 0;
}
init[BTLessStrategyNumber - 1] = 0;
init[BTLessEqualStrategyNumber - 1] = 0;
init[BTGreaterEqualStrategyNumber - 1] = 0;
init[BTGreaterStrategyNumber - 1] = 0;
} }
/* only one of <, <= */ so->numberOfKeys = new_numberOfKeys;
if (init[BTLessStrategyNumber - 1]
&& init[BTLessEqualStrategyNumber - 1]) {
ScanKeyData *lt, *le;
lt = &xform[BTLessStrategyNumber - 1];
le = &xform[BTLessEqualStrategyNumber - 1];
/*
* DO NOT use the cached function stuff here -- this is key
* ordering, happens only when the user expresses a hokey
* qualification, and gets executed only once, anyway. The
* transform maps are hard-coded, and can't be initialized
* in the correct way.
*/
test = (long) fmgr(le->sk_procedure, lt->sk_argument, le->sk_argument);
if (test)
init[BTLessEqualStrategyNumber - 1] = 0;
else
init[BTLessStrategyNumber - 1] = 0;
}
/* only one of >, >= */
if (init[BTGreaterStrategyNumber - 1]
&& init[BTGreaterEqualStrategyNumber - 1]) {
ScanKeyData *gt, *ge;
gt = &xform[BTGreaterStrategyNumber - 1];
ge = &xform[BTGreaterEqualStrategyNumber - 1];
/* see note above on function cache */
test = (long) fmgr(ge->sk_procedure, gt->sk_argument, ge->sk_argument);
if (test)
init[BTGreaterEqualStrategyNumber - 1] = 0;
else
init[BTGreaterStrategyNumber - 1] = 0;
}
/* okay, reorder and count */
j = 0;
for (i = BTMaxStrategyNumber; --i >= 0; )
if (init[i])
key[j++] = xform[i];
*numberOfKeys = j;
pfree(xform); pfree(xform);
} }
...@@ -230,9 +246,25 @@ _bt_orderkeys(Relation relation, uint16 *numberOfKeys, ScanKey key, uint16 *qual ...@@ -230,9 +246,25 @@ _bt_orderkeys(Relation relation, uint16 *numberOfKeys, ScanKey key, uint16 *qual
bool bool
_bt_checkqual(IndexScanDesc scan, IndexTuple itup) _bt_checkqual(IndexScanDesc scan, IndexTuple itup)
{ {
if (scan->numberOfKeys > 0) BTScanOpaque so;
so = (BTScanOpaque) scan->opaque;
if (so->numberOfKeys > 0)
return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation),
so->numberOfKeys, so->keyData));
else
return (true);
}
bool
_bt_checkforkeys(IndexScanDesc scan, IndexTuple itup, Size keysz)
{
BTScanOpaque so;
so = (BTScanOpaque) scan->opaque;
if ( keysz > 0 && so->numberOfKeys >= keysz )
return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation), return (index_keytest(itup, RelationGetTupleDescriptor(scan->relation),
scan->numberOfKeys, scan->keyData)); keysz, so->keyData));
else else
return (true); return (true);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.6 1997/03/12 21:00:17 scrappy Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.7 1997/03/18 18:39:40 scrappy Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,8 +53,9 @@ static bool match_index_to_operand(int indexkey, Expr *operand, ...@@ -53,8 +53,9 @@ static bool match_index_to_operand(int indexkey, Expr *operand,
static List *match_index_orclause(Rel *rel, Rel *index, int indexkey, static List *match_index_orclause(Rel *rel, Rel *index, int indexkey,
int xclass, List *or_clauses, List *other_matching_indices); int xclass, List *or_clauses, List *other_matching_indices);
static List *group_clauses_by_indexkey(Rel *rel, Rel *index, static List *group_clauses_by_indexkey(Rel *rel, Rel *index,
int *indexkeys, Oid *classes, List *clauseinfo_list, int *indexkeys, Oid *classes, List *clauseinfo_list);
bool join); static List *group_clauses_by_ikey_for_joins(Rel *rel, Rel *index,
int *indexkeys, Oid *classes, List *join_cinfo_list, List *restr_cinfo_list);
static CInfo *match_clause_to_indexkey(Rel *rel, Rel *index, int indexkey, static CInfo *match_clause_to_indexkey(Rel *rel, Rel *index, int indexkey,
int xclass, CInfo *clauseInfo, bool join); int xclass, CInfo *clauseInfo, bool join);
static bool pred_test(List *predicate_list, List *clauseinfo_list, static bool pred_test(List *predicate_list, List *clauseinfo_list,
...@@ -63,7 +64,8 @@ static bool one_pred_test(Expr *predicate, List *clauseinfo_list); ...@@ -63,7 +64,8 @@ static bool one_pred_test(Expr *predicate, List *clauseinfo_list);
static bool one_pred_clause_expr_test(Expr *predicate, Node *clause); static bool one_pred_clause_expr_test(Expr *predicate, Node *clause);
static bool one_pred_clause_test(Expr *predicate, Node *clause); static bool one_pred_clause_test(Expr *predicate, Node *clause);
static bool clause_pred_clause_test(Expr *predicate, Node *clause); static bool clause_pred_clause_test(Expr *predicate, Node *clause);
static List *indexable_joinclauses(Rel *rel, Rel *index, List *joininfo_list); static List *indexable_joinclauses (Rel *rel, Rel *index,
List *joininfo_list, List *clauseinfo_list);
static List *index_innerjoin(Query *root, Rel *rel, static List *index_innerjoin(Query *root, Rel *rel,
List *clausegroup_list, Rel *index); List *clausegroup_list, Rel *index);
static List *create_index_paths(Query *root, Rel *rel, Rel *index, static List *create_index_paths(Query *root, Rel *rel, Rel *index,
...@@ -114,7 +116,6 @@ find_index_paths (Query *root, ...@@ -114,7 +116,6 @@ find_index_paths (Query *root,
List *joinclausegroups = NIL; List *joinclausegroups = NIL;
List *joinpaths = NIL; List *joinpaths = NIL;
List *retval = NIL; List *retval = NIL;
extern List *add_index_paths();
if(indices == NIL) if(indices == NIL)
return(NULL); return(NULL);
...@@ -160,8 +161,7 @@ find_index_paths (Query *root, ...@@ -160,8 +161,7 @@ find_index_paths (Query *root,
index, index,
index->indexkeys, index->indexkeys,
index->classlist, index->classlist,
clauseinfo_list, clauseinfo_list);
false);
scanpaths = NIL; scanpaths = NIL;
if (scanclausegroups != NIL) if (scanclausegroups != NIL)
...@@ -178,7 +178,7 @@ find_index_paths (Query *root, ...@@ -178,7 +178,7 @@ find_index_paths (Query *root,
* useful for a mergejoin, or if the index can possibly be * useful for a mergejoin, or if the index can possibly be
* used for scanning the inner relation of a nestloop join. * used for scanning the inner relation of a nestloop join.
*/ */
joinclausegroups = indexable_joinclauses(rel,index,joininfo_list); joinclausegroups = indexable_joinclauses(rel,index,joininfo_list, clauseinfo_list);
joinpaths = NIL; joinpaths = NIL;
if (joinclausegroups != NIL) if (joinclausegroups != NIL)
...@@ -375,10 +375,8 @@ match_index_orclause(Rel *rel, ...@@ -375,10 +375,8 @@ match_index_orclause(Rel *rel,
* (2) a list of join clauses between 'rel' and a fixed set of * (2) a list of join clauses between 'rel' and a fixed set of
* relations, * relations,
* depending on the value of 'join'. * depending on the value of 'join'.
* 'startlist' is a list of those clause nodes that have matched the keys *
* that have already been checked. * NOTE: it works now for restriction clauses only. - vadim 03/18/97
* 'join' is a flag indicating that the clauses being checked are join
* clauses.
* *
* Returns all possible groups of clauses that will match (given that * Returns all possible groups of clauses that will match (given that
* one or more clauses can match any of the remaining keys). * one or more clauses can match any of the remaining keys).
...@@ -391,45 +389,144 @@ group_clauses_by_indexkey(Rel *rel, ...@@ -391,45 +389,144 @@ group_clauses_by_indexkey(Rel *rel,
Rel *index, Rel *index,
int *indexkeys, int *indexkeys,
Oid *classes, Oid *classes,
List *clauseinfo_list, List *clauseinfo_list)
bool join)
{ {
List *curCinfo = NIL; List *curCinfo = NIL;
CInfo *matched_clause = (CInfo*)NULL; CInfo *matched_clause = (CInfo*)NULL;
List *clausegroup = NIL; List *clausegroup = NIL;
int curIndxKey;
Oid curClass;
if (clauseinfo_list == NIL) if (clauseinfo_list == NIL)
return NIL; return NIL;
foreach (curCinfo,clauseinfo_list) { while ( !DoneMatchingIndexKeys(indexkeys, index) )
CInfo *temp = (CInfo*)lfirst(curCinfo); {
int *curIndxKey = indexkeys; List *tempgroup = NIL;
Oid *curClass = classes;
curIndxKey = indexkeys[0];
curClass = classes[0];
foreach (curCinfo,clauseinfo_list)
{
CInfo *temp = (CInfo*)lfirst(curCinfo);
matched_clause = match_clause_to_indexkey (rel,
index,
curIndxKey,
curClass,
temp,
false);
if (!matched_clause)
continue;
tempgroup = lappend(tempgroup, matched_clause);
}
if ( tempgroup == NIL )
break;
clausegroup = nconc (clausegroup, tempgroup);
indexkeys++;
classes++;
}
/* clausegroup holds all matched clauses ordered by indexkeys */
if (clausegroup != NIL)
return(lcons(clausegroup, NIL));
return NIL;
}
/*
* group-clauses-by-ikey-for-joins--
* special edition of group-clauses-by-indexkey - will
* match join & restriction clauses. See comment in indexable_joinclauses.
* - vadim 03/18/97
*
*/
static List *
group_clauses_by_ikey_for_joins(Rel *rel,
Rel *index,
int *indexkeys,
Oid *classes,
List *join_cinfo_list,
List *restr_cinfo_list)
{
List *curCinfo = NIL;
CInfo *matched_clause = (CInfo*)NULL;
List *clausegroup = NIL;
int curIndxKey;
Oid curClass;
bool jfound = false;
if (join_cinfo_list == NIL)
return NIL;
while ( !DoneMatchingIndexKeys(indexkeys, index) )
{
List *tempgroup = NIL;
curIndxKey = indexkeys[0];
curClass = classes[0];
foreach (curCinfo,join_cinfo_list)
{
CInfo *temp = (CInfo*)lfirst(curCinfo);
matched_clause = match_clause_to_indexkey (rel,
index,
curIndxKey,
curClass,
temp,
true);
if (!matched_clause)
continue;
tempgroup = lappend(tempgroup, matched_clause);
jfound = true;
}
foreach (curCinfo,restr_cinfo_list)
{
CInfo *temp = (CInfo*)lfirst(curCinfo);
do {
/*
* If we can't find any matching clauses for the first of
* the remaining keys, give up.
*/
matched_clause = match_clause_to_indexkey (rel, matched_clause = match_clause_to_indexkey (rel,
index, index,
curIndxKey[0], curIndxKey,
curClass[0], curClass,
temp, temp,
join); false);
if (!matched_clause) if (!matched_clause)
break; continue;
clausegroup = lcons(matched_clause, clausegroup); tempgroup = lappend(tempgroup, matched_clause);
curIndxKey++; }
curClass++; if ( tempgroup == NIL )
break;
} while ( !DoneMatchingIndexKeys(curIndxKey, index) ); clausegroup = nconc (clausegroup, tempgroup);
indexkeys++;
classes++;
} }
/* clausegroup holds all matched clauses ordered by indexkeys */
if (clausegroup != NIL) if (clausegroup != NIL)
{
/*
* if no one join clause was matched then there ain't clauses
* for joins at all.
*/
if ( !jfound )
{
freeList (clausegroup);
return NIL;
}
return(lcons(clausegroup, NIL)); return(lcons(clausegroup, NIL));
}
return NIL; return NIL;
} }
...@@ -482,6 +579,7 @@ match_clause_to_indexkey(Rel *rel, ...@@ -482,6 +579,7 @@ match_clause_to_indexkey(Rel *rel,
Expr *clause = clauseInfo->clause; Expr *clause = clauseInfo->clause;
Var *leftop, *rightop; Var *leftop, *rightop;
Oid join_op = InvalidOid; Oid join_op = InvalidOid;
Oid restrict_op = InvalidOid;
bool isIndexable = false; bool isIndexable = false;
if (or_clause((Node*)clause) || if (or_clause((Node*)clause) ||
...@@ -495,90 +593,87 @@ match_clause_to_indexkey(Rel *rel, ...@@ -495,90 +593,87 @@ match_clause_to_indexkey(Rel *rel,
* (operator var/func constant) and (operator constant var/func) * (operator var/func constant) and (operator constant var/func)
*/ */
if (!join) if (!join)
{ {
Oid restrict_op = InvalidOid; /*
* Check for standard s-argable clause
/* */
* Check for standard s-argable clause
*/
#ifdef INDEXSCAN_PATCH #ifdef INDEXSCAN_PATCH
/* Handle also function parameters. DZ - 27-8-1996 */ /* Handle also function parameters. DZ - 27-8-1996 */
if ((rightop && IsA(rightop,Const)) || if ((rightop && IsA(rightop,Const)) ||
(rightop && IsA(rightop,Param))) (rightop && IsA(rightop,Param)))
#else #else
if (rightop && IsA(rightop,Const)) if (rightop && IsA(rightop,Const))
#endif #endif
{ {
restrict_op = ((Oper*)((Expr*)clause)->oper)->opno; restrict_op = ((Oper*)((Expr*)clause)->oper)->opno;
isIndexable = isIndexable =
( op_class(restrict_op, xclass, index->relam) && ( op_class(restrict_op, xclass, index->relam) &&
IndexScanableOperand(leftop, IndexScanableOperand(leftop,
indexkey, indexkey,
rel, rel,
index) ); index) );
} }
/* /*
* Must try to commute the clause to standard s-arg format. * Must try to commute the clause to standard s-arg format.
*/ */
#ifdef INDEXSCAN_PATCH #ifdef INDEXSCAN_PATCH
/* ...And here... - vadim 01/22/97 */ /* ...And here... - vadim 01/22/97 */
else if ((leftop && IsA(leftop,Const)) || else if ((leftop && IsA(leftop,Const)) ||
(leftop && IsA(leftop,Param))) (leftop && IsA(leftop,Param)))
#else #else
else if (leftop && IsA(leftop,Const)) else if (leftop && IsA(leftop,Const))
#endif #endif
{ {
restrict_op = restrict_op =
get_commutator(((Oper*)((Expr*)clause)->oper)->opno); get_commutator(((Oper*)((Expr*)clause)->oper)->opno);
if ( (restrict_op != InvalidOid) && if ( (restrict_op != InvalidOid) &&
op_class(restrict_op, xclass, index->relam) && op_class(restrict_op, xclass, index->relam) &&
IndexScanableOperand(rightop, IndexScanableOperand(rightop,
indexkey,rel,index) ) indexkey,rel,index) )
{ {
isIndexable = true; isIndexable = true;
/* /*
* In place list modification. * In place list modification.
* (op const var/func) -> (op var/func const) * (op const var/func) -> (op var/func const)
*/ */
/* BUG! Old version: CommuteClause((Node*)clause);
CommuteClause(clause, restrict_op); }
*/ }
CommuteClause((Node*)clause); }
}
}
}
/* /*
* Check for an indexable scan on one of the join relations. * Check for an indexable scan on one of the join relations.
* clause is of the form (operator var/func var/func) * clause is of the form (operator var/func var/func)
*/ */
else else
{
if (rightop
&& match_index_to_operand(indexkey,(Expr*)rightop,rel,index))
{ {
if (rightop
&& match_index_to_operand(indexkey,(Expr*)rightop,rel,index)) {
join_op = get_commutator(((Oper*)((Expr*)clause)->oper)->opno); join_op = get_commutator(((Oper*)((Expr*)clause)->oper)->opno);
} else if (leftop } else if (leftop
&& match_index_to_operand(indexkey, && match_index_to_operand(indexkey,
(Expr*)leftop,rel,index)) { (Expr*)leftop,rel,index))
{
join_op = ((Oper*)((Expr*)clause)->oper)->opno; join_op = ((Oper*)((Expr*)clause)->oper)->opno;
} }
if ( join_op && op_class(join_op,xclass,index->relam) && if ( join_op && op_class(join_op,xclass,index->relam) &&
join_clause_p((Node*)clause)) join_clause_p((Node*)clause))
{ {
isIndexable = true; isIndexable = true;
/* /*
* If we're using the operand's commutator we must * If we're using the operand's commutator we must
* commute the clause. * commute the clause.
*/ */
if (join_op != ((Oper*)((Expr*)clause)->oper)->opno) if (join_op != ((Oper*)((Expr*)clause)->oper)->opno)
CommuteClause((Node*)clause); CommuteClause((Node*)clause);
}
} }
}
if (isIndexable) if (isIndexable)
return(clauseInfo); return(clauseInfo);
...@@ -955,10 +1050,15 @@ clause_pred_clause_test(Expr *predicate, Node *clause) ...@@ -955,10 +1050,15 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
* in the join clause as its outer join relation. * in the join clause as its outer join relation.
* *
* Returns a list of these clause groups. * Returns a list of these clause groups.
*
* Added: clauseinfo_list - list of restriction CInfos. It's to
* support multi-column indices in joins and for cases
* when a key is in both join & restriction clauses. - vadim 03/18/97
* *
*/ */
static List * static List *
indexable_joinclauses(Rel *rel, Rel *index, List *joininfo_list) indexable_joinclauses(Rel *rel, Rel *index,
List *joininfo_list, List *clauseinfo_list)
{ {
JInfo *joininfo = (JInfo*)NULL; JInfo *joininfo = (JInfo*)NULL;
List *cg_list = NIL; List *cg_list = NIL;
...@@ -967,13 +1067,16 @@ indexable_joinclauses(Rel *rel, Rel *index, List *joininfo_list) ...@@ -967,13 +1067,16 @@ indexable_joinclauses(Rel *rel, Rel *index, List *joininfo_list)
foreach(i,joininfo_list) { foreach(i,joininfo_list) {
joininfo = (JInfo*)lfirst(i); joininfo = (JInfo*)lfirst(i);
if ( joininfo->jinfoclauseinfo == NIL )
continue;
clausegroups = clausegroups =
group_clauses_by_indexkey (rel, group_clauses_by_ikey_for_joins (rel,
index, index,
index->indexkeys, index->indexkeys,
index->classlist, index->classlist,
joininfo->jinfoclauseinfo, joininfo->jinfoclauseinfo,
true); clauseinfo_list);
if (clausegroups != NIL) { if (clausegroups != NIL) {
List *clauses = lfirst(clausegroups); List *clauses = lfirst(clausegroups);
...@@ -1056,6 +1159,7 @@ index_innerjoin(Query *root, Rel *rel, List *clausegroup_list, Rel *index) ...@@ -1056,6 +1159,7 @@ index_innerjoin(Query *root, Rel *rel, List *clausegroup_list, Rel *index)
pathnode->path.pathtype = T_IndexScan; pathnode->path.pathtype = T_IndexScan;
pathnode->path.parent = rel; pathnode->path.parent = rel;
pathnode->indexid = index->relids; pathnode->indexid = index->relids;
pathnode->indexkeys = index->indexkeys;
pathnode->indexqual = clausegroup; pathnode->indexqual = clausegroup;
pathnode->path.joinid = ((CInfo*)lfirst(clausegroup))->cinfojoinid; pathnode->path.joinid = ((CInfo*)lfirst(clausegroup))->cinfojoinid;
...@@ -1130,7 +1234,7 @@ create_index_paths(Query *root, ...@@ -1130,7 +1234,7 @@ create_index_paths(Query *root,
temp = false; temp = false;
} }
} }
if (!join || temp) { /* restriction, ordering scan */ if (!join || temp) { /* restriction, ordering scan */
temp_path = create_index_path (root, rel,index,clausegroup,join); temp_path = create_index_path (root, rel,index,clausegroup,join);
temp_node = temp_node =
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.8 1997/03/12 21:05:56 scrappy Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.9 1997/03/18 18:40:05 scrappy Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -417,22 +417,35 @@ create_nestloop_node(JoinPath *best_path, ...@@ -417,22 +417,35 @@ create_nestloop_node(JoinPath *best_path,
NestLoop *join_node = (NestLoop*)NULL; NestLoop *join_node = (NestLoop*)NULL;
if (IsA(inner_node,IndexScan)) { if (IsA(inner_node,IndexScan)) {
/* An index is being used to reduce the number of tuples scanned in /* An index is being used to reduce the number of tuples scanned in
* the inner relation. * the inner relation. There will never be more than one index used
* There will never be more than one index used in the inner * in the inner scan path, so we need only consider the first set of
* scan path, so we need only consider the first set of * qualifications in indxqual.
* qualifications in indxqual. *
* But there may be more than one clauses in this "first set"
* in the case of multi-column indices. - vadim 03/18/97
*/ */
List *inner_indxqual = lfirst(((IndexScan*)inner_node)->indxqual); List *inner_indxqual = lfirst(((IndexScan*)inner_node)->indxqual);
List *inner_qual = (inner_indxqual == NULL)? NULL:lfirst(inner_indxqual); List *inner_qual;
bool found = false;
foreach (inner_qual, inner_indxqual)
{
if ( !(qual_clause_p ((Node*)inner_qual)) )
{
found = true;
break;
}
}
/* If we have in fact found a join index qualification, remove these /* If we have in fact found a join index qualification, remove these
* index clauses from the nestloop's join clauses and reset the * index clauses from the nestloop's join clauses and reset the
* inner(index) scan's qualification so that the var nodes refer to * inner(index) scan's qualification so that the var nodes refer to
* the proper outer join relation attributes. * the proper outer join relation attributes.
*/ */
if (!(qual_clause_p((Node*)inner_qual))) { if ( found )
{
List *new_inner_qual = NIL; List *new_inner_qual = NIL;
clauses = set_difference(clauses,inner_indxqual); clauses = set_difference(clauses,inner_indxqual);
...@@ -613,7 +626,7 @@ fix_indxqual_references(Node *clause, Path *index_path) ...@@ -613,7 +626,7 @@ fix_indxqual_references(Node *clause, Path *index_path)
if (lfirsti(index_path->parent->relids) == ((Var*)clause)->varno) { if (lfirsti(index_path->parent->relids) == ((Var*)clause)->varno) {
int pos = 0; int pos = 0;
int varatt = ((Var*)clause)->varattno; int varatt = ((Var*)clause)->varattno;
int *indexkeys = index_path->parent->indexkeys; int *indexkeys = ((IndexPath*)index_path)->indexkeys;
if (indexkeys) { if (indexkeys) {
while (indexkeys[pos] != 0) { while (indexkeys[pos] != 0) {
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.2 1997/03/18 18:40:40 scrappy Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -248,10 +248,11 @@ create_index_path(Query *root, ...@@ -248,10 +248,11 @@ create_index_path(Query *root,
pathnode->path.pathtype = T_IndexScan; pathnode->path.pathtype = T_IndexScan;
pathnode->path.parent = rel; pathnode->path.parent = rel;
pathnode->indexid = index->relids;
pathnode->path.p_ordering.ordtype = SORTOP_ORDER; pathnode->path.p_ordering.ordtype = SORTOP_ORDER;
pathnode->path.p_ordering.ord.sortop = index->ordering; pathnode->path.p_ordering.ord.sortop = index->ordering;
pathnode->indexid = index->relids;
pathnode->indexkeys = index->indexkeys;
pathnode->indexqual = NIL; pathnode->indexqual = NIL;
/* copy clauseinfo list into path for expensive function processing /* copy clauseinfo list into path for expensive function processing
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: nbtree.h,v 1.9 1997/02/22 10:08:27 vadim Exp $ * $Id: nbtree.h,v 1.10 1997/03/18 18:41:16 scrappy Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -68,6 +68,7 @@ typedef struct BTScanOpaqueData { ...@@ -68,6 +68,7 @@ typedef struct BTScanOpaqueData {
Buffer btso_mrkbuf; Buffer btso_mrkbuf;
uint16 qual_ok; /* 0 for quals like key == 1 && key > 2 */ uint16 qual_ok; /* 0 for quals like key == 1 && key > 2 */
uint16 numberOfKeys; /* number of key attributes */ uint16 numberOfKeys; /* number of key attributes */
uint16 numberOfFirstKeys; /* number of first key attributes */
ScanKey keyData; /* key descriptor */ ScanKey keyData; /* key descriptor */
} BTScanOpaqueData; } BTScanOpaqueData;
...@@ -270,9 +271,9 @@ extern bool _bt_invokestrat(Relation rel, AttrNumber attno, ...@@ -270,9 +271,9 @@ extern bool _bt_invokestrat(Relation rel, AttrNumber attno,
extern ScanKey _bt_mkscankey(Relation rel, IndexTuple itup); extern ScanKey _bt_mkscankey(Relation rel, IndexTuple itup);
extern void _bt_freeskey(ScanKey skey); extern void _bt_freeskey(ScanKey skey);
extern void _bt_freestack(BTStack stack); extern void _bt_freestack(BTStack stack);
extern void _bt_orderkeys(Relation relation, uint16 *numberOfKeys, extern void _bt_orderkeys(Relation relation, BTScanOpaque so);
ScanKey key, uint16 *qual_ok);
extern bool _bt_checkqual(IndexScanDesc scan, IndexTuple itup); extern bool _bt_checkqual(IndexScanDesc scan, IndexTuple itup);
extern bool _bt_checkforkeys(IndexScanDesc scan, IndexTuple itup, Size keysz);
extern BTItem _bt_formitem(IndexTuple itup); extern BTItem _bt_formitem(IndexTuple itup);
/* /*
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* $Id: relation.h,v 1.3 1996/11/06 07:44:18 scrappy Exp $ * $Id: relation.h,v 1.4 1997/03/18 18:41:37 scrappy Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -150,6 +150,7 @@ typedef struct IndexPath { ...@@ -150,6 +150,7 @@ typedef struct IndexPath {
Path path; Path path;
List *indexid; List *indexid;
List *indexqual; List *indexqual;
int *indexkeys; /* to transform heap attnos into index ones */
} IndexPath; } IndexPath;
typedef struct JoinPath { typedef struct JoinPath {
......
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