Split out code into new getKeyJsonValueFromContainer()
The new function stashes its output value in a JsonbValue that can be passed in by the caller, which enables some of them to pass stack-allocated structs -- saving palloc cycles. It also allows some callers that know they are handling a jsonb object to use this new jsonb object-specific API, instead of going through generic container findJsonbValueFromContainer. Author: Nikita Glukhov Discussion: https://postgr.es/m/7c417f90-f95f-247e-ba63-d95e39c0ad14@postgrespro.ru
This commit is contained in:
parent
dbb9aeda99
commit
1a2983231d
@ -56,6 +56,8 @@ static void appendKey(JsonbParseState *pstate, JsonbValue *scalarVal);
|
|||||||
static void appendValue(JsonbParseState *pstate, JsonbValue *scalarVal);
|
static void appendValue(JsonbParseState *pstate, JsonbValue *scalarVal);
|
||||||
static void appendElement(JsonbParseState *pstate, JsonbValue *scalarVal);
|
static void appendElement(JsonbParseState *pstate, JsonbValue *scalarVal);
|
||||||
static int lengthCompareJsonbStringValue(const void *a, const void *b);
|
static int lengthCompareJsonbStringValue(const void *a, const void *b);
|
||||||
|
static int lengthCompareJsonbString(const char *val1, int len1,
|
||||||
|
const char *val2, int len2);
|
||||||
static int lengthCompareJsonbPair(const void *a, const void *b, void *arg);
|
static int lengthCompareJsonbPair(const void *a, const void *b, void *arg);
|
||||||
static void uniqueifyJsonbObject(JsonbValue *object);
|
static void uniqueifyJsonbObject(JsonbValue *object);
|
||||||
static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
|
static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
|
||||||
@ -329,7 +331,6 @@ findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
|
|||||||
{
|
{
|
||||||
JEntry *children = container->children;
|
JEntry *children = container->children;
|
||||||
int count = JsonContainerSize(container);
|
int count = JsonContainerSize(container);
|
||||||
JsonbValue *result;
|
|
||||||
|
|
||||||
Assert((flags & ~(JB_FARRAY | JB_FOBJECT)) == 0);
|
Assert((flags & ~(JB_FARRAY | JB_FOBJECT)) == 0);
|
||||||
|
|
||||||
@ -337,10 +338,9 @@ findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
|
|||||||
if (count <= 0)
|
if (count <= 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
result = palloc(sizeof(JsonbValue));
|
|
||||||
|
|
||||||
if ((flags & JB_FARRAY) && JsonContainerIsArray(container))
|
if ((flags & JB_FARRAY) && JsonContainerIsArray(container))
|
||||||
{
|
{
|
||||||
|
JsonbValue *result = palloc(sizeof(JsonbValue));
|
||||||
char *base_addr = (char *) (children + count);
|
char *base_addr = (char *) (children + count);
|
||||||
uint32 offset = 0;
|
uint32 offset = 0;
|
||||||
int i;
|
int i;
|
||||||
@ -357,56 +357,90 @@ findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
|
|||||||
|
|
||||||
JBE_ADVANCE_OFFSET(offset, children[i]);
|
JBE_ADVANCE_OFFSET(offset, children[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pfree(result);
|
||||||
}
|
}
|
||||||
else if ((flags & JB_FOBJECT) && JsonContainerIsObject(container))
|
else if ((flags & JB_FOBJECT) && JsonContainerIsObject(container))
|
||||||
{
|
{
|
||||||
/* Since this is an object, account for *Pairs* of Jentrys */
|
|
||||||
char *base_addr = (char *) (children + count * 2);
|
|
||||||
uint32 stopLow = 0,
|
|
||||||
stopHigh = count;
|
|
||||||
|
|
||||||
/* Object key passed by caller must be a string */
|
/* Object key passed by caller must be a string */
|
||||||
Assert(key->type == jbvString);
|
Assert(key->type == jbvString);
|
||||||
|
|
||||||
/* Binary search on object/pair keys *only* */
|
return getKeyJsonValueFromContainer(container, key->val.string.val,
|
||||||
while (stopLow < stopHigh)
|
key->val.string.len, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not found */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find value by key in Jsonb object and fetch it into 'res', which is also
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* 'res' can be passed in as NULL, in which case it's newly palloc'ed here.
|
||||||
|
*/
|
||||||
|
JsonbValue *
|
||||||
|
getKeyJsonValueFromContainer(JsonbContainer *container,
|
||||||
|
const char *keyVal, int keyLen, JsonbValue *res)
|
||||||
|
{
|
||||||
|
JEntry *children = container->children;
|
||||||
|
int count = JsonContainerSize(container);
|
||||||
|
char *baseAddr;
|
||||||
|
uint32 stopLow,
|
||||||
|
stopHigh;
|
||||||
|
|
||||||
|
Assert(JsonContainerIsObject(container));
|
||||||
|
|
||||||
|
/* Quick out without a palloc cycle if object is empty */
|
||||||
|
if (count <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Binary search the container. Since we know this is an object, account
|
||||||
|
* for *Pairs* of Jentrys
|
||||||
|
*/
|
||||||
|
baseAddr = (char *) (children + count * 2);
|
||||||
|
stopLow = 0;
|
||||||
|
stopHigh = count;
|
||||||
|
while (stopLow < stopHigh)
|
||||||
|
{
|
||||||
|
uint32 stopMiddle;
|
||||||
|
int difference;
|
||||||
|
const char *candidateVal;
|
||||||
|
int candidateLen;
|
||||||
|
|
||||||
|
stopMiddle = stopLow + (stopHigh - stopLow) / 2;
|
||||||
|
|
||||||
|
candidateVal = baseAddr + getJsonbOffset(container, stopMiddle);
|
||||||
|
candidateLen = getJsonbLength(container, stopMiddle);
|
||||||
|
|
||||||
|
difference = lengthCompareJsonbString(candidateVal, candidateLen,
|
||||||
|
keyVal, keyLen);
|
||||||
|
|
||||||
|
if (difference == 0)
|
||||||
{
|
{
|
||||||
uint32 stopMiddle;
|
/* Found our key, return corresponding value */
|
||||||
int difference;
|
int index = stopMiddle + count;
|
||||||
JsonbValue candidate;
|
|
||||||
|
|
||||||
stopMiddle = stopLow + (stopHigh - stopLow) / 2;
|
if (!res)
|
||||||
|
res = palloc(sizeof(JsonbValue));
|
||||||
|
|
||||||
candidate.type = jbvString;
|
fillJsonbValue(container, index, baseAddr,
|
||||||
candidate.val.string.val =
|
getJsonbOffset(container, index),
|
||||||
base_addr + getJsonbOffset(container, stopMiddle);
|
res);
|
||||||
candidate.val.string.len = getJsonbLength(container, stopMiddle);
|
|
||||||
|
|
||||||
difference = lengthCompareJsonbStringValue(&candidate, key);
|
return res;
|
||||||
|
}
|
||||||
if (difference == 0)
|
else
|
||||||
{
|
{
|
||||||
/* Found our key, return corresponding value */
|
if (difference < 0)
|
||||||
int index = stopMiddle + count;
|
stopLow = stopMiddle + 1;
|
||||||
|
|
||||||
fillJsonbValue(container, index, base_addr,
|
|
||||||
getJsonbOffset(container, index),
|
|
||||||
result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
stopHigh = stopMiddle;
|
||||||
if (difference < 0)
|
|
||||||
stopLow = stopMiddle + 1;
|
|
||||||
else
|
|
||||||
stopHigh = stopMiddle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Not found */
|
/* Not found */
|
||||||
pfree(result);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1009,6 +1043,7 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
|
|||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
JsonbValue *lhsVal; /* lhsVal is from pair in lhs object */
|
JsonbValue *lhsVal; /* lhsVal is from pair in lhs object */
|
||||||
|
JsonbValue lhsValBuf;
|
||||||
|
|
||||||
rcont = JsonbIteratorNext(mContained, &vcontained, false);
|
rcont = JsonbIteratorNext(mContained, &vcontained, false);
|
||||||
|
|
||||||
@ -1021,12 +1056,14 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
Assert(rcont == WJB_KEY);
|
Assert(rcont == WJB_KEY);
|
||||||
|
Assert(vcontained.type == jbvString);
|
||||||
|
|
||||||
/* First, find value by key... */
|
/* First, find value by key... */
|
||||||
lhsVal = findJsonbValueFromContainer((*val)->container,
|
lhsVal =
|
||||||
JB_FOBJECT,
|
getKeyJsonValueFromContainer((*val)->container,
|
||||||
&vcontained);
|
vcontained.val.string.val,
|
||||||
|
vcontained.val.string.len,
|
||||||
|
&lhsValBuf);
|
||||||
if (!lhsVal)
|
if (!lhsVal)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -1771,21 +1808,27 @@ lengthCompareJsonbStringValue(const void *a, const void *b)
|
|||||||
{
|
{
|
||||||
const JsonbValue *va = (const JsonbValue *) a;
|
const JsonbValue *va = (const JsonbValue *) a;
|
||||||
const JsonbValue *vb = (const JsonbValue *) b;
|
const JsonbValue *vb = (const JsonbValue *) b;
|
||||||
int res;
|
|
||||||
|
|
||||||
Assert(va->type == jbvString);
|
Assert(va->type == jbvString);
|
||||||
Assert(vb->type == jbvString);
|
Assert(vb->type == jbvString);
|
||||||
|
|
||||||
if (va->val.string.len == vb->val.string.len)
|
return lengthCompareJsonbString(va->val.string.val, va->val.string.len,
|
||||||
{
|
vb->val.string.val, vb->val.string.len);
|
||||||
res = memcmp(va->val.string.val, vb->val.string.val, va->val.string.len);
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res = (va->val.string.len > vb->val.string.len) ? 1 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
/*
|
||||||
|
* Subroutine for lengthCompareJsonbStringValue
|
||||||
|
*
|
||||||
|
* This is also useful separately to implement binary search on
|
||||||
|
* JsonbContainers.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
lengthCompareJsonbString(const char *val1, int len1, const char *val2, int len2)
|
||||||
|
{
|
||||||
|
if (len1 == len2)
|
||||||
|
return memcmp(val1, val2, len1);
|
||||||
|
else
|
||||||
|
return len1 > len2 ? 1 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -454,12 +454,6 @@ static Datum populate_array(ArrayIOData *aio, const char *colname,
|
|||||||
static Datum populate_domain(DomainIOData *io, Oid typid, const char *colname,
|
static Datum populate_domain(DomainIOData *io, Oid typid, const char *colname,
|
||||||
MemoryContext mcxt, JsValue *jsv, bool isnull);
|
MemoryContext mcxt, JsValue *jsv, bool isnull);
|
||||||
|
|
||||||
/* Worker that takes care of common setup for us */
|
|
||||||
static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
|
|
||||||
uint32 flags,
|
|
||||||
char *key,
|
|
||||||
uint32 keylen);
|
|
||||||
|
|
||||||
/* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
|
/* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
|
||||||
static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
|
static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
|
||||||
JsonbParseState **state);
|
JsonbParseState **state);
|
||||||
@ -718,13 +712,15 @@ jsonb_object_field(PG_FUNCTION_ARGS)
|
|||||||
Jsonb *jb = PG_GETARG_JSONB_P(0);
|
Jsonb *jb = PG_GETARG_JSONB_P(0);
|
||||||
text *key = PG_GETARG_TEXT_PP(1);
|
text *key = PG_GETARG_TEXT_PP(1);
|
||||||
JsonbValue *v;
|
JsonbValue *v;
|
||||||
|
JsonbValue vbuf;
|
||||||
|
|
||||||
if (!JB_ROOT_IS_OBJECT(jb))
|
if (!JB_ROOT_IS_OBJECT(jb))
|
||||||
PG_RETURN_NULL();
|
PG_RETURN_NULL();
|
||||||
|
|
||||||
v = findJsonbValueFromContainerLen(&jb->root, JB_FOBJECT,
|
v = getKeyJsonValueFromContainer(&jb->root,
|
||||||
VARDATA_ANY(key),
|
VARDATA_ANY(key),
|
||||||
VARSIZE_ANY_EXHDR(key));
|
VARSIZE_ANY_EXHDR(key),
|
||||||
|
&vbuf);
|
||||||
|
|
||||||
if (v != NULL)
|
if (v != NULL)
|
||||||
PG_RETURN_JSONB_P(JsonbValueToJsonb(v));
|
PG_RETURN_JSONB_P(JsonbValueToJsonb(v));
|
||||||
@ -754,14 +750,15 @@ jsonb_object_field_text(PG_FUNCTION_ARGS)
|
|||||||
Jsonb *jb = PG_GETARG_JSONB_P(0);
|
Jsonb *jb = PG_GETARG_JSONB_P(0);
|
||||||
text *key = PG_GETARG_TEXT_PP(1);
|
text *key = PG_GETARG_TEXT_PP(1);
|
||||||
JsonbValue *v;
|
JsonbValue *v;
|
||||||
|
JsonbValue vbuf;
|
||||||
|
|
||||||
if (!JB_ROOT_IS_OBJECT(jb))
|
if (!JB_ROOT_IS_OBJECT(jb))
|
||||||
PG_RETURN_NULL();
|
PG_RETURN_NULL();
|
||||||
|
|
||||||
v = findJsonbValueFromContainerLen(&jb->root, JB_FOBJECT,
|
v = getKeyJsonValueFromContainer(&jb->root,
|
||||||
VARDATA_ANY(key),
|
VARDATA_ANY(key),
|
||||||
VARSIZE_ANY_EXHDR(key));
|
VARSIZE_ANY_EXHDR(key),
|
||||||
|
&vbuf);
|
||||||
|
|
||||||
if (v != NULL && v->type != jbvNull)
|
if (v != NULL && v->type != jbvNull)
|
||||||
PG_RETURN_TEXT_P(JsonbValueAsText(v));
|
PG_RETURN_TEXT_P(JsonbValueAsText(v));
|
||||||
@ -1336,6 +1333,7 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
|
|||||||
bool have_object = false,
|
bool have_object = false,
|
||||||
have_array = false;
|
have_array = false;
|
||||||
JsonbValue *jbvp = NULL;
|
JsonbValue *jbvp = NULL;
|
||||||
|
JsonbValue jbvbuf;
|
||||||
JsonbContainer *container;
|
JsonbContainer *container;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1393,10 +1391,10 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
|
|||||||
{
|
{
|
||||||
if (have_object)
|
if (have_object)
|
||||||
{
|
{
|
||||||
jbvp = findJsonbValueFromContainerLen(container,
|
jbvp = getKeyJsonValueFromContainer(container,
|
||||||
JB_FOBJECT,
|
VARDATA(pathtext[i]),
|
||||||
VARDATA(pathtext[i]),
|
VARSIZE(pathtext[i]) - VARHDRSZ,
|
||||||
VARSIZE(pathtext[i]) - VARHDRSZ);
|
&jbvbuf);
|
||||||
}
|
}
|
||||||
else if (have_array)
|
else if (have_array)
|
||||||
{
|
{
|
||||||
@ -3023,8 +3021,8 @@ JsObjectGetField(JsObject *obj, char *field, JsValue *jsv)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
jsv->val.jsonb = !obj->val.jsonb_cont ? NULL :
|
jsv->val.jsonb = !obj->val.jsonb_cont ? NULL :
|
||||||
findJsonbValueFromContainerLen(obj->val.jsonb_cont, JB_FOBJECT,
|
getKeyJsonValueFromContainer(obj->val.jsonb_cont, field, strlen(field),
|
||||||
field, strlen(field));
|
NULL);
|
||||||
|
|
||||||
return jsv->val.jsonb != NULL;
|
return jsv->val.jsonb != NULL;
|
||||||
}
|
}
|
||||||
@ -3848,22 +3846,6 @@ populate_recordset_object_field_end(void *state, char *fname, bool isnull)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* findJsonbValueFromContainer() wrapper that sets up JsonbValue key string.
|
|
||||||
*/
|
|
||||||
static JsonbValue *
|
|
||||||
findJsonbValueFromContainerLen(JsonbContainer *container, uint32 flags,
|
|
||||||
char *key, uint32 keylen)
|
|
||||||
{
|
|
||||||
JsonbValue k;
|
|
||||||
|
|
||||||
k.type = jbvString;
|
|
||||||
k.val.string.val = key;
|
|
||||||
k.val.string.len = keylen;
|
|
||||||
|
|
||||||
return findJsonbValueFromContainer(container, flags, &k);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Semantic actions for json_strip_nulls.
|
* Semantic actions for json_strip_nulls.
|
||||||
*
|
*
|
||||||
|
@ -364,6 +364,9 @@ extern int compareJsonbContainers(JsonbContainer *a, JsonbContainer *b);
|
|||||||
extern JsonbValue *findJsonbValueFromContainer(JsonbContainer *sheader,
|
extern JsonbValue *findJsonbValueFromContainer(JsonbContainer *sheader,
|
||||||
uint32 flags,
|
uint32 flags,
|
||||||
JsonbValue *key);
|
JsonbValue *key);
|
||||||
|
extern JsonbValue *getKeyJsonValueFromContainer(JsonbContainer *container,
|
||||||
|
const char *keyVal, int keyLen,
|
||||||
|
JsonbValue *res);
|
||||||
extern JsonbValue *getIthJsonbValueFromContainer(JsonbContainer *sheader,
|
extern JsonbValue *getIthJsonbValueFromContainer(JsonbContainer *sheader,
|
||||||
uint32 i);
|
uint32 i);
|
||||||
extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,
|
extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user