Datum hstore_to_plpython(PG_FUNCTION_ARGS) { HStore *in = PG_GETARG_HS(0); int i; int count = HS_COUNT(in); char *base = STRPTR(in); HEntry *entries = ARRPTR(in); PyObject *dict; dict = PyDict_New(); for (i = 0; i < count; i++) { PyObject *key; key = PyString_FromStringAndSize(HSTORE_KEY(entries, base, i), HSTORE_KEYLEN(entries, i)); if (HSTORE_VALISNULL(entries, i)) PyDict_SetItem(dict, key, Py_None); else { PyObject *value; value = PyString_FromStringAndSize(HSTORE_VAL(entries, base, i), HSTORE_VALLEN(entries, i)); PyDict_SetItem(dict, key, value); Py_XDECREF(value); } Py_XDECREF(key); } return PointerGetDatum(dict); }
Datum hstore_skeys(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; HStore *hs; int i; if (SRF_IS_FIRSTCALL()) { hs = PG_GETARG_HS(0); funcctx = SRF_FIRSTCALL_INIT(); setup_firstcall(funcctx, hs, NULL); } funcctx = SRF_PERCALL_SETUP(); hs = (HStore *) funcctx->user_fctx; i = funcctx->call_cntr; if (i < HS_COUNT(hs)) { HEntry *entries = ARRPTR(hs); text *item; item = cstring_to_text_with_len(HS_KEY(entries, STRPTR(hs), i), HS_KEYLEN(entries, i)); SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); } SRF_RETURN_DONE(funcctx); }
Datum ghstore_compress(PG_FUNCTION_ARGS) { GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); GISTENTRY *retval = entry; if (entry->leafkey) { GISTTYPE *res = (GISTTYPE *) palloc0(CALCGTSIZE(0)); HStore *val = DatumGetHStoreP(entry->key); HEntry *hsent = ARRPTR(val); char *ptr = STRPTR(val); int count = HS_COUNT(val); int i; SET_VARSIZE(res, CALCGTSIZE(0)); for (i = 0; i < count; ++i) { int h; h = crc32_sz((char *) HSTORE_KEY(hsent, ptr, i), HSTORE_KEYLEN(hsent, i)); HASH(GETSIGN(res), h); if (!HSTORE_VALISNULL(hsent, i)) { h = crc32_sz((char *) HSTORE_VAL(hsent, ptr, i), HSTORE_VALLEN(hsent, i)); HASH(GETSIGN(res), h); } } retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(res), entry->rel, entry->page, entry->offset, FALSE); } else if (!ISALLTRUE(DatumGetPointer(entry->key))) { int32 i; GISTTYPE *res; BITVECP sign = GETSIGN(DatumGetPointer(entry->key)); LOOPBYTE { if ((sign[i] & 0xff) != 0xff) PG_RETURN_POINTER(retval); } res = (GISTTYPE *) palloc(CALCGTSIZE(ALLISTRUE)); SET_VARSIZE(res, CALCGTSIZE(ALLISTRUE)); res->flag = ALLISTRUE; retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(res), entry->rel, entry->page, entry->offset, FALSE); }
Datum gin_extract_hstore(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); int32 *nentries = (int32 *) PG_GETARG_POINTER(1); Datum *entries = NULL; HEntry *hsent = ARRPTR(hs); char *ptr = STRPTR(hs); int count = HS_COUNT(hs); int i; *nentries = 2 * count; if (count) entries = (Datum *) palloc(sizeof(Datum) * 2 * count); for (i = 0; i < count; ++i) { text *item; item = makeitem(HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i), KEYFLAG); entries[2 * i] = PointerGetDatum(item); if (HS_VALISNULL(hsent, i)) item = makeitem(NULL, 0, NULLFLAG); else item = makeitem(HS_VAL(hsent, ptr, i), HS_VALLEN(hsent, i), VALFLAG); entries[2 * i + 1] = PointerGetDatum(item); } PG_RETURN_POINTER(entries); }
Datum hstore_to_plperl(PG_FUNCTION_ARGS) { HStore *in = PG_GETARG_HS(0); int i; int count = HS_COUNT(in); char *base = STRPTR(in); HEntry *entries = ARRPTR(in); HV *hv; hv = newHV(); for (i = 0; i < count; i++) { const char *key; SV *value; key = pnstrdup(HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); value = HS_VALISNULL(entries, i) ? newSV(0) : cstr2sv(pnstrdup(HS_VAL(entries, base, i), HS_VALLEN(entries, i))); (void) hv_store(hv, key, strlen(key), value, 0); } return PointerGetDatum(newRV((SV *) hv)); }
Datum hstore_send(PG_FUNCTION_ARGS) { HStore *in = PG_GETARG_HS(0); int i; int count = HS_COUNT(in); char *base = STRPTR(in); HEntry *entries = ARRPTR(in); StringInfoData buf; pq_begintypsend(&buf); pq_sendint(&buf, count, 4); for (i = 0; i < count; i++) { int32 keylen = HS_KEYLEN(entries, i); pq_sendint(&buf, keylen, 4); pq_sendtext(&buf, HS_KEY(entries, base, i), keylen); if (HS_VALISNULL(entries, i)) { pq_sendint(&buf, -1, 4); } else { int32 vallen = HS_VALLEN(entries, i); pq_sendint(&buf, vallen, 4); pq_sendtext(&buf, HS_VAL(entries, base, i), vallen); } } PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); }
Datum hstore_akeys(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); Datum *d; ArrayType *a; HEntry *entries = ARRPTR(hs); char *base = STRPTR(hs); int count = HS_COUNT(hs); int i; if (count == 0) { a = construct_empty_array(TEXTOID); PG_RETURN_POINTER(a); } d = (Datum *) palloc(sizeof(Datum) * count); for (i = 0; i < count; ++i) { text *item = cstring_to_text_with_len(HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); d[i] = PointerGetDatum(item); } a = construct_array(d, count, TEXTOID, -1, false, 'i'); PG_RETURN_POINTER(a); }
Datum hstore_hash(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); Datum hval = hash_any((unsigned char *) VARDATA(hs), VARSIZE(hs) - VARHDRSZ); /* * this is the only place in the code that cares whether the overall * varlena size exactly matches the true data size; this assertion should * be maintained by all the other code, but we make it explicit here. */ Assert(VARSIZE(hs) == (HS_COUNT(hs) != 0 ? CALCDATASIZE(HS_COUNT(hs), HSE_ENDPOS(ARRPTR(hs)[2 * HS_COUNT(hs) - 1])) : HSHRDSIZE)); PG_FREE_IF_COPY(hs, 0); PG_RETURN_DATUM(hval); }
Datum hstore_each(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; HStore *hs; int i; if (SRF_IS_FIRSTCALL()) { hs = PG_GETARG_HS(0); funcctx = SRF_FIRSTCALL_INIT(); setup_firstcall(funcctx, hs, fcinfo); } funcctx = SRF_PERCALL_SETUP(); hs = (HStore *) funcctx->user_fctx; i = funcctx->call_cntr; if (i < HS_COUNT(hs)) { HEntry *entries = ARRPTR(hs); char *ptr = STRPTR(hs); Datum res, dvalues[2]; bool nulls[2] = {false, false}; text *item; HeapTuple tuple; item = cstring_to_text_with_len(HS_KEY(entries, ptr, i), HS_KEYLEN(entries, i)); dvalues[0] = PointerGetDatum(item); if (HS_VALISNULL(entries, i)) { dvalues[1] = (Datum) 0; nulls[1] = true; } else { item = cstring_to_text_with_len(HS_VAL(entries, ptr, i), HS_VALLEN(entries, i)); dvalues[1] = PointerGetDatum(item); } tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls); res = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, PointerGetDatum(res)); } SRF_RETURN_DONE(funcctx); }
static ArrayType * hstore_to_array_internal(HStore *hs, int ndims) { HEntry *entries = ARRPTR(hs); char *base = STRPTR(hs); int count = HS_COUNT(hs); int out_size[2] = {0, 2}; int lb[2] = {1, 1}; Datum *out_datums; bool *out_nulls; int i; Assert(ndims < 3); if (count == 0 || ndims == 0) return construct_empty_array(TEXTOID); out_size[0] = count * 2 / ndims; out_datums = palloc(sizeof(Datum) * count * 2); out_nulls = palloc(sizeof(bool) * count * 2); for (i = 0; i < count; ++i) { text *key = cstring_to_text_with_len(HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); out_datums[i * 2] = PointerGetDatum(key); out_nulls[i * 2] = false; if (HS_VALISNULL(entries, i)) { out_datums[i * 2 + 1] = (Datum) 0; out_nulls[i * 2 + 1] = true; } else { text *item = cstring_to_text_with_len(HS_VAL(entries, base, i), HS_VALLEN(entries, i)); out_datums[i * 2 + 1] = PointerGetDatum(item); out_nulls[i * 2 + 1] = false; } } return construct_md_array(out_datums, out_nulls, ndims, out_size, lb, TEXTOID, -1, false, 'i'); }
Datum hstore_to_jsonb(PG_FUNCTION_ARGS) { HStore *in = PG_GETARG_HS(0); int i; int count = HS_COUNT(in); char *base = STRPTR(in); HEntry *entries = ARRPTR(in); JsonbParseState *state = NULL; JsonbValue *res; res = pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL); for (i = 0; i < count; i++) { JsonbValue key, val; key.estSize = sizeof(JEntry); key.type = jbvString; key.val.string.len = HS_KEYLEN(entries, i); key.val.string.val = pnstrdup(HS_KEY(entries, base, i), key.val.string.len); key.estSize += key.val.string.len; res = pushJsonbValue(&state, WJB_KEY, &key); if (HS_VALISNULL(entries, i)) { val.estSize = sizeof(JEntry); val.type = jbvNull; } else { val.estSize = sizeof(JEntry); val.type = jbvString; val.val.string.len = HS_VALLEN(entries, i); val.val.string.val = pnstrdup(HS_VAL(entries, base, i), val.val.string.len); val.estSize += val.val.string.len; } res = pushJsonbValue(&state, WJB_VALUE, &val); } res = pushJsonbValue(&state, WJB_END_OBJECT, NULL); PG_RETURN_POINTER(JsonbValueToJsonb(res)); }
Datum hstore_svals(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; HStore *hs; int i; if (SRF_IS_FIRSTCALL()) { hs = PG_GETARG_HS(0); funcctx = SRF_FIRSTCALL_INIT(); setup_firstcall(funcctx, hs, NULL); } funcctx = SRF_PERCALL_SETUP(); hs = (HStore *) funcctx->user_fctx; i = funcctx->call_cntr; if (i < HS_COUNT(hs)) { HEntry *entries = ARRPTR(hs); if (HS_VALISNULL(entries, i)) { ReturnSetInfo *rsi; /* ugly ugly ugly. why no macro for this? */ (funcctx)->call_cntr++; rsi = (ReturnSetInfo *) fcinfo->resultinfo; rsi->isDone = ExprMultipleResult; PG_RETURN_NULL(); } else { text *item; item = cstring_to_text_with_len(HS_VAL(entries, STRPTR(hs), i), HS_VALLEN(entries, i)); SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); } } SRF_RETURN_DONE(funcctx); }
/* * Validity test for a new-format hstore. * 0 = not valid * 1 = valid but with "slop" in the length * 2 = exactly valid */ static int hstoreValidNewFormat(HStore *hs) { int count = HS_COUNT(hs); HEntry *entries = ARRPTR(hs); int buflen = (count) ? HSE_ENDPOS(entries[2 * (count) - 1]) : 0; int vsize = CALCDATASIZE(count, buflen); int i; if (hs->size_ & HS_FLAG_NEWVERSION) return 2; if (count == 0) return 2; if (!HSE_ISFIRST(entries[0])) return 0; if (vsize > VARSIZE(hs)) return 0; /* entry position must be nondecreasing */ for (i = 1; i < 2 * count; ++i) { if (HSE_ISFIRST(entries[i]) || (HSE_ENDPOS(entries[i]) < HSE_ENDPOS(entries[i - 1]))) return 0; } /* key length must be nondecreasing and keys must not be null */ for (i = 1; i < count; ++i) { if (HS_KEYLEN(entries, i) < HS_KEYLEN(entries, i - 1)) return 0; if (HSE_ISNULL(entries[2 * i])) return 0; } if (vsize != VARSIZE(hs)) return 1; return 2; }
Datum hstore_avals(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); Datum *d; bool *nulls; ArrayType *a; HEntry *entries = ARRPTR(hs); char *base = STRPTR(hs); int count = HS_COUNT(hs); int lb = 1; int i; if (count == 0) { a = construct_empty_array(TEXTOID); PG_RETURN_POINTER(a); } d = (Datum *) palloc(sizeof(Datum) * count); nulls = (bool *) palloc(sizeof(bool) * count); for (i = 0; i < count; ++i) { if (HS_VALISNULL(entries, i)) { d[i] = (Datum) 0; nulls[i] = true; } else { text *item = cstring_to_text_with_len(HS_VAL(entries, base, i), HS_VALLEN(entries, i)); d[i] = PointerGetDatum(item); nulls[i] = false; } } a = construct_md_array(d, nulls, 1, &count, &lb, TEXTOID, -1, false, 'i'); PG_RETURN_POINTER(a); }
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); }
Datum hstore_contains(PG_FUNCTION_ARGS) { HStore *val = PG_GETARG_HS(0); HStore *tmpl = PG_GETARG_HS(1); bool res = true; HEntry *te = ARRPTR(tmpl); char *tstr = STRPTR(tmpl); HEntry *ve = ARRPTR(val); char *vstr = STRPTR(val); int tcount = HS_COUNT(tmpl); int lastidx = 0; int i; /* * we exploit the fact that keys in "tmpl" are in strictly increasing * order to narrow the hstoreFindKey search; each search can start one * entry past the previous "found" entry, or at the lower bound of the * search */ for (i = 0; res && i < tcount; ++i) { int idx = hstoreFindKey(val, &lastidx, HS_KEY(te, tstr, i), HS_KEYLEN(te, i)); if (idx >= 0) { bool nullval = HS_VALISNULL(te, i); int vallen = HS_VALLEN(te, i); if (nullval != HS_VALISNULL(ve, idx) || (!nullval && (vallen != HS_VALLEN(ve, idx) || memcmp(HS_VAL(te, tstr, i), HS_VAL(ve, vstr, idx), vallen)))) res = false; } else res = false; } PG_RETURN_BOOL(res); }
Datum hstore_to_json(PG_FUNCTION_ARGS) { HStore *in = PG_GETARG_HS(0); int i; int count = HS_COUNT(in); char *base = STRPTR(in); HEntry *entries = ARRPTR(in); StringInfoData tmp, dst; if (count == 0) PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2)); initStringInfo(&tmp); initStringInfo(&dst); appendStringInfoChar(&dst, '{'); for (i = 0; i < count; i++) { resetStringInfo(&tmp); appendBinaryStringInfo(&tmp, HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); escape_json(&dst, tmp.data); appendStringInfoString(&dst, ": "); if (HS_VALISNULL(entries, i)) appendStringInfoString(&dst, "null"); else { resetStringInfo(&tmp); appendBinaryStringInfo(&tmp, HS_VAL(entries, base, i), HS_VALLEN(entries, i)); escape_json(&dst, tmp.data); } if (i + 1 != count) appendStringInfoString(&dst, ", "); } appendStringInfoChar(&dst, '}'); PG_RETURN_TEXT_P(cstring_to_text(dst.data)); }
/* * We're often finding a sequence of keys in ascending order. The * "lowbound" parameter is used to cache lower bounds of searches * between calls, based on this assumption. Pass NULL for it for * one-off or unordered searches. */ int hstoreFindKey(HStore *hs, int *lowbound, char *key, int keylen) { HEntry *entries = ARRPTR(hs); int stopLow = lowbound ? *lowbound : 0; int stopHigh = HS_COUNT(hs); int stopMiddle; char *base = STRPTR(hs); while (stopLow < stopHigh) { int difference; stopMiddle = stopLow + (stopHigh - stopLow) / 2; if (HS_KEYLEN(entries, stopMiddle) == keylen) difference = memcmp(HS_KEY(entries, base, stopMiddle), key, keylen); else difference = (HS_KEYLEN(entries, stopMiddle) > keylen) ? 1 : -1; if (difference == 0) { if (lowbound) *lowbound = stopMiddle + 1; return stopMiddle; } else if (difference < 0) stopLow = stopMiddle + 1; else stopHigh = stopMiddle; } if (lowbound) *lowbound = stopLow; return -1; }
Datum hstore_out(PG_FUNCTION_ARGS) { HStore *in = PG_GETARG_HS(0); int buflen, i; int count = HS_COUNT(in); char *out, *ptr; char *base = STRPTR(in); HEntry *entries = ARRPTR(in); if (count == 0) { out = palloc(1); *out = '\0'; PG_RETURN_CSTRING(out); } buflen = 0; /* * this loop overestimates due to pessimistic assumptions about escaping, * so very large hstore values can't be output. this could be fixed, but * many other data types probably have the same issue. This replaced code * that used the original varlena size for calculations, which was wrong * in some subtle ways. */ for (i = 0; i < count; i++) { /* include "" and => and comma-space */ buflen += 6 + 2 * HS_KEYLEN(entries, i); /* include "" only if nonnull */ buflen += 2 + (HS_VALISNULL(entries, i) ? 2 : 2 * HS_VALLEN(entries, i)); } out = ptr = palloc(buflen); for (i = 0; i < count; i++) { *ptr++ = '"'; ptr = cpw(ptr, HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); *ptr++ = '"'; *ptr++ = '='; *ptr++ = '>'; if (HS_VALISNULL(entries, i)) { *ptr++ = 'N'; *ptr++ = 'U'; *ptr++ = 'L'; *ptr++ = 'L'; } else { *ptr++ = '"'; ptr = cpw(ptr, HS_VAL(entries, base, i), HS_VALLEN(entries, i)); *ptr++ = '"'; } if (i + 1 != count) { *ptr++ = ','; *ptr++ = ' '; } } *ptr = '\0'; PG_RETURN_CSTRING(out); }
Datum hstore_to_json_loose(PG_FUNCTION_ARGS) { HStore *in = PG_GETARG_HS(0); int i; int count = HS_COUNT(in); char *base = STRPTR(in); HEntry *entries = ARRPTR(in); bool is_number; StringInfoData tmp, dst; if (count == 0) PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2)); initStringInfo(&tmp); initStringInfo(&dst); appendStringInfoChar(&dst, '{'); for (i = 0; i < count; i++) { resetStringInfo(&tmp); appendBinaryStringInfo(&tmp, HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); escape_json(&dst, tmp.data); appendStringInfoString(&dst, ": "); if (HS_VALISNULL(entries, i)) appendStringInfoString(&dst, "null"); /* guess that values of 't' or 'f' are booleans */ else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 't') appendStringInfoString(&dst, "true"); else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 'f') appendStringInfoString(&dst, "false"); else { is_number = false; resetStringInfo(&tmp); appendBinaryStringInfo(&tmp, HS_VAL(entries, base, i), HS_VALLEN(entries, i)); /* * don't treat something with a leading zero followed by another * digit as numeric - could be a zip code or similar */ if (tmp.len > 0 && !(tmp.data[0] == '0' && isdigit((unsigned char) tmp.data[1])) && strspn(tmp.data, "+-0123456789Ee.") == tmp.len) { /* * might be a number. See if we can input it as a numeric * value. Ignore any actual parsed value. */ char *endptr = "junk"; long lval; lval = strtol(tmp.data, &endptr, 10); (void) lval; if (*endptr == '\0') { /* * strol man page says this means the whole string is * valid */ is_number = true; } else { /* not an int - try a double */ double dval; dval = strtod(tmp.data, &endptr); (void) dval; if (*endptr == '\0') is_number = true; } } if (is_number) appendBinaryStringInfo(&dst, tmp.data, tmp.len); else escape_json(&dst, tmp.data); } if (i + 1 != count) appendStringInfoString(&dst, ", "); } appendStringInfoChar(&dst, '}'); PG_RETURN_TEXT_P(cstring_to_text(dst.data)); }
Datum hstore_to_jsonb_loose(PG_FUNCTION_ARGS) { HStore *in = PG_GETARG_HS(0); int i; int count = HS_COUNT(in); char *base = STRPTR(in); HEntry *entries = ARRPTR(in); JsonbParseState *state = NULL; JsonbValue *res; StringInfoData tmp; bool is_number; initStringInfo(&tmp); res = pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL); for (i = 0; i < count; i++) { JsonbValue key, val; key.estSize = sizeof(JEntry); key.type = jbvString; key.val.string.len = HS_KEYLEN(entries, i); key.val.string.val = pnstrdup(HS_KEY(entries, base, i), key.val.string.len); key.estSize += key.val.string.len; res = pushJsonbValue(&state, WJB_KEY, &key); val.estSize = sizeof(JEntry); if (HS_VALISNULL(entries, i)) { val.type = jbvNull; } /* guess that values of 't' or 'f' are booleans */ else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 't') { val.type = jbvBool; val.val.boolean = true; } else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 'f') { val.type = jbvBool; val.val.boolean = false; } else { is_number = false; resetStringInfo(&tmp); appendBinaryStringInfo(&tmp, HS_VAL(entries, base, i), HS_VALLEN(entries, i)); /* * don't treat something with a leading zero followed by another * digit as numeric - could be a zip code or similar */ if (tmp.len > 0 && !(tmp.data[0] == '0' && isdigit((unsigned char) tmp.data[1])) && strspn(tmp.data, "+-0123456789Ee.") == tmp.len) { /* * might be a number. See if we can input it as a numeric * value. Ignore any actual parsed value. */ char *endptr = "junk"; long lval; lval = strtol(tmp.data, &endptr, 10); (void) lval; if (*endptr == '\0') { /* * strol man page says this means the whole string is * valid */ is_number = true; } else { /* not an int - try a double */ double dval; dval = strtod(tmp.data, &endptr); (void) dval; if (*endptr == '\0') is_number = true; } } if (is_number) { val.type = jbvNumeric; val.val.numeric = DatumGetNumeric( DirectFunctionCall3(numeric_in, CStringGetDatum(tmp.data), 0, -1)); val.estSize += VARSIZE_ANY(val.val.numeric) +sizeof(JEntry); } else { val.estSize = sizeof(JEntry); val.type = jbvString; val.val.string.len = HS_VALLEN(entries, i); val.val.string.val = pnstrdup(HS_VAL(entries, base, i), val.val.string.len); val.estSize += val.val.string.len; } } res = pushJsonbValue(&state, WJB_VALUE, &val); } res = pushJsonbValue(&state, WJB_END_OBJECT, NULL); PG_RETURN_POINTER(JsonbValueToJsonb(res)); }
/* * 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_cmp(PG_FUNCTION_ARGS) { HStore *hs1 = PG_GETARG_HS(0); HStore *hs2 = PG_GETARG_HS(1); int hcount1 = HS_COUNT(hs1); int hcount2 = HS_COUNT(hs2); int res = 0; if (hcount1 == 0 || hcount2 == 0) { /* * if either operand is empty, and the other is nonempty, the nonempty * one is larger. If both are empty they are equal. */ if (hcount1 > 0) res = 1; else if (hcount2 > 0) res = -1; } else { /* here we know both operands are nonempty */ char *str1 = STRPTR(hs1); char *str2 = STRPTR(hs2); HEntry *ent1 = ARRPTR(hs1); HEntry *ent2 = ARRPTR(hs2); size_t len1 = HSE_ENDPOS(ent1[2 * hcount1 - 1]); size_t len2 = HSE_ENDPOS(ent2[2 * hcount2 - 1]); res = memcmp(str1, str2, Min(len1, len2)); if (res == 0) { if (len1 > len2) res = 1; else if (len1 < len2) res = -1; else if (hcount1 > hcount2) res = 1; else if (hcount2 > hcount1) res = -1; else { int count = hcount1 * 2; int i; for (i = 0; i < count; ++i) if (HSE_ENDPOS(ent1[i]) != HSE_ENDPOS(ent2[i]) || HSE_ISNULL(ent1[i]) != HSE_ISNULL(ent2[i])) break; if (i < count) { if (HSE_ENDPOS(ent1[i]) < HSE_ENDPOS(ent2[i])) res = -1; else if (HSE_ENDPOS(ent1[i]) > HSE_ENDPOS(ent2[i])) res = 1; else if (HSE_ISNULL(ent1[i])) res = 1; else if (HSE_ISNULL(ent2[i])) res = -1; } } } else { res = (res > 0) ? 1 : -1; } } /* * this is a btree support function; this is one of the few places where * memory needs to be explicitly freed. */ PG_FREE_IF_COPY(hs1, 0); PG_FREE_IF_COPY(hs2, 1); PG_RETURN_INT32(res); }
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); }
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_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_populate_record(PG_FUNCTION_ARGS) { Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0); HStore *hs; HEntry *entries; char *ptr; HeapTupleHeader rec; Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tuple; HeapTuple rettuple; RecordIOData *my_extra; int ncolumns; int i; Datum *values; bool *nulls; if (!type_is_rowtype(argtype)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("first argument must be a rowtype"))); if (PG_ARGISNULL(0)) { if (PG_ARGISNULL(1)) PG_RETURN_NULL(); rec = NULL; /* * have no tuple to look at, so the only source of type info is the * argtype. The lookup_rowtype_tupdesc call below will error out if we * don't have a known composite type oid here. */ tupType = argtype; tupTypmod = -1; } else { rec = PG_GETARG_HEAPTUPLEHEADER(0); if (PG_ARGISNULL(1)) PG_RETURN_POINTER(rec); /* Extract type info from the tuple itself */ tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); } hs = PG_GETARG_HS(1); entries = ARRPTR(hs); ptr = STRPTR(hs); /* * if the input hstore is empty, we can only skip the rest if we were * passed in a non-null record, since otherwise there may be issues with * domain nulls. */ if (HS_COUNT(hs) == 0 && rec) PG_RETURN_POINTER(rec); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); ncolumns = tupdesc->natts; if (rec) { /* Build a temporary HeapTuple control structure */ tuple.t_len = HeapTupleHeaderGetDatumLength(rec); ItemPointerSetInvalid(&(tuple.t_self)); //tuple.t_tableOid = InvalidOid; tuple.t_data = rec; } /* * We arrange to look up the needed I/O info just once per series of * calls, assuming the record type doesn't change underneath us. */ my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; if (my_extra == NULL || my_extra->ncolumns != ncolumns) { fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; my_extra->record_type = InvalidOid; my_extra->record_typmod = 0; } if (my_extra->record_type != tupType || my_extra->record_typmod != tupTypmod) { MemSet(my_extra, 0, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra->record_type = tupType; my_extra->record_typmod = tupTypmod; my_extra->ncolumns = ncolumns; } values = (Datum *) palloc(ncolumns * sizeof(Datum)); nulls = (bool *) palloc(ncolumns * sizeof(bool)); if (rec) { /* Break down the tuple into fields */ heap_deform_tuple(&tuple, tupdesc, values, nulls); } else { for (i = 0; i < ncolumns; ++i) { values[i] = (Datum) 0; nulls[i] = true; } } for (i = 0; i < ncolumns; ++i) { ColumnIOData *column_info = &my_extra->columns[i]; Oid column_type = tupdesc->attrs[i]->atttypid; char *value; int idx; int vallen; /* Ignore dropped columns in datatype */ if (tupdesc->attrs[i]->attisdropped) { nulls[i] = true; continue; } idx = hstoreFindKey(hs, 0, NameStr(tupdesc->attrs[i]->attname), strlen(NameStr(tupdesc->attrs[i]->attname))); /* * we can't just skip here if the key wasn't found since we might have * a domain to deal with. If we were passed in a non-null record * datum, we assume that the existing values are valid (if they're * not, then it's not our fault), but if we were passed in a null, * then every field which we don't populate needs to be run through * the input function just in case it's a domain type. */ if (idx < 0 && rec) continue; /* * Prepare to convert the column value from text */ if (column_info->column_type != column_type) { getTypeInputInfo(column_type, &column_info->typiofunc, &column_info->typioparam); fmgr_info_cxt(column_info->typiofunc, &column_info->proc, fcinfo->flinfo->fn_mcxt); column_info->column_type = column_type; } if (idx < 0 || HS_VALISNULL(entries, idx)) { /* * need InputFunctionCall to happen even for nulls, so that domain * checks are done */ values[i] = InputFunctionCall(&column_info->proc, NULL, column_info->typioparam, tupdesc->attrs[i]->atttypmod); nulls[i] = true; } else { vallen = HS_VALLEN(entries, idx); value = palloc(1 + vallen); memcpy(value, HS_VAL(entries, ptr, idx), vallen); value[vallen] = 0; values[i] = InputFunctionCall(&column_info->proc, value, column_info->typioparam, tupdesc->attrs[i]->atttypmod); nulls[i] = false; } } rettuple = heap_form_tuple(tupdesc, values, nulls); ReleaseTupleDesc(tupdesc); PG_RETURN_DATUM(HeapTupleGetDatum(rettuple)); }
static void apply_changes(lua_State *L, Lua_Hstore *strg){ HStore *hsout; int32 buflen; HStore *in; int i; int count; int pcount; char *base; HEntry *entries; Pairs *pairs; if (strg->issync == 1) return; in = strg->hstore; count = HS_COUNT(in); base = STRPTR(in); entries = ARRPTR(in); lua_pushlightuserdata(L, strg); lua_gettable(L, LUA_REGISTRYINDEX); for (i=0; i < count; i++) { char *key = HS_KEY(entries, base, i); int klen = HS_KEYLEN(entries, i); lua_pushlstring(L, key, klen); lua_rawget(L, -2); if (lua_isnil(L,-1)){ lua_pop(L, 1); lua_pushlstring(L, key, klen); lua_pushinteger(L, i); lua_rawset(L, -3); }else { lua_pop(L, 1); } } pcount = 0; lua_pushnil(L); /* first key */ while (lua_next(L, -2) != 0) { ++pcount; /* removes 'value'; keeps 'key' for next iteration */ lua_pop(L, 1); } MTOLUA(L); pairs = palloc(pcount * sizeof(*pairs)); MTOPG; i = 0; //------------------------------- lua_pushnil(L); while (lua_next(L, -2) != 0) { const char *key = lua_tostring(L, -2); int ltype = lua_type(L, -1); if (ltype == LUA_TNUMBER){ int idx = lua_tointeger(L, -1); set_pair(L, &pairs[i], entries, base, idx); } else { char *mov_value = (char *)lua_touserdata (L, -1); set_pair_from_lv(L, &pairs[i], key, mov_value); } lua_pop(L, 1); ++i; } //------------------------------- lua_pop(L, 1); MTOLUA(L); pcount = hstoreUniquePairs(pairs, pcount, &buflen); hsout = hstorePairs(pairs, pcount, buflen); MTOPG; if (strg->havetodel){ pg_datumFree(strg->datum, hs_type.byval, hs_type.len); } strg->hstore = hsout; strg->datum = PointerGetDatum(hsout); strg->issync = 1; strg->havetodel = 1; lua_pushlightuserdata(L, strg); lua_newtable(L); lua_settable(L, LUA_REGISTRYINDEX); }