-#include "hstore.h"
-#include "utils/array.h"
+/*
+ * contrib/hstore/hstore_op.c
+ */
+#include "postgres.h"
+
+#include "access/hash.h"
+#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
-#include <access/heapam.h>
-#include <fmgr.h>
+#include "utils/builtins.h"
+#include "utils/memutils.h"
+#include "utils/pg_crc.h"
+
+#include "hstore.h"
+
+#ifndef SRF_RETURN_NEXT_NULL
+#define SRF_RETURN_NEXT_NULL(_funcctx) \
+ do { \
+ ReturnSetInfo *rsi; \
+ (_funcctx)->call_cntr++; \
+ rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
+ rsi->isDone = ExprMultipleResult; \
+ PG_RETURN_NULL(); \
+ } while (0)
+#endif
+
+/* old names for C functions */
+HSTORE_POLLUTE(hstore_fetchval, fetchval);
+HSTORE_POLLUTE(hstore_exists, exists);
+HSTORE_POLLUTE(hstore_defined, defined);
+HSTORE_POLLUTE(hstore_delete, delete);
+HSTORE_POLLUTE(hstore_concat, hs_concat);
+HSTORE_POLLUTE(hstore_contains, hs_contains);
+HSTORE_POLLUTE(hstore_contained, hs_contained);
+HSTORE_POLLUTE(hstore_akeys, akeys);
+HSTORE_POLLUTE(hstore_avals, avals);
+HSTORE_POLLUTE(hstore_skeys, skeys);
+HSTORE_POLLUTE(hstore_svals, svals);
+HSTORE_POLLUTE(hstore_each, each);
+
+static HStoreValue*
+arrayToHStoreSortedArray(ArrayType *a)
+{
+ Datum *key_datums;
+ bool *key_nulls;
+ int key_count;
+ HStoreValue *v;
+ int i,
+ j;
+ bool hasNonUniq = false;
+
+ deconstruct_array(a,
+ TEXTOID, -1, false, 'i',
+ &key_datums, &key_nulls, &key_count);
+
+ if (key_count == 0)
+ return NULL;
+
+ /*
+ * A text array uses at least eight bytes per element, so any overflow in
+ * "key_count * sizeof(Pairs)" is small enough for palloc() to catch.
+ * However, credible improvements to the array format could invalidate
+ * that assumption. Therefore, use an explicit check rather than relying
+ * on palloc() to complain.
+ */
+ if (key_count > MaxAllocSize / sizeof(HStorePair))
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
+ key_count, (int) (MaxAllocSize / sizeof(HStorePair)))));
+
+ v = palloc(sizeof(*v));
+ v->type = hsvArray;
+ v->array.scalar = false;
+
+ v->array.elems = palloc(sizeof(*v->hash.pairs) * key_count);
+
+ for (i = 0, j = 0; i < key_count; i++)
+ {
+ if (!key_nulls[i])
+ {
+ v->array.elems[j].type = hsvString;
+ v->array.elems[j].string.val = VARDATA(key_datums[i]);
+ v->array.elems[j].string.len = VARSIZE(key_datums[i]) - VARHDRSZ;
+ j++;
+ }
+ }
+ v->array.nelems = j;
+
+ if (v->array.nelems > 1)
+ qsort_arg(v->array.elems, v->array.nelems, sizeof(*v->array.elems),
+ compareHStoreStringValue, &hasNonUniq);
+
+ if (hasNonUniq)
+ {
+ HStoreValue *ptr = v->array.elems + 1,
+ *res = v->array.elems;
+
+ while (ptr - v->array.elems < v->array.nelems)
+ {
+ if (!(ptr->string.len == res->string.len &&
+ memcmp(ptr->string.val, res->string.val, ptr->string.len) == 0))
+ {
+ res++;
+ *res = *ptr;
+ }
+
+ ptr++;
+ }
+
+ v->array.nelems = res + 1 - v->array.elems;
+ }
+
+ return v;
+}
+
+static HStoreValue*
+findInHStoreSortedArray(HStoreValue *a, uint32 *lowbound,
+ char *key, uint32 keylen)
+{
+ HStoreValue *stopLow = a->array.elems + ((lowbound) ? *lowbound : 0),
+ *stopHigh = a->array.elems + a->array.nelems,
+ *stopMiddle;
+
+ while (stopLow < stopHigh)
+ {
+ int diff;
+
+ stopMiddle = stopLow + (stopHigh - stopLow) / 2;
+
+ if (keylen == stopMiddle->string.len)
+ diff = memcmp(stopMiddle->string.val, key, keylen);
+ else
+ diff = (stopMiddle->string.len > keylen) ? 1 : -1;
+
+ if (diff == 0)
+ {
+ if (lowbound)
+ *lowbound = (stopMiddle - a->array.elems) + 1;
+ return stopMiddle;
+ }
+ else if (diff < 0)
+ {
+ stopLow = stopMiddle + 1;
+ }
+ else
+ {
+ stopHigh = stopMiddle;
+ }
+ }
+
+ if (lowbound)
+ *lowbound = (stopLow - a->array.elems) + 1;
+
+ return NULL;
+}
+
+PG_FUNCTION_INFO_V1(hstore_fetchval);
+Datum hstore_fetchval(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ text *key = PG_GETARG_TEXT_PP(1);
+ HStoreValue *v = NULL;
+ text *out;
+
+ if (!HS_ISEMPTY(hs))
+ v = findUncompressedHStoreValue(VARDATA(hs),
+ HS_FLAG_HSTORE | HS_FLAG_ARRAY,
+ NULL,
+ VARDATA_ANY(key),
+ VARSIZE_ANY_EXHDR(key));
+
+ if ((out = HStoreValueToText(v)) == NULL)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_TEXT_P(out);
+}
+
+PG_FUNCTION_INFO_V1(hstore_fetchval_numeric);
+Datum hstore_fetchval_numeric(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval_numeric(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ text *key = PG_GETARG_TEXT_PP(1);
+ HStoreValue *v = NULL;
+
+ if (!HS_ISEMPTY(hs))
+ v = findUncompressedHStoreValue(VARDATA(hs),
+ HS_FLAG_HSTORE | HS_FLAG_ARRAY,
+ NULL,
+ VARDATA_ANY(key),
+ VARSIZE_ANY_EXHDR(key));
+
+ if (v && v->type == hsvNumeric)
+ {
+ Numeric out = palloc(VARSIZE_ANY(v->numeric));
+
+ memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
+ PG_RETURN_NUMERIC(out);
+ }
+
+ PG_RETURN_NULL();
+}
+
+PG_FUNCTION_INFO_V1(hstore_fetchval_boolean);
+Datum hstore_fetchval_boolean(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval_boolean(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ text *key = PG_GETARG_TEXT_PP(1);
+ HStoreValue *v = NULL;
+
+ if (!HS_ISEMPTY(hs))
+ v = findUncompressedHStoreValue(VARDATA(hs),
+ HS_FLAG_HSTORE | HS_FLAG_ARRAY,
+ NULL,
+ VARDATA_ANY(key),
+ VARSIZE_ANY_EXHDR(key));
+
+ if (v && v->type == hsvBool)
+ PG_RETURN_BOOL(v->boolean);
+
+ PG_RETURN_NULL();
+}
+
+PG_FUNCTION_INFO_V1(hstore_fetchval_n);
+Datum hstore_fetchval_n(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval_n(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ int i = PG_GETARG_INT32(1);
+ HStoreValue *v = NULL;
+ text *out;
+
+ if (!HS_ISEMPTY(hs))
+ v = getHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, i);
+
+ if ((out = HStoreValueToText(v)) == NULL)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_TEXT_P(out);
+}
+
+PG_FUNCTION_INFO_V1(hstore_fetchval_n_numeric);
+Datum hstore_fetchval_n_numeric(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval_n_numeric(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ int i = PG_GETARG_INT32(1);
+ HStoreValue *v = NULL;
+
+ if (!HS_ISEMPTY(hs))
+ v = getHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, i);
+
+ if (v && v->type == hsvNumeric)
+ {
+ Numeric out = palloc(VARSIZE_ANY(v->numeric));
+
+ memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
+ PG_RETURN_NUMERIC(out);
+ }
+
+ PG_RETURN_NULL();
+}
+
+PG_FUNCTION_INFO_V1(hstore_fetchval_n_boolean);
+Datum hstore_fetchval_n_boolean(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval_n_boolean(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ int i = PG_GETARG_INT32(1);
+ HStoreValue *v = NULL;
+
+ if (!HS_ISEMPTY(hs))
+ v = getHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, i);
+
+ if (v && v->type == hsvBool)
+ PG_RETURN_BOOL(v->boolean);
+
+ PG_RETURN_NULL();
+}
+
+static bool
+h_atoi(char *c, int l, int *acc)
+{
+ bool negative = false;
+ char *p = c;
+
+ *acc = 0;
+
+ while(isspace(*p) && p - c < l)
+ p++;
+
+ if (p - c >= l)
+ return false;
+
+ if (*p == '-')
+ {
+ negative = true;
+ p++;
+ }
+ else if (*p == '+')
+ {
+ p++;
+ }
+
+ if (p - c >= l)
+ return false;
+
+
+ while(p - c < l)
+ {
+ if (!isdigit(*p))
+ return false;
+
+ *acc *= 10;
+ *acc += (*p - '0');
+ p++;
+ }
+
+ if (negative)
+ *acc = - *acc;
+
+ return true;
+}
+
+static HStoreValue*
+hstoreDeepFetch(HStore *in, ArrayType *path)
+{
+ HStoreValue *v = NULL;
+ static HStoreValue init /* could be returned */;
+ Datum *path_elems;
+ bool *path_nulls;
+ int path_len, i;
+
+ Assert(ARR_ELEMTYPE(path) == TEXTOID);
+
+ if (ARR_NDIM(path) > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("wrong number of array subscripts")));
+
+ if (HS_ROOT_COUNT(in) == 0)
+ return NULL;
+
+ deconstruct_array(path, TEXTOID, -1, false, 'i',
+ &path_elems, &path_nulls, &path_len);
+
+ init.type = hsvBinary;
+ init.size = VARSIZE(in);
+ init.binary.data = VARDATA(in);
+ init.binary.len = VARSIZE_ANY_EXHDR(in);
+
+ v = &init;
+
+ if (path_len == 0)
+ return v;
+
+ for(i=0; v != NULL && i<path_len; i++)
+ {
+ uint32 header;
+
+ if (v->type != hsvBinary || path_nulls[i])
+ return NULL;
+
+ header = *(uint32*)v->binary.data;
+
+ if (header & HS_FLAG_HSTORE)
+ {
+ v = findUncompressedHStoreValue(v->binary.data, HS_FLAG_HSTORE,
+ NULL,
+ VARDATA_ANY(path_elems[i]),
+ VARSIZE_ANY_EXHDR(path_elems[i]));
+ }
+ else if (header & HS_FLAG_ARRAY)
+ {
+ int ith;
+
+ if (h_atoi(VARDATA_ANY(path_elems[i]),
+ VARSIZE_ANY_EXHDR(path_elems[i]), &ith) == false)
+ return NULL;
+
+ if (ith < 0)
+ {
+ if (-ith > (int)(header & HS_COUNT_MASK))
+ return NULL;
+ else
+ ith = ((int)(header & HS_COUNT_MASK)) + ith;
+ }
+ else
+ {
+ if (ith >= (int)(header & HS_COUNT_MASK))
+ return NULL;
+ }
+
+ v = getHStoreValue(v->binary.data, HS_FLAG_ARRAY, ith);
+ }
+ else
+ {
+ elog(PANIC,"wrong header type: %08x", header);
+ }
+ }
+
+ return v;
+}
+
+PG_FUNCTION_INFO_V1(hstore_fetchval_path);
+Datum hstore_fetchval_path(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval_path(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
+ text *out;
+
+ if ((out = HStoreValueToText(hstoreDeepFetch(hs, path))) == NULL)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_TEXT_P(out);
+}
+
+PG_FUNCTION_INFO_V1(hstore_fetchval_path_numeric);
+Datum hstore_fetchval_path_numeric(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval_path_numeric(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
+ HStoreValue *v = NULL;
+
+ if (!HS_ISEMPTY(hs))
+ v = hstoreDeepFetch(hs, path);
+
+ if (v && v->type == hsvNumeric)
+ {
+ Numeric out = palloc(VARSIZE_ANY(v->numeric));
+
+ memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
+ PG_RETURN_NUMERIC(out);
+ }
+
+ PG_RETURN_NULL();
+}
+
+PG_FUNCTION_INFO_V1(hstore_fetchval_path_boolean);
+Datum hstore_fetchval_path_boolean(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval_path_boolean(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
+ HStoreValue *v = NULL;
+
+ if (!HS_ISEMPTY(hs))
+ v = hstoreDeepFetch(hs, path);
+
+ if (v && v->type == hsvBool)
+ PG_RETURN_BOOL(v->boolean);
+
+ PG_RETURN_NULL();
+}
+
+static HStore *
+HStoreValueToHStore(HStoreValue *v)
+{
+ HStore *out;
+
+ if (v == NULL || v->type == hsvNull)
+ {
+ out = NULL;
+ }
+ else if (v->type == hsvString || v->type == hsvBool ||
+ v->type == hsvNumeric)
+ {
+ ToHStoreState *state = NULL;
+ HStoreValue *res;
+ int r;
+ HStoreValue scalarArray;
+
+ scalarArray.type = hsvArray;
+ scalarArray.array.scalar = true;
+ scalarArray.array.nelems = 1;
+
+ pushHStoreValue(&state, WHS_BEGIN_ARRAY, &scalarArray);
+ pushHStoreValue(&state, WHS_ELEM, v);
+ res = pushHStoreValue(&state, WHS_END_ARRAY, NULL);
+
+ out = palloc(VARHDRSZ + res->size);
+ SET_VARSIZE(out, VARHDRSZ + res->size);
+ r = compressHStore(res, VARDATA(out));
+ Assert(r <= res->size);
+ SET_VARSIZE(out, r + VARHDRSZ);
+ }
+ else
+ {
+ out = palloc(VARHDRSZ + v->size);
+
+ Assert(v->type == hsvBinary);
+ SET_VARSIZE(out, VARHDRSZ + v->binary.len);
+ memcpy(VARDATA(out), v->binary.data, v->binary.len);
+ }
+
+ return out;
+}
+
+PG_FUNCTION_INFO_V1(hstore_fetchval_hstore);
+Datum hstore_fetchval_hstore(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval_hstore(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ text *key = PG_GETARG_TEXT_PP(1);
+ HStoreValue *v = NULL;
+ HStore *out;
+
+ if (!HS_ISEMPTY(hs))
+ v = findUncompressedHStoreValue(VARDATA(hs),
+ HS_FLAG_HSTORE | HS_FLAG_ARRAY,
+ NULL,
+ VARDATA_ANY(key),
+ VARSIZE_ANY_EXHDR(key));
+
+
+ if ((out = HStoreValueToHStore(v)) == NULL)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_POINTER(out);
+}
+
+PG_FUNCTION_INFO_V1(hstore_fetchval_n_hstore);
+Datum hstore_fetchval_n_hstore(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval_n_hstore(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ int i = PG_GETARG_INT32(1);
+ HStoreValue *v = NULL;
+ HStore *out;
+
+ if (!HS_ISEMPTY(hs))
+ v = getHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, i);
+
+ if ((out = HStoreValueToHStore(v)) == NULL)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_POINTER(out);
+}
+
+PG_FUNCTION_INFO_V1(hstore_fetchval_path_hstore);
+Datum hstore_fetchval_path_hstore(PG_FUNCTION_ARGS);
+Datum
+hstore_fetchval_path_hstore(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
+ HStore *out;
+
+ if ((out = HStoreValueToHStore(hstoreDeepFetch(hs, path))) == NULL)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_POINTER(out);
+}
+
+PG_FUNCTION_INFO_V1(hstore_exists);
+Datum hstore_exists(PG_FUNCTION_ARGS);
+Datum
+hstore_exists(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ text *key = PG_GETARG_TEXT_PP(1);
+ HStoreValue *v = NULL;
+
+ if (!HS_ISEMPTY(hs))
+ v = findUncompressedHStoreValue(VARDATA(hs),
+ HS_FLAG_HSTORE | HS_FLAG_ARRAY,
+ NULL,
+ VARDATA_ANY(key),
+ VARSIZE_ANY_EXHDR(key));
+
+ PG_RETURN_BOOL(v != NULL);
+}
+
+
+PG_FUNCTION_INFO_V1(hstore_exists_idx);
+Datum hstore_exists_idx(PG_FUNCTION_ARGS);
+Datum
+hstore_exists_idx(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ int ith = PG_GETARG_INT32(1);
+ HStoreValue *v = NULL;
+
+ if (!HS_ISEMPTY(hs))
+ v = getHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, ith);
+
+ PG_RETURN_BOOL(v != NULL);
+}
+
+PG_FUNCTION_INFO_V1(hstore_exists_path);
+Datum hstore_exists_path(PG_FUNCTION_ARGS);
+Datum
+hstore_exists_path(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
+
+ PG_RETURN_BOOL(hstoreDeepFetch(hs, path) != NULL);
+}
+
+
+
+PG_FUNCTION_INFO_V1(hstore_exists_any);
+Datum hstore_exists_any(PG_FUNCTION_ARGS);
+Datum
+hstore_exists_any(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
+ HStoreValue *v = arrayToHStoreSortedArray(keys);
+ int i;
+ uint32 *plowbound = NULL, lowbound = 0;
+ bool res = false;
+
+ if (HS_ISEMPTY(hs) || v == NULL || v->hash.npairs == 0)
+ PG_RETURN_BOOL(false);
+
+ if (HS_ROOT_IS_HASH(hs))
+ plowbound = &lowbound;
+ /*
+ * we exploit the fact that the pairs list is already sorted into strictly
+ * increasing order to narrow the findUncompressedHStoreValue search; each search can
+ * start one entry past the previous "found" entry, or at the lower bound
+ * of the last search.
+ */
+ for (i = 0; i < v->array.nelems; i++)
+ {
+ if (findUncompressedHStoreValueByValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, plowbound,
+ v->array.elems + i) != NULL)
+ {
+ res = true;
+ break;
+ }
+ }
+
+ PG_RETURN_BOOL(res);
+}
+
+
+PG_FUNCTION_INFO_V1(hstore_exists_all);
+Datum hstore_exists_all(PG_FUNCTION_ARGS);
+Datum
+hstore_exists_all(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
+ HStoreValue *v = arrayToHStoreSortedArray(keys);
+ int i;
+ uint32 *plowbound = NULL, lowbound = 0;
+ bool res = true;
+
+ if (HS_ISEMPTY(hs) || v == NULL || v->array.nelems == 0)
+ {
+
+ if (v == NULL || v->array.nelems == 0)
+ PG_RETURN_BOOL(true); /* compatibility */
+ else
+ PG_RETURN_BOOL(false);
+ }
+
+ if (HS_ROOT_IS_HASH(hs))
+ plowbound = &lowbound;
+ /*
+ * we exploit the fact that the pairs list is already sorted into strictly
+ * increasing order to narrow the findUncompressedHStoreValue search;
+ * each search can start one entry past the previous "found" entry,
+ * or at the lower bound of the last search.
+ */
+ for (i = 0; i < v->array.nelems; i++)
+ {
+ if (findUncompressedHStoreValueByValue(VARDATA(hs),
+ HS_FLAG_HSTORE | HS_FLAG_ARRAY,
+ plowbound,
+ v->array.elems + i) == NULL)
+ {
+ res = false;
+ break;
+ }
+ }
+
+ PG_RETURN_BOOL(res);
+}
+
+
+PG_FUNCTION_INFO_V1(hstore_defined);
+Datum hstore_defined(PG_FUNCTION_ARGS);
+Datum
+hstore_defined(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ text *key = PG_GETARG_TEXT_PP(1);
+ HStoreValue *v = NULL;
+
+ if (!HS_ISEMPTY(hs))
+ v = findUncompressedHStoreValue(VARDATA(hs),
+ HS_FLAG_HSTORE | HS_FLAG_ARRAY,
+ NULL,
+ VARDATA_ANY(key),
+ VARSIZE_ANY_EXHDR(key));
+
+ PG_RETURN_BOOL(!(v == NULL || v->type == hsvNull));
+}
+
+
+PG_FUNCTION_INFO_V1(hstore_delete);
+Datum hstore_delete(PG_FUNCTION_ARGS);
+Datum
+hstore_delete(PG_FUNCTION_ARGS)
+{
+ HStore *in = PG_GETARG_HS(0);
+ text *key = PG_GETARG_TEXT_PP(1);
+ char *keyptr = VARDATA_ANY(key);
+ int keylen = VARSIZE_ANY_EXHDR(key);
+ HStore *out = palloc(VARSIZE(in));
+ ToHStoreState *toState = NULL;
+ HStoreIterator *it;
+ uint32 r;
+ HStoreValue v, *res = NULL;
+ bool skipNested = false;
+
+ SET_VARSIZE(out, VARSIZE(in));
+
+ if (HS_ISEMPTY(in))
+ PG_RETURN_POINTER(out);
+
+ it = HStoreIteratorInit(VARDATA(in));
+
+ while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
+ {
+ skipNested = true;
+
+ if ((r == WHS_ELEM || r == WHS_KEY) &&
+ (v.type == hsvString && keylen == v.string.len &&
+ memcmp(keyptr, v.string.val, keylen) == 0))
+ {
+ if (r == WHS_KEY)
+ /* skip corresponding value */
+ HStoreIteratorGet(&it, &v, true);
+
+ continue;
+ }
+
+ res = pushHStoreValue(&toState, r, &v);
+ }
+
+ if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
+ (res->type == hsvHash && res->hash.npairs == 0) )
+ {
+ SET_VARSIZE(out, VARHDRSZ);
+ }
+ else
+ {
+ r = compressHStore(res, VARDATA(out));
+ SET_VARSIZE(out, r + VARHDRSZ);
+ }
+
+ PG_RETURN_POINTER(out);
+}
+
+PG_FUNCTION_INFO_V1(hstore_delete_array);
+Datum hstore_delete_array(PG_FUNCTION_ARGS);
+Datum
+hstore_delete_array(PG_FUNCTION_ARGS)
+{
+ HStore *in = PG_GETARG_HS(0);
+ HStore *out = palloc(VARSIZE(in));
+ HStoreValue *a = arrayToHStoreSortedArray(PG_GETARG_ARRAYTYPE_P(1));
+ HStoreIterator *it;
+ ToHStoreState *toState = NULL;
+ uint32 r, i = 0;
+ HStoreValue v, *res = NULL;
+ bool skipNested = false;
+ bool isHash = false;
+
+
+ if (HS_ISEMPTY(in) || a == NULL || a->array.nelems == 0)
+ {
+ memcpy(out, in, VARSIZE(in));
+ PG_RETURN_POINTER(out);
+ }
+
+ it = HStoreIteratorInit(VARDATA(in));
+
+ while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
+ {
+
+ if (skipNested == false)
+ {
+ Assert(v.type == hsvArray || v.type == hsvHash);
+ isHash = (v.type == hsvArray) ? false : true;
+ skipNested = true;
+ }
+
+ if ((r == WHS_ELEM || r == WHS_KEY) && v.type == hsvString &&
+ i < a->array.nelems)
+ {
+ int diff;
+
+ if (isHash)
+ {
+ do {
+ diff = compareHStoreStringValue(&v, a->array.elems + i,
+ NULL);
+
+ if (diff >= 0)
+ i++;
+ } while(diff > 0 && i < a->array.nelems);
+ }
+ else
+ {
+ diff = (findInHStoreSortedArray(a, NULL,
+ v.string.val,
+ v.string.len) == NULL) ? 1 : 0;
+ }
+
+ if (diff == 0)
+ {
+ if (r == WHS_KEY)
+ /* skip corresponding value */
+ HStoreIteratorGet(&it, &v, true);
+
+ continue;
+ }
+ }
+
+ res = pushHStoreValue(&toState, r, &v);
+ }
+
+ if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
+ (res->type == hsvHash && res->hash.npairs == 0) )
+ {
+ SET_VARSIZE(out, VARHDRSZ);
+ }
+ else
+ {
+ r = compressHStore(res, VARDATA(out));
+ SET_VARSIZE(out, r + VARHDRSZ);
+ }
+
+ PG_RETURN_POINTER(out);
+}
+
+
+PG_FUNCTION_INFO_V1(hstore_delete_hstore);
+Datum hstore_delete_hstore(PG_FUNCTION_ARGS);
+Datum
+hstore_delete_hstore(PG_FUNCTION_ARGS)
+{
+ HStore *hs1 = PG_GETARG_HS(0);
+ HStore *hs2 = PG_GETARG_HS(1);
+ HStore *out = palloc(VARSIZE(hs1));
+ HStoreIterator *it1, *it2;
+ ToHStoreState *toState = NULL;
+ uint32 r1, r2;
+ HStoreValue v1, v2, *res = NULL;
+ bool isHash1, isHash2;
+
+ if (HS_ISEMPTY(hs1) || HS_ISEMPTY(hs2))
+ {
+ memcpy(out, hs1, VARSIZE(hs1));
+ PG_RETURN_POINTER(out);
+ }
+
+ it1 = HStoreIteratorInit(VARDATA(hs1));
+ r1 = HStoreIteratorGet(&it1, &v1, false);
+ isHash1 = (v1.type == hsvArray) ? false : true;
+
+ it2 = HStoreIteratorInit(VARDATA(hs2));
+ r2 = HStoreIteratorGet(&it2, &v2, false);
+ isHash2 = (v2.type == hsvArray) ? false : true;
+
+ res = pushHStoreValue(&toState, r1, &v1);
+
+ if (isHash1 == true && isHash2 == true)
+ {
+ bool fin2 = false,
+ keyIsDef = false;
+
+ while((r1 = HStoreIteratorGet(&it1, &v1, true)) != 0)
+ {
+ if (r1 == WHS_KEY && fin2 == false)
+ {
+ int diff = 1;
+
+ if (keyIsDef)
+ r2 = WHS_KEY;
+
+ while(keyIsDef ||
+ (r2 = HStoreIteratorGet(&it2, &v2, true)) != 0)
+ {
+ if (r2 != WHS_KEY)
+ continue;
+
+ diff = compareHStoreStringValue(&v1, &v2, NULL);
+
+ if (diff > 0 && keyIsDef)
+ keyIsDef = false;
+ if (diff <= 0)
+ break;
+ }
+
+ if (r2 == 0)
+ {
+ fin2 = true;
+ }
+ else if (diff == 0)
+ {
+ HStoreValue vk;
+
+ keyIsDef = false;
+
+ r1 = HStoreIteratorGet(&it1, &vk, true);
+ r2 = HStoreIteratorGet(&it2, &v2, true);
+
+ Assert(r1 == WHS_VALUE && r2 == WHS_VALUE);
+
+ if (compareHStoreValue(&vk, &v2) != 0)
+ {
+ res = pushHStoreValue(&toState, WHS_KEY, &v1);
+ res = pushHStoreValue(&toState, WHS_VALUE, &vk);
+ }
+
+ continue;
+ }
+ else
+ {
+ keyIsDef = true;
+ }
+ }
+
+ res = pushHStoreValue(&toState, r1, &v1);
+ }
+ }
+ else
+ {
+ while((r1 = HStoreIteratorGet(&it1, &v1, true)) != 0)
+ {
+
+ if (r1 == WHS_ELEM || r1 == WHS_KEY)
+ {
+ int diff = 1;
+
+ it2 = HStoreIteratorInit(VARDATA(hs2));
+
+ r2 = HStoreIteratorGet(&it2, &v2, false);
+
+ while(diff && (r2 = HStoreIteratorGet(&it2, &v2, true)) != 0)
+ {
+ if (r2 == WHS_KEY || r2 == WHS_VALUE || r2 == WHS_ELEM)
+ diff = compareHStoreValue(&v1, &v2);
+ }
+
+ if (diff == 0)
+ {
+ if (r1 == WHS_KEY)
+ HStoreIteratorGet(&it1, &v1, true);
+ continue;
+ }
+ }
+
+ res = pushHStoreValue(&toState, r1, &v1);
+ }
+ }
+
+ if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
+ (res->type == hsvHash && res->hash.npairs == 0) )
+ {
+ SET_VARSIZE(out, VARHDRSZ);
+ }
+ else
+ {
+ int r = compressHStore(res, VARDATA(out));
+ SET_VARSIZE(out, r + VARHDRSZ);
+ }
+
+ PG_RETURN_POINTER(out);
+}
+
+static HStoreValue*
+deletePathDo(HStoreIterator **it, Datum *path_elems,
+ bool *path_nulls, int path_len,
+ ToHStoreState **st, int level)
+{
+ HStoreValue v, *res = NULL;
+ int r;
+
+ r = HStoreIteratorGet(it, &v, false);
+
+ if (r == WHS_BEGIN_ARRAY)
+ {
+ int skipIdx, i;
+ uint32 n = v.array.nelems;
+
+ skipIdx = n;
+ if (level >= path_len || path_nulls[level] ||
+ h_atoi(VARDATA_ANY(path_elems[level]),
+ VARSIZE_ANY_EXHDR(path_elems[level]), &skipIdx) == false)
+ {
+ skipIdx = n;
+ }
+ else if (skipIdx < 0)
+ {
+ if (-skipIdx > n)
+ skipIdx = n;
+ else
+ skipIdx = n + skipIdx;
+ }
+
+ if (skipIdx > n)
+ skipIdx = n;
+
+ if (skipIdx == 0 && n == 1)
+ {
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_ELEM);
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_END_ARRAY);
+ return NULL;
+ }
+
+ pushHStoreValue(st, r, &v);
+
+ for(i=0; i<skipIdx; i++) {
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_ELEM);
+ res = pushHStoreValue(st, r, &v);
+ }
+
+ if (level >= path_len || skipIdx == n) {
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_END_ARRAY);
+ res = pushHStoreValue(st, r, &v);
+ return res;
+ }
+
+ if (level == path_len - 1)
+ {
+ /* last level in path, skip all elem */
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_ELEM);
+ }
+ else
+ {
+ res = deletePathDo(it, path_elems, path_nulls, path_len, st,
+ level + 1);
+ }
+
+ for(i = skipIdx + 1; i<n; i++) {
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_ELEM);
+ res = pushHStoreValue(st, r, &v);
+ }
+
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_END_ARRAY);
+ res = pushHStoreValue(st, r, &v);
+ }
+ else if (r == WHS_BEGIN_HASH)
+ {
+ int i;
+ uint32 n = v.hash.npairs;
+ HStoreValue k;
+ bool done = false;
+
+ if (n == 1 && level == path_len - 1)
+ {
+ r = HStoreIteratorGet(it, &k, false);
+ Assert(r == WHS_KEY);
+
+ if ( path_nulls[level] == false &&
+ k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) &&
+ memcmp(k.string.val, VARDATA_ANY(path_elems[level]),
+ k.string.len) == 0)
+ {
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_VALUE);
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_END_HASH);
+ return NULL;
+ }
+
+ pushHStoreValue(st, WHS_BEGIN_HASH, &v);
+ pushHStoreValue(st, WHS_KEY, &k);
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_VALUE);
+ pushHStoreValue(st, r, &v);
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_END_HASH);
+ return pushHStoreValue(st, r, &v);
+ }
+
+ pushHStoreValue(st, WHS_BEGIN_HASH, &v);
+
+ if (level >= path_len || path_nulls[level])
+ done = true;
+
+ for(i=0; i<n; i++)
+ {
+ r = HStoreIteratorGet(it, &k, false);
+ Assert(r == WHS_KEY);
+
+ if (done == false &&
+ k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) &&
+ memcmp(k.string.val, VARDATA_ANY(path_elems[level]),
+ k.string.len) == 0)
+ {
+ done = true;
+
+ if (level == path_len - 1)
+ {
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_VALUE);
+ }
+ else
+ {
+ pushHStoreValue(st, r, &k);
+ res = deletePathDo(it, path_elems, path_nulls, path_len,
+ st, level + 1);
+ if (res == NULL)
+ {
+ v.type = hsvNull;
+ pushHStoreValue(st, WHS_VALUE, &v);
+ }
+ }
+
+ continue;
+ }
+
+ pushHStoreValue(st, r, &k);
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_VALUE);
+ pushHStoreValue(st, r, &v);
+ }
+
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_END_HASH);
+ res = pushHStoreValue(st, r, &v);
+ }
+ else if (r == WHS_ELEM || r == WHS_VALUE) /* just a string or null */
+ {
+ pushHStoreValue(st, r, &v);
+ res = (void*)0x01; /* dummy value */
+ }
+ else
+ {
+ elog(PANIC, "impossible state");
+ }
+
+ return res;
+}
+
+
+PG_FUNCTION_INFO_V1(hstore_delete_path);
+Datum hstore_delete_path(PG_FUNCTION_ARGS);
+Datum
+hstore_delete_path(PG_FUNCTION_ARGS)
+{
+ HStore *in = PG_GETARG_HS(0);
+ HStore *out = palloc(VARSIZE(in));
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
+ HStoreValue *res = NULL;
+ Datum *path_elems;
+ bool *path_nulls;
+ int path_len;
+ HStoreIterator *it;
+ ToHStoreState *st = NULL;
+
+ Assert(ARR_ELEMTYPE(path) == TEXTOID);
+
+ if (ARR_NDIM(path) > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("wrong number of array subscripts")));
+
+ if (HS_ROOT_COUNT(in) == 0)
+ {
+ memcpy(out, in, VARSIZE(in));
+ PG_RETURN_POINTER(out);
+ }
+
+ deconstruct_array(path, TEXTOID, -1, false, 'i',
+ &path_elems, &path_nulls, &path_len);
+
+ if (path_len == 0)
+ {
+ memcpy(out, in, VARSIZE(in));
+ PG_RETURN_POINTER(out);
+ }
+
+ it = HStoreIteratorInit(VARDATA(in));
+
+ res = deletePathDo(&it, path_elems, path_nulls, path_len, &st, 0);
+
+ if (res == NULL)
+ {
+ SET_VARSIZE(out, VARHDRSZ);
+ }
+ else
+ {
+ int sz;
+
+ sz = compressHStore(res, VARDATA(out));
+ SET_VARSIZE(out, sz + VARHDRSZ);
+ }
+
+ PG_RETURN_POINTER(out);
+}
+
+PG_FUNCTION_INFO_V1(hstore_delete_idx);
+Datum hstore_delete_idx(PG_FUNCTION_ARGS);
+Datum
+hstore_delete_idx(PG_FUNCTION_ARGS)
+{
+ HStore *in = PG_GETARG_HS(0);
+ int idx = PG_GETARG_INT32(1);
+ HStore *out = palloc(VARSIZE(in));
+ ToHStoreState *toState = NULL;
+ HStoreIterator *it;
+ uint32 r, i = 0, n;
+ HStoreValue v, *res = NULL;
+
+ if (HS_ISEMPTY(in))
+ {
+ memcpy(out, in, VARSIZE(in));
+ PG_RETURN_POINTER(out);
+ }
+
+ it = HStoreIteratorInit(VARDATA(in));
+
+ r = HStoreIteratorGet(&it, &v, false);
+ if (r == WHS_BEGIN_ARRAY)
+ n = v.array.nelems;
+ else
+ n = v.hash.npairs;
+
+ if (idx < 0)
+ {
+ if (-idx > n)
+ idx = n;
+ else
+ idx = n + idx;
+ }
+
+ if (idx >= n)
+ {
+ memcpy(out, in, VARSIZE(in));
+ PG_RETURN_POINTER(out);
+ }
+
+ pushHStoreValue(&toState, r, &v);
+
+ while((r = HStoreIteratorGet(&it, &v, true)) != 0)
+ {
+ if (r == WHS_ELEM || r == WHS_KEY)
+ {
+ if (i++ == idx)
+ {
+ if (r == WHS_KEY)
+ HStoreIteratorGet(&it, &v, true); /* skip value */
+ continue;
+ }
+ }
+
+ res = pushHStoreValue(&toState, r, &v);
+ }
+
+ if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
+ (res->type == hsvHash && res->hash.npairs == 0) )
+ {
+ SET_VARSIZE(out, VARHDRSZ);
+ }
+ else
+ {
+ r = compressHStore(res, VARDATA(out));
+ SET_VARSIZE(out, r + VARHDRSZ);
+ }
+
+ PG_RETURN_POINTER(out);
+}
+
+static void
+convertScalarToString(HStoreValue *v)
+{
+ switch(v->type) {
+ case hsvNull:
+ elog(ERROR, "key in hstore type could not be a NULL");
+ break;
+ case hsvBool:
+ v->type = hsvString;
+ v->string.val = pnstrdup((v->boolean) ? "t" : "f", 1);
+ v->string.len = 1;
+ v->size = sizeof(HEntry) + v->string.len;
+ break;
+ case hsvNumeric:
+ v->type = hsvString;
+ v->string.val = DatumGetCString(
+ DirectFunctionCall1(numeric_out,
+ PointerGetDatum(v->numeric)));
+ v->string.len = strlen(v->string.val);
+ v->size = sizeof(HEntry) + v->string.len;
+ break;
+ case hsvString:
+ break;
+ default:
+ elog(PANIC,"Could not convert to string");
+ }
+}
+
+static HStoreValue *
+IteratorConcat(HStoreIterator **it1, HStoreIterator **it2,
+ ToHStoreState **toState)
+{
+ uint32 r1, r2, rk1, rk2;
+ HStoreValue v1, v2, *res = NULL;
+
+ r1 = rk1 = HStoreIteratorGet(it1, &v1, false);
+ r2 = rk2 = HStoreIteratorGet(it2, &v2, false);
+
+ if (rk1 == WHS_BEGIN_HASH && rk2 == WHS_BEGIN_HASH)
+ {
+ bool fin2 = false,
+ keyIsDef = false;
+
+ res = pushHStoreValue(toState, r1, &v1);
+
+ for(;;)
+ {
+ r1 = HStoreIteratorGet(it1, &v1, true);
+
+ Assert(r1 == WHS_KEY || r1 == WHS_VALUE || r1 == WHS_END_HASH);
+
+ if (r1 == WHS_KEY && fin2 == false)
+ {
+ int diff = 1;
+
+ if (keyIsDef)
+ r2 = WHS_KEY;
+
+ while(keyIsDef || (r2 = HStoreIteratorGet(it2, &v2, true)) != 0)
+ {
+ if (r2 != WHS_KEY)
+ continue;
+
+ diff = compareHStoreStringValue(&v1, &v2, NULL);
+
+ if (diff > 0)
+ {
+ if (keyIsDef)
+ keyIsDef = false;
+
+ pushHStoreValue(toState, r2, &v2);
+ r2 = HStoreIteratorGet(it2, &v2, true);
+ Assert(r2 == WHS_VALUE);
+ pushHStoreValue(toState, r2, &v2);
+ }
+ else if (diff <= 0)
+ {
+ break;
+ }
+ }
+
+ if (r2 == 0)
+ {
+ fin2 = true;
+ }
+ else if (diff == 0)
+ {
+ keyIsDef = false;
+
+ pushHStoreValue(toState, r1, &v1);
+
+ r1 = HStoreIteratorGet(it1, &v1, true); /* ignore */
+ r2 = HStoreIteratorGet(it2, &v2, true); /* new val */
+
+ Assert(r1 == WHS_VALUE && r2 == WHS_VALUE);
+ pushHStoreValue(toState, r2, &v2);
+
+ continue;
+ }
+ else
+ {
+ keyIsDef = true;
+ }
+ }
+ else if (r1 == WHS_END_HASH)
+ {
+ if (r2 != 0)
+ {
+ if (keyIsDef)
+ r2 = WHS_KEY;
+
+ while(keyIsDef ||
+ (r2 = HStoreIteratorGet(it2, &v2, true)) != 0)
+ {
+ if (r2 != WHS_KEY)
+ continue;
+
+ pushHStoreValue(toState, r2, &v2);
+ r2 = HStoreIteratorGet(it2, &v2, true);
+ Assert(r2 == WHS_VALUE);
+ pushHStoreValue(toState, r2, &v2);
+ keyIsDef = false;
+ }
+ }
+
+ res = pushHStoreValue(toState, r1, &v1);
+ break;
+ }
+
+ res = pushHStoreValue(toState, r1, &v1);
+ }
+ }
+ else if ((rk1 == WHS_BEGIN_HASH || rk1 == WHS_BEGIN_ARRAY) &&
+ (rk2 == WHS_BEGIN_HASH || rk2 == WHS_BEGIN_ARRAY))
+ {
+ if (rk1 == WHS_BEGIN_HASH && rk2 == WHS_BEGIN_ARRAY &&
+ v2.array.nelems % 2 != 0)
+ elog(ERROR, "hstore's array must have even number of elements");
+
+ res = pushHStoreValue(toState, r1, &v1);
+
+ for(;;)
+ {
+ r1 = HStoreIteratorGet(it1, &v1, true);
+ if (r1 == WHS_END_HASH || r1 == WHS_END_ARRAY)
+ break;
+ Assert(r1 == WHS_KEY || r1 == WHS_VALUE || r1 == WHS_ELEM);
+ pushHStoreValue(toState, r1, &v1);
+ }
+
+ while((r2 = HStoreIteratorGet(it2, &v2, true)) != 0)
+ {
+ if (!(r2 == WHS_END_HASH || r2 == WHS_END_ARRAY))
+ {
+ if (rk1 == WHS_BEGIN_HASH)
+ {
+ convertScalarToString(&v2);
+ pushHStoreValue(toState, WHS_KEY, &v2);
+ r2 = HStoreIteratorGet(it2, &v2, true);
+ Assert(r2 == WHS_ELEM);
+ pushHStoreValue(toState, WHS_VALUE, &v2);
+ }
+ else
+ {
+ pushHStoreValue(toState, WHS_ELEM, &v2);
+ }
+ }
+ }
+
+ res = pushHStoreValue(toState,
+ (rk1 == WHS_BEGIN_HASH) ? WHS_END_HASH : WHS_END_ARRAY,
+ NULL/* signal to sort */);
+ }
+ else if ((rk1 & (WHS_VALUE | WHS_ELEM)) != 0)
+ {
+ if (v2.type == hsvArray && v2.array.scalar)
+ {
+ Assert(v2.array.nelems == 1);
+ r2 = HStoreIteratorGet(it2, &v2, false);
+ pushHStoreValue(toState, r1, &v2);
+ }
+ else
+ {
+ res = pushHStoreValue(toState, r2, &v2);
+ while((r2 = HStoreIteratorGet(it2, &v2, true)) != 0)
+ res = pushHStoreValue(toState, r2, &v2);
+ }
+ }
+ else
+ {
+ elog(ERROR, "invalid concatnation of hstores");
+ }
+
+ return res;
+}
+
+PG_FUNCTION_INFO_V1(hstore_concat);
+Datum hstore_concat(PG_FUNCTION_ARGS);
+Datum
+hstore_concat(PG_FUNCTION_ARGS)
+{
+ HStore *hs1 = PG_GETARG_HS(0);
+ HStore *hs2 = PG_GETARG_HS(1);
+ HStore *out = palloc(VARSIZE(hs1) + VARSIZE(hs2));
+ ToHStoreState *toState = NULL;
+ HStoreValue *res;
+ HStoreIterator *it1, *it2;
+
+ if (HS_ISEMPTY(hs1))
+ {
+ memcpy(out, hs2, VARSIZE(hs2));
+ PG_RETURN_POINTER(out);
+ }
+ else if (HS_ISEMPTY(hs2))
+ {
+ memcpy(out, hs1, VARSIZE(hs1));
+ PG_RETURN_POINTER(out);
+ }
+
+ it1 = HStoreIteratorInit(VARDATA(hs1));
+ it2 = HStoreIteratorInit(VARDATA(hs2));
+
+ res = IteratorConcat(&it1, &it2, &toState);
+
+ if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
+ (res->type == hsvHash && res->hash.npairs == 0) )
+ {
+ SET_VARSIZE(out, VARHDRSZ);
+ }
+ else
+ {
+ uint32 r;
+
+ if (res->type == hsvArray && res->array.nelems > 1)
+ res->array.scalar = false;
+
+ r = compressHStore(res, VARDATA(out));
+ SET_VARSIZE(out, r + VARHDRSZ);
+ }
+
+ PG_RETURN_POINTER(out);
+}
+
+
+PG_FUNCTION_INFO_V1(hstore_slice_to_array);
+Datum hstore_slice_to_array(PG_FUNCTION_ARGS);
+Datum
+hstore_slice_to_array(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(1);
+ ArrayType *aout;
+ Datum *key_datums;
+ bool *key_nulls;
+ Datum *out_datums;
+ bool *out_nulls;
+ int key_count;
+ int i;
+
+ deconstruct_array(key_array,
+ TEXTOID, -1, false, 'i',
+ &key_datums, &key_nulls, &key_count);
+
+ if (key_count == 0 || HS_ISEMPTY(hs))
+ {
+ aout = construct_empty_array(TEXTOID);
+ PG_RETURN_POINTER(aout);
+ }
+
+ out_datums = palloc(sizeof(Datum) * key_count);
+ out_nulls = palloc(sizeof(bool) * key_count);
+
+ for (i = 0; i < key_count; ++i)
+ {
+ text *key = (text *) DatumGetPointer(key_datums[i]);
+ HStoreValue *v = NULL;
+
+ if (key_nulls[i] == false)
+ v = findUncompressedHStoreValue(VARDATA(hs),
+ HS_FLAG_HSTORE | HS_FLAG_ARRAY,
+ NULL,
+ VARDATA(key),
+ VARSIZE(key) - VARHDRSZ);
+
+ out_datums[i] = PointerGetDatum(HStoreValueToText(v));
+ out_nulls[i] = (DatumGetPointer(out_datums[i]) == NULL) ? true : false;
+ }
+
+ aout = construct_md_array(out_datums, out_nulls,
+ ARR_NDIM(key_array),
+ ARR_DIMS(key_array),
+ ARR_LBOUND(key_array),
+ TEXTOID, -1, false, 'i');
+
+ PG_RETURN_POINTER(aout);
+}
+
+
+PG_FUNCTION_INFO_V1(hstore_slice_to_hstore);
+Datum hstore_slice_to_hstore(PG_FUNCTION_ARGS);
+Datum
+hstore_slice_to_hstore(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ HStoreValue *a = arrayToHStoreSortedArray(PG_GETARG_ARRAYTYPE_P(1));
+ uint32 lowbound = 0,
+ *plowbound;
+ HStoreValue *res = NULL;
+ ToHStoreState *state = NULL;
+ text *out;
+ uint32 i;
+
+ out = palloc(VARSIZE(hs));
+
+ if (a == NULL || a->array.nelems == 0 || HS_ISEMPTY(hs))
+ {
+ memcpy(out, hs, VARSIZE(hs));
+ PG_RETURN_POINTER(out);
+ }
+
+ if (HS_ROOT_IS_HASH(hs))
+ {
+ plowbound = &lowbound;
+ pushHStoreValue(&state, WHS_BEGIN_HASH, NULL);
+ }
+ else
+ {
+ plowbound = NULL;
+ pushHStoreValue(&state, WHS_BEGIN_ARRAY, NULL);
+ }
+
+ for (i = 0; i < a->array.nelems; ++i)
+ {
+ HStoreValue *v = findUncompressedHStoreValueByValue(VARDATA(hs),
+ HS_FLAG_HSTORE | HS_FLAG_ARRAY,
+ plowbound,
+ a->array.elems + i);
+
+ if (v)
+ {
+ if (plowbound)
+ {
+ pushHStoreValue(&state, WHS_KEY, a->array.elems + i);
+ pushHStoreValue(&state, WHS_VALUE, v);
+ }
+ else
+ {
+ pushHStoreValue(&state, WHS_ELEM, v);
+ }
+ }
+ }
+
+ if (plowbound)
+ res = pushHStoreValue(&state, WHS_END_HASH, a /* any non-null value */);
+ else
+ res = pushHStoreValue(&state, WHS_END_ARRAY, NULL);
+
+
+ if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
+ (res->type == hsvHash && res->hash.npairs == 0) )
+ {
+ SET_VARSIZE(out, VARHDRSZ);
+ }
+ else
+ {
+ int r = compressHStore(res, VARDATA(out));
+ SET_VARSIZE(out, r + VARHDRSZ);
+ }
+ PG_RETURN_POINTER(out);
+}
-static HEntry *
-findkey(HStore *hs, char *key, int keylen) {
- HEntry *StopLow = ARRPTR(hs);
- HEntry *StopHigh = StopLow + hs->size;
- HEntry *StopMiddle;
- int difference;
- char *base = STRPTR(hs);
+static HStoreValue*
+replacePathDo(HStoreIterator **it, Datum *path_elems,
+ bool *path_nulls, int path_len,
+ ToHStoreState **st, int level, HStoreValue *newval)
+{
+ HStoreValue v, *res = NULL;
+ int r;
+
+ r = HStoreIteratorGet(it, &v, false);
+
+ if (r == WHS_BEGIN_ARRAY)
+ {
+ int idx, i;
+ uint32 n = v.array.nelems;
+
+ idx = n;
+ if (level >= path_len || path_nulls[level] ||
+ h_atoi(VARDATA_ANY(path_elems[level]),
+ VARSIZE_ANY_EXHDR(path_elems[level]), &idx) == false)
+ {
+ idx = n;
+ }
+ else if (idx < 0)
+ {
+ if (-idx > n)
+ idx = n;
+ else
+ idx = n + idx;
+ }
- while (StopLow < StopHigh) {
- StopMiddle = StopLow + (StopHigh - StopLow) / 2;
+ if (idx > n)
+ idx = n;
+
+ pushHStoreValue(st, r, &v);
+
+ for(i=0; i<n; i++)
+ {
+ if (i == idx && level < path_len)
+ {
+ if (level == path_len - 1)
+ {
+ r = HStoreIteratorGet(it, &v, true); /* skip */
+ Assert(r == WHS_ELEM);
+ res = pushHStoreValue(st, r, newval);
+ }
+ else
+ {
+ res = replacePathDo(it, path_elems, path_nulls, path_len,
+ st, level + 1, newval);
+ }
+ }
+ else
+ {
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_ELEM);
+ res = pushHStoreValue(st, r, &v);
+ }
+ }
- if ( StopMiddle->keylen == keylen )
- difference=strncmp(base+StopMiddle->pos, key, StopMiddle->keylen);
- else
- difference=(StopMiddle->keylen > keylen) ? 1 : -1;
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_END_ARRAY);
+ res = pushHStoreValue(st, r, &v);
+ }
+ else if (r == WHS_BEGIN_HASH)
+ {
+ int i;
+ uint32 n = v.hash.npairs;
+ HStoreValue k;
+ bool done = false;
+
+ pushHStoreValue(st, WHS_BEGIN_HASH, &v);
+
+ if (level >= path_len || path_nulls[level])
+ done = true;
+
+ for(i=0; i<n; i++)
+ {
+ r = HStoreIteratorGet(it, &k, false);
+ Assert(r == WHS_KEY);
+ res = pushHStoreValue(st, r, &k);
+
+ if (done == false &&
+ k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) &&
+ memcmp(k.string.val, VARDATA_ANY(path_elems[level]),
+ k.string.len) == 0)
+ {
+ if (level == path_len - 1)
+ {
+ r = HStoreIteratorGet(it, &v, true); /* skip */
+ Assert(r == WHS_VALUE);
+ res = pushHStoreValue(st, r, newval);
+ }
+ else
+ {
+ res = replacePathDo(it, path_elems, path_nulls, path_len,
+ st, level + 1, newval);
+ }
+ }
+ else
+ {
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_VALUE);
+ res = pushHStoreValue(st, r, &v);
+ }
+ }
- if (difference == 0)
- return StopMiddle;
- else if (difference < 0)
- StopLow = StopMiddle + 1;
- else
- StopHigh = StopMiddle;
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_END_HASH);
+ res = pushHStoreValue(st, r, &v);
}
-
- return NULL;
+ else if (r == WHS_ELEM || r == WHS_VALUE)
+ {
+ pushHStoreValue(st, r, &v);
+ res = (void*)0x01; /* dummy value */
+ }
+ else
+ {
+ elog(PANIC, "impossible state");
+ }
+
+ return res;
}
-PG_FUNCTION_INFO_V1(fetchval);
-Datum fetchval(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_replace);
+Datum hstore_replace(PG_FUNCTION_ARGS);
Datum
-fetchval(PG_FUNCTION_ARGS) {
- HStore *hs = PG_GETARG_HS(0);
- text *key = PG_GETARG_TEXT_P(1);
- HEntry *entry;
- text *out;
+hstore_replace(PG_FUNCTION_ARGS)
+{
+ HStore *in = PG_GETARG_HS(0);
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
+ HStore *newval = PG_GETARG_HS(2);
+ HStore *out = palloc(VARSIZE(in) + VARSIZE(newval));
+ HStoreValue *res = NULL;
+ HStoreValue value;
+ Datum *path_elems;
+ bool *path_nulls;
+ int path_len;
+ HStoreIterator *it;
+ ToHStoreState *st = NULL;
+
+ Assert(ARR_ELEMTYPE(path) == TEXTOID);
+
+ if (ARR_NDIM(path) > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("wrong number of array subscripts")));
+
+ if (HS_ROOT_COUNT(in) == 0)
+ {
+ memcpy(out, in, VARSIZE(in));
+ PG_RETURN_POINTER(out);
+ }
- if ((entry=findkey(hs,VARDATA(key), VARSIZE(key)-VARHDRSZ))==NULL || entry->valisnull) {
- PG_FREE_IF_COPY(hs,0);
- PG_FREE_IF_COPY(key,1);
- PG_RETURN_NULL();
+ deconstruct_array(path, TEXTOID, -1, false, 'i',
+ &path_elems, &path_nulls, &path_len);
+
+ if (path_len == 0)
+ {
+ memcpy(out, in, VARSIZE(in));
+ PG_RETURN_POINTER(out);
+ }
+
+ if (HS_ROOT_COUNT(newval) == 0)
+ {
+ value.type = hsvNull;
+ value.size = sizeof(HEntry);
}
+ else
+ {
+ value.type = hsvBinary;
+ value.binary.data = VARDATA(newval);
+ value.binary.len = VARSIZE_ANY_EXHDR(newval);
+ value.size = value.binary.len + sizeof(HEntry);
+ }
+
+ it = HStoreIteratorInit(VARDATA(in));
+
+ res = replacePathDo(&it, path_elems, path_nulls, path_len, &st, 0, &value);
+
+ if (res == NULL)
+ {
+ SET_VARSIZE(out, VARHDRSZ);
+ }
+ else
+ {
+ int sz;
- out=palloc(VARHDRSZ+entry->vallen);
- memcpy(VARDATA(out),STRPTR(hs) + entry->pos + entry->keylen, entry->vallen);
- VARATT_SIZEP(out) = VARHDRSZ+entry->vallen;
+ sz = compressHStore(res, VARDATA(out));
+ SET_VARSIZE(out, sz + VARHDRSZ);
+ }
- PG_FREE_IF_COPY(hs,0);
- PG_FREE_IF_COPY(key,1);
PG_RETURN_POINTER(out);
}
-PG_FUNCTION_INFO_V1(exists);
-Datum exists(PG_FUNCTION_ARGS);
+static HStoreValue*
+concatPathDo(HStoreIterator **it, Datum *path_elems,
+ bool *path_nulls, int path_len,
+ ToHStoreState **st, int level, HStoreIterator *toConcat)
+{
+ HStoreValue v, *res = NULL;
+ int r;
+
+ r = HStoreIteratorGet(it, &v, false);
+
+ if (r == WHS_BEGIN_ARRAY)
+ {
+ int idx, i;
+ uint32 n = v.array.nelems;
+
+ idx = n;
+ if (level >= path_len || path_nulls[level] ||
+ h_atoi(VARDATA_ANY(path_elems[level]),
+ VARSIZE_ANY_EXHDR(path_elems[level]), &idx) == false)
+ {
+ idx = n;
+ }
+ else if (idx < 0)
+ {
+ if (-idx > n)
+ idx = n;
+ else
+ idx = n + idx;
+ }
+
+ if (idx > n)
+ idx = n;
+
+ pushHStoreValue(st, r, &v);
+
+ for(i=0; i<n; i++)
+ {
+ if (i == idx && level < path_len)
+ {
+ if (level == path_len - 1)
+ res = IteratorConcat(it, &toConcat, st);
+ else
+ res = concatPathDo(it, path_elems, path_nulls, path_len,
+ st, level + 1, toConcat);
+ }
+ else
+ {
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_ELEM);
+ res = pushHStoreValue(st, r, &v);
+ }
+ }
+
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_END_ARRAY);
+ res = pushHStoreValue(st, r, &v);
+ }
+ else if (r == WHS_BEGIN_HASH)
+ {
+ int i;
+ uint32 n = v.hash.npairs;
+ HStoreValue k;
+ bool done = false;
+
+ pushHStoreValue(st, WHS_BEGIN_HASH, &v);
+
+ if (level >= path_len || path_nulls[level])
+ done = true;
+
+ for(i=0; i<n; i++)
+ {
+ r = HStoreIteratorGet(it, &k, false);
+ Assert(r == WHS_KEY);
+ res = pushHStoreValue(st, r, &k);
+
+ if (done == false && level < path_len &&
+ k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) &&
+ memcmp(k.string.val, VARDATA_ANY(path_elems[level]),
+ k.string.len) == 0)
+ {
+ if (level == path_len - 1)
+ res = IteratorConcat(it, &toConcat, st);
+ else
+ res = concatPathDo(it, path_elems, path_nulls, path_len,
+ st, level + 1, toConcat);
+ }
+ else
+ {
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_VALUE);
+ res = pushHStoreValue(st, r, &v);
+ }
+ }
+
+ r = HStoreIteratorGet(it, &v, true);
+ Assert(r == WHS_END_HASH);
+ res = pushHStoreValue(st, r, &v);
+ }
+ else if (r == WHS_ELEM || r == WHS_VALUE)
+ {
+ pushHStoreValue(st, r, &v);
+ res = (void*)0x01; /* dummy value */
+ }
+ else
+ {
+ elog(PANIC, "impossible state");
+ }
+
+ return res;
+}
+
+PG_FUNCTION_INFO_V1(hstore_deep_concat);
+Datum hstore_deep_concat(PG_FUNCTION_ARGS);
Datum
-exists(PG_FUNCTION_ARGS) {
- HStore *hs = PG_GETARG_HS(0);
- text *key = PG_GETARG_TEXT_P(1);
- HEntry *entry;
+hstore_deep_concat(PG_FUNCTION_ARGS)
+{
+ HStore *in = PG_GETARG_HS(0);
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
+ HStore *newval = PG_GETARG_HS(2);
+ HStore *out = palloc(VARSIZE(in) + VARSIZE(newval));
+ HStoreValue *res = NULL;
+ Datum *path_elems;
+ bool *path_nulls;
+ int path_len;
+ HStoreIterator *it1, *it2;
+ ToHStoreState *st = NULL;
+
+ Assert(ARR_ELEMTYPE(path) == TEXTOID);
+
+ if (ARR_NDIM(path) > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("wrong number of array subscripts")));
+
+ if (HS_ROOT_COUNT(in) == 0 || HS_ROOT_COUNT(newval) == 0)
+ {
+ memcpy(out, in, VARSIZE(in));
+ PG_RETURN_POINTER(out);
+ }
+
+ deconstruct_array(path, TEXTOID, -1, false, 'i',
+ &path_elems, &path_nulls, &path_len);
+
+ it1 = HStoreIteratorInit(VARDATA(in));
+ it2 = HStoreIteratorInit(VARDATA(newval));
+
+ if (path_len == 0)
+ res = IteratorConcat(&it1, &it2, &st);
+ else
+ res = concatPathDo(&it1, path_elems, path_nulls, path_len, &st, 0, it2);
+
+ if (res == NULL)
+ {
+ SET_VARSIZE(out, VARHDRSZ);
+ }
+ else
+ {
+ int sz;
- entry=findkey(hs,VARDATA(key), VARSIZE(key)-VARHDRSZ);
+ if (res->type == hsvArray && res->array.nelems > 1)
+ res->array.scalar = false;
- PG_FREE_IF_COPY(hs,0);
- PG_FREE_IF_COPY(key,1);
+ sz = compressHStore(res, VARDATA(out));
+ SET_VARSIZE(out, sz + VARHDRSZ);
+ }
- PG_RETURN_BOOL(entry);
+ PG_RETURN_POINTER(out);
}
-PG_FUNCTION_INFO_V1(defined);
-Datum defined(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_akeys);
+Datum hstore_akeys(PG_FUNCTION_ARGS);
Datum
-defined(PG_FUNCTION_ARGS) {
- HStore *hs = PG_GETARG_HS(0);
- text *key = PG_GETARG_TEXT_P(1);
- HEntry *entry;
- bool res;
+hstore_akeys(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ Datum *d;
+ ArrayType *a;
+ int i = 0, r = 0;
+ HStoreIterator *it;
+ HStoreValue v;
+ bool skipNested = false;
+
+ if (HS_ISEMPTY(hs))
+ {
+ a = construct_empty_array(TEXTOID);
+ PG_RETURN_POINTER(a);
+ }
- entry=findkey(hs,VARDATA(key), VARSIZE(key)-VARHDRSZ);
+ d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
- res = ( entry && !entry->valisnull ) ? true : false;
+ it = HStoreIteratorInit(VARDATA(hs));
- PG_FREE_IF_COPY(hs,0);
- PG_FREE_IF_COPY(key,1);
+ while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
+ {
+ skipNested = true;
- PG_RETURN_BOOL(res);
+ if ((r == WHS_ELEM && v.type != hsvNull) || r == WHS_KEY)
+ d[i++] = PointerGetDatum(HStoreValueToText(&v));
+ }
+
+ a = construct_array(d, i,
+ TEXTOID, -1, false, 'i');
+
+ PG_RETURN_POINTER(a);
}
-PG_FUNCTION_INFO_V1(delete);
-Datum delete(PG_FUNCTION_ARGS);
+
+PG_FUNCTION_INFO_V1(hstore_avals);
+Datum hstore_avals(PG_FUNCTION_ARGS);
Datum
-delete(PG_FUNCTION_ARGS) {
- HStore *hs = PG_GETARG_HS(0);
- text *key = PG_GETARG_TEXT_P(1);
- HStore *out = palloc(hs->len);
- char *ptrs, *ptrd;
- HEntry *es, *ed;
+hstore_avals(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ Datum *d;
+ ArrayType *a;
+ int i = 0, r = 0;
+ HStoreIterator *it;
+ HStoreValue v;
+ bool skipNested = false;
+ bool *nulls;
+ int lb = 1;
+
+ if (HS_ISEMPTY(hs))
+ {
+ a = construct_empty_array(TEXTOID);
+ PG_RETURN_POINTER(a);
+ }
- out->len=hs->len;
- out->size=hs->size; /* temprorary! */
+ d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
+ nulls = (bool *) palloc(sizeof(bool) * HS_ROOT_COUNT(hs));
- ptrs=STRPTR(hs);
- es =ARRPTR(hs);
- ptrd=STRPTR(out);
- ed =ARRPTR(out);
+ it = HStoreIteratorInit(VARDATA(hs));
- while( es - ARRPTR(hs) < hs->size ) {
- if ( !(es->keylen == VARSIZE(key) - VARHDRSZ && strncmp(ptrs, VARDATA(key), es->keylen)==0) ) {
- memcpy( ed, es, sizeof(HEntry) );
- memcpy( ptrd, ptrs, es->keylen + ( (es->valisnull) ? 0 : es->vallen ) );
- ed->pos = ptrd - STRPTR(out);
- ptrd += es->keylen + ( (es->valisnull) ? 0 : es->vallen );
- ed++;
+ while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
+ {
+ skipNested = true;
+
+ if (r == WHS_ELEM || r == WHS_VALUE)
+ {
+ d[i] = PointerGetDatum(HStoreValueToText(&v));
+ nulls[i] = (DatumGetPointer(d[i]) == NULL) ? true : false;
+ i++;
}
- ptrs += es->keylen + ( (es->valisnull) ? 0 : es->vallen );
- es++;
}
- if ( ed - ARRPTR(out) != out->size ) {
- int buflen=ptrd-STRPTR(out);
- ptrd = STRPTR(out);
+ a = construct_md_array(d, nulls, 1, &i, &lb,
+ TEXTOID, -1, false, 'i');
+
+ PG_RETURN_POINTER(a);
+}
- out->size = ed - ARRPTR(out);
- memmove( STRPTR(out), ptrd, buflen);
- out->len = CALCDATASIZE(out->size, buflen);
+static ArrayType *
+hstore_to_array_internal(HStore *hs, int ndims)
+{
+ int count = HS_ROOT_COUNT(hs);
+ int out_size[2] = {0, 2};
+ int lb[2] = {1, 1};
+ Datum *out_datums;
+ bool *out_nulls;
+ bool isHash = HS_ROOT_IS_HASH(hs) ? true : false;
+ int i = 0, r = 0;
+ HStoreIterator *it;
+ HStoreValue v;
+ bool skipNested = false;
+
+ Assert(ndims < 3);
+
+ if (count == 0 || ndims == 0)
+ return construct_empty_array(TEXTOID);
+
+ if (isHash == false && ndims == 2 && count % 2 != 0)
+ elog(ERROR, "hstore's array should have even number of elements");
+
+ out_size[0] = count * (isHash ? 2 : 1) / ndims;
+ out_datums = palloc(sizeof(Datum) * count * 2);
+ out_nulls = palloc(sizeof(bool) * count * 2);
+
+ it = HStoreIteratorInit(VARDATA(hs));
+
+ while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
+ {
+ skipNested = true;
+
+ switch(r)
+ {
+ case WHS_ELEM:
+ out_datums[i] = PointerGetDatum(HStoreValueToText(&v));
+ out_nulls[i] = (DatumGetPointer(out_datums[i]) == NULL) ? true : false;
+ i++;
+ break;
+ case WHS_KEY:
+ out_datums[i * 2] = PointerGetDatum(HStoreValueToText(&v));
+ out_nulls[i * 2] = (DatumGetPointer(out_datums[i * 2]) == NULL) ? true : false;
+ break;
+ case WHS_VALUE:
+ out_datums[i * 2 + 1] = PointerGetDatum(HStoreValueToText(&v));
+ out_nulls[i * 2 + 1] = (DatumGetPointer(out_datums[i * 2 + 1]) == NULL) ? true : false;
+ i++;
+ break;
+ default:
+ break;
+ }
}
-
- PG_FREE_IF_COPY(hs,0);
- PG_FREE_IF_COPY(key,1);
+ return construct_md_array(out_datums, out_nulls,
+ ndims, out_size, lb,
+ TEXTOID, -1, false, 'i');
+}
+
+PG_FUNCTION_INFO_V1(hstore_to_array);
+Datum hstore_to_array(PG_FUNCTION_ARGS);
+Datum
+hstore_to_array(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ ArrayType *out = hstore_to_array_internal(hs, 1);
PG_RETURN_POINTER(out);
}
-PG_FUNCTION_INFO_V1(hs_concat);
-Datum hs_concat(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_to_matrix);
+Datum hstore_to_matrix(PG_FUNCTION_ARGS);
Datum
-hs_concat(PG_FUNCTION_ARGS) {
- HStore *s1 = PG_GETARG_HS(0);
- HStore *s2 = PG_GETARG_HS(1);
- HStore *out = palloc( s1->len + s2->len );
- char *ps1, *ps2, *pd;
- HEntry *es1, *es2, *ed;
+hstore_to_matrix(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ ArrayType *out = hstore_to_array_internal(hs, 2);
- out->len = s1->len + s2->len;
- out->size = s1->size + s2->size;
+ PG_RETURN_POINTER(out);
+}
- ps1=STRPTR(s1);
- ps2=STRPTR(s2);
- pd=STRPTR(out);
- es1=ARRPTR(s1);
- es2=ARRPTR(s2);
- ed=ARRPTR(out);
+/*
+ * Common initialization function for the various set-returning
+ * funcs. fcinfo is only passed if the function is to return a
+ * composite; it will be used to look up the return tupledesc.
+ * we stash a copy of the hstore in the multi-call context in
+ * case it was originally toasted. (At least I assume that's why;
+ * there was no explanatory comment in the original code. --AG)
+ */
+
+typedef struct SetReturningState
+{
+ HStore *hs;
+ HStoreIterator *it;
+ MemoryContext ctx;
+
+ HStoreValue init;
+ int path_len;
+ int level;
+ struct {
+ HStoreValue v;
+ Datum varStr;
+ int varInt;
+ enum {
+ pathStr,
+ pathInt,
+ pathAny
+ } varKind;
+ int i;
+ } *path;
+} SetReturningState;
+
+static SetReturningState*
+setup_firstcall(FuncCallContext *funcctx, HStore *hs, ArrayType *path,
+ FunctionCallInfoData *fcinfo)
+{
+ MemoryContext oldcontext;
+ SetReturningState *st;
- while( es1 - ARRPTR(s1) < s1->size && es2 - ARRPTR(s2) < s2->size ) {
- int difference;
- if ( es1->keylen == es2->keylen )
- difference=strncmp(ps1, ps2, es1->keylen);
- else
- difference=(es1->keylen > es2->keylen) ? 1 : -1;
-
- if ( difference == 0 ) {
- memcpy( ed, es2, sizeof(HEntry) );
- memcpy( pd, ps2, es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ) );
- ed->pos = pd - STRPTR(out);
- pd += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen );
- ed++;
-
- ps1 += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen );
- es1++;
- ps2 += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen );
- es2++;
- } else if ( difference > 0 ) {
- memcpy( ed, es2, sizeof(HEntry) );
- memcpy( pd, ps2, es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ) );
- ed->pos = pd - STRPTR(out);
- pd += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen );
- ed++;
-
- ps2 += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen );
- es2++;
- } else {
- memcpy( ed, es1, sizeof(HEntry) );
- memcpy( pd, ps1, es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ) );
- ed->pos = pd - STRPTR(out);
- pd += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen );
- ed++;
-
- ps1 += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen );
- es1++;
- }
- }
-
- while( es1 - ARRPTR(s1) < s1->size ) {
- memcpy( ed, es1, sizeof(HEntry) );
- memcpy( pd, ps1, es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ) );
- ed->pos = pd - STRPTR(out);
- pd += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen );
- ed++;
-
- ps1 += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen );
- es1++;
- }
-
- while( es2 - ARRPTR(s2) < s2->size ) {
- memcpy( ed, es2, sizeof(HEntry) );
- memcpy( pd, ps2, es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ) );
- ed->pos = pd - STRPTR(out);
- pd += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen );
- ed++;
-
- ps2 += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen );
- es2++;
- }
-
- if ( ed - ARRPTR(out) != out->size ) {
- int buflen=pd-STRPTR(out);
- pd = STRPTR(out);
-
- out->size = ed - ARRPTR(out);
-
- memmove( STRPTR(out), pd, buflen);
- out->len = CALCDATASIZE(out->size, buflen);
- }
-
- PG_FREE_IF_COPY(s1,0);
- PG_FREE_IF_COPY(s2,1);
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
- PG_RETURN_POINTER(out);
-}
+ st = palloc(sizeof(*st));
-PG_FUNCTION_INFO_V1(tconvert);
-Datum tconvert(PG_FUNCTION_ARGS);
-Datum
-tconvert(PG_FUNCTION_ARGS) {
- text *key = PG_GETARG_TEXT_P(0);
- text *val = PG_GETARG_TEXT_P(1);
- int len;
- HStore *out;
+ st->ctx = funcctx->multi_call_memory_ctx;
- len=CALCDATASIZE(1, VARSIZE(key) + VARSIZE(val) - 2*VARHDRSZ);
- out = palloc(len);
- out->len=len;
- out->size=1;
+ st->hs = (HStore *) palloc(VARSIZE(hs));
+ memcpy(st->hs, hs, VARSIZE(hs));
+ if (HS_ISEMPTY(hs) || path)
+ st->it = NULL;
+ else
+ st->it = HStoreIteratorInit(VARDATA(st->hs));
- ARRPTR(out)->keylen = VARSIZE(key) - VARHDRSZ;
- ARRPTR(out)->vallen = VARSIZE(val) - VARHDRSZ;
- ARRPTR(out)->valisnull = false;
- ARRPTR(out)->pos=0;
+ funcctx->user_fctx = (void *) st;
- memcpy( STRPTR(out), VARDATA(key), ARRPTR(out)->keylen );
- memcpy( STRPTR(out) + ARRPTR(out)->keylen, VARDATA(val), ARRPTR(out)->vallen );
-
- PG_FREE_IF_COPY(key,0);
- PG_FREE_IF_COPY(val,1);
+ if (fcinfo)
+ {
+ TupleDesc tupdesc;
- PG_RETURN_POINTER(out);
+ /* Build a tuple descriptor for our result type */
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
+
+ funcctx->tuple_desc = BlessTupleDesc(tupdesc);
+ }
+
+ st->path_len = st->level = 0;
+ if (path)
+ {
+ Datum *path_elems;
+ bool *path_nulls;
+ int i;
+
+ Assert(ARR_ELEMTYPE(path) == TEXTOID);
+ if (ARR_NDIM(path) > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("wrong number of array subscripts")));
+
+ deconstruct_array(path, TEXTOID, -1, false, 'i',
+ &path_elems, &path_nulls, &st->path_len);
+
+ st->init.type = hsvBinary;
+ st->init.size = VARSIZE(st->hs);
+ st->init.binary.data = VARDATA(st->hs);
+ st->init.binary.len = VARSIZE_ANY_EXHDR(st->hs);
+
+ if (st->path_len > 0)
+ {
+ st->path = palloc(sizeof(*st->path) * st->path_len);
+ st->path[0].v = st->init;
+ }
+
+ for(i=0; i<st->path_len; i++)
+ {
+ st->path[i].varStr = path_elems[i];
+ st->path[i].i = 0;
+
+ if (path_nulls[i])
+ st->path[i].varKind = pathAny;
+ else if (h_atoi(VARDATA_ANY(path_elems[i]),
+ VARSIZE_ANY_EXHDR(path_elems[i]),
+ &st->path[i].varInt))
+ st->path[i].varKind = pathInt;
+ else
+ st->path[i].varKind = pathStr;
+ }
+ }
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return st;
+}
+
+static uint32
+HStoreIteratorGetCtx(SetReturningState *st, HStoreValue *v, bool skipNested)
+{
+ int r;
+ MemoryContext oldctx;
+
+ oldctx = MemoryContextSwitchTo(st->ctx);
+ r = HStoreIteratorGet(&st->it, v, skipNested);
+ MemoryContextSwitchTo(oldctx);
+
+ return r;
}
-PG_FUNCTION_INFO_V1(akeys);
-Datum akeys(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_skeys);
+Datum hstore_skeys(PG_FUNCTION_ARGS);
Datum
-akeys(PG_FUNCTION_ARGS) {
- HStore *hs = PG_GETARG_HS(0);
- Datum *d;
- ArrayType *a;
- HEntry *ptr=ARRPTR(hs);
- char *base=STRPTR(hs);
+hstore_skeys(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ SetReturningState *st;
+ int r;
+ HStoreValue v;
+
+ if (SRF_IS_FIRSTCALL())
+ {
+ funcctx = SRF_FIRSTCALL_INIT();
+ st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, NULL);
+ }
- d=(Datum*)palloc(sizeof(Datum)*(hs->size+1));
- while( ptr-ARRPTR(hs) < hs->size ) {
- text *item=(text*)palloc(VARHDRSZ + ptr->keylen);
- VARATT_SIZEP(item) = VARHDRSZ+ptr->keylen;
- memcpy(VARDATA(item), base + ptr->pos, ptr->keylen);
- d[ ptr-ARRPTR(hs) ] = PointerGetDatum(item);
- ptr++;
+ funcctx = SRF_PERCALL_SETUP();
+ st = (SetReturningState *) funcctx->user_fctx;
+
+ while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
+ {
+ if (r == WHS_KEY || r == WHS_ELEM)
+ {
+ text *item = HStoreValueToText(&v);
+
+ if (item == NULL)
+ SRF_RETURN_NEXT_NULL(funcctx);
+ else
+ SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
+ }
}
-
- a = construct_array(
- d,
- hs->size,
- TEXTOID,
- -1,
- false,
- 'i'
- );
- ptr=ARRPTR(hs);
- while( ptr-ARRPTR(hs) < hs->size ) {
- pfree(DatumGetPointer(d[ ptr-ARRPTR(hs) ]));
- ptr++;
+ SRF_RETURN_DONE(funcctx);
+}
+
+PG_FUNCTION_INFO_V1(hstore_svals);
+Datum hstore_svals(PG_FUNCTION_ARGS);
+Datum
+hstore_svals(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ SetReturningState *st;
+ int r;
+ HStoreValue v;
+
+ if (SRF_IS_FIRSTCALL())
+ {
+ funcctx = SRF_FIRSTCALL_INIT();
+ st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, NULL);
}
- pfree(d);
- PG_FREE_IF_COPY(hs,0);
+ funcctx = SRF_PERCALL_SETUP();
+ st = (SetReturningState *) funcctx->user_fctx;
+
+ while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
+ {
+ if (r == WHS_VALUE || r == WHS_ELEM)
+ {
+ text *item = HStoreValueToText(&v);
+
+ if (item == NULL)
+ SRF_RETURN_NEXT_NULL(funcctx);
+ else
+ SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
+ }
+ }
- PG_RETURN_POINTER(a);
+ SRF_RETURN_DONE(funcctx);
}
-PG_FUNCTION_INFO_V1(avals);
-Datum avals(PG_FUNCTION_ARGS);
-Datum
-avals(PG_FUNCTION_ARGS) {
- HStore *hs = PG_GETARG_HS(0);
- Datum *d;
- ArrayType *a;
- HEntry *ptr=ARRPTR(hs);
- char *base=STRPTR(hs);
-
- d=(Datum*)palloc(sizeof(Datum)*(hs->size+1));
- while( ptr-ARRPTR(hs) < hs->size ) {
- int vallen = (ptr->valisnull) ? 0 : ptr->vallen;
- text *item=(text*)palloc(VARHDRSZ + vallen);
- VARATT_SIZEP(item) = VARHDRSZ+vallen;
- memcpy(VARDATA(item), base + ptr->pos + ptr->keylen, vallen);
- d[ ptr-ARRPTR(hs) ] = PointerGetDatum(item);
- ptr++;
- }
-
- a = construct_array(
- d,
- hs->size,
- TEXTOID,
- -1,
- false,
- 'i'
- );
-
- ptr=ARRPTR(hs);
- while( ptr-ARRPTR(hs) < hs->size ) {
- pfree(DatumGetPointer(d[ ptr-ARRPTR(hs) ]));
- ptr++;
- }
-
- pfree(d);
- PG_FREE_IF_COPY(hs,0);
+PG_FUNCTION_INFO_V1(hstore_hvals);
+Datum hstore_hvals(PG_FUNCTION_ARGS);
+Datum
+hstore_hvals(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ SetReturningState *st;
+ int r;
+ HStoreValue v;
+
+ if (SRF_IS_FIRSTCALL())
+ {
+ funcctx = SRF_FIRSTCALL_INIT();
+ st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, NULL);
+ }
- PG_RETURN_POINTER(a);
+ funcctx = SRF_PERCALL_SETUP();
+ st = (SetReturningState *) funcctx->user_fctx;
+
+ while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
+ {
+ if (r == WHS_VALUE || r == WHS_ELEM)
+ {
+ HStore *item = HStoreValueToHStore(&v);
+
+ if (item == NULL)
+ SRF_RETURN_NEXT_NULL(funcctx);
+ else
+ SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
+ }
+ }
+
+ SRF_RETURN_DONE(funcctx);
}
-typedef struct {
- HStore *hs;
- int i;
-} AKStore;
+static HStoreValue*
+getNextValsPath(SetReturningState *st)
+{
+ HStoreValue *v = NULL;
+
+ if (st->path_len == 0)
+ {
+ /* empty path */
+ if (st->level == 0)
+ {
+ v = &st->init;
+ st->level ++;
+ }
-static void
-setup_firstcall(FuncCallContext *funcctx, HStore *hs) {
- MemoryContext oldcontext;
- AKStore *st;
+ return v;
+ }
- oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+ while(st->level >= 0)
+ {
+ uint32 header;
- st=(AKStore*)palloc( sizeof(AKStore) );
- st->i=0;
- st->hs = (HStore*)palloc(hs->len);
- memcpy( st->hs, hs, hs->len );
+ v = NULL;
+ if (st->path[st->level].v.type != hsvBinary)
+ {
+ st->level--;
+ continue;
+ }
- funcctx->user_fctx = (void*)st;
- MemoryContextSwitchTo(oldcontext);
+ header = *(uint32*)st->path[st->level].v.binary.data;
+
+ if (header & HS_FLAG_HSTORE)
+ {
+ if (st->path[st->level].varKind == pathAny)
+ {
+ v = getHStoreValue(st->path[st->level].v.binary.data,
+ HS_FLAG_HSTORE,
+ st->path[st->level].i++);
+ }
+ else
+ {
+ v = findUncompressedHStoreValue(st->path[st->level].v.binary.data,
+ HS_FLAG_HSTORE, NULL,
+ VARDATA_ANY(st->path[st->level].varStr),
+ VARSIZE_ANY_EXHDR(st->path[st->level].varStr));
+ }
+ }
+ else if (header & HS_FLAG_ARRAY)
+ {
+ if (st->path[st->level].varKind == pathAny)
+ {
+ v = getHStoreValue(st->path[st->level].v.binary.data,
+ HS_FLAG_ARRAY, st->path[st->level].i++);
+ }
+ else if (st->path[st->level].varKind == pathInt)
+ {
+ int ith = st->path[st->level].varInt;
+
+ if (ith < 0)
+ {
+ if (-ith > (int)(header & HS_COUNT_MASK))
+ {
+ st->level--;
+ continue;
+ }
+ else
+ {
+ ith = ((int)(header & HS_COUNT_MASK)) + ith;
+ }
+ }
+ else
+ {
+ if (ith >= (int)(header & HS_COUNT_MASK))
+ {
+ st->level--;
+ continue;
+ }
+ }
+
+ v = getHStoreValue(st->path[st->level].v.binary.data,
+ HS_FLAG_ARRAY, ith);
+ }
+ else
+ {
+ st->level--;
+ continue;
+ }
+ }
+ else
+ {
+ elog(PANIC, "impossible state");
+ }
+
+ if (v == NULL)
+ {
+ st->level--;
+ }
+ else if (st->level == st->path_len - 1)
+ {
+ if (st->path[st->level].varKind != pathAny)
+ {
+ st->path[st->level].v.type = hsvNull;
+ st->level--;
+ }
+ break;
+ }
+ else
+ {
+ if (st->path[st->level].varKind != pathAny)
+ st->path[st->level].v.type = hsvNull;
+ st->level++;
+ st->path[st->level].v = *v;
+ st->path[st->level].i = 0;
+ }
+ }
+
+ return v;
}
-PG_FUNCTION_INFO_V1(skeys);
-Datum skeys(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_svals_path);
+Datum hstore_svals_path(PG_FUNCTION_ARGS);
Datum
-skeys(PG_FUNCTION_ARGS) {
- FuncCallContext *funcctx;
- AKStore *st;
-
- if (SRF_IS_FIRSTCALL()) {
- HStore *hs = PG_GETARG_HS(0);
+hstore_svals_path(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ SetReturningState *st;
+ HStoreValue *v;
+
+ if (SRF_IS_FIRSTCALL())
+ {
funcctx = SRF_FIRSTCALL_INIT();
- setup_firstcall(funcctx, hs);
- PG_FREE_IF_COPY(hs,0);
+ st = setup_firstcall(funcctx, PG_GETARG_HS(0), PG_GETARG_ARRAYTYPE_P(1), NULL);
}
funcctx = SRF_PERCALL_SETUP();
- st = (AKStore*)funcctx->user_fctx;
-
- if ( st->i < st->hs->size ) {
- HEntry *ptr = &(ARRPTR(st->hs)[st->i]);
- text *item=(text*)palloc(VARHDRSZ + ptr->keylen);
+ st = (SetReturningState *) funcctx->user_fctx;
- VARATT_SIZEP(item) = VARHDRSZ+ptr->keylen;
- memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos, ptr->keylen);
- st->i++;
+ if ((v = getNextValsPath(st)) != NULL)
+ {
+ text *item = HStoreValueToText(v);
- SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
+ if (item == NULL)
+ SRF_RETURN_NEXT_NULL(funcctx);
+ else
+ SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
}
-
- pfree( st->hs );
- pfree( st );
- SRF_RETURN_DONE(funcctx);
+ SRF_RETURN_DONE(funcctx);
}
-PG_FUNCTION_INFO_V1(svals);
-Datum svals(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_hvals_path);
+Datum hstore_hvals_path(PG_FUNCTION_ARGS);
Datum
-svals(PG_FUNCTION_ARGS) {
- FuncCallContext *funcctx;
- AKStore *st;
-
- if (SRF_IS_FIRSTCALL()) {
- HStore *hs = PG_GETARG_HS(0);
+hstore_hvals_path(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ SetReturningState *st;
+ HStoreValue *v;
+
+ if (SRF_IS_FIRSTCALL())
+ {
funcctx = SRF_FIRSTCALL_INIT();
- setup_firstcall(funcctx, hs);
- PG_FREE_IF_COPY(hs,0);
+ st = setup_firstcall(funcctx, PG_GETARG_HS(0),
+ PG_GETARG_ARRAYTYPE_P(1), NULL);
}
funcctx = SRF_PERCALL_SETUP();
- st = (AKStore*)funcctx->user_fctx;
-
- if ( st->i < st->hs->size ) {
- HEntry *ptr = &(ARRPTR(st->hs)[st->i]);
-
- if ( ptr->valisnull ) {
- ReturnSetInfo *rsi;
-
- st->i++;
- (funcctx)->call_cntr++;
- rsi = (ReturnSetInfo *) fcinfo->resultinfo;
- rsi->isDone = ExprMultipleResult;
- PG_RETURN_NULL();
- } else {
- int vallen = ptr->vallen;
- text *item=(text*)palloc(VARHDRSZ + vallen);
+ st = (SetReturningState *) funcctx->user_fctx;
- VARATT_SIZEP(item) = VARHDRSZ+vallen;
- memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos + ptr->keylen, vallen);
- st->i++;
+ if ((v = getNextValsPath(st)) != NULL)
+ {
+ HStore *item = HStoreValueToHStore(v);
+ if (item == NULL)
+ SRF_RETURN_NEXT_NULL(funcctx);
+ else
SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
+ }
+
+ SRF_RETURN_DONE(funcctx);
+}
+
+PG_FUNCTION_INFO_V1(hstore_each);
+Datum hstore_each(PG_FUNCTION_ARGS);
+Datum
+hstore_each(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ SetReturningState *st;
+ int r;
+ HStoreValue v;
+
+ if (SRF_IS_FIRSTCALL())
+ {
+ funcctx = SRF_FIRSTCALL_INIT();
+ st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo);
+ }
+
+ funcctx = SRF_PERCALL_SETUP();
+ st = (SetReturningState *) funcctx->user_fctx;
+
+ while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
+ {
+ Datum res,
+ dvalues[2] = {0, 0};
+ bool nulls[2] = {false, false};
+ text *item;
+ HeapTuple tuple;
+
+ if (r == WHS_ELEM)
+ {
+ nulls[0] = true;
+
+ item = HStoreValueToText(&v);
+ if (item == NULL)
+ nulls[1] = true;
+ else
+ dvalues[1] = PointerGetDatum(item);
+ }
+ else if (r == WHS_KEY)
+ {
+ item = HStoreValueToText(&v);
+ dvalues[0] = PointerGetDatum(item);
+
+ r = HStoreIteratorGetCtx(st, &v, true);
+ Assert(r == WHS_VALUE);
+ item = HStoreValueToText(&v);
+ if (item == NULL)
+ nulls[1] = true;
+ else
+ dvalues[1] = PointerGetDatum(item);
}
+ else
+ {
+ continue;
+ }
+
+ tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
+ res = HeapTupleGetDatum(tuple);
+
+ SRF_RETURN_NEXT(funcctx, PointerGetDatum(res));
}
-
- pfree( st->hs );
- pfree( st );
- SRF_RETURN_DONE(funcctx);
+ SRF_RETURN_DONE(funcctx);
}
-PG_FUNCTION_INFO_V1(hs_contains);
-Datum hs_contains(PG_FUNCTION_ARGS);
+PG_FUNCTION_INFO_V1(hstore_each_hstore);
+Datum hstore_each_hstore(PG_FUNCTION_ARGS);
Datum
-hs_contains(PG_FUNCTION_ARGS) {
- HStore *val = PG_GETARG_HS(0);
- HStore *tmpl = PG_GETARG_HS(1);
- bool res = true;
- HEntry *te = ARRPTR(tmpl);
- char *vv = STRPTR(val);
- char *tv = STRPTR(tmpl);
+hstore_each_hstore(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ SetReturningState *st;
+ int r;
+ HStoreValue v;
+
+ if (SRF_IS_FIRSTCALL())
+ {
+ funcctx = SRF_FIRSTCALL_INIT();
+ st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo);
+ }
+
+ funcctx = SRF_PERCALL_SETUP();
+ st = (SetReturningState *) funcctx->user_fctx;
+
+ while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
+ {
+ Datum res,
+ dvalues[2] = {0, 0};
+ bool nulls[2] = {false, false};
+ text *item;
+ HStore *hitem;
+ HeapTuple tuple;
+
+ if (r == WHS_ELEM)
+ {
+ nulls[0] = true;
+
+ hitem = HStoreValueToHStore(&v);
+ if (hitem == NULL)
+ nulls[1] = true;
+ else
+ dvalues[1] = PointerGetDatum(hitem);
+ }
+ else if (r == WHS_KEY)
+ {
+ item = HStoreValueToText(&v);
+ dvalues[0] = PointerGetDatum(item);
+
+ r = HStoreIteratorGetCtx(st, &v, true);
+ Assert(r == WHS_VALUE);
+ hitem = HStoreValueToHStore(&v);
+ if (hitem == NULL)
+ nulls[1] = true;
+ else
+ dvalues[1] = PointerGetDatum(hitem);
+ }
+ else
+ {
+ continue;
+ }
+
+ tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
+ res = HeapTupleGetDatum(tuple);
+
+ SRF_RETURN_NEXT(funcctx, PointerGetDatum(res));
+ }
+
+ SRF_RETURN_DONE(funcctx);
+}
+
+static bool
+deepContains(HStoreIterator **it1, HStoreIterator **it2)
+{
+ uint32 r1, r2;
+ HStoreValue v1, v2;
+ bool res = true;
- while(res && te-ARRPTR(tmpl) < tmpl->size) {
- HEntry *entry = findkey(val, tv + te->pos, te->keylen);
- if ( entry ) {
- if ( ! te->valisnull ) {
- if ( entry->valisnull || !(
- te->vallen==entry->vallen &&
- strncmp(
- vv + entry->pos + entry->keylen,
- tv + te->pos + te->keylen,
- te->vallen ) == 0
- ) )
- res=false;
+ r1 = HStoreIteratorGet(it1, &v1, false);
+ r2 = HStoreIteratorGet(it2, &v2, false);
+
+ if (r1 != r2)
+ {
+ res = false;
+ }
+ else if (r1 == WHS_BEGIN_HASH)
+ {
+ uint32 lowbound = 0;
+ HStoreValue *v;
+
+ for(;;) {
+ r2 = HStoreIteratorGet(it2, &v2, false);
+ if (r2 == WHS_END_HASH)
+ break;
+
+ Assert(r2 == WHS_KEY);
+
+ v = findUncompressedHStoreValueByValue((*it1)->buffer,
+ HS_FLAG_HSTORE,
+ &lowbound, &v2);
+
+ if (v == NULL)
+ {
+ res = false;
+ break;
}
- } else
- res = false;
- te++;
+
+ r2 = HStoreIteratorGet(it2, &v2, true);
+ Assert(r2 == WHS_VALUE);
+
+ if (v->type != v2.type)
+ {
+ res = false;
+ break;
+ }
+ else if (v->type == hsvString || v->type == hsvNull ||
+ v->type == hsvBool || v->type == hsvNumeric)
+ {
+ if (compareHStoreValue(v, &v2) != 0)
+ {
+ res = false;
+ break;
+ }
+ }
+ else
+ {
+ HStoreIterator *it1a, *it2a;
+
+ Assert(v2.type == hsvBinary);
+ Assert(v->type == hsvBinary);
+
+ it1a = HStoreIteratorInit(v->binary.data);
+ it2a = HStoreIteratorInit(v2.binary.data);
+
+ if ((res = deepContains(&it1a, &it2a)) == false)
+ break;
+ }
+ }
+ }
+ else if (r1 == WHS_BEGIN_ARRAY)
+ {
+ HStoreValue *v;
+ HStoreValue *av = NULL;
+ uint32 nelems = v1.array.nelems;
+
+ for(;;) {
+ r2 = HStoreIteratorGet(it2, &v2, true);
+ if (r2 == WHS_END_ARRAY)
+ break;
+
+ Assert(r2 == WHS_ELEM);
+
+ if (v2.type == hsvString || v2.type == hsvNull ||
+ v2.type == hsvBool || v2.type == hsvNumeric)
+ {
+ v = findUncompressedHStoreValueByValue((*it1)->buffer,
+ HS_FLAG_ARRAY, NULL,
+ &v2);
+ if (v == NULL)
+ {
+ res = false;
+ break;
+ }
+ }
+ else
+ {
+ uint32 i;
+
+ if (av == NULL)
+ {
+ uint32 j = 0;
+
+ av = palloc(sizeof(*av) * nelems);
+
+ for(i=0; i<nelems; i++)
+ {
+ r2 = HStoreIteratorGet(it1, &v1, true);
+ Assert(r2 == WHS_ELEM);
+
+ if (v1.type == hsvBinary)
+ av[j++] = v1;
+ }
+
+ if (j == 0)
+ {
+ res = false;
+ break;
+ }
+
+ nelems = j;
+ }
+
+ res = false;
+ for(i = 0; res == false && i<nelems; i++)
+ {
+ HStoreIterator *it1a, *it2a;
+
+ it1a = HStoreIteratorInit(av[i].binary.data);
+ it2a = HStoreIteratorInit(v2.binary.data);
+
+ res = deepContains(&it1a, &it2a);
+ }
+
+ if (res == false)
+ break;
+ }
+ }
}
+ else
+ {
+ elog(PANIC, "impossible state");
+ }
+
+ return res;
+}
+
+PG_FUNCTION_INFO_V1(hstore_contains);
+Datum hstore_contains(PG_FUNCTION_ARGS);
+Datum
+hstore_contains(PG_FUNCTION_ARGS)
+{
+ HStore *val = PG_GETARG_HS(0);
+ HStore *tmpl = PG_GETARG_HS(1);
+ bool res = true;
+ HStoreIterator *it1, *it2;
+
+ if (HS_ROOT_COUNT(val) < HS_ROOT_COUNT(tmpl) ||
+ HS_ROOT_IS_HASH(val) != HS_ROOT_IS_HASH(tmpl))
+ PG_RETURN_BOOL(false);
- PG_FREE_IF_COPY(val,0);
- PG_FREE_IF_COPY(tmpl,1);
+ it1 = HStoreIteratorInit(VARDATA(val));
+ it2 = HStoreIteratorInit(VARDATA(tmpl));
+ res = deepContains(&it1, &it2);
PG_RETURN_BOOL(res);
}
-
-PG_FUNCTION_INFO_V1(hs_contained);
-Datum hs_contained(PG_FUNCTION_ARGS);
+
+
+PG_FUNCTION_INFO_V1(hstore_contained);
+Datum hstore_contained(PG_FUNCTION_ARGS);
Datum
-hs_contained(PG_FUNCTION_ARGS) {
- PG_RETURN_DATUM( DirectFunctionCall2(
- hs_contains,
- PG_GETARG_DATUM(1),
- PG_GETARG_DATUM(0)
- ));
+hstore_contained(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_DATUM(DirectFunctionCall2(hstore_contains,
+ PG_GETARG_DATUM(1),
+ PG_GETARG_DATUM(0)
+ ));
}
-PG_FUNCTION_INFO_V1(each);
-Datum each(PG_FUNCTION_ARGS);
+/*
+ * btree sort order for hstores isn't intended to be useful; we really only
+ * care about equality versus non-equality. we compare the entire string
+ * buffer first, then the entry pos array.
+ */
+
+PG_FUNCTION_INFO_V1(hstore_cmp);
+Datum hstore_cmp(PG_FUNCTION_ARGS);
Datum
-each(PG_FUNCTION_ARGS) {
- FuncCallContext *funcctx;
- AKStore *st;
+hstore_cmp(PG_FUNCTION_ARGS)
+{
+ HStore *hs1 = PG_GETARG_HS(0);
+ HStore *hs2 = PG_GETARG_HS(1);
+ int res;
+
+ if (HS_ISEMPTY(hs1) || HS_ISEMPTY(hs2))
+ {
+ if (HS_ISEMPTY(hs1))
+ {
+ if (HS_ISEMPTY(hs2))
+ res = 0;
+ else
+ res = -1;
+ }
+ else
+ {
+ res = 1;
+ }
+ }
+ else
+ {
+ res = compareHStoreBinaryValue(VARDATA(hs1), VARDATA(hs2));
+ }
- if (SRF_IS_FIRSTCALL()) {
- TupleDesc tupdesc;
- MemoryContext oldcontext;
- HStore *hs = PG_GETARG_HS(0);
+ /*
+ * this is a btree support function; this is one of the few places where
+ * memory needs to be explicitly freed.
+ */
+ PG_FREE_IF_COPY(hs1, 0);
+ PG_FREE_IF_COPY(hs2, 1);
+ PG_RETURN_INT32(res);
+}
- funcctx = SRF_FIRSTCALL_INIT();
- oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
- st=(AKStore*)palloc( sizeof(AKStore) );
- st->i=0;
- st->hs = (HStore*)palloc(hs->len);
- memcpy( st->hs, hs, hs->len );
- funcctx->user_fctx = (void*)st;
-
- tupdesc = RelationNameGetTupleDesc("hs_each");
- funcctx->slot = TupleDescGetSlot(tupdesc);
- funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
- MemoryContextSwitchTo(oldcontext);
- PG_FREE_IF_COPY(hs,0);
- }
+PG_FUNCTION_INFO_V1(hstore_eq);
+Datum hstore_eq(PG_FUNCTION_ARGS);
+Datum
+hstore_eq(PG_FUNCTION_ARGS)
+{
+ int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
- funcctx = SRF_PERCALL_SETUP();
- st = (AKStore*)funcctx->user_fctx;
-
- if ( st->i < st->hs->size ) {
- HEntry *ptr = &(ARRPTR(st->hs)[st->i]);
- Datum res, dvalues[2];
- char nulls[] = {' ', ' '};
- text *item;
- HeapTuple tuple;
-
- item=(text*)palloc(VARHDRSZ + ptr->keylen);
- VARATT_SIZEP(item) = VARHDRSZ+ptr->keylen;
- memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos, ptr->keylen);
- dvalues[0] = PointerGetDatum(item);
-
- if ( ptr->valisnull ) {
- dvalues[1]=(Datum)0;
- nulls[1]='n';
- } else {
- int vallen = ptr->vallen;
-
- item=(text*)palloc(VARHDRSZ + vallen);
- VARATT_SIZEP(item) = VARHDRSZ+vallen;
- memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos + ptr->keylen, vallen);
- dvalues[1] = PointerGetDatum(item);
- }
- st->i++;
-
- tuple = heap_formtuple(funcctx->attinmeta->tupdesc, dvalues, nulls);
- res = TupleGetDatum(funcctx->slot, tuple);
-
- pfree( DatumGetPointer(dvalues[0]) );
- if ( nulls[1] != 'n' )
- pfree( DatumGetPointer(dvalues[1]) );
+ PG_RETURN_BOOL(res == 0);
+}
- SRF_RETURN_NEXT(funcctx, PointerGetDatum(res));
+PG_FUNCTION_INFO_V1(hstore_ne);
+Datum hstore_ne(PG_FUNCTION_ARGS);
+Datum
+hstore_ne(PG_FUNCTION_ARGS)
+{
+ int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
+
+ PG_RETURN_BOOL(res != 0);
+}
+
+PG_FUNCTION_INFO_V1(hstore_gt);
+Datum hstore_gt(PG_FUNCTION_ARGS);
+Datum
+hstore_gt(PG_FUNCTION_ARGS)
+{
+ int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
+
+ PG_RETURN_BOOL(res > 0);
+}
+
+PG_FUNCTION_INFO_V1(hstore_ge);
+Datum hstore_ge(PG_FUNCTION_ARGS);
+Datum
+hstore_ge(PG_FUNCTION_ARGS)
+{
+ int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
+
+ PG_RETURN_BOOL(res >= 0);
+}
+
+PG_FUNCTION_INFO_V1(hstore_lt);
+Datum hstore_lt(PG_FUNCTION_ARGS);
+Datum
+hstore_lt(PG_FUNCTION_ARGS)
+{
+ int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
+
+ PG_RETURN_BOOL(res < 0);
+}
+
+PG_FUNCTION_INFO_V1(hstore_le);
+Datum hstore_le(PG_FUNCTION_ARGS);
+Datum
+hstore_le(PG_FUNCTION_ARGS)
+{
+ int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
+ PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1)));
+
+ PG_RETURN_BOOL(res <= 0);
+}
+
+
+PG_FUNCTION_INFO_V1(hstore_hash);
+Datum hstore_hash(PG_FUNCTION_ARGS);
+Datum
+hstore_hash(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ HStoreIterator *it;
+ int32 r;
+ HStoreValue v;
+ int crc;
+
+ if (HS_ROOT_COUNT(hs) == 0)
+ PG_RETURN_INT32(0x1EEE);
+
+ it = HStoreIteratorInit(VARDATA(hs));
+ INIT_CRC32(crc);
+
+ while((r = HStoreIteratorGet(&it, &v, false)) != 0)
+ {
+ switch(r)
+ {
+ case WHS_BEGIN_ARRAY:
+ COMP_CRC32(crc, "ab", 3);
+ COMP_CRC32(crc, &v.array.nelems, sizeof(v.array.nelems));
+ COMP_CRC32(crc, &v.array.scalar, sizeof(v.array.scalar));
+ break;
+ case WHS_BEGIN_HASH:
+ COMP_CRC32(crc, "hb", 3);
+ COMP_CRC32(crc, &v.hash.npairs, sizeof(v.hash.npairs));
+ break;
+ case WHS_KEY:
+ COMP_CRC32(crc, "k", 2);
+ case WHS_VALUE:
+ case WHS_ELEM:
+ switch(v.type)
+ {
+ case hsvString:
+ COMP_CRC32(crc, v.string.val, v.string.len);
+ break;
+ case hsvNull:
+ COMP_CRC32(crc, "N", 2);
+ break;
+ case hsvBool:
+ COMP_CRC32(crc, &v.boolean, sizeof(v.boolean));
+ break;
+ case hsvNumeric:
+ crc ^= DatumGetInt32(DirectFunctionCall1(hash_numeric,
+ NumericGetDatum(v.numeric)));
+ break;
+ default:
+ elog(ERROR, "unexpected state of hstore iterator");
+ }
+
+ break;
+ case WHS_END_ARRAY:
+ COMP_CRC32(crc, "ae", 3);
+ break;
+ case WHS_END_HASH:
+ COMP_CRC32(crc, "he", 3);
+ break;
+ default:
+ elog(ERROR, "unexpected state of hstore iterator");
+ }
}
-
- pfree( st->hs );
- pfree( st );
- SRF_RETURN_DONE(funcctx);
+ FIN_CRC32(crc);
+
+ PG_FREE_IF_COPY(hs, 0);
+ PG_RETURN_INT32(crc);
}
+PG_FUNCTION_INFO_V1(hstore_typeof);
+Datum hstore_typeof(PG_FUNCTION_ARGS);
+Datum
+hstore_typeof(PG_FUNCTION_ARGS)
+{
+ HStore *hs = PG_GETARG_HS(0);
+ HStoreIterator *it;
+ HStoreValue v;
+ uint32 r;
+
+ if (HS_ISEMPTY(hs))
+ PG_RETURN_NULL();
+
+ it = HStoreIteratorInit(VARDATA(hs));
+ r = HStoreIteratorGet(&it, &v, false);
+
+ switch(r)
+ {
+ case WHS_BEGIN_ARRAY:
+ if (v.array.scalar)
+ {
+ Assert(v.array.nelems == 1);
+ r = HStoreIteratorGet(&it, &v, false);
+ Assert(r == WHS_ELEM);
+
+ switch(v.type)
+ {
+ case hsvNull:
+ PG_RETURN_TEXT_P(cstring_to_text("null"));
+ case hsvBool:
+ PG_RETURN_TEXT_P(cstring_to_text("bool"));
+ case hsvNumeric:
+ PG_RETURN_TEXT_P(cstring_to_text("numeric"));
+ case hsvString:
+ PG_RETURN_TEXT_P(cstring_to_text("string"));
+ default:
+ elog(ERROR, "bogus hstore");
+ }
+ }
+ else
+ {
+ PG_RETURN_TEXT_P(cstring_to_text("array"));
+ }
+ case WHS_BEGIN_HASH:
+ PG_RETURN_TEXT_P(cstring_to_text("hash"));
+ case 0:
+ PG_RETURN_NULL();
+ default:
+ elog(ERROR, "bogus hstore");
+ }
+
+ PG_RETURN_NULL();
+}