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