]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/jsonb_util.c
Fix initialization of fake LSN for unlogged relations
[postgresql] / src / backend / utils / adt / jsonb_util.c
index a9bf0d3a7d1aee87d79df09fda9327586cdfb208..8b739031609d223d40d15d25b17c5d23b7207646 100644 (file)
@@ -1,9 +1,9 @@
 /*-------------------------------------------------------------------------
  *
  * jsonb_util.c
- *       Utilities for jsonb datatype
+ *       converting between Jsonb and JsonbValues, and iterating.
  *
- * Copyright (c) 2014, PostgreSQL Global Development Group
+ * Copyright (c) 2014-2019, PostgreSQL Global Development Group
  *
  *
  * IDENTIFICATION
  */
 #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/datetime.h"
+#include "utils/hashutils.h"
+#include "utils/jsonapi.h"
 #include "utils/jsonb.h"
 #include "utils/memutils.h"
+#include "utils/varlena.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)
+ * Maximum number of elements in an array (or key/value pairs in an object).
+ * This is limited by two things: the size of the JEntry array must fit
+ * in MaxAllocSize, and the number of elements (or pairs) must fit in the bits
+ * reserved for that in the JsonbContainer.header field.
  *
- * 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
+ * (The total size of an array's or object's elements is also limited by
+ * JENTRY_OFFLENMASK, but we're not concerned about that here.)
  */
-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;
+#define JSONB_MAX_ELEMS (Min(MaxAllocSize / sizeof(JsonbValue), JB_CMASK))
+#define JSONB_MAX_PAIRS (Min(MaxAllocSize / sizeof(JsonbPair), JB_CMASK))
 
+static void fillJsonbValue(JsonbContainer *container, int index,
+                                                  char *base_addr, uint32 offset,
+                                                  JsonbValue *result);
+static bool equalsJsonbScalarValue(JsonbValue *a, JsonbValue *b);
 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 Jsonb *convertToJsonb(JsonbValue *val);
+static void convertJsonbValue(StringInfo buffer, JEntry *header, JsonbValue *val, int level);
+static void convertJsonbArray(StringInfo buffer, JEntry *header, JsonbValue *val, int level);
+static void convertJsonbObject(StringInfo buffer, JEntry *header, JsonbValue *val, int level);
+static void convertJsonbScalar(StringInfo buffer, JEntry *header, JsonbValue *scalarVal);
+
+static int     reserveFromBuffer(StringInfo buffer, int len);
+static void appendToBuffer(StringInfo buffer, const char *data, int len);
+static void copyToBuffer(StringInfo buffer, int offset, const char *data, int len);
+static short padBufferToInt(StringInfo buffer);
+
+static JsonbIterator *iteratorFromContainer(JsonbContainer *container, JsonbIterator *parent);
 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     lengthCompareJsonbStringValue(const void *a, const void *b);
+static int     lengthCompareJsonbString(const char *val1, int len1,
+                                                                        const char *val2, int len2);
 static int     lengthCompareJsonbPair(const void *a, const void *b, void *arg);
 static void uniqueifyJsonbObject(JsonbValue *object);
-static void uniqueifyJsonbArray(JsonbValue *array);
+static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
+                                                                               JsonbIteratorToken seq,
+                                                                               JsonbValue *scalarVal);
 
 /*
  * 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.
+ * really convert nested scalar values.  JsonbIteratorNext() 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))
        {
@@ -127,17 +100,11 @@ JsonbValueToJsonb(JsonbValue *val)
                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);
+               out = convertToJsonb(res);
        }
        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);
+               out = convertToJsonb(val);
        }
        else
        {
@@ -150,6 +117,58 @@ JsonbValueToJsonb(JsonbValue *val)
        return out;
 }
 
+/*
+ * Get the offset of the variable-length portion of a Jsonb node within
+ * the variable-length-data part of its container.  The node is identified
+ * by index within the container's JEntry array.
+ */
+uint32
+getJsonbOffset(const JsonbContainer *jc, int index)
+{
+       uint32          offset = 0;
+       int                     i;
+
+       /*
+        * Start offset of this entry is equal to the end offset of the previous
+        * entry.  Walk backwards to the most recent entry stored as an end
+        * offset, returning that offset plus any lengths in between.
+        */
+       for (i = index - 1; i >= 0; i--)
+       {
+               offset += JBE_OFFLENFLD(jc->children[i]);
+               if (JBE_HAS_OFF(jc->children[i]))
+                       break;
+       }
+
+       return offset;
+}
+
+/*
+ * Get the length of the variable-length portion of a Jsonb node.
+ * The node is identified by index within the container's JEntry array.
+ */
+uint32
+getJsonbLength(const JsonbContainer *jc, int index)
+{
+       uint32          off;
+       uint32          len;
+
+       /*
+        * If the length is stored directly in the JEntry, just return it.
+        * Otherwise, get the begin offset of the entry, and subtract that from
+        * the stored end+1 offset.
+        */
+       if (JBE_HAS_OFF(jc->children[index]))
+       {
+               off = getJsonbOffset(jc, index);
+               len = JBE_OFFLENFLD(jc->children[index]) - off;
+       }
+       else
+               len = JBE_OFFLENFLD(jc->children[index]);
+
+       return len;
+}
+
 /*
  * 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
@@ -161,7 +180,7 @@ JsonbValueToJsonb(JsonbValue *val)
  * memory here.
  */
 int
-compareJsonbSuperHeaderValue(JsonbSuperHeader a, JsonbSuperHeader b)
+compareJsonbContainers(JsonbContainer *a, JsonbContainer *b)
 {
        JsonbIterator *ita,
                           *itb;
@@ -174,19 +193,12 @@ compareJsonbSuperHeaderValue(JsonbSuperHeader a, JsonbSuperHeader b)
        {
                JsonbValue      va,
                                        vb;
-               int                     ra,
+               JsonbIteratorToken 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)
@@ -195,13 +207,22 @@ compareJsonbSuperHeaderValue(JsonbSuperHeader a, JsonbSuperHeader b)
                                break;
                        }
 
+                       if (ra == WJB_END_ARRAY || ra == WJB_END_OBJECT)
+                       {
+                               /*
+                                * There is no array or object to compare at this stage of
+                                * processing.  jbvArray/jbvObject values are compared
+                                * initially, at the WJB_BEGIN_ARRAY and WJB_BEGIN_OBJECT
+                                * tokens.
+                                */
+                               continue;
+                       }
+
                        if (va.type == vb.type)
                        {
                                switch (va.type)
                                {
                                        case jbvString:
-                                               res = lexicalCompareJsonbStringValue(&va, &vb);
-                                               break;
                                        case jbvNull:
                                        case jbvNumeric:
                                        case jbvBool:
@@ -226,6 +247,10 @@ compareJsonbSuperHeaderValue(JsonbSuperHeader a, JsonbSuperHeader b)
                                                break;
                                        case jbvBinary:
                                                elog(ERROR, "unexpected jbvBinary value");
+                                               break;
+                                       case jbvDatetime:
+                                               elog(ERROR, "unexpected jbvDatetime value");
+                                               break;
                                }
                        }
                        else
@@ -237,19 +262,26 @@ compareJsonbSuperHeaderValue(JsonbSuperHeader a, JsonbSuperHeader b)
                else
                {
                        /*
-                        * It's safe to assume that the types differed.
+                        * It's safe to assume that the types differed, and that the va
+                        * and vb values passed were set.
                         *
-                        * If the two values were the same container type, then there'd
+                        * If the two values were of 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.
+                        * elements/pairs (when processing WJB_BEGIN_OBJECT, say). They're
+                        * either two heterogeneously-typed containers, or a container and
+                        * some scalar type.
+                        *
+                        * We don't have to consider the WJB_END_ARRAY and WJB_END_OBJECT
+                        * cases here, because we would have seen the corresponding
+                        * WJB_BEGIN_ARRAY and WJB_BEGIN_OBJECT tokens first, and
+                        * concluded that they don't match.
                         */
+                       Assert(ra != WJB_END_ARRAY && ra != WJB_END_OBJECT);
+                       Assert(rb != WJB_END_ARRAY && rb != WJB_END_OBJECT);
+
                        Assert(va.type != vb.type);
-                       Assert(va.type == jbvArray || va.type == jbvObject);
-                       Assert(vb.type == jbvArray || vb.type == jbvObject);
+                       Assert(va.type != jbvBinary);
+                       Assert(vb.type != jbvBinary);
                        /* Type-defined order */
                        res = (va.type > vb.type) ? 1 : -1;
                }
@@ -288,249 +320,221 @@ compareJsonbSuperHeaderValue(JsonbSuperHeader a, JsonbSuperHeader b)
  *
  * 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
+ * appropriate flag, as well as having the pointed-to Jsonb container 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.)
+ * most one can make sense, because the container either points to an array
+ * (possibly 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,
+ * a container type that is not of the type pointed to by the container,
  * 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)
+findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
+                                                       JsonbValue *key)
 {
-       uint32          superheader = *(uint32 *) sheader;
-       JEntry     *array = (JEntry *) (sheader + sizeof(uint32));
-       int                     count = (superheader & JB_CMASK);
-       JsonbValue *result = palloc(sizeof(JsonbValue));
+       JEntry     *children = container->children;
+       int                     count = JsonContainerSize(container);
 
        Assert((flags & ~(JB_FARRAY | JB_FOBJECT)) == 0);
 
-       if (flags & JB_FARRAY & superheader)
+       /* Quick out without a palloc cycle if object/array is empty */
+       if (count <= 0)
+               return NULL;
+
+       if ((flags & JB_FARRAY) && JsonContainerIsArray(container))
        {
-               char       *data = (char *) (array + (superheader & JB_CMASK));
+               JsonbValue *result = palloc(sizeof(JsonbValue));
+               char       *base_addr = (char *) (children + count);
+               uint32          offset = 0;
                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->val.string.val = data + JBE_OFF(*e);
-                               result->val.string.len = JBE_LEN(*e);
-                               result->estSize = sizeof(JEntry) + result->val.string.len;
-                       }
-                       else if (JBE_ISNUMERIC(*e) && key->type == jbvNumeric)
-                       {
-                               result->type = jbvNumeric;
-                               result->val.numeric = (Numeric) (data + INTALIGN(JBE_OFF(*e)));
+                       fillJsonbValue(container, i, base_addr, offset, result);
 
-                               result->estSize = 2 * sizeof(JEntry) +
-                                       VARSIZE_ANY(result->val.numeric);
-                       }
-                       else if (JBE_ISBOOL(*e) && key->type == jbvBool)
+                       if (key->type == result->type)
                        {
-                               result->type = jbvBool;
-                               result->val.boolean = JBE_ISBOOL_TRUE(*e) != 0;
-                               result->estSize = sizeof(JEntry);
+                               if (equalsJsonbScalarValue(key, result))
+                                       return result;
                        }
-                       else
-                               continue;
 
-                       if (compareJsonbScalarValue(key, result) == 0)
-                               return result;
+                       JBE_ADVANCE_OFFSET(offset, children[i]);
                }
+
+               pfree(result);
        }
-       else if (flags & JB_FOBJECT & superheader)
+       else if ((flags & JB_FOBJECT) && JsonContainerIsObject(container))
        {
-               /* 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 */
+               /* Object key passed 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;
+               return getKeyJsonValueFromContainer(container, key->val.string.val,
+                                                                                       key->val.string.len, NULL);
+       }
 
-                       /*
-                        * Note how we compensate for the fact that we're iterating
-                        * through pairs (not entries) throughout.
-                        */
-                       stopMiddle = stopLow + (count - stopLow) / 2;
+       /* Not found */
+       return NULL;
+}
 
-                       entry = array + stopMiddle * 2;
+/*
+ * Find value by key in Jsonb object and fetch it into 'res', which is also
+ * returned.
+ *
+ * 'res' can be passed in as NULL, in which case it's newly palloc'ed here.
+ */
+JsonbValue *
+getKeyJsonValueFromContainer(JsonbContainer *container,
+                                                        const char *keyVal, int keyLen, JsonbValue *res)
+{
+       JEntry     *children = container->children;
+       int                     count = JsonContainerSize(container);
+       char       *baseAddr;
+       uint32          stopLow,
+                               stopHigh;
 
-                       candidate.type = jbvString;
-                       candidate.val.string.val = data + JBE_OFF(*entry);
-                       candidate.val.string.len = JBE_LEN(*entry);
-                       candidate.estSize = sizeof(JEntry) + candidate.val.string.len;
+       Assert(JsonContainerIsObject(container));
 
-                       difference = lengthCompareJsonbStringValue(&candidate, key, NULL);
+       /* Quick out without a palloc cycle if object is empty */
+       if (count <= 0)
+               return NULL;
 
-                       if (difference == 0)
-                       {
-                               /* Found our value (from key/value pair) */
-                               JEntry     *v = entry + 1;
+       /*
+        * Binary search the container. Since we know this is an object, account
+        * for *Pairs* of Jentrys
+        */
+       baseAddr = (char *) (children + count * 2);
+       stopLow = 0;
+       stopHigh = count;
+       while (stopLow < stopHigh)
+       {
+               uint32          stopMiddle;
+               int                     difference;
+               const char *candidateVal;
+               int                     candidateLen;
 
-                               if (lowbound)
-                                       *lowbound = stopMiddle + 1;
+               stopMiddle = stopLow + (stopHigh - stopLow) / 2;
 
-                               if (JBE_ISNULL(*v))
-                               {
-                                       result->type = jbvNull;
-                                       result->estSize = sizeof(JEntry);
-                               }
-                               else if (JBE_ISSTRING(*v))
-                               {
-                                       result->type = jbvString;
-                                       result->val.string.val = data + JBE_OFF(*v);
-                                       result->val.string.len = JBE_LEN(*v);
-                                       result->estSize = sizeof(JEntry) + result->val.string.len;
-                               }
-                               else if (JBE_ISNUMERIC(*v))
-                               {
-                                       result->type = jbvNumeric;
-                                       result->val.numeric = (Numeric) (data + INTALIGN(JBE_OFF(*v)));
+               candidateVal = baseAddr + getJsonbOffset(container, stopMiddle);
+               candidateLen = getJsonbLength(container, stopMiddle);
 
-                                       result->estSize = 2 * sizeof(JEntry) +
-                                               VARSIZE_ANY(result->val.numeric);
-                               }
-                               else if (JBE_ISBOOL(*v))
-                               {
-                                       result->type = jbvBool;
-                                       result->val.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->val.binary.data = data + INTALIGN(JBE_OFF(*v));
-                                       result->val.binary.len = JBE_LEN(*v) -
-                                               (INTALIGN(JBE_OFF(*v)) - JBE_OFF(*v));
-                                       result->estSize = 2 * sizeof(JEntry) + result->val.binary.len;
-                               }
+               difference = lengthCompareJsonbString(candidateVal, candidateLen,
+                                                                                         keyVal, keyLen);
 
-                               return result;
-                       }
+               if (difference == 0)
+               {
+                       /* Found our key, return corresponding value */
+                       int                     index = stopMiddle + count;
+
+                       if (!res)
+                               res = palloc(sizeof(JsonbValue));
+
+                       fillJsonbValue(container, index, baseAddr,
+                                                  getJsonbOffset(container, index),
+                                                  res);
+
+                       return res;
+               }
+               else
+               {
+                       if (difference < 0)
+                               stopLow = stopMiddle + 1;
                        else
-                       {
-                               if (difference < 0)
-                                       stopLow = stopMiddle + 1;
-                               else
-                                       count = stopMiddle;
-                       }
+                               stopHigh = stopMiddle;
                }
-
-               if (lowbound)
-                       *lowbound = stopLow;
        }
 
        /* Not found */
-       pfree(result);
        return NULL;
 }
 
 /*
- * Get i-th value of Jsonb array from superheader.
+ * Get i-th value of a Jsonb array.
  *
- * Returns palloc()'d copy of value.
+ * Returns palloc()'d copy of the value, or NULL if it does not exist.
  */
 JsonbValue *
