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 "mb/pg_wchar.h"
10 : #include "utils/builtins.h"
11 : #include "utils/date.h"
12 : #include "utils/float.h"
13 : #include "utils/inet.h"
14 : #include "utils/numeric.h"
15 : #include "utils/timestamp.h"
16 : #include "utils/uuid.h"
17 : #include "varatt.h"
18 :
19 60 : PG_MODULE_MAGIC_EXT(
20 : .name = "btree_gin",
21 : .version = PG_VERSION
22 : );
23 :
24 : /*
25 : * Our opclasses use the same strategy numbers as btree (1-5) for same-type
26 : * comparison operators. For cross-type comparison operators, the
27 : * low 4 bits of our strategy numbers are the btree strategy number,
28 : * and the upper bits are a code for the right-hand-side data type.
29 : */
30 : #define BTGIN_GET_BTREE_STRATEGY(strat) ((strat) & 0x0F)
31 : #define BTGIN_GET_RHS_TYPE_CODE(strat) ((strat) >> 4)
32 :
33 : /* extra data passed from gin_btree_extract_query to gin_btree_compare_prefix */
34 : typedef struct QueryInfo
35 : {
36 : StrategyNumber strategy; /* operator strategy number */
37 : Datum orig_datum; /* original query (comparison) datum */
38 : Datum entry_datum; /* datum we reported as the entry value */
39 : PGFunction typecmp; /* appropriate btree comparison function */
40 : } QueryInfo;
41 :
42 : typedef Datum (*btree_gin_convert_function) (Datum input);
43 :
44 : typedef Datum (*btree_gin_leftmost_function) (void);
45 :
46 :
47 : /*** GIN support functions shared by all datatypes ***/
48 :
49 : static Datum
50 200370 : gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
51 : {
52 200370 : Datum datum = PG_GETARG_DATUM(0);
53 200370 : int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
54 200370 : Datum *entries = (Datum *) palloc(sizeof(Datum));
55 :
56 : /* Ensure that values stored in the index are not toasted */
57 200370 : if (is_varlena)
58 112 : datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
59 200370 : entries[0] = datum;
60 200370 : *nentries = 1;
61 :
62 200370 : PG_RETURN_POINTER(entries);
63 : }
64 :
65 : static Datum
66 1372 : gin_btree_extract_query(FunctionCallInfo fcinfo,
67 : btree_gin_leftmost_function leftmostvalue,
68 : const bool *rhs_is_varlena,
69 : const btree_gin_convert_function *cvt_fns,
70 : const PGFunction *cmp_fns)
71 : {
72 1372 : Datum datum = PG_GETARG_DATUM(0);
73 1372 : int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
74 1372 : StrategyNumber strategy = PG_GETARG_UINT16(2);
75 1372 : bool **partialmatch = (bool **) PG_GETARG_POINTER(3);
76 1372 : Pointer **extra_data = (Pointer **) PG_GETARG_POINTER(4);
77 1372 : Datum *entries = (Datum *) palloc(sizeof(Datum));
78 1372 : QueryInfo *data = (QueryInfo *) palloc(sizeof(QueryInfo));
79 1372 : bool *ptr_partialmatch = (bool *) palloc(sizeof(bool));
80 : int btree_strat,
81 : rhs_code;
82 :
83 : /*
84 : * Extract the btree strategy code and the RHS data type code from the
85 : * given strategy number.
86 : */
87 1372 : btree_strat = BTGIN_GET_BTREE_STRATEGY(strategy);
88 1372 : rhs_code = BTGIN_GET_RHS_TYPE_CODE(strategy);
89 :
90 : /*
91 : * Detoast the comparison datum. This isn't necessary for correctness,
92 : * but it can save repeat detoastings within the comparison function.
93 : */
94 1372 : if (rhs_is_varlena[rhs_code])
95 220 : datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
96 :
97 : /* Prep single comparison key with possible partial-match flag */
98 1372 : *nentries = 1;
99 1372 : *partialmatch = ptr_partialmatch;
100 1372 : *ptr_partialmatch = false;
101 :
102 : /*
103 : * For BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, and
104 : * BTEqualStrategyNumber we want to start the index scan at the supplied
105 : * query datum, and work forward. For BTLessStrategyNumber and
106 : * BTLessEqualStrategyNumber, we need to start at the leftmost key, and
107 : * work forward until the supplied query datum (which we'll send along
108 : * inside the QueryInfo structure). Use partial match rules except for
109 : * BTEqualStrategyNumber without a conversion function. (If there is a
110 : * conversion function, comparison to the entry value is not trustworthy.)
111 : */
112 1372 : switch (btree_strat)
113 : {
114 568 : case BTLessStrategyNumber:
115 : case BTLessEqualStrategyNumber:
116 568 : entries[0] = leftmostvalue();
117 568 : *ptr_partialmatch = true;
118 568 : break;
119 534 : case BTGreaterEqualStrategyNumber:
120 : case BTGreaterStrategyNumber:
121 534 : *ptr_partialmatch = true;
122 : /* FALLTHROUGH */
123 804 : case BTEqualStrategyNumber:
124 : /* If we have a conversion function, apply it */
125 804 : if (cvt_fns && cvt_fns[rhs_code])
126 : {
127 412 : entries[0] = (*cvt_fns[rhs_code]) (datum);
128 412 : *ptr_partialmatch = true;
129 : }
130 : else
131 392 : entries[0] = datum;
132 804 : break;
133 0 : default:
134 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
135 : }
136 :
137 : /* Fill "extra" data */
138 1372 : data->strategy = strategy;
139 1372 : data->orig_datum = datum;
140 1372 : data->entry_datum = entries[0];
141 1372 : data->typecmp = cmp_fns[rhs_code];
142 1372 : *extra_data = (Pointer *) palloc(sizeof(Pointer));
143 1372 : **extra_data = (Pointer) data;
144 :
145 1372 : PG_RETURN_POINTER(entries);
146 : }
147 :
148 : static Datum
149 2188 : gin_btree_compare_prefix(FunctionCallInfo fcinfo)
150 : {
151 2188 : Datum partial_key PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(0);
152 2188 : Datum key = PG_GETARG_DATUM(1);
153 2188 : QueryInfo *data = (QueryInfo *) PG_GETARG_POINTER(3);
154 : int32 res,
155 : cmp;
156 :
157 : /*
158 : * partial_key is only an approximation to the real comparison value,
159 : * especially if it's a leftmost value. We can get an accurate answer by
160 : * doing a possibly-cross-type comparison to the real comparison value.
161 : * (Note that partial_key and key are of the indexed datatype while
162 : * orig_datum is of the query operator's RHS datatype.)
163 : *
164 : * But just to be sure that things are what we expect, let's assert that
165 : * partial_key is indeed what gin_btree_extract_query reported, so that
166 : * we'll notice if anyone ever changes the core code in a way that breaks
167 : * our assumptions.
168 : */
169 : Assert(partial_key == data->entry_datum);
170 :
171 2188 : cmp = DatumGetInt32(CallerFInfoFunctionCall2(data->typecmp,
172 : fcinfo->flinfo,
173 : PG_GET_COLLATION(),
174 : data->orig_datum,
175 : key));
176 :
177 : /*
178 : * Convert the comparison result to the correct thing for the search
179 : * operator strategy. When dealing with cross-type comparisons, an
180 : * imprecise entry datum could lead GIN to start the scan just before the
181 : * first possible match, so we must continue the scan if the current index
182 : * entry doesn't satisfy the search condition for >= and > cases. But if
183 : * that happens in an = search we can stop, because an imprecise entry
184 : * datum means that the search value is unrepresentable in the indexed
185 : * data type, so that there will be no exact matches.
186 : */
187 2188 : switch (BTGIN_GET_BTREE_STRATEGY(data->strategy))
188 : {
189 544 : case BTLessStrategyNumber:
190 : /* If original datum > indexed one then return match */
191 544 : if (cmp > 0)
192 418 : res = 0;
193 : else
194 126 : res = 1; /* end scan */
195 544 : break;
196 656 : case BTLessEqualStrategyNumber:
197 : /* If original datum >= indexed one then return match */
198 656 : if (cmp >= 0)
199 538 : res = 0;
200 : else
201 118 : res = 1; /* end scan */
202 656 : break;
203 108 : case BTEqualStrategyNumber:
204 : /* If original datum = indexed one then return match */
205 : /* See above about why we can end scan when cmp < 0 */
206 108 : if (cmp == 0)
207 50 : res = 0;
208 : else
209 58 : res = 1; /* end scan */
210 108 : break;
211 432 : case BTGreaterEqualStrategyNumber:
212 : /* If original datum <= indexed one then return match */
213 432 : if (cmp <= 0)
214 424 : res = 0;
215 : else
216 8 : res = -1; /* keep scanning */
217 432 : break;
218 448 : case BTGreaterStrategyNumber:
219 : /* If original datum < indexed one then return match */
220 448 : if (cmp < 0)
221 328 : res = 0;
222 : else
223 120 : res = -1; /* keep scanning */
224 448 : break;
225 0 : default:
226 0 : elog(ERROR, "unrecognized strategy number: %d",
227 : data->strategy);
228 : res = 0;
229 : }
230 :
231 2188 : PG_RETURN_INT32(res);
232 : }
233 :
234 60 : PG_FUNCTION_INFO_V1(gin_btree_consistent);
235 : Datum
236 1832 : gin_btree_consistent(PG_FUNCTION_ARGS)
237 : {
238 1832 : bool *recheck = (bool *) PG_GETARG_POINTER(5);
239 :
240 1832 : *recheck = false;
241 1832 : PG_RETURN_BOOL(true);
242 : }
243 :
244 : /*** GIN_SUPPORT macro defines the datatype specific functions ***/
245 :
246 : #define GIN_SUPPORT(type, leftmostvalue, is_varlena, cvtfns, cmpfns) \
247 : PG_FUNCTION_INFO_V1(gin_extract_value_##type); \
248 : Datum \
249 : gin_extract_value_##type(PG_FUNCTION_ARGS) \
250 : { \
251 : return gin_btree_extract_value(fcinfo, is_varlena[0]); \
252 : } \
253 : PG_FUNCTION_INFO_V1(gin_extract_query_##type); \
254 : Datum \
255 : gin_extract_query_##type(PG_FUNCTION_ARGS) \
256 : { \
257 : return gin_btree_extract_query(fcinfo, \
258 : leftmostvalue, is_varlena, \
259 : cvtfns, cmpfns); \
260 : } \
261 : PG_FUNCTION_INFO_V1(gin_compare_prefix_##type); \
262 : Datum \
263 : gin_compare_prefix_##type(PG_FUNCTION_ARGS) \
264 : { \
265 : return gin_btree_compare_prefix(fcinfo); \
266 : }
267 :
268 :
269 : /*** Datatype specifications ***/
270 :
271 : /* Function to produce the least possible value of the indexed datatype */
272 : static Datum
273 44 : leftmostvalue_int2(void)
274 : {
275 44 : return Int16GetDatum(SHRT_MIN);
276 : }
277 :
278 : /*
279 : * For cross-type support, we must provide conversion functions that produce
280 : * a Datum of the indexed datatype, since GIN requires the "entry" datums to
281 : * be of that type. If an exact conversion is not possible, produce a value
282 : * that will lead GIN to find the first index entry that is greater than
283 : * or equal to the actual comparison value. (But rounding down is OK, so
284 : * sometimes we might find an index entry that's just less than the
285 : * comparison value.)
286 : *
287 : * For integer values, it's sufficient to clamp the input to be in-range.
288 : *
289 : * Note: for out-of-range input values, we could in theory detect that the
290 : * search condition matches all or none of the index, and avoid a useless
291 : * index descent in the latter case. Such searches are probably rare though,
292 : * so we don't contort this code enough to do that.
293 : */
294 : static Datum
295 36 : cvt_int4_int2(Datum input)
296 : {
297 36 : int32 val = DatumGetInt32(input);
298 :
299 36 : val = Max(val, SHRT_MIN);
300 36 : val = Min(val, SHRT_MAX);
301 36 : return Int16GetDatum((int16) val);
302 : }
303 :
304 : static Datum
305 12 : cvt_int8_int2(Datum input)
306 : {
307 12 : int64 val = DatumGetInt64(input);
308 :
309 12 : val = Max(val, SHRT_MIN);
310 12 : val = Min(val, SHRT_MAX);
311 12 : return Int16GetDatum((int16) val);
312 : }
313 :
314 : /*
315 : * RHS-type-is-varlena flags, conversion and comparison function arrays,
316 : * indexed by high bits of the operator strategy number. A NULL in the
317 : * conversion function array indicates that no conversion is needed, which
318 : * will always be the case for the zero'th entry. Note that the cross-type
319 : * comparison functions should be the ones with the indexed datatype second.
320 : */
321 : static const bool int2_rhs_is_varlena[] =
322 : {false, false, false};
323 :
324 : static const btree_gin_convert_function int2_cvt_fns[] =
325 : {NULL, cvt_int4_int2, cvt_int8_int2};
326 :
327 : static const PGFunction int2_cmp_fns[] =
328 : {btint2cmp, btint42cmp, btint82cmp};
329 :
330 306 : GIN_SUPPORT(int2, leftmostvalue_int2, int2_rhs_is_varlena, int2_cvt_fns, int2_cmp_fns)
331 :
332 : static Datum
333 28 : leftmostvalue_int4(void)
334 : {
335 28 : return Int32GetDatum(INT_MIN);
336 : }
337 :
338 : static Datum
339 12 : cvt_int2_int4(Datum input)
340 : {
341 12 : int16 val = DatumGetInt16(input);
342 :
343 12 : return Int32GetDatum((int32) val);
344 : }
345 :
346 : static Datum
347 12 : cvt_int8_int4(Datum input)
348 : {
349 12 : int64 val = DatumGetInt64(input);
350 :
351 12 : val = Max(val, INT_MIN);
352 12 : val = Min(val, INT_MAX);
353 12 : return Int32GetDatum((int32) val);
354 : }
355 :
356 : static const bool int4_rhs_is_varlena[] =
357 : {false, false, false};
358 :
359 : static const btree_gin_convert_function int4_cvt_fns[] =
360 : {NULL, cvt_int2_int4, cvt_int8_int4};
361 :
362 : static const PGFunction int4_cmp_fns[] =
363 : {btint4cmp, btint24cmp, btint84cmp};
364 :
365 186 : GIN_SUPPORT(int4, leftmostvalue_int4, int4_rhs_is_varlena, int4_cvt_fns, int4_cmp_fns)
366 :
367 : static Datum
368 28 : leftmostvalue_int8(void)
369 : {
370 28 : return Int64GetDatum(PG_INT64_MIN);
371 : }
372 :
373 : static Datum
374 12 : cvt_int2_int8(Datum input)
375 : {
376 12 : int16 val = DatumGetInt16(input);
377 :
378 12 : return Int64GetDatum((int64) val);
379 : }
380 :
381 : static Datum
382 12 : cvt_int4_int8(Datum input)
383 : {
384 12 : int32 val = DatumGetInt32(input);
385 :
386 12 : return Int64GetDatum((int64) val);
387 : }
388 :
389 : static const bool int8_rhs_is_varlena[] =
390 : {false, false, false};
391 :
392 : static const btree_gin_convert_function int8_cvt_fns[] =
393 : {NULL, cvt_int2_int8, cvt_int4_int8};
394 :
395 : static const PGFunction int8_cmp_fns[] =
396 : {btint8cmp, btint28cmp, btint48cmp};
397 :
398 186 : GIN_SUPPORT(int8, leftmostvalue_int8, int8_rhs_is_varlena, int8_cvt_fns, int8_cmp_fns)
399 :
400 : static Datum
401 66 : leftmostvalue_float4(void)
402 : {
403 66 : return Float4GetDatum(-get_float4_infinity());
404 : }
405 :
406 : static Datum
407 84 : cvt_float8_float4(Datum input)
408 : {
409 84 : float8 val = DatumGetFloat8(input);
410 : float4 result;
411 :
412 : /*
413 : * Assume that ordinary C conversion will produce a usable result.
414 : * (Compare dtof(), which raises error conditions that we don't need.)
415 : * Note that for inputs that aren't exactly representable as float4, it
416 : * doesn't matter whether the conversion rounds up or down. That might
417 : * cause us to scan a few index entries that we'll reject as not matching,
418 : * but we won't miss any that should match.
419 : */
420 84 : result = (float4) val;
421 84 : return Float4GetDatum(result);
422 : }
423 :
424 : static const bool float4_rhs_is_varlena[] =
425 : {false, false};
426 :
427 : static const btree_gin_convert_function float4_cvt_fns[] =
428 : {NULL, cvt_float8_float4};
429 :
430 : static const PGFunction float4_cmp_fns[] =
431 : {btfloat4cmp, btfloat84cmp};
432 :
433 520 : GIN_SUPPORT(float4, leftmostvalue_float4, float4_rhs_is_varlena, float4_cvt_fns, float4_cmp_fns)
434 :
435 : static Datum
436 18 : leftmostvalue_float8(void)
437 : {
438 18 : return Float8GetDatum(-get_float8_infinity());
439 : }
440 :
441 : static Datum
442 12 : cvt_float4_float8(Datum input)
443 : {
444 12 : float4 val = DatumGetFloat4(input);
445 :
446 12 : return Float8GetDatum((float8) val);
447 : }
448 :
449 : static const bool float8_rhs_is_varlena[] =
450 : {false, false};
451 :
452 : static const btree_gin_convert_function float8_cvt_fns[] =
453 : {NULL, cvt_float4_float8};
454 :
455 : static const PGFunction float8_cmp_fns[] =
456 : {btfloat8cmp, btfloat48cmp};
457 :
458 130 : GIN_SUPPORT(float8, leftmostvalue_float8, float8_rhs_is_varlena, float8_cvt_fns, float8_cmp_fns)
459 :
460 : static Datum
461 8 : leftmostvalue_money(void)
462 : {
463 8 : return Int64GetDatum(PG_INT64_MIN);
464 : }
465 :
466 : static const bool money_rhs_is_varlena[] =
467 : {false};
468 :
469 : static const PGFunction money_cmp_fns[] =
470 : {cash_cmp};
471 :
472 74 : GIN_SUPPORT(money, leftmostvalue_money, money_rhs_is_varlena, NULL, money_cmp_fns)
473 :
474 : static Datum
475 8 : leftmostvalue_oid(void)
476 : {
477 8 : return ObjectIdGetDatum(0);
478 : }
479 :
480 : static const bool oid_rhs_is_varlena[] =
481 : {false};
482 :
483 : static const PGFunction oid_cmp_fns[] =
484 : {btoidcmp};
485 :
486 74 : GIN_SUPPORT(oid, leftmostvalue_oid, oid_rhs_is_varlena, NULL, oid_cmp_fns)
487 :
488 : static Datum
489 92 : leftmostvalue_timestamp(void)
490 : {
491 92 : return TimestampGetDatum(DT_NOBEGIN);
492 : }
493 :
494 : static Datum
495 36 : cvt_date_timestamp(Datum input)
496 : {
497 36 : DateADT val = DatumGetDateADT(input);
498 : Timestamp result;
499 : int overflow;
500 :
501 36 : result = date2timestamp_opt_overflow(val, &overflow);
502 : /* We can ignore the overflow result, since result is useful as-is */
503 36 : return TimestampGetDatum(result);
504 : }
505 :
506 : static Datum
507 40 : cvt_timestamptz_timestamp(Datum input)
508 : {
509 40 : TimestampTz val = DatumGetTimestampTz(input);
510 : Timestamp result;
511 : int overflow;
512 :
513 40 : result = timestamptz2timestamp_opt_overflow(val, &overflow);
514 : /* We can ignore the overflow result, since result is useful as-is */
515 40 : return TimestampGetDatum(result);
516 : }
517 :
518 : static const bool timestamp_rhs_is_varlena[] =
519 : {false, false, false};
520 :
521 : static const btree_gin_convert_function timestamp_cvt_fns[] =
522 : {NULL, cvt_date_timestamp, cvt_timestamptz_timestamp};
523 :
524 : static const PGFunction timestamp_cmp_fns[] =
525 : {timestamp_cmp, date_cmp_timestamp, timestamptz_cmp_timestamp};
526 :
527 454 : GIN_SUPPORT(timestamp, leftmostvalue_timestamp, timestamp_rhs_is_varlena, timestamp_cvt_fns, timestamp_cmp_fns)
528 :
529 : static Datum
530 12 : cvt_date_timestamptz(Datum input)
531 : {
532 12 : DateADT val = DatumGetDateADT(input);
533 : TimestampTz result;
534 : int overflow;
535 :
536 12 : result = date2timestamptz_opt_overflow(val, &overflow);
537 : /* We can ignore the overflow result, since result is useful as-is */
538 12 : return TimestampTzGetDatum(result);
539 : }
540 :
541 : static Datum
542 12 : cvt_timestamp_timestamptz(Datum input)
543 : {
544 12 : Timestamp val = DatumGetTimestamp(input);
545 : TimestampTz result;
546 : int overflow;
547 :
548 12 : result = timestamp2timestamptz_opt_overflow(val, &overflow);
549 : /* We can ignore the overflow result, since result is useful as-is */
550 12 : return TimestampTzGetDatum(result);
551 : }
552 :
553 : static const bool timestamptz_rhs_is_varlena[] =
554 : {false, false, false};
555 :
556 : static const btree_gin_convert_function timestamptz_cvt_fns[] =
557 : {NULL, cvt_date_timestamptz, cvt_timestamp_timestamptz};
558 :
559 : static const PGFunction timestamptz_cmp_fns[] =
560 : {timestamp_cmp, date_cmp_timestamptz, timestamp_cmp_timestamptz};
561 :
562 182 : GIN_SUPPORT(timestamptz, leftmostvalue_timestamp, timestamptz_rhs_is_varlena, timestamptz_cvt_fns, timestamptz_cmp_fns)
563 :
564 : static Datum
565 8 : leftmostvalue_time(void)
566 : {
567 8 : return TimeADTGetDatum(0);
568 : }
569 :
570 : static const bool time_rhs_is_varlena[] =
571 : {false};
572 :
573 : static const PGFunction time_cmp_fns[] =
574 : {time_cmp};
575 :
576 74 : GIN_SUPPORT(time, leftmostvalue_time, time_rhs_is_varlena, NULL, time_cmp_fns)
577 :
578 : static Datum
579 8 : leftmostvalue_timetz(void)
580 : {
581 8 : TimeTzADT *v = palloc(sizeof(TimeTzADT));
582 :
583 8 : v->time = 0;
584 8 : v->zone = -24 * 3600; /* XXX is that true? */
585 :
586 8 : return TimeTzADTPGetDatum(v);
587 : }
588 :
589 : static const bool timetz_rhs_is_varlena[] =
590 : {false};
591 :
592 : static const PGFunction timetz_cmp_fns[] =
593 : {timetz_cmp};
594 :
595 74 : GIN_SUPPORT(timetz, leftmostvalue_timetz, timetz_rhs_is_varlena, NULL, timetz_cmp_fns)
596 :
597 : static Datum
598 76 : leftmostvalue_date(void)
599 : {
600 76 : return DateADTGetDatum(DATEVAL_NOBEGIN);
601 : }
602 :
603 : static Datum
604 48 : cvt_timestamp_date(Datum input)
605 : {
606 48 : Timestamp val = DatumGetTimestamp(input);
607 : DateADT result;
608 : int overflow;
609 :
610 48 : result = timestamp2date_opt_overflow(val, &overflow);
611 : /* We can ignore the overflow result, since result is useful as-is */
612 48 : return DateADTGetDatum(result);
613 : }
614 :
615 : static Datum
616 48 : cvt_timestamptz_date(Datum input)
617 : {
618 48 : TimestampTz val = DatumGetTimestampTz(input);
619 : DateADT result;
620 : int overflow;
621 :
622 48 : result = timestamptz2date_opt_overflow(val, &overflow);
623 : /* We can ignore the overflow result, since result is useful as-is */
624 48 : return DateADTGetDatum(result);
625 : }
626 :
627 : static const bool date_rhs_is_varlena[] =
628 : {false, false, false};
629 :
630 : static const btree_gin_convert_function date_cvt_fns[] =
631 : {NULL, cvt_timestamp_date, cvt_timestamptz_date};
632 :
633 : static const PGFunction date_cmp_fns[] =
634 : {date_cmp, timestamp_cmp_date, timestamptz_cmp_date};
635 :
636 554 : GIN_SUPPORT(date, leftmostvalue_date, date_rhs_is_varlena, date_cvt_fns, date_cmp_fns)
637 :
638 : static Datum
639 8 : leftmostvalue_interval(void)
640 : {
641 8 : Interval *v = palloc(sizeof(Interval));
642 :
643 8 : INTERVAL_NOBEGIN(v);
644 :
645 8 : return IntervalPGetDatum(v);
646 : }
647 :
648 : static const bool interval_rhs_is_varlena[] =
649 : {false};
650 :
651 : static const PGFunction interval_cmp_fns[] =
652 : {interval_cmp};
653 :
654 86 : GIN_SUPPORT(interval, leftmostvalue_interval, interval_rhs_is_varlena, NULL, interval_cmp_fns)
655 :
656 : static Datum
657 8 : leftmostvalue_macaddr(void)
658 : {
659 8 : macaddr *v = palloc0(sizeof(macaddr));
660 :
661 8 : return MacaddrPGetDatum(v);
662 : }
663 :
664 : static const bool macaddr_rhs_is_varlena[] =
665 : {false};
666 :
667 : static const PGFunction macaddr_cmp_fns[] =
668 : {macaddr_cmp};
669 :
670 74 : GIN_SUPPORT(macaddr, leftmostvalue_macaddr, macaddr_rhs_is_varlena, NULL, macaddr_cmp_fns)
671 :
672 : static Datum
673 8 : leftmostvalue_macaddr8(void)
674 : {
675 8 : macaddr8 *v = palloc0(sizeof(macaddr8));
676 :
677 8 : return Macaddr8PGetDatum(v);
678 : }
679 :
680 : static const bool macaddr8_rhs_is_varlena[] =
681 : {false};
682 :
683 : static const PGFunction macaddr8_cmp_fns[] =
684 : {macaddr8_cmp};
685 :
686 74 : GIN_SUPPORT(macaddr8, leftmostvalue_macaddr8, macaddr8_rhs_is_varlena, NULL, macaddr8_cmp_fns)
687 :
688 : static Datum
689 16 : leftmostvalue_inet(void)
690 : {
691 16 : return DirectFunctionCall1(inet_in, CStringGetDatum("0.0.0.0/0"));
692 : }
693 :
694 : static const bool inet_rhs_is_varlena[] =
695 : {true};
696 :
697 : static const PGFunction inet_cmp_fns[] =
698 : {network_cmp};
699 :
700 74 : GIN_SUPPORT(inet, leftmostvalue_inet, inet_rhs_is_varlena, NULL, inet_cmp_fns)
701 :
702 : static const bool cidr_rhs_is_varlena[] =
703 : {true};
704 :
705 : static const PGFunction cidr_cmp_fns[] =
706 : {network_cmp};
707 :
708 74 : GIN_SUPPORT(cidr, leftmostvalue_inet, cidr_rhs_is_varlena, NULL, cidr_cmp_fns)
709 :
710 : static Datum
711 46 : leftmostvalue_text(void)
712 : {
713 46 : return PointerGetDatum(cstring_to_text_with_len("", 0));
714 : }
715 :
716 : static Datum
717 12 : cvt_name_text(Datum input)
718 : {
719 12 : Name val = DatumGetName(input);
720 :
721 12 : return PointerGetDatum(cstring_to_text(NameStr(*val)));
722 : }
723 :
724 : static const bool text_rhs_is_varlena[] =
725 : {true, false};
726 :
727 : static const btree_gin_convert_function text_cvt_fns[] =
728 : {NULL, cvt_name_text};
729 :
730 : static const PGFunction text_cmp_fns[] =
731 : {bttextcmp, btnametextcmp};
732 :
733 198 : GIN_SUPPORT(text, leftmostvalue_text, text_rhs_is_varlena, text_cvt_fns, text_cmp_fns)
734 :
735 : static const bool bpchar_rhs_is_varlena[] =
736 : {true};
737 :
738 : static const PGFunction bpchar_cmp_fns[] =
739 : {bpcharcmp};
740 :
741 92 : GIN_SUPPORT(bpchar, leftmostvalue_text, bpchar_rhs_is_varlena, NULL, bpchar_cmp_fns)
742 :
743 : static Datum
744 8 : leftmostvalue_char(void)
745 : {
746 8 : return CharGetDatum(0);
747 : }
748 :
749 : static const bool char_rhs_is_varlena[] =
750 : {false};
751 :
752 : static const PGFunction char_cmp_fns[] =
753 : {btcharcmp};
754 :
755 74 : GIN_SUPPORT(char, leftmostvalue_char, char_rhs_is_varlena, NULL, char_cmp_fns)
756 :
757 : static const bool bytea_rhs_is_varlena[] =
758 : {true};
759 :
760 : static const PGFunction bytea_cmp_fns[] =
761 : {byteacmp};
762 :
763 74 : GIN_SUPPORT(bytea, leftmostvalue_text, bytea_rhs_is_varlena, NULL, bytea_cmp_fns)
764 :
765 : static Datum
766 8 : leftmostvalue_bit(void)
767 : {
768 8 : return DirectFunctionCall3(bit_in,
769 : CStringGetDatum(""),
770 : ObjectIdGetDatum(0),
771 : Int32GetDatum(-1));
772 : }
773 :
774 : static const bool bit_rhs_is_varlena[] =
775 : {true};
776 :
777 : static const PGFunction bit_cmp_fns[] =
778 : {bitcmp};
779 :
780 74 : GIN_SUPPORT(bit, leftmostvalue_bit, bit_rhs_is_varlena, NULL, bit_cmp_fns)
781 :
782 : static Datum
783 8 : leftmostvalue_varbit(void)
784 : {
785 8 : return DirectFunctionCall3(varbit_in,
786 : CStringGetDatum(""),
787 : ObjectIdGetDatum(0),
788 : Int32GetDatum(-1));
789 : }
790 :
791 : static const bool varbit_rhs_is_varlena[] =
792 : {true};
793 :
794 : static const PGFunction varbit_cmp_fns[] =
795 : {bitcmp};
796 :
797 74 : GIN_SUPPORT(varbit, leftmostvalue_varbit, varbit_rhs_is_varlena, NULL, varbit_cmp_fns)
798 :
799 : /*
800 : * Numeric type hasn't a real left-most value, so we use PointerGetDatum(NULL)
801 : * (*not* a SQL NULL) to represent that. We can get away with that because
802 : * the value returned by our leftmostvalue function will never be stored in
803 : * the index nor passed to anything except our compare and prefix-comparison
804 : * functions. The same trick could be used for other pass-by-reference types.
805 : */
806 :
807 : #define NUMERIC_IS_LEFTMOST(x) ((x) == NULL)
808 :
809 4 : PG_FUNCTION_INFO_V1(gin_numeric_cmp);
810 :
811 : Datum
812 86 : gin_numeric_cmp(PG_FUNCTION_ARGS)
813 : {
814 86 : Numeric a = (Numeric) PG_GETARG_POINTER(0);
815 86 : Numeric b = (Numeric) PG_GETARG_POINTER(1);
816 86 : int res = 0;
817 :
818 86 : if (NUMERIC_IS_LEFTMOST(a))
819 : {
820 12 : res = (NUMERIC_IS_LEFTMOST(b)) ? 0 : -1;
821 : }
822 74 : else if (NUMERIC_IS_LEFTMOST(b))
823 : {
824 0 : res = 1;
825 : }
826 : else
827 : {
828 74 : res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
829 : NumericGetDatum(a),
830 : NumericGetDatum(b)));
831 : }
832 :
833 86 : PG_RETURN_INT32(res);
834 : }
835 :
836 : static Datum
837 8 : leftmostvalue_numeric(void)
838 : {
839 8 : return PointerGetDatum(NULL);
840 : }
841 :
842 : static const bool numeric_rhs_is_varlena[] =
843 : {true};
844 :
845 : static const PGFunction numeric_cmp_fns[] =
846 : {gin_numeric_cmp};
847 :
848 74 : GIN_SUPPORT(numeric, leftmostvalue_numeric, numeric_rhs_is_varlena, NULL, numeric_cmp_fns)
849 :
850 : /*
851 : * Use a similar trick to that used for numeric for enums, since we don't
852 : * actually know the leftmost value of any enum without knowing the concrete
853 : * type, so we use a dummy leftmost value of InvalidOid.
854 : *
855 : * Note that we use CallerFInfoFunctionCall2 here so that enum_cmp
856 : * gets a valid fn_extra to work with. Unlike most other type comparison
857 : * routines it needs it, so we can't use DirectFunctionCall2.
858 : */
859 :
860 : #define ENUM_IS_LEFTMOST(x) ((x) == InvalidOid)
861 :
862 4 : PG_FUNCTION_INFO_V1(gin_enum_cmp);
863 :
864 : Datum
865 400104 : gin_enum_cmp(PG_FUNCTION_ARGS)
866 : {
867 400104 : Oid a = PG_GETARG_OID(0);
868 400104 : Oid b = PG_GETARG_OID(1);
869 400104 : int res = 0;
870 :
871 400104 : if (ENUM_IS_LEFTMOST(a))
872 : {
873 12 : res = (ENUM_IS_LEFTMOST(b)) ? 0 : -1;
874 : }
875 400092 : else if (ENUM_IS_LEFTMOST(b))
876 : {
877 0 : res = 1;
878 : }
879 : else
880 : {
881 400092 : res = DatumGetInt32(CallerFInfoFunctionCall2(enum_cmp,
882 : fcinfo->flinfo,
883 : PG_GET_COLLATION(),
884 : ObjectIdGetDatum(a),
885 : ObjectIdGetDatum(b)));
886 : }
887 :
888 400104 : PG_RETURN_INT32(res);
889 : }
890 :
891 : static Datum
892 8 : leftmostvalue_enum(void)
893 : {
894 8 : return ObjectIdGetDatum(InvalidOid);
895 : }
896 :
897 : static const bool enum_rhs_is_varlena[] =
898 : {false};
899 :
900 : static const PGFunction enum_cmp_fns[] =
901 : {gin_enum_cmp};
902 :
903 200084 : GIN_SUPPORT(anyenum, leftmostvalue_enum, enum_rhs_is_varlena, NULL, enum_cmp_fns)
904 :
905 : static Datum
906 12 : leftmostvalue_uuid(void)
907 : {
908 : /*
909 : * palloc0 will create the UUID with all zeroes:
910 : * "00000000-0000-0000-0000-000000000000"
911 : */
912 12 : pg_uuid_t *retval = (pg_uuid_t *) palloc0(sizeof(pg_uuid_t));
913 :
914 12 : return UUIDPGetDatum(retval);
915 : }
916 :
917 : static const bool uuid_rhs_is_varlena[] =
918 : {false};
919 :
920 : static const PGFunction uuid_cmp_fns[] =
921 : {uuid_cmp};
922 :
923 84 : GIN_SUPPORT(uuid, leftmostvalue_uuid, uuid_rhs_is_varlena, NULL, uuid_cmp_fns)
924 :
925 : static Datum
926 26 : leftmostvalue_name(void)
927 : {
928 26 : NameData *result = (NameData *) palloc0(NAMEDATALEN);
929 :
930 26 : return NameGetDatum(result);
931 : }
932 :
933 : static Datum
934 12 : cvt_text_name(Datum input)
935 : {
936 12 : text *val = DatumGetTextPP(input);
937 12 : NameData *result = (NameData *) palloc0(NAMEDATALEN);
938 12 : int len = VARSIZE_ANY_EXHDR(val);
939 :
940 : /*
941 : * Truncate oversize input. We're assuming this will produce a result
942 : * considered less than the original. That could be a bad assumption in
943 : * some collations, but fortunately an index on "name" is generally going
944 : * to use C collation.
945 : */
946 12 : if (len >= NAMEDATALEN)
947 0 : len = pg_mbcliplen(VARDATA_ANY(val), len, NAMEDATALEN - 1);
948 :
949 12 : memcpy(NameStr(*result), VARDATA_ANY(val), len);
950 :
951 12 : return NameGetDatum(result);
952 : }
953 :
954 : static const bool name_rhs_is_varlena[] =
955 : {false, true};
956 :
957 : static const btree_gin_convert_function name_cvt_fns[] =
958 : {NULL, cvt_text_name};
959 :
960 : static const PGFunction name_cmp_fns[] =
961 : {btnamecmp, bttextnamecmp};
962 :
963 154 : GIN_SUPPORT(name, leftmostvalue_name, name_rhs_is_varlena, name_cvt_fns, name_cmp_fns)
964 :
965 : static Datum
966 20 : leftmostvalue_bool(void)
967 : {
968 20 : return BoolGetDatum(false);
969 : }
970 :
971 : static const bool bool_rhs_is_varlena[] =
972 : {false};
973 :
974 : static const PGFunction bool_cmp_fns[] =
975 : {btboolcmp};
976 :
977 94 : GIN_SUPPORT(bool, leftmostvalue_bool, bool_rhs_is_varlena, NULL, bool_cmp_fns)
|