Commit ba398969 authored by Tom Lane's avatar Tom Lane

Update contrib/hstore for new GIN extractQuery API.

In particular, make hstore @> '' succeed for all hstores, likewise
hstore ?& '{}'.  Previously the results were inconsistent and could
depend on whether you were using a GiST index, GIN index, or seqscan.
parent 327b2576
...@@ -435,7 +435,7 @@ select hstore 'a=>NULL, b=>qq' ?& ARRAY['c','d']; ...@@ -435,7 +435,7 @@ select hstore 'a=>NULL, b=>qq' ?& ARRAY['c','d'];
select hstore 'a=>NULL, b=>qq' ?& '{}'::text[]; select hstore 'a=>NULL, b=>qq' ?& '{}'::text[];
?column? ?column?
---------- ----------
f t
(1 row) (1 row)
-- delete -- delete
......
...@@ -10,6 +10,13 @@ ...@@ -10,6 +10,13 @@
#include "hstore.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 KEYFLAG 'K'
#define VALFLAG 'V' #define VALFLAG 'V'
#define NULLFLAG 'N' #define NULLFLAG 'N'
...@@ -17,14 +24,17 @@ ...@@ -17,14 +24,17 @@
PG_FUNCTION_INFO_V1(gin_extract_hstore); PG_FUNCTION_INFO_V1(gin_extract_hstore);
Datum gin_extract_hstore(PG_FUNCTION_ARGS); Datum gin_extract_hstore(PG_FUNCTION_ARGS);
/* Build an indexable text value */
static text * static text *
makeitem(char *str, int len) makeitem(char *str, int len, char flag)
{ {
text *item; text *item;
item = (text *) palloc(VARHDRSZ + len + 1); item = (text *) palloc(VARHDRSZ + len + 1);
SET_VARSIZE(item, VARHDRSZ + len + 1); SET_VARSIZE(item, VARHDRSZ + len + 1);
*VARDATA(item) = flag;
if (str && len > 0) if (str && len > 0)
memcpy(VARDATA(item) + 1, str, len); memcpy(VARDATA(item) + 1, str, len);
...@@ -50,20 +60,15 @@ gin_extract_hstore(PG_FUNCTION_ARGS) ...@@ -50,20 +60,15 @@ gin_extract_hstore(PG_FUNCTION_ARGS)
{ {
text *item; text *item;
item = makeitem(HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i)); item = makeitem(HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i),
*VARDATA(item) = KEYFLAG; KEYFLAG);
entries[2 * i] = PointerGetDatum(item); entries[2 * i] = PointerGetDatum(item);
if (HS_VALISNULL(hsent, i)) if (HS_VALISNULL(hsent, i))
{ item = makeitem(NULL, 0, NULLFLAG);
item = makeitem(NULL, 0);
*VARDATA(item) = NULLFLAG;
}
else else
{ item = makeitem(HS_VAL(hsent, ptr, i), HS_VALLEN(hsent, i),
item = makeitem(HS_VAL(hsent, ptr, i), HS_VALLEN(hsent, i)); VALFLAG);
*VARDATA(item) = VALFLAG;
}
entries[2 * i + 1] = PointerGetDatum(item); entries[2 * i + 1] = PointerGetDatum(item);
} }
...@@ -76,30 +81,31 @@ Datum gin_extract_hstore_query(PG_FUNCTION_ARGS); ...@@ -76,30 +81,31 @@ Datum gin_extract_hstore_query(PG_FUNCTION_ARGS);
Datum Datum
gin_extract_hstore_query(PG_FUNCTION_ARGS) gin_extract_hstore_query(PG_FUNCTION_ARGS)
{ {
int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
StrategyNumber strategy = PG_GETARG_UINT16(2); StrategyNumber strategy = PG_GETARG_UINT16(2);
int32 *searchMode = (int32 *) PG_GETARG_POINTER(6);
Datum *entries;
if (strategy == HStoreContainsStrategyNumber) if (strategy == HStoreContainsStrategyNumber)
{ {
PG_RETURN_DATUM(DirectFunctionCall2(gin_extract_hstore, /* Query is an hstore, so just apply gin_extract_hstore... */
PG_GETARG_DATUM(0), entries = (Datum *)
PG_GETARG_DATUM(1) 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) else if (strategy == HStoreExistsStrategyNumber)
{ {
text *item, text *query = PG_GETARG_TEXT_PP(0);
*query = PG_GETARG_TEXT_PP(0); text *item;
int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
Datum *entries = NULL;
*nentries = 1; *nentries = 1;
entries = (Datum *) palloc(sizeof(Datum)); entries = (Datum *) palloc(sizeof(Datum));
item = makeitem(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query), KEYFLAG);
item = makeitem(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
*VARDATA(item) = KEYFLAG;
entries[0] = PointerGetDatum(item); entries[0] = PointerGetDatum(item);
PG_RETURN_POINTER(entries);
} }
else if (strategy == HStoreExistsAnyStrategyNumber || else if (strategy == HStoreExistsAnyStrategyNumber ||
strategy == HStoreExistsAllStrategyNumber) strategy == HStoreExistsAllStrategyNumber)
...@@ -110,8 +116,6 @@ gin_extract_hstore_query(PG_FUNCTION_ARGS) ...@@ -110,8 +116,6 @@ gin_extract_hstore_query(PG_FUNCTION_ARGS)
int key_count; int key_count;
int i, int i,
j; j;
int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
Datum *entries = NULL;
text *item; text *item;
deconstruct_array(query, deconstruct_array(query,
...@@ -122,21 +126,25 @@ gin_extract_hstore_query(PG_FUNCTION_ARGS) ...@@ -122,21 +126,25 @@ gin_extract_hstore_query(PG_FUNCTION_ARGS)
for (i = 0, j = 0; i < key_count; ++i) for (i = 0, j = 0; i < key_count; ++i)
{ {
/* Nulls in the array are ignored, cf hstoreArrayToPairs */
if (key_nulls[i]) if (key_nulls[i])
continue; continue;
item = makeitem(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ); item = makeitem(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ, KEYFLAG);
*VARDATA(item) = KEYFLAG;
entries[j++] = PointerGetDatum(item); entries[j++] = PointerGetDatum(item);
} }
*nentries = j ? j : -1; *nentries = j;
/* ExistsAll with no keys should match everything */
PG_RETURN_POINTER(entries); if (j == 0 && strategy == HStoreExistsAllStrategyNumber)
*searchMode = GIN_SEARCH_MODE_ALL;
} }
else else
elog(ERROR, "Unsupported strategy number: %d", strategy); {
elog(ERROR, "unrecognized strategy number: %d", strategy);
entries = NULL; /* keep compiler quiet */
}
PG_RETURN_POINTER(NULL); PG_RETURN_POINTER(entries);
} }
PG_FUNCTION_INFO_V1(gin_consistent_hstore); PG_FUNCTION_INFO_V1(gin_consistent_hstore);
...@@ -154,42 +162,52 @@ gin_consistent_hstore(PG_FUNCTION_ARGS) ...@@ -154,42 +162,52 @@ gin_consistent_hstore(PG_FUNCTION_ARGS)
/* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */ /* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
bool *recheck = (bool *) PG_GETARG_POINTER(5); bool *recheck = (bool *) PG_GETARG_POINTER(5);
bool res = true; bool res = true;
int32 i;
*recheck = false;
if (strategy == HStoreContainsStrategyNumber) if (strategy == HStoreContainsStrategyNumber)
{ {
int i;
/* /*
* Index lost information about correspondence of keys and values, so * Index doesn't have information about correspondence of keys and
* we need recheck (pre-8.4 this is handled at SQL level) * values, so we need recheck. However, if not all the keys are
* present, we can fail at once.
*/ */
*recheck = true; *recheck = true;
for (i = 0; res && i < nkeys; i++) for (i = 0; i < nkeys; i++)
if (check[i] == false) {
if (!check[i])
{
res = false; res = false;
break;
}
}
} }
else if (strategy == HStoreExistsStrategyNumber) else if (strategy == HStoreExistsStrategyNumber)
{ {
/* Existence of key is guaranteed */ /* Existence of key is guaranteed in default search mode */
*recheck = false;
res = true; res = true;
} }
else if (strategy == HStoreExistsAnyStrategyNumber) else if (strategy == HStoreExistsAnyStrategyNumber)
{ {
/* Existence of key is guaranteed */ /* Existence of key is guaranteed in default search mode */
*recheck = false;
res = true; res = true;
} }
else if (strategy == HStoreExistsAllStrategyNumber) else if (strategy == HStoreExistsAllStrategyNumber)
{ {
int i; /* Testing for all the keys being present gives an exact result */
*recheck = false;
for (i = 0; res && i < nkeys; ++i) for (i = 0; i < nkeys; i++)
{
if (!check[i]) if (!check[i])
{
res = false; res = false;
break;
}
}
} }
else else
elog(ERROR, "Unsupported strategy number: %d", strategy); elog(ERROR, "unrecognized strategy number: %d", strategy);
PG_RETURN_BOOL(res); PG_RETURN_BOOL(res);
} }
...@@ -168,14 +168,16 @@ hstore_exists_any(PG_FUNCTION_ARGS) ...@@ -168,14 +168,16 @@ hstore_exists_any(PG_FUNCTION_ARGS)
* start one entry past the previous "found" entry, or at the lower bound * start one entry past the previous "found" entry, or at the lower bound
* of the last search. * of the last search.
*/ */
for (i = 0; i < nkeys; i++)
for (i = 0; !res && i < nkeys; ++i)
{ {
int idx = hstoreFindKey(hs, &lowbound, int idx = hstoreFindKey(hs, &lowbound,
key_pairs[i].key, key_pairs[i].keylen); key_pairs[i].key, key_pairs[i].keylen);
if (idx >= 0) if (idx >= 0)
{
res = true; res = true;
break;
}
} }
PG_RETURN_BOOL(res); PG_RETURN_BOOL(res);
...@@ -193,7 +195,7 @@ hstore_exists_all(PG_FUNCTION_ARGS) ...@@ -193,7 +195,7 @@ hstore_exists_all(PG_FUNCTION_ARGS)
Pairs *key_pairs = hstoreArrayToPairs(keys, &nkeys); Pairs *key_pairs = hstoreArrayToPairs(keys, &nkeys);
int i; int i;
int lowbound = 0; int lowbound = 0;
bool res = nkeys ? true : false; bool res = true;
/* /*
* we exploit the fact that the pairs list is already sorted into strictly * we exploit the fact that the pairs list is already sorted into strictly
...@@ -201,14 +203,16 @@ hstore_exists_all(PG_FUNCTION_ARGS) ...@@ -201,14 +203,16 @@ hstore_exists_all(PG_FUNCTION_ARGS)
* start one entry past the previous "found" entry, or at the lower bound * start one entry past the previous "found" entry, or at the lower bound
* of the last search. * of the last search.
*/ */
for (i = 0; i < nkeys; i++)
for (i = 0; res && i < nkeys; ++i)
{ {
int idx = hstoreFindKey(hs, &lowbound, int idx = hstoreFindKey(hs, &lowbound,
key_pairs[i].key, key_pairs[i].keylen); key_pairs[i].key, key_pairs[i].keylen);
if (idx < 0) if (idx < 0)
{
res = false; res = false;
break;
}
} }
PG_RETURN_BOOL(res); PG_RETURN_BOOL(res);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment