]> granicus.if.org Git - postgresql/blob - contrib/btree_gin/btree_gin.c
Revert commit 843cd0bfe6246d94d9b34a7f36bbb76fdba87b74
[postgresql] / contrib / btree_gin / btree_gin.c
1 /*
2  * contrib/btree_gin/btree_gin.c
3  */
4 #include "postgres.h"
5
6 #include <limits.h>
7
8 #include "access/skey.h"
9 #include "utils/builtins.h"
10 #include "utils/bytea.h"
11 #include "utils/cash.h"
12 #include "utils/date.h"
13 #include "utils/inet.h"
14 #include "utils/numeric.h"
15 #include "utils/timestamp.h"
16 #include "utils/varbit.h"
17
18 PG_MODULE_MAGIC;
19
20 typedef struct QueryInfo
21 {
22         StrategyNumber strategy;
23         Datum           datum;
24         bool            is_varlena;
25         Datum           (*typecmp) (FunctionCallInfo);
26 } QueryInfo;
27
28
29 /*** GIN support functions shared by all datatypes ***/
30
31 static Datum
32 gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
33 {
34         Datum           datum = PG_GETARG_DATUM(0);
35         int32      *nentries = (int32 *) PG_GETARG_POINTER(1);
36         Datum      *entries = (Datum *) palloc(sizeof(Datum));
37
38         if (is_varlena)
39                 datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
40         entries[0] = datum;
41         *nentries = 1;
42
43         PG_RETURN_POINTER(entries);
44 }
45
46 /*
47  * For BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, and
48  * BTEqualStrategyNumber we want to start the index scan at the
49  * supplied query datum, and work forward. For BTLessStrategyNumber
50  * and BTLessEqualStrategyNumber, we need to start at the leftmost
51  * key, and work forward until the supplied query datum (which must be
52  * sent along inside the QueryInfo structure).
53  */
54 static Datum
55 gin_btree_extract_query(FunctionCallInfo fcinfo,
56                                                 bool is_varlena,
57                                                 Datum (*leftmostvalue) (void),
58                                                 Datum (*typecmp) (FunctionCallInfo))
59 {
60         Datum           datum = PG_GETARG_DATUM(0);
61         int32      *nentries = (int32 *) PG_GETARG_POINTER(1);
62         StrategyNumber strategy = PG_GETARG_UINT16(2);
63         bool      **partialmatch = (bool **) PG_GETARG_POINTER(3);
64         Pointer   **extra_data = (Pointer **) PG_GETARG_POINTER(4);
65         Datum      *entries = (Datum *) palloc(sizeof(Datum));
66         QueryInfo  *data = (QueryInfo *) palloc(sizeof(QueryInfo));
67         bool       *ptr_partialmatch;
68
69         *nentries = 1;
70         ptr_partialmatch = *partialmatch = (bool *) palloc(sizeof(bool));
71         *ptr_partialmatch = false;
72         if (is_varlena)
73                 datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
74         data->strategy = strategy;
75         data->datum = datum;
76         data->is_varlena = is_varlena;
77         data->typecmp = typecmp;
78         *extra_data = (Pointer *) palloc(sizeof(Pointer));
79         **extra_data = (Pointer) data;
80
81         switch (strategy)
82         {
83                 case BTLessStrategyNumber:
84                 case BTLessEqualStrategyNumber:
85                         entries[0] = leftmostvalue();
86                         *ptr_partialmatch = true;
87                         break;
88                 case BTGreaterEqualStrategyNumber:
89                 case BTGreaterStrategyNumber:
90                         *ptr_partialmatch = true;
91                 case BTEqualStrategyNumber:
92                         entries[0] = datum;
93                         break;
94                 default:
95                         elog(ERROR, "unrecognized strategy number: %d", strategy);
96         }
97
98         PG_RETURN_POINTER(entries);
99 }
100
101 /*
102  * Datum a is a value from extract_query method and for BTLess*
103  * strategy it is a left-most value.  So, use original datum from QueryInfo
104  * to decide to stop scanning or not.  Datum b is always from index.
105  */
106 static Datum
107 gin_btree_compare_prefix(FunctionCallInfo fcinfo)
108 {
109         Datum           a = PG_GETARG_DATUM(0);
110         Datum           b = PG_GETARG_DATUM(1);
111         QueryInfo  *data = (QueryInfo *) PG_GETARG_POINTER(3);
112         int32           res,
113                                 cmp;
114
115         cmp = DatumGetInt32(DirectFunctionCall2Coll(
116                                 data->typecmp,
117                                 PG_GET_COLLATION(),
118                                 (data->strategy == BTLessStrategyNumber ||
119                                  data->strategy == BTLessEqualStrategyNumber)
120                                  ? data->datum : a,
121                                 b));
122
123         switch (data->strategy)
124         {
125                 case BTLessStrategyNumber:
126                         /* If original datum > indexed one then return match */
127                         if (cmp > 0)
128                                 res = 0;
129                         else
130                                 res = 1;
131                         break;
132                 case BTLessEqualStrategyNumber:
133                         /* The same except equality */
134                         if (cmp >= 0)
135                                 res = 0;
136                         else
137                                 res = 1;
138                         break;
139                 case BTEqualStrategyNumber:
140                         if (cmp != 0)
141                                 res = 1;
142                         else
143                                 res = 0;
144                         break;
145                 case BTGreaterEqualStrategyNumber:
146                         /* If original datum <= indexed one then return match */
147                         if (cmp <= 0)
148                                 res = 0;
149                         else
150                                 res = 1;
151                         break;
152                 case BTGreaterStrategyNumber:
153                         /* If original datum <= indexed one then return match */
154                         /* If original datum == indexed one then continue scan */
155                         if (cmp < 0)
156                                 res = 0;
157                         else if (cmp == 0)
158                                 res = -1;
159                         else
160                                 res = 1;
161                         break;
162                 default:
163                         elog(ERROR, "unrecognized strategy number: %d",
164                                  data->strategy);
165                         res = 0;
166         }
167
168         PG_RETURN_INT32(res);
169 }
170
171 PG_FUNCTION_INFO_V1(gin_btree_consistent);
172 Datum
173 gin_btree_consistent(PG_FUNCTION_ARGS)
174 {
175         bool       *recheck = (bool *) PG_GETARG_POINTER(5);
176
177         *recheck = false;
178         PG_RETURN_BOOL(true);
179 }
180
181 /*** GIN_SUPPORT macro defines the datatype specific functions ***/
182
183 #define GIN_SUPPORT(type, is_varlena, leftmostvalue, typecmp)                           \
184 PG_FUNCTION_INFO_V1(gin_extract_value_##type);                                                          \
185 Datum                                                                                                                                           \
186 gin_extract_value_##type(PG_FUNCTION_ARGS)                                                                      \
187 {                                                                                                                                                       \
188         return gin_btree_extract_value(fcinfo, is_varlena);                                             \
189 }                                                                                                                                                       \
190 PG_FUNCTION_INFO_V1(gin_extract_query_##type);                                                          \
191 Datum                                                                                                                                           \
192 gin_extract_query_##type(PG_FUNCTION_ARGS)                                                                      \
193 {                                                                                                                                                       \
194         return gin_btree_extract_query(fcinfo,                                                                  \
195                                                                    is_varlena, leftmostvalue, typecmp);         \
196 }                                                                                                                                                       \
197 PG_FUNCTION_INFO_V1(gin_compare_prefix_##type);                                                         \
198 Datum                                                                                                                                           \
199 gin_compare_prefix_##type(PG_FUNCTION_ARGS)                                                                     \
200 {                                                                                                                                                       \
201         return gin_btree_compare_prefix(fcinfo);                                                                \
202 }
203
204
205 /*** Datatype specifications ***/
206
207 static Datum
208 leftmostvalue_int2(void)
209 {
210         return Int16GetDatum(SHRT_MIN);
211 }
212 GIN_SUPPORT(int2, false, leftmostvalue_int2, btint2cmp)
213
214 static Datum
215 leftmostvalue_int4(void)
216 {
217         return Int32GetDatum(INT_MIN);
218 }
219 GIN_SUPPORT(int4, false, leftmostvalue_int4, btint4cmp)
220
221 static Datum
222 leftmostvalue_int8(void)
223 {
224         /*
225          * Use sequence's definition to keep compatibility.
226          */
227         return Int64GetDatum(SEQ_MINVALUE);
228 }
229 GIN_SUPPORT(int8, false, leftmostvalue_int8, btint8cmp)
230
231 static Datum
232 leftmostvalue_float4(void)
233 {
234         return Float4GetDatum(-get_float4_infinity());
235 }
236 GIN_SUPPORT(float4, false, leftmostvalue_float4, btfloat4cmp)
237
238 static Datum
239 leftmostvalue_float8(void)
240 {
241         return Float8GetDatum(-get_float8_infinity());
242 }
243 GIN_SUPPORT(float8, false, leftmostvalue_float8, btfloat8cmp)
244
245 static Datum
246 leftmostvalue_money(void)
247 {
248         /*
249          * Use sequence's definition to keep compatibility.
250          */
251         return Int64GetDatum(SEQ_MINVALUE);
252 }
253 GIN_SUPPORT(money, false, leftmostvalue_money, cash_cmp)
254
255 static Datum
256 leftmostvalue_oid(void)
257 {
258         return ObjectIdGetDatum(0);
259 }
260 GIN_SUPPORT(oid, false, leftmostvalue_oid, btoidcmp)
261
262 static Datum
263 leftmostvalue_timestamp(void)
264 {
265         return TimestampGetDatum(DT_NOBEGIN);
266 }
267 GIN_SUPPORT(timestamp, false, leftmostvalue_timestamp, timestamp_cmp)
268
269 GIN_SUPPORT(timestamptz, false, leftmostvalue_timestamp, timestamp_cmp)
270
271 static Datum
272 leftmostvalue_time(void)
273 {
274         return TimeADTGetDatum(0);
275 }
276 GIN_SUPPORT(time, false, leftmostvalue_time, time_cmp)
277
278 static Datum
279 leftmostvalue_timetz(void)
280 {
281         TimeTzADT  *v = palloc(sizeof(TimeTzADT));
282
283         v->time = 0;
284         v->zone = -24 * 3600;           /* XXX is that true? */
285
286         return TimeTzADTPGetDatum(v);
287 }
288 GIN_SUPPORT(timetz, false, leftmostvalue_timetz, timetz_cmp)
289
290 static Datum
291 leftmostvalue_date(void)
292 {
293         return DateADTGetDatum(DATEVAL_NOBEGIN);
294 }
295 GIN_SUPPORT(date, false, leftmostvalue_date, date_cmp)
296
297 static Datum
298 leftmostvalue_interval(void)
299 {
300         Interval   *v = palloc(sizeof(Interval));
301
302         v->time = DT_NOBEGIN;
303         v->day = 0;
304         v->month = 0;
305         return IntervalPGetDatum(v);
306 }
307 GIN_SUPPORT(interval, false, leftmostvalue_interval, interval_cmp)
308
309 static Datum
310 leftmostvalue_macaddr(void)
311 {
312         macaddr    *v = palloc0(sizeof(macaddr));
313
314         return MacaddrPGetDatum(v);
315 }
316 GIN_SUPPORT(macaddr, false, leftmostvalue_macaddr, macaddr_cmp)
317
318 static Datum
319 leftmostvalue_inet(void)
320 {
321         return DirectFunctionCall3(inet_in,
322                                                            CStringGetDatum("0.0.0.0/0"),
323                                                            ObjectIdGetDatum(0),
324                                                            Int32GetDatum(-1));
325 }
326 GIN_SUPPORT(inet, true, leftmostvalue_inet, network_cmp)
327
328 GIN_SUPPORT(cidr, true, leftmostvalue_inet, network_cmp)
329
330 static Datum
331 leftmostvalue_text(void)
332 {
333         return PointerGetDatum(cstring_to_text_with_len("", 0));
334 }
335 GIN_SUPPORT(text, true, leftmostvalue_text, bttextcmp)
336
337 static Datum
338 leftmostvalue_char(void)
339 {
340         return CharGetDatum(SCHAR_MIN);
341 }
342 GIN_SUPPORT(char, false, leftmostvalue_char, btcharcmp)
343
344 GIN_SUPPORT(bytea, true, leftmostvalue_text, byteacmp)
345
346 static Datum
347 leftmostvalue_bit(void)
348 {
349         return DirectFunctionCall3(bit_in,
350                                                            CStringGetDatum(""),
351                                                            ObjectIdGetDatum(0),
352                                                            Int32GetDatum(-1));
353 }
354 GIN_SUPPORT(bit, true, leftmostvalue_bit, bitcmp)
355
356 static Datum
357 leftmostvalue_varbit(void)
358 {
359         return DirectFunctionCall3(varbit_in,
360                                                            CStringGetDatum(""),
361                                                            ObjectIdGetDatum(0),
362                                                            Int32GetDatum(-1));
363 }
364 GIN_SUPPORT(varbit, true, leftmostvalue_varbit, bitcmp)
365
366 /*
367  * Numeric type hasn't a real left-most value, so we use PointerGetDatum(NULL)
368  * (*not* a SQL NULL) to represent that.  We can get away with that because
369  * the value returned by our leftmostvalue function will never be stored in
370  * the index nor passed to anything except our compare and prefix-comparison
371  * functions.  The same trick could be used for other pass-by-reference types.
372  */
373
374 #define NUMERIC_IS_LEFTMOST(x)  ((x) == NULL)
375
376 PG_FUNCTION_INFO_V1(gin_numeric_cmp);
377
378 Datum
379 gin_numeric_cmp(PG_FUNCTION_ARGS)
380 {
381         Numeric         a = (Numeric) PG_GETARG_POINTER(0);
382         Numeric         b = (Numeric) PG_GETARG_POINTER(1);
383         int                     res = 0;
384
385         if (NUMERIC_IS_LEFTMOST(a))
386         {
387                 res = (NUMERIC_IS_LEFTMOST(b)) ? 0 : -1;
388         }
389         else if (NUMERIC_IS_LEFTMOST(b))
390         {
391                 res = 1;
392         }
393         else
394         {
395                 res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
396                                                                                                 NumericGetDatum(a),
397                                                                                                 NumericGetDatum(b)));
398         }
399
400         PG_RETURN_INT32(res);
401 }
402
403 static Datum
404 leftmostvalue_numeric(void)
405 {
406         return PointerGetDatum(NULL);
407 }
408 GIN_SUPPORT(numeric, true, leftmostvalue_numeric, gin_numeric_cmp)