/* * contrib/hstore/hstore_gin.c */ #include "postgres.h" #include "access/gin.h" #include "access/skey.h" #include "catalog/pg_type.h" #include "utils/builtins.h" #include "hstore.h" PG_FUNCTION_INFO_V1(gin_extract_hstore); Datum gin_extract_hstore(PG_FUNCTION_ARGS); /* Build an indexable text value */ static text * makeitem(char *str, int len, char flag) { text *item; item = (text *) palloc(VARHDRSZ + len + 1); SET_VARSIZE(item, VARHDRSZ + len + 1); *VARDATA(item) = flag; if (str && len > 0) memcpy(VARDATA(item) + 1, str, len); return item; } static text * makeitemFromValue(HStoreValue *v, char flag) { text *item; char *cstr; switch(v->type) { case hsvNull: item = makeitem(NULL, 0, NULLFLAG); break; case hsvBool: item = makeitem((v->boolean) ? " t" : " f", 2, flag); break; case hsvNumeric: /* * It's needed to get some text representaion of * numeric independed from locale setting and * preciosion. We use hashed value - it's safe * because recheck flag will be set anyway */ cstr = palloc(8 /* hex numbers */ + 1 /* \0 */); snprintf(cstr, 9, "%08x", DatumGetInt32(DirectFunctionCall1(hash_numeric, NumericGetDatum(v->numeric)))); item = makeitem(cstr, 8, flag); pfree(cstr); break; case hsvString: item = makeitem(v->string.val, v->string.len, flag); break; default: elog(ERROR, "Wrong hstore type"); } return item; } Datum gin_extract_hstore(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); int32 *nentries = (int32 *) PG_GETARG_POINTER(1); Datum *entries = NULL; int total = 2 * HS_ROOT_COUNT(hs); int i = 0, r; HStoreIterator *it; HStoreValue v; if (total == 0) { *nentries = 0; PG_RETURN_POINTER(NULL); } entries = (Datum *) palloc(sizeof(Datum) * total); it = HStoreIteratorInit(VARDATA(hs)); while((r = HStoreIteratorGet(&it, &v, false)) != 0) { if (i >= total) { total *= 2; entries = (Datum *) repalloc(entries, sizeof(Datum) * total); } switch(r) { case WHS_KEY: entries[i++] = PointerGetDatum(makeitemFromValue(&v, KEYFLAG)); break; case WHS_VALUE: entries[i++] = PointerGetDatum(makeitemFromValue(&v, VALFLAG)); break; case WHS_ELEM: entries[i++] = PointerGetDatum(makeitemFromValue(&v, ELEMFLAG)); break; default: break; } } *nentries = i; PG_RETURN_POINTER(entries); } PG_FUNCTION_INFO_V1(gin_extract_hstore_query); Datum gin_extract_hstore_query(PG_FUNCTION_ARGS); Datum gin_extract_hstore_query(PG_FUNCTION_ARGS) { int32 *nentries = (int32 *) PG_GETARG_POINTER(1); StrategyNumber strategy = PG_GETARG_UINT16(2); int32 *searchMode = (int32 *) PG_GETARG_POINTER(6); Datum *entries; if (strategy == HStoreContainsStrategyNumber) { /* Query is an hstore, so just apply gin_extract_hstore... */ entries = (Datum *) DatumGetPointer(DirectFunctionCall2(gin_extract_hstore, PG_GETARG_DATUM(0), PointerGetDatum(nentries))); /* ... except that "contains {}" requires a full index scan */ if (entries == NULL) *searchMode = GIN_SEARCH_MODE_ALL; } else if (strategy == HStoreExistsStrategyNumber) { text *query = PG_GETARG_TEXT_PP(0); text *item; *nentries = 1; entries = (Datum *) palloc(sizeof(Datum)); item = makeitem(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query), KEYFLAG); entries[0] = PointerGetDatum(item); } else if (strategy == HStoreExistsAnyStrategyNumber || strategy == HStoreExistsAllStrategyNumber) { ArrayType *query = PG_GETARG_ARRAYTYPE_P(0); Datum *key_datums; bool *key_nulls; int key_count; int i, j; text *item; deconstruct_array(query, TEXTOID, -1, false, 'i', &key_datums, &key_nulls, &key_count); entries = (Datum *) palloc(sizeof(Datum) * key_count); for (i = 0, j = 0; i < key_count; ++i) { /* Nulls in the array are ignored, cf hstoreArrayToPairs */ if (key_nulls[i]) continue; item = makeitem(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ, KEYFLAG); entries[j++] = PointerGetDatum(item); } *nentries = j; /* ExistsAll with no keys should match everything */ if (j == 0 && strategy == HStoreExistsAllStrategyNumber) *searchMode = GIN_SEARCH_MODE_ALL; } else { elog(ERROR, "unrecognized strategy number: %d", strategy); entries = NULL; /* keep compiler quiet */ } PG_RETURN_POINTER(entries); } PG_FUNCTION_INFO_V1(gin_consistent_hstore); Datum gin_consistent_hstore(PG_FUNCTION_ARGS); Datum gin_consistent_hstore(PG_FUNCTION_ARGS) { bool *check = (bool *) PG_GETARG_POINTER(0); StrategyNumber strategy = PG_GETARG_UINT16(1); /* HStore *query = PG_GETARG_HS(2); */ int32 nkeys = PG_GETARG_INT32(3); /* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */ bool *recheck = (bool *) PG_GETARG_POINTER(5); bool res = true; int32 i; if (strategy == HStoreContainsStrategyNumber) { /* * Index doesn't have information about correspondence of keys and * values, so we need recheck. However, if not all the keys are * present, we can fail at once. */ *recheck = true; for (i = 0; i < nkeys; i++) { if (!check[i]) { res = false; break; } } } else if (strategy == HStoreExistsStrategyNumber) { /* Existence of key is guaranteed in default search mode */ *recheck = false; res = true; } else if (strategy == HStoreExistsAnyStrategyNumber) { /* Existence of key is guaranteed in default search mode */ *recheck = false; res = true; } else if (strategy == HStoreExistsAllStrategyNumber) { /* Testing for all the keys being present gives an exact result */ *recheck = false; for (i = 0; i < nkeys; i++) { if (!check[i]) { res = false; break; } } } else elog(ERROR, "unrecognized strategy number: %d", strategy); PG_RETURN_BOOL(res); } PG_FUNCTION_INFO_V1(gin_consistent_hstore_hash); Datum gin_consistent_hstore_hash(PG_FUNCTION_ARGS); Datum gin_consistent_hstore_hash(PG_FUNCTION_ARGS) { bool *check = (bool *) PG_GETARG_POINTER(0); StrategyNumber strategy = PG_GETARG_UINT16(1); /* HStore *query = PG_GETARG_HS(2); */ int32 nkeys = PG_GETARG_INT32(3); /* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */ bool *recheck = (bool *) PG_GETARG_POINTER(5); bool res = true; int32 i; if (strategy == HStoreContainsStrategyNumber) { /* * Index doesn't have information about correspondence of keys and * values, so we need recheck. However, if not all the keys are * present, we can fail at once. */ *recheck = true; for (i = 0; i < nkeys; i++) { if (!check[i]) { res = false; break; } } } else elog(ERROR, "unrecognized strategy number: %d", strategy); PG_RETURN_BOOL(res); } PG_FUNCTION_INFO_V1(gin_extract_hstore_hash); Datum gin_extract_hstore_hash(PG_FUNCTION_ARGS); typedef struct PathHashStack { pg_crc32 hash_state; struct PathHashStack *next; } PathHashStack; #define PATH_SEPARATOR ("\0") static void hash_value(HStoreValue *v, PathHashStack *stack) { switch(v->type) { case hsvNull: COMP_CRC32(stack->hash_state, "NULL", 5 /* include trailing \0 */); break; case hsvBool: COMP_CRC32(stack->hash_state, (v->boolean) ? " t" : " f", 2 /* include trailing \0 */); break; case hsvNumeric: stack->hash_state ^= DatumGetInt32(DirectFunctionCall1(hash_numeric, NumericGetDatum(v->numeric))); break; case hsvString: COMP_CRC32(stack->hash_state, v->string.val, v->string.len); break; default: elog(ERROR, "Shouldn't take hash of array"); break; } } Datum gin_extract_hstore_hash(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); int32 *nentries = (int32 *) PG_GETARG_POINTER(1); Datum *entries = NULL; int total = 2 * HS_ROOT_COUNT(hs); int i = 0, r; HStoreIterator *it; HStoreValue v; PathHashStack tail; PathHashStack *stack, *tmp; pg_crc32 path_crc32; if (total == 0) { *nentries = 0; PG_RETURN_POINTER(NULL); } entries = (Datum *) palloc(sizeof(Datum) * total); it = HStoreIteratorInit(VARDATA(hs)); tail.next = NULL; INIT_CRC32(tail.hash_state); stack = &tail; /* * Calculate hashes of all key_1.key_2. ... .key_n.value paths as entries. * Order of array elements doesn't matter so array keys are empty in path. * For faster calculation of hashes use stack for precalculated hashes * of prefixes. */ while((r = HStoreIteratorGet(&it, &v, false)) != 0) { if (i >= total) { total *= 2; entries = (Datum *) repalloc(entries, sizeof(Datum) * total); } switch(r) { case WHS_BEGIN_ARRAY: tmp = stack; stack = (PathHashStack *)palloc(sizeof(PathHashStack)); stack->next = tmp; stack->hash_state = tmp->hash_state; COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1); break; case WHS_BEGIN_HASH: /* Preserve stack item for key */ tmp = stack; stack = (PathHashStack *)palloc(sizeof(PathHashStack)); stack->next = tmp; break; case WHS_KEY: /* Calc hash of key and separated into preserved stack item */ stack->hash_state = stack->next->hash_state; hash_value(&v, stack); COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1); break; case WHS_VALUE: case WHS_ELEM: path_crc32 = stack->hash_state; hash_value(&v, stack); FIN_CRC32(path_crc32); entries[i++] = path_crc32; break; case WHS_END_ARRAY: case WHS_END_HASH: /* Pop stack item */ tmp = stack->next; pfree(stack); stack = tmp; break; default: break; } } *nentries = i; PG_RETURN_POINTER(entries); } PG_FUNCTION_INFO_V1(gin_extract_hstore_hash_query); Datum gin_extract_hstore_hash_query(PG_FUNCTION_ARGS); Datum gin_extract_hstore_hash_query(PG_FUNCTION_ARGS) { int32 *nentries = (int32 *) PG_GETARG_POINTER(1); StrategyNumber strategy = PG_GETARG_UINT16(2); int32 *searchMode = (int32 *) PG_GETARG_POINTER(6); Datum *entries; if (strategy == HStoreContainsStrategyNumber) { /* Query is an hstore, so just apply gin_extract_hstore... */ entries = (Datum *) DatumGetPointer(DirectFunctionCall2(gin_extract_hstore_hash, PG_GETARG_DATUM(0), PointerGetDatum(nentries))); /* ... except that "contains {}" requires a full index scan */ if (entries == NULL) *searchMode = GIN_SEARCH_MODE_ALL; } else { elog(ERROR, "unrecognized strategy number: %d", strategy); entries = NULL; /* keep compiler quiet */ } PG_RETURN_POINTER(entries); }