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