]> granicus.if.org Git - postgresql/blob - contrib/btree_gin/btree_gin.c
btree_gin: properly call DirectFunctionCall1()
[postgresql] / contrib / btree_gin / btree_gin.c
1 /*
2  * contrib/btree_gin/btree_gin.c
3  */
4 #include "postgres.h"
5
6 #include <limits.h>
7
8 #include "access/skey.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
29 /*** GIN support functions shared by all datatypes ***/
30
31 static Datum
32 gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
33 {
34         Datum           datum = PG_GETARG_DATUM(0);
35         int32      *nentries = (int32 *) PG_GETARG_POINTER(1);
36         Datum      *entries = (Datum *) palloc(sizeof(Datum));
37
38         if (is_varlena)
39                 datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
40         entries[0] = datum;
41         *nentries = 1;
42
43         PG_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  */
54 static Datum
55 gin_btree_extract_query(FunctionCallInfo fcinfo,
56                                                 bool is_varlena,
57                                                 Datum (*leftmostvalue) (void),
58                                                 Datum (*typecmp) (FunctionCallInfo))
59 {
60         Datum           datum = PG_GETARG_DATUM(0);
61         int32      *nentries = (int32 *) PG_GETARG_POINTER(1);
62         StrategyNumber strategy = PG_GETARG_UINT16(2);
63         bool      **partialmatch = (bool **) PG_GETARG_POINTER(3);
64         Pointer   **extra_data = (Pointer **) PG_GETARG_POINTER(4);
65         Datum      *entries = (Datum *) palloc(sizeof(Datum));
66         QueryInfo  *data = (QueryInfo *) palloc(sizeof(QueryInfo));
67         bool       *ptr_partialmatch;
68
69         *nentries = 1;
70         ptr_partialmatch = *partialmatch = (bool *) palloc(sizeof(bool));
71         *ptr_partialmatch = false;
72         if (is_varlena)
73                 datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
74         data->strategy = strategy;
75         data->datum = datum;
76         data->is_varlena = is_varlena;
77         data->typecmp = typecmp;
78         *extra_data = (Pointer *) palloc(sizeof(Pointer));
79         **extra_data = (Pointer) data;
80
81         switch (strategy)
82         {
83                 case BTLessStrategyNumber:
84                 case BTLessEqualStrategyNumber:
85                         entries[0] = leftmostvalue();
86                         *ptr_partialmatch = true;
87                         break;
88                 case BTGreaterEqualStrategyNumber:
89                 case BTGreaterStrategyNumber:
90                         *ptr_partialmatch = true;
91                 case BTEqualStrategyNumber:
92                         entries[0] = datum;
93                         break;
94                 default:
95                         elog(ERROR, "unrecognized strategy number: %d", strategy);
96         }
97
98         PG_RETURN_POINTER(entries);
99 }
100
101 /*
102  * Datum a is a value from extract_query method and for BTLess*
103  * strategy it is a left-most value.  So, use original datum from QueryInfo
104  * to decide to stop scanning or not.  Datum b is always from index.
105  */
106 static Datum
107 gin_btree_compare_prefix(FunctionCallInfo fcinfo)
108 {
109         Datum           a = PG_GETARG_DATUM(0);
110         Datum           b = PG_GETARG_DATUM(1);
111         QueryInfo  *data = (QueryInfo *) PG_GETARG_POINTER(3);
112         int32           res,
113                                 cmp;
114
115         cmp = DatumGetInt32(DirectFunctionCall2Coll(
116                                 data->typecmp,
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
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
208 leftmostvalue_int2(void)
209 {
210         return Int16GetDatum(SHRT_MIN);
211 }
212 GIN_SUPPORT(int2, false, leftmostvalue_int2, btint2cmp)
213
214 static Datum
215 leftmostvalue_int4(void)
216 {
217         return Int32GetDatum(INT_MIN);
218 }
219 GIN_SUPPORT(int4, false, leftmostvalue_int4, btint4cmp)
220
221 static Datum
222 leftmostvalue_int8(void)
223 {
224         /*
225          * Use sequence's definition to keep compatibility.
226          */
227         return Int64GetDatum(SEQ_MINVALUE);
228 }
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
238 static Datum
239 leftmostvalue_float8(void)
240 {
241         return Float8GetDatum(-get_float8_infinity());
242 }
243 GIN_SUPPORT(float8, false, leftmostvalue_float8, btfloat8cmp)
244
245 static Datum
246 leftmostvalue_money(void)
247 {
248         /*
249          * Use sequence's definition to keep compatibility.
250          */
251         return Int64GetDatum(SEQ_MINVALUE);
252 }
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
262 static Datum
263 leftmostvalue_timestamp(void)
264 {
265         return TimestampGetDatum(DT_NOBEGIN);
266 }
267 GIN_SUPPORT(timestamp, false, leftmostvalue_timestamp, timestamp_cmp)
268
269 GIN_SUPPORT(timestamptz, false, leftmostvalue_timestamp, timestamp_cmp)
270
271 static Datum
272 leftmostvalue_time(void)
273 {
274         return TimeADTGetDatum(0);
275 }
276 GIN_SUPPORT(time, false, leftmostvalue_time, time_cmp)
277
278 static Datum
279 leftmostvalue_timetz(void)
280 {
281         TimeTzADT  *v = palloc(sizeof(TimeTzADT));
282
283         v->time = 0;
284         v->zone = -24 * 3600;           /* XXX is that true? */
285
286         return TimeTzADTPGetDatum(v);
287 }
288 GIN_SUPPORT(timetz, false, leftmostvalue_timetz, timetz_cmp)
289
290 static Datum
291 leftmostvalue_date(void)
292 {
293         return DateADTGetDatum(DATEVAL_NOBEGIN);
294 }
295 GIN_SUPPORT(date, false, leftmostvalue_date, date_cmp)
296
297 static Datum
298 leftmostvalue_interval(void)
299 {
300         Interval   *v = palloc(sizeof(Interval));
301
302         v->time = DT_NOBEGIN;
303         v->day = 0;
304         v->month = 0;
305         return IntervalPGetDatum(v);
306 }
307 GIN_SUPPORT(interval, false, leftmostvalue_interval, interval_cmp)
308
309 static Datum
310 leftmostvalue_macaddr(void)
311 {
312         macaddr    *v = palloc0(sizeof(macaddr));
313
314         return MacaddrPGetDatum(v);
315 }
316 GIN_SUPPORT(macaddr, false, leftmostvalue_macaddr, macaddr_cmp)
317
318 static Datum
319 leftmostvalue_inet(void)
320 {
321         return DirectFunctionCall1(inet_in, CStringGetDatum("0.0.0.0/0"));
322 }
323 GIN_SUPPORT(inet, true, leftmostvalue_inet, network_cmp)
324
325 GIN_SUPPORT(cidr, true, leftmostvalue_inet, network_cmp)
326
327 static Datum
328 leftmostvalue_text(void)
329 {
330         return PointerGetDatum(cstring_to_text_with_len("", 0));
331 }
332 GIN_SUPPORT(text, true, leftmostvalue_text, bttextcmp)
333
334 static Datum
335 leftmostvalue_char(void)
336 {
337         return CharGetDatum(SCHAR_MIN);
338 }
339 GIN_SUPPORT(char, false, leftmostvalue_char, btcharcmp)
340
341 GIN_SUPPORT(bytea, true, leftmostvalue_text, byteacmp)
342
343 static Datum
344 leftmostvalue_bit(void)
345 {
346         return DirectFunctionCall1(bit_in, CStringGetDatum(""));
347 }
348 GIN_SUPPORT(bit, true, leftmostvalue_bit, bitcmp)
349
350 static Datum
351 leftmostvalue_varbit(void)
352 {
353         return DirectFunctionCall1(varbit_in, CStringGetDatum(""));
354 }
355 GIN_SUPPORT(varbit, true, leftmostvalue_varbit, bitcmp)
356
357 /*
358  * Numeric type hasn't a real left-most value, so we use PointerGetDatum(NULL)
359  * (*not* a SQL NULL) to represent that.  We can get away with that because
360  * the value returned by our leftmostvalue function will never be stored in
361  * the index nor passed to anything except our compare and prefix-comparison
362  * functions.  The same trick could be used for other pass-by-reference types.
363  */
364
365 #define NUMERIC_IS_LEFTMOST(x)  ((x) == NULL)
366
367 PG_FUNCTION_INFO_V1(gin_numeric_cmp);
368
369 Datum
370 gin_numeric_cmp(PG_FUNCTION_ARGS)
371 {
372         Numeric         a = (Numeric) PG_GETARG_POINTER(0);
373         Numeric         b = (Numeric) PG_GETARG_POINTER(1);
374         int                     res = 0;
375
376         if (NUMERIC_IS_LEFTMOST(a))
377         {
378                 res = (NUMERIC_IS_LEFTMOST(b)) ? 0 : -1;
379         }
380         else if (NUMERIC_IS_LEFTMOST(b))
381         {
382                 res = 1;
383         }
384         else
385         {
386                 res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
387                                                                                                 NumericGetDatum(a),
388                                                                                                 NumericGetDatum(b)));
389         }
390
391         PG_RETURN_INT32(res);
392 }
393
394 static Datum
395 leftmostvalue_numeric(void)
396 {
397         return PointerGetDatum(NULL);
398 }
399 GIN_SUPPORT(numeric, true, leftmostvalue_numeric, gin_numeric_cmp)