static int hstore_index(lua_State *L) { HStore *hs; char *base; HEntry *entries; const char *key; char *k_str; int idx; Lua_Hstore *strg; BEGINLUA; strg = lua_touserdata(L, 1); hs = strg->hstore; base = STRPTR(hs); entries = ARRPTR(hs); key = luaL_checkstring(L, 2); lua_pushlightuserdata(L, strg); lua_gettable(L, LUA_REGISTRYINDEX); lua_pushstring(L, key); lua_gettable(L, -2); if (!lua_isnil(L, -1)) { char *data_ptr = lua_touserdata (L, -1); lua_pop(L,2); if (data_ptr){ lua_pushstring(L, data_ptr); }else { lua_pushnil(L); } ENDLUAV(1); return 1; } lua_pop(L,2); k_str = strdup(key); idx = hstoreFindKey(hs, NULL, k_str, strlen(k_str)); free(k_str); if (idx<0|| HS_VALISNULL(entries, idx)){ lua_pushnil(L); ENDLUAV(1); return 1; } lua_pushlstring(L, HS_VAL(entries, base, idx),HS_VALLEN(entries, idx)); ENDLUAV(1); return 1; }
Datum hstore_defined(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); text *key = PG_GETARG_TEXT_PP(1); HEntry *entries = ARRPTR(hs); int idx = hstoreFindKey(hs, NULL, VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); bool res = (idx >= 0 && !HS_VALISNULL(entries, idx)); PG_RETURN_BOOL(res); }
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_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); }
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_fetchval(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); text *key = PG_GETARG_TEXT_PP(1); HEntry *entries = ARRPTR(hs); text *out; int idx = hstoreFindKey(hs, NULL, VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); if (idx < 0 || HS_VALISNULL(entries, idx)) PG_RETURN_NULL(); out = cstring_to_text_with_len(HS_VAL(entries, STRPTR(hs), idx), HS_VALLEN(entries, idx)); PG_RETURN_TEXT_P(out); }
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); }
static void set_pair(lua_State *L, Pairs *pairs, HEntry *entries, char *base, int i){ char * kvalue; int klen; MTOLUA(L); kvalue = NULL; klen = HS_KEYLEN(entries, i); if (klen){ kvalue = (char*)palloc(klen+1); memset(kvalue,0,klen+1); memcpy(kvalue,HS_KEY(entries, base, i),klen); } pairs->key = kvalue; pairs->keylen = klen; pairs->needfree = true; if (HS_VALISNULL(entries, i)) { pairs->val = NULL; pairs->vallen = 0; pairs->isnull = true; } else { char * vvalue = NULL; int vlen = HS_VALLEN(entries, i); if (vlen){ vvalue = (char*)palloc(vlen+1); memset(vvalue,0,vlen+1); memcpy(vvalue,HS_VAL(entries, base, i),vlen); } pairs->val = vvalue; pairs->vallen = vlen; pairs->isnull = false; } MTOPG; }
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)); }
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)); *VARDATA(item) = KEYFLAG; entries[2 * i] = PointerGetDatum(item); if (HS_VALISNULL(hsent, i)) { item = makeitem(NULL, 0); *VARDATA(item) = NULLFLAG; } else { item = makeitem(HS_VAL(hsent, ptr, i), HS_VALLEN(hsent, i)); *VARDATA(item) = VALFLAG; } entries[2 * i + 1] = PointerGetDatum(item); } PG_RETURN_POINTER(entries); }
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)); }
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_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)); }
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); }