Line data Source code
1 : /*
2 : * contrib/btree_gin/btree_gin.c
3 : */
4 : #include "postgres.h"
5 :
6 : #include <limits.h>
7 :
8 : #include "access/stratnum.h"
9 : #include "utils/builtins.h"
10 : #include "utils/date.h"
11 : #include "utils/float.h"
12 : #include "utils/inet.h"
13 : #include "utils/numeric.h"
14 : #include "utils/timestamp.h"
15 : #include "utils/uuid.h"
16 :
17 60 : PG_MODULE_MAGIC_EXT(
18 : .name = "btree_gin",
19 : .version = PG_VERSION
20 : );
21 :
22 : typedef struct QueryInfo
23 : {
24 : StrategyNumber strategy;
25 : Datum datum;
26 : bool is_varlena;
27 : Datum (*typecmp) (FunctionCallInfo);
28 : } QueryInfo;
29 :
30 : /*** GIN support functions shared by all datatypes ***/
31 :
32 : static Datum
33 200352 : gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
34 : {
35 200352 : Datum datum = PG_GETARG_DATUM(0);
36 200352 : int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
37 200352 : Datum *entries = (Datum *) palloc(sizeof(Datum));
38 :
39 200352 : if (is_varlena)
40 112 : datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
41 200352 : entries[0] = datum;
42 200352 : *nentries = 1;
43 :
44 200352 : PG_RETURN_POINTER(entries);
45 : }
46 :
47 : /*
48 : * For BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, and
49 : * BTEqualStrategyNumber we want to start the index scan at the
50 : * supplied query datum, and work forward. For BTLessStrategyNumber
51 : * and BTLessEqualStrategyNumber, we need to start at the leftmost
52 : * key, and work forward until the supplied query datum (which must be
53 : * sent along inside the QueryInfo structure).
54 : */
55 : static Datum
56 648 : gin_btree_extract_query(FunctionCallInfo fcinfo,
57 : bool is_varlena,
58 : Datum (*leftmostvalue) (void),
59 : Datum (*typecmp) (FunctionCallInfo))
60 : {
61 648 : Datum datum = PG_GETARG_DATUM(0);
62 648 : int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
63 648 : StrategyNumber strategy = PG_GETARG_UINT16(2);
64 648 : bool **partialmatch = (bool **) PG_GETARG_POINTER(3);
65 648 : Pointer **extra_data = (Pointer **) PG_GETARG_POINTER(4);
66 648 : Datum *entries = (Datum *) palloc(sizeof(Datum));
67 648 : QueryInfo *data = (QueryInfo *) palloc(sizeof(QueryInfo));
68 : bool *ptr_partialmatch;
69 :
70 648 : *nentries = 1;
71 648 : ptr_partialmatch = *partialmatch = (bool *) palloc(sizeof(bool));
72 648 : *ptr_partialmatch = false;
73 648 : if (is_varlena)
74 194 : datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
75 648 : data->strategy = strategy;
76 648 : data->datum = datum;
77 648 : data->is_varlena = is_varlena;
78 648 : data->typecmp = typecmp;
79 648 : *extra_data = (Pointer *) palloc(sizeof(Pointer));
80 648 : **extra_data = (Pointer) data;
81 :
82 648 : switch (strategy)
83 : {
84 256 : case BTLessStrategyNumber:
85 : case BTLessEqualStrategyNumber:
86 256 : entries[0] = leftmostvalue();
87 256 : *ptr_partialmatch = true;
88 256 : break;
89 258 : case BTGreaterEqualStrategyNumber:
90 : case BTGreaterStrategyNumber:
91 258 : *ptr_partialmatch = true;
92 : /* FALLTHROUGH */
93 392 : case BTEqualStrategyNumber:
94 392 : entries[0] = datum;
95 392 : break;
96 0 : default:
97 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
98 : }
99 :
100 648 : PG_RETURN_POINTER(entries);
101 : }
102 :
103 : /*
104 : * Datum a is a value from extract_query method and for BTLess*
105 : * strategy it is a left-most value. So, use original datum from QueryInfo
106 : * to decide to stop scanning or not. Datum b is always from index.
107 : */
108 : static Datum
109 878 : gin_btree_compare_prefix(FunctionCallInfo fcinfo)
110 : {
111 878 : Datum a = PG_GETARG_DATUM(0);
112 878 : Datum b = PG_GETARG_DATUM(1);
113 878 : QueryInfo *data = (QueryInfo *) PG_GETARG_POINTER(3);
114 : int32 res,
115 : cmp;
116 :
117 878 : cmp = DatumGetInt32(CallerFInfoFunctionCall2(data->typecmp,
118 : fcinfo->flinfo,
119 : PG_GET_COLLATION(),
120 878 : (data->strategy == BTLessStrategyNumber ||
121 646 : data->strategy == BTLessEqualStrategyNumber)
122 : ? data->datum : a,
123 : b));
124 :
125 878 : switch (data->strategy)
126 : {
127 232 : case BTLessStrategyNumber:
128 : /* If original datum > indexed one then return match */
129 232 : if (cmp > 0)
130 172 : res = 0;
131 : else
132 60 : res = 1;
133 232 : break;
134 290 : case BTLessEqualStrategyNumber:
135 : /* The same except equality */
136 290 : if (cmp >= 0)
137 232 : res = 0;
138 : else
139 58 : res = 1;
140 290 : break;
141 0 : case BTEqualStrategyNumber:
142 0 : if (cmp != 0)
143 0 : res = 1;
144 : else
145 0 : res = 0;
146 0 : break;
147 178 : case BTGreaterEqualStrategyNumber:
148 : /* If original datum <= indexed one then return match */
149 178 : if (cmp <= 0)
150 178 : res = 0;
151 : else
152 0 : res = 1;
153 178 : break;
154 178 : case BTGreaterStrategyNumber:
155 : /* If original datum <= indexed one then return match */
156 : /* If original datum == indexed one then continue scan */
157 178 : if (cmp < 0)
158 118 : res = 0;
159 60 : else if (cmp == 0)
160 60 : res = -1;
161 : else
162 0 : res = 1;
163 178 : break;
164 0 : default:
165 0 : elog(ERROR, "unrecognized strategy number: %d",
166 : data->strategy);
167 : res = 0;
168 : }
169 :
170 878 : PG_RETURN_INT32(res);
171 : }
172 :
173 60 : PG_FUNCTION_INFO_V1(gin_btree_consistent);
174 : Datum
175 774 : gin_btree_consistent(PG_FUNCTION_ARGS)
176 : {
177 774 : bool *recheck = (bool *) PG_GETARG_POINTER(5);
178 :
179 774 : *recheck = false;
180 774 : PG_RETURN_BOOL(true);
181 : }
182 :
183 : /*** GIN_SUPPORT macro defines the datatype specific functions ***/
184 :
185 : #define GIN_SUPPORT(type, is_varlena, leftmostvalue, typecmp) \
186 : PG_FUNCTION_INFO_V1(gin_extract_value_##type); \
187 : Datum \
188 : gin_extract_value_##type(PG_FUNCTION_ARGS) \
189 : { \
190 : return gin_btree_extract_value(fcinfo, is_varlena); \
191 : } \
192 : PG_FUNCTION_INFO_V1(gin_extract_query_##type); \
193 : Datum \
194 : gin_extract_query_##type(PG_FUNCTION_ARGS) \
195 : { \
196 : return gin_btree_extract_query(fcinfo, \
197 : is_varlena, leftmostvalue, typecmp); \
198 : } \
199 : PG_FUNCTION_INFO_V1(gin_compare_prefix_##type); \
200 : Datum \
201 : gin_compare_prefix_##type(PG_FUNCTION_ARGS) \
202 : { \
203 : return gin_btree_compare_prefix(fcinfo); \
204 : }
205 :
206 :
207 : /*** Datatype specifications ***/
208 :
209 : static Datum
210 8 : leftmostvalue_int2(void)
211 : {
212 8 : return Int16GetDatum(SHRT_MIN);
213 : }
214 :
215 74 : GIN_SUPPORT(int2, false, leftmostvalue_int2, btint2cmp)
216 :
217 : static Datum
218 8 : leftmostvalue_int4(void)
219 : {
220 8 : return Int32GetDatum(INT_MIN);
221 : }
222 :
223 74 : GIN_SUPPORT(int4, false, leftmostvalue_int4, btint4cmp)
224 :
225 : static Datum
226 8 : leftmostvalue_int8(void)
227 : {
228 8 : return Int64GetDatum(PG_INT64_MIN);
229 : }
230 :
231 74 : GIN_SUPPORT(int8, false, leftmostvalue_int8, btint8cmp)
232 :
233 : static Datum
234 8 : leftmostvalue_float4(void)
235 : {
236 8 : return Float4GetDatum(-get_float4_infinity());
237 : }
238 :
239 74 : GIN_SUPPORT(float4, false, leftmostvalue_float4, btfloat4cmp)
240 :
241 : static Datum
242 8 : leftmostvalue_float8(void)
243 : {
244 8 : return Float8GetDatum(-get_float8_infinity());
245 : }
246 :
247 74 : GIN_SUPPORT(float8, false, leftmostvalue_float8, btfloat8cmp)
248 :
249 : static Datum
250 8 : leftmostvalue_money(void)
251 : {
252 8 : return Int64GetDatum(PG_INT64_MIN);
253 : }
254 :
255 74 : GIN_SUPPORT(money, false, leftmostvalue_money, cash_cmp)
256 :
257 : static Datum
258 8 : leftmostvalue_oid(void)
259 : {
260 8 : return ObjectIdGetDatum(0);
261 : }
262 :
263 74 : GIN_SUPPORT(oid, false, leftmostvalue_oid, btoidcmp)
264 :
265 : static Datum
266 16 : leftmostvalue_timestamp(void)
267 : {
268 16 : return TimestampGetDatum(DT_NOBEGIN);
269 : }
270 :
271 74 : GIN_SUPPORT(timestamp, false, leftmostvalue_timestamp, timestamp_cmp)
272 :
273 74 : GIN_SUPPORT(timestamptz, false, leftmostvalue_timestamp, timestamp_cmp)
274 :
275 : static Datum
276 8 : leftmostvalue_time(void)
277 : {
278 8 : return TimeADTGetDatum(0);
279 : }
280 :
281 74 : GIN_SUPPORT(time, false, leftmostvalue_time, time_cmp)
282 :
283 : static Datum
284 8 : leftmostvalue_timetz(void)
285 : {
286 8 : TimeTzADT *v = palloc(sizeof(TimeTzADT));
287 :
288 8 : v->time = 0;
289 8 : v->zone = -24 * 3600; /* XXX is that true? */
290 :
291 8 : return TimeTzADTPGetDatum(v);
292 : }
293 :
294 74 : GIN_SUPPORT(timetz, false, leftmostvalue_timetz, timetz_cmp)
295 :
296 : static Datum
297 8 : leftmostvalue_date(void)
298 : {
299 8 : return DateADTGetDatum(DATEVAL_NOBEGIN);
300 : }
301 :
302 74 : GIN_SUPPORT(date, false, leftmostvalue_date, date_cmp)
303 :
304 : static Datum
305 8 : leftmostvalue_interval(void)
306 : {
307 8 : Interval *v = palloc(sizeof(Interval));
308 :
309 8 : INTERVAL_NOBEGIN(v);
310 :
311 8 : return IntervalPGetDatum(v);
312 : }
313 :
314 86 : GIN_SUPPORT(interval, false, leftmostvalue_interval, interval_cmp)
315 :
316 : static Datum
317 8 : leftmostvalue_macaddr(void)
318 : {
319 8 : macaddr *v = palloc0(sizeof(macaddr));
320 :
321 8 : return MacaddrPGetDatum(v);
322 : }
323 :
324 74 : GIN_SUPPORT(macaddr, false, leftmostvalue_macaddr, macaddr_cmp)
325 :
326 : static Datum
327 8 : leftmostvalue_macaddr8(void)
328 : {
329 8 : macaddr8 *v = palloc0(sizeof(macaddr8));
330 :
331 8 : return Macaddr8PGetDatum(v);
332 : }
333 :
334 74 : GIN_SUPPORT(macaddr8, false, leftmostvalue_macaddr8, macaddr8_cmp)
335 :
336 : static Datum
337 16 : leftmostvalue_inet(void)
338 : {
339 16 : return DirectFunctionCall1(inet_in, CStringGetDatum("0.0.0.0/0"));
340 : }
341 :
342 74 : GIN_SUPPORT(inet, true, leftmostvalue_inet, network_cmp)
343 :
344 74 : GIN_SUPPORT(cidr, true, leftmostvalue_inet, network_cmp)
345 :
346 : static Datum
347 36 : leftmostvalue_text(void)
348 : {
349 36 : return PointerGetDatum(cstring_to_text_with_len("", 0));
350 : }
351 :
352 142 : GIN_SUPPORT(text, true, leftmostvalue_text, bttextcmp)
353 :
354 92 : GIN_SUPPORT(bpchar, true, leftmostvalue_text, bpcharcmp)
355 :
356 : static Datum
357 8 : leftmostvalue_char(void)
358 : {
359 8 : return CharGetDatum(0);
360 : }
361 :
362 74 : GIN_SUPPORT(char, false, leftmostvalue_char, btcharcmp)
363 :
364 74 : GIN_SUPPORT(bytea, true, leftmostvalue_text, byteacmp)
365 :
366 : static Datum
367 8 : leftmostvalue_bit(void)
368 : {
369 8 : return DirectFunctionCall3(bit_in,
370 : CStringGetDatum(""),
371 : ObjectIdGetDatum(0),
372 : Int32GetDatum(-1));
373 : }
374 :
375 74 : GIN_SUPPORT(bit, true, leftmostvalue_bit, bitcmp)
376 :
377 : static Datum
378 8 : leftmostvalue_varbit(void)
379 : {
380 8 : return DirectFunctionCall3(varbit_in,
381 : CStringGetDatum(""),
382 : ObjectIdGetDatum(0),
383 : Int32GetDatum(-1));
384 : }
385 :
386 74 : GIN_SUPPORT(varbit, true, leftmostvalue_varbit, bitcmp)
387 :
388 : /*
389 : * Numeric type hasn't a real left-most value, so we use PointerGetDatum(NULL)
390 : * (*not* a SQL NULL) to represent that. We can get away with that because
391 : * the value returned by our leftmostvalue function will never be stored in
392 : * the index nor passed to anything except our compare and prefix-comparison
393 : * functions. The same trick could be used for other pass-by-reference types.
394 : */
395 :
396 : #define NUMERIC_IS_LEFTMOST(x) ((x) == NULL)
397 :
398 4 : PG_FUNCTION_INFO_V1(gin_numeric_cmp);
399 :
400 : Datum
401 86 : gin_numeric_cmp(PG_FUNCTION_ARGS)
402 : {
403 86 : Numeric a = (Numeric) PG_GETARG_POINTER(0);
404 86 : Numeric b = (Numeric) PG_GETARG_POINTER(1);
405 86 : int res = 0;
406 :
407 86 : if (NUMERIC_IS_LEFTMOST(a))
408 : {
409 12 : res = (NUMERIC_IS_LEFTMOST(b)) ? 0 : -1;
410 : }
411 74 : else if (NUMERIC_IS_LEFTMOST(b))
412 : {
413 0 : res = 1;
414 : }
415 : else
416 : {
417 74 : res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
418 : NumericGetDatum(a),
419 : NumericGetDatum(b)));
420 : }
421 :
422 86 : PG_RETURN_INT32(res);
423 : }
424 :
425 : static Datum
426 8 : leftmostvalue_numeric(void)
427 : {
428 8 : return PointerGetDatum(NULL);
429 : }
430 :
431 74 : GIN_SUPPORT(numeric, true, leftmostvalue_numeric, gin_numeric_cmp)
432 :
433 : /*
434 : * Use a similar trick to that used for numeric for enums, since we don't
435 : * actually know the leftmost value of any enum without knowing the concrete
436 : * type, so we use a dummy leftmost value of InvalidOid.
437 : *
438 : * Note that we use CallerFInfoFunctionCall2 here so that enum_cmp
439 : * gets a valid fn_extra to work with. Unlike most other type comparison
440 : * routines it needs it, so we can't use DirectFunctionCall2.
441 : */
442 :
443 : #define ENUM_IS_LEFTMOST(x) ((x) == InvalidOid)
444 :
445 4 : PG_FUNCTION_INFO_V1(gin_enum_cmp);
446 :
447 : Datum
448 400104 : gin_enum_cmp(PG_FUNCTION_ARGS)
449 : {
450 400104 : Oid a = PG_GETARG_OID(0);
451 400104 : Oid b = PG_GETARG_OID(1);
452 400104 : int res = 0;
453 :
454 400104 : if (ENUM_IS_LEFTMOST(a))
455 : {
456 12 : res = (ENUM_IS_LEFTMOST(b)) ? 0 : -1;
457 : }
458 400092 : else if (ENUM_IS_LEFTMOST(b))
459 : {
460 0 : res = 1;
461 : }
462 : else
463 : {
464 400092 : res = DatumGetInt32(CallerFInfoFunctionCall2(enum_cmp,
465 : fcinfo->flinfo,
466 : PG_GET_COLLATION(),
467 : ObjectIdGetDatum(a),
468 : ObjectIdGetDatum(b)));
469 : }
470 :
471 400104 : PG_RETURN_INT32(res);
472 : }
473 :
474 : static Datum
475 8 : leftmostvalue_enum(void)
476 : {
477 8 : return ObjectIdGetDatum(InvalidOid);
478 : }
479 :
480 200084 : GIN_SUPPORT(anyenum, false, leftmostvalue_enum, gin_enum_cmp)
481 :
482 : static Datum
483 12 : leftmostvalue_uuid(void)
484 : {
485 : /*
486 : * palloc0 will create the UUID with all zeroes:
487 : * "00000000-0000-0000-0000-000000000000"
488 : */
489 12 : pg_uuid_t *retval = (pg_uuid_t *) palloc0(sizeof(pg_uuid_t));
490 :
491 12 : return UUIDPGetDatum(retval);
492 : }
493 :
494 84 : GIN_SUPPORT(uuid, false, leftmostvalue_uuid, uuid_cmp)
495 :
496 : static Datum
497 12 : leftmostvalue_name(void)
498 : {
499 12 : NameData *result = (NameData *) palloc0(NAMEDATALEN);
500 :
501 12 : return NameGetDatum(result);
502 : }
503 :
504 84 : GIN_SUPPORT(name, false, leftmostvalue_name, btnamecmp)
505 :
506 : static Datum
507 20 : leftmostvalue_bool(void)
508 : {
509 20 : return BoolGetDatum(false);
510 : }
511 :
512 94 : GIN_SUPPORT(bool, false, leftmostvalue_bool, btboolcmp)
|