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 0 : 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 0 : gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
52 : {
53 0 : Datum datum = PG_GETARG_DATUM(0);
54 0 : int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
55 0 : Datum *entries = palloc_object(Datum);
56 :
57 : /* Ensure that values stored in the index are not toasted */
58 0 : if (is_varlena)
59 0 : datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
60 0 : entries[0] = datum;
61 0 : *nentries = 1;
62 :
63 0 : PG_RETURN_POINTER(entries);
64 0 : }
65 :
66 : static Datum
67 0 : 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 0 : Datum datum = PG_GETARG_DATUM(0);
74 0 : int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
75 0 : StrategyNumber strategy = PG_GETARG_UINT16(2);
76 0 : bool **partialmatch = (bool **) PG_GETARG_POINTER(3);
77 0 : Pointer **extra_data = (Pointer **) PG_GETARG_POINTER(4);
78 0 : Datum *entries = palloc_object(Datum);
79 0 : QueryInfo *data = palloc_object(QueryInfo);
80 0 : bool *ptr_partialmatch = palloc_object(bool);
81 0 : 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 0 : btree_strat = BTGIN_GET_BTREE_STRATEGY(strategy);
89 0 : 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 0 : if (rhs_is_varlena[rhs_code])
96 0 : datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
97 :
98 : /* Prep single comparison key with possible partial-match flag */
99 0 : *nentries = 1;
100 0 : *partialmatch = ptr_partialmatch;
101 0 : *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 0 : switch (btree_strat)
114 : {
115 : case BTLessStrategyNumber:
116 : case BTLessEqualStrategyNumber:
117 0 : entries[0] = leftmostvalue();
118 0 : *ptr_partialmatch = true;
119 0 : break;
120 : case BTGreaterEqualStrategyNumber:
121 : case BTGreaterStrategyNumber:
122 0 : *ptr_partialmatch = true;
123 : /* FALLTHROUGH */
124 : case BTEqualStrategyNumber:
125 : /* If we have a conversion function, apply it */
126 0 : if (cvt_fns && cvt_fns[rhs_code])
127 : {
128 0 : entries[0] = (*cvt_fns[rhs_code]) (datum);
129 0 : *ptr_partialmatch = true;
130 0 : }
131 : else
132 0 : entries[0] = datum;
133 0 : break;
134 : default:
135 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
136 0 : }
137 :
138 : /* Fill "extra" data */
139 0 : data->strategy = strategy;
140 0 : data->orig_datum = datum;
141 0 : data->entry_datum = entries[0];
142 0 : data->typecmp = cmp_fns[rhs_code];
143 0 : *extra_data = palloc_object(Pointer);
144 0 : **extra_data = (Pointer) data;
145 :
146 0 : PG_RETURN_POINTER(entries);
147 0 : }
148 :
149 : static Datum
150 0 : gin_btree_compare_prefix(FunctionCallInfo fcinfo)
151 : {
152 0 : Datum partial_key PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(0);
153 0 : Datum key = PG_GETARG_DATUM(1);
154 0 : QueryInfo *data = (QueryInfo *) PG_GETARG_POINTER(3);
155 0 : 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 0 : Assert(partial_key == data->entry_datum);
171 :
172 0 : cmp = DatumGetInt32(CallerFInfoFunctionCall2(data->typecmp,
173 0 : fcinfo->flinfo,
174 0 : PG_GET_COLLATION(),
175 0 : data->orig_datum,
176 0 : 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 0 : switch (BTGIN_GET_BTREE_STRATEGY(data->strategy))
189 : {
190 : case BTLessStrategyNumber:
191 : /* If original datum > indexed one then return match */
192 0 : if (cmp > 0)
193 0 : res = 0;
194 : else
195 0 : res = 1; /* end scan */
196 0 : break;
197 : case BTLessEqualStrategyNumber:
198 : /* If original datum >= indexed one then return match */
199 0 : if (cmp >= 0)
200 0 : res = 0;
201 : else
202 0 : res = 1; /* end scan */
203 0 : break;
204 : case BTEqualStrategyNumber:
205 : /* If original datum = indexed one then return match */
206 : /* See above about why we can end scan when cmp < 0 */
207 0 : if (cmp == 0)
208 0 : res = 0;
209 : else
210 0 : res = 1; /* end scan */
211 0 : break;
212 : case BTGreaterEqualStrategyNumber:
213 : /* If original datum <= indexed one then return match */
214 0 : if (cmp <= 0)
215 0 : res = 0;
216 : else
217 0 : res = -1; /* keep scanning */
218 0 : break;
219 : case BTGreaterStrategyNumber:
220 : /* If original datum < indexed one then return match */
221 0 : if (cmp < 0)
222 0 : res = 0;
223 : else
224 0 : res = -1; /* keep scanning */
225 0 : break;
226 : default:
227 0 : elog(ERROR, "unrecognized strategy number: %d",
228 : data->strategy);
229 0 : res = 0;
230 0 : }
231 :
232 0 : PG_RETURN_INT32(res);
233 0 : }
234 :
235 0 : PG_FUNCTION_INFO_V1(gin_btree_consistent);
236 : Datum
237 0 : gin_btree_consistent(PG_FUNCTION_ARGS)
238 : {
239 0 : bool *recheck = (bool *) PG_GETARG_POINTER(5);
240 :
241 0 : *recheck = false;
242 0 : PG_RETURN_BOOL(true);
243 0 : }
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 0 : leftmostvalue_int2(void)
275 : {
276 0 : 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 0 : cvt_int4_int2(Datum input)
297 : {
298 0 : int32 val = DatumGetInt32(input);
299 :
300 0 : val = Max(val, SHRT_MIN);
301 0 : val = Min(val, SHRT_MAX);
302 0 : return Int16GetDatum((int16) val);
303 0 : }
304 :
305 : static Datum
306 0 : cvt_int8_int2(Datum input)
307 : {
308 0 : int64 val = DatumGetInt64(input);
309 :
310 0 : val = Max(val, SHRT_MIN);
311 0 : val = Min(val, SHRT_MAX);
312 0 : return Int16GetDatum((int16) val);
313 0 : }
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 0 : GIN_SUPPORT(int2, leftmostvalue_int2, int2_rhs_is_varlena, int2_cvt_fns, int2_cmp_fns)
332 :
333 : static Datum
334 0 : leftmostvalue_int4(void)
335 : {
336 0 : return Int32GetDatum(INT_MIN);
337 : }
338 :
339 : static Datum
340 0 : cvt_int2_int4(Datum input)
341 : {
342 0 : int16 val = DatumGetInt16(input);
343 :
344 0 : return Int32GetDatum((int32) val);
345 0 : }
346 :
347 : static Datum
348 0 : cvt_int8_int4(Datum input)
349 : {
350 0 : int64 val = DatumGetInt64(input);
351 :
352 0 : val = Max(val, INT_MIN);
353 0 : val = Min(val, INT_MAX);
354 0 : return Int32GetDatum((int32) val);
355 0 : }
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 0 : GIN_SUPPORT(int4, leftmostvalue_int4, int4_rhs_is_varlena, int4_cvt_fns, int4_cmp_fns)
367 :
368 : static Datum
369 0 : leftmostvalue_int8(void)
370 : {
371 0 : return Int64GetDatum(PG_INT64_MIN);
372 : }
373 :
374 : static Datum
375 0 : cvt_int2_int8(Datum input)
376 : {
377 0 : int16 val = DatumGetInt16(input);
378 :
379 0 : return Int64GetDatum((int64) val);
380 0 : }
381 :
382 : static Datum
383 0 : cvt_int4_int8(Datum input)
384 : {
385 0 : int32 val = DatumGetInt32(input);
386 :
387 0 : return Int64GetDatum((int64) val);
388 0 : }
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 0 : GIN_SUPPORT(int8, leftmostvalue_int8, int8_rhs_is_varlena, int8_cvt_fns, int8_cmp_fns)
400 :
401 : static Datum
402 0 : leftmostvalue_float4(void)
403 : {
404 0 : return Float4GetDatum(-get_float4_infinity());
405 : }
406 :
407 : static Datum
408 0 : cvt_float8_float4(Datum input)
409 : {
410 0 : float8 val = DatumGetFloat8(input);
411 0 : 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 0 : result = (float4) val;
422 0 : return Float4GetDatum(result);
423 0 : }
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 0 : GIN_SUPPORT(float4, leftmostvalue_float4, float4_rhs_is_varlena, float4_cvt_fns, float4_cmp_fns)
435 :
436 : static Datum
437 0 : leftmostvalue_float8(void)
438 : {
439 0 : return Float8GetDatum(-get_float8_infinity());
440 : }
441 :
442 : static Datum
443 0 : cvt_float4_float8(Datum input)
444 : {
445 0 : float4 val = DatumGetFloat4(input);
446 :
447 0 : return Float8GetDatum((float8) val);
448 0 : }
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 0 : GIN_SUPPORT(float8, leftmostvalue_float8, float8_rhs_is_varlena, float8_cvt_fns, float8_cmp_fns)
460 :
461 : static Datum
462 0 : leftmostvalue_money(void)
463 : {
464 0 : 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 0 : GIN_SUPPORT(money, leftmostvalue_money, money_rhs_is_varlena, NULL, money_cmp_fns)
474 :
475 : static Datum
476 0 : leftmostvalue_oid(void)
477 : {
478 0 : 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 0 : GIN_SUPPORT(oid, leftmostvalue_oid, oid_rhs_is_varlena, NULL, oid_cmp_fns)
488 :
489 : static Datum
490 0 : leftmostvalue_timestamp(void)
491 : {
492 0 : return TimestampGetDatum(DT_NOBEGIN);
493 : }
494 :
495 : static Datum
496 0 : cvt_date_timestamp(Datum input)
497 : {
498 0 : DateADT val = DatumGetDateADT(input);
499 0 : Timestamp result;
500 0 : ErrorSaveContext escontext = {T_ErrorSaveContext};
501 :
502 0 : result = date2timestamp_safe(val, (Node *) &escontext);
503 : /* We can ignore errors, since result is useful as-is */
504 0 : return TimestampGetDatum(result);
505 0 : }
506 :
507 : static Datum
508 0 : cvt_timestamptz_timestamp(Datum input)
509 : {
510 0 : TimestampTz val = DatumGetTimestampTz(input);
511 0 : ErrorSaveContext escontext = {T_ErrorSaveContext};
512 0 : Timestamp result;
513 :
514 0 : result = timestamptz2timestamp_safe(val, (Node *) &escontext);
515 : /* We can ignore errors, since result is useful as-is */
516 0 : return TimestampGetDatum(result);
517 0 : }
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 0 : GIN_SUPPORT(timestamp, leftmostvalue_timestamp, timestamp_rhs_is_varlena, timestamp_cvt_fns, timestamp_cmp_fns)
529 :
530 : static Datum
531 0 : cvt_date_timestamptz(Datum input)
532 : {
533 0 : DateADT val = DatumGetDateADT(input);
534 0 : ErrorSaveContext escontext = {T_ErrorSaveContext};
535 0 : TimestampTz result;
536 :
537 0 : result = date2timestamptz_safe(val, (Node *) &escontext);
538 : /* We can ignore errors, since result is useful as-is */
539 0 : return TimestampTzGetDatum(result);
540 0 : }
541 :
542 : static Datum
543 0 : cvt_timestamp_timestamptz(Datum input)
544 : {
545 0 : Timestamp val = DatumGetTimestamp(input);
546 0 : ErrorSaveContext escontext = {T_ErrorSaveContext};
547 0 : TimestampTz result;
548 :
549 0 : result = timestamp2timestamptz_safe(val, (Node *) &escontext);
550 : /* We can ignore errors, since result is useful as-is */
551 0 : return TimestampTzGetDatum(result);
552 0 : }
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 0 : GIN_SUPPORT(timestamptz, leftmostvalue_timestamp, timestamptz_rhs_is_varlena, timestamptz_cvt_fns, timestamptz_cmp_fns)
564 :
565 : static Datum
566 0 : leftmostvalue_time(void)
567 : {
568 0 : 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 0 : GIN_SUPPORT(time, leftmostvalue_time, time_rhs_is_varlena, NULL, time_cmp_fns)
578 :
579 : static Datum
580 0 : leftmostvalue_timetz(void)
581 : {
582 0 : TimeTzADT *v = palloc_object(TimeTzADT);
583 :
584 0 : v->time = 0;
585 0 : v->zone = -24 * 3600; /* XXX is that true? */
586 :
587 0 : return TimeTzADTPGetDatum(v);
588 0 : }
589 :
590 : static const bool timetz_rhs_is_varlena[] =
591 : {false};
592 :
593 : static const PGFunction timetz_cmp_fns[] =
594 : {timetz_cmp};
595 :
596 0 : GIN_SUPPORT(timetz, leftmostvalue_timetz, timetz_rhs_is_varlena, NULL, timetz_cmp_fns)
597 :
598 : static Datum
599 0 : leftmostvalue_date(void)
600 : {
601 0 : return DateADTGetDatum(DATEVAL_NOBEGIN);
602 : }
603 :
604 : static Datum
605 0 : cvt_timestamp_date(Datum input)
606 : {
607 0 : Timestamp val = DatumGetTimestamp(input);
608 0 : ErrorSaveContext escontext = {T_ErrorSaveContext};
609 0 : DateADT result;
610 :
611 0 : result = timestamp2date_safe(val, (Node *) &escontext);
612 : /* We can ignore errors, since result is useful as-is */
613 0 : return DateADTGetDatum(result);
614 0 : }
615 :
616 : static Datum
617 0 : cvt_timestamptz_date(Datum input)
618 : {
619 0 : TimestampTz val = DatumGetTimestampTz(input);
620 0 : ErrorSaveContext escontext = {T_ErrorSaveContext};
621 0 : DateADT result;
622 :
623 0 : result = timestamptz2date_safe(val, (Node *) &escontext);
624 : /* We can ignore errors, since result is useful as-is */
625 0 : return DateADTGetDatum(result);
626 0 : }
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 0 : GIN_SUPPORT(date, leftmostvalue_date, date_rhs_is_varlena, date_cvt_fns, date_cmp_fns)
638 :
639 : static Datum
640 0 : leftmostvalue_interval(void)
641 : {
642 0 : Interval *v = palloc_object(Interval);
643 :
644 0 : INTERVAL_NOBEGIN(v);
645 :
646 0 : return IntervalPGetDatum(v);
647 0 : }
648 :
649 : static const bool interval_rhs_is_varlena[] =
650 : {false};
651 :
652 : static const PGFunction interval_cmp_fns[] =
653 : {interval_cmp};
654 :
655 0 : GIN_SUPPORT(interval, leftmostvalue_interval, interval_rhs_is_varlena, NULL, interval_cmp_fns)
656 :
657 : static Datum
658 0 : leftmostvalue_macaddr(void)
659 : {
660 0 : macaddr *v = palloc0_object(macaddr);
661 :
662 0 : return MacaddrPGetDatum(v);
663 0 : }
664 :
665 : static const bool macaddr_rhs_is_varlena[] =
666 : {false};
667 :
668 : static const PGFunction macaddr_cmp_fns[] =
669 : {macaddr_cmp};
670 :
671 0 : GIN_SUPPORT(macaddr, leftmostvalue_macaddr, macaddr_rhs_is_varlena, NULL, macaddr_cmp_fns)
672 :
673 : static Datum
674 0 : leftmostvalue_macaddr8(void)
675 : {
676 0 : macaddr8 *v = palloc0_object(macaddr8);
677 :
678 0 : return Macaddr8PGetDatum(v);
679 0 : }
680 :
681 : static const bool macaddr8_rhs_is_varlena[] =
682 : {false};
683 :
684 : static const PGFunction macaddr8_cmp_fns[] =
685 : {macaddr8_cmp};
686 :
687 0 : GIN_SUPPORT(macaddr8, leftmostvalue_macaddr8, macaddr8_rhs_is_varlena, NULL, macaddr8_cmp_fns)
688 :
689 : static Datum
690 0 : leftmostvalue_inet(void)
691 : {
692 0 : 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 0 : 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 0 : GIN_SUPPORT(cidr, leftmostvalue_inet, cidr_rhs_is_varlena, NULL, cidr_cmp_fns)
710 :
711 : static Datum
712 0 : leftmostvalue_text(void)
713 : {
714 0 : return PointerGetDatum(cstring_to_text_with_len("", 0));
715 : }
716 :
717 : static Datum
718 0 : cvt_name_text(Datum input)
719 : {
720 0 : Name val = DatumGetName(input);
721 :
722 0 : return PointerGetDatum(cstring_to_text(NameStr(*val)));
723 0 : }
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 0 : 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 0 : GIN_SUPPORT(bpchar, leftmostvalue_text, bpchar_rhs_is_varlena, NULL, bpchar_cmp_fns)
743 :
744 : static Datum
745 0 : leftmostvalue_char(void)
746 : {
747 0 : 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 0 : 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 0 : GIN_SUPPORT(bytea, leftmostvalue_text, bytea_rhs_is_varlena, NULL, bytea_cmp_fns)
765 :
766 : static Datum
767 0 : leftmostvalue_bit(void)
768 : {
769 0 : 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 0 : GIN_SUPPORT(bit, leftmostvalue_bit, bit_rhs_is_varlena, NULL, bit_cmp_fns)
782 :
783 : static Datum
784 0 : leftmostvalue_varbit(void)
785 : {
786 0 : 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 0 : 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 0 : PG_FUNCTION_INFO_V1(gin_numeric_cmp);
811 :
812 : Datum
813 0 : gin_numeric_cmp(PG_FUNCTION_ARGS)
814 : {
815 0 : Numeric a = (Numeric) PG_GETARG_POINTER(0);
816 0 : Numeric b = (Numeric) PG_GETARG_POINTER(1);
817 0 : int res = 0;
818 :
819 0 : if (NUMERIC_IS_LEFTMOST(a))
820 : {
821 0 : res = (NUMERIC_IS_LEFTMOST(b)) ? 0 : -1;
822 0 : }
823 0 : else if (NUMERIC_IS_LEFTMOST(b))
824 : {
825 0 : res = 1;
826 0 : }
827 : else
828 : {
829 0 : res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
830 : NumericGetDatum(a),
831 : NumericGetDatum(b)));
832 : }
833 :
834 0 : PG_RETURN_INT32(res);
835 0 : }
836 :
837 : static Datum
838 0 : leftmostvalue_numeric(void)
839 : {
840 0 : 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 0 : 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 0 : PG_FUNCTION_INFO_V1(gin_enum_cmp);
864 :
865 : Datum
866 0 : gin_enum_cmp(PG_FUNCTION_ARGS)
867 : {
868 0 : Oid a = PG_GETARG_OID(0);
869 0 : Oid b = PG_GETARG_OID(1);
870 0 : int res = 0;
871 :
872 0 : if (ENUM_IS_LEFTMOST(a))
873 : {
874 0 : res = (ENUM_IS_LEFTMOST(b)) ? 0 : -1;
875 0 : }
876 0 : else if (ENUM_IS_LEFTMOST(b))
877 : {
878 0 : res = 1;
879 0 : }
880 : else
881 : {
882 0 : res = DatumGetInt32(CallerFInfoFunctionCall2(enum_cmp,
883 0 : fcinfo->flinfo,
884 0 : PG_GET_COLLATION(),
885 0 : ObjectIdGetDatum(a),
886 0 : ObjectIdGetDatum(b)));
887 : }
888 :
889 0 : PG_RETURN_INT32(res);
890 0 : }
891 :
892 : static Datum
893 0 : leftmostvalue_enum(void)
894 : {
895 0 : 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 0 : GIN_SUPPORT(anyenum, leftmostvalue_enum, enum_rhs_is_varlena, NULL, enum_cmp_fns)
905 :
906 : static Datum
907 0 : leftmostvalue_uuid(void)
908 : {
909 : /*
910 : * palloc0 will create the UUID with all zeroes:
911 : * "00000000-0000-0000-0000-000000000000"
912 : */
913 0 : pg_uuid_t *retval = palloc0_object(pg_uuid_t);
914 :
915 0 : return UUIDPGetDatum(retval);
916 0 : }
917 :
918 : static const bool uuid_rhs_is_varlena[] =
919 : {false};
920 :
921 : static const PGFunction uuid_cmp_fns[] =
922 : {uuid_cmp};
923 :
924 0 : GIN_SUPPORT(uuid, leftmostvalue_uuid, uuid_rhs_is_varlena, NULL, uuid_cmp_fns)
925 :
926 : static Datum
927 0 : leftmostvalue_name(void)
928 : {
929 0 : NameData *result = (NameData *) palloc0(NAMEDATALEN);
930 :
931 0 : return NameGetDatum(result);
932 0 : }
933 :
934 : static Datum
935 0 : cvt_text_name(Datum input)
936 : {
937 0 : text *val = DatumGetTextPP(input);
938 0 : NameData *result = (NameData *) palloc0(NAMEDATALEN);
939 0 : 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 0 : if (len >= NAMEDATALEN)
948 0 : len = pg_mbcliplen(VARDATA_ANY(val), len, NAMEDATALEN - 1);
949 :
950 0 : memcpy(NameStr(*result), VARDATA_ANY(val), len);
951 :
952 0 : return NameGetDatum(result);
953 0 : }
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 0 : GIN_SUPPORT(name, leftmostvalue_name, name_rhs_is_varlena, name_cvt_fns, name_cmp_fns)
965 :
966 : static Datum
967 0 : leftmostvalue_bool(void)
968 : {
969 0 : 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 0 : GIN_SUPPORT(bool, leftmostvalue_bool, bool_rhs_is_varlena, NULL, bool_cmp_fns)
|