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 "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 : :
265 tgl@sss.pgh.pa.us 20 :CBC 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
4012 heikki.linnakangas@i 51 : 100685 : gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
52 : : {
53 : 100685 : Datum datum = PG_GETARG_DATUM(0);
54 : 100685 : int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
11 michael@paquier.xyz 55 :GNC 100685 : Datum *entries = palloc_object(Datum);
56 : :
57 : : /* Ensure that values stored in the index are not toasted */
4012 heikki.linnakangas@i 58 [ + + ]:CBC 100685 : if (is_varlena)
59 : 56 : datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
60 : 100685 : entries[0] = datum;
61 : 100685 : *nentries = 1;
62 : :
63 : 100685 : PG_RETURN_POINTER(entries);
64 : : }
65 : :
66 : : static Datum
67 : 690 : 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 : 690 : Datum datum = PG_GETARG_DATUM(0);
74 : 690 : int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
75 : 690 : StrategyNumber strategy = PG_GETARG_UINT16(2);
76 : 690 : bool **partialmatch = (bool **) PG_GETARG_POINTER(3);
77 : 690 : Pointer **extra_data = (Pointer **) PG_GETARG_POINTER(4);
11 michael@paquier.xyz 78 :GNC 690 : Datum *entries = palloc_object(Datum);
79 : 690 : QueryInfo *data = palloc_object(QueryInfo);
80 : 690 : 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 : : */
166 tgl@sss.pgh.pa.us 88 : 690 : btree_strat = BTGIN_GET_BTREE_STRATEGY(strategy);
89 : 690 : 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 [ + + ]: 690 : 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 */
4012 heikki.linnakangas@i 99 :CBC 690 : *nentries = 1;
166 tgl@sss.pgh.pa.us 100 :GNC 690 : *partialmatch = ptr_partialmatch;
4012 heikki.linnakangas@i 101 :CBC 690 : *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 : : */
166 tgl@sss.pgh.pa.us 113 [ + + + - ]:GNC 690 : switch (btree_strat)
114 : : {
4012 heikki.linnakangas@i 115 :CBC 284 : case BTLessStrategyNumber:
116 : : case BTLessEqualStrategyNumber:
117 : 284 : entries[0] = leftmostvalue();
118 : 284 : *ptr_partialmatch = true;
119 : 284 : break;
120 : 267 : case BTGreaterEqualStrategyNumber:
121 : : case BTGreaterStrategyNumber:
122 : 267 : *ptr_partialmatch = true;
123 : : /* FALLTHROUGH */
124 : 406 : case BTEqualStrategyNumber:
125 : : /* If we have a conversion function, apply it */
166 tgl@sss.pgh.pa.us 126 [ + + + + ]:GNC 406 : if (cvt_fns && cvt_fns[rhs_code])
127 : : {
128 : 206 : entries[0] = (*cvt_fns[rhs_code]) (datum);
129 : 206 : *ptr_partialmatch = true;
130 : : }
131 : : else
132 : 200 : entries[0] = datum;
4012 heikki.linnakangas@i 133 :CBC 406 : break;
4012 heikki.linnakangas@i 134 :UBC 0 : default:
135 [ # # ]: 0 : elog(ERROR, "unrecognized strategy number: %d", strategy);
136 : : }
137 : :
138 : : /* Fill "extra" data */
166 tgl@sss.pgh.pa.us 139 :GNC 690 : data->strategy = strategy;
140 : 690 : data->orig_datum = datum;
141 : 690 : data->entry_datum = entries[0];
142 : 690 : data->typecmp = cmp_fns[rhs_code];
11 michael@paquier.xyz 143 : 690 : *extra_data = palloc_object(Pointer);
166 tgl@sss.pgh.pa.us 144 : 690 : **extra_data = (Pointer) data;
145 : :
4012 heikki.linnakangas@i 146 :CBC 690 : PG_RETURN_POINTER(entries);
147 : : }
148 : :
149 : : static Datum
150 : 1094 : gin_btree_compare_prefix(FunctionCallInfo fcinfo)
151 : : {
166 tgl@sss.pgh.pa.us 152 :GNC 1094 : Datum partial_key PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM(0);
153 : 1094 : Datum key = PG_GETARG_DATUM(1);
4012 heikki.linnakangas@i 154 :CBC 1094 : 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 : : */
166 tgl@sss.pgh.pa.us 170 [ - + ]:GNC 1094 : Assert(partial_key == data->entry_datum);
171 : :
2147 alvherre@alvh.no-ip. 172 :CBC 1094 : 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 : : */
166 tgl@sss.pgh.pa.us 188 [ + + + + :GNC 1094 : switch (BTGIN_GET_BTREE_STRATEGY(data->strategy))
+ - ]
189 : : {
4012 heikki.linnakangas@i 190 :CBC 272 : case BTLessStrategyNumber:
191 : : /* If original datum > indexed one then return match */
192 [ + + ]: 272 : if (cmp > 0)
193 : 209 : res = 0;
194 : : else
166 tgl@sss.pgh.pa.us 195 :GNC 63 : res = 1; /* end scan */
4012 heikki.linnakangas@i 196 :CBC 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
166 tgl@sss.pgh.pa.us 202 :GNC 59 : res = 1; /* end scan */
4012 heikki.linnakangas@i 203 :CBC 328 : break;
4012 heikki.linnakangas@i 204 :GBC 54 : case BTEqualStrategyNumber:
205 : : /* If original datum = indexed one then return match */
206 : : /* See above about why we can end scan when cmp < 0 */
166 tgl@sss.pgh.pa.us 207 [ + + ]:GNC 54 : if (cmp == 0)
4012 heikki.linnakangas@i 208 :GBC 25 : res = 0;
209 : : else
166 tgl@sss.pgh.pa.us 210 :GNC 29 : res = 1; /* end scan */
4012 heikki.linnakangas@i 211 :GBC 54 : break;
4012 heikki.linnakangas@i 212 :CBC 216 : case BTGreaterEqualStrategyNumber:
213 : : /* If original datum <= indexed one then return match */
214 [ + + ]: 216 : if (cmp <= 0)
215 : 212 : res = 0;
216 : : else
166 tgl@sss.pgh.pa.us 217 :GNC 4 : res = -1; /* keep scanning */
4012 heikki.linnakangas@i 218 :CBC 216 : break;
219 : 224 : case BTGreaterStrategyNumber:
220 : : /* If original datum < indexed one then return match */
221 [ + + ]: 224 : if (cmp < 0)
222 : 164 : res = 0;
223 : : else
166 tgl@sss.pgh.pa.us 224 :GNC 60 : res = -1; /* keep scanning */
4012 heikki.linnakangas@i 225 :CBC 224 : break;
4012 heikki.linnakangas@i 226 :UBC 0 : default:
227 [ # # ]: 0 : elog(ERROR, "unrecognized strategy number: %d",
228 : : data->strategy);
229 : : res = 0;
230 : : }
231 : :
4012 heikki.linnakangas@i 232 :CBC 1094 : PG_RETURN_INT32(res);
233 : : }
234 : :
6110 tgl@sss.pgh.pa.us 235 : 32 : PG_FUNCTION_INFO_V1(gin_btree_consistent);
236 : : Datum
237 : 918 : gin_btree_consistent(PG_FUNCTION_ARGS)
238 : : {
6032 bruce@momjian.us 239 : 918 : bool *recheck = (bool *) PG_GETARG_POINTER(5);
240 : :
6110 tgl@sss.pgh.pa.us 241 : 918 : *recheck = false;
242 : 918 : 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
166 tgl@sss.pgh.pa.us 296 :GNC 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
6110 tgl@sss.pgh.pa.us 334 :CBC 14 : leftmostvalue_int4(void)
335 : : {
336 : 14 : return Int32GetDatum(INT_MIN);
337 : : }
338 : :
339 : : static Datum
166 tgl@sss.pgh.pa.us 340 :GNC 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
6110 tgl@sss.pgh.pa.us 369 :CBC 14 : leftmostvalue_int8(void)
370 : : {
3439 peter_e@gmx.net 371 : 14 : return Int64GetDatum(PG_INT64_MIN);
372 : : }
373 : :
374 : : static Datum
166 tgl@sss.pgh.pa.us 375 :GNC 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
6110 tgl@sss.pgh.pa.us 402 :CBC 33 : leftmostvalue_float4(void)
403 : : {
404 : 33 : return Float4GetDatum(-get_float4_infinity());
405 : : }
406 : :
407 : : static Datum
166 tgl@sss.pgh.pa.us 408 :GNC 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
6110 tgl@sss.pgh.pa.us 437 :CBC 9 : leftmostvalue_float8(void)
438 : : {
439 : 9 : return Float8GetDatum(-get_float8_infinity());
440 : : }
441 : :
442 : : static Datum
166 tgl@sss.pgh.pa.us 443 :GNC 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
6110 tgl@sss.pgh.pa.us 462 :CBC 4 : leftmostvalue_money(void)
463 : : {
3439 peter_e@gmx.net 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 : :
166 tgl@sss.pgh.pa.us 473 :GNC 40 : GIN_SUPPORT(money, leftmostvalue_money, money_rhs_is_varlena, NULL, money_cmp_fns)
474 : :
475 : : static Datum
6110 tgl@sss.pgh.pa.us 476 :CBC 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 : :
166 tgl@sss.pgh.pa.us 487 :GNC 40 : GIN_SUPPORT(oid, leftmostvalue_oid, oid_rhs_is_varlena, NULL, oid_cmp_fns)
488 : :
489 : : static Datum
6110 tgl@sss.pgh.pa.us 490 :CBC 46 : leftmostvalue_timestamp(void)
491 : : {
492 : 46 : return TimestampGetDatum(DT_NOBEGIN);
493 : : }
494 : :
495 : : static Datum
166 tgl@sss.pgh.pa.us 496 :GNC 18 : cvt_date_timestamp(Datum input)
497 : : {
498 : 18 : DateADT val = DatumGetDateADT(input);
499 : : Timestamp result;
15 michael@paquier.xyz 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 */
166 tgl@sss.pgh.pa.us 504 : 18 : return TimestampGetDatum(result);
505 : : }
506 : :
507 : : static Datum
508 : 20 : cvt_timestamptz_timestamp(Datum input)
509 : : {
510 : 20 : TimestampTz val = DatumGetTimestampTz(input);
14 michael@paquier.xyz 511 : 20 : ErrorSaveContext escontext = {T_ErrorSaveContext};
512 : : Timestamp result;
513 : :
514 : 20 : result = timestamptz2timestamp_safe(val, (Node *) &escontext);
515 : : /* We can ignore errors, since result is useful as-is */
166 tgl@sss.pgh.pa.us 516 : 20 : return TimestampGetDatum(result);
517 : : }
518 : :
519 : : static const bool timestamp_rhs_is_varlena[] =
520 : : {false, false, false};
521 : :
522 : : static const btree_gin_convert_function timestamp_cvt_fns[] =
523 : : {NULL, cvt_date_timestamp, cvt_timestamptz_timestamp};
524 : :
525 : : static const PGFunction timestamp_cmp_fns[] =
526 : : {timestamp_cmp, date_cmp_timestamp, timestamptz_cmp_timestamp};
527 : :
528 : 230 : GIN_SUPPORT(timestamp, leftmostvalue_timestamp, timestamp_rhs_is_varlena, timestamp_cvt_fns, timestamp_cmp_fns)
529 : :
530 : : static Datum
531 : 6 : cvt_date_timestamptz(Datum input)
532 : : {
533 : 6 : DateADT val = DatumGetDateADT(input);
15 michael@paquier.xyz 534 : 6 : ErrorSaveContext escontext = {T_ErrorSaveContext};
535 : : TimestampTz result;
536 : :
537 : 6 : result = date2timestamptz_safe(val, (Node *) &escontext);
538 : : /* We can ignore errors, since result is useful as-is */
166 tgl@sss.pgh.pa.us 539 : 6 : return TimestampTzGetDatum(result);
540 : : }
541 : :
542 : : static Datum
543 : 6 : cvt_timestamp_timestamptz(Datum input)
544 : : {
545 : 6 : Timestamp val = DatumGetTimestamp(input);
14 michael@paquier.xyz 546 : 6 : ErrorSaveContext escontext = {T_ErrorSaveContext};
547 : : TimestampTz result;
548 : :
549 : 6 : result = timestamp2timestamptz_safe(val, (Node *) &escontext);
550 : : /* We can ignore errors, since result is useful as-is */
166 tgl@sss.pgh.pa.us 551 : 6 : return TimestampTzGetDatum(result);
552 : : }
553 : :
554 : : static const bool timestamptz_rhs_is_varlena[] =
555 : : {false, false, false};
556 : :
557 : : static const btree_gin_convert_function timestamptz_cvt_fns[] =
558 : : {NULL, cvt_date_timestamptz, cvt_timestamp_timestamptz};
559 : :
560 : : static const PGFunction timestamptz_cmp_fns[] =
561 : : {timestamp_cmp, date_cmp_timestamptz, timestamp_cmp_timestamptz};
562 : :
563 : 94 : GIN_SUPPORT(timestamptz, leftmostvalue_timestamp, timestamptz_rhs_is_varlena, timestamptz_cvt_fns, timestamptz_cmp_fns)
564 : :
565 : : static Datum
6110 tgl@sss.pgh.pa.us 566 :CBC 4 : leftmostvalue_time(void)
567 : : {
568 : 4 : 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 : :
166 tgl@sss.pgh.pa.us 577 :GNC 40 : GIN_SUPPORT(time, leftmostvalue_time, time_rhs_is_varlena, NULL, time_cmp_fns)
578 : :
579 : : static Datum
6110 tgl@sss.pgh.pa.us 580 :CBC 4 : leftmostvalue_timetz(void)
581 : : {
11 michael@paquier.xyz 582 :GNC 4 : TimeTzADT *v = palloc_object(TimeTzADT);
583 : :
6110 tgl@sss.pgh.pa.us 584 :CBC 4 : v->time = 0;
6032 bruce@momjian.us 585 : 4 : v->zone = -24 * 3600; /* XXX is that true? */
586 : :
6110 tgl@sss.pgh.pa.us 587 : 4 : return TimeTzADTPGetDatum(v);
588 : : }
589 : :
590 : : static const bool timetz_rhs_is_varlena[] =
591 : : {false};
592 : :
593 : : static const PGFunction timetz_cmp_fns[] =
594 : : {timetz_cmp};
595 : :
166 tgl@sss.pgh.pa.us 596 :GNC 40 : GIN_SUPPORT(timetz, leftmostvalue_timetz, timetz_rhs_is_varlena, NULL, timetz_cmp_fns)
597 : :
598 : : static Datum
6110 tgl@sss.pgh.pa.us 599 :CBC 38 : leftmostvalue_date(void)
600 : : {
601 : 38 : return DateADTGetDatum(DATEVAL_NOBEGIN);
602 : : }
603 : :
604 : : static Datum
166 tgl@sss.pgh.pa.us 605 :GNC 24 : cvt_timestamp_date(Datum input)
606 : : {
607 : 24 : Timestamp val = DatumGetTimestamp(input);
15 michael@paquier.xyz 608 : 24 : ErrorSaveContext escontext = {T_ErrorSaveContext};
609 : : DateADT result;
610 : :
611 : 24 : result = timestamp2date_safe(val, (Node *) &escontext);
612 : : /* We can ignore errors, since result is useful as-is */
166 tgl@sss.pgh.pa.us 613 : 24 : return DateADTGetDatum(result);
614 : : }
615 : :
616 : : static Datum
617 : 24 : cvt_timestamptz_date(Datum input)
618 : : {
619 : 24 : TimestampTz val = DatumGetTimestampTz(input);
15 michael@paquier.xyz 620 : 24 : ErrorSaveContext escontext = {T_ErrorSaveContext};
621 : : DateADT result;
622 : :
623 : 24 : result = timestamptz2date_safe(val, (Node *) &escontext);
624 : : /* We can ignore errors, since result is useful as-is */
166 tgl@sss.pgh.pa.us 625 : 24 : return DateADTGetDatum(result);
626 : : }
627 : :
628 : : static const bool date_rhs_is_varlena[] =
629 : : {false, false, false};
630 : :
631 : : static const btree_gin_convert_function date_cvt_fns[] =
632 : : {NULL, cvt_timestamp_date, cvt_timestamptz_date};
633 : :
634 : : static const PGFunction date_cmp_fns[] =
635 : : {date_cmp, timestamp_cmp_date, timestamptz_cmp_date};
636 : :
637 : 280 : GIN_SUPPORT(date, leftmostvalue_date, date_rhs_is_varlena, date_cvt_fns, date_cmp_fns)
638 : :
639 : : static Datum
6110 tgl@sss.pgh.pa.us 640 :CBC 4 : leftmostvalue_interval(void)
641 : : {
11 michael@paquier.xyz 642 :GNC 4 : Interval *v = palloc_object(Interval);
643 : :
763 dean.a.rasheed@gmail 644 :CBC 4 : INTERVAL_NOBEGIN(v);
645 : :
6110 tgl@sss.pgh.pa.us 646 : 4 : return IntervalPGetDatum(v);
647 : : }
648 : :
649 : : static const bool interval_rhs_is_varlena[] =
650 : : {false};
651 : :
652 : : static const PGFunction interval_cmp_fns[] =
653 : : {interval_cmp};
654 : :
166 tgl@sss.pgh.pa.us 655 :GNC 46 : GIN_SUPPORT(interval, leftmostvalue_interval, interval_rhs_is_varlena, NULL, interval_cmp_fns)
656 : :
657 : : static Datum
6110 tgl@sss.pgh.pa.us 658 :CBC 4 : leftmostvalue_macaddr(void)
659 : : {
11 michael@paquier.xyz 660 :GNC 4 : macaddr *v = palloc0_object(macaddr);
661 : :
6110 tgl@sss.pgh.pa.us 662 :CBC 4 : return MacaddrPGetDatum(v);
663 : : }
664 : :
665 : : static const bool macaddr_rhs_is_varlena[] =
666 : : {false};
667 : :
668 : : static const PGFunction macaddr_cmp_fns[] =
669 : : {macaddr_cmp};
670 : :
166 tgl@sss.pgh.pa.us 671 :GNC 40 : GIN_SUPPORT(macaddr, leftmostvalue_macaddr, macaddr_rhs_is_varlena, NULL, macaddr_cmp_fns)
672 : :
673 : : static Datum
3198 sfrost@snowman.net 674 :CBC 4 : leftmostvalue_macaddr8(void)
675 : : {
11 michael@paquier.xyz 676 :GNC 4 : macaddr8 *v = palloc0_object(macaddr8);
677 : :
3198 sfrost@snowman.net 678 :CBC 4 : return Macaddr8PGetDatum(v);
679 : : }
680 : :
681 : : static const bool macaddr8_rhs_is_varlena[] =
682 : : {false};
683 : :
684 : : static const PGFunction macaddr8_cmp_fns[] =
685 : : {macaddr8_cmp};
686 : :
166 tgl@sss.pgh.pa.us 687 :GNC 40 : GIN_SUPPORT(macaddr8, leftmostvalue_macaddr8, macaddr8_rhs_is_varlena, NULL, macaddr8_cmp_fns)
688 : :
689 : : static Datum
6110 tgl@sss.pgh.pa.us 690 :CBC 8 : leftmostvalue_inet(void)
691 : : {
3913 bruce@momjian.us 692 : 8 : 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 : :
166 tgl@sss.pgh.pa.us 701 :GNC 40 : 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 : 40 : GIN_SUPPORT(cidr, leftmostvalue_inet, cidr_rhs_is_varlena, NULL, cidr_cmp_fns)
710 : :
711 : : static Datum
6110 tgl@sss.pgh.pa.us 712 :CBC 23 : leftmostvalue_text(void)
713 : : {
714 : 23 : return PointerGetDatum(cstring_to_text_with_len("", 0));
715 : : }
716 : :
717 : : static Datum
166 tgl@sss.pgh.pa.us 718 :GNC 6 : cvt_name_text(Datum input)
719 : : {
720 : 6 : Name val = DatumGetName(input);
721 : :
722 : 6 : return PointerGetDatum(cstring_to_text(NameStr(*val)));
723 : : }
724 : :
725 : : static const bool text_rhs_is_varlena[] =
726 : : {true, false};
727 : :
728 : : static const btree_gin_convert_function text_cvt_fns[] =
729 : : {NULL, cvt_name_text};
730 : :
731 : : static const PGFunction text_cmp_fns[] =
732 : : {bttextcmp, btnametextcmp};
733 : :
734 : 102 : 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 : 49 : GIN_SUPPORT(bpchar, leftmostvalue_text, bpchar_rhs_is_varlena, NULL, bpchar_cmp_fns)
743 : :
744 : : static Datum
6110 tgl@sss.pgh.pa.us 745 :CBC 4 : leftmostvalue_char(void)
746 : : {
1589 747 : 4 : 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 : :
166 tgl@sss.pgh.pa.us 756 :GNC 40 : 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 : 40 : GIN_SUPPORT(bytea, leftmostvalue_text, bytea_rhs_is_varlena, NULL, bytea_cmp_fns)
765 : :
766 : : static Datum
6110 tgl@sss.pgh.pa.us 767 :CBC 4 : leftmostvalue_bit(void)
768 : : {
3920 bruce@momjian.us 769 : 4 : 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 : :
166 tgl@sss.pgh.pa.us 781 :GNC 40 : GIN_SUPPORT(bit, leftmostvalue_bit, bit_rhs_is_varlena, NULL, bit_cmp_fns)
782 : :
783 : : static Datum
6110 tgl@sss.pgh.pa.us 784 :CBC 4 : leftmostvalue_varbit(void)
785 : : {
3920 bruce@momjian.us 786 : 4 : 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 : :
166 tgl@sss.pgh.pa.us 798 :GNC 40 : 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 : :
6110 tgl@sss.pgh.pa.us 810 :CBC 3 : PG_FUNCTION_INFO_V1(gin_numeric_cmp);
811 : :
812 : : Datum
813 : 43 : gin_numeric_cmp(PG_FUNCTION_ARGS)
814 : : {
6032 bruce@momjian.us 815 : 43 : Numeric a = (Numeric) PG_GETARG_POINTER(0);
816 : 43 : Numeric b = (Numeric) PG_GETARG_POINTER(1);
817 : 43 : int res = 0;
818 : :
819 [ + + ]: 43 : if (NUMERIC_IS_LEFTMOST(a))
820 : : {
821 [ - + ]: 6 : res = (NUMERIC_IS_LEFTMOST(b)) ? 0 : -1;
822 : : }
823 [ - + ]: 37 : else if (NUMERIC_IS_LEFTMOST(b))
824 : : {
6110 tgl@sss.pgh.pa.us 825 :UBC 0 : res = 1;
826 : : }
827 : : else
828 : : {
6110 tgl@sss.pgh.pa.us 829 :CBC 37 : res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
830 : : NumericGetDatum(a),
831 : : NumericGetDatum(b)));
832 : : }
833 : :
834 : 43 : PG_RETURN_INT32(res);
835 : : }
836 : :
837 : : static Datum
838 : 4 : leftmostvalue_numeric(void)
839 : : {
840 : 4 : 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 : :
166 tgl@sss.pgh.pa.us 849 :GNC 40 : 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 : :
3192 andrew@dunslane.net 863 :CBC 3 : PG_FUNCTION_INFO_V1(gin_enum_cmp);
864 : :
865 : : Datum
866 : 200052 : gin_enum_cmp(PG_FUNCTION_ARGS)
867 : : {
3135 bruce@momjian.us 868 : 200052 : Oid a = PG_GETARG_OID(0);
869 : 200052 : Oid b = PG_GETARG_OID(1);
870 : 200052 : int res = 0;
871 : :
3192 andrew@dunslane.net 872 [ + + ]: 200052 : if (ENUM_IS_LEFTMOST(a))
873 : : {
874 [ - + ]: 6 : res = (ENUM_IS_LEFTMOST(b)) ? 0 : -1;
875 : : }
876 [ - + ]: 200046 : else if (ENUM_IS_LEFTMOST(b))
877 : : {
3192 andrew@dunslane.net 878 :UBC 0 : res = 1;
879 : : }
880 : : else
881 : : {
2147 alvherre@alvh.no-ip. 882 :CBC 200046 : res = DatumGetInt32(CallerFInfoFunctionCall2(enum_cmp,
883 : : fcinfo->flinfo,
884 : : PG_GET_COLLATION(),
885 : : ObjectIdGetDatum(a),
886 : : ObjectIdGetDatum(b)));
887 : : }
888 : :
3192 andrew@dunslane.net 889 : 200052 : PG_RETURN_INT32(res);
890 : : }
891 : :
892 : : static Datum
893 : 4 : leftmostvalue_enum(void)
894 : : {
895 : 4 : 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 : :
166 tgl@sss.pgh.pa.us 904 :GNC 100045 : GIN_SUPPORT(anyenum, leftmostvalue_enum, enum_rhs_is_varlena, NULL, enum_cmp_fns)
905 : :
906 : : static Datum
2812 teodor@sigaev.ru 907 :CBC 6 : leftmostvalue_uuid(void)
908 : : {
909 : : /*
910 : : * palloc0 will create the UUID with all zeroes:
911 : : * "00000000-0000-0000-0000-000000000000"
912 : : */
11 michael@paquier.xyz 913 :GNC 6 : pg_uuid_t *retval = palloc0_object(pg_uuid_t);
914 : :
2812 teodor@sigaev.ru 915 :CBC 6 : return UUIDPGetDatum(retval);
916 : : }
917 : :
918 : : static const bool uuid_rhs_is_varlena[] =
919 : : {false};
920 : :
921 : : static const PGFunction uuid_cmp_fns[] =
922 : : {uuid_cmp};
923 : :
166 tgl@sss.pgh.pa.us 924 :GNC 45 : GIN_SUPPORT(uuid, leftmostvalue_uuid, uuid_rhs_is_varlena, NULL, uuid_cmp_fns)
925 : :
926 : : static Datum
2812 teodor@sigaev.ru 927 :CBC 13 : leftmostvalue_name(void)
928 : : {
2791 tgl@sss.pgh.pa.us 929 : 13 : NameData *result = (NameData *) palloc0(NAMEDATALEN);
930 : :
2812 teodor@sigaev.ru 931 : 13 : return NameGetDatum(result);
932 : : }
933 : :
934 : : static Datum
166 tgl@sss.pgh.pa.us 935 :GNC 6 : cvt_text_name(Datum input)
936 : : {
937 : 6 : text *val = DatumGetTextPP(input);
938 : 6 : NameData *result = (NameData *) palloc0(NAMEDATALEN);
939 : 6 : 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 [ - + ]: 6 : if (len >= NAMEDATALEN)
166 tgl@sss.pgh.pa.us 948 :UNC 0 : len = pg_mbcliplen(VARDATA_ANY(val), len, NAMEDATALEN - 1);
949 : :
166 tgl@sss.pgh.pa.us 950 :GNC 6 : memcpy(NameStr(*result), VARDATA_ANY(val), len);
951 : :
952 : 6 : return NameGetDatum(result);
953 : : }
954 : :
955 : : static const bool name_rhs_is_varlena[] =
956 : : {false, true};
957 : :
958 : : static const btree_gin_convert_function name_cvt_fns[] =
959 : : {NULL, cvt_text_name};
960 : :
961 : : static const PGFunction name_cmp_fns[] =
962 : : {btnamecmp, bttextnamecmp};
963 : :
964 : 80 : GIN_SUPPORT(name, leftmostvalue_name, name_rhs_is_varlena, name_cvt_fns, name_cmp_fns)
965 : :
966 : : static Datum
2812 teodor@sigaev.ru 967 :CBC 10 : leftmostvalue_bool(void)
968 : : {
969 : 10 : 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 : :
166 tgl@sss.pgh.pa.us 978 :GNC 50 : GIN_SUPPORT(bool, leftmostvalue_bool, bool_rhs_is_varlena, NULL, bool_cmp_fns)
|