Branch data 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 : 31 : 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 : 100690 : gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
52 : : {
53 : 100690 : Datum datum = PG_GETARG_DATUM(0);
54 : 100690 : int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
55 : 100690 : Datum *entries = palloc_object(Datum);
56 : :
57 : : /* Ensure that values stored in the index are not toasted */
58 [ + + ]: 100690 : if (is_varlena)
59 : 56 : datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
60 : 100690 : entries[0] = datum;
61 : 100690 : *nentries = 1;
62 : :
63 : 100690 : PG_RETURN_POINTER(entries);
64 : : }
65 : :
66 : : static Datum
67 : 694 : 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 : 694 : Datum datum = PG_GETARG_DATUM(0);
74 : 694 : int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
75 : 694 : StrategyNumber strategy = PG_GETARG_UINT16(2);
76 : 694 : bool **partialmatch = (bool **) PG_GETARG_POINTER(3);
77 : 694 : Pointer **extra_data = (Pointer **) PG_GETARG_POINTER(4);
78 : 694 : Datum *entries = palloc_object(Datum);
79 : 694 : QueryInfo *data = palloc_object(QueryInfo);
80 : 694 : 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 : 694 : btree_strat = BTGIN_GET_BTREE_STRATEGY(strategy);
89 : 694 : 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 [ + + ]: 694 : if (rhs_is_varlena[rhs_code])
96 : 110 : datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
97 : :
98 : : /* Prep single comparison key with possible partial-match flag */
99 : 694 : *nentries = 1;
100 : 694 : *partialmatch = ptr_partialmatch;
101 : 694 : *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 [ + + + - ]: 694 : switch (btree_strat)
114 : : {
115 : 284 : case BTLessStrategyNumber:
116 : : case BTLessEqualStrategyNumber:
117 : 284 : entries[0] = leftmostvalue();
118 : 284 : *ptr_partialmatch = true;
119 : 284 : break;
120 : 271 : case BTGreaterEqualStrategyNumber:
121 : : case BTGreaterStrategyNumber:
122 : 271 : *ptr_partialmatch = true;
123 : : pg_fallthrough;
124 : 410 : case BTEqualStrategyNumber:
125 : : /* If we have a conversion function, apply it */
126 [ + + + + ]: 410 : if (cvt_fns && cvt_fns[rhs_code])
127 : : {
128 : 210 : entries[0] = (*cvt_fns[rhs_code]) (datum);
129 : 210 : *ptr_partialmatch = true;
130 : : }
131 : : else
132 : 200 : entries[0] = datum;
133 : 410 : break;
134 : 0 : default:
135 [ # # ]: 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
136 : : }
137 : :
138 : : /* Fill "extra" data */
139 : 694 : data->strategy = strategy;
140 : 694 : data->orig_datum = datum;
141 : 694 : data->entry_datum = entries[0];
142 : 694 : data->typecmp = cmp_fns[rhs_code];
143 : 694 : *extra_data = palloc_object(Pointer);
144 : 694 : **extra_data = (Pointer) data;
145 : :
146 : 694 : PG_RETURN_POINTER(entries);
147 : : }
148 : :
149 : : static Datum
150 : 1106 : gin_btree_compare_prefix(FunctionCallInfo fcinfo)
151 : : {
152 : 1106 : Datum partial_key PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(0);
153 : 1106 : Datum key = PG_GETARG_DATUM(1);
154 : 1106 : 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 : 1106 : 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 [ + + + + : 1106 : switch (BTGIN_GET_BTREE_STRATEGY(data->strategy))
+ - ]
189 : : {
190 : 272 : case BTLessStrategyNumber:
191 : : /* If original datum > indexed one then return match */
192 [ + + ]: 272 : if (cmp > 0)
193 : 209 : res = 0;
194 : : else
195 : 63 : res = 1; /* end scan */
196 : 272 : break;
197 : 328 : case BTLessEqualStrategyNumber:
198 : : /* If original datum >= indexed one then return match */
199 [ + + ]: 328 : if (cmp >= 0)
200 : 269 : res = 0;
201 : : else
202 : 59 : res = 1; /* end scan */
203 : 328 : break;
204 : 54 : case BTEqualStrategyNumber:
205 : : /* If original datum = indexed one then return match */
206 : : /* See above about why we can end scan when cmp < 0 */
207 [ + + ]: 54 : if (cmp == 0)
208 : 25 : res = 0;
209 : : else
210 : 29 : res = 1; /* end scan */
211 : 54 : break;
212 : 222 : case BTGreaterEqualStrategyNumber:
213 : : /* If original datum <= indexed one then return match */
214 [ + + ]: 222 : if (cmp <= 0)
215 : 217 : res = 0;
216 : : else
217 : 5 : res = -1; /* keep scanning */
218 : 222 : break;
219 : 230 : case BTGreaterStrategyNumber:
220 : : /* If original datum < indexed one then return match */
221 [ + + ]: 230 : if (cmp < 0)
222 : 169 : res = 0;
223 : : else
224 : 61 : res = -1; /* keep scanning */
225 : 230 : break;
226 : 0 : default:
227 [ # # ]: 0 : elog(ERROR, "unrecognized strategy number: %d",
228 : : data->strategy);
229 : : res = 0;
230 : : }
231 : :
232 : 1106 : PG_RETURN_INT32(res);
233 : : }
234 : :
235 : 32 : PG_FUNCTION_INFO_V1(gin_btree_consistent);
236 : : Datum
237 : 928 : gin_btree_consistent(PG_FUNCTION_ARGS)
238 : : {
239 : 928 : bool *recheck = (bool *) PG_GETARG_POINTER(5);
240 : :
241 : 928 : *recheck = false;
242 : 928 : 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 : 22 : leftmostvalue_int2(void)
275 : : {
276 : 22 : 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 : 18 : cvt_int4_int2(Datum input)
297 : : {
298 : 18 : int32 val = DatumGetInt32(input);
299 : :
300 : 18 : val = Max(val, SHRT_MIN);
301 : 18 : val = Min(val, SHRT_MAX);
302 : 18 : return Int16GetDatum((int16) val);
303 : : }
304 : :
305 : : static Datum
306 : 6 : cvt_int8_int2(Datum input)
307 : : {
308 : 6 : int64 val = DatumGetInt64(input);
309 : :
310 : 6 : val = Max(val, SHRT_MIN);
311 : 6 : val = Min(val, SHRT_MAX);
312 : 6 : 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 : 156 : GIN_SUPPORT(int2, leftmostvalue_int2, int2_rhs_is_varlena, int2_cvt_fns, int2_cmp_fns)
332 : :
333 : : static Datum
334 : 14 : leftmostvalue_int4(void)
335 : : {
336 : 14 : return Int32GetDatum(INT_MIN);
337 : : }
338 : :
339 : : static Datum
340 : 6 : cvt_int2_int4(Datum input)
341 : : {
342 : 6 : int16 val = DatumGetInt16(input);
343 : :
344 : 6 : return Int32GetDatum((int32) val);
345 : : }
346 : :
347 : : static Datum
348 : 6 : cvt_int8_int4(Datum input)
349 : : {
350 : 6 : int64 val = DatumGetInt64(input);
351 : :
352 : 6 : val = Max(val, INT_MIN);
353 : 6 : val = Min(val, INT_MAX);
354 : 6 : 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 : 603 : GIN_SUPPORT(int4, leftmostvalue_int4, int4_rhs_is_varlena, int4_cvt_fns, int4_cmp_fns)
367 : :
368 : : static Datum
369 : 14 : leftmostvalue_int8(void)
370 : : {
371 : 14 : return Int64GetDatum(PG_INT64_MIN);
372 : : }
373 : :
374 : : static Datum
375 : 6 : cvt_int2_int8(Datum input)
376 : : {
377 : 6 : int16 val = DatumGetInt16(input);
378 : :
379 : 6 : return Int64GetDatum((int64) val);
380 : : }
381 : :
382 : : static Datum
383 : 6 : cvt_int4_int8(Datum input)
384 : : {
385 : 6 : int32 val = DatumGetInt32(input);
386 : :
387 : 6 : 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 : 96 : GIN_SUPPORT(int8, leftmostvalue_int8, int8_rhs_is_varlena, int8_cvt_fns, int8_cmp_fns)
400 : :
401 : : static Datum
402 : 33 : leftmostvalue_float4(void)
403 : : {
404 : 33 : return Float4GetDatum(-get_float4_infinity());
405 : : }
406 : :
407 : : static Datum
408 : 42 : cvt_float8_float4(Datum input)
409 : : {
410 : 42 : 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 : 42 : result = (float4) val;
422 : 42 : 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 : 263 : GIN_SUPPORT(float4, leftmostvalue_float4, float4_rhs_is_varlena, float4_cvt_fns, float4_cmp_fns)
435 : :
436 : : static Datum
437 : 9 : leftmostvalue_float8(void)
438 : : {
439 : 9 : return Float8GetDatum(-get_float8_infinity());
440 : : }
441 : :
442 : : static Datum
443 : 6 : cvt_float4_float8(Datum input)
444 : : {
445 : 6 : float4 val = DatumGetFloat4(input);
446 : :
447 : 6 : 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 : 68 : GIN_SUPPORT(float8, leftmostvalue_float8, float8_rhs_is_varlena, float8_cvt_fns, float8_cmp_fns)
460 : :
461 : : static Datum
462 : 4 : leftmostvalue_money(void)
463 : : {
464 : 4 : 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 : 40 : GIN_SUPPORT(money, leftmostvalue_money, money_rhs_is_varlena, NULL, money_cmp_fns)
474 : :
475 : : static Datum
476 : 4 : leftmostvalue_oid(void)
477 : : {
478 : 4 : 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 : 40 : GIN_SUPPORT(oid, leftmostvalue_oid, oid_rhs_is_varlena, NULL, oid_cmp_fns)
488 : :
489 : : static Datum
490 : 46 : leftmostvalue_timestamp(void)
491 : : {
492 : 46 : return TimestampGetDatum(DT_NOBEGIN);
493 : : }
494 : :
495 : : static Datum
496 : 18 : cvt_date_timestamp(Datum input)
497 : : {
498 : 18 : DateADT val = DatumGetDateADT(input);
499 : : Timestamp result;
500 : 18 : ErrorSaveContext escontext = {T_ErrorSaveContext};
501 : :
502 : 18 : result = date2timestamp_safe(val, (Node *) &escontext);
503 : : /* We can ignore errors, since result is useful as-is */
504 : 18 : return TimestampGetDatum(result);
505 : : }
506 : :
507 : : static Datum
508 : 24 : cvt_timestamptz_timestamp(Datum input)
509 : : {
510 : 24 : TimestampTz val = DatumGetTimestampTz(input);
511 : 24 : ErrorSaveContext escontext = {T_ErrorSaveContext};
512 : : Timestamp result;
513 : :
514 : 24 : result = timestamptz2timestamp_safe(val, (Node *) &escontext);
515 : : /* We can ignore errors, since result is useful as-is */
516 : :
517 : : /*
518 : : * It is critical that result <= val, else the index scan can begin after
519 : : * potentially-matching index entries. There are two known ways that this
520 : : * might fail to be true:
521 : : *
522 : : * 1. In the to-be-repeated hour of a fall-back DST transition, the
523 : : * ambiguous converted timestamp will be interpreted by <= as the later
524 : : * (standard-time) timestamptz, thus comparing later than the given val.
525 : : *
526 : : * 2. timestamptz2timestamp_safe will return +Infinity if the input is
527 : : * close to the range limit of timestamptz and its rotated value would
528 : : * exceed the limit of timestamp. (In itself this might not cause any
529 : : * failures, but if #1 also applies then it possibly could. In any case
530 : : * we don't want to be repeatedly subtracting an hour from +Infinity.)
531 : : *
532 : : * To fix both things, first replace +Infinity with the maximum finite
533 : : * timestamp, and then back up an hour at a time until we have a result
534 : : * that compares <= val. (It won't hurt if we end with a result that is
535 : : * slightly less than the nominal lower limit of timestamp.) This may
536 : : * result in scanning more index entries than we really need to, but we
537 : : * won't miss any we need.
538 : : */
539 [ + + ]: 24 : if (TIMESTAMP_IS_NOEND(result))
540 : 6 : result = END_TIMESTAMP;
541 [ + + ]: 28 : while (timestamp_cmp_timestamptz_internal(result, val) > 0)
542 : 4 : result -= USECS_PER_HOUR;
543 : :
544 : 24 : return TimestampGetDatum(result);
545 : : }
546 : :
547 : : static const bool timestamp_rhs_is_varlena[] =
548 : : {false, false, false};
549 : :
550 : : static const btree_gin_convert_function timestamp_cvt_fns[] =
551 : : {NULL, cvt_date_timestamp, cvt_timestamptz_timestamp};
552 : :
553 : : static const PGFunction timestamp_cmp_fns[] =
554 : : {timestamp_cmp, date_cmp_timestamp, timestamptz_cmp_timestamp};
555 : :
556 : 251 : GIN_SUPPORT(timestamp, leftmostvalue_timestamp, timestamp_rhs_is_varlena, timestamp_cvt_fns, timestamp_cmp_fns)
557 : :
558 : : static Datum
559 : 6 : cvt_date_timestamptz(Datum input)
560 : : {
561 : 6 : DateADT val = DatumGetDateADT(input);
562 : 6 : ErrorSaveContext escontext = {T_ErrorSaveContext};
563 : : TimestampTz result;
564 : :
565 : 6 : result = date2timestamptz_safe(val, (Node *) &escontext);
566 : : /* We can ignore errors, since result is useful as-is */
567 : 6 : return TimestampTzGetDatum(result);
568 : : }
569 : :
570 : : static Datum
571 : 6 : cvt_timestamp_timestamptz(Datum input)
572 : : {
573 : 6 : Timestamp val = DatumGetTimestamp(input);
574 : 6 : ErrorSaveContext escontext = {T_ErrorSaveContext};
575 : : TimestampTz result;
576 : :
577 : 6 : result = timestamp2timestamptz_safe(val, (Node *) &escontext);
578 : : /* We can ignore errors, since result is useful as-is */
579 : 6 : return TimestampTzGetDatum(result);
580 : : }
581 : :
582 : : static const bool timestamptz_rhs_is_varlena[] =
583 : : {false, false, false};
584 : :
585 : : static const btree_gin_convert_function timestamptz_cvt_fns[] =
586 : : {NULL, cvt_date_timestamptz, cvt_timestamp_timestamptz};
587 : :
588 : : static const PGFunction timestamptz_cmp_fns[] =
589 : : {timestamp_cmp, date_cmp_timestamptz, timestamp_cmp_timestamptz};
590 : :
591 : 94 : GIN_SUPPORT(timestamptz, leftmostvalue_timestamp, timestamptz_rhs_is_varlena, timestamptz_cvt_fns, timestamptz_cmp_fns)
592 : :
593 : : static Datum
594 : 4 : leftmostvalue_time(void)
595 : : {
596 : 4 : return TimeADTGetDatum(0);
597 : : }
598 : :
599 : : static const bool time_rhs_is_varlena[] =
600 : : {false};
601 : :
602 : : static const PGFunction time_cmp_fns[] =
603 : : {time_cmp};
604 : :
605 : 40 : GIN_SUPPORT(time, leftmostvalue_time, time_rhs_is_varlena, NULL, time_cmp_fns)
606 : :
607 : : static Datum
608 : 4 : leftmostvalue_timetz(void)
609 : : {
610 : 4 : TimeTzADT *v = palloc_object(TimeTzADT);
611 : :
612 : 4 : v->time = 0;
613 : 4 : v->zone = -24 * 3600; /* XXX is that true? */
614 : :
615 : 4 : return TimeTzADTPGetDatum(v);
616 : : }
617 : :
618 : : static const bool timetz_rhs_is_varlena[] =
619 : : {false};
620 : :
621 : : static const PGFunction timetz_cmp_fns[] =
622 : : {timetz_cmp};
623 : :
624 : 40 : GIN_SUPPORT(timetz, leftmostvalue_timetz, timetz_rhs_is_varlena, NULL, timetz_cmp_fns)
625 : :
626 : : static Datum
627 : 38 : leftmostvalue_date(void)
628 : : {
629 : 38 : return DateADTGetDatum(DATEVAL_NOBEGIN);
630 : : }
631 : :
632 : : static Datum
633 : 24 : cvt_timestamp_date(Datum input)
634 : : {
635 : 24 : Timestamp val = DatumGetTimestamp(input);
636 : 24 : ErrorSaveContext escontext = {T_ErrorSaveContext};
637 : : DateADT result;
638 : :
639 : 24 : result = timestamp2date_safe(val, (Node *) &escontext);
640 : : /* We can ignore errors, since result is useful as-is */
641 : 24 : return DateADTGetDatum(result);
642 : : }
643 : :
644 : : static Datum
645 : 24 : cvt_timestamptz_date(Datum input)
646 : : {
647 : 24 : TimestampTz val = DatumGetTimestampTz(input);
648 : 24 : ErrorSaveContext escontext = {T_ErrorSaveContext};
649 : : DateADT result;
650 : :
651 : 24 : result = timestamptz2date_safe(val, (Node *) &escontext);
652 : : /* We can ignore errors, since result is useful as-is */
653 : 24 : return DateADTGetDatum(result);
654 : : }
655 : :
656 : : static const bool date_rhs_is_varlena[] =
657 : : {false, false, false};
658 : :
659 : : static const btree_gin_convert_function date_cvt_fns[] =
660 : : {NULL, cvt_timestamp_date, cvt_timestamptz_date};
661 : :
662 : : static const PGFunction date_cmp_fns[] =
663 : : {date_cmp, timestamp_cmp_date, timestamptz_cmp_date};
664 : :
665 : 280 : GIN_SUPPORT(date, leftmostvalue_date, date_rhs_is_varlena, date_cvt_fns, date_cmp_fns)
666 : :
667 : : static Datum
668 : 4 : leftmostvalue_interval(void)
669 : : {
670 : 4 : Interval *v = palloc_object(Interval);
671 : :
672 : 4 : INTERVAL_NOBEGIN(v);
673 : :
674 : 4 : return IntervalPGetDatum(v);
675 : : }
676 : :
677 : : static const bool interval_rhs_is_varlena[] =
678 : : {false};
679 : :
680 : : static const PGFunction interval_cmp_fns[] =
681 : : {interval_cmp};
682 : :
683 : 46 : GIN_SUPPORT(interval, leftmostvalue_interval, interval_rhs_is_varlena, NULL, interval_cmp_fns)
684 : :
685 : : static Datum
686 : 4 : leftmostvalue_macaddr(void)
687 : : {
688 : 4 : macaddr *v = palloc0_object(macaddr);
689 : :
690 : 4 : return MacaddrPGetDatum(v);
691 : : }
692 : :
693 : : static const bool macaddr_rhs_is_varlena[] =
694 : : {false};
695 : :
696 : : static const PGFunction macaddr_cmp_fns[] =
697 : : {macaddr_cmp};
698 : :
699 : 40 : GIN_SUPPORT(macaddr, leftmostvalue_macaddr, macaddr_rhs_is_varlena, NULL, macaddr_cmp_fns)
700 : :
701 : : static Datum
702 : 4 : leftmostvalue_macaddr8(void)
703 : : {
704 : 4 : macaddr8 *v = palloc0_object(macaddr8);
705 : :
706 : 4 : return Macaddr8PGetDatum(v);
707 : : }
708 : :
709 : : static const bool macaddr8_rhs_is_varlena[] =
710 : : {false};
711 : :
712 : : static const PGFunction macaddr8_cmp_fns[] =
713 : : {macaddr8_cmp};
714 : :
715 : 40 : GIN_SUPPORT(macaddr8, leftmostvalue_macaddr8, macaddr8_rhs_is_varlena, NULL, macaddr8_cmp_fns)
716 : :
717 : : static Datum
718 : 8 : leftmostvalue_inet(void)
719 : : {
720 : 8 : return DirectFunctionCall1(inet_in, CStringGetDatum("0.0.0.0/0"));
721 : : }
722 : :
723 : : static const bool inet_rhs_is_varlena[] =
724 : : {true};
725 : :
726 : : static const PGFunction inet_cmp_fns[] =
727 : : {network_cmp};
728 : :
729 : 40 : GIN_SUPPORT(inet, leftmostvalue_inet, inet_rhs_is_varlena, NULL, inet_cmp_fns)
730 : :
731 : : static const bool cidr_rhs_is_varlena[] =
732 : : {true};
733 : :
734 : : static const PGFunction cidr_cmp_fns[] =
735 : : {network_cmp};
736 : :
737 : 40 : GIN_SUPPORT(cidr, leftmostvalue_inet, cidr_rhs_is_varlena, NULL, cidr_cmp_fns)
738 : :
739 : : static Datum
740 : 23 : leftmostvalue_text(void)
741 : : {
742 : 23 : return PointerGetDatum(cstring_to_text_with_len("", 0));
743 : : }
744 : :
745 : : static Datum
746 : 6 : cvt_name_text(Datum input)
747 : : {
748 : 6 : Name val = DatumGetName(input);
749 : :
750 : 6 : return PointerGetDatum(cstring_to_text(NameStr(*val)));
751 : : }
752 : :
753 : : static const bool text_rhs_is_varlena[] =
754 : : {true, false};
755 : :
756 : : static const btree_gin_convert_function text_cvt_fns[] =
757 : : {NULL, cvt_name_text};
758 : :
759 : : static const PGFunction text_cmp_fns[] =
760 : : {bttextcmp, btnametextcmp};
761 : :
762 : 102 : GIN_SUPPORT(text, leftmostvalue_text, text_rhs_is_varlena, text_cvt_fns, text_cmp_fns)
763 : :
764 : : static const bool bpchar_rhs_is_varlena[] =
765 : : {true};
766 : :
767 : : static const PGFunction bpchar_cmp_fns[] =
768 : : {bpcharcmp};
769 : :
770 : 49 : GIN_SUPPORT(bpchar, leftmostvalue_text, bpchar_rhs_is_varlena, NULL, bpchar_cmp_fns)
771 : :
772 : : static Datum
773 : 4 : leftmostvalue_char(void)
774 : : {
775 : 4 : return CharGetDatum(0);
776 : : }
777 : :
778 : : static const bool char_rhs_is_varlena[] =
779 : : {false};
780 : :
781 : : static const PGFunction char_cmp_fns[] =
782 : : {btcharcmp};
783 : :
784 : 40 : GIN_SUPPORT(char, leftmostvalue_char, char_rhs_is_varlena, NULL, char_cmp_fns)
785 : :
786 : : static const bool bytea_rhs_is_varlena[] =
787 : : {true};
788 : :
789 : : static const PGFunction bytea_cmp_fns[] =
790 : : {byteacmp};
791 : :
792 : 40 : GIN_SUPPORT(bytea, leftmostvalue_text, bytea_rhs_is_varlena, NULL, bytea_cmp_fns)
793 : :
794 : : static Datum
795 : 4 : leftmostvalue_bit(void)
796 : : {
797 : 4 : return DirectFunctionCall3(bit_in,
798 : : CStringGetDatum(""),
799 : : ObjectIdGetDatum(0),
800 : : Int32GetDatum(-1));
801 : : }
802 : :
803 : : static const bool bit_rhs_is_varlena[] =
804 : : {true};
805 : :
806 : : static const PGFunction bit_cmp_fns[] =
807 : : {bitcmp};
808 : :
809 : 40 : GIN_SUPPORT(bit, leftmostvalue_bit, bit_rhs_is_varlena, NULL, bit_cmp_fns)
810 : :
811 : : static Datum
812 : 4 : leftmostvalue_varbit(void)
813 : : {
814 : 4 : return DirectFunctionCall3(varbit_in,
815 : : CStringGetDatum(""),
816 : : ObjectIdGetDatum(0),
817 : : Int32GetDatum(-1));
818 : : }
819 : :
820 : : static const bool varbit_rhs_is_varlena[] =
821 : : {true};
822 : :
823 : : static const PGFunction varbit_cmp_fns[] =
824 : : {bitcmp};
825 : :
826 : 40 : GIN_SUPPORT(varbit, leftmostvalue_varbit, varbit_rhs_is_varlena, NULL, varbit_cmp_fns)
827 : :
828 : : /*
829 : : * Numeric type hasn't a real left-most value, so we use PointerGetDatum(NULL)
830 : : * (*not* a SQL NULL) to represent that. We can get away with that because
831 : : * the value returned by our leftmostvalue function will never be stored in
832 : : * the index nor passed to anything except our compare and prefix-comparison
833 : : * functions. The same trick could be used for other pass-by-reference types.
834 : : */
835 : :
836 : : #define NUMERIC_IS_LEFTMOST(x) ((x) == NULL)
837 : :
838 : 3 : PG_FUNCTION_INFO_V1(gin_numeric_cmp);
839 : :
840 : : Datum
841 : 43 : gin_numeric_cmp(PG_FUNCTION_ARGS)
842 : : {
843 : 43 : Numeric a = (Numeric) PG_GETARG_POINTER(0);
844 : 43 : Numeric b = (Numeric) PG_GETARG_POINTER(1);
845 : 43 : int res = 0;
846 : :
847 [ + + ]: 43 : if (NUMERIC_IS_LEFTMOST(a))
848 : : {
849 [ - + ]: 6 : res = (NUMERIC_IS_LEFTMOST(b)) ? 0 : -1;
850 : : }
851 [ - + ]: 37 : else if (NUMERIC_IS_LEFTMOST(b))
852 : : {
853 : 0 : res = 1;
854 : : }
855 : : else
856 : : {
857 : 37 : res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
858 : : NumericGetDatum(a),
859 : : NumericGetDatum(b)));
860 : : }
861 : :
862 : 43 : PG_RETURN_INT32(res);
863 : : }
864 : :
865 : : static Datum
866 : 4 : leftmostvalue_numeric(void)
867 : : {
868 : 4 : return PointerGetDatum(NULL);
869 : : }
870 : :
871 : : static const bool numeric_rhs_is_varlena[] =
872 : : {true};
873 : :
874 : : static const PGFunction numeric_cmp_fns[] =
875 : : {gin_numeric_cmp};
876 : :
877 : 40 : GIN_SUPPORT(numeric, leftmostvalue_numeric, numeric_rhs_is_varlena, NULL, numeric_cmp_fns)
878 : :
879 : : /*
880 : : * Use a similar trick to that used for numeric for enums, since we don't
881 : : * actually know the leftmost value of any enum without knowing the concrete
882 : : * type, so we use a dummy leftmost value of InvalidOid.
883 : : *
884 : : * Note that we use CallerFInfoFunctionCall2 here so that enum_cmp
885 : : * gets a valid fn_extra to work with. Unlike most other type comparison
886 : : * routines it needs it, so we can't use DirectFunctionCall2.
887 : : */
888 : :
889 : : #define ENUM_IS_LEFTMOST(x) ((x) == InvalidOid)
890 : :
891 : 3 : PG_FUNCTION_INFO_V1(gin_enum_cmp);
892 : :
893 : : Datum
894 : 200052 : gin_enum_cmp(PG_FUNCTION_ARGS)
895 : : {
896 : 200052 : Oid a = PG_GETARG_OID(0);
897 : 200052 : Oid b = PG_GETARG_OID(1);
898 : 200052 : int res = 0;
899 : :
900 [ + + ]: 200052 : if (ENUM_IS_LEFTMOST(a))
901 : : {
902 [ - + ]: 6 : res = (ENUM_IS_LEFTMOST(b)) ? 0 : -1;
903 : : }
904 [ - + ]: 200046 : else if (ENUM_IS_LEFTMOST(b))
905 : : {
906 : 0 : res = 1;
907 : : }
908 : : else
909 : : {
910 : 200046 : res = DatumGetInt32(CallerFInfoFunctionCall2(enum_cmp,
911 : : fcinfo->flinfo,
912 : : PG_GET_COLLATION(),
913 : : ObjectIdGetDatum(a),
914 : : ObjectIdGetDatum(b)));
915 : : }
916 : :
917 : 200052 : PG_RETURN_INT32(res);
918 : : }
919 : :
920 : : static Datum
921 : 4 : leftmostvalue_enum(void)
922 : : {
923 : 4 : return ObjectIdGetDatum(InvalidOid);
924 : : }
925 : :
926 : : static const bool enum_rhs_is_varlena[] =
927 : : {false};
928 : :
929 : : static const PGFunction enum_cmp_fns[] =
930 : : {gin_enum_cmp};
931 : :
932 : 100045 : GIN_SUPPORT(anyenum, leftmostvalue_enum, enum_rhs_is_varlena, NULL, enum_cmp_fns)
933 : :
934 : : static Datum
935 : 6 : leftmostvalue_uuid(void)
936 : : {
937 : : /*
938 : : * palloc0 will create the UUID with all zeroes:
939 : : * "00000000-0000-0000-0000-000000000000"
940 : : */
941 : 6 : pg_uuid_t *retval = palloc0_object(pg_uuid_t);
942 : :
943 : 6 : return UUIDPGetDatum(retval);
944 : : }
945 : :
946 : : static const bool uuid_rhs_is_varlena[] =
947 : : {false};
948 : :
949 : : static const PGFunction uuid_cmp_fns[] =
950 : : {uuid_cmp};
951 : :
952 : 45 : GIN_SUPPORT(uuid, leftmostvalue_uuid, uuid_rhs_is_varlena, NULL, uuid_cmp_fns)
953 : :
954 : : static Datum
955 : 13 : leftmostvalue_name(void)
956 : : {
957 : 13 : NameData *result = (NameData *) palloc0(NAMEDATALEN);
958 : :
959 : 13 : return NameGetDatum(result);
960 : : }
961 : :
962 : : static Datum
963 : 6 : cvt_text_name(Datum input)
964 : : {
965 : 6 : text *val = DatumGetTextPP(input);
966 : 6 : NameData *result = (NameData *) palloc0(NAMEDATALEN);
967 : 6 : int len = VARSIZE_ANY_EXHDR(val);
968 : :
969 : : /*
970 : : * Truncate oversize input. We're assuming this will produce a result
971 : : * considered less than the original. That could be a bad assumption in
972 : : * some collations, but fortunately an index on "name" is generally going
973 : : * to use C collation.
974 : : */
975 [ - + ]: 6 : if (len >= NAMEDATALEN)
976 : 0 : len = pg_mbcliplen(VARDATA_ANY(val), len, NAMEDATALEN - 1);
977 : :
978 : 6 : memcpy(NameStr(*result), VARDATA_ANY(val), len);
979 : :
980 : 6 : return NameGetDatum(result);
981 : : }
982 : :
983 : : static const bool name_rhs_is_varlena[] =
984 : : {false, true};
985 : :
986 : : static const btree_gin_convert_function name_cvt_fns[] =
987 : : {NULL, cvt_text_name};
988 : :
989 : : static const PGFunction name_cmp_fns[] =
990 : : {btnamecmp, bttextnamecmp};
991 : :
992 : 80 : GIN_SUPPORT(name, leftmostvalue_name, name_rhs_is_varlena, name_cvt_fns, name_cmp_fns)
993 : :
994 : : static Datum
995 : 10 : leftmostvalue_bool(void)
996 : : {
997 : 10 : return BoolGetDatum(false);
998 : : }
999 : :
1000 : : static const bool bool_rhs_is_varlena[] =
1001 : : {false};
1002 : :
1003 : : static const PGFunction bool_cmp_fns[] =
1004 : : {btboolcmp};
1005 : :
1006 : 50 : GIN_SUPPORT(bool, leftmostvalue_bool, bool_rhs_is_varlena, NULL, bool_cmp_fns)
|