Datum hstore_slice_to_array(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); HEntry *entries = ARRPTR(hs); char *ptr = STRPTR(hs); 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) { 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]); int idx; if (key_nulls[i]) idx = -1; else idx = hstoreFindKey(hs, NULL, VARDATA(key), VARSIZE(key) - VARHDRSZ); if (idx < 0 || HS_VALISNULL(entries, idx)) { out_nulls[i] = true; out_datums[i] = (Datum) 0; } else { out_datums[i] = PointerGetDatum( cstring_to_text_with_len(HS_VAL(entries, ptr, idx), HS_VALLEN(entries, idx))); out_nulls[i] = 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); }
Datum hstore_exists_all(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1); int nkeys; Pairs *key_pairs = hstoreArrayToPairs(keys, &nkeys); int i; int lowbound = 0; bool res = true; /* * we exploit the fact that the pairs list is already sorted into 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 last search. */ for (i = 0; i < nkeys; i++) { int idx = hstoreFindKey(hs, &lowbound, key_pairs[i].key, key_pairs[i].keylen); if (idx < 0) { res = false; break; } } PG_RETURN_BOOL(res); }
Datum hstore_slice_to_hstore(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); HEntry *entries = ARRPTR(hs); char *ptr = STRPTR(hs); ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(1); HStore *out; int nkeys; Pairs *key_pairs = hstoreArrayToPairs(key_array, &nkeys); Pairs *out_pairs; int bufsiz; int lastidx = 0; int i; int out_count = 0; if (nkeys == 0) { out = hstorePairs(NULL, 0, 0); PG_RETURN_POINTER(out); } out_pairs = palloc(sizeof(Pairs) * nkeys); bufsiz = 0; /* * we exploit the fact that the pairs list is already sorted into 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 last search. */ for (i = 0; i < nkeys; ++i) { int idx = hstoreFindKey(hs, &lastidx, key_pairs[i].key, key_pairs[i].keylen); if (idx >= 0) { out_pairs[out_count].key = key_pairs[i].key; bufsiz += (out_pairs[out_count].keylen = key_pairs[i].keylen); out_pairs[out_count].val = HS_VAL(entries, ptr, idx); bufsiz += (out_pairs[out_count].vallen = HS_VALLEN(entries, idx)); out_pairs[out_count].isnull = HS_VALISNULL(entries, idx); out_pairs[out_count].needfree = false; ++out_count; } } /* * we don't use uniquePairs here because we know that the pairs list is * already sorted and uniq'ed. */ out = hstorePairs(out_pairs, out_count, bufsiz); PG_RETURN_POINTER(out); }
Datum hstore_exists(PG_FUNCTION_ARGS) { HStore *hs = PG_GETARG_HS(0); text *key = PG_GETARG_TEXT_PP(1); int idx = hstoreFindKey(hs, NULL, VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); PG_RETURN_BOOL(idx >= 0); }
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_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_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_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)); }