Skip to content

Commit 1a29832

Browse files
committed
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
1 parent dbb9aed commit 1a29832

File tree

3 files changed

+113
-85
lines changed

3 files changed

+113
-85
lines changed

src/backend/utils/adt/jsonb_util.c

Lines changed: 93 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ static void appendKey(JsonbParseState *pstate, JsonbValue *scalarVal);
5656
static void appendValue(JsonbParseState *pstate, JsonbValue *scalarVal);
5757
static void appendElement(JsonbParseState *pstate, JsonbValue *scalarVal);
5858
static int lengthCompareJsonbStringValue(const void *a, const void *b);
59+
static int lengthCompareJsonbString(const char *val1, int len1,
60+
const char *val2, int len2);
5961
static int lengthCompareJsonbPair(const void *a, const void *b, void *arg);
6062
static void uniqueifyJsonbObject(JsonbValue *object);
6163
static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
@@ -329,18 +331,16 @@ findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
329331
{
330332
JEntry *children = container->children;
331333
int count = JsonContainerSize(container);
332-
JsonbValue *result;
333334

334335
Assert((flags & ~(JB_FARRAY | JB_FOBJECT)) == 0);
335336

336337
/* Quick out without a palloc cycle if object/array is empty */
337338
if (count <= 0)
338339
return NULL;
339340

340-
result = palloc(sizeof(JsonbValue));
341-
342341
if ((flags & JB_FARRAY) && JsonContainerIsArray(container))
343342
{
343+
JsonbValue *result = palloc(sizeof(JsonbValue));
344344
char *base_addr = (char *) (children + count);
345345
uint32 offset = 0;
346346
int i;
@@ -357,56 +357,90 @@ findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
357357

358358
JBE_ADVANCE_OFFSET(offset, children[i]);
359359
}
360+
361+
pfree(result);
360362
}
361363
else if ((flags & JB_FOBJECT) && JsonContainerIsObject(container))
362364
{
363-
/* Since this is an object, account for *Pairs* of Jentrys */
364-
char *base_addr = (char *) (children + count * 2);
365-
uint32 stopLow = 0,
366-
stopHigh = count;
367-
368365
/* Object key passed by caller must be a string */
369366
Assert(key->type == jbvString);
370367

371-
/* Binary search on object/pair keys *only* */
372-
while (stopLow < stopHigh)
373-
{
374-
uint32 stopMiddle;
375-
int difference;
376-
JsonbValue candidate;
368+
return getKeyJsonValueFromContainer(container, key->val.string.val,
369+
key->val.string.len, NULL);
370+
}
371+
372+
/* Not found */
373+
return NULL;
374+
}
375+
376+
/*
377+
* Find value by key in Jsonb object and fetch it into 'res', which is also
378+
* returned.
379+
*
380+
* 'res' can be passed in as NULL, in which case it's newly palloc'ed here.
381+
*/
382+
JsonbValue *
383+
getKeyJsonValueFromContainer(JsonbContainer *container,
384+
const char *keyVal, int keyLen, JsonbValue *res)
385+
{
386+
JEntry *children = container->children;
387+
int count = JsonContainerSize(container);
388+
char *baseAddr;
389+
uint32 stopLow,
390+
stopHigh;
377391

378-
stopMiddle = stopLow + (stopHigh - stopLow) / 2;
392+
Assert(JsonContainerIsObject(container));
379393

380-
candidate.type = jbvString;
381-
candidate.val.string.val =
382-
base_addr + getJsonbOffset(container, stopMiddle);
383-
candidate.val.string.len = getJsonbLength(container, stopMiddle);
394+
/* Quick out without a palloc cycle if object is empty */
395+
if (count <= 0)
396+
return NULL;
384397

385-
difference = lengthCompareJsonbStringValue(&candidate, key);
398+
/*
399+
* Binary search the container. Since we know this is an object, account
400+
* for *Pairs* of Jentrys
401+
*/
402+
baseAddr = (char *) (children + count * 2);
403+
stopLow = 0;
404+
stopHigh = count;
405+
while (stopLow < stopHigh)
406+
{
407+
uint32 stopMiddle;
408+
int difference;
409+
const char *candidateVal;
410+
int candidateLen;
386411

387-
if (difference == 0)
388-
{
389-
/* Found our key, return corresponding value */
390-
int index = stopMiddle + count;
412+
stopMiddle = stopLow + (stopHigh - stopLow) / 2;
391413

392-
fillJsonbValue(container, index, base_addr,
393-
getJsonbOffset(container, index),
394-
result);
414+
candidateVal = baseAddr + getJsonbOffset(container, stopMiddle);
415+
candidateLen = getJsonbLength(container, stopMiddle);
395416

396-
return result;
397-
}
417+
difference = lengthCompareJsonbString(candidateVal, candidateLen,
418+
keyVal, keyLen);
419+
420+
if (difference == 0)
421+
{
422+
/* Found our key, return corresponding value */
423+
int index = stopMiddle + count;
424+
425+
if (!res)
426+
res = palloc(sizeof(JsonbValue));
427+
428+
fillJsonbValue(container, index, baseAddr,
429+
getJsonbOffset(container, index),
430+
res);
431+
432+
return res;
433+
}
434+
else
435+
{
436+
if (difference < 0)
437+
stopLow = stopMiddle + 1;
398438
else
399-
{
400-
if (difference < 0)
401-
stopLow = stopMiddle + 1;
402-
else
403-
stopHigh = stopMiddle;
404-
}
439+
stopHigh = stopMiddle;
405440
}
406441
}
407442

408443
/* Not found */
409-
pfree(result);
410444
return NULL;
411445
}
412446

