Age Owner Branch data 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 : :
5499 tgl@sss.pgh.pa.us 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
3401 heikki.linnakangas@i 33 : 100176 : gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
34 : : {
35 : 100176 : Datum datum = PG_GETARG_DATUM(0);
36 : 100176 : int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
37 : 100176 : Datum *entries = (Datum *) palloc(sizeof(Datum));
38 : :
39 [ + + ]: 100176 : if (is_varlena)
40 : 56 : datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
41 : 100176 : entries[0] = datum;
42 : 100176 : *nentries = 1;
43 : :
44 : 100176 : 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;
3401 heikki.linnakangas@i 96 :UBC 0 : default:
97 [ # # ]: 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
98 : : }
99 : :
3401 heikki.linnakangas@i 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 : 439 : gin_btree_compare_prefix(FunctionCallInfo fcinfo)
110 : : {
111 : 439 : Datum a = PG_GETARG_DATUM(0);
112 : 439 : Datum b = PG_GETARG_DATUM(1);
113 : 439 : QueryInfo *data = (QueryInfo *) PG_GETARG_POINTER(3);
114 : : int32 res,
115 : : cmp;
116 : :
1536 alvherre@alvh.no-ip. 117 : 439 : cmp = DatumGetInt32(CallerFInfoFunctionCall2(data->typecmp,
118 : : fcinfo->flinfo,
119 : : PG_GET_COLLATION(),
2489 tgl@sss.pgh.pa.us 120 [ + + ]: 439 : (data->strategy == BTLessStrategyNumber ||
121 [ + + ]: 323 : data->strategy == BTLessEqualStrategyNumber)
122 : : ? data->datum : a,
123 : : b));
124 : :
3401 heikki.linnakangas@i 125 [ + + - + : 439 : switch (data->strategy)
+ - ]
126 : : {
127 : 116 : case BTLessStrategyNumber:
128 : : /* If original datum > indexed one then return match */
129 [ + + ]: 116 : if (cmp > 0)
130 : 86 : res = 0;
131 : : else
132 : 30 : res = 1;
133 : 116 : break;
134 : 145 : case BTLessEqualStrategyNumber:
135 : : /* The same except equality */
136 [ + + ]: 145 : if (cmp >= 0)
137 : 116 : res = 0;
138 : : else
139 : 29 : res = 1;
140 : 145 : break;
3401 heikki.linnakangas@i 141 :UBC 0 : case BTEqualStrategyNumber:
142 [ # # ]: 0 : if (cmp != 0)
143 : 0 : res = 1;
144 : : else
145 : 0 : res = 0;
146 : 0 : break;
3401 heikki.linnakangas@i 147 :CBC 89 : case BTGreaterEqualStrategyNumber:
148 : : /* If original datum <= indexed one then return match */
149 [ + - ]: 89 : if (cmp <= 0)
150 : 89 : res = 0;
151 : : else
3401 heikki.linnakangas@i 152 :UBC 0 : res = 1;
3401 heikki.linnakangas@i 153 :CBC 89 : break;
154 : 89 : case BTGreaterStrategyNumber:
155 : : /* If original datum <= indexed one then return match */
156 : : /* If original datum == indexed one then continue scan */
157 [ + + ]: 89 : if (cmp < 0)
158 : 59 : res = 0;
159 [ + - ]: 30 : else if (cmp == 0)
160 : 30 : res = -1;
161 : : else
3401 heikki.linnakangas@i 162 :UBC 0 : res = 1;
3401 heikki.linnakangas@i 163 :CBC 89 : break;
3401 heikki.linnakangas@i 164 :UBC 0 : default:
165 [ # # ]: 0 : elog(ERROR, "unrecognized strategy number: %d",
166 : : data->strategy);
167 : : res = 0;
168 : : }
169 : :
3401 heikki.linnakangas@i 170 :CBC 439 : PG_RETURN_INT32(res);
171 : : }
172 : :
5499 tgl@sss.pgh.pa.us 173 : 30 : PG_FUNCTION_INFO_V1(gin_btree_consistent);
174 : : Datum
175 : 387 : gin_btree_consistent(PG_FUNCTION_ARGS)
176 : : {
5421 bruce@momjian.us 177 : 387 : bool *recheck = (bool *) PG_GETARG_POINTER(5);
178 : :
5499 tgl@sss.pgh.pa.us 179 : 387 : *recheck = false;
180 : 387 : 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 : :
3401 heikki.linnakangas@i 215 : 37 : GIN_SUPPORT(int2, false, leftmostvalue_int2, btint2cmp)
216 : :
217 : : static Datum
5499 tgl@sss.pgh.pa.us 218 : 4 : leftmostvalue_int4(void)
219 : : {
220 : 4 : return Int32GetDatum(INT_MIN);
221 : : }
222 : :
3401 heikki.linnakangas@i 223 : 37 : GIN_SUPPORT(int4, false, leftmostvalue_int4, btint4cmp)
224 : :
225 : : static Datum
5499 tgl@sss.pgh.pa.us 226 : 4 : leftmostvalue_int8(void)
227 : : {
2828 peter_e@gmx.net 228 : 4 : return Int64GetDatum(PG_INT64_MIN);
229 : : }
230 : :
3401 heikki.linnakangas@i 231 : 37 : GIN_SUPPORT(int8, false, leftmostvalue_int8, btint8cmp)
232 : :
233 : : static Datum
5499 tgl@sss.pgh.pa.us 234 : 4 : leftmostvalue_float4(void)
235 : : {
236 : 4 : return Float4GetDatum(-get_float4_infinity());
237 : : }
238 : :
3401 heikki.linnakangas@i 239 : 37 : GIN_SUPPORT(float4, false, leftmostvalue_float4, btfloat4cmp)
240 : :
241 : : static Datum
5499 tgl@sss.pgh.pa.us 242 : 4 : leftmostvalue_float8(void)
243 : : {
244 : 4 : return Float8GetDatum(-get_float8_infinity());
245 : : }
246 : :
3401 heikki.linnakangas@i 247 : 37 : GIN_SUPPORT(float8, false, leftmostvalue_float8, btfloat8cmp)
248 : :
249 : : static Datum
5499 tgl@sss.pgh.pa.us 250 : 4 : leftmostvalue_money(void)
251 : : {
2828 peter_e@gmx.net 252 : 4 : return Int64GetDatum(PG_INT64_MIN);
253 : : }
254 : :
3401 heikki.linnakangas@i 255 : 37 : GIN_SUPPORT(money, false, leftmostvalue_money, cash_cmp)
256 : :
257 : : static Datum
5499 tgl@sss.pgh.pa.us 258 : 4 : leftmostvalue_oid(void)
259 : : {
260 : 4 : return ObjectIdGetDatum(0);
261 : : }
262 : :
3401 heikki.linnakangas@i 263 : 37 : GIN_SUPPORT(oid, false, leftmostvalue_oid, btoidcmp)
264 : :
265 : : static Datum
5499 tgl@sss.pgh.pa.us 266 : 8 : leftmostvalue_timestamp(void)
267 : : {
268 : 8 : return TimestampGetDatum(DT_NOBEGIN);
269 : : }
270 : :
3401 heikki.linnakangas@i 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
5499 tgl@sss.pgh.pa.us 276 : 4 : leftmostvalue_time(void)
277 : : {
278 : 4 : return TimeADTGetDatum(0);
279 : : }
280 : :
3401 heikki.linnakangas@i 281 : 37 : GIN_SUPPORT(time, false, leftmostvalue_time, time_cmp)
282 : :
283 : : static Datum
5499 tgl@sss.pgh.pa.us 284 : 4 : leftmostvalue_timetz(void)
285 : : {
5421 bruce@momjian.us 286 : 4 : TimeTzADT *v = palloc(sizeof(TimeTzADT));
287 : :
5499 tgl@sss.pgh.pa.us 288 : 4 : v->time = 0;
5421 bruce@momjian.us 289 : 4 : v->zone = -24 * 3600; /* XXX is that true? */
290 : :
5499 tgl@sss.pgh.pa.us 291 : 4 : return TimeTzADTPGetDatum(v);
292 : : }
293 : :
3401 heikki.linnakangas@i 294 : 37 : GIN_SUPPORT(timetz, false, leftmostvalue_timetz, timetz_cmp)
295 : :
296 : : static Datum
5499 tgl@sss.pgh.pa.us 297 : 4 : leftmostvalue_date(void)
298 : : {
299 : 4 : return DateADTGetDatum(DATEVAL_NOBEGIN);
300 : : }
301 : :
3401 heikki.linnakangas@i 302 : 37 : GIN_SUPPORT(date, false, leftmostvalue_date, date_cmp)
303 : :
304 : : static Datum
5499 tgl@sss.pgh.pa.us 305 : 4 : leftmostvalue_interval(void)
306 : : {
5421 bruce@momjian.us 307 : 4 : Interval *v = palloc(sizeof(Interval));
308 : :
152 dean.a.rasheed@gmail 309 :GNC 4 : INTERVAL_NOBEGIN(v);
310 : :
5499 tgl@sss.pgh.pa.us 311 :CBC 4 : return IntervalPGetDatum(v);
312 : : }
313 : :
3401 heikki.linnakangas@i 314 : 43 : GIN_SUPPORT(interval, false, leftmostvalue_interval, interval_cmp)
315 : :
316 : : static Datum
5499 tgl@sss.pgh.pa.us 317 : 4 : leftmostvalue_macaddr(void)
318 : : {
5421 bruce@momjian.us 319 : 4 : macaddr *v = palloc0(sizeof(macaddr));
320 : :
5499 tgl@sss.pgh.pa.us 321 : 4 : return MacaddrPGetDatum(v);
322 : : }
323 : :
3401 heikki.linnakangas@i 324 : 37 : GIN_SUPPORT(macaddr, false, leftmostvalue_macaddr, macaddr_cmp)
325 : :
326 : : static Datum
2587 sfrost@snowman.net 327 : 4 : leftmostvalue_macaddr8(void)
328 : : {
329 : 4 : macaddr8 *v = palloc0(sizeof(macaddr8));
330 : :
331 : 4 : return Macaddr8PGetDatum(v);
332 : : }
333 : :
334 : 37 : GIN_SUPPORT(macaddr8, false, leftmostvalue_macaddr8, macaddr8_cmp)
335 : :
336 : : static Datum
5499 tgl@sss.pgh.pa.us 337 : 8 : leftmostvalue_inet(void)
338 : : {
3302 bruce@momjian.us 339 : 8 : return DirectFunctionCall1(inet_in, CStringGetDatum("0.0.0.0/0"));
340 : : }
341 : :
3401 heikki.linnakangas@i 342 : 37 : GIN_SUPPORT(inet, true, leftmostvalue_inet, network_cmp)
343 : :
344 : 37 : GIN_SUPPORT(cidr, true, leftmostvalue_inet, network_cmp)
345 : :
346 : : static Datum
5499 tgl@sss.pgh.pa.us 347 : 18 : leftmostvalue_text(void)
348 : : {
349 : 18 : return PointerGetDatum(cstring_to_text_with_len("", 0));
350 : : }
351 : :
3401 heikki.linnakangas@i 352 : 71 : GIN_SUPPORT(text, true, leftmostvalue_text, bttextcmp)
353 : :
2201 teodor@sigaev.ru 354 : 46 : GIN_SUPPORT(bpchar, true, leftmostvalue_text, bpcharcmp)
355 : :
356 : : static Datum
5499 tgl@sss.pgh.pa.us 357 : 4 : leftmostvalue_char(void)
358 : : {
978 359 : 4 : return CharGetDatum(0);
360 : : }
361 : :
3401 heikki.linnakangas@i 362 : 37 : GIN_SUPPORT(char, false, leftmostvalue_char, btcharcmp)
363 : :
364 : 37 : GIN_SUPPORT(bytea, true, leftmostvalue_text, byteacmp)
365 : :
366 : : static Datum
5499 tgl@sss.pgh.pa.us 367 : 4 : leftmostvalue_bit(void)
368 : : {
3309 bruce@momjian.us 369 : 4 : return DirectFunctionCall3(bit_in,
370 : : CStringGetDatum(""),
371 : : ObjectIdGetDatum(0),
372 : : Int32GetDatum(-1));
373 : : }
374 : :
3401 heikki.linnakangas@i 375 : 37 : GIN_SUPPORT(bit, true, leftmostvalue_bit, bitcmp)
376 : :
377 : : static Datum
5499 tgl@sss.pgh.pa.us 378 : 4 : leftmostvalue_varbit(void)
379 : : {
3309 bruce@momjian.us 380 : 4 : return DirectFunctionCall3(varbit_in,
381 : : CStringGetDatum(""),
382 : : ObjectIdGetDatum(0),
383 : : Int32GetDatum(-1));
384 : : }
385 : :
3401 heikki.linnakangas@i 386 : 37 : 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 : :
5499 tgl@sss.pgh.pa.us 398 : 2 : PG_FUNCTION_INFO_V1(gin_numeric_cmp);
399 : :
400 : : Datum
401 : 43 : gin_numeric_cmp(PG_FUNCTION_ARGS)
402 : : {
5421 bruce@momjian.us 403 : 43 : Numeric a = (Numeric) PG_GETARG_POINTER(0);
404 : 43 : Numeric b = (Numeric) PG_GETARG_POINTER(1);
405 : 43 : int res = 0;
406 : :
407 [ + + ]: 43 : if (NUMERIC_IS_LEFTMOST(a))
408 : : {
409 [ - + ]: 6 : res = (NUMERIC_IS_LEFTMOST(b)) ? 0 : -1;
410 : : }
411 [ - + ]: 37 : else if (NUMERIC_IS_LEFTMOST(b))
412 : : {
5499 tgl@sss.pgh.pa.us 413 :UBC 0 : res = 1;
414 : : }
415 : : else
416 : : {
5499 tgl@sss.pgh.pa.us 417 :CBC 37 : res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
418 : : NumericGetDatum(a),
419 : : NumericGetDatum(b)));
420 : : }
421 : :
422 : 43 : PG_RETURN_INT32(res);
423 : : }
424 : :
425 : : static Datum
426 : 4 : leftmostvalue_numeric(void)
427 : : {
428 : 4 : return PointerGetDatum(NULL);
429 : : }
430 : :
3401 heikki.linnakangas@i 431 : 37 : 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 : :
2581 andrew@dunslane.net 445 : 2 : PG_FUNCTION_INFO_V1(gin_enum_cmp);
446 : :
447 : : Datum
448 : 200052 : gin_enum_cmp(PG_FUNCTION_ARGS)
449 : : {
2524 bruce@momjian.us 450 : 200052 : Oid a = PG_GETARG_OID(0);
451 : 200052 : Oid b = PG_GETARG_OID(1);
452 : 200052 : int res = 0;
453 : :
2581 andrew@dunslane.net 454 [ + + ]: 200052 : if (ENUM_IS_LEFTMOST(a))
455 : : {
456 [ - + ]: 6 : res = (ENUM_IS_LEFTMOST(b)) ? 0 : -1;
457 : : }
458 [ - + ]: 200046 : else if (ENUM_IS_LEFTMOST(b))
459 : : {
2581 andrew@dunslane.net 460 :UBC 0 : res = 1;
461 : : }
462 : : else
463 : : {
1536 alvherre@alvh.no-ip. 464 :CBC 200046 : res = DatumGetInt32(CallerFInfoFunctionCall2(enum_cmp,
465 : : fcinfo->flinfo,
466 : : PG_GET_COLLATION(),
467 : : ObjectIdGetDatum(a),
468 : : ObjectIdGetDatum(b)));
469 : : }
470 : :
2581 andrew@dunslane.net 471 : 200052 : PG_RETURN_INT32(res);
472 : : }
473 : :
474 : : static Datum
475 : 4 : leftmostvalue_enum(void)
476 : : {
477 : 4 : return ObjectIdGetDatum(InvalidOid);
478 : : }
479 : :
480 : 100042 : GIN_SUPPORT(anyenum, false, leftmostvalue_enum, gin_enum_cmp)
481 : :
482 : : static Datum
2201 teodor@sigaev.ru 483 : 6 : leftmostvalue_uuid(void)
484 : : {
485 : : /*
486 : : * palloc0 will create the UUID with all zeroes:
487 : : * "00000000-0000-0000-0000-000000000000"
488 : : */
2180 tgl@sss.pgh.pa.us 489 : 6 : pg_uuid_t *retval = (pg_uuid_t *) palloc0(sizeof(pg_uuid_t));
490 : :
2201 teodor@sigaev.ru 491 : 6 : return UUIDPGetDatum(retval);
492 : : }
493 : :
494 : 42 : GIN_SUPPORT(uuid, false, leftmostvalue_uuid, uuid_cmp)
495 : :
496 : : static Datum
497 : 6 : leftmostvalue_name(void)
498 : : {
2180 tgl@sss.pgh.pa.us 499 : 6 : NameData *result = (NameData *) palloc0(NAMEDATALEN);
500 : :
2201 teodor@sigaev.ru 501 : 6 : return NameGetDatum(result);
502 : : }
503 : :
504 : 42 : GIN_SUPPORT(name, false, leftmostvalue_name, btnamecmp)
505 : :
506 : : static Datum
507 : 10 : leftmostvalue_bool(void)
508 : : {
509 : 10 : return BoolGetDatum(false);
510 : : }
511 : :
512 : 47 : GIN_SUPPORT(bool, false, leftmostvalue_bool, btboolcmp)
|