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