-getIthJsonbValueFromSuperHeader(JsonbSuperHeader sheader, uint32 i)
+getIthJsonbValueFromContainer(JsonbContainer *container, uint32 i)
 {
-       uint32          superheader = *(uint32 *) sheader;
        JsonbValue *result;
-       JEntry     *array,
-                          *e;
-       char       *data;
+       char       *base_addr;
+       uint32          nelements;
 
-       result = palloc(sizeof(JsonbValue));
+       if (!JsonContainerIsArray(container))
+               elog(ERROR, "not a jsonb array");
 
-       if (i >= (superheader & JB_CMASK))
+       nelements = JsonContainerSize(container);
+       base_addr = (char *) &container->children[nelements];
+
+       if (i >= nelements)
                return NULL;
 
-       array = (JEntry *) (sheader + sizeof(uint32));
+       result = palloc(sizeof(JsonbValue));
 
-       if (superheader & JB_FARRAY)
-       {
-               e = array + i;
-               data = (char *) (array + (superheader & JB_CMASK));
-       }
-       else
-       {
-               elog(ERROR, "not a jsonb array");
-       }
+       fillJsonbValue(container, i, base_addr,
+                                  getJsonbOffset(container, i),
+                                  result);
+
+       return result;
+}
+
+/*
+ * A helper function to fill in a JsonbValue to represent an element of an
+ * array, or a key or value of an object.
+ *
+ * The node's JEntry is at container->children[index], and its variable-length
+ * data is at base_addr + offset.  We make the caller determine the offset
+ * since in many cases the caller can amortize that work across multiple
+ * children.  When it can't, it can just call getJsonbOffset().
+ *
+ * A nested array or object will be returned as jbvBinary, ie. it won't be
+ * expanded.
+ */
+static void
+fillJsonbValue(JsonbContainer *container, int index,
+                          char *base_addr, uint32 offset,
+                          JsonbValue *result)
+{
+       JEntry          entry = container->children[index];
 
-       if (JBE_ISNULL(*e))
+       if (JBE_ISNULL(entry))
        {
                result->type = jbvNull;
-               result->estSize = sizeof(JEntry);
        }
-       else if (JBE_ISSTRING(*e))
+       else if (JBE_ISSTRING(entry))
        {
                result->type = jbvString;
-               result->val.string.val = data + JBE_OFF(*e);
-               result->val.string.len = JBE_LEN(*e);
-               result->estSize = sizeof(JEntry) + result->val.string.len;
+               result->val.string.val = base_addr + offset;
+               result->val.string.len = getJsonbLength(container, index);
+               Assert(result->val.string.len >= 0);
        }
-       else if (JBE_ISNUMERIC(*e))
+       else if (JBE_ISNUMERIC(entry))
        {
                result->type = jbvNumeric;
-               result->val.numeric = (Numeric) (data + INTALIGN(JBE_OFF(*e)));
-
-               result->estSize = 2 * sizeof(JEntry) + VARSIZE_ANY(result->val.numeric);
+               result->val.numeric = (Numeric) (base_addr + INTALIGN(offset));
+       }
+       else if (JBE_ISBOOL_TRUE(entry))
+       {
+               result->type = jbvBool;
+               result->val.boolean = true;
        }
-       else if (JBE_ISBOOL(*e))
+       else if (JBE_ISBOOL_FALSE(entry))
        {
                result->type = jbvBool;
-               result->val.boolean = JBE_ISBOOL_TRUE(*e) != 0;
-               result->estSize = sizeof(JEntry);
+               result->val.boolean = false;
        }
        else
        {
+               Assert(JBE_ISCONTAINER(entry));
                result->type = jbvBinary;
-               result->val.binary.data = data + INTALIGN(JBE_OFF(*e));
-               result->val.binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
-               result->estSize = result->val.binary.len + 2 * sizeof(JEntry);
+               /* Remove alignment padding from data pointer and length */
+               result->val.binary.data = (JsonbContainer *) (base_addr + INTALIGN(offset));
+               result->val.binary.len = getJsonbLength(container, index) -
+                       (INTALIGN(offset) - offset);
        }
-
-       return result;
 }
 
 /*
@@ -544,10 +548,44 @@ getIthJsonbValueFromSuperHeader(JsonbSuperHeader sheader, uint32 i)
  *
  * 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.
+ * "raw scalar" pseudo array to append it - the actual scalar should be passed
+ * next and it will be added as the only member of the array.
+ *
+ * Values of type jbvBinary, which are rolled up arrays and objects,
+ * are unpacked before being added to the result.
  */
 JsonbValue *
