diff options
Diffstat (limited to 'contrib/hstore/hstore_gin.c')
-rw-r--r-- | contrib/hstore/hstore_gin.c | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/contrib/hstore/hstore_gin.c b/contrib/hstore/hstore_gin.c new file mode 100644 index 0000000..9085302 --- /dev/null +++ b/contrib/hstore/hstore_gin.c @@ -0,0 +1,212 @@ +/* + * contrib/hstore/hstore_gin.c + */ +#include "postgres.h" + +#include "access/gin.h" +#include "access/stratnum.h" +#include "catalog/pg_type.h" + +#include "hstore.h" + + +/* + * When using a GIN index for hstore, we choose to index both keys and values. + * The storage format is "text" values, with K, V, or N prepended to the string + * to indicate key, value, or null values. (As of 9.1 it might be better to + * store null values as nulls, but we'll keep it this way for on-disk + * compatibility.) + */ +#define KEYFLAG 'K' +#define VALFLAG 'V' +#define NULLFLAG 'N' + +PG_FUNCTION_INFO_V1(gin_extract_hstore); + +/* 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; +} + +Datum +gin_extract_hstore(PG_FUNCTION_ARGS) +{ + HStore *hs = PG_GETARG_HSTORE_P(0); + int32 *nentries = (int32 *) PG_GETARG_POINTER(1); + Datum *entries = NULL; + HEntry *hsent = ARRPTR(hs); + char *ptr = STRPTR(hs); + int count = HS_COUNT(hs); + int i; + + *nentries = 2 * count; + if (count) + entries = (Datum *) palloc(sizeof(Datum) * 2 * count); + + for (i = 0; i < count; ++i) + { + text *item; + + item = makeitem(HSTORE_KEY(hsent, ptr, i), + HSTORE_KEYLEN(hsent, i), + KEYFLAG); + entries[2 * i] = PointerGetDatum(item); + + if (HSTORE_VALISNULL(hsent, i)) + item = makeitem(NULL, 0, NULLFLAG); + else + item = makeitem(HSTORE_VAL(hsent, ptr, i), + HSTORE_VALLEN(hsent, i), + VALFLAG); + entries[2 * i + 1] = PointerGetDatum(item); + } + + PG_RETURN_POINTER(entries); +} + +PG_FUNCTION_INFO_V1(gin_extract_hstore_query); + +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, TYPALIGN_INT, + &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) +{ + bool *check = (bool *) PG_GETARG_POINTER(0); + StrategyNumber strategy = PG_GETARG_UINT16(1); + + /* HStore *query = PG_GETARG_HSTORE_P(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); +} |