mirror of
https://github.com/postgres/postgres.git
synced 2025-06-02 00:01:40 -04:00
Allow row comparisons to be used as indexscan qualifications.
This completes the project to upgrade our handling of row comparisons.
This commit is contained in:
parent
06d45e485d
commit
3a0a16cb7e
@ -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/nbtree/nbtsearch.c,v 1.101 2006/01/23 22:31:40 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.102 2006/01/25 20:29:23 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -551,6 +551,10 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
|
|||||||
* one we use --- by definition, they are either redundant or
|
* one we use --- by definition, they are either redundant or
|
||||||
* contradictory.
|
* contradictory.
|
||||||
*
|
*
|
||||||
|
* In this loop, row-comparison keys are treated the same as keys on their
|
||||||
|
* first (leftmost) columns. We'll add on lower-order columns of the row
|
||||||
|
* comparison below, if possible.
|
||||||
|
*
|
||||||
* The selected scan keys (at most one per index column) are remembered by
|
* The selected scan keys (at most one per index column) are remembered by
|
||||||
* storing their addresses into the local startKeys[] array.
|
* storing their addresses into the local startKeys[] array.
|
||||||
*----------
|
*----------
|
||||||
@ -657,44 +661,91 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
|
|||||||
{
|
{
|
||||||
ScanKey cur = startKeys[i];
|
ScanKey cur = startKeys[i];
|
||||||
|
|
||||||
/*
|
Assert(cur->sk_attno == i+1);
|
||||||
* _bt_preprocess_keys disallows it, but it's place to add some code
|
|
||||||
* later
|
|
||||||
*/
|
|
||||||
if (cur->sk_flags & SK_ISNULL)
|
|
||||||
elog(ERROR, "btree doesn't support is(not)null, yet");
|
|
||||||
|
|
||||||
/*
|
if (cur->sk_flags & SK_ROW_HEADER)
|
||||||
* If scankey operator is of default subtype, we can use the cached
|
|
||||||
* comparison procedure; otherwise gotta look it up in the catalogs.
|
|
||||||
*/
|
|
||||||
if (cur->sk_subtype == InvalidOid)
|
|
||||||
{
|
{
|
||||||
FmgrInfo *procinfo;
|
/*
|
||||||
|
* Row comparison header: look to the first row member instead.
|
||||||
procinfo = index_getprocinfo(rel, i + 1, BTORDER_PROC);
|
*
|
||||||
ScanKeyEntryInitializeWithInfo(scankeys + i,
|
* The member scankeys are already in insertion format (ie, they
|
||||||
cur->sk_flags,
|
* have sk_func = 3-way-comparison function), but we have to
|
||||||
i + 1,
|
* watch out for nulls, which _bt_preprocess_keys didn't check.
|
||||||
InvalidStrategy,
|
* A null in the first row member makes the condition unmatchable,
|
||||||
InvalidOid,
|
* just like qual_ok = false.
|
||||||
procinfo,
|
*/
|
||||||
cur->sk_argument);
|
cur = (ScanKey) DatumGetPointer(cur->sk_argument);
|
||||||
|
Assert(cur->sk_flags & SK_ROW_MEMBER);
|
||||||
|
if (cur->sk_flags & SK_ISNULL)
|
||||||
|
return false;
|
||||||
|
memcpy(scankeys + i, cur, sizeof(ScanKeyData));
|
||||||
|
/*
|
||||||
|
* If the row comparison is the last positioning key we accepted,
|
||||||
|
* try to add additional keys from the lower-order row members.
|
||||||
|
* (If we accepted independent conditions on additional index
|
||||||
|
* columns, we use those instead --- doesn't seem worth trying to
|
||||||
|
* determine which is more restrictive.) Note that this is OK
|
||||||
|
* even if the row comparison is of ">" or "<" type, because the
|
||||||
|
* condition applied to all but the last row member is effectively
|
||||||
|
* ">=" or "<=", and so the extra keys don't break the positioning
|
||||||
|
* scheme.
|
||||||
|
*/
|
||||||
|
if (i == keysCount - 1)
|
||||||
|
{
|
||||||
|
while (!(cur->sk_flags & SK_ROW_END))
|
||||||
|
{
|
||||||
|
cur++;
|
||||||
|
Assert(cur->sk_flags & SK_ROW_MEMBER);
|
||||||
|
if (cur->sk_attno != keysCount + 1)
|
||||||
|
break; /* out-of-sequence, can't use it */
|
||||||
|
if (cur->sk_flags & SK_ISNULL)
|
||||||
|
break; /* can't use null keys */
|
||||||
|
Assert(keysCount < INDEX_MAX_KEYS);
|
||||||
|
memcpy(scankeys + keysCount, cur, sizeof(ScanKeyData));
|
||||||
|
keysCount++;
|
||||||
|
}
|
||||||
|
break; /* done with outer loop */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
RegProcedure cmp_proc;
|
/*
|
||||||
|
* Ordinary comparison key. Transform the search-style scan key
|
||||||
|
* to an insertion scan key by replacing the sk_func with the
|
||||||
|
* appropriate btree comparison function.
|
||||||
|
*
|
||||||
|
* If scankey operator is of default subtype, we can use the
|
||||||
|
* cached comparison function; otherwise gotta look it up in the
|
||||||
|
* catalogs.
|
||||||
|
*/
|
||||||
|
if (cur->sk_subtype == InvalidOid)
|
||||||
|
{
|
||||||
|
FmgrInfo *procinfo;
|
||||||
|
|
||||||
cmp_proc = get_opclass_proc(rel->rd_indclass->values[i],
|
procinfo = index_getprocinfo(rel, cur->sk_attno, BTORDER_PROC);
|
||||||
cur->sk_subtype,
|
ScanKeyEntryInitializeWithInfo(scankeys + i,
|
||||||
BTORDER_PROC);
|
cur->sk_flags,
|
||||||
ScanKeyEntryInitialize(scankeys + i,
|
cur->sk_attno,
|
||||||
cur->sk_flags,
|
InvalidStrategy,
|
||||||
i + 1,
|
InvalidOid,
|
||||||
InvalidStrategy,
|
procinfo,
|
||||||
cur->sk_subtype,
|
cur->sk_argument);
|
||||||
cmp_proc,
|
}
|
||||||
cur->sk_argument);
|
else
|
||||||
|
{
|
||||||
|
RegProcedure cmp_proc;
|
||||||
|
|
||||||
|
cmp_proc = get_opclass_proc(rel->rd_indclass->values[i],
|
||||||
|
cur->sk_subtype,
|
||||||
|
BTORDER_PROC);
|
||||||
|
ScanKeyEntryInitialize(scankeys + i,
|
||||||
|
cur->sk_flags,
|
||||||
|
cur->sk_attno,
|
||||||
|
InvalidStrategy,
|
||||||
|
cur->sk_subtype,
|
||||||
|
cmp_proc,
|
||||||
|
cur->sk_argument);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.69 2006/01/23 22:31:40 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.70 2006/01/25 20:29:23 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -21,6 +21,12 @@
|
|||||||
#include "executor/execdebug.h"
|
#include "executor/execdebug.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void _bt_mark_scankey_required(ScanKey skey);
|
||||||
|
static bool _bt_check_rowcompare(ScanKey skey,
|
||||||
|
IndexTuple tuple, TupleDesc tupdesc,
|
||||||
|
ScanDirection dir, bool *continuescan);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* _bt_mkscankey
|
* _bt_mkscankey
|
||||||
* Build an insertion scan key that contains comparison data from itup
|
* Build an insertion scan key that contains comparison data from itup
|
||||||
@ -218,6 +224,17 @@ _bt_formitem(IndexTuple itup)
|
|||||||
* equality quals for all columns. In this case there can be at most one
|
* equality quals for all columns. In this case there can be at most one
|
||||||
* (visible) matching tuple. index_getnext uses this to avoid uselessly
|
* (visible) matching tuple. index_getnext uses this to avoid uselessly
|
||||||
* continuing the scan after finding one match.
|
* continuing the scan after finding one match.
|
||||||
|
*
|
||||||
|
* Row comparison keys are treated the same as comparisons to nondefault
|
||||||
|
* datatypes: we just transfer them into the preprocessed array without any
|
||||||
|
* editorialization. We can treat them the same as an ordinary inequality
|
||||||
|
* comparison on the row's first index column, for the purposes of the logic
|
||||||
|
* about required keys.
|
||||||
|
*
|
||||||
|
* Note: the reason we have to copy the preprocessed scan keys into private
|
||||||
|
* storage is that we are modifying the array based on comparisons of the
|
||||||
|
* key argument values, which could change on a rescan. Therefore we can't
|
||||||
|
* overwrite the caller's data structure.
|
||||||
*----------
|
*----------
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
@ -273,26 +290,8 @@ _bt_preprocess_keys(IndexScanDesc scan)
|
|||||||
memcpy(outkeys, inkeys, sizeof(ScanKeyData));
|
memcpy(outkeys, inkeys, sizeof(ScanKeyData));
|
||||||
so->numberOfKeys = 1;
|
so->numberOfKeys = 1;
|
||||||
/* We can mark the qual as required if it's for first index col */
|
/* We can mark the qual as required if it's for first index col */
|
||||||
if (outkeys->sk_attno == 1)
|
if (cur->sk_attno == 1)
|
||||||
{
|
_bt_mark_scankey_required(outkeys);
|
||||||
switch (outkeys->sk_strategy)
|
|
||||||
{
|
|
||||||
case BTLessStrategyNumber:
|
|
||||||
case BTLessEqualStrategyNumber:
|
|
||||||
outkeys->sk_flags |= SK_BT_REQFWD;
|
|
||||||
break;
|
|
||||||
case BTEqualStrategyNumber:
|
|
||||||
outkeys->sk_flags |= (SK_BT_REQFWD | SK_BT_REQBKWD);
|
|
||||||
break;
|
|
||||||
case BTGreaterEqualStrategyNumber:
|
|
||||||
case BTGreaterStrategyNumber:
|
|
||||||
outkeys->sk_flags |= SK_BT_REQBKWD;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
elog(ERROR, "unrecognized StrategyNumber: %d",
|
|
||||||
(int) outkeys->sk_strategy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,6 +324,7 @@ _bt_preprocess_keys(IndexScanDesc scan)
|
|||||||
if (i < numberOfKeys)
|
if (i < numberOfKeys)
|
||||||
{
|
{
|
||||||
/* See comments above: any NULL implies cannot match qual */
|
/* See comments above: any NULL implies cannot match qual */
|
||||||
|
/* Note: we assume SK_ISNULL is never set in a row header key */
|
||||||
if (cur->sk_flags & SK_ISNULL)
|
if (cur->sk_flags & SK_ISNULL)
|
||||||
{
|
{
|
||||||
so->qual_ok = false;
|
so->qual_ok = false;
|
||||||
@ -432,26 +432,7 @@ _bt_preprocess_keys(IndexScanDesc scan)
|
|||||||
|
|
||||||
memcpy(outkey, xform[j], sizeof(ScanKeyData));
|
memcpy(outkey, xform[j], sizeof(ScanKeyData));
|
||||||
if (priorNumberOfEqualCols == attno - 1)
|
if (priorNumberOfEqualCols == attno - 1)
|
||||||
{
|
_bt_mark_scankey_required(outkey);
|
||||||
switch (outkey->sk_strategy)
|
|
||||||
{
|
|
||||||
case BTLessStrategyNumber:
|
|
||||||
case BTLessEqualStrategyNumber:
|
|
||||||
outkey->sk_flags |= SK_BT_REQFWD;
|
|
||||||
break;
|
|
||||||
case BTEqualStrategyNumber:
|
|
||||||
outkey->sk_flags |= (SK_BT_REQFWD |
|
|
||||||
SK_BT_REQBKWD);
|
|
||||||
break;
|
|
||||||
case BTGreaterEqualStrategyNumber:
|
|
||||||
case BTGreaterStrategyNumber:
|
|
||||||
outkey->sk_flags |= SK_BT_REQBKWD;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
elog(ERROR, "unrecognized StrategyNumber: %d",
|
|
||||||
(int) outkey->sk_strategy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,11 +451,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
|
|||||||
/* check strategy this key's operator corresponds to */
|
/* check strategy this key's operator corresponds to */
|
||||||
j = cur->sk_strategy - 1;
|
j = cur->sk_strategy - 1;
|
||||||
|
|
||||||
/* if wrong RHS data type, punt */
|
/* if row comparison or wrong RHS data type, punt */
|
||||||
if (cur->sk_subtype != InvalidOid)
|
if ((cur->sk_flags & SK_ROW_HEADER) || cur->sk_subtype != InvalidOid)
|
||||||
{
|
{
|
||||||
memcpy(&outkeys[new_numberOfKeys++], cur,
|
ScanKey outkey = &outkeys[new_numberOfKeys++];
|
||||||
sizeof(ScanKeyData));
|
|
||||||
|
memcpy(outkey, cur, sizeof(ScanKeyData));
|
||||||
|
if (numberOfEqualCols == attno - 1)
|
||||||
|
_bt_mark_scankey_required(outkey);
|
||||||
if (j == (BTEqualStrategyNumber - 1))
|
if (j == (BTEqualStrategyNumber - 1))
|
||||||
hasOtherTypeEqual = true;
|
hasOtherTypeEqual = true;
|
||||||
continue;
|
continue;
|
||||||
@ -514,6 +498,73 @@ _bt_preprocess_keys(IndexScanDesc scan)
|
|||||||
scan->keys_are_unique = true;
|
scan->keys_are_unique = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark a scankey as "required to continue the scan".
|
||||||
|
*
|
||||||
|
* Depending on the operator type, the key may be required for both scan
|
||||||
|
* directions or just one. Also, if the key is a row comparison header,
|
||||||
|
* we have to mark the appropriate subsidiary ScanKeys as required. In
|
||||||
|
* such cases, the first subsidiary key is required, but subsequent ones
|
||||||
|
* are required only as long as they correspond to successive index columns.
|
||||||
|
* Otherwise the row comparison ordering is different from the index ordering
|
||||||
|
* and so we can't stop the scan on the basis of those lower-order columns.
|
||||||
|
*
|
||||||
|
* Note: when we set required-key flag bits in a subsidiary scankey, we are
|
||||||
|
* scribbling on a data structure belonging to the index AM's caller, not on
|
||||||
|
* our private copy. This should be OK because the marking will not change
|
||||||
|
* from scan to scan within a query, and so we'd just re-mark the same way
|
||||||
|
* anyway on a rescan. Something to keep an eye on though.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
_bt_mark_scankey_required(ScanKey skey)
|
||||||
|
{
|
||||||
|
int addflags;
|
||||||
|
|
||||||
|
switch (skey->sk_strategy)
|
||||||
|
{
|
||||||
|
case BTLessStrategyNumber:
|
||||||
|
case BTLessEqualStrategyNumber:
|
||||||
|
addflags = SK_BT_REQFWD;
|
||||||
|
break;
|
||||||
|
case BTEqualStrategyNumber:
|
||||||
|
addflags = SK_BT_REQFWD | SK_BT_REQBKWD;
|
||||||
|
break;
|
||||||
|
case BTGreaterEqualStrategyNumber:
|
||||||
|
case BTGreaterStrategyNumber:
|
||||||
|
addflags = SK_BT_REQBKWD;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unrecognized StrategyNumber: %d",
|
||||||
|
(int) skey->sk_strategy);
|
||||||
|
addflags = 0; /* keep compiler quiet */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
skey->sk_flags |= addflags;
|
||||||
|
|
||||||
|
if (skey->sk_flags & SK_ROW_HEADER)
|
||||||
|
{
|
||||||
|
ScanKey subkey = (ScanKey) DatumGetPointer(skey->sk_argument);
|
||||||
|
AttrNumber attno = skey->sk_attno;
|
||||||
|
|
||||||
|
/* First subkey should be same as the header says */
|
||||||
|
Assert(subkey->sk_attno == attno);
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
Assert(subkey->sk_flags & SK_ROW_MEMBER);
|
||||||
|
Assert(subkey->sk_strategy == skey->sk_strategy);
|
||||||
|
if (subkey->sk_attno != attno)
|
||||||
|
break; /* non-adjacent key, so not required */
|
||||||
|
subkey->sk_flags |= addflags;
|
||||||
|
if (subkey->sk_flags & SK_ROW_END)
|
||||||
|
break;
|
||||||
|
subkey++;
|
||||||
|
attno++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Test whether an indextuple satisfies all the scankey conditions.
|
* Test whether an indextuple satisfies all the scankey conditions.
|
||||||
*
|
*
|
||||||
@ -595,6 +646,14 @@ _bt_checkkeys(IndexScanDesc scan,
|
|||||||
bool isNull;
|
bool isNull;
|
||||||
Datum test;
|
Datum test;
|
||||||
|
|
||||||
|
/* row-comparison keys need special processing */
|
||||||
|
if (key->sk_flags & SK_ROW_HEADER)
|
||||||
|
{
|
||||||
|
if (_bt_check_rowcompare(key, tuple, tupdesc, dir, continuescan))
|
||||||
|
continue;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
datum = index_getattr(tuple,
|
datum = index_getattr(tuple,
|
||||||
key->sk_attno,
|
key->sk_attno,
|
||||||
tupdesc,
|
tupdesc,
|
||||||
@ -660,3 +719,136 @@ _bt_checkkeys(IndexScanDesc scan,
|
|||||||
|
|
||||||
return tuple_valid;
|
return tuple_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test whether an indextuple satisfies a row-comparison scan condition.
|
||||||
|
*
|
||||||
|
* Return true if so, false if not. If not, also clear *continuescan if
|
||||||
|
* it's not possible for any future tuples in the current scan direction
|
||||||
|
* to pass the qual.
|
||||||
|
*
|
||||||
|
* This is a subroutine for _bt_checkkeys, which see for more info.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
_bt_check_rowcompare(ScanKey skey, IndexTuple tuple, TupleDesc tupdesc,
|
||||||
|
ScanDirection dir, bool *continuescan)
|
||||||
|
{
|
||||||
|
ScanKey subkey = (ScanKey) DatumGetPointer(skey->sk_argument);
|
||||||
|
int32 cmpresult = 0;
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
/* First subkey should be same as the header says */
|
||||||
|
Assert(subkey->sk_attno == skey->sk_attno);
|
||||||
|
|
||||||
|
/* Loop over columns of the row condition */
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
Datum datum;
|
||||||
|
bool isNull;
|
||||||
|
|
||||||
|
Assert(subkey->sk_flags & SK_ROW_MEMBER);
|
||||||
|
Assert(subkey->sk_strategy == skey->sk_strategy);
|
||||||
|
|
||||||
|
datum = index_getattr(tuple,
|
||||||
|
subkey->sk_attno,
|
||||||
|
tupdesc,
|
||||||
|
&isNull);
|
||||||
|
|
||||||
|
if (isNull)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Since NULLs are sorted after non-NULLs, we know we have reached
|
||||||
|
* the upper limit of the range of values for this index attr. On
|
||||||
|
* a forward scan, we can stop if this qual is one of the "must
|
||||||
|
* match" subset. On a backward scan, however, we should keep
|
||||||
|
* going.
|
||||||
|
*/
|
||||||
|
if ((subkey->sk_flags & SK_BT_REQFWD) &&
|
||||||
|
ScanDirectionIsForward(dir))
|
||||||
|
*continuescan = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In any case, this indextuple doesn't match the qual.
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subkey->sk_flags & SK_ISNULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Unlike the simple-scankey case, this isn't a disallowed case.
|
||||||
|
* But it can never match. If all the earlier row comparison
|
||||||
|
* columns are required for the scan direction, we can stop
|
||||||
|
* the scan, because there can't be another tuple that will
|
||||||
|
* succeed.
|
||||||
|
*/
|
||||||
|
if (subkey != (ScanKey) DatumGetPointer(skey->sk_argument))
|
||||||
|
subkey--;
|
||||||
|
if ((subkey->sk_flags & SK_BT_REQFWD) &&
|
||||||
|
ScanDirectionIsForward(dir))
|
||||||
|
*continuescan = false;
|
||||||
|
else if ((subkey->sk_flags & SK_BT_REQBKWD) &&
|
||||||
|
ScanDirectionIsBackward(dir))
|
||||||
|
*continuescan = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform the test --- three-way comparison not bool operator */
|
||||||
|
cmpresult = DatumGetInt32(FunctionCall2(&subkey->sk_func,
|
||||||
|
datum,
|
||||||
|
subkey->sk_argument));
|
||||||
|
|
||||||
|
/* Done comparing if unequal, else advance to next column */
|
||||||
|
if (cmpresult != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (subkey->sk_flags & SK_ROW_END)
|
||||||
|
break;
|
||||||
|
subkey++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At this point cmpresult indicates the overall result of the row
|
||||||
|
* comparison, and subkey points to the deciding column (or the last
|
||||||
|
* column if the result is "=").
|
||||||
|
*/
|
||||||
|
switch (subkey->sk_strategy)
|
||||||
|
{
|
||||||
|
/* EQ and NE cases aren't allowed here */
|
||||||
|
case BTLessStrategyNumber:
|
||||||
|
result = (cmpresult < 0);
|
||||||
|
break;
|
||||||
|
case BTLessEqualStrategyNumber:
|
||||||
|
result = (cmpresult <= 0);
|
||||||
|
break;
|
||||||
|
case BTGreaterEqualStrategyNumber:
|
||||||
|
result = (cmpresult >= 0);
|
||||||
|
break;
|
||||||
|
case BTGreaterStrategyNumber:
|
||||||
|
result = (cmpresult > 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unrecognized RowCompareType: %d",
|
||||||
|
(int) subkey->sk_strategy);
|
||||||
|
result = 0; /* keep compiler quiet */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Tuple fails this qual. If it's a required qual for the current
|
||||||
|
* scan direction, then we can conclude no further tuples will
|
||||||
|
* pass, either. Note we have to look at the deciding column, not
|
||||||
|
* necessarily the first or last column of the row condition.
|
||||||
|
*/
|
||||||
|
if ((subkey->sk_flags & SK_BT_REQFWD) &&
|
||||||
|
ScanDirectionIsForward(dir))
|
||||||
|
*continuescan = false;
|
||||||
|
else if ((subkey->sk_flags & SK_BT_REQBKWD) &&
|
||||||
|
ScanDirectionIsBackward(dir))
|
||||||
|
*continuescan = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/nodeBitmapIndexscan.c,v 1.14 2005/12/03 05:51:01 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/nodeBitmapIndexscan.c,v 1.15 2006/01/25 20:29:23 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -244,6 +244,20 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate)
|
|||||||
|
|
||||||
#define BITMAPINDEXSCAN_NSLOTS 0
|
#define BITMAPINDEXSCAN_NSLOTS 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We do not open or lock the base relation here. We assume that an
|
||||||
|
* ancestor BitmapHeapScan node is holding AccessShareLock (or better)
|
||||||
|
* on the heap relation throughout the execution of the plan tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
indexstate->ss.ss_currentRelation = NULL;
|
||||||
|
indexstate->ss.ss_currentScanDesc = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open the index relation.
|
||||||
|
*/
|
||||||
|
indexstate->biss_RelationDesc = index_open(node->indexid);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize index-specific scan state
|
* Initialize index-specific scan state
|
||||||
*/
|
*/
|
||||||
@ -255,6 +269,7 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate)
|
|||||||
* build the index scan keys from the index qualification
|
* build the index scan keys from the index qualification
|
||||||
*/
|
*/
|
||||||
ExecIndexBuildScanKeys((PlanState *) indexstate,
|
ExecIndexBuildScanKeys((PlanState *) indexstate,
|
||||||
|
indexstate->biss_RelationDesc,
|
||||||
node->indexqual,
|
node->indexqual,
|
||||||
node->indexstrategy,
|
node->indexstrategy,
|
||||||
node->indexsubtype,
|
node->indexsubtype,
|
||||||
@ -286,16 +301,8 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We do not open or lock the base relation here. We assume that an
|
* Initialize scan descriptor.
|
||||||
* ancestor BitmapHeapScan node is holding AccessShareLock (or better)
|
*
|
||||||
* on the heap relation throughout the execution of the plan tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
indexstate->ss.ss_currentRelation = NULL;
|
|
||||||
indexstate->ss.ss_currentScanDesc = NULL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Open the index relation and initialize relation and scan descriptors.
|
|
||||||
* Note we acquire no locks here; the index machinery does its own locks
|
* Note we acquire no locks here; the index machinery does its own locks
|
||||||
* and unlocks. (We rely on having a lock on the parent table to
|
* and unlocks. (We rely on having a lock on the parent table to
|
||||||
* ensure the index won't go away!) Furthermore, if the parent table
|
* ensure the index won't go away!) Furthermore, if the parent table
|
||||||
@ -303,7 +310,6 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate)
|
|||||||
* opened and write-locked the index, so we can tell the index machinery
|
* opened and write-locked the index, so we can tell the index machinery
|
||||||
* not to bother getting an extra lock.
|
* not to bother getting an extra lock.
|
||||||
*/
|
*/
|
||||||
indexstate->biss_RelationDesc = index_open(node->indexid);
|
|
||||||
relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid);
|
relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid);
|
||||||
indexstate->biss_ScanDesc =
|
indexstate->biss_ScanDesc =
|
||||||
index_beginscan_multi(indexstate->biss_RelationDesc,
|
index_beginscan_multi(indexstate->biss_RelationDesc,
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.109 2005/12/03 05:51:02 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.110 2006/01/25 20:29:23 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include "access/genam.h"
|
#include "access/genam.h"
|
||||||
#include "access/heapam.h"
|
#include "access/heapam.h"
|
||||||
|
#include "access/nbtree.h"
|
||||||
#include "executor/execdebug.h"
|
#include "executor/execdebug.h"
|
||||||
#include "executor/nodeIndexscan.h"
|
#include "executor/nodeIndexscan.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
@ -504,6 +505,24 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
|
|||||||
ExecInitResultTupleSlot(estate, &indexstate->ss.ps);
|
ExecInitResultTupleSlot(estate, &indexstate->ss.ps);
|
||||||
ExecInitScanTupleSlot(estate, &indexstate->ss);
|
ExecInitScanTupleSlot(estate, &indexstate->ss);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* open the base relation and acquire appropriate lock on it.
|
||||||
|
*/
|
||||||
|
currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
|
||||||
|
|
||||||
|
indexstate->ss.ss_currentRelation = currentRelation;
|
||||||
|
indexstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get the scan type from the relation descriptor.
|
||||||
|
*/
|
||||||
|
ExecAssignScanType(&indexstate->ss, RelationGetDescr(currentRelation), false);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open the index relation.
|
||||||
|
*/
|
||||||
|
indexstate->iss_RelationDesc = index_open(node->indexid);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize index-specific scan state
|
* Initialize index-specific scan state
|
||||||
*/
|
*/
|
||||||
@ -515,6 +534,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
|
|||||||
* build the index scan keys from the index qualification
|
* build the index scan keys from the index qualification
|
||||||
*/
|
*/
|
||||||
ExecIndexBuildScanKeys((PlanState *) indexstate,
|
ExecIndexBuildScanKeys((PlanState *) indexstate,
|
||||||
|
indexstate->iss_RelationDesc,
|
||||||
node->indexqual,
|
node->indexqual,
|
||||||
node->indexstrategy,
|
node->indexstrategy,
|
||||||
node->indexsubtype,
|
node->indexsubtype,
|
||||||
@ -545,20 +565,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* open the base relation and acquire appropriate lock on it.
|
* Initialize scan descriptor.
|
||||||
*/
|
*
|
||||||
currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
|
|
||||||
|
|
||||||
indexstate->ss.ss_currentRelation = currentRelation;
|
|
||||||
indexstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* get the scan type from the relation descriptor.
|
|
||||||
*/
|
|
||||||
ExecAssignScanType(&indexstate->ss, RelationGetDescr(currentRelation), false);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Open the index relation and initialize relation and scan descriptors.
|
|
||||||
* Note we acquire no locks here; the index machinery does its own locks
|
* Note we acquire no locks here; the index machinery does its own locks
|
||||||
* and unlocks. (We rely on having a lock on the parent table to
|
* and unlocks. (We rely on having a lock on the parent table to
|
||||||
* ensure the index won't go away!) Furthermore, if the parent table
|
* ensure the index won't go away!) Furthermore, if the parent table
|
||||||
@ -566,7 +574,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
|
|||||||
* opened and write-locked the index, so we can tell the index machinery
|
* opened and write-locked the index, so we can tell the index machinery
|
||||||
* not to bother getting an extra lock.
|
* not to bother getting an extra lock.
|
||||||
*/
|
*/
|
||||||
indexstate->iss_RelationDesc = index_open(node->indexid);
|
|
||||||
relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid);
|
relistarget = ExecRelationIsTargetRelation(estate, node->scan.scanrelid);
|
||||||
indexstate->iss_ScanDesc = index_beginscan(currentRelation,
|
indexstate->iss_ScanDesc = index_beginscan(currentRelation,
|
||||||
indexstate->iss_RelationDesc,
|
indexstate->iss_RelationDesc,
|
||||||
@ -595,7 +602,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
|
|||||||
* The index quals are passed to the index AM in the form of a ScanKey array.
|
* The index quals are passed to the index AM in the form of a ScanKey array.
|
||||||
* This routine sets up the ScanKeys, fills in all constant fields of the
|
* This routine sets up the ScanKeys, fills in all constant fields of the
|
||||||
* ScanKeys, and prepares information about the keys that have non-constant
|
* ScanKeys, and prepares information about the keys that have non-constant
|
||||||
* comparison values. We divide index qual expressions into three types:
|
* comparison values. We divide index qual expressions into four types:
|
||||||
*
|
*
|
||||||
* 1. Simple operator with constant comparison value ("indexkey op constant").
|
* 1. Simple operator with constant comparison value ("indexkey op constant").
|
||||||
* For these, we just fill in a ScanKey containing the constant value.
|
* For these, we just fill in a ScanKey containing the constant value.
|
||||||
@ -605,7 +612,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
|
|||||||
* expression value, and set up an IndexRuntimeKeyInfo struct to drive
|
* expression value, and set up an IndexRuntimeKeyInfo struct to drive
|
||||||
* evaluation of the expression at the right times.
|
* evaluation of the expression at the right times.
|
||||||
*
|
*
|
||||||
* 3. ScalarArrayOpExpr ("indexkey op ANY (array-expression)"). For these,
|
* 3. RowCompareExpr ("(indexkey, indexkey, ...) op (expr, expr, ...)").
|
||||||
|
* For these, we create a header ScanKey plus a subsidiary ScanKey array,
|
||||||
|
* as specified in access/skey.h. The elements of the row comparison
|
||||||
|
* can have either constant or non-constant comparison values.
|
||||||
|
*
|
||||||
|
* 4. ScalarArrayOpExpr ("indexkey op ANY (array-expression)"). For these,
|
||||||
* we create a ScanKey with everything filled in except the comparison value,
|
* we create a ScanKey with everything filled in except the comparison value,
|
||||||
* and set up an IndexArrayKeyInfo struct to drive processing of the qual.
|
* and set up an IndexArrayKeyInfo struct to drive processing of the qual.
|
||||||
* (Note that we treat all array-expressions as requiring runtime evaluation,
|
* (Note that we treat all array-expressions as requiring runtime evaluation,
|
||||||
@ -614,10 +626,15 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
|
|||||||
* Input params are:
|
* Input params are:
|
||||||
*
|
*
|
||||||
* planstate: executor state node we are working for
|
* planstate: executor state node we are working for
|
||||||
|
* index: the index we are building scan keys for
|
||||||
* quals: indexquals expressions
|
* quals: indexquals expressions
|
||||||
* strategies: associated operator strategy numbers
|
* strategies: associated operator strategy numbers
|
||||||
* subtypes: associated operator subtype OIDs
|
* subtypes: associated operator subtype OIDs
|
||||||
*
|
*
|
||||||
|
* (Any elements of the strategies and subtypes lists that correspond to
|
||||||
|
* RowCompareExpr quals are not used here; instead we look up the info
|
||||||
|
* afresh.)
|
||||||
|
*
|
||||||
* Output params are:
|
* Output params are:
|
||||||
*
|
*
|
||||||
* *scanKeys: receives ptr to array of ScanKeys
|
* *scanKeys: receives ptr to array of ScanKeys
|
||||||
@ -631,8 +648,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
|
|||||||
* ScalarArrayOpExpr quals are not supported.
|
* ScalarArrayOpExpr quals are not supported.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ExecIndexBuildScanKeys(PlanState *planstate, List *quals,
|
ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
|
||||||
List *strategies, List *subtypes,
|
List *quals, List *strategies, List *subtypes,
|
||||||
ScanKey *scanKeys, int *numScanKeys,
|
ScanKey *scanKeys, int *numScanKeys,
|
||||||
IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys,
|
IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys,
|
||||||
IndexArrayKeyInfo **arrayKeys, int *numArrayKeys)
|
IndexArrayKeyInfo **arrayKeys, int *numArrayKeys)
|
||||||
@ -644,20 +661,42 @@ ExecIndexBuildScanKeys(PlanState *planstate, List *quals,
|
|||||||
IndexRuntimeKeyInfo *runtime_keys;
|
IndexRuntimeKeyInfo *runtime_keys;
|
||||||
IndexArrayKeyInfo *array_keys;
|
IndexArrayKeyInfo *array_keys;
|
||||||
int n_scan_keys;
|
int n_scan_keys;
|
||||||
|
int extra_scan_keys;
|
||||||
int n_runtime_keys;
|
int n_runtime_keys;
|
||||||
int n_array_keys;
|
int n_array_keys;
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there are any RowCompareExpr quals, we need extra ScanKey entries
|
||||||
|
* for them, and possibly extra runtime-key entries. Count up what's
|
||||||
|
* needed. (The subsidiary ScanKey arrays for the RowCompareExprs could
|
||||||
|
* be allocated as separate chunks, but we have to count anyway to make
|
||||||
|
* runtime_keys large enough, so might as well just do one palloc.)
|
||||||
|
*/
|
||||||
n_scan_keys = list_length(quals);
|
n_scan_keys = list_length(quals);
|
||||||
scan_keys = (ScanKey) palloc(n_scan_keys * sizeof(ScanKeyData));
|
extra_scan_keys = 0;
|
||||||
|
foreach(qual_cell, quals)
|
||||||
|
{
|
||||||
|
if (IsA(lfirst(qual_cell), RowCompareExpr))
|
||||||
|
extra_scan_keys +=
|
||||||
|
list_length(((RowCompareExpr *) lfirst(qual_cell))->opnos);
|
||||||
|
}
|
||||||
|
scan_keys = (ScanKey)
|
||||||
|
palloc((n_scan_keys + extra_scan_keys) * sizeof(ScanKeyData));
|
||||||
/* Allocate these arrays as large as they could possibly need to be */
|
/* Allocate these arrays as large as they could possibly need to be */
|
||||||
runtime_keys = (IndexRuntimeKeyInfo *)
|
runtime_keys = (IndexRuntimeKeyInfo *)
|
||||||
palloc(n_scan_keys * sizeof(IndexRuntimeKeyInfo));
|
palloc((n_scan_keys + extra_scan_keys) * sizeof(IndexRuntimeKeyInfo));
|
||||||
array_keys = (IndexArrayKeyInfo *)
|
array_keys = (IndexArrayKeyInfo *)
|
||||||
palloc0(n_scan_keys * sizeof(IndexArrayKeyInfo));
|
palloc0(n_scan_keys * sizeof(IndexArrayKeyInfo));
|
||||||
n_runtime_keys = 0;
|
n_runtime_keys = 0;
|
||||||
n_array_keys = 0;
|
n_array_keys = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Below here, extra_scan_keys is index of first cell to use for next
|
||||||
|
* RowCompareExpr
|
||||||
|
*/
|
||||||
|
extra_scan_keys = n_scan_keys;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* for each opclause in the given qual, convert each qual's opclause into
|
* for each opclause in the given qual, convert each qual's opclause into
|
||||||
* a single scan key
|
* a single scan key
|
||||||
@ -749,6 +788,119 @@ ExecIndexBuildScanKeys(PlanState *planstate, List *quals,
|
|||||||
opfuncid, /* reg proc to use */
|
opfuncid, /* reg proc to use */
|
||||||
scanvalue); /* constant */
|
scanvalue); /* constant */
|
||||||
}
|
}
|
||||||
|
else if (IsA(clause, RowCompareExpr))
|
||||||
|
{
|
||||||
|
/* (indexkey, indexkey, ...) op (expression, expression, ...) */
|
||||||
|
RowCompareExpr *rc = (RowCompareExpr *) clause;
|
||||||
|
ListCell *largs_cell = list_head(rc->largs);
|
||||||
|
ListCell *rargs_cell = list_head(rc->rargs);
|
||||||
|
ListCell *opnos_cell = list_head(rc->opnos);
|
||||||
|
ScanKey first_sub_key = &scan_keys[extra_scan_keys];
|
||||||
|
|
||||||
|
/* Scan RowCompare columns and generate subsidiary ScanKey items */
|
||||||
|
while (opnos_cell != NULL)
|
||||||
|
{
|
||||||
|
ScanKey this_sub_key = &scan_keys[extra_scan_keys];
|
||||||
|
int flags = SK_ROW_MEMBER;
|
||||||
|
Datum scanvalue;
|
||||||
|
Oid opno;
|
||||||
|
Oid opclass;
|
||||||
|
int op_strategy;
|
||||||
|
Oid op_subtype;
|
||||||
|
bool op_recheck;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* leftop should be the index key Var, possibly relabeled
|
||||||
|
*/
|
||||||
|
leftop = (Expr *) lfirst(largs_cell);
|
||||||
|
largs_cell = lnext(largs_cell);
|
||||||
|
|
||||||
|
if (leftop && IsA(leftop, RelabelType))
|
||||||
|
leftop = ((RelabelType *) leftop)->arg;
|
||||||
|
|
||||||
|
Assert(leftop != NULL);
|
||||||
|
|
||||||
|
if (!(IsA(leftop, Var) &&
|
||||||
|
var_is_rel((Var *) leftop)))
|
||||||
|
elog(ERROR, "indexqual doesn't have key on left side");
|
||||||
|
|
||||||
|
varattno = ((Var *) leftop)->varattno;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rightop is the constant or variable comparison value
|
||||||
|
*/
|
||||||
|
rightop = (Expr *) lfirst(rargs_cell);
|
||||||
|
rargs_cell = lnext(rargs_cell);
|
||||||
|
|
||||||
|
if (rightop && IsA(rightop, RelabelType))
|
||||||
|
rightop = ((RelabelType *) rightop)->arg;
|
||||||
|
|
||||||
|
Assert(rightop != NULL);
|
||||||
|
|
||||||
|
if (IsA(rightop, Const))
|
||||||
|
{
|
||||||
|
/* OK, simple constant comparison value */
|
||||||
|
scanvalue = ((Const *) rightop)->constvalue;
|
||||||
|
if (((Const *) rightop)->constisnull)
|
||||||
|
flags |= SK_ISNULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Need to treat this one as a runtime key */
|
||||||
|
runtime_keys[n_runtime_keys].scan_key = this_sub_key;
|
||||||
|
runtime_keys[n_runtime_keys].key_expr =
|
||||||
|
ExecInitExpr(rightop, planstate);
|
||||||
|
n_runtime_keys++;
|
||||||
|
scanvalue = (Datum) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We have to look up the operator's associated btree support
|
||||||
|
* function
|
||||||
|
*/
|
||||||
|
opno = lfirst_oid(opnos_cell);
|
||||||
|
opnos_cell = lnext(opnos_cell);
|
||||||
|
|
||||||
|
if (index->rd_rel->relam != BTREE_AM_OID ||
|
||||||
|
varattno < 1 || varattno > index->rd_index->indnatts)
|
||||||
|
elog(ERROR, "bogus RowCompare index qualification");
|
||||||
|
opclass = index->rd_indclass->values[varattno - 1];
|
||||||
|
|
||||||
|
get_op_opclass_properties(opno, opclass,
|
||||||
|
&op_strategy, &op_subtype, &op_recheck);
|
||||||
|
|
||||||
|
if (op_strategy != rc->rctype)
|
||||||
|
elog(ERROR, "RowCompare index qualification contains wrong operator");
|
||||||
|
|
||||||
|
opfuncid = get_opclass_proc(opclass, op_subtype, BTORDER_PROC);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* initialize the subsidiary scan key's fields appropriately
|
||||||
|
*/
|
||||||
|
ScanKeyEntryInitialize(this_sub_key,
|
||||||
|
flags,
|
||||||
|
varattno, /* attribute number */
|
||||||
|
op_strategy, /* op's strategy */
|
||||||
|
op_subtype, /* strategy subtype */
|
||||||
|
opfuncid, /* reg proc to use */
|
||||||
|
scanvalue); /* constant */
|
||||||
|
extra_scan_keys++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark the last subsidiary scankey correctly */
|
||||||
|
scan_keys[extra_scan_keys - 1].sk_flags |= SK_ROW_END;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't use ScanKeyEntryInitialize for the header because
|
||||||
|
* it isn't going to contain a valid sk_func pointer.
|
||||||
|
*/
|
||||||
|
MemSet(this_scan_key, 0, sizeof(ScanKeyData));
|
||||||
|
this_scan_key->sk_flags = SK_ROW_HEADER;
|
||||||
|
this_scan_key->sk_attno = first_sub_key->sk_attno;
|
||||||
|
this_scan_key->sk_strategy = rc->rctype;
|
||||||
|
/* sk_subtype, sk_func not used in a header */
|
||||||
|
this_scan_key->sk_argument = PointerGetDatum(first_sub_key);
|
||||||
|
}
|
||||||
else if (IsA(clause, ScalarArrayOpExpr))
|
else if (IsA(clause, ScalarArrayOpExpr))
|
||||||
{
|
{
|
||||||
/* indexkey op ANY (array-expression) */
|
/* indexkey op ANY (array-expression) */
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.196 2005/12/06 16:50:36 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.197 2006/01/25 20:29:23 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -61,6 +61,11 @@ static bool match_clause_to_indexcol(IndexOptInfo *index,
|
|||||||
SaOpControl saop_control);
|
SaOpControl saop_control);
|
||||||
static bool is_indexable_operator(Oid expr_op, Oid opclass,
|
static bool is_indexable_operator(Oid expr_op, Oid opclass,
|
||||||
bool indexkey_on_left);
|
bool indexkey_on_left);
|
||||||
|
static bool match_rowcompare_to_indexcol(IndexOptInfo *index,
|
||||||
|
int indexcol,
|
||||||
|
Oid opclass,
|
||||||
|
RowCompareExpr *clause,
|
||||||
|
Relids outer_relids);
|
||||||
static Relids indexable_outerrelids(RelOptInfo *rel);
|
static Relids indexable_outerrelids(RelOptInfo *rel);
|
||||||
static bool matches_any_index(RestrictInfo *rinfo, RelOptInfo *rel,
|
static bool matches_any_index(RestrictInfo *rinfo, RelOptInfo *rel,
|
||||||
Relids outer_relids);
|
Relids outer_relids);
|
||||||
@ -82,7 +87,10 @@ static bool match_special_index_operator(Expr *clause, Oid opclass,
|
|||||||
bool indexkey_on_left);
|
bool indexkey_on_left);
|
||||||
static Expr *expand_boolean_index_clause(Node *clause, int indexcol,
|
static Expr *expand_boolean_index_clause(Node *clause, int indexcol,
|
||||||
IndexOptInfo *index);
|
IndexOptInfo *index);
|
||||||
static List *expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass);
|
static List *expand_indexqual_opclause(RestrictInfo *rinfo, Oid opclass);
|
||||||
|
static RestrictInfo *expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
||||||
|
IndexOptInfo *index,
|
||||||
|
int indexcol);
|
||||||
static List *prefix_quals(Node *leftop, Oid opclass,
|
static List *prefix_quals(Node *leftop, Oid opclass,
|
||||||
Const *prefix, Pattern_Prefix_Status pstatus);
|
Const *prefix, Pattern_Prefix_Status pstatus);
|
||||||
static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass,
|
static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass,
|
||||||
@ -900,6 +908,14 @@ group_clauses_by_indexkey(IndexOptInfo *index,
|
|||||||
* We do not actually do the commuting here, but we check whether a
|
* We do not actually do the commuting here, but we check whether a
|
||||||
* suitable commutator operator is available.
|
* suitable commutator operator is available.
|
||||||
*
|
*
|
||||||
|
* It is also possible to match RowCompareExpr clauses to indexes (but
|
||||||
|
* currently, only btree indexes handle this). In this routine we will
|
||||||
|
* report a match if the first column of the row comparison matches the
|
||||||
|
* target index column. This is sufficient to guarantee that some index
|
||||||
|
* condition can be constructed from the RowCompareExpr --- whether the
|
||||||
|
* remaining columns match the index too is considered in
|
||||||
|
* expand_indexqual_rowcompare().
|
||||||
|
*
|
||||||
* It is also possible to match ScalarArrayOpExpr clauses to indexes, when
|
* It is also possible to match ScalarArrayOpExpr clauses to indexes, when
|
||||||
* the clause is of the form "indexkey op ANY (arrayconst)". Since the
|
* the clause is of the form "indexkey op ANY (arrayconst)". Since the
|
||||||
* executor can only handle these in the context of bitmap index scans,
|
* executor can only handle these in the context of bitmap index scans,
|
||||||
@ -944,7 +960,8 @@ match_clause_to_indexcol(IndexOptInfo *index,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Clause must be a binary opclause, or possibly a ScalarArrayOpExpr
|
* Clause must be a binary opclause, or possibly a ScalarArrayOpExpr
|
||||||
* (which is always binary, by definition).
|
* (which is always binary, by definition). Or it could be a
|
||||||
|
* RowCompareExpr, which we pass off to match_rowcompare_to_indexcol().
|
||||||
*/
|
*/
|
||||||
if (is_opclause(clause))
|
if (is_opclause(clause))
|
||||||
{
|
{
|
||||||
@ -972,6 +989,12 @@ match_clause_to_indexcol(IndexOptInfo *index,
|
|||||||
expr_op = saop->opno;
|
expr_op = saop->opno;
|
||||||
plain_op = false;
|
plain_op = false;
|
||||||
}
|
}
|
||||||
|
else if (clause && IsA(clause, RowCompareExpr))
|
||||||
|
{
|
||||||
|
return match_rowcompare_to_indexcol(index, indexcol, opclass,
|
||||||
|
(RowCompareExpr *) clause,
|
||||||
|
outer_relids);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -1039,6 +1062,74 @@ is_indexable_operator(Oid expr_op, Oid opclass, bool indexkey_on_left)
|
|||||||
return op_in_opclass(expr_op, opclass);
|
return op_in_opclass(expr_op, opclass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* match_rowcompare_to_indexcol()
|
||||||
|
* Handles the RowCompareExpr case for match_clause_to_indexcol(),
|
||||||
|
* which see for comments.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
match_rowcompare_to_indexcol(IndexOptInfo *index,
|
||||||
|
int indexcol,
|
||||||
|
Oid opclass,
|
||||||
|
RowCompareExpr *clause,
|
||||||
|
Relids outer_relids)
|
||||||
|
{
|
||||||
|
Node *leftop,
|
||||||
|
*rightop;
|
||||||
|
Oid expr_op;
|
||||||
|
|
||||||
|
/* Forget it if we're not dealing with a btree index */
|
||||||
|
if (index->relam != BTREE_AM_OID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We could do the matching on the basis of insisting that the opclass
|
||||||
|
* shown in the RowCompareExpr be the same as the index column's opclass,
|
||||||
|
* but that does not work well for cross-type comparisons (the opclass
|
||||||
|
* could be for the other datatype). Also it would fail to handle indexes
|
||||||
|
* using reverse-sort opclasses. Instead, match if the operator listed in
|
||||||
|
* the RowCompareExpr is the < <= > or >= member of the index opclass
|
||||||
|
* (after commutation, if the indexkey is on the right).
|
||||||
|
*/
|
||||||
|
leftop = (Node *) linitial(clause->largs);
|
||||||
|
rightop = (Node *) linitial(clause->rargs);
|
||||||
|
expr_op = linitial_oid(clause->opnos);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These syntactic tests are the same as in match_clause_to_indexcol()
|
||||||
|
*/
|
||||||
|
if (match_index_to_operand(leftop, indexcol, index) &&
|
||||||
|
bms_is_subset(pull_varnos(rightop), outer_relids) &&
|
||||||
|
!contain_volatile_functions(rightop))
|
||||||
|
{
|
||||||
|
/* OK, indexkey is on left */
|
||||||
|
}
|
||||||
|
else if (match_index_to_operand(rightop, indexcol, index) &&
|
||||||
|
bms_is_subset(pull_varnos(leftop), outer_relids) &&
|
||||||
|
!contain_volatile_functions(leftop))
|
||||||
|
{
|
||||||
|
/* indexkey is on right, so commute the operator */
|
||||||
|
expr_op = get_commutator(expr_op);
|
||||||
|
if (expr_op == InvalidOid)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* We're good if the operator is the right type of opclass member */
|
||||||
|
switch (get_op_opclass_strategy(expr_op, opclass))
|
||||||
|
{
|
||||||
|
case BTLessStrategyNumber:
|
||||||
|
case BTLessEqualStrategyNumber:
|
||||||
|
case BTGreaterEqualStrategyNumber:
|
||||||
|
case BTGreaterStrategyNumber:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* ---- ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS ----
|
* ---- ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS ----
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@ -2014,7 +2105,8 @@ match_special_index_operator(Expr *clause, Oid opclass,
|
|||||||
* of index qual clauses. Standard qual clauses (those in the index's
|
* of index qual clauses. Standard qual clauses (those in the index's
|
||||||
* opclass) are passed through unchanged. Boolean clauses and "special"
|
* opclass) are passed through unchanged. Boolean clauses and "special"
|
||||||
* index operators are expanded into clauses that the indexscan machinery
|
* index operators are expanded into clauses that the indexscan machinery
|
||||||
* will know what to do with.
|
* will know what to do with. RowCompare clauses are simplified if
|
||||||
|
* necessary to create a clause that is fully checkable by the index.
|
||||||
*
|
*
|
||||||
* The input list is ordered by index key, and so the output list is too.
|
* The input list is ordered by index key, and so the output list is too.
|
||||||
* (The latter is not depended on by any part of the core planner, I believe,
|
* (The latter is not depended on by any part of the core planner, I believe,
|
||||||
@ -2041,13 +2133,14 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
|
|||||||
foreach(l, (List *) lfirst(clausegroup_item))
|
foreach(l, (List *) lfirst(clausegroup_item))
|
||||||
{
|
{
|
||||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
||||||
|
Expr *clause = rinfo->clause;
|
||||||
|
|
||||||
/* First check for boolean cases */
|
/* First check for boolean cases */
|
||||||
if (IsBooleanOpclass(curClass))
|
if (IsBooleanOpclass(curClass))
|
||||||
{
|
{
|
||||||
Expr *boolqual;
|
Expr *boolqual;
|
||||||
|
|
||||||
boolqual = expand_boolean_index_clause((Node *) rinfo->clause,
|
boolqual = expand_boolean_index_clause((Node *) clause,
|
||||||
indexcol,
|
indexcol,
|
||||||
index);
|
index);
|
||||||
if (boolqual)
|
if (boolqual)
|
||||||
@ -2061,16 +2154,31 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Next check for ScalarArrayOp cases */
|
/*
|
||||||
if (IsA(rinfo->clause, ScalarArrayOpExpr))
|
* Else it must be an opclause (usual case), ScalarArrayOp, or
|
||||||
|
* RowCompare
|
||||||
|
*/
|
||||||
|
if (is_opclause(clause))
|
||||||
{
|
{
|
||||||
resultquals = lappend(resultquals, rinfo);
|
resultquals = list_concat(resultquals,
|
||||||
continue;
|
expand_indexqual_opclause(rinfo,
|
||||||
|
curClass));
|
||||||
}
|
}
|
||||||
|
else if (IsA(clause, ScalarArrayOpExpr))
|
||||||
resultquals = list_concat(resultquals,
|
{
|
||||||
expand_indexqual_condition(rinfo,
|
/* no extra work at this time */
|
||||||
curClass));
|
resultquals = lappend(resultquals, rinfo);
|
||||||
|
}
|
||||||
|
else if (IsA(clause, RowCompareExpr))
|
||||||
|
{
|
||||||
|
resultquals = lappend(resultquals,
|
||||||
|
expand_indexqual_rowcompare(rinfo,
|
||||||
|
index,
|
||||||
|
indexcol));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
elog(ERROR, "unsupported indexqual type: %d",
|
||||||
|
(int) nodeTag(clause));
|
||||||
}
|
}
|
||||||
|
|
||||||
clausegroup_item = lnext(clausegroup_item);
|
clausegroup_item = lnext(clausegroup_item);
|
||||||
@ -2145,16 +2253,15 @@ expand_boolean_index_clause(Node *clause,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* expand_indexqual_condition --- expand a single indexqual condition
|
* expand_indexqual_opclause --- expand a single indexqual condition
|
||||||
* (other than a boolean-qual or ScalarArrayOp case)
|
* that is an operator clause
|
||||||
*
|
*
|
||||||
* The input is a single RestrictInfo, the output a list of RestrictInfos
|
* The input is a single RestrictInfo, the output a list of RestrictInfos
|
||||||
*/
|
*/
|
||||||
static List *
|
static List *
|
||||||
expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass)
|
expand_indexqual_opclause(RestrictInfo *rinfo, Oid opclass)
|
||||||
{
|
{
|
||||||
Expr *clause = rinfo->clause;
|
Expr *clause = rinfo->clause;
|
||||||
|
|
||||||
/* we know these will succeed */
|
/* we know these will succeed */
|
||||||
Node *leftop = get_leftop(clause);
|
Node *leftop = get_leftop(clause);
|
||||||
Node *rightop = get_rightop(clause);
|
Node *rightop = get_rightop(clause);
|
||||||
@ -2224,6 +2331,204 @@ expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* expand_indexqual_rowcompare --- expand a single indexqual condition
|
||||||
|
* that is a RowCompareExpr
|
||||||
|
*
|
||||||
|
* It's already known that the first column of the row comparison matches
|
||||||
|
* the specified column of the index. We can use additional columns of the
|
||||||
|
* row comparison as index qualifications, so long as they match the index
|
||||||
|
* in the "same direction", ie, the indexkeys are all on the same side of the
|
||||||
|
* clause and the operators are all the same-type members of the opclasses.
|
||||||
|
* If all the columns of the RowCompareExpr match in this way, we just use it
|
||||||
|
* as-is. Otherwise, we build a shortened RowCompareExpr (if more than one
|
||||||
|
* column matches) or a simple OpExpr (if the first-column match is all
|
||||||
|
* there is). In these cases the modified clause is always "<=" or ">="
|
||||||
|
* even when the original was "<" or ">" --- this is necessary to match all
|
||||||
|
* the rows that could match the original. (We are essentially building a
|
||||||
|
* lossy version of the row comparison when we do this.)
|
||||||
|
*/
|
||||||
|
static RestrictInfo *
|
||||||
|
expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
||||||
|
IndexOptInfo *index,
|
||||||
|
int indexcol)
|
||||||
|
{
|
||||||
|
RowCompareExpr *clause = (RowCompareExpr *) rinfo->clause;
|
||||||
|
bool var_on_left;
|
||||||
|
int op_strategy;
|
||||||
|
Oid op_subtype;
|
||||||
|
bool op_recheck;
|
||||||
|
int matching_cols;
|
||||||
|
Oid expr_op;
|
||||||
|
List *opclasses;
|
||||||
|
List *subtypes;
|
||||||
|
List *new_ops;
|
||||||
|
ListCell *largs_cell;
|
||||||
|
ListCell *rargs_cell;
|
||||||
|
ListCell *opnos_cell;
|
||||||
|
|
||||||
|
/* We have to figure out (again) how the first col matches */
|
||||||
|
var_on_left = match_index_to_operand((Node *) linitial(clause->largs),
|
||||||
|
indexcol, index);
|
||||||
|
Assert(var_on_left ||
|
||||||
|
match_index_to_operand((Node *) linitial(clause->rargs),
|
||||||
|
indexcol, index));
|
||||||
|
expr_op = linitial_oid(clause->opnos);
|
||||||
|
if (!var_on_left)
|
||||||
|
expr_op = get_commutator(expr_op);
|
||||||
|
get_op_opclass_properties(expr_op, index->classlist[indexcol],
|
||||||
|
&op_strategy, &op_subtype, &op_recheck);
|
||||||
|
/* Build lists of the opclasses and operator subtypes in case needed */
|
||||||
|
opclasses = list_make1_oid(index->classlist[indexcol]);
|
||||||
|
subtypes = list_make1_oid(op_subtype);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See how many of the remaining columns match some index column
|
||||||
|
* in the same way. A note about rel membership tests: we assume
|
||||||
|
* that the clause as a whole is already known to use only Vars from
|
||||||
|
* the indexed relation and possibly some acceptable outer relations.
|
||||||
|
* So the "other" side of any potential index condition is OK as long
|
||||||
|
* as it doesn't use Vars from the indexed relation.
|
||||||
|
*/
|
||||||
|
matching_cols = 1;
|
||||||
|
largs_cell = lnext(list_head(clause->largs));
|
||||||
|
rargs_cell = lnext(list_head(clause->rargs));
|
||||||
|
opnos_cell = lnext(list_head(clause->opnos));
|
||||||
|
|
||||||
|
while (largs_cell != NULL)
|
||||||
|
{
|
||||||
|
Node *varop;
|
||||||
|
Node *constop;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
expr_op = lfirst_oid(opnos_cell);
|
||||||
|
if (var_on_left)
|
||||||
|
{
|
||||||
|
varop = (Node *) lfirst(largs_cell);
|
||||||
|
constop = (Node *) lfirst(rargs_cell);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
varop = (Node *) lfirst(rargs_cell);
|
||||||
|
constop = (Node *) lfirst(largs_cell);
|
||||||
|
/* indexkey is on right, so commute the operator */
|
||||||
|
expr_op = get_commutator(expr_op);
|
||||||
|
if (expr_op == InvalidOid)
|
||||||
|
break; /* operator is not usable */
|
||||||
|
}
|
||||||
|
if (bms_is_member(index->rel->relid, pull_varnos(constop)))
|
||||||
|
break; /* no good, Var on wrong side */
|
||||||
|
if (contain_volatile_functions(constop))
|
||||||
|
break; /* no good, volatile comparison value */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Var side can match any column of the index. If the user
|
||||||
|
* does something weird like having multiple identical index
|
||||||
|
* columns, we insist the match be on the first such column,
|
||||||
|
* to avoid confusing the executor.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < index->ncolumns; i++)
|
||||||
|
{
|
||||||
|
if (match_index_to_operand(varop, i, index))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i >= index->ncolumns)
|
||||||
|
break; /* no match found */
|
||||||
|
|
||||||
|
/* Now, do we have the right operator for this column? */
|
||||||
|
if (get_op_opclass_strategy(expr_op, index->classlist[i])
|
||||||
|
!= op_strategy)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Add opclass and subtype to lists */
|
||||||
|
get_op_opclass_properties(expr_op, index->classlist[i],
|
||||||
|
&op_strategy, &op_subtype, &op_recheck);
|
||||||
|
opclasses = lappend_oid(opclasses, index->classlist[i]);
|
||||||
|
subtypes = lappend_oid(subtypes, op_subtype);
|
||||||
|
|
||||||
|
/* This column matches, keep scanning */
|
||||||
|
matching_cols++;
|
||||||
|
largs_cell = lnext(largs_cell);
|
||||||
|
rargs_cell = lnext(rargs_cell);
|
||||||
|
opnos_cell = lnext(opnos_cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return clause as-is if it's all usable as index quals */
|
||||||
|
if (matching_cols == list_length(clause->opnos))
|
||||||
|
return rinfo;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We have to generate a subset rowcompare (possibly just one OpExpr).
|
||||||
|
* The painful part of this is changing < to <= or > to >=, so deal with
|
||||||
|
* that first.
|
||||||
|
*/
|
||||||
|
if (op_strategy == BTLessEqualStrategyNumber ||
|
||||||
|
op_strategy == BTGreaterEqualStrategyNumber)
|
||||||
|
{
|
||||||
|
/* easy, just use the same operators */
|
||||||
|
new_ops = list_truncate(list_copy(clause->opnos), matching_cols);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ListCell *opclasses_cell;
|
||||||
|
ListCell *subtypes_cell;
|
||||||
|
|
||||||
|
if (op_strategy == BTLessStrategyNumber)
|
||||||
|
op_strategy = BTLessEqualStrategyNumber;
|
||||||
|
else if (op_strategy == BTGreaterStrategyNumber)
|
||||||
|
op_strategy = BTGreaterEqualStrategyNumber;
|
||||||
|
else
|
||||||
|
elog(ERROR, "unexpected strategy number %d", op_strategy);
|
||||||
|
new_ops = NIL;
|
||||||
|
forboth(opclasses_cell, opclasses, subtypes_cell, subtypes)
|
||||||
|
{
|
||||||
|
expr_op = get_opclass_member(lfirst_oid(opclasses_cell),
|
||||||
|
lfirst_oid(subtypes_cell),
|
||||||
|
op_strategy);
|
||||||
|
if (!OidIsValid(expr_op)) /* should not happen */
|
||||||
|
elog(ERROR, "could not find member %d of opclass %u",
|
||||||
|
op_strategy, lfirst_oid(opclasses_cell));
|
||||||
|
if (!var_on_left)
|
||||||
|
{
|
||||||
|
expr_op = get_commutator(expr_op);
|
||||||
|
if (!OidIsValid(expr_op)) /* should not happen */
|
||||||
|
elog(ERROR, "could not find commutator of member %d of opclass %u",
|
||||||
|
op_strategy, lfirst_oid(opclasses_cell));
|
||||||
|
}
|
||||||
|
new_ops = lappend_oid(new_ops, expr_op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we have more than one matching col, create a subset rowcompare */
|
||||||
|
if (matching_cols > 1)
|
||||||
|
{
|
||||||
|
RowCompareExpr *rc = makeNode(RowCompareExpr);
|
||||||
|
|
||||||
|
if (var_on_left)
|
||||||
|
rc->rctype = (RowCompareType) op_strategy;
|
||||||
|
else
|
||||||
|
rc->rctype = (op_strategy == BTLessEqualStrategyNumber) ?
|
||||||
|
ROWCOMPARE_GE : ROWCOMPARE_LE;
|
||||||
|
rc->opnos = new_ops;
|
||||||
|
rc->opclasses = list_truncate(list_copy(clause->opclasses),
|
||||||
|
matching_cols);
|
||||||
|
rc->largs = list_truncate((List *) copyObject(clause->largs),
|
||||||
|
matching_cols);
|
||||||
|
rc->rargs = list_truncate((List *) copyObject(clause->rargs),
|
||||||
|
matching_cols);
|
||||||
|
return make_restrictinfo((Expr *) rc, true, false, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Expr *opexpr;
|
||||||
|
|
||||||
|
opexpr = make_opclause(linitial_oid(new_ops), BOOLOID, false,
|
||||||
|
copyObject(linitial(clause->largs)),
|
||||||
|
copyObject(linitial(clause->rargs)));
|
||||||
|
return make_restrictinfo(opexpr, true, false, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given a fixed prefix that all the "leftop" values must have,
|
* Given a fixed prefix that all the "leftop" values must have,
|
||||||
* generate suitable indexqual condition(s). opclass is the index
|
* generate suitable indexqual condition(s). opclass is the index
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.205 2005/11/26 22:14:56 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.206 2006/01/25 20:29:23 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1583,7 +1583,7 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
|
|||||||
* (only) the base relation.
|
* (only) the base relation.
|
||||||
*/
|
*/
|
||||||
if (!bms_equal(rinfo->left_relids, index->rel->relids))
|
if (!bms_equal(rinfo->left_relids, index->rel->relids))
|
||||||
CommuteClause(op);
|
CommuteOpExpr(op);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now, determine which index attribute this is, change the
|
* Now, determine which index attribute this is, change the
|
||||||
@ -1594,6 +1594,41 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
|
|||||||
&opclass);
|
&opclass);
|
||||||
clause_op = op->opno;
|
clause_op = op->opno;
|
||||||
}
|
}
|
||||||
|
else if (IsA(clause, RowCompareExpr))
|
||||||
|
{
|
||||||
|
RowCompareExpr *rc = (RowCompareExpr *) clause;
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check to see if the indexkey is on the right; if so, commute
|
||||||
|
* the clause. The indexkey should be the side that refers to
|
||||||
|
* (only) the base relation.
|
||||||
|
*/
|
||||||
|
if (!bms_overlap(pull_varnos(linitial(rc->largs)),
|
||||||
|
index->rel->relids))
|
||||||
|
CommuteRowCompareExpr(rc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For each column in the row comparison, determine which index
|
||||||
|
* attribute this is and change the indexkey operand as needed.
|
||||||
|
*
|
||||||
|
* Save the index opclass for only the first column. We will
|
||||||
|
* return the operator and opclass info for just the first
|
||||||
|
* column of the row comparison; the executor will have to
|
||||||
|
* look up the rest if it needs them.
|
||||||
|
*/
|
||||||
|
foreach(lc, rc->largs)
|
||||||
|
{
|
||||||
|
Oid tmp_opclass;
|
||||||
|
|
||||||
|
lfirst(lc) = fix_indexqual_operand(lfirst(lc),
|
||||||
|
index,
|
||||||
|
&tmp_opclass);
|
||||||
|
if (lc == list_head(rc->largs))
|
||||||
|
opclass = tmp_opclass;
|
||||||
|
}
|
||||||
|
clause_op = linitial_oid(rc->opnos);
|
||||||
|
}
|
||||||
else if (IsA(clause, ScalarArrayOpExpr))
|
else if (IsA(clause, ScalarArrayOpExpr))
|
||||||
{
|
{
|
||||||
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
|
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
|
||||||
@ -1745,7 +1780,7 @@ get_switched_clauses(List *clauses, Relids outerrelids)
|
|||||||
temp->opretset = clause->opretset;
|
temp->opretset = clause->opretset;
|
||||||
temp->args = list_copy(clause->args);
|
temp->args = list_copy(clause->args);
|
||||||
/* Commute it --- note this modifies the temp node in-place. */
|
/* Commute it --- note this modifies the temp node in-place. */
|
||||||
CommuteClause(temp);
|
CommuteOpExpr(temp);
|
||||||
t_list = lappend(t_list, temp);
|
t_list = lappend(t_list, temp);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.205 2005/12/28 01:30:00 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.206 2006/01/25 20:29:23 tgl Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -1167,12 +1167,12 @@ NumRelids(Node *clause)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CommuteClause: commute a binary operator clause
|
* CommuteOpExpr: commute a binary operator clause
|
||||||
*
|
*
|
||||||
* XXX the clause is destructively modified!
|
* XXX the clause is destructively modified!
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
CommuteClause(OpExpr *clause)
|
CommuteOpExpr(OpExpr *clause)
|
||||||
{
|
{
|
||||||
Oid opoid;
|
Oid opoid;
|
||||||
Node *temp;
|
Node *temp;
|
||||||
@ -1200,6 +1200,73 @@ CommuteClause(OpExpr *clause)
|
|||||||
lsecond(clause->args) = temp;
|
lsecond(clause->args) = temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CommuteRowCompareExpr: commute a RowCompareExpr clause
|
||||||
|
*
|
||||||
|
* XXX the clause is destructively modified!
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
CommuteRowCompareExpr(RowCompareExpr *clause)
|
||||||
|
{
|
||||||
|
List *newops;
|
||||||
|
List *temp;
|
||||||
|
ListCell *l;
|
||||||
|
|
||||||
|
/* Sanity checks: caller is at fault if these fail */
|
||||||
|
if (!IsA(clause, RowCompareExpr))
|
||||||
|
elog(ERROR, "expected a RowCompareExpr");
|
||||||
|
|
||||||
|
/* Build list of commuted operators */
|
||||||
|
newops = NIL;
|
||||||
|
foreach(l, clause->opnos)
|
||||||
|
{
|
||||||
|
Oid opoid = lfirst_oid(l);
|
||||||
|
|
||||||
|
opoid = get_commutator(opoid);
|
||||||
|
if (!OidIsValid(opoid))
|
||||||
|
elog(ERROR, "could not find commutator for operator %u",
|
||||||
|
lfirst_oid(l));
|
||||||
|
newops = lappend_oid(newops, opoid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* modify the clause in-place!
|
||||||
|
*/
|
||||||
|
switch (clause->rctype)
|
||||||
|
{
|
||||||
|
case ROWCOMPARE_LT:
|
||||||
|
clause->rctype = ROWCOMPARE_GT;
|
||||||
|
break;
|
||||||
|
case ROWCOMPARE_LE:
|
||||||
|
clause->rctype = ROWCOMPARE_GE;
|
||||||
|
break;
|
||||||
|
case ROWCOMPARE_GE:
|
||||||
|
clause->rctype = ROWCOMPARE_LE;
|
||||||
|
break;
|
||||||
|
case ROWCOMPARE_GT:
|
||||||
|
clause->rctype = ROWCOMPARE_LT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unexpected RowCompare type: %d",
|
||||||
|
(int) clause->rctype);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
clause->opnos = newops;
|
||||||
|
/*
|
||||||
|
* Note: we don't bother to update the opclasses list, but just set
|
||||||
|
* it to empty. This is OK since this routine is currently only used
|
||||||
|
* for index quals, and the index machinery won't use the opclass
|
||||||
|
* information. The original opclass list is NOT valid if we have
|
||||||
|
* commuted any cross-type comparisons, so don't leave it in place.
|
||||||
|
*/
|
||||||
|
clause->opclasses = NIL; /* XXX */
|
||||||
|
|
||||||
|
temp = clause->largs;
|
||||||
|
clause->largs = clause->rargs;
|
||||||
|
clause->rargs = temp;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* strip_implicit_coercions: remove implicit coercions at top level of tree
|
* strip_implicit_coercions: remove implicit coercions at top level of tree
|
||||||
*
|
*
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.196 2006/01/14 00:14:11 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.197 2006/01/25 20:29:24 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -4657,6 +4657,9 @@ btcostestimate(PG_FUNCTION_ARGS)
|
|||||||
* to find out which ones count as boundary quals. We rely on the
|
* to find out which ones count as boundary quals. We rely on the
|
||||||
* knowledge that they are given in index column order.
|
* knowledge that they are given in index column order.
|
||||||
*
|
*
|
||||||
|
* For a RowCompareExpr, we consider only the first column, just as
|
||||||
|
* rowcomparesel() does.
|
||||||
|
*
|
||||||
* If there's a ScalarArrayOpExpr in the quals, we'll actually perform
|
* If there's a ScalarArrayOpExpr in the quals, we'll actually perform
|
||||||
* N index scans not one, but the ScalarArrayOpExpr's operator can be
|
* N index scans not one, but the ScalarArrayOpExpr's operator can be
|
||||||
* considered to act the same as it normally does.
|
* considered to act the same as it normally does.
|
||||||
@ -4682,6 +4685,14 @@ btcostestimate(PG_FUNCTION_ARGS)
|
|||||||
rightop = get_rightop(clause);
|
rightop = get_rightop(clause);
|
||||||
clause_op = ((OpExpr *) clause)->opno;
|
clause_op = ((OpExpr *) clause)->opno;
|
||||||
}
|
}
|
||||||
|
else if (IsA(clause, RowCompareExpr))
|
||||||
|
{
|
||||||
|
RowCompareExpr *rc = (RowCompareExpr *) clause;
|
||||||
|
|
||||||
|
leftop = (Node *) linitial(rc->largs);
|
||||||
|
rightop = (Node *) linitial(rc->rargs);
|
||||||
|
clause_op = linitial_oid(rc->opnos);
|
||||||
|
}
|
||||||
else if (IsA(clause, ScalarArrayOpExpr))
|
else if (IsA(clause, ScalarArrayOpExpr))
|
||||||
{
|
{
|
||||||
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
|
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/skey.h,v 1.30 2006/01/14 22:03:35 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/access/skey.h,v 1.31 2006/01/25 20:29:24 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -69,6 +69,36 @@ typedef struct ScanKeyData
|
|||||||
|
|
||||||
typedef ScanKeyData *ScanKey;
|
typedef ScanKeyData *ScanKey;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* About row comparisons:
|
||||||
|
*
|
||||||
|
* The ScanKey data structure also supports row comparisons, that is ordered
|
||||||
|
* tuple comparisons like (x, y) > (c1, c2), having the SQL-spec semantics
|
||||||
|
* "x > c1 OR (x = c1 AND y > c2)". Note that this is currently only
|
||||||
|
* implemented for btree index searches, not for heapscans or any other index
|
||||||
|
* type. A row comparison is represented by a "header" ScanKey entry plus
|
||||||
|
* a separate array of ScanKeys, one for each column of the row comparison.
|
||||||
|
* The header entry has these properties:
|
||||||
|
* sk_flags = SK_ROW_HEADER
|
||||||
|
* sk_attno = index column number for leading column of row comparison
|
||||||
|
* sk_strategy = btree strategy code for semantics of row comparison
|
||||||
|
* (ie, < <= > or >=)
|
||||||
|
* sk_subtype, sk_func: not used
|
||||||
|
* sk_argument: pointer to subsidiary ScanKey array
|
||||||
|
* If the header is part of a ScanKey array that's sorted by attno, it
|
||||||
|
* must be sorted according to the leading column number.
|
||||||
|
*
|
||||||
|
* The subsidiary ScanKey array appears in logical column order of the row
|
||||||
|
* comparison, which may be different from index column order. The array
|
||||||
|
* elements are like a normal ScanKey array except that:
|
||||||
|
* sk_flags must include SK_ROW_MEMBER, plus SK_ROW_END in the last
|
||||||
|
* element (needed since row header does not include a count)
|
||||||
|
* sk_func points to the btree comparison support function for the
|
||||||
|
* opclass, NOT the operator's implementation function.
|
||||||
|
* sk_strategy must be the same in all elements of the subsidiary array,
|
||||||
|
* that is, the same as in the header entry.
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ScanKeyData sk_flags
|
* ScanKeyData sk_flags
|
||||||
*
|
*
|
||||||
@ -78,6 +108,9 @@ typedef ScanKeyData *ScanKey;
|
|||||||
*/
|
*/
|
||||||
#define SK_ISNULL 0x0001 /* sk_argument is NULL */
|
#define SK_ISNULL 0x0001 /* sk_argument is NULL */
|
||||||
#define SK_UNARY 0x0002 /* unary operator (currently unsupported) */
|
#define SK_UNARY 0x0002 /* unary operator (currently unsupported) */
|
||||||
|
#define SK_ROW_HEADER 0x0004 /* row comparison header (see above) */
|
||||||
|
#define SK_ROW_MEMBER 0x0008 /* row comparison member (see above) */
|
||||||
|
#define SK_ROW_END 0x0010 /* last row comparison member (see above) */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/executor/nodeIndexscan.h,v 1.25 2005/11/25 19:47:50 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/executor/nodeIndexscan.h,v 1.26 2006/01/25 20:29:24 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -25,8 +25,8 @@ extern void ExecIndexRestrPos(IndexScanState *node);
|
|||||||
extern void ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt);
|
extern void ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt);
|
||||||
|
|
||||||
/* routines exported to share code with nodeBitmapIndexscan.c */
|
/* routines exported to share code with nodeBitmapIndexscan.c */
|
||||||
extern void ExecIndexBuildScanKeys(PlanState *planstate, List *quals,
|
extern void ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
|
||||||
List *strategies, List *subtypes,
|
List *quals, List *strategies, List *subtypes,
|
||||||
ScanKey *scanKeys, int *numScanKeys,
|
ScanKey *scanKeys, int *numScanKeys,
|
||||||
IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys,
|
IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys,
|
||||||
IndexArrayKeyInfo **arrayKeys, int *numArrayKeys);
|
IndexArrayKeyInfo **arrayKeys, int *numArrayKeys);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/optimizer/clauses.h,v 1.81 2005/12/20 02:30:36 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.82 2006/01/25 20:29:24 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -67,7 +67,9 @@ extern bool has_distinct_clause(Query *query);
|
|||||||
extern bool has_distinct_on_clause(Query *query);
|
extern bool has_distinct_on_clause(Query *query);
|
||||||
|
|
||||||
extern int NumRelids(Node *clause);
|
extern int NumRelids(Node *clause);
|
||||||
extern void CommuteClause(OpExpr *clause);
|
|
||||||
|
extern void CommuteOpExpr(OpExpr *clause);
|
||||||
|
extern void CommuteRowCompareExpr(RowCompareExpr *clause);
|
||||||
|
|
||||||
extern Node *strip_implicit_coercions(Node *node);
|
extern Node *strip_implicit_coercions(Node *node);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user