-pushJsonbValue(JsonbParseState **pstate, int seq, JsonbValue *scalarVal)
+pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
+                          JsonbValue *jbval)
+{
+       JsonbIterator *it;
+       JsonbValue *res = NULL;
+       JsonbValue      v;
+       JsonbIteratorToken tok;
+
+       if (!jbval || (seq != WJB_ELEM && seq != WJB_VALUE) ||
+               jbval->type != jbvBinary)
+       {
+               /* drop through */
+               return pushJsonbValueScalar(pstate, seq, jbval);
+       }
+
+       /* unpack the binary and add each piece to the pstate */
+       it = JsonbIteratorInit(jbval->val.binary.data);
+       while ((tok = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
+               res = pushJsonbValueScalar(pstate, tok,
+                                                                  tok < WJB_BEGIN_ARRAY ? &v : NULL);
+
+       return res;
+}
+
+/*
+ * Do the actual pushing, with only scalar or pseudo-scalar-array values
+ * accepted.
+ */
+static JsonbValue *
+pushJsonbValueScalar(JsonbParseState **pstate, JsonbIteratorToken seq,
+                                        JsonbValue *scalarVal)
 {
        JsonbValue *result = NULL;
 
@@ -558,10 +596,9 @@ pushJsonbValue(JsonbParseState **pstate, int seq, JsonbValue *scalarVal)
                        *pstate = pushState(pstate);
                        result = &(*pstate)->contVal;
                        (*pstate)->contVal.type = jbvArray;
-                       (*pstate)->contVal.estSize = 3 * sizeof(JEntry);
                        (*pstate)->contVal.val.array.nElems = 0;
                        (*pstate)->contVal.val.array.rawScalar = (scalarVal &&
-                                                                                        scalarVal->val.array.rawScalar);
+                                                                                                         scalarVal->val.array.rawScalar);
                        if (scalarVal && scalarVal->val.array.nElems > 0)
                        {
                                /* Assume that this array is still really a scalar */
@@ -580,7 +617,6 @@ pushJsonbValue(JsonbParseState **pstate, int seq, JsonbValue *scalarVal)
                        *pstate = pushState(pstate);
                        result = &(*pstate)->contVal;
                        (*pstate)->contVal.type = jbvObject;
-                       (*pstate)->contVal.estSize = 3 * sizeof(JEntry);
                        (*pstate)->contVal.val.object.nPairs = 0;
                        (*pstate)->size = 4;
                        (*pstate)->contVal.val.object.pairs = palloc(sizeof(JsonbPair) *
@@ -591,17 +627,16 @@ pushJsonbValue(JsonbParseState **pstate, int seq, JsonbValue *scalarVal)
                        appendKey(*pstate, scalarVal);
                        break;
                case WJB_VALUE:
-                       Assert(IsAJsonbScalar(scalarVal) ||
-                                  scalarVal->type == jbvBinary);
+                       Assert(IsAJsonbScalar(scalarVal));
                        appendValue(*pstate, scalarVal);
                        break;
                case WJB_ELEM:
-                       Assert(IsAJsonbScalar(scalarVal) ||
-                                  scalarVal->type == jbvBinary);
+                       Assert(IsAJsonbScalar(scalarVal));
                        appendElement(*pstate, scalarVal);
                        break;
                case WJB_END_OBJECT:
                        uniqueifyJsonbObject(&(*pstate)->contVal);
+                       /* fall through! */
                case WJB_END_ARRAY:
                        /* Steps here common to WJB_END_OBJECT case */
                        Assert(!scalarVal);
@@ -635,20 +670,95 @@ pushJsonbValue(JsonbParseState **pstate, int seq, JsonbValue *scalarVal)
 }
 
 /*
- * Given a Jsonb superheader, expand to JsonbIterator to iterate over items
+ * 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->val.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->val.object.nPairs >= pstate->size)
+       {
+               pstate->size *= 2;
+               object->val.object.pairs = repalloc(object->val.object.pairs,
+                                                                                       sizeof(JsonbPair) * pstate->size);
+       }
+
+       object->val.object.pairs[object->val.object.nPairs].key = *string;
+       object->val.object.pairs[object->val.object.nPairs].order = object->val.object.nPairs;
+}
+
+/*
+ * 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->val.object.pairs[object->val.object.nPairs++].value = *scalarVal;
+}
+
+/*
+ * 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->val.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->val.array.nElems >= pstate->size)
+       {
+               pstate->size *= 2;
+               array->val.array.elems = repalloc(array->val.array.elems,
+                                                                                 sizeof(JsonbValue) * pstate->size);
+       }
+
+       array->val.array.elems[array->val.array.nElems++] = *scalarVal;
+}
+
+/*
+ * Given a JsonbContainer, 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)
+JsonbIteratorInit(JsonbContainer *container)
 {
-       JsonbIterator *it = palloc(sizeof(JsonbIterator));
-
-       iteratorFromContainerBuf(it, sheader);
-       it->parent = NULL;
-
-       return it;
+       return iteratorFromContainer(container, NULL);
 }
 
 /*
@@ -677,26 +787,26 @@ JsonbIteratorInit(JsonbSuperHeader sheader)
  * 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.
+ * garbage.  Also, *val will not be set when returning WJB_END_ARRAY or
+ * WJB_END_OBJECT, on the assumption that it's only useful to access values
+ * when recursing in.
  */
-int
+JsonbIteratorToken
 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)
+       /*
+        * When stepping into a nested container, we jump back here to start
+        * processing the child. We will not recurse further in one call, because
+        * processing the child will always begin in JBI_ARRAY_START or
+        * JBI_OBJECT_START state.
+        */
+recurse:
+       switch ((*it)->state)
        {
-               if (state == jbi_start)
-               {
+               case JBI_ARRAY_START:
                        /* Set v to array on first array call */
                        val->type = jbvArray;
                        val->val.array.nElems = (*it)->nElems;
@@ -706,14 +816,15 @@ JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
                         * a full conversion
                         */
                        val->val.array.rawScalar = (*it)->isScalar;
-                       (*it)->i = 0;
+                       (*it)->curIndex = 0;
+                       (*it)->curDataOffset = 0;
+                       (*it)->curValueOffset = 0;      /* not actually used */
                        /* Set state for next call */
-                       (*it)->state = jbi_elem;
+                       (*it)->state = JBI_ARRAY_ELEM;
                        return WJB_BEGIN_ARRAY;
-               }
-               else if (state == jbi_elem)
-               {
-                       if ((*it)->i >= (*it)->nElems)
+
+               case JBI_ARRAY_ELEM:
+                       if ((*it)->curIndex >= (*it)->nElems)
                        {
                                /*
                                 * All elements within array already processed.  Report this
@@ -724,27 +835,31 @@ JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
                                *it = freeAndGetParent(*it);
                                return WJB_END_ARRAY;
                        }
-                       else if (formIterIsContainer(it, val, &(*it)->meta[(*it)->i++],
-                                                                                skipNested))
+
+                       fillJsonbValue((*it)->container, (*it)->curIndex,
+                                                  (*it)->dataProper, (*it)->curDataOffset,
+                                                  val);
+
+                       JBE_ADVANCE_OFFSET((*it)->curDataOffset,
+                                                          (*it)->children[(*it)->curIndex]);
+                       (*it)->curIndex++;
+
+                       if (!IsAJsonbScalar(val) && !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);
+                               /* Recurse into container. */
+                               *it = iteratorFromContainer(val->val.binary.data, *it);
+                               goto recurse;
                        }
                        else
                        {
-                               /* Scalar item in array */
+                               /*
+                                * Scalar item in array, or a container and caller didn't want
+                                * us to recurse into it.
+                                */
                                return WJB_ELEM;
                        }
-               }
-       }
-       else if ((*it)->containerType == JB_FOBJECT)
-       {
-               if (state == jbi_start)
-               {
+
+               case JBI_OBJECT_START:
                        /* Set v to object on first object call */
                        val->type = jbvObject;
                        val->val.object.nPairs = (*it)->nElems;
@@ -753,14 +868,16 @@ JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
                         * v->val.object.pairs is not actually set, because we aren't
                         * doing a full conversion
                         */
-                       (*it)->i = 0;
+                       (*it)->curIndex = 0;
+                       (*it)->curDataOffset = 0;
+                       (*it)->curValueOffset = getJsonbOffset((*it)->container,
+                                                                                                  (*it)->nElems);
                        /* Set state for next call */
-                       (*it)->state = jbi_key;
+                       (*it)->state = JBI_OBJECT_KEY;
                        return WJB_BEGIN_OBJECT;
-               }
-               else if (state == jbi_key)
-               {
-                       if ((*it)->i >= (*it)->nElems)
+
+               case JBI_OBJECT_KEY:
+                       if ((*it)->curIndex >= (*it)->nElems)
                        {
                                /*
                                 * All pairs within object already processed.  Report this to
@@ -773,43 +890,104 @@ JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
                        }
                        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");
+                               /* Return key of a key/value pair.  */
+                               fillJsonbValue((*it)->container, (*it)->curIndex,
+                                                          (*it)->dataProper, (*it)->curDataOffset,
+                                                          val);
+                               if (val->type != jbvString)
+                                       elog(ERROR, "unexpected jsonb type as object key");
 
-                               Assert(val->type == jbvString);
                                /* Set state for next call */
-                               (*it)->state = jbi_value;
+                               (*it)->state = JBI_OBJECT_VALUE;
                                return WJB_KEY;
                        }
-               }
-               else if (state == jbi_value)
-               {
+
+               case JBI_OBJECT_VALUE:
                        /* Set state for next call */
-                       (*it)->state = jbi_key;
+                       (*it)->state = JBI_OBJECT_KEY;
+
+                       fillJsonbValue((*it)->container, (*it)->curIndex + (*it)->nElems,
+                                                  (*it)->dataProper, (*it)->curValueOffset,
+                                                  val);
+
+                       JBE_ADVANCE_OFFSET((*it)->curDataOffset,
+                                                          (*it)->children[(*it)->curIndex]);
+                       JBE_ADVANCE_OFFSET((*it)->curValueOffset,
+                                                          (*it)->children[(*it)->curIndex + (*it)->nElems]);
+                       (*it)->curIndex++;
 
                        /*
                         * 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.
+                        * child iterator (unless the caller asked not to, by passing
+                        * skipNested).
                         */
-                       if (formIterIsContainer(it, val, &(*it)->meta[((*it)->i++) * 2 + 1],
-                                                                       skipNested))
-                               return JsonbIteratorNext(it, val, skipNested);
+                       if (!IsAJsonbScalar(val) && !skipNested)
+                       {
+                               *it = iteratorFromContainer(val->val.binary.data, *it);
+                               goto recurse;
+                       }
                        else
                                return WJB_VALUE;
-               }
        }
 
        elog(ERROR, "invalid iterator state");
        return -1;
 }
 
+/*
+ * Initialize an iterator for iterating all elements in a container.
+ */
+static JsonbIterator *
+iteratorFromContainer(JsonbContainer *container, JsonbIterator *parent)
+{
+       JsonbIterator *it;
+
+       it = palloc0(sizeof(JsonbIterator));
+       it->container = container;
+       it->parent = parent;
+       it->nElems = JsonContainerSize(container);
+
+       /* Array starts just after header */
+       it->children = container->children;
+
+       switch (container->header & (JB_FARRAY | JB_FOBJECT))
+       {
+               case JB_FARRAY:
+                       it->dataProper =
+                               (char *) it->children + it->nElems * sizeof(JEntry);
+                       it->isScalar = JsonContainerIsScalar(container);
+                       /* This is either a "raw scalar", or an array */
+                       Assert(!it->isScalar || it->nElems == 1);
+
+                       it->state = JBI_ARRAY_START;
+                       break;
+
+               case JB_FOBJECT:
+                       it->dataProper =
+                               (char *) it->children + it->nElems * sizeof(JEntry) * 2;
+                       it->state = JBI_OBJECT_START;
+                       break;
+
+               default:
+                       elog(ERROR, "unknown type of jsonb container");
+       }
+
+       return it;
+}
+
+/*
+ * JsonbIteratorNext() worker: Return parent, while freeing memory for current
+ * iterator
+ */
+static JsonbIterator *
+freeAndGetParent(JsonbIterator *it)
+{
+       JsonbIterator *v = it->parent;
+
+       pfree(it);
+       return v;
+}
+
 /*
  * Worker for "contains" operator's function
  *
@@ -825,10 +1003,10 @@ JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
 bool
 JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
 {
-       uint32          rval,
-                               rcont;
        JsonbValue      vval,
                                vcontained;
+       JsonbIteratorToken rval,
+                               rcont;
 
        /*
         * Guard against stack overflow due to overly complex Jsonb.
@@ -855,13 +1033,25 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
        }
        else if (rcont == WJB_BEGIN_OBJECT)
        {
-               JsonbValue *lhsVal;             /* lhsVal is from pair in lhs object */
-
+               Assert(vval.type == jbvObject);
                Assert(vcontained.type == jbvObject);
 
+               /*
+                * If the lhs has fewer pairs than the rhs, it can't possibly contain
+                * the rhs.  (This conclusion is safe only because we de-duplicate
+                * keys in all Jsonb objects; thus there can be no corresponding
+                * optimization in the array case.)  The case probably won't arise
+                * often, but since it's such a cheap check we may as well make it.
+                */
+               if (vval.val.object.nPairs < vcontained.val.object.nPairs)
+                       return false;
+
                /* Work through rhs "is it contained within?" object */
                for (;;)
                {
+                       JsonbValue *lhsVal; /* lhsVal is from pair in lhs object */
+                       JsonbValue      lhsValBuf;
+
                        rcont = JsonbIteratorNext(mContained, &vcontained, false);
 
                        /*
@@ -873,13 +1063,14 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
                                return true;
 
                        Assert(rcont == WJB_KEY);
+                       Assert(vcontained.type == jbvString);
 
                        /* First, find value by key... */
-                       lhsVal = findJsonbValueFromSuperHeader((*val)->buffer,
-                                                                                                  JB_FOBJECT,
-                                                                                                  NULL,
-                                                                                                  &vcontained);
-
+                       lhsVal =
+                               getKeyJsonValueFromContainer((*val)->container,
+                                                                                        vcontained.val.string.val,
+                                                                                        vcontained.val.string.len,
+                                                                                        &lhsValBuf);
                        if (!lhsVal)
                                return false;
 
@@ -901,7 +1092,7 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
                        }
                        else if (IsAJsonbScalar(lhsVal))
                        {
-                               if (compareJsonbScalarValue(lhsVal, &vcontained) != 0)
+                               if (!equalsJsonbScalarValue(lhsVal, &vcontained))
                                        return false;
                        }
                        else
@@ -946,6 +1137,7 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
                JsonbValue *lhsConts = NULL;
                uint32          nLhsElems = vval.val.array.nElems;
 
+               Assert(vval.type == jbvArray);
                Assert(vcontained.type == jbvArray);
 
                /*
@@ -978,10 +1170,9 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
 
                        if (IsAJsonbScalar(&vcontained))
                        {
-                               if (!findJsonbValueFromSuperHeader((*val)->buffer,
-                                                                                                  JB_FARRAY,
-                                                                                                  NULL,
-                                                                                                  &vcontained))
+                               if (!findJsonbValueFromContainer((*val)->container,
+                                                                                                JB_FARRAY,
+                                                                                                &vcontained))
                                        return false;
                        }
                        else
@@ -1057,64 +1248,7 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
 }
 
 /*
- * 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->val.array.rawScalar = false;
-       result->val.array.elems = palloc(sizeof(JsonbPair) * elem_count);
-
-       for (i = 0, j = 0; i < elem_count; i++)
-       {
-               if (!key_nulls[i])
-               {
-                       result->val.array.elems[j].type = jbvString;
-                       result->val.array.elems[j].val.string.val = VARDATA(key_datums[i]);
-                       result->val.array.elems[j].val.string.len = VARSIZE(key_datums[i]) - VARHDRSZ;
-                       j++;
-               }
-       }
-       result->val.array.nElems = j;
-
-       uniqueifyJsonbArray(result);
-       return result;
-}
-
-/*
- * Hash a JsonbValue scalar value, mixing in the hash value with an existing
+ * Hash a JsonbValue scalar value, mixing the hash value into an existing
  * hash provided by the caller.
  *
  * Some callers may wish to independently XOR in JB_FOBJECT and JB_FARRAY
@@ -1123,7 +1257,32 @@ arrayToJsonbSortedArray(ArrayType *array)
 void
 JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash)
 {
-       int                     tmp;
+       uint32          tmp;
+
+       /* Compute hash value for scalarVal */
+       switch (scalarVal->type)
+       {
+               case jbvNull:
+                       tmp = 0x01;
+                       break;
+               case jbvString:
+                       tmp = DatumGetUInt32(hash_any((const unsigned char *) scalarVal->val.string.val,
+                                                                                 scalarVal->val.string.len));
+                       break;
+               case jbvNumeric:
+                       /* Must hash equal numerics to equal hash codes */
+                       tmp = DatumGetUInt32(DirectFunctionCall1(hash_numeric,
+                                                                                                        NumericGetDatum(scalarVal->val.numeric)));
+                       break;
+               case jbvBool:
+                       tmp = scalarVal->val.boolean ? 0x02 : 0x04;
+
+                       break;
+               default:
+                       elog(ERROR, "invalid jsonb scalar type");
+                       tmp = 0;                        /* keep compiler quiet */
+                       break;
+       }
 
        /*
         * Combine hash values of successive keys, values and elements by rotating
@@ -1131,36 +1290,86 @@ JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash)
         * key/value/element's hash value.
         */
        *hash = (*hash << 1) | (*hash >> 31);
+       *hash ^= tmp;
+}
+
+/*
+ * Hash a value to a 64-bit value, with a seed. Otherwise, similar to
+ * JsonbHashScalarValue.
+ */
+void
+JsonbHashScalarValueExtended(const JsonbValue *scalarVal, uint64 *hash,
+                                                        uint64 seed)
+{
+       uint64          tmp;
+
        switch (scalarVal->type)
        {
                case jbvNull:
-                       *hash ^= 0x01;
-                       return;
+                       tmp = seed + 0x01;
+                       break;
                case jbvString:
-                       tmp = hash_any((unsigned char *) scalarVal->val.string.val,
-                                                  scalarVal->val.string.len);
-                       *hash ^= tmp;
-                       return;
+                       tmp = DatumGetUInt64(hash_any_extended((const unsigned char *) scalarVal->val.string.val,
+                                                                                                  scalarVal->val.string.len,
+                                                                                                  seed));
+                       break;
                case jbvNumeric:
-                       /* Must be unaffected by trailing zeroes */
-                       tmp = DatumGetInt32(DirectFunctionCall1(hash_numeric,
-                                                                  NumericGetDatum(scalarVal->val.numeric)));
-                       *hash ^= tmp;
-                       return;
+                       tmp = DatumGetUInt64(DirectFunctionCall2(hash_numeric_extended,
+                                                                                                        NumericGetDatum(scalarVal->val.numeric),
+                                                                                                        UInt64GetDatum(seed)));
+                       break;
                case jbvBool:
-                       *hash ^= scalarVal->val.boolean ? 0x02 : 0x04;
-                       return;
+                       if (seed)
+                               tmp = DatumGetUInt64(DirectFunctionCall2(hashcharextended,
+                                                                                                                BoolGetDatum(scalarVal->val.boolean),
+                                                                                                                UInt64GetDatum(seed)));
+                       else
+                               tmp = scalarVal->val.boolean ? 0x02 : 0x04;
+
+                       break;
                default:
                        elog(ERROR, "invalid jsonb scalar type");
+                       break;
        }
+
+       *hash = ROTATE_HIGH_AND_LOW_32BITS(*hash);
+       *hash ^= tmp;
 }
 
 /*
  * Are two scalar JsonbValues of the same type a and b equal?
+ */
+static bool
+equalsJsonbScalarValue(JsonbValue *aScalar, JsonbValue *bScalar)
+{
+       if (aScalar->type == bScalar->type)
+       {
+               switch (aScalar->type)
+               {
+                       case jbvNull:
+                               return true;
+                       case jbvString:
+                               return lengthCompareJsonbStringValue(aScalar, bScalar) == 0;
+                       case jbvNumeric:
+                               return DatumGetBool(DirectFunctionCall2(numeric_eq,
+                                                                                                               PointerGetDatum(aScalar->val.numeric),
+                                                                                                               PointerGetDatum(bScalar->val.numeric)));
+                       case jbvBool:
+                               return aScalar->val.boolean == bScalar->val.boolean;
+
+                       default:
+                               elog(ERROR, "invalid jsonb scalar type");
+               }
+       }
+       elog(ERROR, "jsonb scalar type mismatch");
+       return false;
+}
+
+/*
+ * Compare two scalar JsonbValues, returning -1, 0, or 1.
  *
- * 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.
+ * Strings are compared using the default collation.  Used by B-tree
+ * operators, where a lexical sort order is generally expected.
  */
 static int
 compareJsonbScalarValue(JsonbValue *aScalar, JsonbValue *bScalar)
