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