Skip to content

Commit 364ddc3

Browse files
committed
Clean up jsonb code.
The main target of this cleanup is the convertJsonb() function, but I also touched a lot of other things that I spotted into in the process. The new convertToJsonb() function uses an output buffer that's resized on demand, so the code to estimate of the size of JsonbValue is removed. The on-disk format was not changed, even though I refactored the structs used to handle it. The term "superheader" is replaced with "container". The jsonb_exists_any and jsonb_exists_all functions no longer sort the input array. That was a premature optimization, the idea being that if there are duplicates in the input array, you only need to check them once. Also, sorting the array saves some effort in the binary search used to find a key within an object. But there were drawbacks too: the sorting and deduplicating obviously isn't free, and in the typical case there are no duplicates to remove, and the gain in the binary search was minimal. Remove all that, which makes the code simpler too. This includes a bug-fix; the total length of the elements in a jsonb array or object mustn't exceed 2^28. That is now checked.
1 parent 4d155d8 commit 364ddc3

File tree

6 files changed

+556
-776
lines changed

6 files changed

+556
-776
lines changed

src/backend/utils/adt/jsonb.c

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ static void jsonb_in_array_end(void *pstate);
3333
static void jsonb_in_object_field_start(void *pstate, char *fname, bool isnull);
3434
static void jsonb_put_escaped_value(StringInfo out, JsonbValue *scalarVal);
3535
static void jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype);
36-
char *JsonbToCString(StringInfo out, char *in, int estimated_len);
3736

