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 "utils/builtins.h"
10 #include "utils/bytea.h"
11 #include "utils/cash.h"
12 #include "utils/date.h"
13 #include "utils/inet.h"
14 #include "utils/numeric.h"
15 #include "utils/timestamp.h"
16 #include "utils/varbit.h"
17
18 PG_MODULE_MAGIC;
19
20 typedef struct QueryInfo
21 {
22 StrategyNumber strategy;
23 Datum datum;
24 bool is_varlena;
25 Datum (*typecmp) (FunctionCallInfo);
26 } QueryInfo;
27
28 /*** GIN support functions shared by all datatypes ***/
29
30 static Datum
gin_btree_extract_value(FunctionCallInfo fcinfo,bool is_varlena)31 gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
32 {
33 Datum datum = PG_GETARG_DATUM(0);
34 int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
35 Datum *entries = (Datum *) palloc(sizeof(Datum));
36
37 if (is_varlena)
38 datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
39 entries[0] = datum;
40 *nentries = 1;
41
42 PG_RETURN_POINTER(entries);
43 }
44
45 /*
46 * For BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, and
47 * BTEqualStrategyNumber we want to start the index scan at the
48 * supplied query datum, and work forward. For BTLessStrategyNumber
49 * and BTLessEqualStrategyNumber, we need to start at the leftmost
50 * key, and work forward until the supplied query datum (which must be
51 * sent along inside the QueryInfo structure).
52 */
53 static Datum
gin_btree_extract_query(FunctionCallInfo fcinfo,bool is_varlena,Datum (* leftmostvalue)(void),Datum (* typecmp)(FunctionCallInfo))54 gin_btree_extract_query(FunctionCallInfo fcinfo,
55 bool is_varlena,
56 Datum (*leftmostvalue) (void),
57 Datum (*typecmp) (FunctionCallInfo))
58 {
59 Datum datum = PG_GETARG_DATUM(0);
60 int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
61 StrategyNumber strategy = PG_GETARG_UINT16(2);
62 bool **partialmatch = (bool **) PG_GETARG_POINTER(3);
63 Pointer **extra_data = (Pointer **) PG_GETARG_POINTER(4);
64 Datum *entries = (Datum *) palloc(sizeof(Datum));
65 QueryInfo *data = (QueryInfo *) palloc(sizeof(QueryInfo));
66 bool *ptr_partialmatch;
67
68 *nentries = 1;
69 ptr_partialmatch = *partialmatch = (bool *) palloc(sizeof(bool));
70 *ptr_partialmatch = false;
71 if (is_varlena)
72 datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
73 data->strategy = strategy;
74 data->datum = datum;
75 data->is_varlena = is_varlena;
76 data->typecmp = typecmp;
77 *extra_data = (Pointer *) palloc(sizeof(Pointer));
78 **extra_data = (Pointer) data;
79
80 switch (strategy)
81 {
82 case BTLessStrategyNumber:
83 case BTLessEqualStrategyNumber:
84 entries[0] = leftmostvalue();
85 *ptr_partialmatch = true;
86 break;
87 case BTGreaterEqualStrategyNumber:
88 case BTGreaterStrategyNumber:
89 *ptr_partialmatch = true;
90 case BTEqualStrategyNumber:
91 entries[0] = datum;
92 break;
93 default:
94 elog(ERROR, "unrecognized strategy number: %d", strategy);
95 }
96
97 PG_RETURN_POINTER(entries);
98 }
99
100 /*
101 * Datum a is a value from extract_query method and for BTLess*
102 * strategy it is a left-most value. So, use original datum from QueryInfo
103 * to decide to stop scanning or not. Datum b is always from index.
104 */
105 static Datum
gin_btree_compare_prefix(FunctionCallInfo fcinfo)106 gin_btree_compare_prefix(FunctionCallInfo fcinfo)
107 {
108 Datum a = PG_GETARG_DATUM(0);
109 Datum b = PG_GETARG_DATUM(1);
110 QueryInfo *data = (QueryInfo *) PG_GETARG_POINTER(3);
111 int32 res,
112 cmp;
113
114 cmp = DatumGetInt32(CallerFInfoFunctionCall2(
115 data->typecmp,
116 fcinfo->flinfo,
117 PG_GET_COLLATION(),
118 (data->strategy == BTLessStrategyNumber ||
119 data->strategy == BTLessEqualStrategyNumber)
120 ? data->datum : a,
121 b));
122
123 switch (data->strategy)
124 {
125 case BTLessStrategyNumber:
126 /* If original datum > indexed one then return match */
127 if (cmp > 0)
128 res = 0;
129 else
130 res = 1;
131 break;
132 case BTLessEqualStrategyNumber:
133 /* The same except equality */
134 if (cmp >= 0)
135 res = 0;
136 else
137 res = 1;
138 break;
139 case BTEqualStrategyNumber:
140 if (cmp != 0)
141 res = 1;
142 else
143 res = 0;
144 break;
145 case BTGreaterEqualStrategyNumber:
146 /* If original datum <= indexed one then return match */
147 if (cmp <= 0)
148 res = 0;
149 else
150 res = 1;
151 break;
152 case BTGreaterStrategyNumber:
153 /* If original datum <= indexed one then return match */
154 /* If original datum == indexed one then continue scan */
155 if (cmp < 0)
156 res = 0;
157 else if (cmp == 0)
158 res = -1;
159 else
160 res = 1;
161 break;
162 default:
163 elog(ERROR, "unrecognized strategy number: %d",
164 data->strategy);
165 res = 0;
166 }
167
168 PG_RETURN_INT32(res);
169 }
170
171 PG_FUNCTION_INFO_V1(gin_btree_consistent);
172 Datum
gin_btree_consistent(PG_FUNCTION_ARGS)173 gin_btree_consistent(PG_FUNCTION_ARGS)
174 {
175 bool *recheck = (bool *) PG_GETARG_POINTER(5);
176
177 *recheck = false;
178 PG_RETURN_BOOL(true);
179 }
180
181 /*** GIN_SUPPORT macro defines the datatype specific functions ***/
182
183 #define GIN_SUPPORT(type, is_varlena, leftmostvalue, typecmp) \
184 PG_FUNCTION_INFO_V1(gin_extract_value_##type); \
185 Datum \
186 gin_extract_value_##type(PG_FUNCTION_ARGS) \
187 { \
188 return gin_btree_extract_value(fcinfo, is_varlena); \
189 } \
190 PG_FUNCTION_INFO_V1(gin_extract_query_##type); \
191 Datum \
192 gin_extract_query_##type(PG_FUNCTION_ARGS) \
193 { \
194 return gin_btree_extract_query(fcinfo, \
195 is_varlena, leftmostvalue, typecmp); \
196 } \
197 PG_FUNCTION_INFO_V1(gin_compare_prefix_##type); \
198 Datum \
199 gin_compare_prefix_##type(PG_FUNCTION_ARGS) \
200 { \
201 return gin_btree_compare_prefix(fcinfo); \
202 }
203
204
205 /*** Datatype specifications ***/
206
207 static Datum
leftmostvalue_int2(void)208 leftmostvalue_int2(void)
209 {
210 return Int16GetDatum(SHRT_MIN);
211 }
212
GIN_SUPPORT(int2,false,leftmostvalue_int2,btint2cmp)213 GIN_SUPPORT(int2, false, leftmostvalue_int2, btint2cmp)
214
215 static Datum
216 leftmostvalue_int4(void)
217 {
218 return Int32GetDatum(INT_MIN);
219 }
220
GIN_SUPPORT(int4,false,leftmostvalue_int4,btint4cmp)221 GIN_SUPPORT(int4, false, leftmostvalue_int4, btint4cmp)
222
223 static Datum
224 leftmostvalue_int8(void)
225 {
226 return Int64GetDatum(PG_INT64_MIN);
227 }
228
GIN_SUPPORT(int8,false,leftmostvalue_int8,btint8cmp)229 GIN_SUPPORT(int8, false, leftmostvalue_int8, btint8cmp)
230
231 static Datum
232 leftmostvalue_float4(void)
233 {
234 return Float4GetDatum(-get_float4_infinity());
235 }
236
GIN_SUPPORT(float4,false,leftmostvalue_float4,btfloat4cmp)237 GIN_SUPPORT(float4, false, leftmostvalue_float4, btfloat4cmp)
238
239 static Datum
240 leftmostvalue_float8(void)
241 {
242 return Float8GetDatum(-get_float8_infinity());
243 }
244
GIN_SUPPORT(float8,false,leftmostvalue_float8,btfloat8cmp)245 GIN_SUPPORT(float8, false, leftmostvalue_float8, btfloat8cmp)
246
247 static Datum
248 leftmostvalue_money(void)
249 {
250 return Int64GetDatum(PG_INT64_MIN);
251 }
252
GIN_SUPPORT(money,false,leftmostvalue_money,cash_cmp)253 GIN_SUPPORT(money, false, leftmostvalue_money, cash_cmp)
254
255 static Datum
256 leftmostvalue_oid(void)
257 {
258 return ObjectIdGetDatum(0);
259 }
260
GIN_SUPPORT(oid,false,leftmostvalue_oid,btoidcmp)261 GIN_SUPPORT(oid, false, leftmostvalue_oid, btoidcmp)
262
263 static Datum
264 leftmostvalue_timestamp(void)
265 {
266 return TimestampGetDatum(DT_NOBEGIN);
267 }
268
GIN_SUPPORT(timestamp,false,leftmostvalue_timestamp,timestamp_cmp)269 GIN_SUPPORT(timestamp, false, leftmostvalue_timestamp, timestamp_cmp)
270
271 GIN_SUPPORT(timestamptz, false, leftmostvalue_timestamp, timestamp_cmp)
272
273 static Datum
274 leftmostvalue_time(void)
275 {
276 return TimeADTGetDatum(0);
277 }
278
GIN_SUPPORT(time,false,leftmostvalue_time,time_cmp)279 GIN_SUPPORT(time, false, leftmostvalue_time, time_cmp)
280
281 static Datum
282 leftmostvalue_timetz(void)
283 {
284 TimeTzADT *v = palloc(sizeof(TimeTzADT));
285
286 v->time = 0;
287 v->zone = -24 * 3600; /* XXX is that true? */
288
289 return TimeTzADTPGetDatum(v);
290 }
291
GIN_SUPPORT(timetz,false,leftmostvalue_timetz,timetz_cmp)292 GIN_SUPPORT(timetz, false, leftmostvalue_timetz, timetz_cmp)
293
294 static Datum
295 leftmostvalue_date(void)
296 {
297 return DateADTGetDatum(DATEVAL_NOBEGIN);
298 }
299
GIN_SUPPORT(date,false,leftmostvalue_date,date_cmp)300 GIN_SUPPORT(date, false, leftmostvalue_date, date_cmp)
301
302 static Datum
303 leftmostvalue_interval(void)
304 {
305 Interval *v = palloc(sizeof(Interval));
306
307 v->time = DT_NOBEGIN;
308 v->day = 0;
309 v->month = 0;
310 return IntervalPGetDatum(v);
311 }
312
GIN_SUPPORT(interval,false,leftmostvalue_interval,interval_cmp)313 GIN_SUPPORT(interval, false, leftmostvalue_interval, interval_cmp)
314
315 static Datum
316 leftmostvalue_macaddr(void)
317 {
318 macaddr *v = palloc0(sizeof(macaddr));
319
320 return MacaddrPGetDatum(v);
321 }
322
GIN_SUPPORT(macaddr,false,leftmostvalue_macaddr,macaddr_cmp)323 GIN_SUPPORT(macaddr, false, leftmostvalue_macaddr, macaddr_cmp)
324
325 static Datum
326 leftmostvalue_macaddr8(void)
327 {
328 macaddr8 *v = palloc0(sizeof(macaddr8));
329
330 return Macaddr8PGetDatum(v);
331 }
332
GIN_SUPPORT(macaddr8,false,leftmostvalue_macaddr8,macaddr8_cmp)333 GIN_SUPPORT(macaddr8, false, leftmostvalue_macaddr8, macaddr8_cmp)
334
335 static Datum
336 leftmostvalue_inet(void)
337 {
338 return DirectFunctionCall1(inet_in, CStringGetDatum("0.0.0.0/0"));
339 }
340
GIN_SUPPORT(inet,true,leftmostvalue_inet,network_cmp)341 GIN_SUPPORT(inet, true, leftmostvalue_inet, network_cmp)
342
343 GIN_SUPPORT(cidr, true, leftmostvalue_inet, network_cmp)
344
345 static Datum
346 leftmostvalue_text(void)
347 {
348 return PointerGetDatum(cstring_to_text_with_len("", 0));
349 }
350
GIN_SUPPORT(text,true,leftmostvalue_text,bttextcmp)351 GIN_SUPPORT(text, true, leftmostvalue_text, bttextcmp)
352
353 static Datum
354 leftmostvalue_char(void)
355 {
356 return CharGetDatum(0);
357 }
358
GIN_SUPPORT(char,false,leftmostvalue_char,btcharcmp)359 GIN_SUPPORT(char, false, leftmostvalue_char, btcharcmp)
360
361 GIN_SUPPORT(bytea, true, leftmostvalue_text, byteacmp)
362
363 static Datum
364 leftmostvalue_bit(void)
365 {
366 return DirectFunctionCall3(bit_in,
367 CStringGetDatum(""),
368 ObjectIdGetDatum(0),
369 Int32GetDatum(-1));
370 }
371
GIN_SUPPORT(bit,true,leftmostvalue_bit,bitcmp)372 GIN_SUPPORT(bit, true, leftmostvalue_bit, bitcmp)
373
374 static Datum
375 leftmostvalue_varbit(void)
376 {
377 return DirectFunctionCall3(varbit_in,
378 CStringGetDatum(""),
379 ObjectIdGetDatum(0),
380 Int32GetDatum(-1));
381 }
382
383 GIN_SUPPORT(varbit, true, leftmostvalue_varbit, bitcmp)
384
385 /*
386 * Numeric type hasn't a real left-most value, so we use PointerGetDatum(NULL)
387 * (*not* a SQL NULL) to represent that. We can get away with that because
388 * the value returned by our leftmostvalue function will never be stored in
389 * the index nor passed to anything except our compare and prefix-comparison
390 * functions. The same trick could be used for other pass-by-reference types.
391 */
392
393 #define NUMERIC_IS_LEFTMOST(x) ((x) == NULL)
394
395 PG_FUNCTION_INFO_V1(gin_numeric_cmp);
396
397 Datum
gin_numeric_cmp(PG_FUNCTION_ARGS)398 gin_numeric_cmp(PG_FUNCTION_ARGS)
399 {
400 Numeric a = (Numeric) PG_GETARG_POINTER(0);
401 Numeric b = (Numeric) PG_GETARG_POINTER(1);
402 int res = 0;
403
404 if (NUMERIC_IS_LEFTMOST(a))
405 {
406 res = (NUMERIC_IS_LEFTMOST(b)) ? 0 : -1;
407 }
408 else if (NUMERIC_IS_LEFTMOST(b))
409 {
410 res = 1;
411 }
412 else
413 {
414 res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
415 NumericGetDatum(a),
416 NumericGetDatum(b)));
417 }
418
419 PG_RETURN_INT32(res);
420 }
421
422 static Datum
leftmostvalue_numeric(void)423 leftmostvalue_numeric(void)
424 {
425 return PointerGetDatum(NULL);
426 }
427
428 GIN_SUPPORT(numeric, true, leftmostvalue_numeric, gin_numeric_cmp)
429
430 /*
431 * Use a similar trick to that used for numeric for enums, since we don't
432 * actually know the leftmost value of any enum without knowing the concrete
433 * type, so we use a dummy leftmost value of InvalidOid.
434 *
435 * Note that we use CallerFInfoFunctionCall2 here so that enum_cmp
436 * gets a valid fn_extra to work with. Unlike most other type comparison
437 * routines it needs it, so we can't use DirectFunctionCall2.
438 */
439
440
441 #define ENUM_IS_LEFTMOST(x) ((x) == InvalidOid)
442
443 PG_FUNCTION_INFO_V1(gin_enum_cmp);
444
445 Datum
gin_enum_cmp(PG_FUNCTION_ARGS)446 gin_enum_cmp(PG_FUNCTION_ARGS)
447 {
448 Oid a = PG_GETARG_OID(0);
449 Oid b = PG_GETARG_OID(1);
450 int res = 0;
451
452 if (ENUM_IS_LEFTMOST(a))
453 {
454 res = (ENUM_IS_LEFTMOST(b)) ? 0 : -1;
455 }
456 else if (ENUM_IS_LEFTMOST(b))
457 {
458 res = 1;
459 }
460 else
461 {
462 res = DatumGetInt32(CallerFInfoFunctionCall2(
463 enum_cmp,
464 fcinfo->flinfo,
465 PG_GET_COLLATION(),
466 ObjectIdGetDatum(a),
467 ObjectIdGetDatum(b)));
468 }
469
470 PG_RETURN_INT32(res);
471 }
472
473 static Datum
leftmostvalue_enum(void)474 leftmostvalue_enum(void)
475 {
476 return ObjectIdGetDatum(InvalidOid);
477 }
478
479 GIN_SUPPORT(anyenum, false, leftmostvalue_enum, gin_enum_cmp)
480