Skip to content

Commit cae8663

Browse files
author
Nikita Glukhov
committed
Variable-length jsonb KV map entries
1 parent f100794 commit cae8663

File tree

1 file changed

+98
-33
lines changed

1 file changed

+98
-33
lines changed

src/backend/utils/adt/jsonb_util.c

Lines changed: 98 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,27 @@ typedef struct CompressedJsonb
6666
int offset;
6767
} CompressedJsonb;
6868

69+
typedef struct JsonbKVMap
70+
{
71+
union
72+
{
73+
const uint8 *entries1;
74+
const uint16 *entries2;
75+
const int32 *entries4;
76+
const void *entries;
77+
} map;
78+
int entry_size;
79+
} JsonbKVMap;
80+
81+
#define JSONB_KVMAP_ENTRY_SIZE(nPairs) \
82+
((nPairs) < 256 ? 1 : (nPairs) < 65536 ? 2 : 4)
83+
84+
#define JSONB_KVMAP_ENTRY(kvmap, index) \
85+
(!(kvmap)->entry_size ? (index) : \
86+
(kvmap)->entry_size == 1 ? (int32) (kvmap)->map.entries1[index] : \
87+
(kvmap)->entry_size == 2 ? (int32) (kvmap)->map.entries2[index] : \
88+
(kvmap)->map.entries4[index])
89+
6990
struct JsonbIterator
7091
{
7192
JsonIterator ji;
@@ -81,7 +102,7 @@ struct JsonbIterator
81102
const JEntry *children; /* JEntrys for child nodes */
82103
/* Data proper. This points to the beginning of the variable-length data */
83104
char *dataProper;
84-
uint32 *kvMap;
105+
JsonbKVMap kvmap;
85106

86107
/* Current item in buffer (up to nElems) */
87108
int curIndex;
@@ -550,6 +571,24 @@ JsonFindValueInContainer(JsonContainer *json, uint32 flags, JsonValue *key)
550571
return NULL;
551572
}
552573

