summaryrefslogtreecommitdiff
path: root/src/backend/access/rtree/rtstrat.c
blob: 028625d48a1388bbafdac12244081859b8199fe0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/*-------------------------------------------------------------------------
 *
 * rtstrat.c--
 *	  strategy map data for rtrees.
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtstrat.c,v 1.8 1997/09/08 02:21:11 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */

#include <postgres.h>

#include <utils/rel.h>
#include <access/rtree.h>
#include <access/istrat.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, the 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 StrategyEvaluationData RTEvaluationData = {
	RTNStrategies,				/* # of strategies */
	(StrategyTransformMap) RTNegate,	/* how to do (not qual) */
	(StrategyTransformMap) RTCommute,	/* how to swap operands */
	(StrategyTransformMap) RTNegateCommute,		/* how to do both */
	{
		NULL,					/* express left */
		NULL,					/* express overleft */
		NULL,					/* express over */
		NULL,					/* express overright */
		NULL,					/* express right */
		(StrategyExpression) RTEqualExpressionData,		/* express same */
		NULL,					/* express contains */
		NULL,					/* express contained-by */
		NULL,
		NULL,
		NULL
	}
};

/*
 *	Okay, now 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
 *	that are the same as (0,0,10,10), then we need to find all leaf pages
 *	that overlap that region.  So internally we search for overlap, and at
 *	the leaf we search for equality.
 *
 *	This array maps leaf search operators to the internal search operators.
 *	We assume the normal ordering on operators:
 *
 *		left, left-or-overlap, overlap, right-or-overlap, right, same,
 *		contains, contained-by
 */
static StrategyNumber RTOperMap[RTNStrategies] = {
	RTOverLeftStrategyNumber,
	RTOverLeftStrategyNumber,
	RTOverlapStrategyNumber,
	RTOverRightStrategyNumber,
	RTOverRightStrategyNumber,
	RTContainsStrategyNumber,
	RTContainsStrategyNumber,
	RTOverlapStrategyNumber
};

static StrategyNumber
RelationGetRTStrategy(Relation r,
					  AttrNumber attnum,
					  RegProcedure proc)
{
	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);
}