3837
/*
3938
* jsonb type input function
@@ -65,7 +64,7 @@ jsonb_recv(PG_FUNCTION_ARGS)
6564
if (version == 1)
6665
str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
6766
else
68-
elog(ERROR, "Unsupported jsonb version number %d", version);
67+
elog(ERROR, "unsupported jsonb version number %d", version);
6968

7069
return jsonb_from_cstring(str, nbytes);
7170
}
@@ -79,7 +78,7 @@ jsonb_out(PG_FUNCTION_ARGS)
7978
Jsonb *jb = PG_GETARG_JSONB(0);
8079
char *out;
8180

82-
out = JsonbToCString(NULL, VARDATA(jb), VARSIZE(jb));
81+
out = JsonbToCString(NULL, &jb->root, VARSIZE(jb));
8382

8483
PG_RETURN_CSTRING(out);
8584
}
@@ -97,7 +96,7 @@ jsonb_send(PG_FUNCTION_ARGS)
9796
StringInfo jtext = makeStringInfo();
9897
int version = 1;
9998

100-
(void) JsonbToCString(jtext, VARDATA(jb), VARSIZE(jb));
99+
(void) JsonbToCString(jtext, &jb->root, VARSIZE(jb));
101100

102101
pq_begintypsend(&buf);
103102
pq_sendint(&buf, version, 1);
@@ -130,7 +129,7 @@ jsonb_typeof(PG_FUNCTION_ARGS)
130129
{
131130
Assert(JB_ROOT_IS_SCALAR(in));
132131

133-
it = JsonbIteratorInit(VARDATA_ANY(in));
132+
it = JsonbIteratorInit(&in->root);
134133

135134
/*
136135
* A root scalar is stored as an array of one element, so we get the
@@ -249,7 +248,6 @@ jsonb_in_object_field_start(void *pstate, char *fname, bool isnull)
249248
v.type = jbvString;
250249
v.val.string.len = checkStringLen(strlen(fname));
251250
v.val.string.val = pnstrdup(fname, v.val.string.len);
252-
v.estSize = sizeof(JEntry) + v.val.string.len;
253251

254252
_state->res = pushJsonbValue(&_state->parseState, WJB_KEY, &v);
255253
}
@@ -290,8 +288,6 @@ jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
290288
JsonbInState *_state = (JsonbInState *) pstate;
291289
JsonbValue v;
292290

293-
v.estSize = sizeof(JEntry);
294-
295291
switch (tokentype)
296292
{
297293

@@ -300,7 +296,6 @@ jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
300296
v.type = jbvString;
301297
v.val.string.len = checkStringLen(strlen(token));
302298
v.val.string.val = pnstrdup(token, v.val.string.len);
303-
v.estSize += v.val.string.len;
304299
break;
305300
case JSON_TOKEN_NUMBER:
306301

@@ -312,7 +307,6 @@ jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
312307
v.type = jbvNumeric;
313308
v.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(token), 0, -1));
314309

315-
v.estSize += VARSIZE_ANY(v.val.numeric) +sizeof(JEntry) /* alignment */ ;
316310
break;
317311
case JSON_TOKEN_TRUE:
318312
v.type = jbvBool;
@@ -374,7 +368,7 @@ jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
374368
* if they are converting it to a text* object.
375369
*/
376370
char *
377-
JsonbToCString(StringInfo out, JsonbSuperHeader in, int estimated_len)
371+
JsonbToCString(StringInfo out, JsonbContainer *in, int estimated_len)
378372
{
379373
bool first = true;
380374
JsonbIterator *it;

src/backend/utils/adt/jsonb_gin.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ gin_extract_jsonb(PG_FUNCTION_ARGS)
8080

8181
entries = (Datum *) palloc(sizeof(Datum) * total);
8282

83-
it = JsonbIteratorInit(VARDATA(jb));
83+
it = JsonbIteratorInit(&jb->root);
8484

8585
while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
8686
{
@@ -487,7 +487,7 @@ gin_extract_jsonb_hash(PG_FUNCTION_ARGS)
487487

488488
entries = (Datum *) palloc(sizeof(Datum) * total);
489489

490-
it = JsonbIteratorInit(VARDATA(jb));
490+
it = JsonbIteratorInit(&jb->root);
491491

492492
tail.parent = NULL;
493493
tail.hash = 0;

src/backend/utils/adt/jsonb_op.c

Lines changed: 52 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*/
1414
#include "postgres.h"
1515

16+
#include "catalog/pg_type.h"
1617
#include "miscadmin.h"
1718
#include "utils/jsonb.h"
1819

@@ -34,10 +35,9 @@ jsonb_exists(PG_FUNCTION_ARGS)
3435
kval.val.string.val = VARDATA_ANY(key);
3536
kval.val.string.len = VARSIZE_ANY_EXHDR(key);
3637

37-
v = findJsonbValueFromSuperHeader(VARDATA(jb),
38-
JB_FOBJECT | JB_FARRAY,
39-
NULL,
40-
&kval);
38+
v = findJsonbValueFromContainer(&jb->root,
39+
JB_FOBJECT | JB_FARRAY,
40+
&kval);
4141

4242
PG_RETURN_BOOL(v != NULL);
4343
}
@@ -47,29 +47,28 @@ jsonb_exists_any(PG_FUNCTION_ARGS)
4747
{
4848
Jsonb *jb = PG_GETARG_JSONB(0);
4949
ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
50-
JsonbValue *arrKey = arrayToJsonbSortedArray(keys);
51-
uint32 *plowbound = NULL,
52-
lowbound = 0;
5350
int i;
51+
Datum *key_datums;
52+
bool *key_nulls;
53+
int elem_count;
5454

55-
if (arrKey == NULL || arrKey->val.object.nPairs == 0)
56-
PG_RETURN_BOOL(false);
57-
58-
if (JB_ROOT_IS_OBJECT(jb))
59-
plowbound = &lowbound;
55+
deconstruct_array(keys, TEXTOID, -1, false, 'i', &key_datums, &key_nulls,
56+
&elem_count);
6057

61-
/*
62-
* We exploit the fact that the pairs list is already sorted into strictly
63-
* increasing order to narrow the findJsonbValueFromSuperHeader search;
64-
* each search can start one entry past the previous "found" entry, or at
65-
* the lower bound of the last search.
66-
*/
67-
for (i = 0; i < arrKey->val.array.nElems; i++)
58+
for (i = 0; i < elem_count; i++)
6859
{
69-
if (findJsonbValueFromSuperHeader(VARDATA(jb),
70-
JB_FOBJECT | JB_FARRAY,
71-
plowbound,
72-
arrKey->val.array.elems + i) != NULL)
60+
JsonbValue strVal;
61+
62+
if (key_nulls[i])
63+
continue;
64+
65+
strVal.type = jbvString;
66+
strVal.val.string.val = VARDATA(key_datums[i]);
67+
strVal.val.string.len = VARSIZE(key_datums[i]) - VARHDRSZ;
68+
69+
if (findJsonbValueFromContainer(&jb->root,
70+
JB_FOBJECT | JB_FARRAY,
71+
&strVal) != NULL)
7372
PG_RETURN_BOOL(true);
7473
}
7574

@@ -81,29 +80,28 @@ jsonb_exists_all(PG_FUNCTION_ARGS)
8180
{
8281
Jsonb *jb = PG_GETARG_JSONB(0);
8382
ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
84-
JsonbValue *arrKey = arrayToJsonbSortedArray(keys);
85-
uint32 *plowbound = NULL;
86-
uint32 lowbound = 0;
8783
int i;
84+
Datum *key_datums;
85+
bool *key_nulls;
86+
int elem_count;
8887

89-
if (arrKey == NULL || arrKey->val.array.nElems == 0)
90-
PG_RETURN_BOOL(true);
88+
deconstruct_array(keys, TEXTOID, -1, false, 'i', &key_datums, &key_nulls,
89+
&elem_count);
9190

92-
if (JB_ROOT_IS_OBJECT(jb))
93-
plowbound = &lowbound;
94-
95-
/*
96-
* We exploit the fact that the pairs list is already sorted into strictly
97-
* increasing order to narrow the findJsonbValueFromSuperHeader search;
98-
* each search can start one entry past the previous "found" entry, or at
99-
* the lower bound of the last search.
100-
*/
101-
for (i = 0; i < arrKey->val.array.nElems; i++)
91+
for (i = 0; i < elem_count; i++)
10292
{
103-
if (findJsonbValueFromSuperHeader(VARDATA(jb),
104-
JB_FOBJECT | JB_FARRAY,
105-
plowbound,
106-
arrKey->val.array.elems + i) == NULL)
93+
JsonbValue strVal;
94+
95+
if (key_nulls[i])
96+
continue;
97+
98+
strVal.type = jbvString;
99+
strVal.val.string.val = VARDATA(key_datums[i]);
100+
strVal.val.string.len = VARSIZE(key_datums[i]) - VARHDRSZ;
101+
102+
if (findJsonbValueFromContainer(&jb->root,
103+
JB_FOBJECT | JB_FARRAY,
104+
&strVal) == NULL)
107105
PG_RETURN_BOOL(false);
108106
}
109107

@@ -123,8 +121,8 @@ jsonb_contains(PG_FUNCTION_ARGS)
123121
JB_ROOT_IS_OBJECT(val) != JB_ROOT_IS_OBJECT(tmpl))
124122
PG_RETURN_BOOL(false);
125123

126-
it1 = JsonbIteratorInit(VARDATA(val));
127-
it2 = JsonbIteratorInit(VARDATA(tmpl));
124+
it1 = JsonbIteratorInit(&val->root);
125+
it2 = JsonbIteratorInit(&tmpl->root);
128126

129127
PG_RETURN_BOOL(JsonbDeepContains(&it1, &it2));
130128
}
@@ -143,8 +141,8 @@ jsonb_contained(PG_FUNCTION_ARGS)
143141
JB_ROOT_IS_OBJECT(val) != JB_ROOT_IS_OBJECT(tmpl))
144142
PG_RETURN_BOOL(false);
145143

