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