574+
static void *
575+
initKVMap(JsonbKVMap *kvmap, void *pentries, int field_count, bool sorted)
576+
{
577+
if (sorted)
578+
{
579+
kvmap->map.entries = pentries;
580+
kvmap->entry_size = JSONB_KVMAP_ENTRY_SIZE(field_count);
581+
582+
return (char *) pentries + INTALIGN(field_count * kvmap->entry_size);
583+
}
584+
else
585+
{
586+
kvmap->entry_size = 0;
587+
588+
return pentries;
589+
}
590+
}
591+
553592
/*
554593
* Find value by key in Jsonb object and fetch it into 'res', which is also
555594
* returned.
@@ -563,9 +602,9 @@ getKeyJsonValueFromContainer(JsonContainer *jsc,
563602
const JsonbContainer *container = JsonContainerDataPtr(jsc);
564603
const JEntry *children = container->children;
565604
int count = JsonContainerSize(jsc);
566-
char *baseAddr;
605+
char *baseAddr = (char *) (children + count * 2);
567606
bool sorted_values = (container->header & JB_TMASK) == JB_TOBJECT_SORTED;
568-
const uint32 *kvmap;
607+
JsonbKVMap kvmap;
569608
uint32 stopLow,
570609
stopHigh;
571610

@@ -579,16 +618,8 @@ getKeyJsonValueFromContainer(JsonContainer *jsc,
579618
* Binary search the container. Since we know this is an object, account
580619
* for *Pairs* of Jentrys
581620
*/
582-
if (sorted_values)
583-
{
584-
kvmap = &children[count * 2];
585-
baseAddr = (char *) &kvmap[count];
586-
}
587-
else
588-
{
589-
kvmap = NULL;
590-
baseAddr = (char *) (children + count * 2);
591-
}
621+
baseAddr = initKVMap(&kvmap, baseAddr, count, sorted_values);
622+
592623
stopLow = 0;
593624
stopHigh = count;
594625
while (stopLow < stopHigh)
@@ -609,7 +640,7 @@ getKeyJsonValueFromContainer(JsonContainer *jsc,
609640
if (difference == 0)
610641
{
611642
/* Found our key, return corresponding value */
612-
int index = (sorted_values ? kvmap[stopMiddle] : stopMiddle) + count;
643+
int index = JSONB_KVMAP_ENTRY(&kvmap, stopMiddle) + count;
613644

614645
if (!res)
615646
res = palloc(sizeof(JsonbValue));
@@ -1034,6 +1065,7 @@ JsonbIteratorToken
10341065
JsonbIteratorNext(JsonIterator **jsit, JsonbValue *val, bool skipNested)
10351066
{
10361067
JsonbIterator **it = (JsonbIterator **) jsit;
1068+
int entry_index;
10371069

10381070
if (*it == NULL)
10391071
return WJB_DONE;
@@ -1146,17 +1178,19 @@ JsonbIteratorNext(JsonIterator **jsit, JsonbValue *val, bool skipNested)
11461178
/* Set state for next call */
11471179
(*it)->state = JBI_OBJECT_KEY;
11481180

1181+
entry_index = JSONB_KVMAP_ENTRY(&(*it)->kvmap, (*it)->curIndex) + (*it)->nElems;
1182+
11491183
fillCompressedJsonbValue((*it)->compressed, (*it)->container,
1150-
((*it)->kvMap ? (*it)->kvMap[(*it)->curIndex] : (*it)->curIndex) + (*it)->nElems,
1184+
entry_index,
11511185
(*it)->dataProper,
1152-
(*it)->kvMap ?
1153-
getJsonbOffset((*it)->container, (*it)->kvMap[(*it)->curIndex] + (*it)->nElems) :
1186+
(*it)->kvmap.entry_size ?
1187+
getJsonbOffset((*it)->container, entry_index) :
11541188
(*it)->curValueOffset,
11551189
val);
11561190

11571191
JBE_ADVANCE_OFFSET((*it)->curDataOffset,
11581192
(*it)->children[(*it)->curIndex]);
1159-
if (!(*it)->kvMap)
1193+
if (!(*it)->kvmap.entry_size)
11601194
JBE_ADVANCE_OFFSET((*it)->curValueOffset,
11611195
(*it)->children[(*it)->curIndex + (*it)->nElems]);
11621196
(*it)->curIndex++;
@@ -1198,6 +1232,7 @@ jsonbIteratorInit(JsonContainer *cont, const JsonbContainer *container,
11981232
struct CompressedJsonb *cjb)
11991233
{
12001234
JsonbIterator *it;
1235+
int type = container->header & JB_TMASK;
12011236

12021237
it = palloc0(sizeof(JsonbIterator));
12031238
it->ji.container = cont;
@@ -1210,7 +1245,7 @@ jsonbIteratorInit(JsonContainer *cont, const JsonbContainer *container,
12101245
/* Array starts just after header */
12111246
it->children = container->children;
12121247

1213-
switch (container->header & JB_TMASK)
1248+
switch (type)
12141249
{
12151250
case JB_TSCALAR:
12161251
it->isScalar = true;
@@ -1225,16 +1260,12 @@ jsonbIteratorInit(JsonContainer *cont, const JsonbContainer *container,
12251260
break;
12261261

12271262
case JB_TOBJECT:
1228-
it->kvMap = NULL;
1263+
case JB_TOBJECT_SORTED:
12291264
it->dataProper =
12301265
(char *) it->children + it->nElems * sizeof(JEntry) * 2;
1231-
it->state = JBI_OBJECT_START;
1232-
break;
1266+
it->dataProper = initKVMap(&it->kvmap, it->dataProper, it->nElems,
1267+
type == JB_TOBJECT_SORTED);
12331268

1234-
case JB_TOBJECT_SORTED:
1235-
it->kvMap = (uint32 *)
1236-
((char *) it->children + it->nElems * sizeof(JEntry) * 2);
1237-
it->dataProper = (char *) &it->kvMap[it->nElems];
12381269
it->state = JBI_OBJECT_START;
12391270
break;
12401271

@@ -1958,6 +1989,7 @@ convertJsonbObject(StringInfo buffer, JEntry *pheader, const JsonbValue *val, in
19581989
uint32 header;
19591990
int nPairs = val->val.object.nPairs;
19601991
int reserved_size;
1992+
int kvmap_entry_size;
19611993
bool sorted_values = jsonb_sort_field_values && nPairs > 1;
19621994
struct
19631995
{
@@ -1984,6 +2016,7 @@ convertJsonbObject(StringInfo buffer, JEntry *pheader, const JsonbValue *val, in
19842016
{
19852017
if (values[i].index != i)
19862018
{
2019+
kvmap_entry_size = JSONB_KVMAP_ENTRY_SIZE(nPairs);
19872020
sorted_values = true;
19882021
break;
19892022
}
@@ -2006,16 +2039,45 @@ convertJsonbObject(StringInfo buffer, JEntry *pheader, const JsonbValue *val, in
20062039
/* Reserve space for the JEntries of the keys and values. */
20072040
reserved_size = sizeof(JEntry) * nPairs * 2;
20082041
if (sorted_values)
2009-
reserved_size += sizeof(int32) * nPairs;
2042+
reserved_size += INTALIGN(kvmap_entry_size * nPairs);
20102043

20112044
jentry_offset = reserveFromBuffer(buffer, reserved_size);
20122045

20132046
/* Write key-value map */
20142047
if (sorted_values)
20152048
{
2049+
int kvmap_offset = jentry_offset + sizeof(JEntry) * nPairs * 2;
2050+
20162051
for (i = 0; i < nPairs; i++)
2017-
copyToBuffer(buffer, jentry_offset + sizeof(JEntry) * nPairs * 2 + values[i].index * sizeof(int32),
2018-
&i, sizeof(int32));
2052+
{
2053+
uint8 entry1;
2054+
uint16 entry2;
2055+
uint32 entry4;
2056+
void *pentry;
2057+
2058+
if (kvmap_entry_size == 1)
2059+
{
2060+
entry1 = (uint8) i;
2061+
pentry = &entry1;
2062+
}
2063+
else if (kvmap_entry_size == 2)
2064+
{
2065+
entry2 = (uint16) i;
2066+
pentry = &entry2;
2067+
}
2068+
else
2069+
{
2070+
entry4 = (int32) i;
2071+
pentry = &entry4;
2072+
}
2073+
2074+
copyToBuffer(buffer, kvmap_offset + values[i].index * kvmap_entry_size,
2075+
pentry, kvmap_entry_size);
2076+
}
2077+
2078+
if ((kvmap_entry_size * nPairs) % ALIGNOF_INT)
2079+
memset(buffer->data + kvmap_offset + kvmap_entry_size * nPairs, 0,
2080+
ALIGNOF_INT - (kvmap_entry_size * nPairs) % ALIGNOF_INT);
20192081
}
20202082

20212083
/*
@@ -2607,9 +2669,9 @@ findValueInCompressedJsonbObject(CompressedJsonb *cjb, const char *keystr, int k
26072669
int count = container->header & JB_CMASK;
26082670
/* Since this is an object, account for *Pairs* of Jentrys */
26092671
bool sorted_values = (container->header & JB_TMASK) == JB_TOBJECT_SORTED;
2610-
char *base_addr = (char *) (children + count * 2) + (sorted_values ? sizeof(uint32) * count : 0);
2611-
uint32 *kvmap = sorted_values ? &container->children[count * 2] : NULL;
2612-
Size base_offset = base_addr - (char *) jb;
2672+
char *base_addr = (char *) (children + count * 2);
2673+
JsonbKVMap kvmap;
2674+
Size base_offset;
26132675
uint32 stopLow = 0,
26142676
stopHigh = count;
26152677

@@ -2619,6 +2681,9 @@ findValueInCompressedJsonbObject(CompressedJsonb *cjb, const char *keystr, int k
26192681
if (count <= 0)
26202682
return NULL;
26212683

2684+
base_addr = initKVMap(&kvmap, base_addr, count, sorted_values);
2685+
base_offset = base_addr - (char *) jb;
2686+
26222687
key.type = jbvString;
26232688
key.val.string.val = keystr;
26242689
key.val.string.len = keylen;
@@ -2650,7 +2715,7 @@ findValueInCompressedJsonbObject(CompressedJsonb *cjb, const char *keystr, int k
26502715
if (difference == 0)
26512716
{
26522717
/* Found our key, return corresponding value */
2653-
int index = (sorted_values ? kvmap[stopMiddle] : stopMiddle) + count;
2718+
int index = JSONB_KVMAP_ENTRY(&kvmap, stopMiddle) + count;
26542719

26552720
return fillCompressedJsonbValue(cjb, container, index, base_addr,
26562721
getJsonbOffset(container, index),

0 commit comments

Comments
 (0)