146-
it1 = JsonbIteratorInit(VARDATA(val));
147-
it2 = JsonbIteratorInit(VARDATA(tmpl));
144+
it1 = JsonbIteratorInit(&val->root);
145+
it2 = JsonbIteratorInit(&tmpl->root);
148146

149147
PG_RETURN_BOOL(JsonbDeepContains(&it1, &it2));
150148
}
@@ -156,7 +154,7 @@ jsonb_ne(PG_FUNCTION_ARGS)
156154
Jsonb *jbb = PG_GETARG_JSONB(1);
157155
bool res;
158156

159-
res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) != 0);
157+
res = (compareJsonbContainers(&jba->root, &jbb->root) != 0);
160158

161159
PG_FREE_IF_COPY(jba, 0);
162160
PG_FREE_IF_COPY(jbb, 1);
@@ -173,7 +171,7 @@ jsonb_lt(PG_FUNCTION_ARGS)
173171
Jsonb *jbb = PG_GETARG_JSONB(1);
174172
bool res;
175173

176-
res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) < 0);
174+
res = (compareJsonbContainers(&jba->root, &jbb->root) < 0);
177175

178176
PG_FREE_IF_COPY(jba, 0);
179177
PG_FREE_IF_COPY(jbb, 1);
@@ -187,7 +185,7 @@ jsonb_gt(PG_FUNCTION_ARGS)
187185
Jsonb *jbb = PG_GETARG_JSONB(1);
188186
bool res;
189187

