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