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