/* * 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(); }