2 * contrib/hstore/hstore_gin.c
6 #include "access/gin.h"
7 #include "access/skey.h"
8 #include "catalog/pg_type.h"
9 #include "utils/builtins.h"
14 PG_FUNCTION_INFO_V1(gin_extract_hstore);
15 Datum gin_extract_hstore(PG_FUNCTION_ARGS);
17 /* Build an indexable text value */
19 makeitem(char *str, int len, char flag)
23 item = (text *) palloc(VARHDRSZ + len + 1);
24 SET_VARSIZE(item, VARHDRSZ + len + 1);
26 *VARDATA(item) = flag;
29 memcpy(VARDATA(item) + 1, str, len);
35 makeitemFromValue(HStoreValue *v, char flag)
43 item = makeitem(NULL, 0, NULLFLAG);
46 item = makeitem((v->boolean) ? " t" : " f", 2, flag);
50 * It's needed to get some text representaion of
51 * numeric independed from locale setting and
52 * preciosion. We use hashed value - it's safe
53 * because recheck flag will be set anyway
55 cstr = palloc(8 /* hex numbers */ + 1 /* \0 */);
56 snprintf(cstr, 9, "%08x", DatumGetInt32(DirectFunctionCall1(hash_numeric,
57 NumericGetDatum(v->numeric))));
58 item = makeitem(cstr, 8, flag);
62 item = makeitem(v->string.val, v->string.len, flag);
65 elog(ERROR, "Wrong hstore type");
73 gin_extract_hstore(PG_FUNCTION_ARGS)
75 HStore *hs = PG_GETARG_HS(0);
76 int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
77 Datum *entries = NULL;
78 int total = 2 * HS_ROOT_COUNT(hs);
86 PG_RETURN_POINTER(NULL);
89 entries = (Datum *) palloc(sizeof(Datum) * total);
91 it = HStoreIteratorInit(VARDATA(hs));
93 while((r = HStoreIteratorGet(&it, &v, false)) != 0)
98 entries = (Datum *) repalloc(entries, sizeof(Datum) * total);
104 entries[i++] = PointerGetDatum(makeitemFromValue(&v, KEYFLAG));
107 entries[i++] = PointerGetDatum(makeitemFromValue(&v, VALFLAG));
110 entries[i++] = PointerGetDatum(makeitemFromValue(&v, ELEMFLAG));
119 PG_RETURN_POINTER(entries);
122 PG_FUNCTION_INFO_V1(gin_extract_hstore_query);
123 Datum gin_extract_hstore_query(PG_FUNCTION_ARGS);
126 gin_extract_hstore_query(PG_FUNCTION_ARGS)
128 int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
129 StrategyNumber strategy = PG_GETARG_UINT16(2);
130 int32 *searchMode = (int32 *) PG_GETARG_POINTER(6);
133 if (strategy == HStoreContainsStrategyNumber)
135 /* Query is an hstore, so just apply gin_extract_hstore... */
137 DatumGetPointer(DirectFunctionCall2(gin_extract_hstore,
139 PointerGetDatum(nentries)));
140 /* ... except that "contains {}" requires a full index scan */
142 *searchMode = GIN_SEARCH_MODE_ALL;
144 else if (strategy == HStoreExistsStrategyNumber)
146 text *query = PG_GETARG_TEXT_PP(0);
150 entries = (Datum *) palloc(sizeof(Datum));
151 item = makeitem(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query), KEYFLAG);
152 entries[0] = PointerGetDatum(item);
154 else if (strategy == HStoreExistsAnyStrategyNumber ||
155 strategy == HStoreExistsAllStrategyNumber)
157 ArrayType *query = PG_GETARG_ARRAYTYPE_P(0);
165 deconstruct_array(query,
166 TEXTOID, -1, false, 'i',
167 &key_datums, &key_nulls, &key_count);
169 entries = (Datum *) palloc(sizeof(Datum) * key_count);
171 for (i = 0, j = 0; i < key_count; ++i)
173 /* Nulls in the array are ignored, cf hstoreArrayToPairs */
176 item = makeitem(VARDATA(key_datums[i]),
177 VARSIZE(key_datums[i]) - VARHDRSZ, KEYFLAG);
178 entries[j++] = PointerGetDatum(item);
182 /* ExistsAll with no keys should match everything */
183 if (j == 0 && strategy == HStoreExistsAllStrategyNumber)
184 *searchMode = GIN_SEARCH_MODE_ALL;
188 elog(ERROR, "unrecognized strategy number: %d", strategy);
189 entries = NULL; /* keep compiler quiet */
192 PG_RETURN_POINTER(entries);
195 PG_FUNCTION_INFO_V1(gin_consistent_hstore);
196 Datum gin_consistent_hstore(PG_FUNCTION_ARGS);
199 gin_consistent_hstore(PG_FUNCTION_ARGS)
201 bool *check = (bool *) PG_GETARG_POINTER(0);
202 StrategyNumber strategy = PG_GETARG_UINT16(1);
204 /* HStore *query = PG_GETARG_HS(2); */
205 int32 nkeys = PG_GETARG_INT32(3);
207 /* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
208 bool *recheck = (bool *) PG_GETARG_POINTER(5);
212 if (strategy == HStoreContainsStrategyNumber)
215 * Index doesn't have information about correspondence of keys and
216 * values, so we need recheck. However, if not all the keys are
217 * present, we can fail at once.
220 for (i = 0; i < nkeys; i++)
229 else if (strategy == HStoreExistsStrategyNumber)
231 /* Existence of key is guaranteed in default search mode */
235 else if (strategy == HStoreExistsAnyStrategyNumber)
237 /* Existence of key is guaranteed in default search mode */
241 else if (strategy == HStoreExistsAllStrategyNumber)
243 /* Testing for all the keys being present gives an exact result */
245 for (i = 0; i < nkeys; i++)
255 elog(ERROR, "unrecognized strategy number: %d", strategy);
260 PG_FUNCTION_INFO_V1(gin_consistent_hstore_hash);
261 Datum gin_consistent_hstore_hash(PG_FUNCTION_ARGS);
264 gin_consistent_hstore_hash(PG_FUNCTION_ARGS)
266 bool *check = (bool *) PG_GETARG_POINTER(0);
267 StrategyNumber strategy = PG_GETARG_UINT16(1);
269 /* HStore *query = PG_GETARG_HS(2); */
270 int32 nkeys = PG_GETARG_INT32(3);
272 /* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
273 bool *recheck = (bool *) PG_GETARG_POINTER(5);
277 if (strategy == HStoreContainsStrategyNumber)
280 * Index doesn't have information about correspondence of keys and
281 * values, so we need recheck. However, if not all the keys are
282 * present, we can fail at once.
285 for (i = 0; i < nkeys; i++)
295 elog(ERROR, "unrecognized strategy number: %d", strategy);
300 PG_FUNCTION_INFO_V1(gin_extract_hstore_hash);
301 Datum gin_extract_hstore_hash(PG_FUNCTION_ARGS);
303 typedef struct PathHashStack
306 struct PathHashStack *next;
309 #define PATH_SEPARATOR ("\0")
312 hash_value(HStoreValue *v, PathHashStack *stack)
317 COMP_CRC32(stack->hash_state, "NULL", 5 /* include trailing \0 */);
320 COMP_CRC32(stack->hash_state, (v->boolean) ? " t" : " f", 2 /* include trailing \0 */);
323 stack->hash_state ^= DatumGetInt32(DirectFunctionCall1(hash_numeric,
324 NumericGetDatum(v->numeric)));
327 COMP_CRC32(stack->hash_state, v->string.val, v->string.len);
330 elog(ERROR, "Shouldn't take hash of array");
336 gin_extract_hstore_hash(PG_FUNCTION_ARGS)
338 HStore *hs = PG_GETARG_HS(0);
339 int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
340 Datum *entries = NULL;
341 int total = 2 * HS_ROOT_COUNT(hs);
346 PathHashStack *stack, *tmp;
352 PG_RETURN_POINTER(NULL);
355 entries = (Datum *) palloc(sizeof(Datum) * total);
357 it = HStoreIteratorInit(VARDATA(hs));
360 INIT_CRC32(tail.hash_state);
364 * Calculate hashes of all key_1.key_2. ... .key_n.value paths as entries.
365 * Order of array elements doesn't matter so array keys are empty in path.
366 * For faster calculation of hashes use stack for precalculated hashes
369 while((r = HStoreIteratorGet(&it, &v, false)) != 0)
374 entries = (Datum *) repalloc(entries, sizeof(Datum) * total);
379 case WHS_BEGIN_ARRAY:
381 stack = (PathHashStack *)palloc(sizeof(PathHashStack));
383 stack->hash_state = tmp->hash_state;
384 COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1);
387 /* Preserve stack item for key */
389 stack = (PathHashStack *)palloc(sizeof(PathHashStack));
393 /* Calc hash of key and separated into preserved stack item */
394 stack->hash_state = stack->next->hash_state;
395 hash_value(&v, stack);
396 COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1);
400 path_crc32 = stack->hash_state;
401 hash_value(&v, stack);
402 FIN_CRC32(path_crc32);
403 entries[i++] = path_crc32;
419 PG_RETURN_POINTER(entries);
422 PG_FUNCTION_INFO_V1(gin_extract_hstore_hash_query);
423 Datum gin_extract_hstore_hash_query(PG_FUNCTION_ARGS);
426 gin_extract_hstore_hash_query(PG_FUNCTION_ARGS)
428 int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
429 StrategyNumber strategy = PG_GETARG_UINT16(2);
430 int32 *searchMode = (int32 *) PG_GETARG_POINTER(6);
433 if (strategy == HStoreContainsStrategyNumber)
435 /* Query is an hstore, so just apply gin_extract_hstore... */
437 DatumGetPointer(DirectFunctionCall2(gin_extract_hstore_hash,
439 PointerGetDatum(nentries)));
440 /* ... except that "contains {}" requires a full index scan */
442 *searchMode = GIN_SEARCH_MODE_ALL;
446 elog(ERROR, "unrecognized strategy number: %d", strategy);
447 entries = NULL; /* keep compiler quiet */
450 PG_RETURN_POINTER(entries);