190-
res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) > 0);
188+
res = (compareJsonbContainers(&jba->root, &jbb->root) > 0);
191189

192190
PG_FREE_IF_COPY(jba, 0);
193191
PG_FREE_IF_COPY(jbb, 1);
@@ -201,7 +199,7 @@ jsonb_le(PG_FUNCTION_ARGS)
201199
Jsonb *jbb = PG_GETARG_JSONB(1);
202200
bool res;
203201

204-
res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) <= 0);
202+
res = (compareJsonbContainers(&jba->root, &jbb->root) <= 0);
205203

206204
PG_FREE_IF_COPY(jba, 0);
207205
PG_FREE_IF_COPY(jbb, 1);
@@ -215,7 +213,7 @@ jsonb_ge(PG_FUNCTION_ARGS)
215213
Jsonb *jbb = PG_GETARG_JSONB(1);
216214
bool res;
217215

218-
res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) >= 0);
216+
res = (compareJsonbContainers(&jba->root, &jbb->root) >= 0);
219217

220218
PG_FREE_IF_COPY(jba, 0);
221219
PG_FREE_IF_COPY(jbb, 1);
@@ -229,7 +227,7 @@ jsonb_eq(PG_FUNCTION_ARGS)
229227
Jsonb *jbb = PG_GETARG_JSONB(1);
230228
bool res;
231229

232-
res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) == 0);
230+
res = (compareJsonbContainers(&jba->root, &jbb->root) == 0);
233231

234232
PG_FREE_IF_COPY(jba, 0);
235233
PG_FREE_IF_COPY(jbb, 1);
@@ -243,7 +241,7 @@ jsonb_cmp(PG_FUNCTION_ARGS)
243241
Jsonb *jbb = PG_GETARG_JSONB(1);
244242
int res;
245243

246-
res = compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb));
244+
res = compareJsonbContainers(&jba->root, &jbb->root);
247245

248246
PG_FREE_IF_COPY(jba, 0);
249247
PG_FREE_IF_COPY(jbb, 1);
@@ -265,7 +263,7 @@ jsonb_hash(PG_FUNCTION_ARGS)
265263
if (JB_ROOT_COUNT(jb) == 0)
266264
PG_RETURN_INT32(0);
267265

268-
it = JsonbIteratorInit(VARDATA(jb));
266+
it = JsonbIteratorInit(&jb->root);
269267

270268
while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
271269
{

0 commit comments

Comments
 (0)