@@ -1172,16 +1381,22 @@ compareJsonbScalarValue(JsonbValue *aScalar, JsonbValue *bScalar)
                        case jbvNull:
                                return 0;
                        case jbvString:
-                               return lengthCompareJsonbStringValue(aScalar, bScalar, NULL);
+                               return varstr_cmp(aScalar->val.string.val,
+                                                                 aScalar->val.string.len,
+                                                                 bScalar->val.string.val,
+                                                                 bScalar->val.string.len,
+                                                                 DEFAULT_COLLATION_OID);
                        case jbvNumeric:
                                return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
-                                                                          PointerGetDatum(aScalar->val.numeric),
-                                                                        PointerGetDatum(bScalar->val.numeric)));
+                                                                                                                PointerGetDatum(aScalar->val.numeric),
+                                                                                                                PointerGetDatum(bScalar->val.numeric)));
                        case jbvBool:
-                               if (aScalar->val.boolean != bScalar->val.boolean)
-                                       return (aScalar->val.boolean > bScalar->val.boolean) ? 1 : -1;
-                               else
+                               if (aScalar->val.boolean == bScalar->val.boolean)
                                        return 0;
+                               else if (aScalar->val.boolean > bScalar->val.boolean)
+                                       return 1;
+                               else
+                                       return -1;
                        default:
                                elog(ERROR, "invalid jsonb scalar type");
                }
@@ -1190,560 +1405,419 @@ compareJsonbScalarValue(JsonbValue *aScalar, JsonbValue *bScalar)
        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.
+ * Functions for manipulating the resizable buffer used by convertJsonb and
+ * its subroutines.
  */
-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->val.string.val, va->val.string.len, vb->val.string.val,
-                                         vb->val.string.len, DEFAULT_COLLATION_OID);
-}
 
 /*
- * Given a JsonbValue, convert to Jsonb and store in preallocated Jsonb buffer
- * sufficiently large to fit the value
+ * Reserve 'len' bytes, at the end of the buffer, enlarging it if necessary.
+ * Returns the offset to the reserved area. The caller is expected to fill
+ * the reserved area later with copyToBuffer().
  */
-static Size
-convertJsonb(JsonbValue *val, Jsonb *buffer)
+static int
+reserveFromBuffer(StringInfo buffer, int len)
 {
-       convertState state;
-       Size            len;
+       int                     offset;
 
-       /* Should not already have binary representation */
-       Assert(val->type != jbvBinary);
+       /* Make more room if needed */
+       enlargeStringInfo(buffer, len);
 
-       state.buffer = buffer;
-       /* Start from superheader */
-       state.ptr = VARDATA(state.buffer);
-       state.levelSz = 8;
-       state.allState = palloc(sizeof(convertLevel) * state.levelSz);
+       /* remember current offset */
+       offset = buffer->len;
 
-       walkJsonbValueConversion(val, &state, 0);
+       /* reserve the space */
+       buffer->len += len;
 
-       len = state.ptr - VARDATA(state.buffer);
+       /*
+        * Keep a trailing null in place, even though it's not useful for us; it
+        * seems best to preserve the invariants of StringInfos.
+        */
+       buffer->data[buffer->len] = '\0';
 
-       Assert(len <= val->estSize);
-       return len;
+       return offset;
 }
 
 /*
- * 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).
+ * Copy 'len' bytes to a previously reserved area in buffer.
  */
 static void
