mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
Add operator strategy and comparison-value datatype fields to ScanKey.
Remove the 'strategy map' code, which was a large amount of mechanism that no longer had any use except reverse-mapping from procedure OID to strategy number. Passing the strategy number to the index AM in the first place is simpler and faster. This is a preliminary step in planned support for cross-datatype index operations. I'm committing it now since the ScanKeyEntryInitialize() API change touches quite a lot of files, and I want to commit those changes before the tree drifts under me.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtscan.c,v 1.47 2003/08/04 02:39:57 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtscan.c,v 1.48 2003/11/09 21:30:35 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#include "access/genam.h"
|
||||
#include "access/rtree.h"
|
||||
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
/* routines defined and used here */
|
||||
@@ -71,7 +71,6 @@ rtrescan(PG_FUNCTION_ARGS)
|
||||
IndexScanDesc s = (IndexScanDesc) PG_GETARG_POINTER(0);
|
||||
ScanKey key = (ScanKey) PG_GETARG_POINTER(1);
|
||||
RTreeScanOpaque p;
|
||||
RegProcedure internal_proc;
|
||||
int i;
|
||||
|
||||
/*
|
||||
@@ -116,14 +115,23 @@ rtrescan(PG_FUNCTION_ARGS)
|
||||
*/
|
||||
for (i = 0; i < s->numberOfKeys; i++)
|
||||
{
|
||||
internal_proc = RTMapOperator(s->indexRelation,
|
||||
s->keyData[i].sk_attno,
|
||||
s->keyData[i].sk_procedure);
|
||||
AttrNumber attno = s->keyData[i].sk_attno;
|
||||
Oid opclass;
|
||||
StrategyNumber int_strategy;
|
||||
Oid int_oper;
|
||||
RegProcedure int_proc;
|
||||
|
||||
opclass = s->indexRelation->rd_index->indclass[attno-1];
|
||||
int_strategy = RTMapToInternalOperator(s->keyData[i].sk_strategy);
|
||||
int_oper = get_opclass_member(opclass, int_strategy);
|
||||
int_proc = get_opcode(int_oper);
|
||||
ScanKeyEntryInitialize(&(p->s_internalKey[i]),
|
||||
s->keyData[i].sk_flags,
|
||||
s->keyData[i].sk_attno,
|
||||
internal_proc,
|
||||
s->keyData[i].sk_argument);
|
||||
attno,
|
||||
int_strategy,
|
||||
int_proc,
|
||||
s->keyData[i].sk_argument,
|
||||
s->keyData[i].sk_argtype);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,176 +8,18 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtstrat.c,v 1.21 2003/08/04 02:39:57 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtstrat.c,v 1.22 2003/11/09 21:30:35 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/istrat.h"
|
||||
#include "access/rtree.h"
|
||||
|
||||
static StrategyNumber RelationGetRTStrategy(Relation r,
|
||||
AttrNumber attnum, RegProcedure proc);
|
||||
|
||||
/*
|
||||
* Note: negate, commute, and negatecommute all assume that operators are
|
||||
* ordered as follows in the strategy map:
|
||||
*
|
||||
* left, left-or-overlap, overlap, right-or-overlap, right, same,
|
||||
* contains, contained-by
|
||||
*
|
||||
* The negate, commute, and negatecommute arrays are used by the planner
|
||||
* to plan indexed scans over data that appears in the qualificiation in
|
||||
* a boolean negation, or whose operands appear in the wrong order. For
|
||||
* example, if the operator "<%" means "contains", and the user says
|
||||
*
|
||||
* where not rel.box <% "(10,10,20,20)"::box
|
||||
*
|
||||
* the planner can plan an index scan by noting that rtree indices have
|
||||
* an operator in their operator class for negating <%.
|
||||
*
|
||||
* Similarly, if the user says something like
|
||||
*
|
||||
* where "(10,10,20,20)"::box <% rel.box
|
||||
*
|
||||
* the planner can see that the rtree index on rel.box has an operator in
|
||||
* its opclass for commuting <%, and plan the scan using that operator.
|
||||
* This added complexity in the access methods makes the planner a lot easier
|
||||
* to write.
|
||||
*/
|
||||
|
||||
/* if a op b, what operator tells us if (not a op b)? */
|
||||
static StrategyNumber RTNegate[RTNStrategies] = {
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy
|
||||
};
|
||||
|
||||
/* if a op_1 b, what is the operator op_2 such that b op_2 a? */
|
||||
static StrategyNumber RTCommute[RTNStrategies] = {
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy
|
||||
};
|
||||
|
||||
/* if a op_1 b, what is the operator op_2 such that (b !op_2 a)? */
|
||||
static StrategyNumber RTNegateCommute[RTNStrategies] = {
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy,
|
||||
InvalidStrategy
|
||||
};
|
||||
|
||||
/*
|
||||
* Now do the TermData arrays. These exist in case the user doesn't give
|
||||
* us a full set of operators for a particular operator class. The idea
|
||||
* is that by making multiple comparisons using any one of the supplied
|
||||
* operators, we can decide whether two n-dimensional polygons are equal.
|
||||
* For example, if a contains b and b contains a, we may conclude that
|
||||
* a and b are equal.
|
||||
*
|
||||
* The presence of the TermData arrays in all this is a historical accident.
|
||||
* Early in the development of the POSTGRES access methods, it was believed
|
||||
* that writing functions was harder than writing arrays. This is wrong;
|
||||
* TermData is hard to understand and hard to get right. In general, when
|
||||
* someone populates a new operator class, they populate it completely. If
|
||||
* Mike Hirohama had forced Cimarron Taylor to populate the strategy map
|
||||
* for btree int2_ops completely in 1988, you wouldn't have to deal with
|
||||
* all this now. Too bad for you.
|
||||
*
|
||||
* Since you can't necessarily do this in all cases (for example, you can't
|
||||
* do it given only "intersects" or "disjoint"), TermData arrays for some
|
||||
* operators don't appear below.
|
||||
*
|
||||
* Note that if you DO supply all the operators required in a given opclass
|
||||
* by inserting them into the pg_opclass system catalog, you can get away
|
||||
* without doing all this TermData stuff. Since the rtree code is intended
|
||||
* to be a reference for access method implementors, I'm doing TermData
|
||||
* correctly here.
|
||||
*
|
||||
* Note on style: these are all actually of type StrategyTermData, but
|
||||
* since those have variable-length data at the end of the struct we can't
|
||||
* properly initialize them if we declare them to be what they are.
|
||||
*/
|
||||
|
||||
/* if you only have "contained-by", how do you determine equality? */
|
||||
static uint16 RTContainedByTermData[] = {
|
||||
2, /* make two comparisons */
|
||||
RTContainedByStrategyNumber, /* use "a contained-by b" */
|
||||
0x0, /* without any magic */
|
||||
RTContainedByStrategyNumber, /* then use contained-by, */
|
||||
SK_COMMUTE /* swapping a and b */
|
||||
};
|
||||
|
||||
/* if you only have "contains", how do you determine equality? */
|
||||
static uint16 RTContainsTermData[] = {
|
||||
2, /* make two comparisons */
|
||||
RTContainsStrategyNumber, /* use "a contains b" */
|
||||
0x0, /* without any magic */
|
||||
RTContainsStrategyNumber, /* then use contains again, */
|
||||
SK_COMMUTE /* swapping a and b */
|
||||
};
|
||||
|
||||
/* now put all that together in one place for the planner */
|
||||
static StrategyTerm RTEqualExpressionData[] = {
|
||||
(StrategyTerm) RTContainedByTermData,
|
||||
(StrategyTerm) RTContainsTermData,
|
||||
NULL
|
||||
};
|
||||
|
||||
/*
|
||||
* If you were sufficiently attentive to detail, you would go through
|
||||
* the ExpressionData pain above for every one of the seven strategies
|
||||
* we defined. I am not. Now we declare the StrategyEvaluationData
|
||||
* structure that gets shipped around to help the planner and the access
|
||||
* method decide what sort of scan it should do, based on (a) what the
|
||||
* user asked for, (b) what operators are defined for a particular opclass,
|
||||
* and (c) the reams of information we supplied above.
|
||||
*
|
||||
* The idea of all of this initialized data is to make life easier on the
|
||||
* user when he defines a new operator class to use this access method.
|
||||
* By filling in all the data, we let him get away with leaving holes in his
|
||||
* operator class, and still let him use the index. The added complexity
|
||||
* in the access methods just isn't worth the trouble, though.
|
||||
*/
|
||||
|
||||
static StrategyExpression RTEvaluationExpressions[RTNStrategies] = {
|
||||
NULL, /* express left */
|
||||
NULL, /* express overleft */
|
||||
NULL, /* express overlap */
|
||||
NULL, /* express overright */
|
||||
NULL, /* express right */
|
||||
(StrategyExpression) RTEqualExpressionData, /* express same */
|
||||
NULL, /* express contains */
|
||||
NULL /* express contained-by */
|
||||
};
|
||||
|
||||
static StrategyEvaluationData RTEvaluationData = {
|
||||
RTNStrategies, /* # of strategies */
|
||||
(StrategyTransformMap) RTNegate, /* how to do (not qual) */
|
||||
(StrategyTransformMap) RTCommute, /* how to swap operands */
|
||||
(StrategyTransformMap) RTNegateCommute, /* how to do both */
|
||||
RTEvaluationExpressions
|
||||
};
|
||||
|
||||
/*
|
||||
* Okay, now something peculiar to rtrees that doesn't apply to most other
|
||||
* Here's something peculiar to rtrees that doesn't apply to most other
|
||||
* indexing structures: When we're searching a tree for a given value, we
|
||||
* can't do the same sorts of comparisons on internal node entries as we
|
||||
* do at leaves. The reason is that if we're looking for (say) all boxes
|
||||
@@ -191,7 +33,7 @@ static StrategyEvaluationData RTEvaluationData = {
|
||||
* left, left-or-overlap, overlap, right-or-overlap, right, same,
|
||||
* contains, contained-by
|
||||
*/
|
||||
static StrategyNumber RTOperMap[RTNStrategies] = {
|
||||
static const StrategyNumber RTOperMap[RTNStrategies] = {
|
||||
RTOverLeftStrategyNumber,
|
||||
RTOverLeftStrategyNumber,
|
||||
RTOverlapStrategyNumber,
|
||||
@@ -202,39 +44,10 @@ static StrategyNumber RTOperMap[RTNStrategies] = {
|
||||
RTOverlapStrategyNumber
|
||||
};
|
||||
|
||||
static StrategyNumber
|
||||
RelationGetRTStrategy(Relation r,
|
||||
AttrNumber attnum,
|
||||
RegProcedure proc)
|
||||
|
||||
StrategyNumber
|
||||
RTMapToInternalOperator(StrategyNumber strat)
|
||||
{
|
||||
return RelationGetStrategy(r, attnum, &RTEvaluationData, proc);
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
bool
|
||||
RelationInvokeRTStrategy(Relation r,
|
||||
AttrNumber attnum,
|
||||
StrategyNumber s,
|
||||
Datum left,
|
||||
Datum right)
|
||||
{
|
||||
return (RelationInvokeStrategy(r, &RTEvaluationData, attnum, s,
|
||||
left, right));
|
||||
}
|
||||
#endif
|
||||
|
||||
RegProcedure
|
||||
RTMapOperator(Relation r,
|
||||
AttrNumber attnum,
|
||||
RegProcedure proc)
|
||||
{
|
||||
StrategyNumber procstrat;
|
||||
StrategyMap strategyMap;
|
||||
|
||||
procstrat = RelationGetRTStrategy(r, attnum, proc);
|
||||
strategyMap = IndexStrategyGetStrategyMap(RelationGetIndexStrategy(r),
|
||||
RTNStrategies,
|
||||
attnum);
|
||||
|
||||
return strategyMap->entry[RTOperMap[procstrat - 1] - 1].sk_procedure;
|
||||
Assert(strat > 0 && strat <= RTNStrategies);
|
||||
return RTOperMap[strat - 1];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user