X-Git-Url: http://www.sigaev.ru/git/gitweb.cgi?a=blobdiff_plain;f=hstore_op.c;h=ef210cba5cded12cc2fde350fb77973fdab65ddd;hb=HEAD;hp=12f783cb256c3ee21cf47484baf941a292fc9d29;hpb=82407e8609ec9afe3e7cdeb293c4ebac42ecd133;p=hstore.git diff --git a/hstore_op.c b/hstore_op.c index 12f783c..ef210cb 100644 --- a/hstore_op.c +++ b/hstore_op.c @@ -1,567 +1,3154 @@ -#include "hstore.h" -#include "utils/array.h" +/* + * contrib/hstore/hstore_op.c + */ +#include "postgres.h" + +#include "access/hash.h" +#include "access/htup_details.h" #include "catalog/pg_type.h" #include "funcapi.h" -#include -#include +#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 HEntry * -findkey(HStore *hs, char *key, int keylen) { - HEntry *StopLow = ARRPTR(hs); - HEntry *StopHigh = StopLow + hs->size; - HEntry *StopMiddle; - int difference; - char *base = STRPTR(hs); +static HStoreValue* +replacePathDo(HStoreIterator **it, Datum *path_elems, + bool *path_nulls, int path_len, + ToHStoreState **st, int level, HStoreValue *newval) +{ + HStoreValue v, *res = NULL; + int r; + + r = HStoreIteratorGet(it, &v, false); + + if (r == WHS_BEGIN_ARRAY) + { + int idx, i; + uint32 n = v.array.nelems; + + idx = n; + if (level >= path_len || path_nulls[level] || + h_atoi(VARDATA_ANY(path_elems[level]), + VARSIZE_ANY_EXHDR(path_elems[level]), &idx) == false) + { + idx = n; + } + else if (idx < 0) + { + if (-idx > n) + idx = n; + else + idx = n + idx; + } - while (StopLow < StopHigh) { - StopMiddle = StopLow + (StopHigh - StopLow) / 2; + if (idx > n) + idx = n; + + pushHStoreValue(st, r, &v); + + for(i=0; ikeylen == keylen ) - difference=strncmp(base+StopMiddle->pos, key, StopMiddle->keylen); - else - difference=(StopMiddle->keylen > keylen) ? 1 : -1; + r = HStoreIteratorGet(it, &v, true); + Assert(r == WHS_END_ARRAY); + res = pushHStoreValue(st, r, &v); + } + else if (r == WHS_BEGIN_HASH) + { + int i; + uint32 n = v.hash.npairs; + HStoreValue k; + bool done = false; + + pushHStoreValue(st, WHS_BEGIN_HASH, &v); + + if (level >= path_len || path_nulls[level]) + done = true; + + for(i=0; i 1) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + errmsg("wrong number of array subscripts"))); + + if (HS_ROOT_COUNT(in) == 0) + { + memcpy(out, in, VARSIZE(in)); + PG_RETURN_POINTER(out); + } - if ((entry=findkey(hs,VARDATA(key), VARSIZE(key)-VARHDRSZ))==NULL || entry->valisnull) { - PG_FREE_IF_COPY(hs,0); - PG_FREE_IF_COPY(key,1); - PG_RETURN_NULL(); + deconstruct_array(path, TEXTOID, -1, false, 'i', + &path_elems, &path_nulls, &path_len); + + if (path_len == 0) + { + memcpy(out, in, VARSIZE(in)); + PG_RETURN_POINTER(out); + } + + if (HS_ROOT_COUNT(newval) == 0) + { + value.type = hsvNull; + value.size = sizeof(HEntry); } + else + { + value.type = hsvBinary; + value.binary.data = VARDATA(newval); + value.binary.len = VARSIZE_ANY_EXHDR(newval); + value.size = value.binary.len + sizeof(HEntry); + } + + it = HStoreIteratorInit(VARDATA(in)); + + res = replacePathDo(&it, path_elems, path_nulls, path_len, &st, 0, &value); + + if (res == NULL) + { + SET_VARSIZE(out, VARHDRSZ); + } + else + { + int sz; - out=palloc(VARHDRSZ+entry->vallen); - memcpy(VARDATA(out),STRPTR(hs) + entry->pos + entry->keylen, entry->vallen); - VARATT_SIZEP(out) = VARHDRSZ+entry->vallen; + sz = compressHStore(res, VARDATA(out)); + SET_VARSIZE(out, sz + VARHDRSZ); + } - PG_FREE_IF_COPY(hs,0); - PG_FREE_IF_COPY(key,1); PG_RETURN_POINTER(out); } -PG_FUNCTION_INFO_V1(exists); -Datum exists(PG_FUNCTION_ARGS); +static HStoreValue* +concatPathDo(HStoreIterator **it, Datum *path_elems, + bool *path_nulls, int path_len, + ToHStoreState **st, int level, HStoreIterator *toConcat) +{ + HStoreValue v, *res = NULL; + int r; + + r = HStoreIteratorGet(it, &v, false); + + if (r == WHS_BEGIN_ARRAY) + { + int idx, i; + uint32 n = v.array.nelems; + + idx = n; + if (level >= path_len || path_nulls[level] || + h_atoi(VARDATA_ANY(path_elems[level]), + VARSIZE_ANY_EXHDR(path_elems[level]), &idx) == false) + { + idx = n; + } + else if (idx < 0) + { + if (-idx > n) + idx = n; + else + idx = n + idx; + } + + if (idx > n) + idx = n; + + pushHStoreValue(st, r, &v); + + for(i=0; i= 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; - entry=findkey(hs,VARDATA(key), VARSIZE(key)-VARHDRSZ); + if (res->type == hsvArray && res->array.nelems > 1) + res->array.scalar = false; - PG_FREE_IF_COPY(hs,0); - PG_FREE_IF_COPY(key,1); + sz = compressHStore(res, VARDATA(out)); + SET_VARSIZE(out, sz + VARHDRSZ); + } - PG_RETURN_BOOL(entry); + PG_RETURN_POINTER(out); } -PG_FUNCTION_INFO_V1(defined); -Datum defined(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(hstore_akeys); +Datum hstore_akeys(PG_FUNCTION_ARGS); Datum -defined(PG_FUNCTION_ARGS) { - HStore *hs = PG_GETARG_HS(0); - text *key = PG_GETARG_TEXT_P(1); - HEntry *entry; - bool res; +hstore_akeys(PG_FUNCTION_ARGS) +{ + HStore *hs = PG_GETARG_HS(0); + Datum *d; + ArrayType *a; + int i = 0, r = 0; + HStoreIterator *it; + HStoreValue v; + bool skipNested = false; + + if (HS_ISEMPTY(hs)) + { + a = construct_empty_array(TEXTOID); + PG_RETURN_POINTER(a); + } - entry=findkey(hs,VARDATA(key), VARSIZE(key)-VARHDRSZ); + d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs)); - res = ( entry && !entry->valisnull ) ? true : false; + it = HStoreIteratorInit(VARDATA(hs)); - PG_FREE_IF_COPY(hs,0); - PG_FREE_IF_COPY(key,1); + while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0) + { + skipNested = true; - PG_RETURN_BOOL(res); + if ((r == WHS_ELEM && v.type != hsvNull) || r == WHS_KEY) + d[i++] = PointerGetDatum(HStoreValueToText(&v)); + } + + a = construct_array(d, i, + TEXTOID, -1, false, 'i'); + + PG_RETURN_POINTER(a); } -PG_FUNCTION_INFO_V1(delete); -Datum delete(PG_FUNCTION_ARGS); + +PG_FUNCTION_INFO_V1(hstore_avals); +Datum hstore_avals(PG_FUNCTION_ARGS); Datum -delete(PG_FUNCTION_ARGS) { - HStore *hs = PG_GETARG_HS(0); - text *key = PG_GETARG_TEXT_P(1); - HStore *out = palloc(hs->len); - char *ptrs, *ptrd; - HEntry *es, *ed; +hstore_avals(PG_FUNCTION_ARGS) +{ + HStore *hs = PG_GETARG_HS(0); + Datum *d; + ArrayType *a; + int i = 0, r = 0; + HStoreIterator *it; + HStoreValue v; + bool skipNested = false; + bool *nulls; + int lb = 1; + + if (HS_ISEMPTY(hs)) + { + a = construct_empty_array(TEXTOID); + PG_RETURN_POINTER(a); + } - out->len=hs->len; - out->size=hs->size; /* temprorary! */ + d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs)); + nulls = (bool *) palloc(sizeof(bool) * HS_ROOT_COUNT(hs)); - ptrs=STRPTR(hs); - es =ARRPTR(hs); - ptrd=STRPTR(out); - ed =ARRPTR(out); + it = HStoreIteratorInit(VARDATA(hs)); - while( es - ARRPTR(hs) < hs->size ) { - if ( !(es->keylen == VARSIZE(key) - VARHDRSZ && strncmp(ptrs, VARDATA(key), es->keylen)==0) ) { - memcpy( ed, es, sizeof(HEntry) ); - memcpy( ptrd, ptrs, es->keylen + ( (es->valisnull) ? 0 : es->vallen ) ); - ed->pos = ptrd - STRPTR(out); - ptrd += es->keylen + ( (es->valisnull) ? 0 : es->vallen ); - ed++; + while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0) + { + skipNested = true; + + if (r == WHS_ELEM || r == WHS_VALUE) + { + d[i] = PointerGetDatum(HStoreValueToText(&v)); + nulls[i] = (DatumGetPointer(d[i]) == NULL) ? true : false; + i++; } - ptrs += es->keylen + ( (es->valisnull) ? 0 : es->vallen ); - es++; } - if ( ed - ARRPTR(out) != out->size ) { - int buflen=ptrd-STRPTR(out); - ptrd = STRPTR(out); + a = construct_md_array(d, nulls, 1, &i, &lb, + TEXTOID, -1, false, 'i'); + + PG_RETURN_POINTER(a); +} - out->size = ed - ARRPTR(out); - memmove( STRPTR(out), ptrd, buflen); - out->len = CALCDATASIZE(out->size, buflen); +static ArrayType * +hstore_to_array_internal(HStore *hs, int ndims) +{ + int count = HS_ROOT_COUNT(hs); + int out_size[2] = {0, 2}; + int lb[2] = {1, 1}; + Datum *out_datums; + bool *out_nulls; + bool isHash = HS_ROOT_IS_HASH(hs) ? true : false; + int i = 0, r = 0; + HStoreIterator *it; + HStoreValue v; + bool skipNested = false; + + Assert(ndims < 3); + + if (count == 0 || ndims == 0) + return construct_empty_array(TEXTOID); + + if (isHash == false && ndims == 2 && count % 2 != 0) + elog(ERROR, "hstore's array should have even number of elements"); + + out_size[0] = count * (isHash ? 2 : 1) / ndims; + out_datums = palloc(sizeof(Datum) * count * 2); + out_nulls = palloc(sizeof(bool) * count * 2); + + it = HStoreIteratorInit(VARDATA(hs)); + + while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0) + { + skipNested = true; + + switch(r) + { + case WHS_ELEM: + out_datums[i] = PointerGetDatum(HStoreValueToText(&v)); + out_nulls[i] = (DatumGetPointer(out_datums[i]) == NULL) ? true : false; + i++; + break; + case WHS_KEY: + out_datums[i * 2] = PointerGetDatum(HStoreValueToText(&v)); + out_nulls[i * 2] = (DatumGetPointer(out_datums[i * 2]) == NULL) ? true : false; + break; + case WHS_VALUE: + out_datums[i * 2 + 1] = PointerGetDatum(HStoreValueToText(&v)); + out_nulls[i * 2 + 1] = (DatumGetPointer(out_datums[i * 2 + 1]) == NULL) ? true : false; + i++; + break; + default: + break; + } } - - PG_FREE_IF_COPY(hs,0); - PG_FREE_IF_COPY(key,1); + return construct_md_array(out_datums, out_nulls, + ndims, out_size, lb, + TEXTOID, -1, false, 'i'); +} + +PG_FUNCTION_INFO_V1(hstore_to_array); +Datum hstore_to_array(PG_FUNCTION_ARGS); +Datum +hstore_to_array(PG_FUNCTION_ARGS) +{ + HStore *hs = PG_GETARG_HS(0); + ArrayType *out = hstore_to_array_internal(hs, 1); PG_RETURN_POINTER(out); } -PG_FUNCTION_INFO_V1(hs_concat); -Datum hs_concat(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(hstore_to_matrix); +Datum hstore_to_matrix(PG_FUNCTION_ARGS); Datum -hs_concat(PG_FUNCTION_ARGS) { - HStore *s1 = PG_GETARG_HS(0); - HStore *s2 = PG_GETARG_HS(1); - HStore *out = palloc( s1->len + s2->len ); - char *ps1, *ps2, *pd; - HEntry *es1, *es2, *ed; +hstore_to_matrix(PG_FUNCTION_ARGS) +{ + HStore *hs = PG_GETARG_HS(0); + ArrayType *out = hstore_to_array_internal(hs, 2); - out->len = s1->len + s2->len; - out->size = s1->size + s2->size; + PG_RETURN_POINTER(out); +} - ps1=STRPTR(s1); - ps2=STRPTR(s2); - pd=STRPTR(out); - es1=ARRPTR(s1); - es2=ARRPTR(s2); - ed=ARRPTR(out); +/* + * Common initialization function for the various set-returning + * funcs. fcinfo is only passed if the function is to return a + * composite; it will be used to look up the return tupledesc. + * we stash a copy of the hstore in the multi-call context in + * case it was originally toasted. (At least I assume that's why; + * there was no explanatory comment in the original code. --AG) + */ + +typedef struct SetReturningState +{ + HStore *hs; + HStoreIterator *it; + MemoryContext ctx; + + HStoreValue init; + int path_len; + int level; + struct { + HStoreValue v; + Datum varStr; + int varInt; + enum { + pathStr, + pathInt, + pathAny + } varKind; + int i; + } *path; +} SetReturningState; + +static SetReturningState* +setup_firstcall(FuncCallContext *funcctx, HStore *hs, ArrayType *path, + FunctionCallInfoData *fcinfo) +{ + MemoryContext oldcontext; + SetReturningState *st; - while( es1 - ARRPTR(s1) < s1->size && es2 - ARRPTR(s2) < s2->size ) { - int difference; - if ( es1->keylen == es2->keylen ) - difference=strncmp(ps1, ps2, es1->keylen); - else - difference=(es1->keylen > es2->keylen) ? 1 : -1; - - if ( difference == 0 ) { - memcpy( ed, es2, sizeof(HEntry) ); - memcpy( pd, ps2, es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ) ); - ed->pos = pd - STRPTR(out); - pd += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ); - ed++; - - ps1 += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ); - es1++; - ps2 += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ); - es2++; - } else if ( difference > 0 ) { - memcpy( ed, es2, sizeof(HEntry) ); - memcpy( pd, ps2, es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ) ); - ed->pos = pd - STRPTR(out); - pd += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ); - ed++; - - ps2 += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ); - es2++; - } else { - memcpy( ed, es1, sizeof(HEntry) ); - memcpy( pd, ps1, es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ) ); - ed->pos = pd - STRPTR(out); - pd += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ); - ed++; - - ps1 += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ); - es1++; - } - } - - while( es1 - ARRPTR(s1) < s1->size ) { - memcpy( ed, es1, sizeof(HEntry) ); - memcpy( pd, ps1, es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ) ); - ed->pos = pd - STRPTR(out); - pd += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ); - ed++; - - ps1 += es1->keylen + ( (es1->valisnull) ? 0 : es1->vallen ); - es1++; - } - - while( es2 - ARRPTR(s2) < s2->size ) { - memcpy( ed, es2, sizeof(HEntry) ); - memcpy( pd, ps2, es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ) ); - ed->pos = pd - STRPTR(out); - pd += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ); - ed++; - - ps2 += es2->keylen + ( (es2->valisnull) ? 0 : es2->vallen ); - es2++; - } - - if ( ed - ARRPTR(out) != out->size ) { - int buflen=pd-STRPTR(out); - pd = STRPTR(out); - - out->size = ed - ARRPTR(out); - - memmove( STRPTR(out), pd, buflen); - out->len = CALCDATASIZE(out->size, buflen); - } - - PG_FREE_IF_COPY(s1,0); - PG_FREE_IF_COPY(s2,1); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - PG_RETURN_POINTER(out); -} + st = palloc(sizeof(*st)); -PG_FUNCTION_INFO_V1(tconvert); -Datum tconvert(PG_FUNCTION_ARGS); -Datum -tconvert(PG_FUNCTION_ARGS) { - text *key = PG_GETARG_TEXT_P(0); - text *val = PG_GETARG_TEXT_P(1); - int len; - HStore *out; + st->ctx = funcctx->multi_call_memory_ctx; - len=CALCDATASIZE(1, VARSIZE(key) + VARSIZE(val) - 2*VARHDRSZ); - out = palloc(len); - out->len=len; - out->size=1; + st->hs = (HStore *) palloc(VARSIZE(hs)); + memcpy(st->hs, hs, VARSIZE(hs)); + if (HS_ISEMPTY(hs) || path) + st->it = NULL; + else + st->it = HStoreIteratorInit(VARDATA(st->hs)); - ARRPTR(out)->keylen = VARSIZE(key) - VARHDRSZ; - ARRPTR(out)->vallen = VARSIZE(val) - VARHDRSZ; - ARRPTR(out)->valisnull = false; - ARRPTR(out)->pos=0; + funcctx->user_fctx = (void *) st; - memcpy( STRPTR(out), VARDATA(key), ARRPTR(out)->keylen ); - memcpy( STRPTR(out) + ARRPTR(out)->keylen, VARDATA(val), ARRPTR(out)->vallen ); - - PG_FREE_IF_COPY(key,0); - PG_FREE_IF_COPY(val,1); + if (fcinfo) + { + TupleDesc tupdesc; - PG_RETURN_POINTER(out); + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + funcctx->tuple_desc = BlessTupleDesc(tupdesc); + } + + st->path_len = st->level = 0; + if (path) + { + Datum *path_elems; + bool *path_nulls; + int i; + + Assert(ARR_ELEMTYPE(path) == TEXTOID); + if (ARR_NDIM(path) > 1) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + errmsg("wrong number of array subscripts"))); + + deconstruct_array(path, TEXTOID, -1, false, 'i', + &path_elems, &path_nulls, &st->path_len); + + st->init.type = hsvBinary; + st->init.size = VARSIZE(st->hs); + st->init.binary.data = VARDATA(st->hs); + st->init.binary.len = VARSIZE_ANY_EXHDR(st->hs); + + if (st->path_len > 0) + { + st->path = palloc(sizeof(*st->path) * st->path_len); + st->path[0].v = st->init; + } + + for(i=0; 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(akeys); -Datum akeys(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(hstore_skeys); +Datum hstore_skeys(PG_FUNCTION_ARGS); Datum -akeys(PG_FUNCTION_ARGS) { - HStore *hs = PG_GETARG_HS(0); - Datum *d; - ArrayType *a; - HEntry *ptr=ARRPTR(hs); - char *base=STRPTR(hs); +hstore_skeys(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + SetReturningState *st; + int r; + HStoreValue v; + + if (SRF_IS_FIRSTCALL()) + { + funcctx = SRF_FIRSTCALL_INIT(); + st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, NULL); + } - d=(Datum*)palloc(sizeof(Datum)*(hs->size+1)); - while( ptr-ARRPTR(hs) < hs->size ) { - text *item=(text*)palloc(VARHDRSZ + ptr->keylen); - VARATT_SIZEP(item) = VARHDRSZ+ptr->keylen; - memcpy(VARDATA(item), base + ptr->pos, ptr->keylen); - d[ ptr-ARRPTR(hs) ] = PointerGetDatum(item); - ptr++; + funcctx = SRF_PERCALL_SETUP(); + st = (SetReturningState *) funcctx->user_fctx; + + while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0) + { + if (r == WHS_KEY || r == WHS_ELEM) + { + text *item = HStoreValueToText(&v); + + if (item == NULL) + SRF_RETURN_NEXT_NULL(funcctx); + else + SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); + } } - - a = construct_array( - d, - hs->size, - TEXTOID, - -1, - false, - 'i' - ); - ptr=ARRPTR(hs); - while( ptr-ARRPTR(hs) < hs->size ) { - pfree(DatumGetPointer(d[ ptr-ARRPTR(hs) ])); - ptr++; + SRF_RETURN_DONE(funcctx); +} + +PG_FUNCTION_INFO_V1(hstore_svals); +Datum hstore_svals(PG_FUNCTION_ARGS); +Datum +hstore_svals(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + SetReturningState *st; + int r; + HStoreValue v; + + if (SRF_IS_FIRSTCALL()) + { + funcctx = SRF_FIRSTCALL_INIT(); + st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, NULL); } - pfree(d); - PG_FREE_IF_COPY(hs,0); + funcctx = SRF_PERCALL_SETUP(); + st = (SetReturningState *) funcctx->user_fctx; + + while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0) + { + if (r == WHS_VALUE || r == WHS_ELEM) + { + text *item = HStoreValueToText(&v); + + if (item == NULL) + SRF_RETURN_NEXT_NULL(funcctx); + else + SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); + } + } - PG_RETURN_POINTER(a); + SRF_RETURN_DONE(funcctx); } -PG_FUNCTION_INFO_V1(avals); -Datum avals(PG_FUNCTION_ARGS); -Datum -avals(PG_FUNCTION_ARGS) { - HStore *hs = PG_GETARG_HS(0); - Datum *d; - ArrayType *a; - HEntry *ptr=ARRPTR(hs); - char *base=STRPTR(hs); - - d=(Datum*)palloc(sizeof(Datum)*(hs->size+1)); - while( ptr-ARRPTR(hs) < hs->size ) { - int vallen = (ptr->valisnull) ? 0 : ptr->vallen; - text *item=(text*)palloc(VARHDRSZ + vallen); - VARATT_SIZEP(item) = VARHDRSZ+vallen; - memcpy(VARDATA(item), base + ptr->pos + ptr->keylen, vallen); - d[ ptr-ARRPTR(hs) ] = PointerGetDatum(item); - ptr++; - } - - a = construct_array( - d, - hs->size, - TEXTOID, - -1, - false, - 'i' - ); - - ptr=ARRPTR(hs); - while( ptr-ARRPTR(hs) < hs->size ) { - pfree(DatumGetPointer(d[ ptr-ARRPTR(hs) ])); - ptr++; - } - - pfree(d); - PG_FREE_IF_COPY(hs,0); +PG_FUNCTION_INFO_V1(hstore_hvals); +Datum hstore_hvals(PG_FUNCTION_ARGS); +Datum +hstore_hvals(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + SetReturningState *st; + int r; + HStoreValue v; + + if (SRF_IS_FIRSTCALL()) + { + funcctx = SRF_FIRSTCALL_INIT(); + st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, NULL); + } - PG_RETURN_POINTER(a); + funcctx = SRF_PERCALL_SETUP(); + st = (SetReturningState *) funcctx->user_fctx; + + while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0) + { + if (r == WHS_VALUE || r == WHS_ELEM) + { + HStore *item = HStoreValueToHStore(&v); + + if (item == NULL) + SRF_RETURN_NEXT_NULL(funcctx); + else + SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); + } + } + + SRF_RETURN_DONE(funcctx); } -typedef struct { - HStore *hs; - int i; -} AKStore; +static HStoreValue* +getNextValsPath(SetReturningState *st) +{ + HStoreValue *v = NULL; + + if (st->path_len == 0) + { + /* empty path */ + if (st->level == 0) + { + v = &st->init; + st->level ++; + } -static void -setup_firstcall(FuncCallContext *funcctx, HStore *hs) { - MemoryContext oldcontext; - AKStore *st; + return v; + } - oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + while(st->level >= 0) + { + uint32 header; - st=(AKStore*)palloc( sizeof(AKStore) ); - st->i=0; - st->hs = (HStore*)palloc(hs->len); - memcpy( st->hs, hs, hs->len ); + v = NULL; + if (st->path[st->level].v.type != hsvBinary) + { + st->level--; + continue; + } - funcctx->user_fctx = (void*)st; - MemoryContextSwitchTo(oldcontext); + header = *(uint32*)st->path[st->level].v.binary.data; + + if (header & HS_FLAG_HSTORE) + { + if (st->path[st->level].varKind == pathAny) + { + v = getHStoreValue(st->path[st->level].v.binary.data, + HS_FLAG_HSTORE, + st->path[st->level].i++); + } + else + { + v = findUncompressedHStoreValue(st->path[st->level].v.binary.data, + HS_FLAG_HSTORE, NULL, + VARDATA_ANY(st->path[st->level].varStr), + VARSIZE_ANY_EXHDR(st->path[st->level].varStr)); + } + } + else if (header & HS_FLAG_ARRAY) + { + if (st->path[st->level].varKind == pathAny) + { + v = getHStoreValue(st->path[st->level].v.binary.data, + HS_FLAG_ARRAY, st->path[st->level].i++); + } + else if (st->path[st->level].varKind == pathInt) + { + int ith = st->path[st->level].varInt; + + if (ith < 0) + { + if (-ith > (int)(header & HS_COUNT_MASK)) + { + st->level--; + continue; + } + else + { + ith = ((int)(header & HS_COUNT_MASK)) + ith; + } + } + else + { + if (ith >= (int)(header & HS_COUNT_MASK)) + { + st->level--; + continue; + } + } + + v = getHStoreValue(st->path[st->level].v.binary.data, + HS_FLAG_ARRAY, ith); + } + else + { + st->level--; + continue; + } + } + else + { + elog(PANIC, "impossible state"); + } + + if (v == NULL) + { + st->level--; + } + else if (st->level == st->path_len - 1) + { + if (st->path[st->level].varKind != pathAny) + { + st->path[st->level].v.type = hsvNull; + st->level--; + } + break; + } + else + { + if (st->path[st->level].varKind != pathAny) + st->path[st->level].v.type = hsvNull; + st->level++; + st->path[st->level].v = *v; + st->path[st->level].i = 0; + } + } + + return v; } -PG_FUNCTION_INFO_V1(skeys); -Datum skeys(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(hstore_svals_path); +Datum hstore_svals_path(PG_FUNCTION_ARGS); Datum -skeys(PG_FUNCTION_ARGS) { - FuncCallContext *funcctx; - AKStore *st; - - if (SRF_IS_FIRSTCALL()) { - HStore *hs = PG_GETARG_HS(0); +hstore_svals_path(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + SetReturningState *st; + HStoreValue *v; + + if (SRF_IS_FIRSTCALL()) + { funcctx = SRF_FIRSTCALL_INIT(); - setup_firstcall(funcctx, hs); - PG_FREE_IF_COPY(hs,0); + st = setup_firstcall(funcctx, PG_GETARG_HS(0), PG_GETARG_ARRAYTYPE_P(1), NULL); } funcctx = SRF_PERCALL_SETUP(); - st = (AKStore*)funcctx->user_fctx; - - if ( st->i < st->hs->size ) { - HEntry *ptr = &(ARRPTR(st->hs)[st->i]); - text *item=(text*)palloc(VARHDRSZ + ptr->keylen); + st = (SetReturningState *) funcctx->user_fctx; - VARATT_SIZEP(item) = VARHDRSZ+ptr->keylen; - memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos, ptr->keylen); - st->i++; + if ((v = getNextValsPath(st)) != NULL) + { + text *item = HStoreValueToText(v); - SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); + if (item == NULL) + SRF_RETURN_NEXT_NULL(funcctx); + else + SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); } - - pfree( st->hs ); - pfree( st ); - SRF_RETURN_DONE(funcctx); + SRF_RETURN_DONE(funcctx); } -PG_FUNCTION_INFO_V1(svals); -Datum svals(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(hstore_hvals_path); +Datum hstore_hvals_path(PG_FUNCTION_ARGS); Datum -svals(PG_FUNCTION_ARGS) { - FuncCallContext *funcctx; - AKStore *st; - - if (SRF_IS_FIRSTCALL()) { - HStore *hs = PG_GETARG_HS(0); +hstore_hvals_path(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + SetReturningState *st; + HStoreValue *v; + + if (SRF_IS_FIRSTCALL()) + { funcctx = SRF_FIRSTCALL_INIT(); - setup_firstcall(funcctx, hs); - PG_FREE_IF_COPY(hs,0); + st = setup_firstcall(funcctx, PG_GETARG_HS(0), + PG_GETARG_ARRAYTYPE_P(1), NULL); } funcctx = SRF_PERCALL_SETUP(); - st = (AKStore*)funcctx->user_fctx; - - if ( st->i < st->hs->size ) { - HEntry *ptr = &(ARRPTR(st->hs)[st->i]); - - if ( ptr->valisnull ) { - ReturnSetInfo *rsi; - - st->i++; - (funcctx)->call_cntr++; - rsi = (ReturnSetInfo *) fcinfo->resultinfo; - rsi->isDone = ExprMultipleResult; - PG_RETURN_NULL(); - } else { - int vallen = ptr->vallen; - text *item=(text*)palloc(VARHDRSZ + vallen); + st = (SetReturningState *) funcctx->user_fctx; - VARATT_SIZEP(item) = VARHDRSZ+vallen; - memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos + ptr->keylen, vallen); - st->i++; + if ((v = getNextValsPath(st)) != NULL) + { + HStore *item = HStoreValueToHStore(v); + if (item == NULL) + SRF_RETURN_NEXT_NULL(funcctx); + else SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); + } + + SRF_RETURN_DONE(funcctx); +} + +PG_FUNCTION_INFO_V1(hstore_each); +Datum hstore_each(PG_FUNCTION_ARGS); +Datum +hstore_each(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + SetReturningState *st; + int r; + HStoreValue v; + + if (SRF_IS_FIRSTCALL()) + { + funcctx = SRF_FIRSTCALL_INIT(); + st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo); + } + + funcctx = SRF_PERCALL_SETUP(); + st = (SetReturningState *) funcctx->user_fctx; + + while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0) + { + Datum res, + dvalues[2] = {0, 0}; + bool nulls[2] = {false, false}; + text *item; + HeapTuple tuple; + + if (r == WHS_ELEM) + { + nulls[0] = true; + + item = HStoreValueToText(&v); + if (item == NULL) + nulls[1] = true; + else + dvalues[1] = PointerGetDatum(item); + } + else if (r == WHS_KEY) + { + item = HStoreValueToText(&v); + dvalues[0] = PointerGetDatum(item); + + r = HStoreIteratorGetCtx(st, &v, true); + Assert(r == WHS_VALUE); + item = HStoreValueToText(&v); + if (item == NULL) + nulls[1] = true; + else + dvalues[1] = PointerGetDatum(item); } + else + { + continue; + } + + tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls); + res = HeapTupleGetDatum(tuple); + + SRF_RETURN_NEXT(funcctx, PointerGetDatum(res)); } - - pfree( st->hs ); - pfree( st ); - SRF_RETURN_DONE(funcctx); + SRF_RETURN_DONE(funcctx); } -PG_FUNCTION_INFO_V1(hs_contains); -Datum hs_contains(PG_FUNCTION_ARGS); +PG_FUNCTION_INFO_V1(hstore_each_hstore); +Datum hstore_each_hstore(PG_FUNCTION_ARGS); Datum -hs_contains(PG_FUNCTION_ARGS) { - HStore *val = PG_GETARG_HS(0); - HStore *tmpl = PG_GETARG_HS(1); - bool res = true; - HEntry *te = ARRPTR(tmpl); - char *vv = STRPTR(val); - char *tv = STRPTR(tmpl); +hstore_each_hstore(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + SetReturningState *st; + int r; + HStoreValue v; + + if (SRF_IS_FIRSTCALL()) + { + funcctx = SRF_FIRSTCALL_INIT(); + st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo); + } + + funcctx = SRF_PERCALL_SETUP(); + st = (SetReturningState *) funcctx->user_fctx; + + while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0) + { + Datum res, + dvalues[2] = {0, 0}; + bool nulls[2] = {false, false}; + text *item; + HStore *hitem; + HeapTuple tuple; + + if (r == WHS_ELEM) + { + nulls[0] = true; + + hitem = HStoreValueToHStore(&v); + if (hitem == NULL) + nulls[1] = true; + else + dvalues[1] = PointerGetDatum(hitem); + } + else if (r == WHS_KEY) + { + item = HStoreValueToText(&v); + dvalues[0] = PointerGetDatum(item); + + r = HStoreIteratorGetCtx(st, &v, true); + Assert(r == WHS_VALUE); + hitem = HStoreValueToHStore(&v); + if (hitem == NULL) + nulls[1] = true; + else + dvalues[1] = PointerGetDatum(hitem); + } + else + { + continue; + } + + tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls); + res = HeapTupleGetDatum(tuple); + + SRF_RETURN_NEXT(funcctx, PointerGetDatum(res)); + } + + SRF_RETURN_DONE(funcctx); +} + +static bool +deepContains(HStoreIterator **it1, HStoreIterator **it2) +{ + uint32 r1, r2; + HStoreValue v1, v2; + bool res = true; - while(res && te-ARRPTR(tmpl) < tmpl->size) { - HEntry *entry = findkey(val, tv + te->pos, te->keylen); - if ( entry ) { - if ( ! te->valisnull ) { - if ( entry->valisnull || !( - te->vallen==entry->vallen && - strncmp( - vv + entry->pos + entry->keylen, - tv + te->pos + te->keylen, - te->vallen ) == 0 - ) ) - res=false; + r1 = HStoreIteratorGet(it1, &v1, false); + r2 = HStoreIteratorGet(it2, &v2, false); + + if (r1 != r2) + { + res = false; + } + else if (r1 == WHS_BEGIN_HASH) + { + uint32 lowbound = 0; + HStoreValue *v; + + for(;;) { + r2 = HStoreIteratorGet(it2, &v2, false); + if (r2 == WHS_END_HASH) + break; + + Assert(r2 == WHS_KEY); + + v = findUncompressedHStoreValueByValue((*it1)->buffer, + HS_FLAG_HSTORE, + &lowbound, &v2); + + if (v == NULL) + { + res = false; + break; } - } else - res = false; - te++; + + r2 = HStoreIteratorGet(it2, &v2, true); + Assert(r2 == WHS_VALUE); + + if (v->type != v2.type) + { + res = false; + break; + } + else if (v->type == hsvString || v->type == hsvNull || + v->type == hsvBool || v->type == hsvNumeric) + { + if (compareHStoreValue(v, &v2) != 0) + { + res = false; + break; + } + } + else + { + HStoreIterator *it1a, *it2a; + + Assert(v2.type == hsvBinary); + Assert(v->type == hsvBinary); + + it1a = HStoreIteratorInit(v->binary.data); + it2a = HStoreIteratorInit(v2.binary.data); + + if ((res = deepContains(&it1a, &it2a)) == false) + break; + } + } + } + else if (r1 == WHS_BEGIN_ARRAY) + { + HStoreValue *v; + HStoreValue *av = NULL; + uint32 nelems = v1.array.nelems; + + for(;;) { + r2 = HStoreIteratorGet(it2, &v2, true); + if (r2 == WHS_END_ARRAY) + break; + + Assert(r2 == WHS_ELEM); + + if (v2.type == hsvString || v2.type == hsvNull || + v2.type == hsvBool || v2.type == hsvNumeric) + { + v = findUncompressedHStoreValueByValue((*it1)->buffer, + HS_FLAG_ARRAY, NULL, + &v2); + if (v == NULL) + { + res = false; + break; + } + } + else + { + uint32 i; + + if (av == NULL) + { + uint32 j = 0; + + av = palloc(sizeof(*av) * nelems); + + for(i=0; imulti_call_memory_ctx); - st=(AKStore*)palloc( sizeof(AKStore) ); - st->i=0; - st->hs = (HStore*)palloc(hs->len); - memcpy( st->hs, hs, hs->len ); - funcctx->user_fctx = (void*)st; - - tupdesc = RelationNameGetTupleDesc("hs_each"); - funcctx->slot = TupleDescGetSlot(tupdesc); - funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); - MemoryContextSwitchTo(oldcontext); - PG_FREE_IF_COPY(hs,0); - } +PG_FUNCTION_INFO_V1(hstore_eq); +Datum hstore_eq(PG_FUNCTION_ARGS); +Datum +hstore_eq(PG_FUNCTION_ARGS) +{ + int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(1))); - funcctx = SRF_PERCALL_SETUP(); - st = (AKStore*)funcctx->user_fctx; - - if ( st->i < st->hs->size ) { - HEntry *ptr = &(ARRPTR(st->hs)[st->i]); - Datum res, dvalues[2]; - char nulls[] = {' ', ' '}; - text *item; - HeapTuple tuple; - - item=(text*)palloc(VARHDRSZ + ptr->keylen); - VARATT_SIZEP(item) = VARHDRSZ+ptr->keylen; - memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos, ptr->keylen); - dvalues[0] = PointerGetDatum(item); - - if ( ptr->valisnull ) { - dvalues[1]=(Datum)0; - nulls[1]='n'; - } else { - int vallen = ptr->vallen; - - item=(text*)palloc(VARHDRSZ + vallen); - VARATT_SIZEP(item) = VARHDRSZ+vallen; - memcpy(VARDATA(item), STRPTR(st->hs) + ptr->pos + ptr->keylen, vallen); - dvalues[1] = PointerGetDatum(item); - } - st->i++; - - tuple = heap_formtuple(funcctx->attinmeta->tupdesc, dvalues, nulls); - res = TupleGetDatum(funcctx->slot, tuple); - - pfree( DatumGetPointer(dvalues[0]) ); - if ( nulls[1] != 'n' ) - pfree( DatumGetPointer(dvalues[1]) ); + PG_RETURN_BOOL(res == 0); +} - SRF_RETURN_NEXT(funcctx, PointerGetDatum(res)); +PG_FUNCTION_INFO_V1(hstore_ne); +Datum hstore_ne(PG_FUNCTION_ARGS); +Datum +hstore_ne(PG_FUNCTION_ARGS) +{ + int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(1))); + + PG_RETURN_BOOL(res != 0); +} + +PG_FUNCTION_INFO_V1(hstore_gt); +Datum hstore_gt(PG_FUNCTION_ARGS); +Datum +hstore_gt(PG_FUNCTION_ARGS) +{ + int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(1))); + + PG_RETURN_BOOL(res > 0); +} + +PG_FUNCTION_INFO_V1(hstore_ge); +Datum hstore_ge(PG_FUNCTION_ARGS); +Datum +hstore_ge(PG_FUNCTION_ARGS) +{ + int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(1))); + + PG_RETURN_BOOL(res >= 0); +} + +PG_FUNCTION_INFO_V1(hstore_lt); +Datum hstore_lt(PG_FUNCTION_ARGS); +Datum +hstore_lt(PG_FUNCTION_ARGS) +{ + int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(1))); + + PG_RETURN_BOOL(res < 0); +} + +PG_FUNCTION_INFO_V1(hstore_le); +Datum hstore_le(PG_FUNCTION_ARGS); +Datum +hstore_le(PG_FUNCTION_ARGS) +{ + int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp, + PG_GETARG_DATUM(0), + PG_GETARG_DATUM(1))); + + PG_RETURN_BOOL(res <= 0); +} + + +PG_FUNCTION_INFO_V1(hstore_hash); +Datum hstore_hash(PG_FUNCTION_ARGS); +Datum +hstore_hash(PG_FUNCTION_ARGS) +{ + HStore *hs = PG_GETARG_HS(0); + HStoreIterator *it; + int32 r; + HStoreValue v; + int crc; + + if (HS_ROOT_COUNT(hs) == 0) + PG_RETURN_INT32(0x1EEE); + + it = HStoreIteratorInit(VARDATA(hs)); + INIT_CRC32(crc); + + while((r = HStoreIteratorGet(&it, &v, false)) != 0) + { + switch(r) + { + case WHS_BEGIN_ARRAY: + COMP_CRC32(crc, "ab", 3); + COMP_CRC32(crc, &v.array.nelems, sizeof(v.array.nelems)); + COMP_CRC32(crc, &v.array.scalar, sizeof(v.array.scalar)); + break; + case WHS_BEGIN_HASH: + COMP_CRC32(crc, "hb", 3); + COMP_CRC32(crc, &v.hash.npairs, sizeof(v.hash.npairs)); + break; + case WHS_KEY: + COMP_CRC32(crc, "k", 2); + case WHS_VALUE: + case WHS_ELEM: + switch(v.type) + { + case hsvString: + COMP_CRC32(crc, v.string.val, v.string.len); + break; + case hsvNull: + COMP_CRC32(crc, "N", 2); + break; + case hsvBool: + COMP_CRC32(crc, &v.boolean, sizeof(v.boolean)); + break; + case hsvNumeric: + crc ^= DatumGetInt32(DirectFunctionCall1(hash_numeric, + NumericGetDatum(v.numeric))); + break; + default: + elog(ERROR, "unexpected state of hstore iterator"); + } + + break; + case WHS_END_ARRAY: + COMP_CRC32(crc, "ae", 3); + break; + case WHS_END_HASH: + COMP_CRC32(crc, "he", 3); + break; + default: + elog(ERROR, "unexpected state of hstore iterator"); + } } - - pfree( st->hs ); - pfree( st ); - SRF_RETURN_DONE(funcctx); + FIN_CRC32(crc); + + PG_FREE_IF_COPY(hs, 0); + PG_RETURN_INT32(crc); } +PG_FUNCTION_INFO_V1(hstore_typeof); +Datum hstore_typeof(PG_FUNCTION_ARGS); +Datum +hstore_typeof(PG_FUNCTION_ARGS) +{ + HStore *hs = PG_GETARG_HS(0); + HStoreIterator *it; + HStoreValue v; + uint32 r; + + if (HS_ISEMPTY(hs)) + PG_RETURN_NULL(); + + it = HStoreIteratorInit(VARDATA(hs)); + r = HStoreIteratorGet(&it, &v, false); + + switch(r) + { + case WHS_BEGIN_ARRAY: + if (v.array.scalar) + { + Assert(v.array.nelems == 1); + r = HStoreIteratorGet(&it, &v, false); + Assert(r == WHS_ELEM); + + switch(v.type) + { + case hsvNull: + PG_RETURN_TEXT_P(cstring_to_text("null")); + case hsvBool: + PG_RETURN_TEXT_P(cstring_to_text("bool")); + case hsvNumeric: + PG_RETURN_TEXT_P(cstring_to_text("numeric")); + case hsvString: + PG_RETURN_TEXT_P(cstring_to_text("string")); + default: + elog(ERROR, "bogus hstore"); + } + } + else + { + PG_RETURN_TEXT_P(cstring_to_text("array")); + } + case WHS_BEGIN_HASH: + PG_RETURN_TEXT_P(cstring_to_text("hash")); + case 0: + PG_RETURN_NULL(); + default: + elog(ERROR, "bogus hstore"); + } + + PG_RETURN_NULL(); +}