summaryrefslogtreecommitdiffstats
path: root/contrib/hstore/hstore_gin.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:19:15 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:19:15 +0000
commit6eb9c5a5657d1fe77b55cc261450f3538d35a94d (patch)
tree657d8194422a5daccecfd42d654b8a245ef7b4c8 /contrib/hstore/hstore_gin.c
parentInitial commit. (diff)
downloadpostgresql-13-6eb9c5a5657d1fe77b55cc261450f3538d35a94d.tar.xz
postgresql-13-6eb9c5a5657d1fe77b55cc261450f3538d35a94d.zip
Adding upstream version 13.4.upstream/13.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/hstore/hstore_gin.c')
-rw-r--r--contrib/hstore/hstore_gin.c212
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);
+}