HStore * hstorePairs(Pairs *pairs, int4 pcount, int4 buflen) { HStore *out; HEntry *entry; char *ptr; char *buf; int4 len; int4 i; len = CALCDATASIZE(pcount, buflen); out = palloc(len); SET_VARSIZE(out, len); HS_SETCOUNT(out, pcount); if (pcount == 0) return out; entry = ARRPTR(out); buf = ptr = STRPTR(out); for (i = 0; i < pcount; i++) HS_ADDITEM(entry, buf, ptr, pairs[i]); HS_FINALIZE(out, pcount, buf, ptr); return out; }
Datum hstore_delete(PG_FUNCTION_ARGS) { HStore *hs = 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(hs)); char *bufs, *bufd, *ptrd; HEntry *es, *ed; int i; int count = HS_COUNT(hs); int outcount = 0; SET_VARSIZE(out, VARSIZE(hs)); HS_SETCOUNT(out, count); /* temporary! */ bufs = STRPTR(hs); es = ARRPTR(hs); bufd = ptrd = STRPTR(out); ed = ARRPTR(out); for (i = 0; i < count; ++i) { int len = HS_KEYLEN(es, i); char *ptrs = HS_KEY(es, bufs, i); if (!(len == keylen && memcmp(ptrs, keyptr, keylen) == 0)) { int vallen = HS_VALLEN(es, i); HS_COPYITEM(ed, bufd, ptrd, ptrs, len, vallen, HS_VALISNULL(es, i)); ++outcount; } } HS_FINALIZE(out, outcount, bufd, ptrd); PG_RETURN_POINTER(out); }
/* * hstoreUpgrade: PG_DETOAST_DATUM plus support for conversion of old hstores */ HStore * hstoreUpgrade(Datum orig) { HStore *hs = (HStore *) PG_DETOAST_DATUM(orig); int valid_new; int valid_old; bool writable; /* Return immediately if no conversion needed */ if ((hs->size_ & HS_FLAG_NEWVERSION) || hs->size_ == 0 || (VARSIZE(hs) < 32768 && HSE_ISFIRST((ARRPTR(hs)[0])))) return hs; valid_new = hstoreValidNewFormat(hs); valid_old = hstoreValidOldFormat(hs); /* Do we have a writable copy? */ writable = ((void *) hs != (void *) DatumGetPointer(orig)); if (!valid_old || hs->size_ == 0) { if (valid_new) { /* * force the "new version" flag and the correct varlena length, * but only if we have a writable copy already (which we almost * always will, since short new-format values won't come through * here) */ if (writable) { HS_SETCOUNT(hs, HS_COUNT(hs)); HS_FIXSIZE(hs, HS_COUNT(hs)); } return hs; } else { elog(ERROR, "invalid hstore value found"); } } /* * this is the tricky edge case. It is only possible in some quite extreme * cases (the hstore must have had a lot of wasted padding space at the * end). But the only way a "new" hstore value could get here is if we're * upgrading in place from a pre-release version of hstore-new (NOT * contrib/hstore), so we work off the following assumptions: 1. If you're * moving from old contrib/hstore to hstore-new, you're required to fix up * any potential conflicts first, e.g. by running ALTER TABLE ... USING * col::text::hstore; on all hstore columns before upgrading. 2. If you're * moving from old contrib/hstore to new contrib/hstore, then "new" values * are impossible here 3. If you're moving from pre-release hstore-new to * hstore-new, then "old" values are impossible here 4. If you're moving * from pre-release hstore-new to new contrib/hstore, you're not doing so * as an in-place upgrade, so there is no issue So the upshot of all this * is that we can treat all the edge cases as "new" if we're being built * as hstore-new, and "old" if we're being built as contrib/hstore. * * XXX the WARNING can probably be downgraded to DEBUG1 once this has been * beta-tested. But for now, it would be very useful to know if anyone can * actually reach this case in a non-contrived setting. */ if (valid_new) { #if HSTORE_IS_HSTORE_NEW elog(WARNING, "ambiguous hstore value resolved as hstore-new"); /* * force the "new version" flag and the correct varlena length, but * only if we have a writable copy already (which we almost always * will, since short new-format values won't come through here) */ if (writable) { HS_SETCOUNT(hs, HS_COUNT(hs)); HS_FIXSIZE(hs, HS_COUNT(hs)); } return hs; #else elog(WARNING, "ambiguous hstore value resolved as hstore-old"); #endif } /* * must have an old-style value. Overwrite it in place as a new-style one, * making sure we have a writable copy first. */ if (!writable) hs = (HStore *) PG_DETOAST_DATUM_COPY(orig); { int count = hs->size_; HEntry *new_entries = ARRPTR(hs); HOldEntry *old_entries = (HOldEntry *) ARRPTR(hs); int i; for (i = 0; i < count; ++i) { uint32 pos = old_entries[i].pos; uint32 keylen = old_entries[i].keylen; uint32 vallen = old_entries[i].vallen; bool isnull = old_entries[i].valisnull; if (isnull) vallen = 0; new_entries[2 * i].entry = (pos + keylen) & HENTRY_POSMASK; new_entries[2 * i + 1].entry = (((pos + keylen + vallen) & HENTRY_POSMASK) | ((isnull) ? HENTRY_ISNULL : 0)); } if (count) new_entries[0].entry |= HENTRY_ISFIRST; HS_SETCOUNT(hs, count); HS_FIXSIZE(hs, count); } return hs; }
Datum hstore_concat(PG_FUNCTION_ARGS) { HStore *s1 = PG_GETARG_HS(0); HStore *s2 = PG_GETARG_HS(1); HStore *out = palloc(VARSIZE(s1) + VARSIZE(s2)); char *ps1, *ps2, *bufd, *pd; HEntry *es1, *es2, *ed; int s1idx; int s2idx; int s1count = HS_COUNT(s1); int s2count = HS_COUNT(s2); int outcount = 0; SET_VARSIZE(out, VARSIZE(s1) + VARSIZE(s2) - HSHRDSIZE); HS_SETCOUNT(out, s1count + s2count); if (s1count == 0) { /* return a copy of the input, unchanged */ memcpy(out, s2, VARSIZE(s2)); HS_FIXSIZE(out, s2count); HS_SETCOUNT(out, s2count); PG_RETURN_POINTER(out); } if (s2count == 0) { /* return a copy of the input, unchanged */ memcpy(out, s1, VARSIZE(s1)); HS_FIXSIZE(out, s1count); HS_SETCOUNT(out, s1count); PG_RETURN_POINTER(out); } ps1 = STRPTR(s1); ps2 = STRPTR(s2); bufd = pd = STRPTR(out); es1 = ARRPTR(s1); es2 = ARRPTR(s2); ed = ARRPTR(out); /* * this is in effect a merge between s1 and s2, both of which are already * sorted by (keylen,key); we take s2 for equal keys */ for (s1idx = s2idx = 0; s1idx < s1count || s2idx < s2count; ++outcount) { int difference; if (s1idx >= s1count) difference = 1; else if (s2idx >= s2count) difference = -1; else { int s1keylen = HS_KEYLEN(es1, s1idx); int s2keylen = HS_KEYLEN(es2, s2idx); if (s1keylen == s2keylen) difference = memcmp(HS_KEY(es1, ps1, s1idx), HS_KEY(es2, ps2, s2idx), s1keylen); else difference = (s1keylen > s2keylen) ? 1 : -1; } if (difference >= 0) { HS_COPYITEM(ed, bufd, pd, HS_KEY(es2, ps2, s2idx), HS_KEYLEN(es2, s2idx), HS_VALLEN(es2, s2idx), HS_VALISNULL(es2, s2idx)); ++s2idx; if (difference == 0) ++s1idx; } else { HS_COPYITEM(ed, bufd, pd, HS_KEY(es1, ps1, s1idx), HS_KEYLEN(es1, s1idx), HS_VALLEN(es1, s1idx), HS_VALISNULL(es1, s1idx)); ++s1idx; } } HS_FINALIZE(out, outcount, bufd, pd); PG_RETURN_POINTER(out); }
Datum hstore_delete_hstore(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); HStore *hs2 = PG_GETARG_HS(1); HStore *out = palloc(VARSIZE(hs)); int hs_count = HS_COUNT(hs); int hs2_count = HS_COUNT(hs2); char *ps, *ps2, *bufd, *pd; HEntry *es, *es2, *ed; int i, j; int outcount = 0; SET_VARSIZE(out, VARSIZE(hs)); HS_SETCOUNT(out, hs_count); /* temporary! */ ps = STRPTR(hs); es = ARRPTR(hs); ps2 = STRPTR(hs2); es2 = ARRPTR(hs2); bufd = pd = STRPTR(out); ed = ARRPTR(out); if (hs2_count == 0) { /* return a copy of the input, unchanged */ memcpy(out, hs, VARSIZE(hs)); HS_FIXSIZE(out, hs_count); HS_SETCOUNT(out, hs_count); PG_RETURN_POINTER(out); } /* * this is in effect a merge between hs and hs2, both of which are already * sorted by (keylen,key); we take keys from hs only; for equal keys, we * take the value from hs unless the values are equal */ for (i = j = 0; i < hs_count;) { int difference; if (j >= hs2_count) difference = -1; else { int skeylen = HS_KEYLEN(es, i); int s2keylen = HS_KEYLEN(es2, j); if (skeylen == s2keylen) difference = memcmp(HS_KEY(es, ps, i), HS_KEY(es2, ps2, j), skeylen); else difference = (skeylen > s2keylen) ? 1 : -1; } if (difference > 0) ++j; else if (difference == 0) { int svallen = HS_VALLEN(es, i); int snullval = HS_VALISNULL(es, i); if (snullval != HS_VALISNULL(es2, j) || (!snullval && (svallen != HS_VALLEN(es2, j) || memcmp(HS_VAL(es, ps, i), HS_VAL(es2, ps2, j), svallen) != 0))) { HS_COPYITEM(ed, bufd, pd, HS_KEY(es, ps, i), HS_KEYLEN(es, i), svallen, snullval); ++outcount; } ++i, ++j; } else { HS_COPYITEM(ed, bufd, pd, HS_KEY(es, ps, i), HS_KEYLEN(es, i), HS_VALLEN(es, i), HS_VALISNULL(es, i)); ++outcount; ++i; } } HS_FINALIZE(out, outcount, bufd, pd); PG_RETURN_POINTER(out); }
Datum hstore_delete_array(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); HStore *out = palloc(VARSIZE(hs)); int hs_count = HS_COUNT(hs); char *ps, *bufd, *pd; HEntry *es, *ed; int i, j; int outcount = 0; ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(1); int nkeys; Pairs *key_pairs = hstoreArrayToPairs(key_array, &nkeys); SET_VARSIZE(out, VARSIZE(hs)); HS_SETCOUNT(out, hs_count); /* temporary! */ ps = STRPTR(hs); es = ARRPTR(hs); bufd = pd = STRPTR(out); ed = ARRPTR(out); if (nkeys == 0) { /* return a copy of the input, unchanged */ memcpy(out, hs, VARSIZE(hs)); HS_FIXSIZE(out, hs_count); HS_SETCOUNT(out, hs_count); PG_RETURN_POINTER(out); } /* * this is in effect a merge between hs and key_pairs, both of which are * already sorted by (keylen,key); we take keys from hs only */ for (i = j = 0; i < hs_count;) { int difference; if (j >= nkeys) difference = -1; else { int skeylen = HS_KEYLEN(es, i); if (skeylen == key_pairs[j].keylen) difference = memcmp(HS_KEY(es, ps, i), key_pairs[j].key, key_pairs[j].keylen); else difference = (skeylen > key_pairs[j].keylen) ? 1 : -1; } if (difference > 0) ++j; else if (difference == 0) ++i, ++j; else { HS_COPYITEM(ed, bufd, pd, HS_KEY(es, ps, i), HS_KEYLEN(es, i), HS_VALLEN(es, i), HS_VALISNULL(es, i)); ++outcount; ++i; } } HS_FINALIZE(out, outcount, bufd, pd); PG_RETURN_POINTER(out); }