-walkJsonbValueConversion(JsonbValue *val, convertState *cstate,
-                                                uint32 nestlevel)
+copyToBuffer(StringInfo buffer, int offset, const char *data, int len)
 {
-       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->val.array.nElems; i++)
-                       {
-                               if (IsAJsonbScalar(&val->val.array.elems[i]) ||
-                                       val->val.array.elems[i].type == jbvBinary)
-                                       putJsonbValueConversion(cstate, val->val.array.elems + i,
-                                                                                       WJB_ELEM, nestlevel);
-                               else
-                                       walkJsonbValueConversion(val->val.array.elems + i, cstate,
-                                                                                        nestlevel + 1);
-                       }
-                       putJsonbValueConversion(cstate, val, WJB_END_ARRAY, nestlevel);
-
-                       break;
-               case jbvObject:
+       memcpy(buffer->data + offset, data, len);
+}
 
-                       putJsonbValueConversion(cstate, val, WJB_BEGIN_OBJECT, nestlevel);
-                       for (i = 0; i < val->val.object.nPairs; i++)
-                       {
-                               putJsonbValueConversion(cstate, &val->val.object.pairs[i].key,
-                                                                               WJB_KEY, nestlevel);
-
-                               if (IsAJsonbScalar(&val->val.object.pairs[i].value) ||
-                                       val->val.object.pairs[i].value.type == jbvBinary)
-                                       putJsonbValueConversion(cstate,
-                                                                                       &val->val.object.pairs[i].value,
-                                                                                       WJB_VALUE, nestlevel);
-                               else
-                                       walkJsonbValueConversion(&val->val.object.pairs[i].value,
-                                                                                        cstate, nestlevel + 1);
-                       }
-                       putJsonbValueConversion(cstate, val, WJB_END_OBJECT, nestlevel);
+/*
+ * A shorthand for reserveFromBuffer + copyToBuffer.
+ */
+static void
+appendToBuffer(StringInfo buffer, const char *data, int len)
+{
+       int                     offset;
 
-                       break;
-               default:
-                       elog(ERROR, "unknown type of jsonb container");
-       }
+       offset = reserveFromBuffer(buffer, len);
+       copyToBuffer(buffer, offset, data, len);
 }
 
+
 /*
- * walkJsonbValueConversion() worker.  Add padding sufficient to int-align our
- * access to conversion buffer.
+ * Append padding, so that the length of the StringInfo is int-aligned.
+ * Returns the number of padding bytes appended.
  */
-static inline
-short
-addPaddingInt(convertState *cstate)
+static short
+padBufferToInt(StringInfo buffer)
 {
-       short           padlen,
-                               p;
+       int                     padlen,
+                               p,
+                               offset;
 
-       padlen = INTALIGN(cstate->ptr - VARDATA(cstate->buffer)) -
-               (cstate->ptr - VARDATA(cstate->buffer));
+       padlen = INTALIGN(buffer->len) - buffer->len;
 
-       for (p = padlen; p > 0; p--)
-       {
-               *cstate->ptr = '\0';
-               cstate->ptr++;
-       }
+       offset = reserveFromBuffer(buffer, padlen);
+
+       /* padlen must be small, so this is probably faster than a memset */
+       for (p = 0; p < padlen; p++)
+               buffer->data[offset + p] = '\0';
 
        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).
+ * Given a JsonbValue, convert to Jsonb. The result is palloc'd.
  */
-static void
-putJsonbValueConversion(convertState *cstate, JsonbValue *val, uint32 flags,
-                                               uint32 level)
+static Jsonb *
+convertToJsonb(JsonbValue *val)
 {
-       if (level == cstate->levelSz)
-       {
-               cstate->levelSz *= 2;
-               cstate->allState = repalloc(cstate->allState,
-                                                                       sizeof(convertLevel) * cstate->levelSz);
-       }
+       StringInfoData buffer;
+       JEntry          jentry;
+       Jsonb      *res;
 
-       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;
+       /* Should not already have binary representation */
+       Assert(val->type != jbvBinary);
 
-               addPaddingInt(cstate);
+       /* Allocate an output buffer. It will be enlarged as needed */
+       initStringInfo(&buffer);
 
-               /* 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;
+       /* Make room for the varlena header */
+       reserveFromBuffer(&buffer, VARHDRSZ);
 
-               if (val->type == jbvArray)
-               {
-                       *cstate->contPtr->header = val->val.array.nElems | JB_FARRAY;
-                       cstate->ptr += sizeof(JEntry) * val->val.array.nElems;
+       convertJsonbValue(&buffer, &jentry, val, 0);
 
-                       if (val->val.array.rawScalar)
-                       {
-                               Assert(val->val.array.nElems == 1);
-                               Assert(level == 0);
-                               *cstate->contPtr->header |= JB_FSCALAR;
-                       }
-               }
-               else
-               {
-                       *cstate->contPtr->header = val->val.object.nPairs | JB_FOBJECT;
-                       cstate->ptr += sizeof(JEntry) * val->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);
+       /*
+        * Note: the JEntry of the root is discarded. Therefore the root
+        * JsonbContainer struct must contain enough information to tell what kind
+        * of value it is.
+        */
 
-               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;
+       res = (Jsonb *) buffer.data;
 
-               Assert(((flags & WJB_END_ARRAY) && val->type == jbvArray) ||
-                          ((flags & WJB_END_OBJECT) && val->type == jbvObject));
+       SET_VARSIZE(res, buffer.len);
 
-               if (level == 0)
-                       return;
+       return res;
+}
 
-               len = cstate->ptr - (char *) cstate->contPtr->begin;
+/*
+ * Subroutine of convertJsonb: serialize a single JsonbValue into buffer.
+ *
+ * The JEntry header for this node is returned in *header.  It is filled in
+ * with the length of this value and appropriate type bits.  If we wish to
+ * store an end offset rather than a length, it is the caller's responsibility
+ * to adjust for that.
+ *
+ * If the value is an array or an object, this recurses. 'level' is only used
+ * for debugging purposes.
+ */
+static void
+convertJsonbValue(StringInfo buffer, JEntry *header, JsonbValue *val, int level)
+{
+       check_stack_depth();
 
-               prevPtr = cstate->contPtr - 1;
+       if (!val)
+               return;
 
-               if (*prevPtr->header & JB_FARRAY)
-               {
-                       i = prevPtr->i;
+       /*
+        * A JsonbValue passed as val should never have a type of jbvBinary, and
+        * neither should any of its sub-components. Those values will be produced
+        * by convertJsonbArray and convertJsonbObject, the results of which will
+        * not be passed back to this function as an argument.
+        */
 
-                       prevPtr->meta[i].header = JENTRY_ISNEST;
+       if (IsAJsonbScalar(val))
+               convertJsonbScalar(buffer, header, val);
+       else if (val->type == jbvArray)
+               convertJsonbArray(buffer, header, val, level);
+       else if (val->type == jbvObject)
+               convertJsonbObject(buffer, header, val, level);
+       else
+               elog(ERROR, "unknown type of jsonb container to convert");
+}
 
-                       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 */
+static void
+convertJsonbArray(StringInfo buffer, JEntry *pheader, JsonbValue *val, int level)
+{
+       int                     base_offset;
+       int                     jentry_offset;
+       int                     i;
+       int                     totallen;
+       uint32          header;
+       int                     nElems = val->val.array.nElems;
 
-                       prevPtr->meta[i].header = JENTRY_ISNEST;
+       /* Remember where in the buffer this array starts. */
+       base_offset = buffer->len;
 
-                       prevPtr->meta[i].header |=
-                               (prevPtr->meta[i - 1].header & JENTRY_POSMASK) + len;
-               }
-               else
-               {
-                       elog(ERROR, "invalid jsonb container type");
-               }
+       /* Align to 4-byte boundary (any padding counts as part of my data) */
+       padBufferToInt(buffer);
 
-               Assert(cstate->ptr - cstate->contPtr->begin <= val->estSize);
-               prevPtr->i++;
-       }
-       else
+       /*
+        * Construct the header Jentry and store it in the beginning of the
+        * variable-length payload.
+        */
+       header = nElems | JB_FARRAY;
+       if (val->val.array.rawScalar)
        {
-               elog(ERROR, "unknown flag encountered during jsonb tree walk");
+               Assert(nElems == 1);
+               Assert(level == 0);
+               header |= JB_FSCALAR;
        }
-}
 
-/*
- * 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;
+       appendToBuffer(buffer, (char *) &header, sizeof(uint32));
 
-       cstate->contPtr = cstate->allState + level;
+       /* Reserve space for the JEntries of the elements. */
+       jentry_offset = reserveFromBuffer(buffer, sizeof(JEntry) * nElems);
 
