1/*-------------------------------------------------------------------------
2 *
3 * ginarrayproc.c
4 * support functions for GIN's indexing of any array
5 *
6 *
7 * Portions Copyright (c) 1996-2019, 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 */
32Datum
33ginarrayextract(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 */
67Datum
68ginarrayextract_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 */
78Datum
79ginqueryarrayextract(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 */
141Datum
142ginarrayconsistent(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 */
225Datum
226ginarraytriconsistent(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