/*------------------------------------------------------------------------- * * jsonb_util.c * Utilities for jsonb datatype * * Copyright (c) 2014, PostgreSQL Global Development Group * * * IDENTIFICATION * src/backend/utils/adt/jsonb_util.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/hash.h" #include "catalog/pg_collation.h" #include "catalog/pg_type.h" #include "miscadmin.h" #include "utils/builtins.h" #include "utils/jsonb.h" #include "utils/memutils.h" /* * Twice as many values may be stored within pairs (for an Object) than within * elements (for an Array), modulo the current MaxAllocSize limitation. Note * that JSONB_MAX_PAIRS is derived from the number of possible pairs, not * values (as is the case for arrays and their elements), because we're * concerned about limitations on the representation of the number of pairs. * Over twice the memory is required to store n JsonbPairs as n JsonbValues. * It only takes exactly twice as much disk space for storage, though. The * JsonbPair (not an actual pair of values) representation is used here because * that is what is subject to the MaxAllocSize restriction when building an * object. */ #define JSONB_MAX_ELEMS (Min(MaxAllocSize / sizeof(JsonbValue), JENTRY_POSMASK)) #define JSONB_MAX_PAIRS (Min(MaxAllocSize / sizeof(JsonbPair), \ JENTRY_POSMASK)) /* * State used while converting an arbitrary JsonbValue into a Jsonb value * (4-byte varlena uncompressed representation of a Jsonb) * * ConvertLevel: Bookkeeping around particular level when converting. */ typedef struct convertLevel { uint32 i; /* Iterates once per element, or once per pair */ uint32 *header; /* Pointer to current container header */ JEntry *meta; /* This level's metadata */ char *begin; /* Pointer into convertState.buffer */ } convertLevel; /* * convertState: Overall bookkeeping state for conversion */ typedef struct convertState { /* Preallocated buffer in which to form varlena/Jsonb value */ Jsonb *buffer; /* Pointer into buffer */ char *ptr; /* State for */ convertLevel *allState, /* Overall state array */ *contPtr; /* Cur container pointer (in allState) */ /* Current size of buffer containing allState array */ Size levelSz; } convertState; static int compareJsonbScalarValue(JsonbValue * a, JsonbValue * b); static int lexicalCompareJsonbStringValue(const void *a, const void *b); static Size convertJsonb(JsonbValue * val, Jsonb* buffer); static inline short addPaddingInt(convertState * cstate); static void walkJsonbValueConversion(JsonbValue * val, convertState * cstate, uint32 nestlevel); static void putJsonbValueConversion(convertState * cstate, JsonbValue * val, uint32 flags, uint32 level); static void putScalarConversion(convertState * cstate, JsonbValue * scalarVal, uint32 level, uint32 i); static void iteratorFromContainerBuf(JsonbIterator * it, char *buffer); static bool formIterIsContainer(JsonbIterator ** it, JsonbValue * val, JEntry * ent, bool skipNested); static JsonbIterator *freeAndGetParent(JsonbIterator * it); static JsonbParseState *pushState(JsonbParseState ** pstate); static void appendKey(JsonbParseState * pstate, JsonbValue * scalarVal); static void appendValue(JsonbParseState * pstate, JsonbValue * scalarVal); static void appendElement(JsonbParseState * pstate, JsonbValue * scalarVal); static int lengthCompareJsonbStringValue(const void *a, const void *b, void *arg); static int lengthCompareJsonbPair(const void *a, const void *b, void *arg); static void uniqueifyJsonbObject(JsonbValue * object); static void uniqueifyJsonbArray(JsonbValue * array); /* * Turn an in-memory JsonbValue into a Jsonb for on-disk storage. * * There isn't a JsonbToJsonbValue(), because generally we find it more * convenient to directly iterate through the Jsonb representation and only * really convert nested scalar values. formIterIsContainer() does this, so * that clients of the iteration code don't have to directly deal with the * binary representation (JsonbDeepContains() is a notable exception, although * all exceptions are internal to this module). In general, functions that * accept a JsonbValue argument are concerned with the manipulation of scalar * values, or simple containers of scalar values, where it would be * inconvenient to deal with a great amount of other state. */ Jsonb * JsonbValueToJsonb(JsonbValue * val) { Jsonb *out; Size sz; if (IsAJsonbScalar(val)) { /* Scalar value */ JsonbParseState *pstate = NULL; JsonbValue *res; JsonbValue scalarArray; scalarArray.type = jbvArray; scalarArray.array.rawScalar = true; scalarArray.array.nElems = 1; pushJsonbValue(&pstate, WJB_BEGIN_ARRAY, &scalarArray); pushJsonbValue(&pstate, WJB_ELEM, val); res = pushJsonbValue(&pstate, WJB_END_ARRAY, NULL); out = palloc(VARHDRSZ + res->estSize); sz = convertJsonb(res, out); Assert(sz <= res->estSize); SET_VARSIZE(out, sz + VARHDRSZ); } else if (val->type == jbvObject || val->type == jbvArray) { out = palloc(VARHDRSZ + val->estSize); sz = convertJsonb(val, out); Assert(sz <= val->estSize); SET_VARSIZE(out, VARHDRSZ + sz); } else { Assert(val->type == jbvBinary); out = palloc(VARHDRSZ + val->binary.len); SET_VARSIZE(out, VARHDRSZ + val->binary.len); memcpy(VARDATA(out), val->binary.data, val->binary.len); } return out; } /* * BT comparator worker function. Returns an integer less than, equal to, or * greater than zero, indicating whether a is less than, equal to, or greater * than b. Consistent with the requirements for a B-Tree operator class * * Strings are compared lexically, in contrast with other places where we use a * much simpler comparator logic for searching through Strings. Since this is * called from B-Tree support function 1, we're careful about not leaking * memory here. */ int compareJsonbSuperHeaderValue(JsonbSuperHeader a, JsonbSuperHeader b) { JsonbIterator *ita, *itb; int res = 0; ita = JsonbIteratorInit(a); itb = JsonbIteratorInit(b); do { JsonbValue va, vb; int ra, rb; ra = JsonbIteratorNext(&ita, &va, false); rb = JsonbIteratorNext(&itb, &vb, false); /* * To a limited extent we'll redundantly iterate over an array/object * while re-performing the same test without any reasonable expectation * of the same container types having differing lengths (as when we * process a WJB_BEGIN_OBJECT, and later the corresponding * WJB_END_OBJECT), but no matter. */ if (ra == rb) { if (ra == WJB_DONE) { /* Decisively equal */ break; } if (va.type == vb.type) { switch (va.type) { case jbvString: res = lexicalCompareJsonbStringValue(&va, &vb); break; case jbvNull: case jbvNumeric: case jbvBool: res = compareJsonbScalarValue(&va, &vb); break; case jbvArray: /* * This could be a "raw scalar" pseudo array. That's a * special case here though, since we still want the * general type-based comparisons to apply, and as far * as we're concerned a pseudo array is just a scalar. */ if (va.array.rawScalar != vb.array.rawScalar) res = (va.array.rawScalar) ? -1 : 1; if (va.array.nElems != vb.array.nElems) res = (va.array.nElems > vb.array.nElems) ? 1 : -1; break; case jbvObject: if (va.object.nPairs != vb.object.nPairs) res = (va.object.nPairs > vb.object.nPairs) ? 1 : -1; break; case jbvBinary: elog(ERROR, "unexpected jbvBinary value"); } } else { /* Type-defined order */ res = (va.type > vb.type) ? 1 : -1; } } else { /* * It's safe to assume that the types differed. * * If the two values were the same container type, then there'd * have been a chance to observe the variation in the number of * elements/pairs (when processing WJB_BEGIN_OBJECT, say). They * can't be scalar types either, because then they'd have to be * contained in containers already ruled unequal due to differing * numbers of pairs/elements, or already directly ruled unequal * with a call to the underlying type's comparator. */ Assert(va.type != vb.type); Assert(va.type == jbvArray || va.type == jbvObject); Assert(vb.type == jbvArray || vb.type == jbvObject); /* Type-defined order */ res = (va.type > vb.type) ? 1 : -1; } } while (res == 0); while (ita != NULL) { JsonbIterator *i = ita->parent; pfree(ita); ita = i; } while (itb != NULL) { JsonbIterator *i = itb->parent; pfree(itb); itb = i; } return res; } /* * Find value in object (i.e. the "value" part of some key/value pair in an * object), or find a matching element if we're looking through an array. Do * so on the basis of equality of the object keys only, or alternatively * element values only, with a caller-supplied value "key". The "flags" * argument allows the caller to specify which container types are of interest. * * This exported utility function exists to facilitate various cases concerned * with "containment". If asked to look through an object, the caller had * better pass a Jsonb String, because their keys can only be strings. * Otherwise, for an array, any type of JsonbValue will do. * * In order to proceed with the search, it is necessary for callers to have * both specified an interest in exactly one particular container type with an * appropriate flag, as well as having the pointed-to Jsonb superheader be of * one of those same container types at the top level. (Actually, we just do * whichever makes sense to save callers the trouble of figuring it out - at * most one can make sense, because the super header either points to an array * (possible a "raw scalar" pseudo array) or an object.) * * Note that we can return a jbvBinary JsonbValue if this is called on an * object, but we never do so on an array. If the caller asks to look through * a container type that is not of the type pointed to by the superheader, * immediately fall through and return NULL. If we cannot find the value, * return NULL. Otherwise, return palloc()'d copy of value. * * lowbound can be NULL, but if not it's used to establish a point at which to * start searching. If the value searched for is found, then lowbound is then * set to an offset into the array or object. Typically, this is used to * exploit the ordering of objects to avoid redundant work, by also sorting a * list of items to be checked using the internal sort criteria for objects * (object pair keys), and then, when searching for the second or subsequent * item, picking it up where we left off knowing that the second or subsequent * item can not be at a point below the low bound set when the first was found. * This is only useful for objects, not arrays (which have a user-defined * order), so array superheader Jsonbs should just pass NULL. Moreover, it's * only useful because we only match object pairs on the basis of their key, so * presumably anyone exploiting this is only interested in matching Object keys * with a String. lowbound is given in units of pairs, not underlying values. */ JsonbValue * findJsonbValueFromSuperHeader(JsonbSuperHeader sheader, uint32 flags, uint32 *lowbound, JsonbValue * key) { uint32 superheader = *(uint32 *) sheader; JEntry *array = (JEntry *) (sheader + sizeof(uint32)); int count = (superheader & JB_CMASK); JsonbValue *result = palloc(sizeof(JsonbValue)); Assert((flags & ~(JB_FARRAY | JB_FOBJECT)) == 0); if (flags & JB_FARRAY & superheader) { char *data = (char *) (array + (superheader & JB_CMASK)); int i; for (i = 0; i < count; i++) { JEntry *e = array + i; if (JBE_ISNULL(*e) && key->type == jbvNull) { result->type = jbvNull; result->estSize = sizeof(JEntry); } else if (JBE_ISSTRING(*e) && key->type == jbvString) { result->type = jbvString; result->string.val = data + JBE_OFF(*e); result->string.len = JBE_LEN(*e); result->estSize = sizeof(JEntry) + result->string.len; } else if (JBE_ISNUMERIC(*e) && key->type == jbvNumeric) { result->type = jbvNumeric; result->numeric = (Numeric) (data + INTALIGN(JBE_OFF(*e))); result->estSize = 2 * sizeof(JEntry) + VARSIZE_ANY(result->numeric); } else if (JBE_ISBOOL(*e) && key->type == jbvBool) { result->type = jbvBool; result->boolean = JBE_ISBOOL_TRUE(*e) != 0; result->estSize = sizeof(JEntry); } else continue; if (compareJsonbScalarValue(key, result) == 0) return result; } } else if (flags & JB_FOBJECT & superheader) { /* Since this is an object, account for *Pairs* of Jentrys */ char *data = (char *) (array + (superheader & JB_CMASK) * 2); uint32 stopLow = lowbound ? *lowbound : 0, stopMiddle; /* Object key past by caller must be a string */ Assert(key->type == jbvString); /* Binary search on object/pair keys *only* */ while (stopLow < count) { JEntry *entry; int difference; JsonbValue candidate; /* * Note how we compensate for the fact that we're iterating through * pairs (not entries) throughout. */ stopMiddle = stopLow + (count - stopLow) / 2; entry = array + stopMiddle * 2; candidate.type = jbvString; candidate.string.val = data + JBE_OFF(*entry); candidate.string.len = JBE_LEN(*entry); candidate.estSize = sizeof(JEntry) + candidate.string.len; difference = lengthCompareJsonbStringValue(&candidate, key, NULL); if (difference == 0) { /* Found our value (from key/value pair) */ JEntry *v = entry + 1; if (lowbound) *lowbound = stopMiddle + 1; if (JBE_ISNULL(*v)) { result->type = jbvNull; result->estSize = sizeof(JEntry); } else if (JBE_ISSTRING(*v)) { result->type = jbvString; result->string.val = data + JBE_OFF(*v); result->string.len = JBE_LEN(*v); result->estSize = sizeof(JEntry) + result->string.len; } else if (JBE_ISNUMERIC(*v)) { result->type = jbvNumeric; result->numeric = (Numeric) (data + INTALIGN(JBE_OFF(*v))); result->estSize = 2 * sizeof(JEntry) + VARSIZE_ANY(result->numeric); } else if (JBE_ISBOOL(*v)) { result->type = jbvBool; result->boolean = JBE_ISBOOL_TRUE(*v) != 0; result->estSize = sizeof(JEntry); } else { /* * See header comments to understand why this never happens * with arrays */ result->type = jbvBinary; result->binary.data = data + INTALIGN(JBE_OFF(*v)); result->binary.len = JBE_LEN(*v) - (INTALIGN(JBE_OFF(*v)) - JBE_OFF(*v)); result->estSize = 2 * sizeof(JEntry) + result->binary.len; } return result; } else { if (difference < 0) stopLow = stopMiddle + 1; else count = stopMiddle; } } if (lowbound) *lowbound = stopLow; } /* Not found */ pfree(result); return NULL; } /* * Get i-th value of Jsonb array from superheader. * * Returns palloc()'d copy of value. */ JsonbValue * getIthJsonbValueFromSuperHeader(JsonbSuperHeader sheader, uint32 i) { uint32 superheader = *(uint32 *) sheader; JsonbValue *result; JEntry *array, *e; char *data; result = palloc(sizeof(JsonbValue)); if (i >= (superheader & JB_CMASK)) return NULL; array = (JEntry *) (sheader + sizeof(uint32)); if (superheader & JB_FARRAY) { e = array + i; data = (char *) (array + (superheader & JB_CMASK)); } else { elog(ERROR, "not a jsonb array"); } if (JBE_ISNULL(*e)) { result->type = jbvNull; result->estSize = sizeof(JEntry); } else if (JBE_ISSTRING(*e)) { result->type = jbvString; result->string.val = data + JBE_OFF(*e); result->string.len = JBE_LEN(*e); result->estSize = sizeof(JEntry) + result->string.len; } else if (JBE_ISNUMERIC(*e)) { result->type = jbvNumeric; result->numeric = (Numeric) (data + INTALIGN(JBE_OFF(*e))); result->estSize = 2 * sizeof(JEntry) + VARSIZE_ANY(result->numeric); } else if (JBE_ISBOOL(*e)) { result->type = jbvBool; result->boolean = JBE_ISBOOL_TRUE(*e) != 0; result->estSize = sizeof(JEntry); } else { result->type = jbvBinary; result->binary.data = data + INTALIGN(JBE_OFF(*e)); result->binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e)); result->estSize = result->binary.len + 2 * sizeof(JEntry); } return result; } /* * Push JsonbValue into JsonbParseState. * * Used when parsing JSON tokens to form Jsonb, or when converting an in-memory * JsonbValue to a Jsonb. * * Initial state of *JsonbParseState is NULL, since it'll be allocated here * originally (caller will get JsonbParseState back by reference). * * Only sequential tokens pertaining to non-container types should pass a * JsonbValue. There is one exception -- WJB_BEGIN_ARRAY callers may pass a * "raw scalar" pseudo array to append that. */ JsonbValue * pushJsonbValue(JsonbParseState ** pstate, int seq, JsonbValue * scalarVal) { JsonbValue *result = NULL; switch (seq) { case WJB_BEGIN_ARRAY: Assert(!scalarVal || scalarVal->array.rawScalar); *pstate = pushState(pstate); result = &(*pstate)->contVal; (*pstate)->contVal.type = jbvArray; (*pstate)->contVal.estSize = 3 * sizeof(JEntry); (*pstate)->contVal.array.nElems = 0; (*pstate)->contVal.array.rawScalar = (scalarVal && scalarVal->array.rawScalar); if (scalarVal && scalarVal->array.nElems > 0) { /* Assume that this array is still really a scalar */ Assert(scalarVal->type == jbvArray); (*pstate)->size = scalarVal->array.nElems; } else { (*pstate)->size = 4; } (*pstate)->contVal.array.elems = palloc(sizeof(JsonbValue) * (*pstate)->size); break; case WJB_BEGIN_OBJECT: Assert(!scalarVal); *pstate = pushState(pstate); result = &(*pstate)->contVal; (*pstate)->contVal.type = jbvObject; (*pstate)->contVal.estSize = 3 * sizeof(JEntry); (*pstate)->contVal.object.nPairs = 0; (*pstate)->size = 4; (*pstate)->contVal.object.pairs = palloc(sizeof(JsonbPair) * (*pstate)->size); break; case WJB_KEY: Assert(scalarVal->type == jbvString); appendKey(*pstate, scalarVal); break; case WJB_VALUE: Assert(IsAJsonbScalar(scalarVal) || scalarVal->type == jbvBinary); appendValue(*pstate, scalarVal); break; case WJB_ELEM: Assert(IsAJsonbScalar(scalarVal) || scalarVal->type == jbvBinary); appendElement(*pstate, scalarVal); break; case WJB_END_OBJECT: uniqueifyJsonbObject(&(*pstate)->contVal); case WJB_END_ARRAY: /* Steps here common to WJB_END_OBJECT case */ Assert(!scalarVal); result = &(*pstate)->contVal; /* * Pop stack and push current array/object as value in parent * array/object */ *pstate = (*pstate)->next; if (*pstate) { switch ((*pstate)->contVal.type) { case jbvArray: appendElement(*pstate, result); break; case jbvObject: appendValue(*pstate, result); break; default: elog(ERROR, "invalid jsonb container type"); } } break; default: elog(ERROR, "unrecognized jsonb sequential processing token"); } return result; } /* * Given a Jsonb superheader, expand to JsonbIterator to iterate over items * fully expanded to in-memory representation for manipulation. * * See JsonbIteratorNext() for notes on memory management. */ JsonbIterator * JsonbIteratorInit(JsonbSuperHeader sheader) { JsonbIterator *it = palloc(sizeof(JsonbIterator)); iteratorFromContainerBuf(it, sheader); it->parent = NULL; return it; } /* * Get next JsonbValue while iterating * * Caller should initially pass their own, original iterator. They may get * back a child iterator palloc()'d here instead. The function can be relied * on to free those child iterators, lest the memory allocated for highly * nested objects become unreasonable, but only if callers don't end iteration * early (by breaking upon having found something in a search, for example). * * Callers in such a scenario, that are particularly sensitive to leaking * memory in a long-lived context may walk the ancestral tree from the final * iterator we left them with to its oldest ancestor, pfree()ing as they go. * They do not have to free any other memory previously allocated for iterators * but not accessible as direct ancestors of the iterator they're last passed * back. * * Returns "Jsonb sequential processing" token value. Iterator "state" * reflects the current stage of the process in a less granular fashion, and is * mostly used here to track things internally with respect to particular * iterators. * * Clients of this function should not have to handle any jbvBinary values * (since recursive calls will deal with this), provided skipNested is false. * It is our job to expand the jbvBinary representation without bothering them * with it. However, clients should not take it upon themselves to touch array * or Object element/pair buffers, since their element/pair pointers are * garbage. */ int JsonbIteratorNext(JsonbIterator ** it, JsonbValue * val, bool skipNested) { JsonbIterState state; /* Guard against stack overflow due to overly complex Jsonb */ check_stack_depth(); /* Recursive caller may have original caller's iterator */ if (*it == NULL) return WJB_DONE; state = (*it)->state; if ((*it)->containerType == JB_FARRAY) { if (state == jbi_start) { /* Set v to array on first array call */ val->type = jbvArray; val->array.nElems = (*it)->nElems; /* * v->array.elems is not actually set, because we aren't doing a * full conversion */ val->array.rawScalar = (*it)->isScalar; (*it)->i = 0; /* Set state for next call */ (*it)->state = jbi_elem; return WJB_BEGIN_ARRAY; } else if (state == jbi_elem) { if ((*it)->i >= (*it)->nElems) { /* * All elements within array already processed. Report this to * caller, and give it back original parent iterator (which * independently tracks iteration progress at its level of * nesting). */ *it = freeAndGetParent(*it); return WJB_END_ARRAY; } else if (formIterIsContainer(it, val, &(*it)->meta[(*it)->i++], skipNested)) { /* * New child iterator acquired within formIterIsContainer. * Recurse into container. Don't directly return jbvBinary * value to top-level client. */ return JsonbIteratorNext(it, val, skipNested); } else { /* Scalar item in array */ return WJB_ELEM; } } } else if ((*it)->containerType == JB_FOBJECT) { if (state == jbi_start) { /* Set v to object on first object call */ val->type = jbvObject; val->object.nPairs = (*it)->nElems; /* * v->object.pairs is not actually set, because we aren't doing a * full conversion */ (*it)->i = 0; /* Set state for next call */ (*it)->state = jbi_key; return WJB_BEGIN_OBJECT; } else if (state == jbi_key) { if ((*it)->i >= (*it)->nElems) { /* * All pairs within object already processed. Report this to * caller, and give it back original containing iterator (which * independently tracks iteration progress at its level of * nesting). */ *it = freeAndGetParent(*it); return WJB_END_OBJECT; } else { /* * Return binary item key (ensured by setting skipNested to * false directly). No child iterator, no further recursion. * When control reaches here, it's probably from a recursive * call. */ if (formIterIsContainer(it, val, &(*it)->meta[(*it)->i * 2], false)) elog(ERROR, "unexpected container as object key"); Assert(val->type == jbvString); /* Set state for next call */ (*it)->state = jbi_value; return WJB_KEY; } } else if (state == jbi_value) { /* Set state for next call */ (*it)->state = jbi_key; /* * Value may be a container, in which case we recurse with new, * child iterator. If it is, don't bother !skipNested callers with * dealing with the jbvBinary representation. */ if (formIterIsContainer(it, val, &(*it)->meta[((*it)->i++) * 2 + 1], skipNested)) return JsonbIteratorNext(it, val, skipNested); else return WJB_VALUE; } } elog(ERROR, "invalid iterator state"); return -1; } /* * Worker for "contains" operator's function * * Formally speaking, containment is top-down, unordered subtree isomorphism. * * Takes iterators that belong to some container type. These iterators * "belong" to those values in the sense that they've just been initialized in * respect of them by the caller (perhaps in a nested fashion). * * "val" is lhs Jsonb, and mContained is rhs Jsonb when called from top level. * We determine if mContained is contained within val. */ bool JsonbDeepContains(JsonbIterator ** val, JsonbIterator ** mContained) { uint32 rval, rcont; JsonbValue vval, vcontained; /* * Guard against stack overflow due to overly complex Jsonb. * * Functions called here independently take this precaution, but that might * not be sufficient since this is also a recursive function. */ check_stack_depth(); rval = JsonbIteratorNext(val, &vval, false); rcont = JsonbIteratorNext(mContained, &vcontained, false); if (rval != rcont) { /* * The differing return values can immediately be taken as indicating * two differing container types at this nesting level, which is * sufficient reason to give up entirely (but it should be the case * that they're both some container type). */ Assert(rval == WJB_BEGIN_OBJECT || rval == WJB_BEGIN_ARRAY); Assert(rcont == WJB_BEGIN_OBJECT || rcont == WJB_BEGIN_ARRAY); return false; } else if (rcont == WJB_BEGIN_OBJECT) { JsonbValue *lhsVal; /* lhsVal is from pair in lhs object */ Assert(vcontained.type == jbvObject); /* Work through rhs "is it contained within?" object */ for (;;) { rcont = JsonbIteratorNext(mContained, &vcontained, false); /* * When we get through caller's rhs "is it contained within?" * object without failing to find one of its values, it's * contained. */ if (rcont == WJB_END_OBJECT) return true; Assert(rcont == WJB_KEY); /* First, find value by key... */ lhsVal = findJsonbValueFromSuperHeader((*val)->buffer, JB_FOBJECT, NULL, &vcontained); if (!lhsVal) return false; /* * ...at this stage it is apparent that there is at least a key * match for this rhs pair. */ rcont = JsonbIteratorNext(mContained, &vcontained, true); Assert(rcont == WJB_VALUE); /* * Compare rhs pair's value with lhs pair's value just found using * key */ if (lhsVal->type != vcontained.type) { return false; } else if (IsAJsonbScalar(lhsVal)) { if (compareJsonbScalarValue(lhsVal, &vcontained) != 0) return false; } else { /* Nested container value (object or array) */ JsonbIterator *nestval, *nestContained; Assert(lhsVal->type == jbvBinary); Assert(vcontained.type == jbvBinary); nestval = JsonbIteratorInit(lhsVal->binary.data); nestContained = JsonbIteratorInit(vcontained.binary.data); /* * Match "value" side of rhs datum object's pair recursively. * It's a nested structure. * * Note that nesting still has to "match up" at the right * nesting sub-levels. However, there need only be zero or * more matching pairs (or elements) at each nesting level * (provided the *rhs* pairs/elements *all* match on each * level), which enables searching nested structures for a * single String or other primitive type sub-datum quite * effectively (provided the user constructed the rhs nested * structure such that we "know where to look"). * * In other words, the mapping of container nodes in the rhs * "vcontained" Jsonb to internal nodes on the lhs is * injective, and parent-child edges on the rhs must be mapped * to parent-child edges on the lhs to satisfy the condition of * containment (plus of course the mapped nodes must be equal). */ if (!JsonbDeepContains(&nestval, &nestContained)) return false; } } } else if (rcont == WJB_BEGIN_ARRAY) { JsonbValue *lhsConts = NULL; uint32 nLhsElems = vval.array.nElems; Assert(vcontained.type == jbvArray); /* * Handle distinction between "raw scalar" pseudo arrays, and real * arrays. * * A raw scalar may contain another raw scalar, and an array may * contain a raw scalar, but a raw scalar may not contain an array. We * don't do something like this for the object case, since objects can * only contain pairs, never raw scalars (a pair is represented by an * rhs object argument with a single contained pair). */ if (vval.array.rawScalar && !vcontained.array.rawScalar) return false; /* Work through rhs "is it contained within?" array */ for (;;) { rcont = JsonbIteratorNext(mContained, &vcontained, true); /* * When we get through caller's rhs "is it contained within?" array * without failing to find one of its values, it's contained. */ if (rcont == WJB_END_ARRAY) return true; Assert(rcont == WJB_ELEM); if (IsAJsonbScalar(&vcontained)) { if (!findJsonbValueFromSuperHeader((*val)->buffer, JB_FARRAY, NULL, &vcontained)) return false; } else { uint32 i; /* * If this is first container found in rhs array (at this * depth), initialize temp lhs array of containers */ if (lhsConts == NULL) { uint32 j = 0; /* Make room for all possible values */ lhsConts = palloc(sizeof(JsonbValue) * nLhsElems); for (i = 0; i < nLhsElems; i++) { /* Store all lhs elements in temp array*/ rcont = JsonbIteratorNext(val, &vval, true); Assert(rcont == WJB_ELEM); if (vval.type == jbvBinary) lhsConts[j++] = vval; } /* No container elements in temp array, so give up now */ if (j == 0) return false; /* We may have only partially filled array */ nLhsElems = j; } /* XXX: Nested array containment is O(N^2) */ for (i = 0; i < nLhsElems; i++) { /* Nested container value (object or array) */ JsonbIterator *nestval, *nestContained; bool contains; nestval = JsonbIteratorInit(lhsConts[i].binary.data); nestContained = JsonbIteratorInit(vcontained.binary.data); contains = JsonbDeepContains(&nestval, &nestContained); if (nestval) pfree(nestval); if (nestContained) pfree(nestContained); if (contains) break; } /* * Report rhs container value is not contained if couldn't * match rhs container to *some* lhs cont */ if (i == nLhsElems) return false; } } } else { elog(ERROR, "invalid jsonb container type"); } elog(ERROR, "unexpectedly fell off end of jsonb container"); return false; } /* * Convert a Postgres text array to a Jsonb array, sorted and with * de-duplicated key elements. This is used for searching an object for items * in the array, so we enforce that the number of strings cannot exceed * JSONB_MAX_PAIRS. */ JsonbValue * arrayToJsonbSortedArray(ArrayType *array) { Datum *key_datums; bool *key_nulls; int elem_count; JsonbValue *result; int i, j; /* Extract data for sorting */ deconstruct_array(array, TEXTOID, -1, false, 'i', &key_datums, &key_nulls, &elem_count); if (elem_count == 0) return NULL; /* * A text array uses at least eight bytes per element, so any overflow in * "key_count * sizeof(JsonbPair)" is small enough for palloc() to catch. * However, credible improvements to the array format could invalidate that * assumption. Therefore, use an explicit check rather than relying on * palloc() to complain. */ if (elem_count > JSONB_MAX_PAIRS) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of array elements (%d) exceeds maximum allowed Jsonb pairs (%zu)", elem_count, JSONB_MAX_PAIRS))); result = palloc(sizeof(JsonbValue)); result->type = jbvArray; result->array.rawScalar = false; result->array.elems = palloc(sizeof(JsonbPair) * elem_count); for (i = 0, j = 0; i < elem_count; i++) { if (!key_nulls[i]) { result->array.elems[j].type = jbvString; result->array.elems[j].string.val = VARDATA(key_datums[i]); result->array.elems[j].string.len = VARSIZE(key_datums[i]) - VARHDRSZ; j++; } } result->array.nElems = j; uniqueifyJsonbArray(result); return result; } /* * Hash a JsonbValue scalar value, mixing in the hash value with an existing * hash provided by the caller. * * Some callers may wish to independently XOR in JB_FOBJECT and JB_FARRAY * flags. */ void JsonbHashScalarValue(const JsonbValue * scalarVal, uint32 * hash) { int tmp; /* * Combine hash values of successive keys, values and elements by rotating * the previous value left 1 bit, then XOR'ing in the new * key/value/element's hash value. */ *hash = (*hash << 1) | (*hash >> 31); switch (scalarVal->type) { case jbvNull: *hash ^= 0x01; return; case jbvString: tmp = hash_any((unsigned char *) scalarVal->string.val, scalarVal->string.len); *hash ^= tmp; return; case jbvNumeric: /* Must be unaffected by trailing zeroes */ tmp = DatumGetInt32(DirectFunctionCall1(hash_numeric, NumericGetDatum(scalarVal->numeric))); *hash ^= tmp; return; case jbvBool: *hash ^= scalarVal->boolean? 0x02:0x04; return; default: elog(ERROR, "invalid jsonb scalar type"); } } /* * Are two scalar JsonbValues of the same type a and b equal? * * Does not use lexical comparisons. Therefore, it is essentially that this * never be used against Strings for anything other than searching for values * within a single jsonb. */ static int compareJsonbScalarValue(JsonbValue * aScalar, JsonbValue * bScalar) { if (aScalar->type == bScalar->type) { switch (aScalar->type) { case jbvNull: return 0; case jbvString: return lengthCompareJsonbStringValue(aScalar, bScalar, NULL); case jbvNumeric: return DatumGetInt32(DirectFunctionCall2(numeric_cmp, PointerGetDatum(aScalar->numeric), PointerGetDatum(bScalar->numeric))); case jbvBool: if (aScalar->boolean != bScalar->boolean) return (aScalar->boolean > bScalar->boolean) ? 1 : -1; else return 0; default: elog(ERROR, "invalid jsonb scalar type"); } } elog(ERROR, "jsonb scalar type mismatch"); return -1; } /* * Standard lexical qsort() comparator of jsonb strings. * * Sorts strings lexically, using the default database collation. Used by * B-Tree operators, where a lexical sort order is generally expected. */ static int lexicalCompareJsonbStringValue(const void *a, const void *b) { const JsonbValue *va = (const JsonbValue *) a; const JsonbValue *vb = (const JsonbValue *) b; Assert(va->type == jbvString); Assert(vb->type == jbvString); return varstr_cmp(va->string.val, va->string.len, vb->string.val, vb->string.len, DEFAULT_COLLATION_OID); } /* * Given a JsonbValue, convert to Jsonb and store in preallocated Jsonb buffer * sufficiently large to fit the value */ static Size convertJsonb(JsonbValue * val, Jsonb *buffer) { convertState state; Size len; /* Should not already have binary representation */ Assert(val->type != jbvBinary); state.buffer = buffer; /* Start from superheader */ state.ptr = VARDATA(state.buffer); state.levelSz = 8; state.allState = palloc(sizeof(convertLevel) * state.levelSz); walkJsonbValueConversion(val, &state, 0); len = state.ptr - VARDATA(state.buffer); Assert(len <= val->estSize); return len; } /* * Walk the tree representation of Jsonb, as part of the process of converting * a JsonbValue to a Jsonb. * * This high-level function takes care of recursion into sub-containers, but at * the top level calls putJsonbValueConversion once per sequential processing * token (in a manner similar to generic iteration). */ static void walkJsonbValueConversion(JsonbValue * val, convertState * cstate, uint32 nestlevel) { int i; check_stack_depth(); if (!val) return; switch (val->type) { case jbvArray: putJsonbValueConversion(cstate, val, WJB_BEGIN_ARRAY, nestlevel); for (i = 0; i < val->array.nElems; i++) { if (IsAJsonbScalar(&val->array.elems[i]) || val->array.elems[i].type == jbvBinary) putJsonbValueConversion(cstate, val->array.elems + i, WJB_ELEM, nestlevel); else walkJsonbValueConversion(val->array.elems + i, cstate, nestlevel + 1); } putJsonbValueConversion(cstate, val, WJB_END_ARRAY, nestlevel); break; case jbvObject: putJsonbValueConversion(cstate, val, WJB_BEGIN_OBJECT, nestlevel); for (i = 0; i < val->object.nPairs; i++) { putJsonbValueConversion(cstate, &val->object.pairs[i].key, WJB_KEY, nestlevel); if (IsAJsonbScalar(&val->object.pairs[i].value) || val->object.pairs[i].value.type == jbvBinary) putJsonbValueConversion(cstate, &val->object.pairs[i].value, WJB_VALUE, nestlevel); else walkJsonbValueConversion(&val->object.pairs[i].value, cstate, nestlevel + 1); } putJsonbValueConversion(cstate, val, WJB_END_OBJECT, nestlevel); break; default: elog(ERROR, "unknown type of jsonb container"); } } /* * walkJsonbValueConversion() worker. Add padding sufficient to int-align our * access to conversion buffer. */ static inline short addPaddingInt(convertState * cstate) { short padlen, p; padlen = INTALIGN(cstate->ptr - VARDATA(cstate->buffer)) - (cstate->ptr - VARDATA(cstate->buffer)); for (p = padlen; p > 0; p--) { *cstate->ptr = '\0'; cstate->ptr++; } return padlen; } /* * walkJsonbValueConversion() worker. * * As part of the process of converting an arbitrary JsonbValue to a Jsonb, * copy over an arbitrary individual JsonbValue. This function may copy any * type of value, even containers (Objects/arrays). However, it is not * responsible for recursive aspects of walking the tree (so only top-level * Object/array details are handled). * * No details about their keys/values/elements are handled recursively - * rather, the function is called as required for the start of an Object/Array, * and the end (i.e. there is one call per sequential processing WJB_* token). */ static void putJsonbValueConversion(convertState * cstate, JsonbValue * val, uint32 flags, uint32 level) { if (level == cstate->levelSz) { cstate->levelSz *= 2; cstate->allState = repalloc(cstate->allState, sizeof(convertLevel) * cstate->levelSz); } cstate->contPtr = cstate->allState + level; if (flags & (WJB_BEGIN_ARRAY | WJB_BEGIN_OBJECT)) { Assert(((flags & WJB_BEGIN_ARRAY) && val->type == jbvArray) || ((flags & WJB_BEGIN_OBJECT) && val->type == jbvObject)); /* Initialize pointer into conversion buffer at this level */ cstate->contPtr->begin = cstate->ptr; addPaddingInt(cstate); /* Initialize everything else at this level */ cstate->contPtr->header = (uint32 *) cstate->ptr; /* Advance past header */ cstate->ptr += sizeof(uint32); cstate->contPtr->meta = (JEntry *) cstate->ptr; cstate->contPtr->i = 0; if (val->type == jbvArray) { *cstate->contPtr->header = val->array.nElems | JB_FARRAY; cstate->ptr += sizeof(JEntry) * val->array.nElems; if (val->array.rawScalar) { Assert(val->array.nElems == 1); Assert(level == 0); *cstate->contPtr->header |= JB_FSCALAR; } } else { *cstate->contPtr->header = val->object.nPairs | JB_FOBJECT; cstate->ptr += sizeof(JEntry) * val->object.nPairs * 2; } } else if (flags & WJB_ELEM) { putScalarConversion(cstate, val, level, cstate->contPtr->i); cstate->contPtr->i++; } else if (flags & WJB_KEY) { Assert(val->type == jbvString); putScalarConversion(cstate, val, level, cstate->contPtr->i * 2); } else if (flags & WJB_VALUE) { putScalarConversion(cstate, val, level, cstate->contPtr->i * 2 + 1); cstate->contPtr->i++; } else if (flags & (WJB_END_ARRAY | WJB_END_OBJECT)) { convertLevel *prevPtr; /* Prev container pointer */ uint32 len, i; Assert(((flags & WJB_END_ARRAY) && val->type == jbvArray) || ((flags & WJB_END_OBJECT) && val->type == jbvObject)); if (level == 0) return; len = cstate->ptr - (char *) cstate->contPtr->begin; prevPtr = cstate->contPtr - 1; if (*prevPtr->header & JB_FARRAY) { i = prevPtr->i; prevPtr->meta[i].header = JENTRY_ISNEST; if (i == 0) prevPtr->meta[0].header |= JENTRY_ISFIRST | len; else prevPtr->meta[i].header |= (prevPtr->meta[i - 1].header & JENTRY_POSMASK) + len; } else if (*prevPtr->header & JB_FOBJECT) { i = 2 * prevPtr->i + 1; /* Value, not key */ prevPtr->meta[i].header = JENTRY_ISNEST; prevPtr->meta[i].header |= (prevPtr->meta[i - 1].header & JENTRY_POSMASK) + len; } else { elog(ERROR, "invalid jsonb container type"); } Assert(cstate->ptr - cstate->contPtr->begin <= val->estSize); prevPtr->i++; } else { elog(ERROR, "unknown flag encountered during jsonb tree walk"); } } /* * As part of the process of converting an arbitrary JsonbValue to a Jsonb, * serialize and copy a scalar value into buffer. * * This is a worker function for putJsonbValueConversion() (itself a worker for * walkJsonbValueConversion()). It handles the details with regard to Jentry * metadata peculiar to each scalar type. */ static void putScalarConversion(convertState * cstate, JsonbValue * scalarVal, uint32 level, uint32 i) { int numlen; short padlen; cstate->contPtr = cstate->allState + level; if (i == 0) cstate->contPtr->meta[0].header = JENTRY_ISFIRST; else cstate->contPtr->meta[i].header = 0; switch (scalarVal->type) { case jbvNull: cstate->contPtr->meta[i].header |= JENTRY_ISNULL; if (i > 0) cstate->contPtr->meta[i].header |= cstate->contPtr->meta[i - 1].header & JENTRY_POSMASK; break; case jbvString: memcpy(cstate->ptr, scalarVal->string.val, scalarVal->string.len); cstate->ptr += scalarVal->string.len; if (i == 0) cstate->contPtr->meta[0].header |= scalarVal->string.len; else cstate->contPtr->meta[i].header |= (cstate->contPtr->meta[i - 1].header & JENTRY_POSMASK) + scalarVal->string.len; break; case jbvNumeric: numlen = VARSIZE_ANY(scalarVal->numeric); padlen = addPaddingInt(cstate); memcpy(cstate->ptr, scalarVal->numeric, numlen); cstate->ptr += numlen; cstate->contPtr->meta[i].header |= JENTRY_ISNUMERIC; if (i == 0) cstate->contPtr->meta[0].header |= padlen + numlen; else cstate->contPtr->meta[i].header |= (cstate->contPtr->meta[i - 1].header & JENTRY_POSMASK) + padlen + numlen; break; case jbvBool: cstate->contPtr->meta[i].header |= (scalarVal->boolean) ? JENTRY_ISTRUE : JENTRY_ISFALSE; if (i > 0) cstate->contPtr->meta[i].header |= cstate->contPtr->meta[i - 1].header & JENTRY_POSMASK; break; default: elog(ERROR, "invalid jsonb scalar type"); } } /* * Given superheader pointer into buffer, initialize iterator. Must be a * container type. */ static void iteratorFromContainerBuf(JsonbIterator * it, JsonbSuperHeader sheader) { uint32 superheader = *(uint32 *) sheader; it->containerType = superheader & (JB_FARRAY | JB_FOBJECT); it->nElems = superheader & JB_CMASK; it->buffer = sheader; /* Array starts just after header */ it->meta = (JEntry *) (sheader + sizeof(uint32)); it->state = jbi_start; switch (it->containerType) { case JB_FARRAY: it->dataProper = (char *) it->meta + it->nElems * sizeof(JEntry); it->isScalar = (superheader & JB_FSCALAR) != 0; /* This is either a "raw scalar", or an array */ Assert(!it->isScalar || it->nElems == 1); break; case JB_FOBJECT: /* * Offset reflects that nElems indicates JsonbPairs in an object. * Each key and each value contain Jentry metadata just the same. */ it->dataProper = (char *) it->meta + it->nElems * sizeof(JEntry) * 2; break; default: elog(ERROR, "unknown type of jsonb container"); } } /* * JsonbIteratorNext() worker * * Returns bool indicating if v was a non-jbvBinary container, and thus if * further recursion is required by caller (according to its skipNested * preference). If it is required, we set the caller's iterator for further * recursion into the nested value. If we're going to skip nested items, just * set v to a jbvBinary value, but don't set caller's iterator. * * Unlike with containers (either in this function or in any * JsonbIteratorNext() infrastructure), we fully convert from what is * ultimately a Jsonb on-disk representation, to a JsonbValue in-memory * representation (for scalar values only). JsonbIteratorNext() initializes * container Jsonbvalues, but without a sane private buffer. For scalar values * it has to be done for real (even if we don't actually allocate more memory * to do this. The point is that our JsonbValues scalars can be passed around * anywhere). */ static bool formIterIsContainer(JsonbIterator ** it, JsonbValue * val, JEntry * ent, bool skipNested) { if (JBE_ISNULL(*ent)) { val->type = jbvNull; val->estSize = sizeof(JEntry); return false; } else if (JBE_ISSTRING(*ent)) { val->type = jbvString; val->string.val = (*it)->dataProper + JBE_OFF(*ent); val->string.len = JBE_LEN(*ent); val->estSize = sizeof(JEntry) + val->string.len; return false; } else if (JBE_ISNUMERIC(*ent)) { val->type = jbvNumeric; val->numeric = (Numeric) ((*it)->dataProper + INTALIGN(JBE_OFF(*ent))); val->estSize = 2 * sizeof(JEntry) + VARSIZE_ANY(val->numeric); return false; } else if (JBE_ISBOOL(*ent)) { val->type = jbvBool; val->boolean = JBE_ISBOOL_TRUE(*ent) != 0; val->estSize = sizeof(JEntry); return false; } else if (skipNested) { val->type = jbvBinary; val->binary.data = (*it)->dataProper + INTALIGN(JBE_OFF(*ent)); val->binary.len = JBE_LEN(*ent) - (INTALIGN(JBE_OFF(*ent)) - JBE_OFF(*ent)); val->estSize = val->binary.len + 2 * sizeof(JEntry); return false; } else { /* * Must be container type, so setup caller's iterator to point to that, * and return indication of that. * * Get child iterator. */ JsonbIterator *child = palloc(sizeof(JsonbIterator)); iteratorFromContainerBuf(child, (*it)->dataProper + INTALIGN(JBE_OFF(*ent))); child->parent = *it; *it = child; return true; } } /* * JsonbIteratorNext() worker: Return parent, while freeing memory for current * iterator */ static JsonbIterator * freeAndGetParent(JsonbIterator * it) { JsonbIterator *v = it->parent; pfree(it); return v; } /* * pushJsonbValue() worker: Iteration-like forming of Jsonb */ static JsonbParseState * pushState(JsonbParseState ** pstate) { JsonbParseState *ns = palloc(sizeof(JsonbParseState)); ns->next = *pstate; return ns; } /* * pushJsonbValue() worker: Append a pair key to state when generating a Jsonb */ static void appendKey(JsonbParseState * pstate, JsonbValue * string) { JsonbValue *object = &pstate->contVal; Assert(object->type == jbvObject); Assert(string->type == jbvString); if (object->object.nPairs >= JSONB_MAX_PAIRS) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of jsonb object pairs exceeds the maximum allowed (%zu)", JSONB_MAX_PAIRS))); if (object->object.nPairs >= pstate->size) { pstate->size *= 2; object->object.pairs = repalloc(object->object.pairs, sizeof(JsonbPair) * pstate->size); } object->object.pairs[object->object.nPairs].key = *string; object->object.pairs[object->object.nPairs].order = object->object.nPairs; object->estSize += string->estSize; } /* * pushJsonbValue() worker: Append a pair value to state when generating a * Jsonb */ static void appendValue(JsonbParseState * pstate, JsonbValue * scalarVal) { JsonbValue *object = &pstate->contVal; Assert(object->type == jbvObject); object->object.pairs[object->object.nPairs++].value = *scalarVal; object->estSize += scalarVal->estSize; } /* * pushJsonbValue() worker: Append an element to state when generating a Jsonb */ static void appendElement(JsonbParseState * pstate, JsonbValue * scalarVal) { JsonbValue *array = &pstate->contVal; Assert(array->type == jbvArray); if (array->array.nElems >= JSONB_MAX_ELEMS) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of jsonb array elements exceeds the maximum allowed (%zu)", JSONB_MAX_ELEMS))); if (array->array.nElems >= pstate->size) { pstate->size *= 2; array->array.elems = repalloc(array->array.elems, sizeof(JsonbValue) * pstate->size); } array->array.elems[array->array.nElems++] = *scalarVal; array->estSize += scalarVal->estSize; } /* * Compare two jbvString JsonbValue values, a and b. * * This is a special qsort_arg() comparator used to sort strings in certain * internal contexts where it is sufficient to have a well-defined sort order. * In particular, object pair keys are sorted according to this criteria to * facilitate cheap binary searches where we don't care about lexical sort * order. * * a and b are first sorted based on their length. If a tie-breaker is * required, only then do we consider string binary equality. * * Third argument 'binequal' may point to a bool. If it's set, *binequal is set * to true iff a and b have full binary equality, since some callers have an * interest in whether the two values are equal or merely equivalent. */ static int lengthCompareJsonbStringValue(const void *a, const void *b, void *binequal) { const JsonbValue *va = (const JsonbValue *) a; const JsonbValue *vb = (const JsonbValue *) b; int res; Assert(va->type == jbvString); Assert(vb->type == jbvString); if (va->string.len == vb->string.len) { res = memcmp(va->string.val, vb->string.val, va->string.len); if (res == 0 && binequal) *((bool *) binequal) = true; } else { res = (va->string.len > vb->string.len) ? 1 : -1; } return res; } /* * qsort_arg() comparator to compare JsonbPair values. * * Function implemented in terms of lengthCompareJsonbStringValue(), and thus the * same "arg setting" hack will be applied here in respect of the pair's key * values. * * N.B: String comparisons here are "length-wise" * * Pairs with equals keys are ordered such that the order field is respected. */ static int lengthCompareJsonbPair(const void *a, const void *b, void *binequal) { const JsonbPair *pa = (const JsonbPair *) a; const JsonbPair *pb = (const JsonbPair *) b; int res; res = lengthCompareJsonbStringValue(&pa->key, &pb->key, binequal); /* * Guarantee keeping order of equal pair. Unique algorithm will prefer * first element as value. */ if (res == 0) res = (pa->order > pb->order) ? -1 : 1; return res; } /* * Sort and unique-ify pairs in JsonbValue object */ static void uniqueifyJsonbObject(JsonbValue * object) { bool hasNonUniq = false; Assert(object->type == jbvObject); if (object->object.nPairs > 1) qsort_arg(object->object.pairs, object->object.nPairs, sizeof(JsonbPair), lengthCompareJsonbPair, &hasNonUniq); if (hasNonUniq) { JsonbPair *ptr = object->object.pairs + 1, *res = object->object.pairs; while (ptr - object->object.pairs < object->object.nPairs) { /* Avoid copying over duplicate */ if (lengthCompareJsonbStringValue(ptr, res, NULL) == 0) { object->estSize -= ptr->key.estSize + ptr->value.estSize; } else { res++; if (ptr != res) memcpy(res, ptr, sizeof(JsonbPair)); } ptr++; } object->object.nPairs = res + 1 - object->object.pairs; } } /* * Sort and unique-ify JsonbArray. * * Sorting uses internal ordering. */ static void uniqueifyJsonbArray(JsonbValue * array) { bool hasNonUniq = false; Assert(array->type == jbvArray); /* * Actually sort values, determining if any were equal on the basis of full * binary equality (rather than just having the same string length). */ if (array->array.nElems > 1) qsort_arg(array->array.elems, array->array.nElems, sizeof(JsonbValue), lengthCompareJsonbStringValue, &hasNonUniq); if (hasNonUniq) { JsonbValue *ptr = array->array.elems + 1, *res = array->array.elems; while (ptr - array->array.elems < array->array.nElems) { /* Avoid copying over duplicate */ if (lengthCompareJsonbStringValue(ptr, res, NULL) != 0) { res++; *res = *ptr; } ptr++; } array->array.nElems = res + 1 - array->array.elems; } }