-       if (i == 0)
-               cstate->contPtr->meta[0].header = JENTRY_ISFIRST;
-       else
-               cstate->contPtr->meta[i].header = 0;
-
-       switch (scalarVal->type)
+       totallen = 0;
+       for (i = 0; i < nElems; i++)
        {
-               case jbvNull:
-                       cstate->contPtr->meta[i].header |= JENTRY_ISNULL;
+               JsonbValue *elem = &val->val.array.elems[i];
+               int                     len;
+               JEntry          meta;
 
-                       if (i > 0)
-                               cstate->contPtr->meta[i].header |=
-                                       cstate->contPtr->meta[i - 1].header & JENTRY_POSMASK;
-                       break;
-               case jbvString:
-                       memcpy(cstate->ptr, scalarVal->val.string.val, scalarVal->val.string.len);
-                       cstate->ptr += scalarVal->val.string.len;
+               /*
+                * Convert element, producing a JEntry and appending its
+                * variable-length data to buffer
+                */
+               convertJsonbValue(buffer, &meta, elem, level + 1);
 
-                       if (i == 0)
-                               cstate->contPtr->meta[0].header |= scalarVal->val.string.len;
-                       else
-                               cstate->contPtr->meta[i].header |=
-                                       (cstate->contPtr->meta[i - 1].header & JENTRY_POSMASK) +
-                                       scalarVal->val.string.len;
-                       break;
-               case jbvNumeric:
-                       numlen = VARSIZE_ANY(scalarVal->val.numeric);
-                       padlen = addPaddingInt(cstate);
+               len = JBE_OFFLENFLD(meta);
+               totallen += len;
 
-                       memcpy(cstate->ptr, scalarVal->val.numeric, numlen);
-                       cstate->ptr += numlen;
+               /*
+                * Bail out if total variable-length data exceeds what will fit in a
+                * JEntry length field.  We check this in each iteration, not just
+                * once at the end, to forestall possible integer overflow.
+                */
+               if (totallen > JENTRY_OFFLENMASK)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                        errmsg("total size of jsonb array elements exceeds the maximum of %u bytes",
+                                                       JENTRY_OFFLENMASK)));
 
-                       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->val.boolean) ?
-                               JENTRY_ISTRUE : JENTRY_ISFALSE;
+               /*
+                * Convert each JB_OFFSET_STRIDE'th length to an offset.
+                */
+               if ((i % JB_OFFSET_STRIDE) == 0)
+                       meta = (meta & JENTRY_TYPEMASK) | totallen | JENTRY_HAS_OFF;
 
-                       if (i > 0)
-                               cstate->contPtr->meta[i].header |=
-                                       cstate->contPtr->meta[i - 1].header & JENTRY_POSMASK;
-                       break;
-               default:
-                       elog(ERROR, "invalid jsonb scalar type");
+               copyToBuffer(buffer, jentry_offset, (char *) &meta, sizeof(JEntry));
+               jentry_offset += sizeof(JEntry);
        }
+
+       /* Total data size is everything we've appended to buffer */
+       totallen = buffer->len - base_offset;
+
+       /* Check length again, since we didn't include the metadata above */
+       if (totallen > JENTRY_OFFLENMASK)
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                errmsg("total size of jsonb array elements exceeds the maximum of %u bytes",
+                                               JENTRY_OFFLENMASK)));
+
+       /* Initialize the header of this node in the container's JEntry array */
+       *pheader = JENTRY_ISCONTAINER | totallen;
 }
 
-/*
- * Given superheader pointer into buffer, initialize iterator.  Must be a
- * container type.
- */
 static void
-iteratorFromContainerBuf(JsonbIterator *it, JsonbSuperHeader sheader)
+convertJsonbObject(StringInfo buffer, JEntry *pheader, JsonbValue *val, int level)
 {
-       uint32          superheader = *(uint32 *) sheader;
+       int                     base_offset;
+       int                     jentry_offset;
+       int                     i;
+       int                     totallen;
+       uint32          header;
+       int                     nPairs = val->val.object.nPairs;
 
-       it->containerType = superheader & (JB_FARRAY | JB_FOBJECT);
-       it->nElems = superheader & JB_CMASK;
-       it->buffer = sheader;
+       /* Remember where in the buffer this object starts. */
+       base_offset = buffer->len;
 
-       /* Array starts just after header */
-       it->meta = (JEntry *) (sheader + sizeof(uint32));
-       it->state = jbi_start;
+       /* Align to 4-byte boundary (any padding counts as part of my data) */
+       padBufferToInt(buffer);
 
-       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:
+       /*
+        * Construct the header Jentry and store it in the beginning of the
+        * variable-length payload.
+        */
+       header = nPairs | JB_FOBJECT;
+       appendToBuffer(buffer, (char *) &header, sizeof(uint32));
 
-                       /*
-                        * 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");
-       }
-}
+       /* Reserve space for the JEntries of the keys and values. */
+       jentry_offset = reserveFromBuffer(buffer, sizeof(JEntry) * nPairs * 2);
 
-/*
- * 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))
+       /*
+        * Iterate over the keys, then over the values, since that is the ordering
+        * we want in the on-disk representation.
+        */
+       totallen = 0;
+       for (i = 0; i < nPairs; i++)
        {
-               val->type = jbvNull;
-               val->estSize = sizeof(JEntry);
+               JsonbPair  *pair = &val->val.object.pairs[i];
+               int                     len;
+               JEntry          meta;
 
-               return false;
-       }
-       else if (JBE_ISSTRING(*ent))
-       {
-               val->type = jbvString;
-               val->val.string.val = (*it)->dataProper + JBE_OFF(*ent);
-               val->val.string.len = JBE_LEN(*ent);
-               val->estSize = sizeof(JEntry) + val->val.string.len;
+               /*
+                * Convert key, producing a JEntry and appending its variable-length
+                * data to buffer
+                */
+               convertJsonbScalar(buffer, &meta, &pair->key);
 
-               return false;
-       }
-       else if (JBE_ISNUMERIC(*ent))
-       {
-               val->type = jbvNumeric;
-               val->val.numeric = (Numeric) ((*it)->dataProper + INTALIGN(JBE_OFF(*ent)));
+               len = JBE_OFFLENFLD(meta);
+               totallen += len;
 
-               val->estSize = 2 * sizeof(JEntry) + VARSIZE_ANY(val->val.numeric);
+               /*
+                * Bail out if total variable-length data exceeds what will fit in a
+                * JEntry length field.  We check this in each iteration, not just
+                * once at the end, to forestall possible integer overflow.
+                */
+               if (totallen > JENTRY_OFFLENMASK)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                        errmsg("total size of jsonb object elements exceeds the maximum of %u bytes",
+                                                       JENTRY_OFFLENMASK)));
 
-               return false;
-       }
-       else if (JBE_ISBOOL(*ent))
-       {
-               val->type = jbvBool;
-               val->val.boolean = JBE_ISBOOL_TRUE(*ent) != 0;
-               val->estSize = sizeof(JEntry);
+               /*
+                * Convert each JB_OFFSET_STRIDE'th length to an offset.
+                */
+               if ((i % JB_OFFSET_STRIDE) == 0)
+                       meta = (meta & JENTRY_TYPEMASK) | totallen | JENTRY_HAS_OFF;
 
-               return false;
+               copyToBuffer(buffer, jentry_offset, (char *) &meta, sizeof(JEntry));
+               jentry_offset += sizeof(JEntry);
        }
-       else if (skipNested)
+       for (i = 0; i < nPairs; i++)
        {
-               val->type = jbvBinary;
-               val->val.binary.data = (*it)->dataProper + INTALIGN(JBE_OFF(*ent));
-               val->val.binary.len = JBE_LEN(*ent) - (INTALIGN(JBE_OFF(*ent)) - JBE_OFF(*ent));
-               val->estSize = val->val.binary.len + 2 * sizeof(JEntry);
+               JsonbPair  *pair = &val->val.object.pairs[i];
+               int                     len;
+               JEntry          meta;
 
-               return false;
-       }
-       else
-       {
                /*
-                * Must be container type, so setup caller's iterator to point to
-                * that, and return indication of that.
-                *
-                * Get child iterator.
+                * Convert value, producing a JEntry and appending its variable-length
+                * data to buffer
                 */
-               JsonbIterator *child = palloc(sizeof(JsonbIterator));
+               convertJsonbValue(buffer, &meta, &pair->value, level + 1);
 
-               iteratorFromContainerBuf(child,
-                                                                (*it)->dataProper + INTALIGN(JBE_OFF(*ent)));
+               len = JBE_OFFLENFLD(meta);
+               totallen += len;
 
-               child->parent = *it;
-               *it = child;
+               /*
+                * Bail out if total variable-length data exceeds what will fit in a
+                * JEntry length field.  We check this in each iteration, not just
+                * once at the end, to forestall possible integer overflow.
+                */
+               if (totallen > JENTRY_OFFLENMASK)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                        errmsg("total size of jsonb object elements exceeds the maximum of %u bytes",
+                                                       JENTRY_OFFLENMASK)));
 
