X-Git-Url: http://www.sigaev.ru/git/gitweb.cgi?p=hstore.git;a=blobdiff_plain;f=hstore_op.c;fp=hstore_op.c;h=ef210cba5cded12cc2fde350fb77973fdab65ddd;hp=0000000000000000000000000000000000000000;hb=2d3cb5062568eab105ed554350ac99bae76ee0ec;hpb=77af220c462dd61507d6cca9b9f54ad3e102e1b6 diff --git a/hstore_op.c b/hstore_op.c new file mode 100644 index 0000000..ef210cb --- /dev/null +++ b/hstore_op.c @@ -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 && itype != 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= 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= path_len || path_nulls[level]) + done = true; + + for(i=0; i 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= path_len || path_nulls[level]) + done = true; + + for(i=0; i 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= path_len || path_nulls[level]) + done = true; + + for(i=0; i 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; ipath_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 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(); +} +