#include "postgres.h" #include "utils/builtins.h" #include "hstore.h" /**************************************************************************** * Compare Functions * ****************************************************************************/ int compareHStoreStringValue(const void *a, const void *b, void *arg) { const HStoreValue *va = a; const HStoreValue *vb = b; int res; Assert(va->type == hsvString); Assert(vb->type == hsvString); if (va->string.len == vb->string.len) { res = memcmp(va->string.val, vb->string.val, va->string.len); if (res == 0 && arg) *(bool*)arg = true; } else { res = (va->string.len > vb->string.len) ? 1 : -1; } return res; } int compareHStorePair(const void *a, const void *b, void *arg) { const HStorePair *pa = a; const HStorePair *pb = b; int res; res = compareHStoreStringValue(&pa->key, &pb->key, arg); /* * guarantee keeping order of equal pair. Unique algorithm will * prefer first element as value */ if (res == 0) res = (pa->order > pb->order) ? -1 : 1; return res; } int compareHStoreValue(HStoreValue *a, HStoreValue *b) { if (a->type == b->type) { switch(a->type) { case hsvNull: return 0; case hsvString: return compareHStoreStringValue(a, b, NULL); case hsvBool: if (a->boolean == b->boolean) return 0; return (a->boolean > b->boolean) ? 1 : -1; case hsvNumeric: return DatumGetInt32(DirectFunctionCall2(numeric_cmp, PointerGetDatum(a->numeric), PointerGetDatum(b->numeric))); case hsvArray: if (a->array.nelems == b->array.nelems) { int i, r; for(i=0; iarray.nelems; i++) if ((r = compareHStoreValue(a->array.elems + i, b->array.elems + i)) != 0) return r; return 0; } return (a->array.nelems > b->array.nelems) ? 1 : -1; case hsvHash: if (a->hash.npairs == b->hash.npairs) { int i, r; for(i=0; ihash.npairs; i++) { if ((r = compareHStoreStringValue(&a->hash.pairs[i].key, &b->hash.pairs[i].key, NULL)) != 0) return r; if ((r = compareHStoreValue(&a->hash.pairs[i].value, &b->hash.pairs[i].value)) != 0) return r; } return 0; } return (a->hash.npairs > b->hash.npairs) ? 1 : -1; case hsvBinary: return compareHStoreBinaryValue(a->binary.data, b->binary.data); default: elog(PANIC, "unknown HStoreValue->type: %d", a->type); } } return (a->type > b->type) ? 1 : -1; } int compareHStoreBinaryValue(char *a, char *b) { HStoreIterator *it1, *it2; int res = 0; it1 = HStoreIteratorInit(a); it2 = HStoreIteratorInit(b); while(res == 0) { HStoreValue v1, v2; int r1, r2; r1 = HStoreIteratorGet(&it1, &v1, false); r2 = HStoreIteratorGet(&it2, &v2, false); if (r1 == r2) { if (r1 == 0) break; /* equal */ if (v1.type == v2.type) { switch(v1.type) { case hsvString: res = compareHStoreStringValue(&v1, &v2, NULL); break; case hsvBool: if (v1.boolean == v2.boolean) res = 0; else res = (v1.boolean > v2.boolean) ? 1 : -1; break; case hsvNumeric: res = DatumGetInt32(DirectFunctionCall2(numeric_cmp, PointerGetDatum(v1.numeric), PointerGetDatum(v2.numeric))); break; case hsvArray: if (v1.array.nelems != v2.array.nelems) res = (v1.array.nelems > v2.array.nelems) ? 1 : -1; break; case hsvHash: if (v1.hash.npairs != v2.hash.npairs) res = (v1.hash.npairs > v2.hash.npairs) ? 1 : -1; break; default: break; } } else { res = (v1.type > v2.type) ? 1 : -1; /* dummy order */ } } else { res = (r1 > r2) ? 1 : -1; /* dummy order */ } } return res; } /* * Sort and unique pairs in hash-like HStoreValue */ void uniqueHStoreValue(HStoreValue *v) { bool hasNonUniq = false; Assert(v->type == hsvHash); if (v->hash.npairs > 1) qsort_arg(v->hash.pairs, v->hash.npairs, sizeof(*v->hash.pairs), compareHStorePair, &hasNonUniq); if (hasNonUniq) { HStorePair *ptr = v->hash.pairs + 1, *res = v->hash.pairs; while(ptr - v->hash.pairs < v->hash.npairs) { if (ptr->key.string.len == res->key.string.len && memcmp(ptr->key.string.val, res->key.string.val, ptr->key.string.len) == 0) { v->size -= ptr->key.size + ptr->value.size; } else { res++; if (ptr != res) memcpy(res, ptr, sizeof(*res)); } ptr++; } v->hash.npairs = res + 1 - v->hash.pairs; } } /**************************************************************************** * find string key in hash or array * ****************************************************************************/ HStoreValue* findUncompressedHStoreValueByValue(char *buffer, uint32 flags, uint32 *lowbound, HStoreValue *key) { uint32 header = *(uint32*)buffer; static HStoreValue r; Assert((header & (HS_FLAG_ARRAY | HS_FLAG_HSTORE)) != (HS_FLAG_ARRAY | HS_FLAG_HSTORE)); if (flags & HS_FLAG_ARRAY & header) { HEntry *array = (HEntry*)(buffer + sizeof(header)); char *data = (char*)(array + (header & HS_COUNT_MASK)); int i; for(i=(lowbound) ? *lowbound : 0; i<(header & HS_COUNT_MASK); i++) { HEntry *e = array + i; if (HSE_ISNULL(*e) && key->type == hsvNull) { r.type = hsvNull; if (lowbound) *lowbound = i; r.size = sizeof(HEntry); return &r; } else if (HSE_ISSTRING(*e) && key->type == hsvString ) { if (key->string.len == HSE_LEN(*e) && memcmp(key->string.val, data + HSE_OFF(*e), key->string.len) == 0) { r.type = hsvString; r.string.val = data + HSE_OFF(*e); r.string.len = key->string.len; r.size = sizeof(HEntry) + r.string.len; if (lowbound) *lowbound = i; return &r; } } else if (HSE_ISBOOL(*e) && key->type == hsvBool) { if ((HSE_ISBOOL_TRUE(*e) && key->boolean == true) || (HSE_ISBOOL_FALSE(*e) && key->boolean == false)) { r = *key; r.size = sizeof(HEntry); if (lowbound) *lowbound = i; return &r; } } else if (HSE_ISNUMERIC(*e) && key->type == hsvNumeric) { if (DatumGetBool(DirectFunctionCall2(numeric_eq, PointerGetDatum(data + INTALIGN(HSE_OFF(*e))), PointerGetDatum(key->numeric))) == true) { r.type = hsvNumeric; r.numeric = (Numeric)(data + INTALIGN(HSE_OFF(*e))); if (lowbound) *lowbound = i; return &r; } } } } else if (flags & HS_FLAG_HSTORE & header) { HEntry *array = (HEntry*)(buffer + sizeof(header)); char *data = (char*)(array + (header & HS_COUNT_MASK) * 2); uint32 stopLow = lowbound ? *lowbound : 0, stopHigh = (header & HS_COUNT_MASK), stopMiddle; if (key->type != hsvString) return NULL; while (stopLow < stopHigh) { int difference; HEntry *e; stopMiddle = stopLow + (stopHigh - stopLow) / 2; e = array + stopMiddle * 2; if (key->string.len == HSE_LEN(*e)) difference = memcmp(data + HSE_OFF(*e), key->string.val, key->string.len); else difference = (HSE_LEN(*e) > key->string.len) ? 1 : -1; if (difference == 0) { HEntry *v = e + 1; if (lowbound) *lowbound = stopMiddle + 1; if (HSE_ISSTRING(*v)) { r.type = hsvString; r.string.val = data + HSE_OFF(*v); r.string.len = HSE_LEN(*v); r.size = sizeof(HEntry) + r.string.len; } else if (HSE_ISBOOL(*v)) { r.type = hsvBool; r.boolean = (HSE_ISBOOL_TRUE(*v)) ? true : false; r.size = sizeof(HEntry); } else if (HSE_ISNUMERIC(*v)) { r.type = hsvNumeric; r.numeric = (Numeric)(data + INTALIGN(HSE_OFF(*v))); r.size = 2*sizeof(HEntry) + VARSIZE_ANY(r.numeric); } else if (HSE_ISNULL(*v)) { r.type = hsvNull; r.size = sizeof(HEntry); } else { r.type = hsvBinary; r.binary.data = data + INTALIGN(HSE_OFF(*v)); r.binary.len = HSE_LEN(*v) - (INTALIGN(HSE_OFF(*v)) - HSE_OFF(*v)); r.size = 2*sizeof(HEntry) + r.binary.len; } return &r; } else if (difference < 0) { stopLow = stopMiddle + 1; } else { stopHigh = stopMiddle; } } if (lowbound) *lowbound = stopLow; } return NULL; } HStoreValue* findUncompressedHStoreValue(char *buffer, uint32 flags, uint32 *lowbound, char *key, uint32 keylen) { HStoreValue v; if (key == NULL) { v.type = hsvNull; } else { v.type = hsvString; v.string.val = key; v.string.len = keylen; } return findUncompressedHStoreValueByValue(buffer, flags, lowbound, &v); } HStoreValue* getHStoreValue(char *buffer, uint32 flags, int32 i) { uint32 header = *(uint32*)buffer; static HStoreValue r; HEntry *array, *e; char *data; Assert((header & (HS_FLAG_ARRAY | HS_FLAG_HSTORE)) != (HS_FLAG_ARRAY | HS_FLAG_HSTORE)); if (i >= 0) { if (i >= (header & HS_COUNT_MASK)) return NULL; } else { if (-i > (header & HS_COUNT_MASK)) return NULL; i = (header & HS_COUNT_MASK) + i; } array = (HEntry*)(buffer + sizeof(header)); if (flags & HS_FLAG_ARRAY & header) { e = array + i; data = (char*)(array + (header & HS_COUNT_MASK)); } else if (flags & HS_FLAG_HSTORE & header) { e = array + i * 2 + 1; data = (char*)(array + (header & HS_COUNT_MASK) * 2); } else { return NULL; } if (HSE_ISSTRING(*e)) { r.type = hsvString; r.string.val = data + HSE_OFF(*e); r.string.len = HSE_LEN(*e); r.size = sizeof(HEntry) + r.string.len; } else if (HSE_ISBOOL(*e)) { r.type = hsvBool; r.boolean = (HSE_ISBOOL_TRUE(*e)) ? true : false; r.size = sizeof(HEntry); } else if (HSE_ISNUMERIC(*e)) { r.type = hsvNumeric; r.numeric = (Numeric)(data + INTALIGN(HSE_OFF(*e))); r.size = 2*sizeof(HEntry) + VARSIZE_ANY(r.numeric); } else if (HSE_ISNULL(*e)) { r.type = hsvNull; r.size = sizeof(HEntry); } else { r.type = hsvBinary; r.binary.data = data + INTALIGN(HSE_OFF(*e)); r.binary.len = HSE_LEN(*e) - (INTALIGN(HSE_OFF(*e)) - HSE_OFF(*e)); r.size = r.binary.len + 2*sizeof(HEntry); } return &r; } /**************************************************************************** * Walk on tree representation of hstore * ****************************************************************************/ static void walkUncompressedHStoreDo(HStoreValue *v, walk_hstore_cb cb, void *cb_arg, uint32 level) { int i; switch(v->type) { case hsvArray: cb(cb_arg, v, WHS_BEGIN_ARRAY, level); for(i=0; iarray.nelems; i++) { if (v->array.elems[i].type == hsvNull || v->array.elems[i].type == hsvString || v->array.elems[i].type == hsvBool || v->array.elems[i].type == hsvNumeric || v->array.elems[i].type == hsvBinary) cb(cb_arg, v->array.elems + i, WHS_ELEM, level); else walkUncompressedHStoreDo(v->array.elems + i, cb, cb_arg, level + 1); } cb(cb_arg, v, WHS_END_ARRAY, level); break; case hsvHash: cb(cb_arg, v, WHS_BEGIN_HASH, level); for(i=0; ihash.npairs; i++) { cb(cb_arg, &v->hash.pairs[i].key, WHS_KEY, level); if (v->hash.pairs[i].value.type == hsvNull || v->hash.pairs[i].value.type == hsvString || v->hash.pairs[i].value.type == hsvBool || v->hash.pairs[i].value.type == hsvNumeric || v->hash.pairs[i].value.type == hsvBinary) cb(cb_arg, &v->hash.pairs[i].value, WHS_VALUE, level); else walkUncompressedHStoreDo(&v->hash.pairs[i].value, cb, cb_arg, level + 1); } cb(cb_arg, v, WHS_END_HASH, level); break; default: elog(PANIC, "impossible HStoreValue->type: %d", v->type); } } void walkUncompressedHStore(HStoreValue *v, walk_hstore_cb cb, void *cb_arg) { if (v) walkUncompressedHStoreDo(v, cb, cb_arg, 0); } /**************************************************************************** * Iteration over binary hstore * ****************************************************************************/ static void parseBuffer(HStoreIterator *it, char *buffer) { uint32 header = *(uint32*)buffer; it->type = header & (HS_FLAG_ARRAY | HS_FLAG_HSTORE); it->nelems = header & HS_COUNT_MASK; it->buffer = buffer; buffer += sizeof(uint32); it->array = (HEntry*)buffer; it->state = hsi_start; switch(it->type) { case HS_FLAG_ARRAY: it->data = buffer + it->nelems * sizeof(HEntry); it->isScalar = (header & HS_FLAG_SCALAR) ? true : false; Assert(it->isScalar == false || it->nelems == 1); break; case HS_FLAG_HSTORE: it->data = buffer + it->nelems * sizeof(HEntry) * 2; break; default: elog(PANIC, "impossible type: %08x", it->type); } } HStoreIterator* HStoreIteratorInit(char *buffer) { HStoreIterator *it = palloc(sizeof(*it)); parseBuffer(it, buffer); it->next = NULL; return it; } static bool formAnswer(HStoreIterator **it, HStoreValue *v, HEntry *e, bool skipNested) { if (HSE_ISSTRING(*e)) { v->type = hsvString; v->string.val = (*it)->data + HSE_OFF(*e); v->string.len = HSE_LEN(*e); v->size = sizeof(HEntry) + v->string.len; return false; } else if (HSE_ISBOOL(*e)) { v->type = hsvBool; v->boolean = (HSE_ISBOOL_TRUE(*e)) ? true : false; v->size = sizeof(HEntry); return false; } else if (HSE_ISNUMERIC(*e)) { v->type = hsvNumeric; v->numeric = (Numeric)((*it)->data + INTALIGN(HSE_OFF(*e))); v->size = 2*sizeof(HEntry) + VARSIZE_ANY(v->numeric); return false; } else if (HSE_ISNULL(*e)) { v->type = hsvNull; v->size = sizeof(HEntry); return false; } else if (skipNested) { v->type = hsvBinary; v->binary.data = (*it)->data + INTALIGN(HSE_OFF(*e)); v->binary.len = HSE_LEN(*e) - (INTALIGN(HSE_OFF(*e)) - HSE_OFF(*e)); v->size = v->binary.len + 2*sizeof(HEntry); return false; } else { HStoreIterator *nit = palloc(sizeof(*nit)); parseBuffer(nit, (*it)->data + INTALIGN(HSE_OFF(*e))); nit->next = *it; *it = nit; return true; } } static HStoreIterator* up(HStoreIterator *it) { HStoreIterator *v = it->next; pfree(it); return v; } int HStoreIteratorGet(HStoreIterator **it, HStoreValue *v, bool skipNested) { int res; if (*it == NULL) return 0; /* * Encode all possible states by one integer. That's possible * because enum members of HStoreIterator->state uses different bits * than HS_FLAG_ARRAY/HS_FLAG_HSTORE. See definition of HStoreIterator */ switch((*it)->type | (*it)->state) { case HS_FLAG_ARRAY | hsi_start: (*it)->state = hsi_elem; (*it)->i = 0; v->type = hsvArray; v->array.nelems = (*it)->nelems; res = WHS_BEGIN_ARRAY; v->array.scalar = (*it)->isScalar; break; case HS_FLAG_ARRAY | hsi_elem: if ((*it)->i >= (*it)->nelems) { *it = up(*it); res = WHS_END_ARRAY; } else if (formAnswer(it, v, &(*it)->array[ (*it)->i++ ], skipNested)) { res = HStoreIteratorGet(it, v, skipNested); } else { res = WHS_ELEM; } break; case HS_FLAG_HSTORE | hsi_start: (*it)->state = hsi_key; (*it)->i = 0; v->type = hsvHash; v->hash.npairs = (*it)->nelems; res = WHS_BEGIN_HASH; break; case HS_FLAG_HSTORE | hsi_key: if ((*it)->i >= (*it)->nelems) { *it = up(*it); res = WHS_END_HASH; } else { formAnswer(it, v, &(*it)->array[ (*it)->i * 2 ], false); (*it)->state = hsi_value; res = WHS_KEY; } break; case HS_FLAG_HSTORE | hsi_value: (*it)->state = hsi_key; if (formAnswer(it, v, &(*it)->array[ ((*it)->i++) * 2 + 1], skipNested)) res = HStoreIteratorGet(it, v, skipNested); else res = WHS_VALUE; break; default: elog(PANIC,"unknown state %08x", (*it)->type & (*it)->state); } return res; } /**************************************************************************** * Transformation from tree to binary representation of hstore * ****************************************************************************/ typedef struct CompressState { char *begin; char *ptr; struct { uint32 i; uint32 *header; HEntry *array; char *begin; } *levelstate, *lptr, *pptr; uint32 maxlevel; } CompressState; #define curLevelState state->lptr #define prevLevelState state->pptr static void putHEntryString(CompressState *state, HStoreValue* value, uint32 level, uint32 i) { curLevelState = state->levelstate + level; if (i == 0) curLevelState->array[0].entry = HENTRY_ISFIRST; else curLevelState->array[i].entry = 0; switch(value->type) { case hsvNull: curLevelState->array[i].entry |= HENTRY_ISNULL; if (i>0) curLevelState->array[i].entry |= curLevelState->array[i - 1].entry & HENTRY_POSMASK; break; case hsvString: memcpy(state->ptr, value->string.val, value->string.len); state->ptr += value->string.len; if (i == 0) curLevelState->array[i].entry |= value->string.len; else curLevelState->array[i].entry |= (curLevelState->array[i - 1].entry & HENTRY_POSMASK) + value->string.len; break; case hsvBool: curLevelState->array[i].entry |= (value->boolean) ? HENTRY_ISTRUE : HENTRY_ISFALSE; if (i>0) curLevelState->array[i].entry |= curLevelState->array[i - 1].entry & HENTRY_POSMASK; break; case hsvNumeric: { int addlen = INTALIGN(state->ptr - state->begin) - (state->ptr - state->begin); int numlen = VARSIZE_ANY(value->numeric); switch(addlen) { case 3: *state->ptr = '\0'; state->ptr++; case 2: *state->ptr = '\0'; state->ptr++; case 1: *state->ptr = '\0'; state->ptr++; case 0: default: break; } memcpy(state->ptr, value->numeric, numlen); state->ptr += numlen; curLevelState->array[i].entry |= HENTRY_ISNUMERIC; if (i == 0) curLevelState->array[i].entry |= addlen + numlen; else curLevelState->array[i].entry |= (curLevelState->array[i - 1].entry & HENTRY_POSMASK) + addlen + numlen; break; } case hsvBinary: { int addlen = INTALIGN(state->ptr - state->begin) - (state->ptr - state->begin); switch(addlen) { case 3: *state->ptr = '\0'; state->ptr++; case 2: *state->ptr = '\0'; state->ptr++; case 1: *state->ptr = '\0'; state->ptr++; case 0: default: break; } memcpy(state->ptr, value->binary.data, value->binary.len); state->ptr += value->binary.len; curLevelState->array[i].entry |= HENTRY_ISNEST; if (i == 0) curLevelState->array[i].entry |= addlen + value->binary.len; else curLevelState->array[i].entry |= (curLevelState->array[i - 1].entry & HENTRY_POSMASK) + addlen + value->binary.len; } break; default: elog(PANIC,"Unsupported HStoreValue type: %d", value->type); } } static void compressCallback(void *arg, HStoreValue* value, uint32 flags, uint32 level) { CompressState *state = arg; if (level == state->maxlevel) { state->maxlevel *= 2; state->levelstate = repalloc(state->levelstate, sizeof(*state->levelstate) * state->maxlevel); } curLevelState = state->levelstate + level; if (flags & (WHS_BEGIN_ARRAY | WHS_BEGIN_HASH)) { Assert(((flags & WHS_BEGIN_ARRAY) && value->type == hsvArray) || ((flags & WHS_BEGIN_HASH) && value->type == hsvHash)); curLevelState->begin = state->ptr; switch(INTALIGN(state->ptr - state->begin) - (state->ptr - state->begin)) { case 3: *state->ptr = '\0'; state->ptr++; case 2: *state->ptr = '\0'; state->ptr++; case 1: *state->ptr = '\0'; state->ptr++; case 0: default: break; } curLevelState->header = (uint32*)state->ptr; state->ptr += sizeof(*curLevelState->header); curLevelState->array = (HEntry*)state->ptr; curLevelState->i = 0; if (value->type == hsvArray) { *curLevelState->header = value->array.nelems | HS_FLAG_ARRAY ; state->ptr += sizeof(HEntry) * value->array.nelems; if (value->array.scalar) { Assert(value->array.nelems == 1); Assert(level == 0); *curLevelState->header |= HS_FLAG_SCALAR; } } else { *curLevelState->header = value->hash.npairs | HS_FLAG_HSTORE ; state->ptr += sizeof(HEntry) * value->hash.npairs * 2; } if (level == 0) *curLevelState->header |= HS_FLAG_NEWVERSION; } else if (flags & WHS_ELEM) { putHEntryString(state, value, level, curLevelState->i); curLevelState->i++; } else if (flags & WHS_KEY) { Assert(value->type == hsvString); putHEntryString(state, value, level, curLevelState->i * 2); } else if (flags & WHS_VALUE) { putHEntryString(state, value, level, curLevelState->i * 2 + 1); curLevelState->i++; } else if (flags & (WHS_END_ARRAY | WHS_END_HASH)) { uint32 len, i; Assert(((flags & WHS_END_ARRAY) && value->type == hsvArray) || ((flags & WHS_END_HASH) && value->type == hsvHash)); if (level == 0) return; len = state->ptr - (char*)curLevelState->begin; prevLevelState = curLevelState - 1; if (*prevLevelState->header & HS_FLAG_ARRAY) { i = prevLevelState->i; prevLevelState->array[i].entry = HENTRY_ISNEST; if (i == 0) prevLevelState->array[0].entry |= HENTRY_ISFIRST | len; else prevLevelState->array[i].entry |= (prevLevelState->array[i - 1].entry & HENTRY_POSMASK) + len; } else if (*prevLevelState->header & HS_FLAG_HSTORE) { i = 2 * prevLevelState->i + 1; /* VALUE, not a KEY */ prevLevelState->array[i].entry = HENTRY_ISNEST; prevLevelState->array[i].entry |= (prevLevelState->array[i - 1].entry & HENTRY_POSMASK) + len; } else { elog(PANIC, "Wrong parent"); } Assert(state->ptr - curLevelState->begin <= value->size); prevLevelState->i++; } else { elog(PANIC, "Wrong flags"); } } uint32 compressHStore(HStoreValue *v, char *buffer) { uint32 l = 0; CompressState state; state.begin = state.ptr = buffer; state.maxlevel = 8; state.levelstate = palloc(sizeof(*state.levelstate) * state.maxlevel); walkUncompressedHStore(v, compressCallback, &state); l = state.ptr - buffer; Assert(l <= v->size); return l; } /**************************************************************************** * Iteration-like forming hstore * * Note: it believ by default in already sorted keys in hash, * * although with r == WHS_END_HASH and v == NULL it will sort itself * ****************************************************************************/ static ToHStoreState* pushState(ToHStoreState **state) { ToHStoreState *ns = palloc(sizeof(*ns)); ns->next = *state; return ns; } static void appendArray(ToHStoreState *state, HStoreValue *v) { HStoreValue *a = &state->v; Assert(a->type == hsvArray); if (a->array.nelems >= state->size) { state->size *= 2; a->array.elems = repalloc(a->array.elems, sizeof(*a->array.elems) * state->size); } a->array.elems[a->array.nelems ++] = *v; a->size += v->size; } static void appendKey(ToHStoreState *state, HStoreValue *v) { HStoreValue *h = &state->v; Assert(h->type == hsvHash); if (h->hash.npairs >= state->size) { state->size *= 2; h->hash.pairs = repalloc(h->hash.pairs, sizeof(*h->hash.pairs) * state->size); } h->hash.pairs[h->hash.npairs].key = *v; h->hash.pairs[h->hash.npairs].order = h->hash.npairs; h->size += v->size; } static void appendValue(ToHStoreState *state, HStoreValue *v) { HStoreValue *h = &state->v; Assert(h->type == hsvHash); h->hash.pairs[h->hash.npairs++].value = *v; h->size += v->size; } HStoreValue* pushHStoreValue(ToHStoreState **state, int r /* WHS_* */, HStoreValue *v) { HStoreValue *h = NULL; switch(r) { case WHS_BEGIN_ARRAY: *state = pushState(state); h = &(*state)->v; (*state)->v.type = hsvArray; (*state)->v.size = 3*sizeof(HEntry); (*state)->v.array.nelems = 0; (*state)->v.array.scalar = (v && v->array.scalar) ? true : false; (*state)->size = (v && v->type == hsvArray && v->array.nelems > 0) ? v->array.nelems : 4; (*state)->v.array.elems = palloc(sizeof(*(*state)->v.array.elems) * (*state)->size); break; case WHS_BEGIN_HASH: *state = pushState(state); h = &(*state)->v; (*state)->v.type = hsvHash; (*state)->v.size = 3*sizeof(HEntry); (*state)->v.hash.npairs = 0; (*state)->size = (v && v->type == hsvHash && v->hash.npairs > 0) ? v->hash.npairs : 4; (*state)->v.hash.pairs = palloc(sizeof(*(*state)->v.hash.pairs) * (*state)->size); break; case WHS_ELEM: Assert(v->type == hsvNull || v->type == hsvString || v->type == hsvBool || v->type == hsvNumeric || v->type == hsvBinary); appendArray(*state, v); break; case WHS_KEY: Assert(v->type == hsvString); appendKey(*state, v); break; case WHS_VALUE: Assert(v->type == hsvNull || v->type == hsvString || v->type == hsvBool || v->type == hsvNumeric || v->type == hsvBinary); appendValue(*state, v); break; case WHS_END_HASH: h = &(*state)->v; if (v == NULL) uniqueHStoreValue(h); case WHS_END_ARRAY: h = &(*state)->v; *state = (*state)->next; if (*state) { switch((*state)->v.type) { case hsvArray: appendArray(*state, h); break; case hsvHash: appendValue(*state, h); break; default: elog(PANIC, "wrong parent type: %d", (*state)->v.type); } } break; default: elog(PANIC, "wrong type: %08x", r); } return h; }