1 /*-------------------------------------------------------------------------
2  *
3  * ginarrayproc.c
4  *	  support functions for GIN's indexing of any array
5  *
6  *
7  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  * IDENTIFICATION
11  *	  src/backend/access/gin/ginarrayproc.c
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15 
16 #include "access/gin.h"
17 #include "access/stratnum.h"
18 #include "utils/array.h"
19 #include "utils/builtins.h"
20 #include "utils/lsyscache.h"
21 
22 
23 #define GinOverlapStrategy		1
24 #define GinContainsStrategy		2
25 #define GinContainedStrategy	3
26 #define GinEqualStrategy		4
27 
28 
29 /*
30  * extractValue support function
31  */
32 Datum
ginarrayextract(PG_FUNCTION_ARGS)33 ginarrayextract(PG_FUNCTION_ARGS)
34 {
35 	/* Make copy of array input to ensure it doesn't disappear while in use */
36 	ArrayType  *array = PG_GETARG_ARRAYTYPE_P_COPY(0);
37 	int32	   *nkeys = (int32 *) PG_GETARG_POINTER(1);
38 	bool	  **nullFlags = (bool **) PG_GETARG_POINTER(2);
39 	int16		elmlen;
40 	bool		elmbyval;
41 	char		elmalign;
42 	Datum	   *elems;
43 	bool	   *nulls;
44 	int			nelems;
45 
46 	get_typlenbyvalalign(ARR_ELEMTYPE(array),
47 						 &elmlen, &elmbyval, &elmalign);
48 
49 	deconstruct_array(array,
50 					  ARR_ELEMTYPE(array),
51 					  elmlen, elmbyval, elmalign,
52 					  &elems, &nulls, &nelems);
53 
54 	*nkeys = nelems;
55 	*nullFlags = nulls;
56 
57 	/* we should not free array, elems[i] points into it */
58 	PG_RETURN_POINTER(elems);
59 }
60 
61 /*
62  * Formerly, ginarrayextract had only two arguments.  Now it has three,
63  * but we still need a pg_proc entry with two args to support reloading
64  * pre-9.1 contrib/intarray opclass declarations.  This compatibility
65  * function should go away eventually.
66  */
67 Datum
ginarrayextract_2args(PG_FUNCTION_ARGS)68 ginarrayextract_2args(PG_FUNCTION_ARGS)
69 {
70 	if (PG_NARGS() < 3)			/* should not happen */
71 		elog(ERROR, "ginarrayextract requires three arguments");
72 	return ginarrayextract(fcinfo);
73 }
74 
75 /*
76  * extractQuery support function
77  */
78 Datum
ginqueryarrayextract(PG_FUNCTION_ARGS)79 ginqueryarrayextract(PG_FUNCTION_ARGS)
80 {
81 	/* Make copy of array input to ensure it doesn't disappear while in use */
82 	ArrayType  *array = PG_GETARG_ARRAYTYPE_P_COPY(0);
83 	int32	   *nkeys = (int32 *) PG_GETARG_POINTER(1);
84 	StrategyNumber strategy = PG_GETARG_UINT16(2);
85 
86 	/* bool   **pmatch = (bool **) PG_GETARG_POINTER(3); */
87 	/* Pointer	   *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
88 	bool	  **nullFlags = (bool **) PG_GETARG_POINTER(5);
89 	int32	   *searchMode = (int32 *) PG_GETARG_POINTER(6);
90 	int16		elmlen;
91 	bool		elmbyval;
92 	char		elmalign;
93 	Datum	   *elems;
94 	bool	   *nulls;
95 	int			nelems;
96 
97 	get_typlenbyvalalign(ARR_ELEMTYPE(array),
98 						 &elmlen, &elmbyval, &elmalign);
99 
100 	deconstruct_array(array,
101 					  ARR_ELEMTYPE(array),
102 					  elmlen, elmbyval, elmalign,
103 					  &elems, &nulls, &nelems);
104 
105 	*nkeys = nelems;
106 	*nullFlags = nulls;
107 
108 	switch (strategy)
109 	{
110 		case GinOverlapStrategy:
111 			*searchMode = GIN_SEARCH_MODE_DEFAULT;
112 			break;
113 		case GinContainsStrategy:
114 			if (nelems > 0)
115 				*searchMode = GIN_SEARCH_MODE_DEFAULT;
116 			else				/* everything contains the empty set */
117 				*searchMode = GIN_SEARCH_MODE_ALL;
118 			break;
119 		case GinContainedStrategy:
120 			/* empty set is contained in everything */
121 			*searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
122 			break;
123 		case GinEqualStrategy:
124 			if (nelems > 0)
125 				*searchMode = GIN_SEARCH_MODE_DEFAULT;
126 			else
127 				*searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
128 			break;
129 		default:
130 			elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
131 				 strategy);
132 	}
133 
134 	/* we should not free array, elems[i] points into it */
135 	PG_RETURN_POINTER(elems);
136 }
137 
138 /*
139  * consistent support function
140  */
141 Datum
ginarrayconsistent(PG_FUNCTION_ARGS)142 ginarrayconsistent(PG_FUNCTION_ARGS)
143 {
144 	bool	   *check = (bool *) PG_GETARG_POINTER(0);
145 	StrategyNumber strategy = PG_GETARG_UINT16(1);
146 
147 	/* ArrayType  *query = PG_GETARG_ARRAYTYPE_P(2); */
148 	int32		nkeys = PG_GETARG_INT32(3);
149 
150 	/* Pointer	   *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
151 	bool	   *recheck = (bool *) PG_GETARG_POINTER(5);
152 
153 	/* Datum	   *queryKeys = (Datum *) PG_GETARG_POINTER(6); */
154 	bool	   *nullFlags = (bool *) PG_GETARG_POINTER(7);
155 	bool		res;
156 	int32		i;
157 
158 	switch (strategy)
159 	{
160 		case GinOverlapStrategy:
161 			/* result is not lossy */
162 			*recheck = false;
163 			/* must have a match for at least one non-null element */
164 			res = false;
165 			for (i = 0; i < nkeys; i++)
166 			{
167 				if (check[i] && !nullFlags[i])
168 				{
169 					res = true;
170 					break;
171 				}
172 			}
173 			break;
174 		case GinContainsStrategy:
175 			/* result is not lossy */
176 			*recheck = false;
177 			/* must have all elements in check[] true, and no nulls */
178 			res = true;
179 			for (i = 0; i < nkeys; i++)
180 			{
181 				if (!check[i] || nullFlags[i])
182 				{
183 					res = false;
184 					break;
185 				}
186 			}
187 			break;
188 		case GinContainedStrategy:
189 			/* we will need recheck */
190 			*recheck = true;
191 			/* can't do anything else useful here */
192 			res = true;
193 			break;
194 		case GinEqualStrategy:
195 			/* we will need recheck */
196 			*recheck = true;
197 
198 			/*
199 			 * Must have all elements in check[] true; no discrimination
200 			 * against nulls here.  This is because array_contain_compare and
201 			 * array_eq handle nulls differently ...
202 			 */
203 			res = true;
204 			for (i = 0; i < nkeys; i++)
205 			{
206 				if (!check[i])
207 				{
208 					res = false;
209 					break;
210 				}
211 			}
212 			break;
213 		default:
214 			elog(ERROR, "ginarrayconsistent: unknown strategy number: %d",
215 				 strategy);
216 			res = false;
217 	}
218 
219 	PG_RETURN_BOOL(res);
220 }
221 
222 /*
223  * triconsistent support function
224  */
225 Datum
ginarraytriconsistent(PG_FUNCTION_ARGS)226 ginarraytriconsistent(PG_FUNCTION_ARGS)
227 {
228 	GinTernaryValue *check = (GinTernaryValue *) PG_GETARG_POINTER(0);
229 	StrategyNumber strategy = PG_GETARG_UINT16(1);
230 
231 	/* ArrayType  *query = PG_GETARG_ARRAYTYPE_P(2); */
232 	int32		nkeys = PG_GETARG_INT32(3);
233 
234 	/* Pointer	   *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
235 	/* Datum	   *queryKeys = (Datum *) PG_GETARG_POINTER(5); */
236 	bool	   *nullFlags = (bool *) PG_GETARG_POINTER(6);
237 	GinTernaryValue res;
238 	int32		i;
239 
240 	switch (strategy)
241 	{
242 		case GinOverlapStrategy:
243 			/* must have a match for at least one non-null element */
244 			res = GIN_FALSE;
245 			for (i = 0; i < nkeys; i++)
246 			{
247 				if (!nullFlags[i])
248 				{
249 					if (check[i] == GIN_TRUE)
250 					{
251 						res = GIN_TRUE;
252 						break;
253 					}
254 					else if (check[i] == GIN_MAYBE && res == GIN_FALSE)
255 					{
256 						res = GIN_MAYBE;
257 					}
258 				}
259 			}
260 			break;
261 		case GinContainsStrategy:
262 			/* must have all elements in check[] true, and no nulls */
263 			res = GIN_TRUE;
264 			for (i = 0; i < nkeys; i++)
265 			{
266 				if (check[i] == GIN_FALSE || nullFlags[i])
267 				{
268 					res = GIN_FALSE;
269 					break;
270 				}
271 				if (check[i] == GIN_MAYBE)
272 				{
273 					res = GIN_MAYBE;
274 				}
275 			}
276 			break;
277 		case GinContainedStrategy:
278 			/* can't do anything else useful here */
279 			res = GIN_MAYBE;
280 			break;
281 		case GinEqualStrategy:
282 
283 			/*
284 			 * Must have all elements in check[] true; no discrimination
285 			 * against nulls here.  This is because array_contain_compare and
286 			 * array_eq handle nulls differently ...
287 			 */
288 			res = GIN_MAYBE;
289 			for (i = 0; i < nkeys; i++)
290 			{
291 				if (check[i] == GIN_FALSE)
292 				{
293 					res = GIN_FALSE;
294 					break;
295 				}
296 			}
297 			break;
298 		default:
299 			elog(ERROR, "ginarrayconsistent: unknown strategy number: %d",
300 				 strategy);
301 			res = false;
302 	}
303 
304 	PG_RETURN_GIN_TERNARY_VALUE(res);
305 }
306