Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * jsonb_util.c
4 : : * converting between Jsonb and JsonbValues, and iterating.
5 : : *
6 : : * Copyright (c) 2014-2024, PostgreSQL Global Development Group
7 : : *
8 : : *
9 : : * IDENTIFICATION
10 : : * src/backend/utils/adt/jsonb_util.c
11 : : *
12 : : *-------------------------------------------------------------------------
13 : : */
14 : : #include "postgres.h"
15 : :
16 : : #include "catalog/pg_collation.h"
17 : : #include "common/hashfn.h"
18 : : #include "miscadmin.h"
19 : : #include "port/pg_bitutils.h"
20 : : #include "utils/datetime.h"
21 : : #include "utils/fmgrprotos.h"
22 : : #include "utils/json.h"
23 : : #include "utils/jsonb.h"
24 : : #include "utils/memutils.h"
25 : : #include "utils/varlena.h"
26 : :
27 : : /*
28 : : * Maximum number of elements in an array (or key/value pairs in an object).
29 : : * This is limited by two things: the size of the JEntry array must fit
30 : : * in MaxAllocSize, and the number of elements (or pairs) must fit in the bits
31 : : * reserved for that in the JsonbContainer.header field.
32 : : *
33 : : * (The total size of an array's or object's elements is also limited by
34 : : * JENTRY_OFFLENMASK, but we're not concerned about that here.)
35 : : */
36 : : #define JSONB_MAX_ELEMS (Min(MaxAllocSize / sizeof(JsonbValue), JB_CMASK))
37 : : #define JSONB_MAX_PAIRS (Min(MaxAllocSize / sizeof(JsonbPair), JB_CMASK))
38 : :
39 : : static void fillJsonbValue(JsonbContainer *container, int index,
40 : : char *base_addr, uint32 offset,
41 : : JsonbValue *result);
42 : : static bool equalsJsonbScalarValue(JsonbValue *a, JsonbValue *b);
43 : : static int compareJsonbScalarValue(JsonbValue *a, JsonbValue *b);
44 : : static Jsonb *convertToJsonb(JsonbValue *val);
45 : : static void convertJsonbValue(StringInfo buffer, JEntry *header, JsonbValue *val, int level);
46 : : static void convertJsonbArray(StringInfo buffer, JEntry *header, JsonbValue *val, int level);
47 : : static void convertJsonbObject(StringInfo buffer, JEntry *header, JsonbValue *val, int level);
48 : : static void convertJsonbScalar(StringInfo buffer, JEntry *header, JsonbValue *scalarVal);
49 : :
50 : : static int reserveFromBuffer(StringInfo buffer, int len);
51 : : static void appendToBuffer(StringInfo buffer, const char *data, int len);
52 : : static void copyToBuffer(StringInfo buffer, int offset, const char *data, int len);
53 : : static short padBufferToInt(StringInfo buffer);
54 : :
55 : : static JsonbIterator *iteratorFromContainer(JsonbContainer *container, JsonbIterator *parent);
56 : : static JsonbIterator *freeAndGetParent(JsonbIterator *it);
57 : : static JsonbParseState *pushState(JsonbParseState **pstate);
58 : : static void appendKey(JsonbParseState *pstate, JsonbValue *string);
59 : : static void appendValue(JsonbParseState *pstate, JsonbValue *scalarVal);
60 : : static void appendElement(JsonbParseState *pstate, JsonbValue *scalarVal);
61 : : static int lengthCompareJsonbStringValue(const void *a, const void *b);
62 : : static int lengthCompareJsonbString(const char *val1, int len1,
63 : : const char *val2, int len2);
64 : : static int lengthCompareJsonbPair(const void *a, const void *b, void *binequal);
65 : : static void uniqueifyJsonbObject(JsonbValue *object, bool unique_keys,
66 : : bool skip_nulls);
67 : : static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
68 : : JsonbIteratorToken seq,
69 : : JsonbValue *scalarVal);
70 : :
71 : : void
1169 akorotkov@postgresql 72 :CBC 327 : JsonbToJsonbValue(Jsonb *jsonb, JsonbValue *val)
73 : : {
74 : 327 : val->type = jbvBinary;
75 : 327 : val->val.binary.data = &jsonb->root;
76 : 327 : val->val.binary.len = VARSIZE(jsonb) - VARHDRSZ;
77 : 327 : }
78 : :
79 : : /*
80 : : * Turn an in-memory JsonbValue into a Jsonb for on-disk storage.
81 : : *
82 : : * Generally we find it more convenient to directly iterate through the Jsonb
83 : : * representation and only really convert nested scalar values.
84 : : * JsonbIteratorNext() does this, so that clients of the iteration code don't
85 : : * have to directly deal with the binary representation (JsonbDeepContains() is
86 : : * a notable exception, although all exceptions are internal to this module).
87 : : * In general, functions that accept a JsonbValue argument are concerned with
88 : : * the manipulation of scalar values, or simple containers of scalar values,
89 : : * where it would be inconvenient to deal with a great amount of other state.
90 : : */
91 : : Jsonb *
3631 bruce@momjian.us 92 : 48146 : JsonbValueToJsonb(JsonbValue *val)
93 : : {
94 : : Jsonb *out;
95 : :
3675 andrew@dunslane.net 96 [ + + + + ]: 48146 : if (IsAJsonbScalar(val))
97 : 34210 : {
98 : : /* Scalar value */
99 : 34210 : JsonbParseState *pstate = NULL;
100 : : JsonbValue *res;
101 : : JsonbValue scalarArray;
102 : :
103 : 34210 : scalarArray.type = jbvArray;
3665 tgl@sss.pgh.pa.us 104 : 34210 : scalarArray.val.array.rawScalar = true;
105 : 34210 : scalarArray.val.array.nElems = 1;
106 : :
3675 andrew@dunslane.net 107 : 34210 : pushJsonbValue(&pstate, WJB_BEGIN_ARRAY, &scalarArray);
108 : 34210 : pushJsonbValue(&pstate, WJB_ELEM, val);
109 : 34210 : res = pushJsonbValue(&pstate, WJB_END_ARRAY, NULL);
110 : :
3630 heikki.linnakangas@i 111 : 34210 : out = convertToJsonb(res);
112 : : }
3675 andrew@dunslane.net 113 [ + + + + ]: 13936 : else if (val->type == jbvObject || val->type == jbvArray)
114 : : {
3630 heikki.linnakangas@i 115 : 12552 : out = convertToJsonb(val);
116 : : }
117 : : else
118 : : {
3675 andrew@dunslane.net 119 [ - + ]: 1384 : Assert(val->type == jbvBinary);
3665 tgl@sss.pgh.pa.us 120 : 1384 : out = palloc(VARHDRSZ + val->val.binary.len);
121 : 1384 : SET_VARSIZE(out, VARHDRSZ + val->val.binary.len);
122 : 1384 : memcpy(VARDATA(out), val->val.binary.data, val->val.binary.len);
123 : : }
124 : :
3675 andrew@dunslane.net 125 : 48146 : return out;
126 : : }
127 : :
128 : : /*
129 : : * Get the offset of the variable-length portion of a Jsonb node within
130 : : * the variable-length-data part of its container. The node is identified
131 : : * by index within the container's JEntry array.
132 : : */
133 : : uint32
3485 tgl@sss.pgh.pa.us 134 : 943345 : getJsonbOffset(const JsonbContainer *jc, int index)
135 : : {
136 : 943345 : uint32 offset = 0;
137 : : int i;
138 : :
139 : : /*
140 : : * Start offset of this entry is equal to the end offset of the previous
141 : : * entry. Walk backwards to the most recent entry stored as an end
142 : : * offset, returning that offset plus any lengths in between.
143 : : */
144 [ + + ]: 2811232 : for (i = index - 1; i >= 0; i--)
145 : : {
146 : 2459021 : offset += JBE_OFFLENFLD(jc->children[i]);
147 [ + + ]: 2459021 : if (JBE_HAS_OFF(jc->children[i]))
148 : 591134 : break;
149 : : }
150 : :
151 : 943345 : return offset;
152 : : }
153 : :
154 : : /*
155 : : * Get the length of the variable-length portion of a Jsonb node.
156 : : * The node is identified by index within the container's JEntry array.
157 : : */
158 : : uint32
159 : 833055 : getJsonbLength(const JsonbContainer *jc, int index)
160 : : {
161 : : uint32 off;
162 : : uint32 len;
163 : :
164 : : /*
165 : : * If the length is stored directly in the JEntry, just return it.
166 : : * Otherwise, get the begin offset of the entry, and subtract that from
167 : : * the stored end+1 offset.
168 : : */
169 [ + + ]: 833055 : if (JBE_HAS_OFF(jc->children[index]))
170 : : {
171 : 273245 : off = getJsonbOffset(jc, index);
172 : 273245 : len = JBE_OFFLENFLD(jc->children[index]) - off;
173 : : }
174 : : else
175 : 559810 : len = JBE_OFFLENFLD(jc->children[index]);
176 : :
177 : 833055 : return len;
178 : : }
179 : :
180 : : /*
181 : : * BT comparator worker function. Returns an integer less than, equal to, or
182 : : * greater than zero, indicating whether a is less than, equal to, or greater
183 : : * than b. Consistent with the requirements for a B-Tree operator class
184 : : *
185 : : * Strings are compared lexically, in contrast with other places where we use a
186 : : * much simpler comparator logic for searching through Strings. Since this is
187 : : * called from B-Tree support function 1, we're careful about not leaking
188 : : * memory here.
189 : : */
190 : : int
3630 heikki.linnakangas@i 191 : 136665 : compareJsonbContainers(JsonbContainer *a, JsonbContainer *b)
192 : : {
193 : : JsonbIterator *ita,
194 : : *itb;
3675 andrew@dunslane.net 195 : 136665 : int res = 0;
196 : :
197 : 136665 : ita = JsonbIteratorInit(a);
198 : 136665 : itb = JsonbIteratorInit(b);
199 : :
200 : : do
201 : : {
202 : : JsonbValue va,
203 : : vb;
204 : : JsonbIteratorToken ra,
205 : : rb;
206 : :
207 : 399338 : ra = JsonbIteratorNext(&ita, &va, false);
208 : 399338 : rb = JsonbIteratorNext(&itb, &vb, false);
209 : :
210 [ + - ]: 399338 : if (ra == rb)
211 : : {
212 [ + + ]: 399338 : if (ra == WJB_DONE)
213 : : {
214 : : /* Decisively equal */
215 : 13838 : break;
216 : : }
217 : :
3609 heikki.linnakangas@i 218 [ + + + + ]: 385500 : if (ra == WJB_END_ARRAY || ra == WJB_END_OBJECT)
219 : : {
220 : : /*
221 : : * There is no array or object to compare at this stage of
222 : : * processing. jbvArray/jbvObject values are compared
223 : : * initially, at the WJB_BEGIN_ARRAY and WJB_BEGIN_OBJECT
224 : : * tokens.
225 : : */
226 : 13914 : continue;
227 : : }
228 : :
3675 andrew@dunslane.net 229 [ + - ]: 371586 : if (va.type == vb.type)
230 : : {
231 [ + + + - : 371586 : switch (va.type)
- - ]
232 : : {
233 : 234688 : case jbvString:
234 : : case jbvNull:
235 : : case jbvNumeric:
236 : : case jbvBool:
237 : 234688 : res = compareJsonbScalarValue(&va, &vb);
238 : 234688 : break;
239 : 198 : case jbvArray:
240 : :
241 : : /*
242 : : * This could be a "raw scalar" pseudo array. That's
243 : : * a special case here though, since we still want the
244 : : * general type-based comparisons to apply, and as far
245 : : * as we're concerned a pseudo array is just a scalar.
246 : : */
3665 tgl@sss.pgh.pa.us 247 [ - + ]: 198 : if (va.val.array.rawScalar != vb.val.array.rawScalar)
3665 tgl@sss.pgh.pa.us 248 [ # # ]:UBC 0 : res = (va.val.array.rawScalar) ? -1 : 1;
3665 tgl@sss.pgh.pa.us 249 [ + + ]:CBC 198 : if (va.val.array.nElems != vb.val.array.nElems)
250 [ + + ]: 91 : res = (va.val.array.nElems > vb.val.array.nElems) ? 1 : -1;
3675 andrew@dunslane.net 251 : 198 : break;
252 : 136700 : case jbvObject:
3665 tgl@sss.pgh.pa.us 253 [ + + ]: 136700 : if (va.val.object.nPairs != vb.val.object.nPairs)
254 [ + + ]: 43938 : res = (va.val.object.nPairs > vb.val.object.nPairs) ? 1 : -1;
3675 andrew@dunslane.net 255 : 136700 : break;
3675 andrew@dunslane.net 256 :UBC 0 : case jbvBinary:
257 [ # # ]: 0 : elog(ERROR, "unexpected jbvBinary value");
258 : : break;
1663 akorotkov@postgresql 259 : 0 : case jbvDatetime:
260 [ # # ]: 0 : elog(ERROR, "unexpected jbvDatetime value");
261 : : break;
262 : : }
263 : : }
264 : : else
265 : : {
266 : : /* Type-defined order */
3675 andrew@dunslane.net 267 [ # # ]: 0 : res = (va.type > vb.type) ? 1 : -1;
268 : : }
269 : : }
270 : : else
271 : : {
272 : : /*
273 : : * It's safe to assume that the types differed, and that the va
274 : : * and vb values passed were set.
275 : : *
276 : : * If the two values were of the same container type, then there'd
277 : : * have been a chance to observe the variation in the number of
278 : : * elements/pairs (when processing WJB_BEGIN_OBJECT, say). They're
279 : : * either two heterogeneously-typed containers, or a container and
280 : : * some scalar type.
281 : : *
282 : : * We don't have to consider the WJB_END_ARRAY and WJB_END_OBJECT
283 : : * cases here, because we would have seen the corresponding
284 : : * WJB_BEGIN_ARRAY and WJB_BEGIN_OBJECT tokens first, and
285 : : * concluded that they don't match.
286 : : */
3609 heikki.linnakangas@i 287 [ # # # # ]: 0 : Assert(ra != WJB_END_ARRAY && ra != WJB_END_OBJECT);
288 [ # # # # ]: 0 : Assert(rb != WJB_END_ARRAY && rb != WJB_END_OBJECT);
289 : :
3675 andrew@dunslane.net 290 [ # # ]: 0 : Assert(va.type != vb.type);
3609 heikki.linnakangas@i 291 [ # # ]: 0 : Assert(va.type != jbvBinary);
292 [ # # ]: 0 : Assert(vb.type != jbvBinary);
293 : : /* Type-defined order */
3675 andrew@dunslane.net 294 [ # # ]: 0 : res = (va.type > vb.type) ? 1 : -1;
295 : : }
296 : : }
3675 andrew@dunslane.net 297 [ + + ]:CBC 385500 : while (res == 0);
298 : :
299 [ + + ]: 259649 : while (ita != NULL)
300 : : {
301 : 122984 : JsonbIterator *i = ita->parent;
302 : :
303 : 122984 : pfree(ita);
304 : 122984 : ita = i;
305 : : }
306 [ + + ]: 259649 : while (itb != NULL)
307 : : {
308 : 122984 : JsonbIterator *i = itb->parent;
309 : :
310 : 122984 : pfree(itb);
311 : 122984 : itb = i;
312 : : }
313 : :
314 : 136665 : return res;
315 : : }
316 : :
317 : : /*
318 : : * Find value in object (i.e. the "value" part of some key/value pair in an
319 : : * object), or find a matching element if we're looking through an array. Do
320 : : * so on the basis of equality of the object keys only, or alternatively
321 : : * element values only, with a caller-supplied value "key". The "flags"
322 : : * argument allows the caller to specify which container types are of interest.
323 : : *
324 : : * This exported utility function exists to facilitate various cases concerned
325 : : * with "containment". If asked to look through an object, the caller had
326 : : * better pass a Jsonb String, because their keys can only be strings.
327 : : * Otherwise, for an array, any type of JsonbValue will do.
328 : : *
329 : : * In order to proceed with the search, it is necessary for callers to have
330 : : * both specified an interest in exactly one particular container type with an
331 : : * appropriate flag, as well as having the pointed-to Jsonb container be of
332 : : * one of those same container types at the top level. (Actually, we just do
333 : : * whichever makes sense to save callers the trouble of figuring it out - at
334 : : * most one can make sense, because the container either points to an array
335 : : * (possibly a "raw scalar" pseudo array) or an object.)
336 : : *
337 : : * Note that we can return a jbvBinary JsonbValue if this is called on an
338 : : * object, but we never do so on an array. If the caller asks to look through
339 : : * a container type that is not of the type pointed to by the container,
340 : : * immediately fall through and return NULL. If we cannot find the value,
341 : : * return NULL. Otherwise, return palloc()'d copy of value.
342 : : */
343 : : JsonbValue *
3630 heikki.linnakangas@i 344 : 103707 : findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
345 : : JsonbValue *key)
346 : : {
3628 347 : 103707 : JEntry *children = container->children;
2636 tgl@sss.pgh.pa.us 348 : 103707 : int count = JsonContainerSize(container);
349 : :
3675 andrew@dunslane.net 350 [ - + ]: 103707 : Assert((flags & ~(JB_FARRAY | JB_FOBJECT)) == 0);
351 : :
352 : : /* Quick out without a palloc cycle if object/array is empty */
3485 tgl@sss.pgh.pa.us 353 [ + + ]: 103707 : if (count <= 0)
354 : 11037 : return NULL;
355 : :
2636 356 [ + + + + ]: 92670 : if ((flags & JB_FARRAY) && JsonContainerIsArray(container))
3675 andrew@dunslane.net 357 : 48 : {
1668 alvherre@alvh.no-ip. 358 : 240 : JsonbValue *result = palloc(sizeof(JsonbValue));
3628 heikki.linnakangas@i 359 : 240 : char *base_addr = (char *) (children + count);
3485 tgl@sss.pgh.pa.us 360 : 240 : uint32 offset = 0;
361 : : int i;
362 : :
3675 andrew@dunslane.net 363 [ + + ]: 447 : for (i = 0; i < count; i++)
364 : : {
3485 tgl@sss.pgh.pa.us 365 : 399 : fillJsonbValue(container, i, base_addr, offset, result);
366 : :
3630 heikki.linnakangas@i 367 [ + + ]: 399 : if (key->type == result->type)
368 : : {
3609 369 [ + + ]: 354 : if (equalsJsonbScalarValue(key, result))
3630 370 : 192 : return result;
371 : : }
372 : :
3485 tgl@sss.pgh.pa.us 373 [ + + ]: 207 : JBE_ADVANCE_OFFSET(offset, children[i]);
374 : : }
375 : :
1668 alvherre@alvh.no-ip. 376 : 48 : pfree(result);
377 : : }
2636 tgl@sss.pgh.pa.us 378 [ + - + - ]: 92430 : else if ((flags & JB_FOBJECT) && JsonContainerIsObject(container))
379 : : {
380 : : /* Object key passed by caller must be a string */
3675 andrew@dunslane.net 381 [ - + ]: 92430 : Assert(key->type == jbvString);
382 : :
1668 alvherre@alvh.no-ip. 383 : 92430 : return getKeyJsonValueFromContainer(container, key->val.string.val,
384 : : key->val.string.len, NULL);
385 : : }
386 : :
387 : : /* Not found */
388 : 48 : return NULL;
389 : : }
390 : :
391 : : /*
392 : : * Find value by key in Jsonb object and fetch it into 'res', which is also
393 : : * returned.
394 : : *
395 : : * 'res' can be passed in as NULL, in which case it's newly palloc'ed here.
396 : : */
397 : : JsonbValue *
398 : 126672 : getKeyJsonValueFromContainer(JsonbContainer *container,
399 : : const char *keyVal, int keyLen, JsonbValue *res)
400 : : {
401 : 126672 : JEntry *children = container->children;
402 : 126672 : int count = JsonContainerSize(container);
403 : : char *baseAddr;
404 : : uint32 stopLow,
405 : : stopHigh;
406 : :
407 [ - + ]: 126672 : Assert(JsonContainerIsObject(container));
408 : :
409 : : /* Quick out without a palloc cycle if object is empty */
410 [ + + ]: 126672 : if (count <= 0)
411 : 1416 : return NULL;
412 : :
413 : : /*
414 : : * Binary search the container. Since we know this is an object, account
415 : : * for *Pairs* of Jentrys
416 : : */
417 : 125256 : baseAddr = (char *) (children + count * 2);
418 : 125256 : stopLow = 0;
419 : 125256 : stopHigh = count;
420 [ + + ]: 402225 : while (stopLow < stopHigh)
421 : : {
422 : : uint32 stopMiddle;
423 : : int difference;
424 : : const char *candidateVal;
425 : : int candidateLen;
426 : :
427 : 302094 : stopMiddle = stopLow + (stopHigh - stopLow) / 2;
428 : :
429 : 302094 : candidateVal = baseAddr + getJsonbOffset(container, stopMiddle);
430 : 302094 : candidateLen = getJsonbLength(container, stopMiddle);
431 : :
432 : 302094 : difference = lengthCompareJsonbString(candidateVal, candidateLen,
433 : : keyVal, keyLen);
434 : :
435 [ + + ]: 302094 : if (difference == 0)
436 : : {
437 : : /* Found our key, return corresponding value */
438 : 25125 : int index = stopMiddle + count;
439 : :
440 [ + + ]: 25125 : if (!res)
441 : 22698 : res = palloc(sizeof(JsonbValue));
442 : :
443 : 25125 : fillJsonbValue(container, index, baseAddr,
444 : : getJsonbOffset(container, index),
445 : : res);
446 : :
447 : 25125 : return res;
448 : : }
449 : : else
450 : : {
451 [ + + ]: 276969 : if (difference < 0)
452 : 102642 : stopLow = stopMiddle + 1;
453 : : else
454 : 174327 : stopHigh = stopMiddle;
455 : : }
456 : : }
457 : :
458 : : /* Not found */
3675 andrew@dunslane.net 459 : 100131 : return NULL;
460 : : }
461 : :
462 : : /*
463 : : * Get i-th value of a Jsonb array.
464 : : *
465 : : * Returns palloc()'d copy of the value, or NULL if it does not exist.
466 : : */
467 : : JsonbValue *
3630 heikki.linnakangas@i 468 : 492 : getIthJsonbValueFromContainer(JsonbContainer *container, uint32 i)
469 : : {
470 : : JsonbValue *result;
471 : : char *base_addr;
472 : : uint32 nelements;
473 : :
2636 tgl@sss.pgh.pa.us 474 [ - + ]: 492 : if (!JsonContainerIsArray(container))
3630 heikki.linnakangas@i 475 [ # # ]:UBC 0 : elog(ERROR, "not a jsonb array");
476 : :
2636 tgl@sss.pgh.pa.us 477 :CBC 492 : nelements = JsonContainerSize(container);
3628 heikki.linnakangas@i 478 : 492 : base_addr = (char *) &container->children[nelements];
479 : :
3630 480 [ + + ]: 492 : if (i >= nelements)
3675 andrew@dunslane.net 481 : 30 : return NULL;
482 : :
3630 heikki.linnakangas@i 483 : 462 : result = palloc(sizeof(JsonbValue));
484 : :
3485 tgl@sss.pgh.pa.us 485 : 462 : fillJsonbValue(container, i, base_addr,
486 : : getJsonbOffset(container, i),
487 : : result);
488 : :
3630 heikki.linnakangas@i 489 : 462 : return result;
490 : : }
491 : :
492 : : /*
493 : : * A helper function to fill in a JsonbValue to represent an element of an
494 : : * array, or a key or value of an object.
495 : : *
496 : : * The node's JEntry is at container->children[index], and its variable-length
497 : : * data is at base_addr + offset. We make the caller determine the offset
498 : : * since in many cases the caller can amortize that work across multiple
499 : : * children. When it can't, it can just call getJsonbOffset().
500 : : *
501 : : * A nested array or object will be returned as jbvBinary, ie. it won't be
502 : : * expanded.
503 : : */
504 : : static void
3485 tgl@sss.pgh.pa.us 505 : 768794 : fillJsonbValue(JsonbContainer *container, int index,
506 : : char *base_addr, uint32 offset,
507 : : JsonbValue *result)
508 : : {
509 : 768794 : JEntry entry = container->children[index];
510 : :
3628 heikki.linnakangas@i 511 [ + + ]: 768794 : if (JBE_ISNULL(entry))
512 : : {
3675 andrew@dunslane.net 513 : 2635 : result->type = jbvNull;
514 : : }
3628 heikki.linnakangas@i 515 [ + + ]: 766159 : else if (JBE_ISSTRING(entry))
516 : : {
3675 andrew@dunslane.net 517 : 523340 : result->type = jbvString;
3485 tgl@sss.pgh.pa.us 518 : 523340 : result->val.string.val = base_addr + offset;
519 : 523340 : result->val.string.len = getJsonbLength(container, index);
3630 heikki.linnakangas@i 520 [ - + ]: 523340 : Assert(result->val.string.len >= 0);
521 : : }
3628 522 [ + + ]: 242819 : else if (JBE_ISNUMERIC(entry))
523 : : {
3675 andrew@dunslane.net 524 : 161779 : result->type = jbvNumeric;
3485 tgl@sss.pgh.pa.us 525 : 161779 : result->val.numeric = (Numeric) (base_addr + INTALIGN(offset));
526 : : }
3628 heikki.linnakangas@i 527 [ + + ]: 81040 : else if (JBE_ISBOOL_TRUE(entry))
528 : : {
3630 529 : 35478 : result->type = jbvBool;
530 : 35478 : result->val.boolean = true;
531 : : }
3628 532 [ + + ]: 45562 : else if (JBE_ISBOOL_FALSE(entry))
533 : : {
3675 andrew@dunslane.net 534 : 37941 : result->type = jbvBool;
3630 heikki.linnakangas@i 535 : 37941 : result->val.boolean = false;
536 : : }
537 : : else
538 : : {
3628 539 [ - + ]: 7621 : Assert(JBE_ISCONTAINER(entry));
3675 andrew@dunslane.net 540 : 7621 : result->type = jbvBinary;
541 : : /* Remove alignment padding from data pointer and length */
3485 tgl@sss.pgh.pa.us 542 : 7621 : result->val.binary.data = (JsonbContainer *) (base_addr + INTALIGN(offset));
543 : 7621 : result->val.binary.len = getJsonbLength(container, index) -
544 : 7621 : (INTALIGN(offset) - offset);
545 : : }
3675 andrew@dunslane.net 546 : 768794 : }
547 : :
548 : : /*
549 : : * Push JsonbValue into JsonbParseState.
550 : : *
551 : : * Used when parsing JSON tokens to form Jsonb, or when converting an in-memory
552 : : * JsonbValue to a Jsonb.
553 : : *
554 : : * Initial state of *JsonbParseState is NULL, since it'll be allocated here
555 : : * originally (caller will get JsonbParseState back by reference).
556 : : *
557 : : * Only sequential tokens pertaining to non-container types should pass a
558 : : * JsonbValue. There is one exception -- WJB_BEGIN_ARRAY callers may pass a
559 : : * "raw scalar" pseudo array to append it - the actual scalar should be passed
560 : : * next and it will be added as the only member of the array.
561 : : *
562 : : * Values of type jbvBinary, which are rolled up arrays and objects,
563 : : * are unpacked before being added to the result.
564 : : */
565 : : JsonbValue *
3630 heikki.linnakangas@i 566 : 214586 : pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
567 : : JsonbValue *jbval)
568 : : {
569 : : JsonbIterator *it;
3250 andrew@dunslane.net 570 : 214586 : JsonbValue *res = NULL;
571 : : JsonbValue v;
572 : : JsonbIteratorToken tok;
573 : : int i;
574 : :
1169 akorotkov@postgresql 575 [ + + + + : 214586 : if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvObject)
+ + - + ]
576 : : {
1169 akorotkov@postgresql 577 :UBC 0 : pushJsonbValue(pstate, WJB_BEGIN_OBJECT, NULL);
578 [ # # ]: 0 : for (i = 0; i < jbval->val.object.nPairs; i++)
579 : : {
580 : 0 : pushJsonbValue(pstate, WJB_KEY, &jbval->val.object.pairs[i].key);
581 : 0 : pushJsonbValue(pstate, WJB_VALUE, &jbval->val.object.pairs[i].value);
582 : : }
583 : :
584 : 0 : return pushJsonbValue(pstate, WJB_END_OBJECT, NULL);
585 : : }
586 : :
1169 akorotkov@postgresql 587 [ + + + + :CBC 214586 : if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvArray)
+ + - + ]
588 : : {
1169 akorotkov@postgresql 589 :UBC 0 : pushJsonbValue(pstate, WJB_BEGIN_ARRAY, NULL);
590 [ # # ]: 0 : for (i = 0; i < jbval->val.array.nElems; i++)
591 : : {
592 : 0 : pushJsonbValue(pstate, WJB_ELEM, &jbval->val.array.elems[i]);
593 : : }
594 : :
595 : 0 : return pushJsonbValue(pstate, WJB_END_ARRAY, NULL);
596 : : }
597 : :
3250 andrew@dunslane.net 598 [ + + + + :CBC 214586 : if (!jbval || (seq != WJB_ELEM && seq != WJB_VALUE) ||
+ + ]
3246 599 [ + + ]: 73626 : jbval->type != jbvBinary)
600 : : {
601 : : /* drop through */
3250 602 : 214151 : return pushJsonbValueScalar(pstate, seq, jbval);
603 : : }
604 : :
605 : : /* unpack the binary and add each piece to the pstate */
3246 606 : 435 : it = JsonbIteratorInit(jbval->val.binary.data);
607 : :
1169 akorotkov@postgresql 608 [ + + + - ]: 435 : if ((jbval->val.binary.data->header & JB_FSCALAR) && *pstate)
609 : : {
610 : 171 : tok = JsonbIteratorNext(&it, &v, true);
611 [ - + ]: 171 : Assert(tok == WJB_BEGIN_ARRAY);
612 [ + - - + ]: 171 : Assert(v.type == jbvArray && v.val.array.rawScalar);
613 : :
614 : 171 : tok = JsonbIteratorNext(&it, &v, true);
615 [ - + ]: 171 : Assert(tok == WJB_ELEM);
616 : :
617 : 171 : res = pushJsonbValueScalar(pstate, seq, &v);
618 : :
619 : 171 : tok = JsonbIteratorNext(&it, &v, true);
620 [ - + ]: 171 : Assert(tok == WJB_END_ARRAY);
621 [ - + ]: 171 : Assert(it == NULL);
622 : :
623 : 171 : return res;
624 : : }
625 : :
3250 andrew@dunslane.net 626 [ + + ]: 1848 : while ((tok = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
627 [ + + ]: 2316 : res = pushJsonbValueScalar(pstate, tok,
1169 akorotkov@postgresql 628 [ + + ]: 732 : tok < WJB_BEGIN_ARRAY ||
629 : 195 : (tok == WJB_BEGIN_ARRAY &&
630 [ - + ]: 195 : v.val.array.rawScalar) ? &v : NULL);
631 : :
3250 andrew@dunslane.net 632 : 264 : return res;
633 : : }
634 : :
635 : : /*
636 : : * Do the actual pushing, with only scalar or pseudo-scalar-array values
637 : : * accepted.
638 : : */
639 : : static JsonbValue *
640 : 215906 : pushJsonbValueScalar(JsonbParseState **pstate, JsonbIteratorToken seq,
641 : : JsonbValue *scalarVal)
642 : : {
3675 643 : 215906 : JsonbValue *result = NULL;
644 : :
645 [ + + + + : 215906 : switch (seq)
+ + + - ]
646 : : {
647 : 44695 : case WJB_BEGIN_ARRAY:
3665 tgl@sss.pgh.pa.us 648 [ + + - + ]: 44695 : Assert(!scalarVal || scalarVal->val.array.rawScalar);
3675 andrew@dunslane.net 649 : 44695 : *pstate = pushState(pstate);
650 : 44695 : result = &(*pstate)->contVal;
651 : 44695 : (*pstate)->contVal.type = jbvArray;
3665 tgl@sss.pgh.pa.us 652 : 44695 : (*pstate)->contVal.val.array.nElems = 0;
653 [ + + ]: 81779 : (*pstate)->contVal.val.array.rawScalar = (scalarVal &&
2489 654 [ + - ]: 37084 : scalarVal->val.array.rawScalar);
3665 655 [ + + + - ]: 44695 : if (scalarVal && scalarVal->val.array.nElems > 0)
656 : : {
657 : : /* Assume that this array is still really a scalar */
3675 andrew@dunslane.net 658 [ - + ]: 37084 : Assert(scalarVal->type == jbvArray);
3665 tgl@sss.pgh.pa.us 659 : 37084 : (*pstate)->size = scalarVal->val.array.nElems;
660 : : }
661 : : else
662 : : {
3675 andrew@dunslane.net 663 : 7611 : (*pstate)->size = 4;
664 : : }
3665 tgl@sss.pgh.pa.us 665 : 89390 : (*pstate)->contVal.val.array.elems = palloc(sizeof(JsonbValue) *
3631 bruce@momjian.us 666 : 44695 : (*pstate)->size);
3675 andrew@dunslane.net 667 : 44695 : break;
668 : 12468 : case WJB_BEGIN_OBJECT:
669 [ - + ]: 12468 : Assert(!scalarVal);
670 : 12468 : *pstate = pushState(pstate);
671 : 12468 : result = &(*pstate)->contVal;
672 : 12468 : (*pstate)->contVal.type = jbvObject;
3665 tgl@sss.pgh.pa.us 673 : 12468 : (*pstate)->contVal.val.object.nPairs = 0;
3675 andrew@dunslane.net 674 : 12468 : (*pstate)->size = 4;
3665 tgl@sss.pgh.pa.us 675 : 24936 : (*pstate)->contVal.val.object.pairs = palloc(sizeof(JsonbPair) *
3631 bruce@momjian.us 676 : 12468 : (*pstate)->size);
3675 andrew@dunslane.net 677 : 12468 : break;
678 : 32024 : case WJB_KEY:
679 [ - + ]: 32024 : Assert(scalarVal->type == jbvString);
680 : 32024 : appendKey(*pstate, scalarVal);
681 : 32024 : break;
682 : 26365 : case WJB_VALUE:
3250 683 [ - + - - ]: 26365 : Assert(IsAJsonbScalar(scalarVal));
3675 684 : 26365 : appendValue(*pstate, scalarVal);
685 : 26365 : break;
686 : 47531 : case WJB_ELEM:
3250 687 [ + + - + ]: 47531 : Assert(IsAJsonbScalar(scalarVal));
3675 688 : 47531 : appendElement(*pstate, scalarVal);
689 : 47531 : break;
690 : 10350 : case WJB_END_OBJECT:
382 alvherre@alvh.no-ip. 691 : 10350 : uniqueifyJsonbObject(&(*pstate)->contVal,
692 : 10350 : (*pstate)->unique_keys,
693 : 10350 : (*pstate)->skip_nulls);
694 : : /* fall through! */
3675 andrew@dunslane.net 695 : 52808 : case WJB_END_ARRAY:
696 : : /* Steps here common to WJB_END_OBJECT case */
697 [ - + ]: 52808 : Assert(!scalarVal);
698 : 52808 : result = &(*pstate)->contVal;
699 : :
700 : : /*
701 : : * Pop stack and push current array/object as value in parent
702 : : * array/object
703 : : */
704 : 52808 : *pstate = (*pstate)->next;
705 [ + + ]: 52808 : if (*pstate)
706 : : {
707 [ + + - ]: 6046 : switch ((*pstate)->contVal.type)
708 : : {
709 : 2400 : case jbvArray:
710 : 2400 : appendElement(*pstate, result);
711 : 2400 : break;
712 : 3646 : case jbvObject:
713 : 3646 : appendValue(*pstate, result);
714 : 3646 : break;
3675 andrew@dunslane.net 715 :UBC 0 : default:
716 [ # # ]: 0 : elog(ERROR, "invalid jsonb container type");
717 : : }
718 : : }
3675 andrew@dunslane.net 719 :CBC 52808 : break;
3675 andrew@dunslane.net 720 :UBC 0 : default:
721 [ # # ]: 0 : elog(ERROR, "unrecognized jsonb sequential processing token");
722 : : }
723 : :
3675 andrew@dunslane.net 724 :CBC 215891 : return result;
725 : : }
726 : :
727 : : /*
728 : : * pushJsonbValue() worker: Iteration-like forming of Jsonb
729 : : */
730 : : static JsonbParseState *
3628 heikki.linnakangas@i 731 : 57163 : pushState(JsonbParseState **pstate)
732 : : {
733 : 57163 : JsonbParseState *ns = palloc(sizeof(JsonbParseState));
734 : :
735 : 57163 : ns->next = *pstate;
382 alvherre@alvh.no-ip. 736 : 57163 : ns->unique_keys = false;
737 : 57163 : ns->skip_nulls = false;
738 : :
3628 heikki.linnakangas@i 739 : 57163 : return ns;
740 : : }
741 : :
742 : : /*
743 : : * pushJsonbValue() worker: Append a pair key to state when generating a Jsonb
744 : : */
745 : : static void
746 : 32024 : appendKey(JsonbParseState *pstate, JsonbValue *string)
747 : : {
748 : 32024 : JsonbValue *object = &pstate->contVal;
749 : :
750 [ - + ]: 32024 : Assert(object->type == jbvObject);
751 [ - + ]: 32024 : Assert(string->type == jbvString);
752 : :
753 [ - + ]: 32024 : if (object->val.object.nPairs >= JSONB_MAX_PAIRS)
3628 heikki.linnakangas@i 754 [ # # ]:UBC 0 : ereport(ERROR,
755 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
756 : : errmsg("number of jsonb object pairs exceeds the maximum allowed (%zu)",
757 : : JSONB_MAX_PAIRS)));
758 : :
3628 heikki.linnakangas@i 759 [ + + ]:CBC 32024 : if (object->val.object.nPairs >= pstate->size)
760 : : {
761 : 2856 : pstate->size *= 2;
762 : 2856 : object->val.object.pairs = repalloc(object->val.object.pairs,
763 : 2856 : sizeof(JsonbPair) * pstate->size);
764 : : }
765 : :
766 : 32024 : object->val.object.pairs[object->val.object.nPairs].key = *string;
767 : 32024 : object->val.object.pairs[object->val.object.nPairs].order = object->val.object.nPairs;
768 : 32024 : }
769 : :
770 : : /*
771 : : * pushJsonbValue() worker: Append a pair value to state when generating a
772 : : * Jsonb
773 : : */
774 : : static void
775 : 30011 : appendValue(JsonbParseState *pstate, JsonbValue *scalarVal)
776 : : {
777 : 30011 : JsonbValue *object = &pstate->contVal;
778 : :
779 [ - + ]: 30011 : Assert(object->type == jbvObject);
780 : :
781 : 30011 : object->val.object.pairs[object->val.object.nPairs++].value = *scalarVal;
782 : 30011 : }
783 : :
784 : : /*
785 : : * pushJsonbValue() worker: Append an element to state when generating a Jsonb
786 : : */
787 : : static void
788 : 49931 : appendElement(JsonbParseState *pstate, JsonbValue *scalarVal)
789 : : {
790 : 49931 : JsonbValue *array = &pstate->contVal;
791 : :
792 [ - + ]: 49931 : Assert(array->type == jbvArray);
793 : :
794 [ - + ]: 49931 : if (array->val.array.nElems >= JSONB_MAX_ELEMS)
3628 heikki.linnakangas@i 795 [ # # ]:UBC 0 : ereport(ERROR,
796 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
797 : : errmsg("number of jsonb array elements exceeds the maximum allowed (%zu)",
798 : : JSONB_MAX_ELEMS)));
799 : :
3628 heikki.linnakangas@i 800 [ + + ]:CBC 49931 : if (array->val.array.nElems >= pstate->size)
801 : : {
802 : 774 : pstate->size *= 2;
803 : 774 : array->val.array.elems = repalloc(array->val.array.elems,
804 : 774 : sizeof(JsonbValue) * pstate->size);
805 : : }
806 : :
807 : 49931 : array->val.array.elems[array->val.array.nElems++] = *scalarVal;
808 : 49931 : }
809 : :
810 : : /*
811 : : * Given a JsonbContainer, expand to JsonbIterator to iterate over items
812 : : * fully expanded to in-memory representation for manipulation.
813 : : *
814 : : * See JsonbIteratorNext() for notes on memory management.
815 : : */
816 : : JsonbIterator *
3630 817 : 353768 : JsonbIteratorInit(JsonbContainer *container)
818 : : {
3628 819 : 353768 : return iteratorFromContainer(container, NULL);
820 : : }
821 : :
822 : : /*
823 : : * Get next JsonbValue while iterating
824 : : *
825 : : * Caller should initially pass their own, original iterator. They may get
826 : : * back a child iterator palloc()'d here instead. The function can be relied
827 : : * on to free those child iterators, lest the memory allocated for highly
828 : : * nested objects become unreasonable, but only if callers don't end iteration
829 : : * early (by breaking upon having found something in a search, for example).
830 : : *
831 : : * Callers in such a scenario, that are particularly sensitive to leaking
832 : : * memory in a long-lived context may walk the ancestral tree from the final
833 : : * iterator we left them with to its oldest ancestor, pfree()ing as they go.
834 : : * They do not have to free any other memory previously allocated for iterators
835 : : * but not accessible as direct ancestors of the iterator they're last passed
836 : : * back.
837 : : *
838 : : * Returns "Jsonb sequential processing" token value. Iterator "state"
839 : : * reflects the current stage of the process in a less granular fashion, and is
840 : : * mostly used here to track things internally with respect to particular
841 : : * iterators.
842 : : *
843 : : * Clients of this function should not have to handle any jbvBinary values
844 : : * (since recursive calls will deal with this), provided skipNested is false.
845 : : * It is our job to expand the jbvBinary representation without bothering them
846 : : * with it. However, clients should not take it upon themselves to touch array
847 : : * or Object element/pair buffers, since their element/pair pointers are
848 : : * garbage. Also, *val will not be set when returning WJB_END_ARRAY or
849 : : * WJB_END_OBJECT, on the assumption that it's only useful to access values
850 : : * when recursing in.
851 : : */
852 : : JsonbIteratorToken
3631 bruce@momjian.us 853 : 1233606 : JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
854 : : {
3675 andrew@dunslane.net 855 [ + + ]: 1233606 : if (*it == NULL)
856 : 63297 : return WJB_DONE;
857 : :
858 : : /*
859 : : * When stepping into a nested container, we jump back here to start
860 : : * processing the child. We will not recurse further in one call, because
861 : : * processing the child will always begin in JBI_ARRAY_START or
862 : : * JBI_OBJECT_START state.
863 : : */
3628 heikki.linnakangas@i 864 : 1173970 : recurse:
865 [ + + + + : 1173970 : switch ((*it)->state)
+ - ]
866 : : {
867 : 15010 : case JBI_ARRAY_START:
868 : : /* Set v to array on first array call */
3675 andrew@dunslane.net 869 : 15010 : val->type = jbvArray;
3665 tgl@sss.pgh.pa.us 870 : 15010 : val->val.array.nElems = (*it)->nElems;
871 : :
872 : : /*
873 : : * v->val.array.elems is not actually set, because we aren't doing
874 : : * a full conversion
875 : : */
876 : 15010 : val->val.array.rawScalar = (*it)->isScalar;
3485 877 : 15010 : (*it)->curIndex = 0;
878 : 15010 : (*it)->curDataOffset = 0;
879 : 15010 : (*it)->curValueOffset = 0; /* not actually used */
880 : : /* Set state for next call */
3628 heikki.linnakangas@i 881 : 15010 : (*it)->state = JBI_ARRAY_ELEM;
3675 andrew@dunslane.net 882 : 15010 : return WJB_BEGIN_ARRAY;
883 : :
3628 heikki.linnakangas@i 884 : 37273 : case JBI_ARRAY_ELEM:
3485 tgl@sss.pgh.pa.us 885 [ + + ]: 37273 : if ((*it)->curIndex >= (*it)->nElems)
886 : : {
887 : : /*
888 : : * All elements within array already processed. Report this
889 : : * to caller, and give it back original parent iterator (which
890 : : * independently tracks iteration progress at its level of
891 : : * nesting).
892 : : */
3675 andrew@dunslane.net 893 : 14031 : *it = freeAndGetParent(*it);
894 : 14031 : return WJB_END_ARRAY;
895 : : }
896 : :
3485 tgl@sss.pgh.pa.us 897 : 23242 : fillJsonbValue((*it)->container, (*it)->curIndex,
898 : 23242 : (*it)->dataProper, (*it)->curDataOffset,
899 : : val);
900 : :
901 [ + + ]: 23242 : JBE_ADVANCE_OFFSET((*it)->curDataOffset,
902 : : (*it)->children[(*it)->curIndex]);
903 : 23242 : (*it)->curIndex++;
904 : :
3628 heikki.linnakangas@i 905 [ + + + - : 23242 : if (!IsAJsonbScalar(val) && !skipNested)
+ + ]
906 : : {
907 : : /* Recurse into container. */
908 : 978 : *it = iteratorFromContainer(val->val.binary.data, *it);
909 : 978 : goto recurse;
910 : : }
911 : : else
912 : : {
913 : : /*
914 : : * Scalar item in array, or a container and caller didn't want
915 : : * us to recurse into it.
916 : : */
3675 andrew@dunslane.net 917 : 22264 : return WJB_ELEM;
918 : : }
919 : :
3628 heikki.linnakangas@i 920 : 342419 : case JBI_OBJECT_START:
921 : : /* Set v to object on first object call */
3675 andrew@dunslane.net 922 : 342419 : val->type = jbvObject;
3665 tgl@sss.pgh.pa.us 923 : 342419 : val->val.object.nPairs = (*it)->nElems;
924 : :
925 : : /*
926 : : * v->val.object.pairs is not actually set, because we aren't
927 : : * doing a full conversion
928 : : */
3485 929 : 342419 : (*it)->curIndex = 0;
930 : 342419 : (*it)->curDataOffset = 0;
931 : 684838 : (*it)->curValueOffset = getJsonbOffset((*it)->container,
932 : 342419 : (*it)->nElems);
933 : : /* Set state for next call */
3628 heikki.linnakangas@i 934 : 342419 : (*it)->state = JBI_OBJECT_KEY;
3675 andrew@dunslane.net 935 : 342419 : return WJB_BEGIN_OBJECT;
936 : :
3628 heikki.linnakangas@i 937 : 456495 : case JBI_OBJECT_KEY:
3485 tgl@sss.pgh.pa.us 938 [ + + ]: 456495 : if ((*it)->curIndex >= (*it)->nElems)
939 : : {
940 : : /*
941 : : * All pairs within object already processed. Report this to
942 : : * caller, and give it back original containing iterator
943 : : * (which independently tracks iteration progress at its level
944 : : * of nesting).
945 : : */
3675 andrew@dunslane.net 946 : 59702 : *it = freeAndGetParent(*it);
947 : 59702 : return WJB_END_OBJECT;
948 : : }
949 : : else
950 : : {
951 : : /* Return key of a key/value pair. */
3485 tgl@sss.pgh.pa.us 952 : 396793 : fillJsonbValue((*it)->container, (*it)->curIndex,
953 : 396793 : (*it)->dataProper, (*it)->curDataOffset,
954 : : val);
3628 heikki.linnakangas@i 955 [ - + ]: 396793 : if (val->type != jbvString)
3628 heikki.linnakangas@i 956 [ # # ]:UBC 0 : elog(ERROR, "unexpected jsonb type as object key");
957 : :
958 : : /* Set state for next call */
3628 heikki.linnakangas@i 959 :CBC 396793 : (*it)->state = JBI_OBJECT_VALUE;
3675 andrew@dunslane.net 960 : 396793 : return WJB_KEY;
961 : : }
962 : :
3628 heikki.linnakangas@i 963 : 322773 : case JBI_OBJECT_VALUE:
964 : : /* Set state for next call */
965 : 322773 : (*it)->state = JBI_OBJECT_KEY;
966 : :
3485 tgl@sss.pgh.pa.us 967 : 322773 : fillJsonbValue((*it)->container, (*it)->curIndex + (*it)->nElems,
968 : 322773 : (*it)->dataProper, (*it)->curValueOffset,
969 : : val);
970 : :
971 [ + + ]: 322773 : JBE_ADVANCE_OFFSET((*it)->curDataOffset,
972 : : (*it)->children[(*it)->curIndex]);
973 [ + + ]: 322773 : JBE_ADVANCE_OFFSET((*it)->curValueOffset,
974 : : (*it)->children[(*it)->curIndex + (*it)->nElems]);
975 : 322773 : (*it)->curIndex++;
976 : :
977 : : /*
978 : : * Value may be a container, in which case we recurse with new,
979 : : * child iterator (unless the caller asked not to, by passing
980 : : * skipNested).
981 : : */
3628 heikki.linnakangas@i 982 [ + + + - : 322773 : if (!IsAJsonbScalar(val) && !skipNested)
+ + ]
983 : : {
984 : 2683 : *it = iteratorFromContainer(val->val.binary.data, *it);
985 : 2683 : goto recurse;
986 : : }
987 : : else
3675 andrew@dunslane.net 988 : 320090 : return WJB_VALUE;
989 : : }
990 : :
3675 andrew@dunslane.net 991 [ # # ]:UBC 0 : elog(ERROR, "invalid iterator state");
992 : : return -1;
993 : : }
994 : :
995 : : /*
996 : : * Initialize an iterator for iterating all elements in a container.
997 : : */
998 : : static JsonbIterator *
3628 heikki.linnakangas@i 999 :CBC 357429 : iteratorFromContainer(JsonbContainer *container, JsonbIterator *parent)
1000 : : {
1001 : : JsonbIterator *it;
1002 : :
2148 peter_e@gmx.net 1003 : 357429 : it = palloc0(sizeof(JsonbIterator));
3628 heikki.linnakangas@i 1004 : 357429 : it->container = container;
1005 : 357429 : it->parent = parent;
2636 tgl@sss.pgh.pa.us 1006 : 357429 : it->nElems = JsonContainerSize(container);
1007 : :
1008 : : /* Array starts just after header */
3628 heikki.linnakangas@i 1009 : 357429 : it->children = container->children;
1010 : :
1011 [ + + - ]: 357429 : switch (container->header & (JB_FARRAY | JB_FOBJECT))
1012 : : {
1013 : 15010 : case JB_FARRAY:
1014 : 15010 : it->dataProper =
1015 : 15010 : (char *) it->children + it->nElems * sizeof(JEntry);
2636 tgl@sss.pgh.pa.us 1016 : 15010 : it->isScalar = JsonContainerIsScalar(container);
1017 : : /* This is either a "raw scalar", or an array */
3628 heikki.linnakangas@i 1018 [ + + - + ]: 15010 : Assert(!it->isScalar || it->nElems == 1);
1019 : :
1020 : 15010 : it->state = JBI_ARRAY_START;
1021 : 15010 : break;
1022 : :
1023 : 342419 : case JB_FOBJECT:
1024 : 342419 : it->dataProper =
1025 : 342419 : (char *) it->children + it->nElems * sizeof(JEntry) * 2;
1026 : 342419 : it->state = JBI_OBJECT_START;
1027 : 342419 : break;
1028 : :
3628 heikki.linnakangas@i 1029 :UBC 0 : default:
1030 [ # # ]: 0 : elog(ERROR, "unknown type of jsonb container");
1031 : : }
1032 : :
3628 heikki.linnakangas@i 1033 :CBC 357429 : return it;
1034 : : }
1035 : :
1036 : : /*
1037 : : * JsonbIteratorNext() worker: Return parent, while freeing memory for current
1038 : : * iterator
1039 : : */
1040 : : static JsonbIterator *
1041 : 73733 : freeAndGetParent(JsonbIterator *it)
1042 : : {
1043 : 73733 : JsonbIterator *v = it->parent;
1044 : :
1045 : 73733 : pfree(it);
1046 : 73733 : return v;
1047 : : }
1048 : :
1049 : : /*
1050 : : * Worker for "contains" operator's function
1051 : : *
1052 : : * Formally speaking, containment is top-down, unordered subtree isomorphism.
1053 : : *
1054 : : * Takes iterators that belong to some container type. These iterators
1055 : : * "belong" to those values in the sense that they've just been initialized in
1056 : : * respect of them by the caller (perhaps in a nested fashion).
1057 : : *
1058 : : * "val" is lhs Jsonb, and mContained is rhs Jsonb when called from top level.
1059 : : * We determine if mContained is contained within val.
1060 : : */
1061 : : bool
3631 bruce@momjian.us 1062 : 21810 : JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
1063 : : {
1064 : : JsonbValue vval,
1065 : : vcontained;
1066 : : JsonbIteratorToken rval,
1067 : : rcont;
1068 : :
1069 : : /*
1070 : : * Guard against stack overflow due to overly complex Jsonb.
1071 : : *
1072 : : * Functions called here independently take this precaution, but that
1073 : : * might not be sufficient since this is also a recursive function.
1074 : : */
3675 andrew@dunslane.net 1075 : 21810 : check_stack_depth();
1076 : :
1077 : 21810 : rval = JsonbIteratorNext(val, &vval, false);
1078 : 21810 : rcont = JsonbIteratorNext(mContained, &vcontained, false);
1079 : :
1080 [ + + ]: 21810 : if (rval != rcont)
1081 : : {
1082 : : /*
1083 : : * The differing return values can immediately be taken as indicating
1084 : : * two differing container types at this nesting level, which is
1085 : : * sufficient reason to give up entirely (but it should be the case
1086 : : * that they're both some container type).
1087 : : */
1088 [ - + - - ]: 6 : Assert(rval == WJB_BEGIN_OBJECT || rval == WJB_BEGIN_ARRAY);
1089 [ + - - + ]: 6 : Assert(rcont == WJB_BEGIN_OBJECT || rcont == WJB_BEGIN_ARRAY);
1090 : 6 : return false;
1091 : : }
1092 [ + + ]: 21804 : else if (rcont == WJB_BEGIN_OBJECT)
1093 : : {
3473 tgl@sss.pgh.pa.us 1094 [ - + ]: 21624 : Assert(vval.type == jbvObject);
3675 andrew@dunslane.net 1095 [ - + ]: 21624 : Assert(vcontained.type == jbvObject);
1096 : :
1097 : : /*
1098 : : * If the lhs has fewer pairs than the rhs, it can't possibly contain
1099 : : * the rhs. (This conclusion is safe only because we de-duplicate
1100 : : * keys in all Jsonb objects; thus there can be no corresponding
1101 : : * optimization in the array case.) The case probably won't arise
1102 : : * often, but since it's such a cheap check we may as well make it.
1103 : : */
3473 tgl@sss.pgh.pa.us 1104 [ + + ]: 21624 : if (vval.val.object.nPairs < vcontained.val.object.nPairs)
1105 : 1800 : return false;
1106 : :
1107 : : /* Work through rhs "is it contained within?" object */
1108 : : for (;;)
3675 andrew@dunslane.net 1109 : 405 : {
1110 : : JsonbValue *lhsVal; /* lhsVal is from pair in lhs object */
1111 : : JsonbValue lhsValBuf;
1112 : :
1113 : 20229 : rcont = JsonbIteratorNext(mContained, &vcontained, false);
1114 : :
1115 : : /*
1116 : : * When we get through caller's rhs "is it contained within?"
1117 : : * object without failing to find one of its values, it's
1118 : : * contained.
1119 : : */
1120 [ + + ]: 20229 : if (rcont == WJB_END_OBJECT)
1121 : 19824 : return true;
1122 : :
1123 [ - + ]: 13848 : Assert(rcont == WJB_KEY);
1668 alvherre@alvh.no-ip. 1124 [ - + ]: 13848 : Assert(vcontained.type == jbvString);
1125 : :
1126 : : /* First, find value by key... */
1127 : : lhsVal =
1128 : 13848 : getKeyJsonValueFromContainer((*val)->container,
1129 : 13848 : vcontained.val.string.val,
1130 : : vcontained.val.string.len,
1131 : : &lhsValBuf);
3675 andrew@dunslane.net 1132 [ + + ]: 13848 : if (!lhsVal)
1133 : 11718 : return false;
1134 : :
1135 : : /*
1136 : : * ...at this stage it is apparent that there is at least a key
1137 : : * match for this rhs pair.
1138 : : */
1139 : 2130 : rcont = JsonbIteratorNext(mContained, &vcontained, true);
1140 : :
1141 [ - + ]: 2130 : Assert(rcont == WJB_VALUE);
1142 : :
1143 : : /*
1144 : : * Compare rhs pair's value with lhs pair's value just found using
1145 : : * key
1146 : : */
1147 [ + + ]: 2130 : if (lhsVal->type != vcontained.type)
1148 : : {
1149 : 585 : return false;
1150 : : }
1151 [ + + - + ]: 1545 : else if (IsAJsonbScalar(lhsVal))
1152 : : {
3609 heikki.linnakangas@i 1153 [ + + ]: 1479 : if (!equalsJsonbScalarValue(lhsVal, &vcontained))
3675 andrew@dunslane.net 1154 : 1122 : return false;
1155 : : }
1156 : : else
1157 : : {
1158 : : /* Nested container value (object or array) */
1159 : : JsonbIterator *nestval,
1160 : : *nestContained;
1161 : :
1162 [ - + ]: 66 : Assert(lhsVal->type == jbvBinary);
1163 [ - + ]: 66 : Assert(vcontained.type == jbvBinary);
1164 : :
3665 tgl@sss.pgh.pa.us 1165 : 66 : nestval = JsonbIteratorInit(lhsVal->val.binary.data);
1166 : 66 : nestContained = JsonbIteratorInit(vcontained.val.binary.data);
1167 : :
1168 : : /*
1169 : : * Match "value" side of rhs datum object's pair recursively.
1170 : : * It's a nested structure.
1171 : : *
1172 : : * Note that nesting still has to "match up" at the right
1173 : : * nesting sub-levels. However, there need only be zero or
1174 : : * more matching pairs (or elements) at each nesting level
1175 : : * (provided the *rhs* pairs/elements *all* match on each
1176 : : * level), which enables searching nested structures for a
1177 : : * single String or other primitive type sub-datum quite
1178 : : * effectively (provided the user constructed the rhs nested
1179 : : * structure such that we "know where to look").
1180 : : *
1181 : : * In other words, the mapping of container nodes in the rhs
1182 : : * "vcontained" Jsonb to internal nodes on the lhs is
1183 : : * injective, and parent-child edges on the rhs must be mapped
1184 : : * to parent-child edges on the lhs to satisfy the condition
1185 : : * of containment (plus of course the mapped nodes must be
1186 : : * equal).
1187 : : */
3675 andrew@dunslane.net 1188 [ + + ]: 66 : if (!JsonbDeepContains(&nestval, &nestContained))
1189 : 18 : return false;
1190 : : }
1191 : : }
1192 : : }
1193 [ + - ]: 180 : else if (rcont == WJB_BEGIN_ARRAY)
1194 : : {
1195 : 180 : JsonbValue *lhsConts = NULL;
3665 tgl@sss.pgh.pa.us 1196 : 180 : uint32 nLhsElems = vval.val.array.nElems;
1197 : :
3473 1198 [ - + ]: 180 : Assert(vval.type == jbvArray);
3675 andrew@dunslane.net 1199 [ - + ]: 180 : Assert(vcontained.type == jbvArray);
1200 : :
1201 : : /*
1202 : : * Handle distinction between "raw scalar" pseudo arrays, and real
1203 : : * arrays.
1204 : : *
1205 : : * A raw scalar may contain another raw scalar, and an array may
1206 : : * contain a raw scalar, but a raw scalar may not contain an array. We
1207 : : * don't do something like this for the object case, since objects can
1208 : : * only contain pairs, never raw scalars (a pair is represented by an
1209 : : * rhs object argument with a single contained pair).
1210 : : */
3665 tgl@sss.pgh.pa.us 1211 [ + + + + ]: 180 : if (vval.val.array.rawScalar && !vcontained.val.array.rawScalar)
3675 andrew@dunslane.net 1212 : 3 : return false;
1213 : :
1214 : : /* Work through rhs "is it contained within?" array */
1215 : : for (;;)
1216 : : {
1217 : 408 : rcont = JsonbIteratorNext(mContained, &vcontained, true);
1218 : :
1219 : : /*
1220 : : * When we get through caller's rhs "is it contained within?"
1221 : : * array without failing to find one of its values, it's
1222 : : * contained.
1223 : : */
1224 [ + + ]: 408 : if (rcont == WJB_END_ARRAY)
1225 : 144 : return true;
1226 : :
1227 [ - + ]: 264 : Assert(rcont == WJB_ELEM);
1228 : :
1229 [ + + - + ]: 264 : if (IsAJsonbScalar(&vcontained))
1230 : : {
3628 heikki.linnakangas@i 1231 [ + + ]: 201 : if (!findJsonbValueFromContainer((*val)->container,
1232 : : JB_FARRAY,
1233 : : &vcontained))
3675 andrew@dunslane.net 1234 : 27 : return false;
1235 : : }
1236 : : else
1237 : : {
1238 : : uint32 i;
1239 : :
1240 : : /*
1241 : : * If this is first container found in rhs array (at this
1242 : : * depth), initialize temp lhs array of containers
1243 : : */
1244 [ + + ]: 63 : if (lhsConts == NULL)
1245 : : {
1246 : 60 : uint32 j = 0;
1247 : :
1248 : : /* Make room for all possible values */
1249 : 60 : lhsConts = palloc(sizeof(JsonbValue) * nLhsElems);
1250 : :
1251 [ + + ]: 198 : for (i = 0; i < nLhsElems; i++)
1252 : : {
1253 : : /* Store all lhs elements in temp array */
1254 : 138 : rcont = JsonbIteratorNext(val, &vval, true);
1255 [ - + ]: 138 : Assert(rcont == WJB_ELEM);
1256 : :
1257 [ + + ]: 138 : if (vval.type == jbvBinary)
1258 : 69 : lhsConts[j++] = vval;
1259 : : }
1260 : :
1261 : : /* No container elements in temp array, so give up now */
1262 [ - + ]: 60 : if (j == 0)
3675 andrew@dunslane.net 1263 :UBC 0 : return false;
1264 : :
1265 : : /* We may have only partially filled array */
3675 andrew@dunslane.net 1266 :CBC 60 : nLhsElems = j;
1267 : : }
1268 : :
1269 : : /* XXX: Nested array containment is O(N^2) */
1270 [ + + ]: 78 : for (i = 0; i < nLhsElems; i++)
1271 : : {
1272 : : /* Nested container value (object or array) */
1273 : : JsonbIterator *nestval,
1274 : : *nestContained;
1275 : : bool contains;
1276 : :
3665 tgl@sss.pgh.pa.us 1277 : 72 : nestval = JsonbIteratorInit(lhsConts[i].val.binary.data);
1278 : 72 : nestContained = JsonbIteratorInit(vcontained.val.binary.data);
1279 : :
3675 andrew@dunslane.net 1280 : 72 : contains = JsonbDeepContains(&nestval, &nestContained);
1281 : :
1282 [ + - ]: 72 : if (nestval)
1283 : 72 : pfree(nestval);
1284 [ + + ]: 72 : if (nestContained)
1285 : 15 : pfree(nestContained);
1286 [ + + ]: 72 : if (contains)
1287 : 57 : break;
1288 : : }
1289 : :
1290 : : /*
1291 : : * Report rhs container value is not contained if couldn't
1292 : : * match rhs container to *some* lhs cont
1293 : : */
1294 [ + + ]: 63 : if (i == nLhsElems)
1295 : 6 : return false;
1296 : : }
1297 : : }
1298 : : }
1299 : : else
1300 : : {
3675 andrew@dunslane.net 1301 [ # # ]:UBC 0 : elog(ERROR, "invalid jsonb container type");
1302 : : }
1303 : :
1304 : : elog(ERROR, "unexpectedly fell off end of jsonb container");
1305 : : return false;
1306 : : }
1307 : :
1308 : : /*
1309 : : * Hash a JsonbValue scalar value, mixing the hash value into an existing
1310 : : * hash provided by the caller.
1311 : : *
1312 : : * Some callers may wish to independently XOR in JB_FOBJECT and JB_FARRAY
1313 : : * flags.
1314 : : */
1315 : : void
3631 bruce@momjian.us 1316 :CBC 86883 : JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash)
1317 : : {
1318 : : uint32 tmp;
1319 : :
1320 : : /* Compute hash value for scalarVal */
3675 andrew@dunslane.net 1321 [ + + + + : 86883 : switch (scalarVal->type)
- ]
1322 : : {
1323 : 48 : case jbvNull:
3631 tgl@sss.pgh.pa.us 1324 : 48 : tmp = 0x01;
1325 : 48 : break;
3675 andrew@dunslane.net 1326 : 63600 : case jbvString:
3631 tgl@sss.pgh.pa.us 1327 : 63600 : tmp = DatumGetUInt32(hash_any((const unsigned char *) scalarVal->val.string.val,
1328 : 63600 : scalarVal->val.string.len));
1329 : 63600 : break;
3675 andrew@dunslane.net 1330 : 14913 : case jbvNumeric:
1331 : : /* Must hash equal numerics to equal hash codes */
3631 tgl@sss.pgh.pa.us 1332 : 14913 : tmp = DatumGetUInt32(DirectFunctionCall1(hash_numeric,
1333 : : NumericGetDatum(scalarVal->val.numeric)));
1334 : 14913 : break;
3675 andrew@dunslane.net 1335 : 8322 : case jbvBool:
3631 tgl@sss.pgh.pa.us 1336 [ + + ]: 8322 : tmp = scalarVal->val.boolean ? 0x02 : 0x04;
1337 : :
1338 : 8322 : break;
3675 andrew@dunslane.net 1339 :UBC 0 : default:
1340 [ # # ]: 0 : elog(ERROR, "invalid jsonb scalar type");
1341 : : tmp = 0; /* keep compiler quiet */
1342 : : break;
1343 : : }
1344 : :
1345 : : /*
1346 : : * Combine hash values of successive keys, values and elements by rotating
1347 : : * the previous value left 1 bit, then XOR'ing in the new
1348 : : * key/value/element's hash value.
1349 : : */
784 john.naylor@postgres 1350 :CBC 86883 : *hash = pg_rotate_left32(*hash, 1);
3631 tgl@sss.pgh.pa.us 1351 : 86883 : *hash ^= tmp;
3675 andrew@dunslane.net 1352 : 86883 : }
1353 : :
1354 : : /*
1355 : : * Hash a value to a 64-bit value, with a seed. Otherwise, similar to
1356 : : * JsonbHashScalarValue.
1357 : : */
1358 : : void
2418 rhaas@postgresql.org 1359 : 108 : JsonbHashScalarValueExtended(const JsonbValue *scalarVal, uint64 *hash,
1360 : : uint64 seed)
1361 : : {
1362 : : uint64 tmp;
1363 : :
1364 [ + + + + : 108 : switch (scalarVal->type)
- ]
1365 : : {
1366 : 6 : case jbvNull:
1367 : 6 : tmp = seed + 0x01;
1368 : 6 : break;
1369 : 90 : case jbvString:
1370 : 90 : tmp = DatumGetUInt64(hash_any_extended((const unsigned char *) scalarVal->val.string.val,
1371 : 90 : scalarVal->val.string.len,
1372 : : seed));
1373 : 90 : break;
1374 : 6 : case jbvNumeric:
1375 : 6 : tmp = DatumGetUInt64(DirectFunctionCall2(hash_numeric_extended,
1376 : : NumericGetDatum(scalarVal->val.numeric),
1377 : : UInt64GetDatum(seed)));
1378 : 6 : break;
1379 : 6 : case jbvBool:
1380 [ + + ]: 6 : if (seed)
1381 : 3 : tmp = DatumGetUInt64(DirectFunctionCall2(hashcharextended,
1382 : : BoolGetDatum(scalarVal->val.boolean),
1383 : : UInt64GetDatum(seed)));
1384 : : else
1385 [ + - ]: 3 : tmp = scalarVal->val.boolean ? 0x02 : 0x04;
1386 : :
1387 : 6 : break;
2418 rhaas@postgresql.org 1388 :UBC 0 : default:
1389 [ # # ]: 0 : elog(ERROR, "invalid jsonb scalar type");
1390 : : break;
1391 : : }
1392 : :
2418 rhaas@postgresql.org 1393 :CBC 108 : *hash = ROTATE_HIGH_AND_LOW_32BITS(*hash);
1394 : 108 : *hash ^= tmp;
1395 : 108 : }
1396 : :
1397 : : /*
1398 : : * Are two scalar JsonbValues of the same type a and b equal?
1399 : : */
1400 : : static bool
572 pg@bowt.ie 1401 : 1833 : equalsJsonbScalarValue(JsonbValue *a, JsonbValue *b)
1402 : : {
1403 [ + - ]: 1833 : if (a->type == b->type)
1404 : : {
1405 [ + + + + : 1833 : switch (a->type)
- ]
1406 : : {
3675 andrew@dunslane.net 1407 : 21 : case jbvNull:
3609 heikki.linnakangas@i 1408 : 21 : return true;
3675 andrew@dunslane.net 1409 : 1524 : case jbvString:
572 pg@bowt.ie 1410 : 1524 : return lengthCompareJsonbStringValue(a, b) == 0;
3675 andrew@dunslane.net 1411 : 261 : case jbvNumeric:
3609 heikki.linnakangas@i 1412 : 261 : return DatumGetBool(DirectFunctionCall2(numeric_eq,
1413 : : PointerGetDatum(a->val.numeric),
1414 : : PointerGetDatum(b->val.numeric)));
3675 andrew@dunslane.net 1415 : 27 : case jbvBool:
572 pg@bowt.ie 1416 : 27 : return a->val.boolean == b->val.boolean;
1417 : :
3675 andrew@dunslane.net 1418 :UBC 0 : default:
1419 [ # # ]: 0 : elog(ERROR, "invalid jsonb scalar type");
1420 : : }
1421 : : }
1422 [ # # ]: 0 : elog(ERROR, "jsonb scalar type mismatch");
1423 : : return false;
1424 : : }
1425 : :
1426 : : /*
1427 : : * Compare two scalar JsonbValues, returning -1, 0, or 1.
1428 : : *
1429 : : * Strings are compared using the default collation. Used by B-tree
1430 : : * operators, where a lexical sort order is generally expected.
1431 : : */
1432 : : static int
572 pg@bowt.ie 1433 :CBC 234688 : compareJsonbScalarValue(JsonbValue *a, JsonbValue *b)
1434 : : {
1435 [ + - ]: 234688 : if (a->type == b->type)
1436 : : {
1437 [ + + + + : 234688 : switch (a->type)
- ]
1438 : : {
3609 heikki.linnakangas@i 1439 : 11 : case jbvNull:
1440 : 11 : return 0;
1441 : 159859 : case jbvString:
572 pg@bowt.ie 1442 : 159859 : return varstr_cmp(a->val.string.val,
1443 : : a->val.string.len,
1444 : 159859 : b->val.string.val,
1445 : : b->val.string.len,
1446 : : DEFAULT_COLLATION_OID);
3609 heikki.linnakangas@i 1447 : 55515 : case jbvNumeric:
1448 : 55515 : return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
1449 : : PointerGetDatum(a->val.numeric),
1450 : : PointerGetDatum(b->val.numeric)));
1451 : 19303 : case jbvBool:
572 pg@bowt.ie 1452 [ + + ]: 19303 : if (a->val.boolean == b->val.boolean)
3609 heikki.linnakangas@i 1453 : 16835 : return 0;
572 pg@bowt.ie 1454 [ + + ]: 2468 : else if (a->val.boolean > b->val.boolean)
3609 heikki.linnakangas@i 1455 : 1316 : return 1;
1456 : : else
1457 : 1152 : return -1;
3609 heikki.linnakangas@i 1458 :UBC 0 : default:
1459 [ # # ]: 0 : elog(ERROR, "invalid jsonb scalar type");
1460 : : }
1461 : : }
1462 [ # # ]: 0 : elog(ERROR, "jsonb scalar type mismatch");
1463 : : return -1;
1464 : : }
1465 : :
1466 : :
1467 : : /*
1468 : : * Functions for manipulating the resizable buffer used by convertJsonb and
1469 : : * its subroutines.
1470 : : */
1471 : :
1472 : : /*
1473 : : * Reserve 'len' bytes, at the end of the buffer, enlarging it if necessary.
1474 : : * Returns the offset to the reserved area. The caller is expected to fill
1475 : : * the reserved area later with copyToBuffer().
1476 : : */
1477 : : static int
3628 tgl@sss.pgh.pa.us 1478 :CBC 323656 : reserveFromBuffer(StringInfo buffer, int len)
1479 : : {
1480 : : int offset;
1481 : :
1482 : : /* Make more room if needed */
1483 : 323656 : enlargeStringInfo(buffer, len);
1484 : :
1485 : : /* remember current offset */
3630 heikki.linnakangas@i 1486 : 323656 : offset = buffer->len;
1487 : :
1488 : : /* reserve the space */
1489 : 323656 : buffer->len += len;
1490 : :
1491 : : /*
1492 : : * Keep a trailing null in place, even though it's not useful for us; it
1493 : : * seems best to preserve the invariants of StringInfos.
1494 : : */
3628 tgl@sss.pgh.pa.us 1495 : 323656 : buffer->data[buffer->len] = '\0';
1496 : :
3630 heikki.linnakangas@i 1497 : 323656 : return offset;
1498 : : }
1499 : :
1500 : : /*
1501 : : * Copy 'len' bytes to a previously reserved area in buffer.
1502 : : */
1503 : : static void
3628 tgl@sss.pgh.pa.us 1504 : 252167 : copyToBuffer(StringInfo buffer, int offset, const char *data, int len)
1505 : : {
1506 : 252167 : memcpy(buffer->data + offset, data, len);
3630 heikki.linnakangas@i 1507 : 252167 : }
1508 : :
1509 : : /*
1510 : : * A shorthand for reserveFromBuffer + copyToBuffer.
1511 : : */
1512 : : static void
3628 tgl@sss.pgh.pa.us 1513 : 142637 : appendToBuffer(StringInfo buffer, const char *data, int len)
1514 : : {
1515 : : int offset;
1516 : :
3630 heikki.linnakangas@i 1517 : 142637 : offset = reserveFromBuffer(buffer, len);
1518 : 142637 : copyToBuffer(buffer, offset, data, len);
1519 : 142637 : }
1520 : :
1521 : :
1522 : : /*
1523 : : * Append padding, so that the length of the StringInfo is int-aligned.
1524 : : * Returns the number of padding bytes appended.
1525 : : */
1526 : : static short
3628 tgl@sss.pgh.pa.us 1527 : 81464 : padBufferToInt(StringInfo buffer)
1528 : : {
1529 : : int padlen,
1530 : : p,
1531 : : offset;
1532 : :
3630 heikki.linnakangas@i 1533 : 81464 : padlen = INTALIGN(buffer->len) - buffer->len;
1534 : :
1535 : 81464 : offset = reserveFromBuffer(buffer, padlen);
1536 : :
1537 : : /* padlen must be small, so this is probably faster than a memset */
1538 [ + + ]: 104365 : for (p = 0; p < padlen; p++)
3628 tgl@sss.pgh.pa.us 1539 : 22901 : buffer->data[offset + p] = '\0';
1540 : :
3630 heikki.linnakangas@i 1541 : 81464 : return padlen;
1542 : : }
1543 : :
1544 : : /*
1545 : : * Given a JsonbValue, convert to Jsonb. The result is palloc'd.
1546 : : */
1547 : : static Jsonb *
1548 : 46762 : convertToJsonb(JsonbValue *val)
1549 : : {
1550 : : StringInfoData buffer;
1551 : : JEntry jentry;
1552 : : Jsonb *res;
1553 : :
1554 : : /* Should not already have binary representation */
1555 [ - + ]: 46762 : Assert(val->type != jbvBinary);
1556 : :
1557 : : /* Allocate an output buffer. It will be enlarged as needed */
3628 tgl@sss.pgh.pa.us 1558 : 46762 : initStringInfo(&buffer);
1559 : :
1560 : : /* Make room for the varlena header */
3413 1561 : 46762 : reserveFromBuffer(&buffer, VARHDRSZ);
1562 : :
3630 heikki.linnakangas@i 1563 : 46762 : convertJsonbValue(&buffer, &jentry, val, 0);
1564 : :
1565 : : /*
1566 : : * Note: the JEntry of the root is discarded. Therefore the root
1567 : : * JsonbContainer struct must contain enough information to tell what kind
1568 : : * of value it is.
1569 : : */
1570 : :
3628 tgl@sss.pgh.pa.us 1571 : 46762 : res = (Jsonb *) buffer.data;
1572 : :
3630 heikki.linnakangas@i 1573 : 46762 : SET_VARSIZE(res, buffer.len);
1574 : :
1575 : 46762 : return res;
1576 : : }
1577 : :
1578 : : /*
1579 : : * Subroutine of convertJsonb: serialize a single JsonbValue into buffer.
1580 : : *
1581 : : * The JEntry header for this node is returned in *header. It is filled in
1582 : : * with the length of this value and appropriate type bits. If we wish to
1583 : : * store an end offset rather than a length, it is the caller's responsibility
1584 : : * to adjust for that.
1585 : : *
1586 : : * If the value is an array or an object, this recurses. 'level' is only used
1587 : : * for debugging purposes.
1588 : : */
1589 : : static void
3628 tgl@sss.pgh.pa.us 1590 : 126473 : convertJsonbValue(StringInfo buffer, JEntry *header, JsonbValue *val, int level)
1591 : : {
3630 heikki.linnakangas@i 1592 : 126473 : check_stack_depth();
1593 : :
1594 [ - + ]: 126473 : if (!val)
3630 heikki.linnakangas@i 1595 :UBC 0 : return;
1596 : :
1597 : : /*
1598 : : * A JsonbValue passed as val should never have a type of jbvBinary, and
1599 : : * neither should any of its sub-components. Those values will be produced
1600 : : * by convertJsonbArray and convertJsonbObject, the results of which will
1601 : : * not be passed back to this function as an argument.
1602 : : */
1603 : :
3588 andrew@dunslane.net 1604 [ + + + + ]:CBC 126473 : if (IsAJsonbScalar(val))
3630 heikki.linnakangas@i 1605 : 73680 : convertJsonbScalar(buffer, header, val);
1606 [ + + ]: 52793 : else if (val->type == jbvArray)
1607 : 42461 : convertJsonbArray(buffer, header, val, level);
1608 [ + - ]: 10332 : else if (val->type == jbvObject)
1609 : 10332 : convertJsonbObject(buffer, header, val, level);
1610 : : else
3411 andrew@dunslane.net 1611 [ # # ]:UBC 0 : elog(ERROR, "unknown type of jsonb container to convert");
1612 : : }
1613 : :
1614 : : static void
572 pg@bowt.ie 1615 :CBC 42461 : convertJsonbArray(StringInfo buffer, JEntry *header, JsonbValue *val, int level)
1616 : : {
1617 : : int base_offset;
1618 : : int jentry_offset;
1619 : : int i;
1620 : : int totallen;
1621 : : uint32 containerhead;
3485 tgl@sss.pgh.pa.us 1622 : 42461 : int nElems = val->val.array.nElems;
1623 : :
1624 : : /* Remember where in the buffer this array starts. */
1625 : 42461 : base_offset = buffer->len;
1626 : :
1627 : : /* Align to 4-byte boundary (any padding counts as part of my data) */
3630 heikki.linnakangas@i 1628 : 42461 : padBufferToInt(buffer);
1629 : :
1630 : : /*
1631 : : * Construct the header Jentry and store it in the beginning of the
1632 : : * variable-length payload.
1633 : : */
572 pg@bowt.ie 1634 : 42461 : containerhead = nElems | JB_FARRAY;
3630 heikki.linnakangas@i 1635 [ + + ]: 42461 : if (val->val.array.rawScalar)
1636 : : {
3485 tgl@sss.pgh.pa.us 1637 [ - + ]: 37081 : Assert(nElems == 1);
3630 heikki.linnakangas@i 1638 [ - + ]: 37081 : Assert(level == 0);
572 pg@bowt.ie 1639 : 37081 : containerhead |= JB_FSCALAR;
1640 : : }
1641 : :
1642 : 42461 : appendToBuffer(buffer, (char *) &containerhead, sizeof(uint32));
1643 : :
1644 : : /* Reserve space for the JEntries of the elements. */
3485 tgl@sss.pgh.pa.us 1645 : 42461 : jentry_offset = reserveFromBuffer(buffer, sizeof(JEntry) * nElems);
1646 : :
3630 heikki.linnakangas@i 1647 : 42461 : totallen = 0;
3485 tgl@sss.pgh.pa.us 1648 [ + + ]: 92353 : for (i = 0; i < nElems; i++)
1649 : : {
3630 heikki.linnakangas@i 1650 : 49892 : JsonbValue *elem = &val->val.array.elems[i];
1651 : : int len;
1652 : : JEntry meta;
1653 : :
1654 : : /*
1655 : : * Convert element, producing a JEntry and appending its
1656 : : * variable-length data to buffer
1657 : : */
1658 : 49892 : convertJsonbValue(buffer, &meta, elem, level + 1);
1659 : :
3485 tgl@sss.pgh.pa.us 1660 : 49892 : len = JBE_OFFLENFLD(meta);
3630 heikki.linnakangas@i 1661 : 49892 : totallen += len;
1662 : :
1663 : : /*
1664 : : * Bail out if total variable-length data exceeds what will fit in a
1665 : : * JEntry length field. We check this in each iteration, not just
1666 : : * once at the end, to forestall possible integer overflow.
1667 : : */
3485 tgl@sss.pgh.pa.us 1668 [ - + ]: 49892 : if (totallen > JENTRY_OFFLENMASK)
3630 heikki.linnakangas@i 1669 [ # # ]:UBC 0 : ereport(ERROR,
1670 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1671 : : errmsg("total size of jsonb array elements exceeds the maximum of %d bytes",
1672 : : JENTRY_OFFLENMASK)));
1673 : :
1674 : : /*
1675 : : * Convert each JB_OFFSET_STRIDE'th length to an offset.
1676 : : */
3485 tgl@sss.pgh.pa.us 1677 [ + + ]:CBC 49892 : if ((i % JB_OFFSET_STRIDE) == 0)
1678 : 41852 : meta = (meta & JENTRY_TYPEMASK) | totallen | JENTRY_HAS_OFF;
1679 : :
1680 : 49892 : copyToBuffer(buffer, jentry_offset, (char *) &meta, sizeof(JEntry));
1681 : 49892 : jentry_offset += sizeof(JEntry);
1682 : : }
1683 : :
1684 : : /* Total data size is everything we've appended to buffer */
1685 : 42461 : totallen = buffer->len - base_offset;
1686 : :
1687 : : /* Check length again, since we didn't include the metadata above */
1688 [ - + ]: 42461 : if (totallen > JENTRY_OFFLENMASK)
3485 tgl@sss.pgh.pa.us 1689 [ # # ]:UBC 0 : ereport(ERROR,
1690 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1691 : : errmsg("total size of jsonb array elements exceeds the maximum of %d bytes",
1692 : : JENTRY_OFFLENMASK)));
1693 : :
1694 : : /* Initialize the header of this node in the container's JEntry array */
572 pg@bowt.ie 1695 :CBC 42461 : *header = JENTRY_ISCONTAINER | totallen;
3630 heikki.linnakangas@i 1696 : 42461 : }
1697 : :
1698 : : static void
572 pg@bowt.ie 1699 : 10332 : convertJsonbObject(StringInfo buffer, JEntry *header, JsonbValue *val, int level)
1700 : : {
1701 : : int base_offset;
1702 : : int jentry_offset;
1703 : : int i;
1704 : : int totallen;
1705 : : uint32 containerheader;
3485 tgl@sss.pgh.pa.us 1706 : 10332 : int nPairs = val->val.object.nPairs;
1707 : :
1708 : : /* Remember where in the buffer this object starts. */
1709 : 10332 : base_offset = buffer->len;
1710 : :
1711 : : /* Align to 4-byte boundary (any padding counts as part of my data) */
3630 heikki.linnakangas@i 1712 : 10332 : padBufferToInt(buffer);
1713 : :
1714 : : /*
1715 : : * Construct the header Jentry and store it in the beginning of the
1716 : : * variable-length payload.
1717 : : */
572 pg@bowt.ie 1718 : 10332 : containerheader = nPairs | JB_FOBJECT;
1719 : 10332 : appendToBuffer(buffer, (char *) &containerheader, sizeof(uint32));
1720 : :
1721 : : /* Reserve space for the JEntries of the keys and values. */
3485 tgl@sss.pgh.pa.us 1722 : 10332 : jentry_offset = reserveFromBuffer(buffer, sizeof(JEntry) * nPairs * 2);
1723 : :
1724 : : /*
1725 : : * Iterate over the keys, then over the values, since that is the ordering
1726 : : * we want in the on-disk representation.
1727 : : */
3630 heikki.linnakangas@i 1728 : 10332 : totallen = 0;
3485 tgl@sss.pgh.pa.us 1729 [ + + ]: 40151 : for (i = 0; i < nPairs; i++)
1730 : : {
1731 : 29819 : JsonbPair *pair = &val->val.object.pairs[i];
1732 : : int len;
1733 : : JEntry meta;
1734 : :
1735 : : /*
1736 : : * Convert key, producing a JEntry and appending its variable-length
1737 : : * data to buffer
1738 : : */
3630 heikki.linnakangas@i 1739 : 29819 : convertJsonbScalar(buffer, &meta, &pair->key);
1740 : :
3485 tgl@sss.pgh.pa.us 1741 : 29819 : len = JBE_OFFLENFLD(meta);
3630 heikki.linnakangas@i 1742 : 29819 : totallen += len;
1743 : :
1744 : : /*
1745 : : * Bail out if total variable-length data exceeds what will fit in a
1746 : : * JEntry length field. We check this in each iteration, not just
1747 : : * once at the end, to forestall possible integer overflow.
1748 : : */
3485 tgl@sss.pgh.pa.us 1749 [ - + ]: 29819 : if (totallen > JENTRY_OFFLENMASK)
3630 heikki.linnakangas@i 1750 [ # # ]:UBC 0 : ereport(ERROR,
1751 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1752 : : errmsg("total size of jsonb object elements exceeds the maximum of %d bytes",
1753 : : JENTRY_OFFLENMASK)));
1754 : :
1755 : : /*
1756 : : * Convert each JB_OFFSET_STRIDE'th length to an offset.
1757 : : */
3485 tgl@sss.pgh.pa.us 1758 [ + + ]:CBC 29819 : if ((i % JB_OFFSET_STRIDE) == 0)
1759 : 9165 : meta = (meta & JENTRY_TYPEMASK) | totallen | JENTRY_HAS_OFF;
1760 : :
1761 : 29819 : copyToBuffer(buffer, jentry_offset, (char *) &meta, sizeof(JEntry));
1762 : 29819 : jentry_offset += sizeof(JEntry);
1763 : : }
1764 [ + + ]: 40151 : for (i = 0; i < nPairs; i++)
1765 : : {
1766 : 29819 : JsonbPair *pair = &val->val.object.pairs[i];
1767 : : int len;
1768 : : JEntry meta;
1769 : :
1770 : : /*
1771 : : * Convert value, producing a JEntry and appending its variable-length
1772 : : * data to buffer
1773 : : */
1774 : 29819 : convertJsonbValue(buffer, &meta, &pair->value, level + 1);
1775 : :
1776 : 29819 : len = JBE_OFFLENFLD(meta);
3630 heikki.linnakangas@i 1777 : 29819 : totallen += len;
1778 : :
1779 : : /*
1780 : : * Bail out if total variable-length data exceeds what will fit in a
1781 : : * JEntry length field. We check this in each iteration, not just
1782 : : * once at the end, to forestall possible integer overflow.
1783 : : */
3485 tgl@sss.pgh.pa.us 1784 [ - + ]: 29819 : if (totallen > JENTRY_OFFLENMASK)
3630 heikki.linnakangas@i 1785 [ # # ]:UBC 0 : ereport(ERROR,
1786 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1787 : : errmsg("total size of jsonb object elements exceeds the maximum of %d bytes",
1788 : : JENTRY_OFFLENMASK)));
1789 : :
1790 : : /*
1791 : : * Convert each JB_OFFSET_STRIDE'th length to an offset.
1792 : : */
3485 tgl@sss.pgh.pa.us 1793 [ + + ]:CBC 29819 : if (((i + nPairs) % JB_OFFSET_STRIDE) == 0)
1794 : 69 : meta = (meta & JENTRY_TYPEMASK) | totallen | JENTRY_HAS_OFF;
1795 : :
1796 : 29819 : copyToBuffer(buffer, jentry_offset, (char *) &meta, sizeof(JEntry));
1797 : 29819 : jentry_offset += sizeof(JEntry);
1798 : : }
1799 : :
1800 : : /* Total data size is everything we've appended to buffer */
1801 : 10332 : totallen = buffer->len - base_offset;
1802 : :
1803 : : /* Check length again, since we didn't include the metadata above */
1804 [ - + ]: 10332 : if (totallen > JENTRY_OFFLENMASK)
3485 tgl@sss.pgh.pa.us 1805 [ # # ]:UBC 0 : ereport(ERROR,
1806 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1807 : : errmsg("total size of jsonb object elements exceeds the maximum of %d bytes",
1808 : : JENTRY_OFFLENMASK)));
1809 : :
1810 : : /* Initialize the header of this node in the container's JEntry array */
572 pg@bowt.ie 1811 :CBC 10332 : *header = JENTRY_ISCONTAINER | totallen;
3675 andrew@dunslane.net 1812 : 10332 : }
1813 : :
1814 : : static void
572 pg@bowt.ie 1815 : 103499 : convertJsonbScalar(StringInfo buffer, JEntry *header, JsonbValue *scalarVal)
1816 : : {
1817 : : int numlen;
1818 : : short padlen;
1819 : :
3675 andrew@dunslane.net 1820 [ + + + + : 103499 : switch (scalarVal->type)
+ - ]
1821 : : {
1822 : 1724 : case jbvNull:
572 pg@bowt.ie 1823 : 1724 : *header = JENTRY_ISNULL;
3675 andrew@dunslane.net 1824 : 1724 : break;
1825 : :
1826 : 60405 : case jbvString:
3630 heikki.linnakangas@i 1827 : 60405 : appendToBuffer(buffer, scalarVal->val.string.val, scalarVal->val.string.len);
1828 : :
572 pg@bowt.ie 1829 : 60405 : *header = scalarVal->val.string.len;
3675 andrew@dunslane.net 1830 : 60405 : break;
1831 : :
1832 : 28671 : case jbvNumeric:
3665 tgl@sss.pgh.pa.us 1833 [ - + - - : 28671 : numlen = VARSIZE_ANY(scalarVal->val.numeric);
- - - - -
+ ]
3630 heikki.linnakangas@i 1834 : 28671 : padlen = padBufferToInt(buffer);
1835 : :
1836 : 28671 : appendToBuffer(buffer, (char *) scalarVal->val.numeric, numlen);
1837 : :
572 pg@bowt.ie 1838 : 28671 : *header = JENTRY_ISNUMERIC | (padlen + numlen);
3675 andrew@dunslane.net 1839 : 28671 : break;
1840 : :
3630 heikki.linnakangas@i 1841 : 11931 : case jbvBool:
572 pg@bowt.ie 1842 : 23862 : *header = (scalarVal->val.boolean) ?
3630 heikki.linnakangas@i 1843 [ + + ]: 11931 : JENTRY_ISBOOL_TRUE : JENTRY_ISBOOL_FALSE;
3675 andrew@dunslane.net 1844 : 11931 : break;
1845 : :
1663 akorotkov@postgresql 1846 : 768 : case jbvDatetime:
1847 : : {
1848 : : char buf[MAXDATELEN + 1];
1849 : : size_t len;
1850 : :
1851 : 768 : JsonEncodeDateTime(buf,
1852 : : scalarVal->val.datetime.value,
1853 : : scalarVal->val.datetime.typid,
1854 : 768 : &scalarVal->val.datetime.tz);
1855 : 768 : len = strlen(buf);
1856 : 768 : appendToBuffer(buffer, buf, len);
1857 : :
572 pg@bowt.ie 1858 : 768 : *header = len;
1859 : : }
1663 akorotkov@postgresql 1860 : 768 : break;
1861 : :
3675 andrew@dunslane.net 1862 :UBC 0 : default:
1863 [ # # ]: 0 : elog(ERROR, "invalid jsonb scalar type");
1864 : : }
3675 andrew@dunslane.net 1865 :CBC 103499 : }
1866 : :
1867 : : /*
1868 : : * Compare two jbvString JsonbValue values, a and b.
1869 : : *
1870 : : * This is a special qsort() comparator used to sort strings in certain
1871 : : * internal contexts where it is sufficient to have a well-defined sort order.
1872 : : * In particular, object pair keys are sorted according to this criteria to
1873 : : * facilitate cheap binary searches where we don't care about lexical sort
1874 : : * order.
1875 : : *
1876 : : * a and b are first sorted based on their length. If a tie-breaker is
1877 : : * required, only then do we consider string binary equality.
1878 : : */
1879 : : static int
3628 heikki.linnakangas@i 1880 : 42297 : lengthCompareJsonbStringValue(const void *a, const void *b)
1881 : : {
3675 andrew@dunslane.net 1882 : 42297 : const JsonbValue *va = (const JsonbValue *) a;
1883 : 42297 : const JsonbValue *vb = (const JsonbValue *) b;
1884 : :
1885 [ - + ]: 42297 : Assert(va->type == jbvString);
1886 [ - + ]: 42297 : Assert(vb->type == jbvString);
1887 : :
1668 alvherre@alvh.no-ip. 1888 : 84594 : return lengthCompareJsonbString(va->val.string.val, va->val.string.len,
1889 : 42297 : vb->val.string.val, vb->val.string.len);
1890 : : }
1891 : :
1892 : : /*
1893 : : * Subroutine for lengthCompareJsonbStringValue
1894 : : *
1895 : : * This is also useful separately to implement binary search on
1896 : : * JsonbContainers.
1897 : : */
1898 : : static int
1899 : 344391 : lengthCompareJsonbString(const char *val1, int len1, const char *val2, int len2)
1900 : : {
1901 [ + + ]: 344391 : if (len1 == len2)
1902 : 107839 : return memcmp(val1, val2, len1);
1903 : : else
1904 [ + + ]: 236552 : return len1 > len2 ? 1 : -1;
1905 : : }
1906 : :
1907 : : /*
1908 : : * qsort_arg() comparator to compare JsonbPair values.
1909 : : *
1910 : : * Third argument 'binequal' may point to a bool. If it's set, *binequal is set
1911 : : * to true iff a and b have full binary equality, since some callers have an
1912 : : * interest in whether the two values are equal or merely equivalent.
1913 : : *
1914 : : * N.B: String comparisons here are "length-wise"
1915 : : *
1916 : : * Pairs with equals keys are ordered such that the order field is respected.
1917 : : */
1918 : : static int
3675 andrew@dunslane.net 1919 : 40623 : lengthCompareJsonbPair(const void *a, const void *b, void *binequal)
1920 : : {
1921 : 40623 : const JsonbPair *pa = (const JsonbPair *) a;
1922 : 40623 : const JsonbPair *pb = (const JsonbPair *) b;
1923 : : int res;
1924 : :
3628 heikki.linnakangas@i 1925 : 40623 : res = lengthCompareJsonbStringValue(&pa->key, &pb->key);
1926 [ + + + - ]: 40623 : if (res == 0 && binequal)
1927 : 87 : *((bool *) binequal) = true;
1928 : :
1929 : : /*
1930 : : * Guarantee keeping order of equal pair. Unique algorithm will prefer
1931 : : * first element as value.
1932 : : */
3675 andrew@dunslane.net 1933 [ + + ]: 40623 : if (res == 0)
1934 [ - + ]: 87 : res = (pa->order > pb->order) ? -1 : 1;
1935 : :
1936 : 40623 : return res;
1937 : : }
1938 : :
1939 : : /*
1940 : : * Sort and unique-ify pairs in JsonbValue object
1941 : : */
1942 : : static void
382 alvherre@alvh.no-ip. 1943 : 10350 : uniqueifyJsonbObject(JsonbValue *object, bool unique_keys, bool skip_nulls)
1944 : : {
3675 andrew@dunslane.net 1945 : 10350 : bool hasNonUniq = false;
1946 : :
1947 [ - + ]: 10350 : Assert(object->type == jbvObject);
1948 : :
3665 tgl@sss.pgh.pa.us 1949 [ + + ]: 10350 : if (object->val.object.nPairs > 1)
1950 : 5910 : qsort_arg(object->val.object.pairs, object->val.object.nPairs, sizeof(JsonbPair),
1951 : : lengthCompareJsonbPair, &hasNonUniq);
1952 : :
382 alvherre@alvh.no-ip. 1953 [ + + + + ]: 10350 : if (hasNonUniq && unique_keys)
1954 [ + - ]: 15 : ereport(ERROR,
1955 : : errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
1956 : : errmsg("duplicate JSON object key value"));
1957 : :
1958 [ + + + + ]: 10335 : if (hasNonUniq || skip_nulls)
1959 : : {
1960 : : JsonbPair *ptr,
1961 : : *res;
1962 : :
1963 [ + + + - ]: 82 : while (skip_nulls && object->val.object.nPairs > 0 &&
1964 [ + + ]: 16 : object->val.object.pairs->value.type == jbvNull)
1965 : : {
1966 : : /* If skip_nulls is true, remove leading items with null */
382 alvherre@alvh.no-ip. 1967 :GBC 3 : object->val.object.pairs++;
1968 : 3 : object->val.object.nPairs--;
1969 : : }
1970 : :
382 alvherre@alvh.no-ip. 1971 [ + - ]:CBC 79 : if (object->val.object.nPairs > 0)
1972 : : {
1973 : 79 : ptr = object->val.object.pairs + 1;
1974 : 79 : res = object->val.object.pairs;
1975 : :
1976 [ + + ]: 229 : while (ptr - object->val.object.pairs < object->val.object.nPairs)
1977 : : {
1978 : : /* Avoid copying over duplicate or null */
1979 [ + + ]: 150 : if (lengthCompareJsonbStringValue(ptr, res) != 0 &&
1980 [ + + + + ]: 78 : (!skip_nulls || ptr->value.type != jbvNull))
1981 : : {
1982 : 63 : res++;
1983 [ + + ]: 63 : if (ptr != res)
1984 : 54 : memcpy(res, ptr, sizeof(JsonbPair));
1985 : : }
1986 : 150 : ptr++;
1987 : : }
1988 : :
1989 : 79 : object->val.object.nPairs = res + 1 - object->val.object.pairs;
1990 : : }
1991 : : }
3675 andrew@dunslane.net 1992 : 10335 : }
|