-               return true;
-       }
-}
+               /*
+                * Convert each JB_OFFSET_STRIDE'th length to an offset.
+                */
+               if (((i + nPairs) % JB_OFFSET_STRIDE) == 0)
+                       meta = (meta & JENTRY_TYPEMASK) | totallen | JENTRY_HAS_OFF;
 
-/*
- * JsonbIteratorNext() worker: Return parent, while freeing memory for current
- * iterator
- */
-static JsonbIterator *
-freeAndGetParent(JsonbIterator *it)
-{
-       JsonbIterator *v = it->parent;
+               copyToBuffer(buffer, jentry_offset, (char *) &meta, sizeof(JEntry));
+               jentry_offset += sizeof(JEntry);
+       }
 
-       pfree(it);
-       return v;
-}
+       /* Total data size is everything we've appended to buffer */
+       totallen = buffer->len - base_offset;
 
-/*
- * pushJsonbValue() worker:  Iteration-like forming of Jsonb
- */
-static JsonbParseState *
-pushState(JsonbParseState **pstate)
-{
-       JsonbParseState *ns = palloc(sizeof(JsonbParseState));
+       /* Check length again, since we didn't include the metadata above */
+       if (totallen > JENTRY_OFFLENMASK)
+               ereport(ERROR,
+                               (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                errmsg("total size of jsonb object elements exceeds the maximum of %u bytes",
+                                               JENTRY_OFFLENMASK)));
 
-       ns->next = *pstate;
-       return ns;
+       /* Initialize the header of this node in the container's JEntry array */
+       *pheader = JENTRY_ISCONTAINER | totallen;
 }
 
-/*
- * pushJsonbValue() worker:  Append a pair key to state when generating a Jsonb
- */
 static void
-appendKey(JsonbParseState *pstate, JsonbValue *string)
+convertJsonbScalar(StringInfo buffer, JEntry *jentry, JsonbValue *scalarVal)
 {
-       JsonbValue *object = &pstate->contVal;
+       int                     numlen;
+       short           padlen;
 
-       Assert(object->type == jbvObject);
-       Assert(string->type == jbvString);
+       switch (scalarVal->type)
+       {
+               case jbvNull:
+                       *jentry = JENTRY_ISNULL;
+                       break;
 
-       if (object->val.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)));
+               case jbvString:
+                       appendToBuffer(buffer, scalarVal->val.string.val, scalarVal->val.string.len);
 
-       if (object->val.object.nPairs >= pstate->size)
-       {
-               pstate->size *= 2;
-               object->val.object.pairs = repalloc(object->val.object.pairs,
-                                                                                       sizeof(JsonbPair) * pstate->size);
-       }
+                       *jentry = scalarVal->val.string.len;
+                       break;
 
-       object->val.object.pairs[object->val.object.nPairs].key = *string;
-       object->val.object.pairs[object->val.object.nPairs].order = object->val.object.nPairs;
+               case jbvNumeric:
+                       /* replace numeric NaN with string "NaN" */
+                       if (numeric_is_nan(scalarVal->val.numeric))
+                       {
+                               appendToBuffer(buffer, "NaN", 3);
+                               *jentry = 3;
+                               break;
+                       }
 
-       object->estSize += string->estSize;
-}
+                       numlen = VARSIZE_ANY(scalarVal->val.numeric);
+                       padlen = padBufferToInt(buffer);
 
-/*
- * pushJsonbValue() worker:  Append a pair value to state when generating a
- * Jsonb
- */
-static void
-appendValue(JsonbParseState *pstate, JsonbValue *scalarVal)
-{
-       JsonbValue *object = &pstate->contVal;
+                       appendToBuffer(buffer, (char *) scalarVal->val.numeric, numlen);
 
-       Assert(object->type == jbvObject);
+                       *jentry = JENTRY_ISNUMERIC | (padlen + numlen);
+                       break;
 
-       object->val.object.pairs[object->val.object.nPairs++].value = *scalarVal;
-       object->estSize += scalarVal->estSize;
-}
+               case jbvBool:
+                       *jentry = (scalarVal->val.boolean) ?
+                               JENTRY_ISBOOL_TRUE : JENTRY_ISBOOL_FALSE;
+                       break;
 
-/*
- * pushJsonbValue() worker:  Append an element to state when generating a Jsonb
- */
-static void
-appendElement(JsonbParseState *pstate, JsonbValue *scalarVal)
-{
-       JsonbValue *array = &pstate->contVal;
+               case jbvDatetime:
+                       {
+                               char            buf[MAXDATELEN + 1];
+                               size_t          len;
 
-       Assert(array->type == jbvArray);
+                               JsonEncodeDateTime(buf,
+                                                                  scalarVal->val.datetime.value,
+                                                                  scalarVal->val.datetime.typid,
+                                                                  &scalarVal->val.datetime.tz);
+                               len = strlen(buf);
+                               appendToBuffer(buffer, buf, len);
 
-       if (array->val.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)));
+                               *jentry = len;
+                       }
+                       break;
 
-       if (array->val.array.nElems >= pstate->size)
-       {
-               pstate->size *= 2;
-               array->val.array.elems = repalloc(array->val.array.elems,
-                                                                                 sizeof(JsonbValue) * pstate->size);
+               default:
+                       elog(ERROR, "invalid jsonb scalar type");
        }
-
-       array->val.array.elems[array->val.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
+ * This is a special qsort() 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
@@ -1751,41 +1825,41 @@ appendElement(JsonbParseState *pstate, JsonbValue *scalarVal)
  *
  * 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)
+lengthCompareJsonbStringValue(const void *a, const void *b)
 {
        const JsonbValue *va = (const JsonbValue *) a;
        const JsonbValue *vb = (const JsonbValue *) b;
-       int                     res;
 
        Assert(va->type == jbvString);
        Assert(vb->type == jbvString);
 
-       if (va->val.string.len == vb->val.string.len)
-       {
-               res = memcmp(va->val.string.val, vb->val.string.val, va->val.string.len);
-               if (res == 0 && binequal)
-                       *((bool *) binequal) = true;
-       }
-       else
-       {
-               res = (va->val.string.len > vb->val.string.len) ? 1 : -1;
-       }
+       return lengthCompareJsonbString(va->val.string.val, va->val.string.len,
+                                                                       vb->val.string.val, vb->val.string.len);
+}
 
-       return res;
+/*
+ * Subroutine for lengthCompareJsonbStringValue
+ *
+ * This is also useful separately to implement binary search on
+ * JsonbContainers.
+ */
+static int
+lengthCompareJsonbString(const char *val1, int len1, const char *val2, int len2)
+{
+       if (len1 == len2)
+               return memcmp(val1, val2, len1);
+       else
+               return len1 > len2 ? 1 : -1;
 }
 
 /*
  * 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.
+ * 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.
  *
  * N.B: String comparisons here are "length-wise"
  *
@@ -1798,7 +1872,9 @@ lengthCompareJsonbPair(const void *a, const void *b, void *binequal)
        const JsonbPair *pb = (const JsonbPair *) b;
        int                     res;
 
-       res = lengthCompareJsonbStringValue(&pa->key, &pb->key, binequal);
+       res = lengthCompareJsonbStringValue(&pa->key, &pb->key);
+       if (res == 0 && binequal)
+               *((bool *) binequal) = true;
 
        /*
         * Guarantee keeping order of equal pair.  Unique algorithm will prefer
@@ -1832,11 +1908,7 @@ uniqueifyJsonbObject(JsonbValue *object)
                while (ptr - object->val.object.pairs < object->val.object.nPairs)
                {
                        /* Avoid copying over duplicate */
-                       if (lengthCompareJsonbStringValue(ptr, res, NULL) == 0)
-                       {
-                               object->estSize -= ptr->key.estSize + ptr->value.estSize;
-                       }
-                       else
+                       if (lengthCompareJsonbStringValue(ptr, res) != 0)
                        {
                                res++;
                                if (ptr != res)
@@ -1848,45 +1920,3 @@ uniqueifyJsonbObject(JsonbValue *object)
                object->val.object.nPairs = res + 1 - object->val.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->val.array.nElems > 1)
-               qsort_arg(array->val.array.elems, array->val.array.nElems,
-                                 sizeof(JsonbValue), lengthCompareJsonbStringValue,
-                                 &hasNonUniq);
-
-       if (hasNonUniq)
-       {
-               JsonbValue *ptr = array->val.array.elems + 1,
-                                  *res = array->val.array.elems;
-
-               while (ptr - array->val.array.elems < array->val.array.nElems)
-               {
-                       /* Avoid copying over duplicate */
-                       if (lengthCompareJsonbStringValue(ptr, res, NULL) != 0)
-                       {
-                               res++;
-                               *res = *ptr;
-                       }
-
-                       ptr++;
-               }
-
-               array->val.array.nElems = res + 1 - array->val.array.elems;
-       }
-}