@@ -1009,6 +1043,7 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
10091043
for (;;)
10101044
{
10111045
JsonbValue *lhsVal; /* lhsVal is from pair in lhs object */
1046+
JsonbValue lhsValBuf;
10121047

10131048
rcont = JsonbIteratorNext(mContained, &vcontained, false);
10141049

@@ -1021,12 +1056,14 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
10211056
return true;
10221057

10231058
Assert(rcont == WJB_KEY);
1059+
Assert(vcontained.type == jbvString);
10241060

10251061
/* First, find value by key... */
1026-
lhsVal = findJsonbValueFromContainer((*val)->container,
1027-
JB_FOBJECT,
1028-
&vcontained);
1029-
1062+
lhsVal =
1063+
getKeyJsonValueFromContainer((*val)->container,
1064+
vcontained.val.string.val,
1065+
vcontained.val.string.len,
1066+
&lhsValBuf);
10301067
if (!lhsVal)
10311068
return false;
10321069

@@ -1771,21 +1808,27 @@ lengthCompareJsonbStringValue(const void *a, const void *b)
17711808
{
17721809
const JsonbValue *va = (const JsonbValue *) a;
17731810
const JsonbValue *vb = (const JsonbValue *) b;
1774-
int res;
17751811

17761812
Assert(va->type == jbvString);
17771813
Assert(vb->type == jbvString);
17781814

1779-
if (va->val.string.len == vb->val.string.len)
1780-
{
1781-
res = memcmp(va->val.string.val, vb->val.string.val, va->val.string.len);
1782-
}
1783-
else
1784-
{
1785-
res = (va->val.string.len > vb->val.string.len) ? 1 : -1;
1786-
}
1815+
return lengthCompareJsonbString(va->val.string.val, va->val.string.len,
1816+
vb->val.string.val, vb->val.string.len);
1817+
}
17871818

1788-
return res;
1819+
/*
1820+
* Subroutine for lengthCompareJsonbStringValue
1821+
*
1822+
* This is also useful separately to implement binary search on
1823+
* JsonbContainers.
1824+
*/
1825+
static int
1826+
lengthCompareJsonbString(const char *val1, int len1, const char *val2, int len2)
1827+
{
1828+
if (len1 == len2)
1829+
return memcmp(val1, val2, len1);
1830+
else
1831+
return len1 > len2 ? 1 : -1;
17891832
}
17901833

17911834
/*

src/backend/utils/adt/jsonfuncs.c

Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -454,12 +454,6 @@ static Datum populate_array(ArrayIOData *aio, const char *colname,
454454
static Datum populate_domain(DomainIOData *io, Oid typid, const char *colname,
455455
MemoryContext mcxt, JsValue *jsv, bool isnull);
456456

457-
/* Worker that takes care of common setup for us */
458-
static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
459-
uint32 flags,
460-
char *key,
461-
uint32 keylen);
462-
463457
/* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
464458
static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
465459
JsonbParseState **state);
@@ -718,13 +712,15 @@ jsonb_object_field(PG_FUNCTION_ARGS)
718712
Jsonb *jb = PG_GETARG_JSONB_P(0);
719713
text *key = PG_GETARG_TEXT_PP(1);
720714
JsonbValue *v;
715+
JsonbValue vbuf;
721716

722717
if (!JB_ROOT_IS_OBJECT(jb))
723718
PG_RETURN_NULL();
724719

725-
v = findJsonbValueFromContainerLen(&jb->root, JB_FOBJECT,
726-
VARDATA_ANY(key),
727-
VARSIZE_ANY_EXHDR(key));
720+
v = getKeyJsonValueFromContainer(&jb->root,
721+
VARDATA_ANY(key),
722+
VARSIZE_ANY_EXHDR(key),
723+
&vbuf);
728724

729725
if (v != NULL)
730726
PG_RETURN_JSONB_P(JsonbValueToJsonb(v));
@@ -754,14 +750,15 @@ jsonb_object_field_text(PG_FUNCTION_ARGS)
754750
Jsonb *jb = PG_GETARG_JSONB_P(0);
755751
text *key = PG_GETARG_TEXT_PP(1);
756752
JsonbValue *v;
753+
JsonbValue vbuf;
757754

758755
if (!JB_ROOT_IS_OBJECT(jb))
759756
PG_RETURN_NULL();
760757

761-
v = findJsonbValueFromContainerLen(&jb->root, JB_FOBJECT,
762-
VARDATA_ANY(key),
763-
VARSIZE_ANY_EXHDR(key));
764-
758+
v = getKeyJsonValueFromContainer(&jb->root,
759+
VARDATA_ANY(key),
760+
VARSIZE_ANY_EXHDR(key),
761+
&vbuf);
765762

766763
if (v != NULL && v->type != jbvNull)
767764
PG_RETURN_TEXT_P(JsonbValueAsText(v));
@@ -1336,6 +1333,7 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
13361333
bool have_object = false,
13371334
have_array = false;
13381335
JsonbValue *jbvp = NULL;
1336+
JsonbValue jbvbuf;
13391337
JsonbContainer *container;
13401338

13411339
/*
@@ -1393,10 +1391,10 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
13931391
{
13941392
if (have_object)
13951393
{
1396-
jbvp = findJsonbValueFromContainerLen(container,
1397-
JB_FOBJECT,
1398-
VARDATA(pathtext[i]),
1399-
VARSIZE(pathtext[i]) - VARHDRSZ);
1394+
jbvp = getKeyJsonValueFromContainer(container,
1395+
VARDATA(pathtext[i]),
1396+
VARSIZE(pathtext[i]) - VARHDRSZ,
1397+
&jbvbuf);
14001398
}
14011399
else if (have_array)
14021400
{
@@ -3023,8 +3021,8 @@ JsObjectGetField(JsObject *obj, char *field, JsValue *jsv)
30233021
else
30243022
{
30253023
jsv->val.jsonb = !obj->val.jsonb_cont ? NULL :
3026-
findJsonbValueFromContainerLen(obj->val.jsonb_cont, JB_FOBJECT,
3027-
field, strlen(field));
3024+
getKeyJsonValueFromContainer(obj->val.jsonb_cont, field, strlen(field),
3025+
NULL);
30283026

30293027
return jsv->val.jsonb != NULL;
30303028
}
@@ -3848,22 +3846,6 @@ populate_recordset_object_field_end(void *state, char *fname, bool isnull)
38483846
}
38493847
}
38503848

3851-
/*
3852-
* findJsonbValueFromContainer() wrapper that sets up JsonbValue key string.
3853-
*/
3854-
static JsonbValue *
3855-
findJsonbValueFromContainerLen(JsonbContainer *container, uint32 flags,
3856-
char *key, uint32 keylen)
3857-
{
3858-
JsonbValue k;
3859-
3860-
k.type = jbvString;
3861-
k.val.string.val = key;
3862-
k.val.string.len = keylen;
3863-
3864-
return findJsonbValueFromContainer(container, flags, &k);
3865-
}
3866-
38673849
/*
38683850
* Semantic actions for json_strip_nulls.
38693851
*

src/include/utils/jsonb.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,9 @@ extern int compareJsonbContainers(JsonbContainer *a, JsonbContainer *b);
364364
extern JsonbValue *findJsonbValueFromContainer(JsonbContainer *sheader,
365365
uint32 flags,
366366
JsonbValue *key);
367+
extern JsonbValue *getKeyJsonValueFromContainer(JsonbContainer *container,
368+
const char *keyVal, int keyLen,
369+
JsonbValue *res);
367370
extern JsonbValue *getIthJsonbValueFromContainer(JsonbContainer *sheader,
368371
uint32 i);
369372
extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,

0 commit comments

Comments
 (0)