nested hstore
[hstore.git] / hstore_op.c
diff --git a/hstore_op.c b/hstore_op.c
new file mode 100644 (file)
index 0000000..ef210cb
--- /dev/null
@@ -0,0 +1,3154 @@
+/*
+ * 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 "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 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;
+               }
+
+               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);
+                       }
+               }
+
+               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);
+                       }
+               }
+
+               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_replace);
+Datum          hstore_replace(PG_FUNCTION_ARGS);
+Datum
+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);
+       }
+
+       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;
+
+               sz = compressHStore(res, VARDATA(out));
+               SET_VARSIZE(out, sz + VARHDRSZ);
+       }
+
+       PG_RETURN_POINTER(out);
+}
+
+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
+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;
+
+               if (res->type == hsvArray && res->array.nelems > 1)
+                       res->array.scalar = false;
+
+               sz = compressHStore(res, VARDATA(out));
+               SET_VARSIZE(out, sz + VARHDRSZ);
+       }
+
+       PG_RETURN_POINTER(out);
+}
+
+PG_FUNCTION_INFO_V1(hstore_akeys);
+Datum          hstore_akeys(PG_FUNCTION_ARGS);
+Datum
+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);
+       }
+
+       d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
+
+       it = HStoreIteratorInit(VARDATA(hs));
+
+       while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
+       {
+               skipNested = true;
+
+               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(hstore_avals);
+Datum          hstore_avals(PG_FUNCTION_ARGS);
+Datum
+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);
+       }
+
+       d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
+       nulls = (bool *) palloc(sizeof(bool) * HS_ROOT_COUNT(hs));
+
+       it = HStoreIteratorInit(VARDATA(hs));
+
+       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++;
+               }
+       }
+
+       a = construct_md_array(d, nulls, 1, &i, &lb,
+                                                  TEXTOID, -1, false, 'i');
+
+       PG_RETURN_POINTER(a);
+}
+
+
+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;
+               }
+       }
+
+       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(hstore_to_matrix);
+Datum          hstore_to_matrix(PG_FUNCTION_ARGS);
+Datum
+hstore_to_matrix(PG_FUNCTION_ARGS)
+{
+       HStore     *hs = PG_GETARG_HS(0);
+       ArrayType  *out = hstore_to_array_internal(hs, 2);
+
+       PG_RETURN_POINTER(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;
+
+       oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+       st = palloc(sizeof(*st));
+
+       st->ctx = funcctx->multi_call_memory_ctx;
+
+       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));
+
+       funcctx->user_fctx = (void *) st;
+
+       if (fcinfo)
+       {
+               TupleDesc       tupdesc;
+
+               /* 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(hstore_skeys);
+Datum          hstore_skeys(PG_FUNCTION_ARGS);
+Datum
+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);
+       }
+
+       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));
+               }
+       }
+
+       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);
+       }
+
+       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));
+               }
+       }
+
+       SRF_RETURN_DONE(funcctx);
+}
+
+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);
+       }
+
+       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);
+}
+
+static HStoreValue*
+getNextValsPath(SetReturningState *st)
+{
+       HStoreValue             *v = NULL;
+
+       if (st->path_len == 0)
+       {
+               /* empty path */
+               if (st->level == 0)
+               {
+                       v = &st->init;
+                       st->level ++;
+               }
+
+               return v;
+       }
+
+       while(st->level >= 0)
+       {
+               uint32  header;
+
+               v = NULL;
+               if (st->path[st->level].v.type != hsvBinary)
+               {
+                       st->level--;
+                       continue;
+               }
+
+               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(hstore_svals_path);
+Datum          hstore_svals_path(PG_FUNCTION_ARGS);
+Datum
+hstore_svals_path(PG_FUNCTION_ARGS)
+{
+       FuncCallContext         *funcctx;
+       SetReturningState       *st;
+       HStoreValue                     *v;
+
+       if (SRF_IS_FIRSTCALL())
+       {
+               funcctx = SRF_FIRSTCALL_INIT();
+               st = setup_firstcall(funcctx, PG_GETARG_HS(0), PG_GETARG_ARRAYTYPE_P(1), NULL);
+       }
+
+       funcctx = SRF_PERCALL_SETUP();
+       st = (SetReturningState *) funcctx->user_fctx;
+
+       if ((v = getNextValsPath(st)) != NULL)
+       {
+               text    *item = HStoreValueToText(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_hvals_path);
+Datum          hstore_hvals_path(PG_FUNCTION_ARGS);
+Datum
+hstore_hvals_path(PG_FUNCTION_ARGS)
+{
+       FuncCallContext         *funcctx;
+       SetReturningState       *st;
+       HStoreValue             *v;
+
+       if (SRF_IS_FIRSTCALL())
+       {
+               funcctx = SRF_FIRSTCALL_INIT();
+               st = setup_firstcall(funcctx, PG_GETARG_HS(0),
+                                                        PG_GETARG_ARRAYTYPE_P(1), NULL);
+       }
+
+       funcctx = SRF_PERCALL_SETUP();
+       st = (SetReturningState *) funcctx->user_fctx;
+
+       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));
+       }
+
+       SRF_RETURN_DONE(funcctx);
+}
+
+PG_FUNCTION_INFO_V1(hstore_each_hstore);
+Datum          hstore_each_hstore(PG_FUNCTION_ARGS);
+Datum
+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;
+
+       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;
+                       }
+
+                       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);
+
+       it1 = HStoreIteratorInit(VARDATA(val));
+       it2 = HStoreIteratorInit(VARDATA(tmpl));
+       res = deepContains(&it1, &it2);
+
+       PG_RETURN_BOOL(res);
+}
+
+
+PG_FUNCTION_INFO_V1(hstore_contained);
+Datum          hstore_contained(PG_FUNCTION_ARGS);
+Datum
+hstore_contained(PG_FUNCTION_ARGS)
+{
+       PG_RETURN_DATUM(DirectFunctionCall2(hstore_contains,
+                                                                               PG_GETARG_DATUM(1),
+                                                                               PG_GETARG_DATUM(0)
+                                                                               ));
+}
+
+/*
+ * 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
+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));
+       }
+
+       /*
+        * 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);
+}
+
+
+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)));
+
+       PG_RETURN_BOOL(res == 0);
+}
+
+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");
+               }
+       }
+
+       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();
+}
+