aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/utils/adt/jsonb.c16
-rw-r--r--src/backend/utils/adt/jsonb_gin.c4
-rw-r--r--src/backend/utils/adt/jsonb_op.c106
-rw-r--r--src/backend/utils/adt/jsonb_util.c929
-rw-r--r--src/backend/utils/adt/jsonfuncs.c64
-rw-r--r--src/include/utils/jsonb.h213
6 files changed, 556 insertions, 776 deletions
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index cf5d6f23264..e1fe45f7128 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -33,7 +33,6 @@ static void jsonb_in_array_end(void *pstate);
static void jsonb_in_object_field_start(void *pstate, char *fname, bool isnull);
static void jsonb_put_escaped_value(StringInfo out, JsonbValue *scalarVal);
static void jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype);
-char *JsonbToCString(StringInfo out, char *in, int estimated_len);
/*
* jsonb type input function
@@ -65,7 +64,7 @@ jsonb_recv(PG_FUNCTION_ARGS)
if (version == 1)
str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
else
- elog(ERROR, "Unsupported jsonb version number %d", version);
+ elog(ERROR, "unsupported jsonb version number %d", version);
return jsonb_from_cstring(str, nbytes);
}
@@ -79,7 +78,7 @@ jsonb_out(PG_FUNCTION_ARGS)
Jsonb *jb = PG_GETARG_JSONB(0);
char *out;
- out = JsonbToCString(NULL, VARDATA(jb), VARSIZE(jb));
+ out = JsonbToCString(NULL, &jb->root, VARSIZE(jb));
PG_RETURN_CSTRING(out);
}
@@ -97,7 +96,7 @@ jsonb_send(PG_FUNCTION_ARGS)
StringInfo jtext = makeStringInfo();
int version = 1;
- (void) JsonbToCString(jtext, VARDATA(jb), VARSIZE(jb));
+ (void) JsonbToCString(jtext, &jb->root, VARSIZE(jb));
pq_begintypsend(&buf);
pq_sendint(&buf, version, 1);
@@ -130,7 +129,7 @@ jsonb_typeof(PG_FUNCTION_ARGS)
{
Assert(JB_ROOT_IS_SCALAR(in));
- it = JsonbIteratorInit(VARDATA_ANY(in));
+ it = JsonbIteratorInit(&in->root);
/*
* A root scalar is stored as an array of one element, so we get the
@@ -249,7 +248,6 @@ jsonb_in_object_field_start(void *pstate, char *fname, bool isnull)
v.type = jbvString;
v.val.string.len = checkStringLen(strlen(fname));
v.val.string.val = pnstrdup(fname, v.val.string.len);
- v.estSize = sizeof(JEntry) + v.val.string.len;
_state->res = pushJsonbValue(&_state->parseState, WJB_KEY, &v);
}
@@ -290,8 +288,6 @@ jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
JsonbInState *_state = (JsonbInState *) pstate;
JsonbValue v;
- v.estSize = sizeof(JEntry);
-
switch (tokentype)
{
@@ -300,7 +296,6 @@ jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
v.type = jbvString;
v.val.string.len = checkStringLen(strlen(token));
v.val.string.val = pnstrdup(token, v.val.string.len);
- v.estSize += v.val.string.len;
break;
case JSON_TOKEN_NUMBER:
@@ -312,7 +307,6 @@ jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
v.type = jbvNumeric;
v.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(token), 0, -1));
- v.estSize += VARSIZE_ANY(v.val.numeric) +sizeof(JEntry) /* alignment */ ;
break;
case JSON_TOKEN_TRUE:
v.type = jbvBool;
@@ -374,7 +368,7 @@ jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
* if they are converting it to a text* object.
*/
char *
-JsonbToCString(StringInfo out, JsonbSuperHeader in, int estimated_len)
+JsonbToCString(StringInfo out, JsonbContainer *in, int estimated_len)
{
bool first = true;
JsonbIterator *it;
diff --git a/src/backend/utils/adt/jsonb_gin.c b/src/backend/utils/adt/jsonb_gin.c
index 67f940ac979..592036ac585 100644
--- a/src/backend/utils/adt/jsonb_gin.c
+++ b/src/backend/utils/adt/jsonb_gin.c
@@ -80,7 +80,7 @@ gin_extract_jsonb(PG_FUNCTION_ARGS)
entries = (Datum *) palloc(sizeof(Datum) * total);
- it = JsonbIteratorInit(VARDATA(jb));
+ it = JsonbIteratorInit(&jb->root);
while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
{
@@ -487,7 +487,7 @@ gin_extract_jsonb_hash(PG_FUNCTION_ARGS)
entries = (Datum *) palloc(sizeof(Datum) * total);
- it = JsonbIteratorInit(VARDATA(jb));
+ it = JsonbIteratorInit(&jb->root);
tail.parent = NULL;
tail.hash = 0;
diff --git a/src/backend/utils/adt/jsonb_op.c b/src/backend/utils/adt/jsonb_op.c
index 38bd5676739..2d071b2523b 100644
--- a/src/backend/utils/adt/jsonb_op.c
+++ b/src/backend/utils/adt/jsonb_op.c
@@ -13,6 +13,7 @@
*/
#include "postgres.h"
+#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "utils/jsonb.h"
@@ -34,10 +35,9 @@ jsonb_exists(PG_FUNCTION_ARGS)
kval.val.string.val = VARDATA_ANY(key);
kval.val.string.len = VARSIZE_ANY_EXHDR(key);
- v = findJsonbValueFromSuperHeader(VARDATA(jb),
- JB_FOBJECT | JB_FARRAY,
- NULL,
- &kval);
+ v = findJsonbValueFromContainer(&jb->root,
+ JB_FOBJECT | JB_FARRAY,
+ &kval);
PG_RETURN_BOOL(v != NULL);
}
@@ -47,29 +47,28 @@ jsonb_exists_any(PG_FUNCTION_ARGS)
{
Jsonb *jb = PG_GETARG_JSONB(0);
ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
- JsonbValue *arrKey = arrayToJsonbSortedArray(keys);
- uint32 *plowbound = NULL,
- lowbound = 0;
int i;
+ Datum *key_datums;
+ bool *key_nulls;
+ int elem_count;
- if (arrKey == NULL || arrKey->val.object.nPairs == 0)
- PG_RETURN_BOOL(false);
-
- if (JB_ROOT_IS_OBJECT(jb))
- plowbound = &lowbound;
+ deconstruct_array(keys, TEXTOID, -1, false, 'i', &key_datums, &key_nulls,
+ &elem_count);
- /*
- * We exploit the fact that the pairs list is already sorted into strictly
- * increasing order to narrow the findJsonbValueFromSuperHeader search;
- * each search can start one entry past the previous "found" entry, or at
- * the lower bound of the last search.
- */
- for (i = 0; i < arrKey->val.array.nElems; i++)
+ for (i = 0; i < elem_count; i++)
{
- if (findJsonbValueFromSuperHeader(VARDATA(jb),
- JB_FOBJECT | JB_FARRAY,
- plowbound,
- arrKey->val.array.elems + i) != NULL)
+ JsonbValue strVal;
+
+ if (key_nulls[i])
+ continue;
+
+ strVal.type = jbvString;
+ strVal.val.string.val = VARDATA(key_datums[i]);
+ strVal.val.string.len = VARSIZE(key_datums[i]) - VARHDRSZ;
+
+ if (findJsonbValueFromContainer(&jb->root,
+ JB_FOBJECT | JB_FARRAY,
+ &strVal) != NULL)
PG_RETURN_BOOL(true);
}
@@ -81,29 +80,28 @@ jsonb_exists_all(PG_FUNCTION_ARGS)
{
Jsonb *jb = PG_GETARG_JSONB(0);
ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
- JsonbValue *arrKey = arrayToJsonbSortedArray(keys);
- uint32 *plowbound = NULL;
- uint32 lowbound = 0;
int i;
+ Datum *key_datums;
+ bool *key_nulls;
+ int elem_count;
- if (arrKey == NULL || arrKey->val.array.nElems == 0)
- PG_RETURN_BOOL(true);
+ deconstruct_array(keys, TEXTOID, -1, false, 'i', &key_datums, &key_nulls,
+ &elem_count);
- if (JB_ROOT_IS_OBJECT(jb))
- plowbound = &lowbound;
-
- /*
- * We exploit the fact that the pairs list is already sorted into strictly
- * increasing order to narrow the findJsonbValueFromSuperHeader search;
- * each search can start one entry past the previous "found" entry, or at
- * the lower bound of the last search.
- */
- for (i = 0; i < arrKey->val.array.nElems; i++)
+ for (i = 0; i < elem_count; i++)
{
- if (findJsonbValueFromSuperHeader(VARDATA(jb),
- JB_FOBJECT | JB_FARRAY,
- plowbound,
- arrKey->val.array.elems + i) == NULL)
+ JsonbValue strVal;
+
+ if (key_nulls[i])
+ continue;
+
+ strVal.type = jbvString;
+ strVal.val.string.val = VARDATA(key_datums[i]);
+ strVal.val.string.len = VARSIZE(key_datums[i]) - VARHDRSZ;
+
+ if (findJsonbValueFromContainer(&jb->root,
+ JB_FOBJECT | JB_FARRAY,
+ &strVal) == NULL)
PG_RETURN_BOOL(false);
}
@@ -123,8 +121,8 @@ jsonb_contains(PG_FUNCTION_ARGS)
JB_ROOT_IS_OBJECT(val) != JB_ROOT_IS_OBJECT(tmpl))
PG_RETURN_BOOL(false);
- it1 = JsonbIteratorInit(VARDATA(val));
- it2 = JsonbIteratorInit(VARDATA(tmpl));
+ it1 = JsonbIteratorInit(&val->root);
+ it2 = JsonbIteratorInit(&tmpl->root);
PG_RETURN_BOOL(JsonbDeepContains(&it1, &it2));
}
@@ -143,8 +141,8 @@ jsonb_contained(PG_FUNCTION_ARGS)
JB_ROOT_IS_OBJECT(val) != JB_ROOT_IS_OBJECT(tmpl))
PG_RETURN_BOOL(false);
- it1 = JsonbIteratorInit(VARDATA(val));
- it2 = JsonbIteratorInit(VARDATA(tmpl));
+ it1 = JsonbIteratorInit(&val->root);
+ it2 = JsonbIteratorInit(&tmpl->root);
PG_RETURN_BOOL(JsonbDeepContains(&it1, &it2));
}
@@ -156,7 +154,7 @@ jsonb_ne(PG_FUNCTION_ARGS)
Jsonb *jbb = PG_GETARG_JSONB(1);
bool res;
- res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) != 0);
+ res = (compareJsonbContainers(&jba->root, &jbb->root) != 0);
PG_FREE_IF_COPY(jba, 0);
PG_FREE_IF_COPY(jbb, 1);
@@ -173,7 +171,7 @@ jsonb_lt(PG_FUNCTION_ARGS)
Jsonb *jbb = PG_GETARG_JSONB(1);
bool res;
- res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) < 0);
+ res = (compareJsonbContainers(&jba->root, &jbb->root) < 0);
PG_FREE_IF_COPY(jba, 0);
PG_FREE_IF_COPY(jbb, 1);
@@ -187,7 +185,7 @@ jsonb_gt(PG_FUNCTION_ARGS)
Jsonb *jbb = PG_GETARG_JSONB(1);
bool res;
- res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) > 0);
+ res = (compareJsonbContainers(&jba->root, &jbb->root) > 0);
PG_FREE_IF_COPY(jba, 0);
PG_FREE_IF_COPY(jbb, 1);
@@ -201,7 +199,7 @@ jsonb_le(PG_FUNCTION_ARGS)
Jsonb *jbb = PG_GETARG_JSONB(1);
bool res;
- res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) <= 0);
+ res = (compareJsonbContainers(&jba->root, &jbb->root) <= 0);
PG_FREE_IF_COPY(jba, 0);
PG_FREE_IF_COPY(jbb, 1);
@@ -215,7 +213,7 @@ jsonb_ge(PG_FUNCTION_ARGS)
Jsonb *jbb = PG_GETARG_JSONB(1);
bool res;
- res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) >= 0);
+ res = (compareJsonbContainers(&jba->root, &jbb->root) >= 0);
PG_FREE_IF_COPY(jba, 0);
PG_FREE_IF_COPY(jbb, 1);
@@ -229,7 +227,7 @@ jsonb_eq(PG_FUNCTION_ARGS)
Jsonb *jbb = PG_GETARG_JSONB(1);
bool res;
- res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) == 0);
+ res = (compareJsonbContainers(&jba->root, &jbb->root) == 0);
PG_FREE_IF_COPY(jba, 0);
PG_FREE_IF_COPY(jbb, 1);
@@ -243,7 +241,7 @@ jsonb_cmp(PG_FUNCTION_ARGS)
Jsonb *jbb = PG_GETARG_JSONB(1);
int res;
- res = compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb));
+ res = compareJsonbContainers(&jba->root, &jbb->root);
PG_FREE_IF_COPY(jba, 0);
PG_FREE_IF_COPY(jbb, 1);
@@ -265,7 +263,7 @@ jsonb_hash(PG_FUNCTION_ARGS)
if (JB_ROOT_COUNT(jb) == 0)
PG_RETURN_INT32(0);
- it = JsonbIteratorInit(VARDATA(jb));
+ it = JsonbIteratorInit(&jb->root);
while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
{
diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
index 411144618d6..f6d6fab74e8 100644
--- a/src/backend/utils/adt/jsonb_util.c
+++ b/src/backend/utils/adt/jsonb_util.c
@@ -1,7 +1,7 @@
/*-------------------------------------------------------------------------
*
* jsonb_util.c
- * Utilities for jsonb datatype
+ * converting between Jsonb and JsonbValues, and iterating.
*
* Copyright (c) 2014, PostgreSQL Global Development Group
*
@@ -15,7 +15,6 @@
#include "access/hash.h"
#include "catalog/pg_collation.h"
-#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/jsonb.h"
@@ -38,49 +37,30 @@
JENTRY_POSMASK))
/*
- * State used while converting an arbitrary JsonbValue into a Jsonb value
- * (4-byte varlena uncompressed representation of a Jsonb)
- *
- * ConvertLevel: Bookkeeping around particular level when converting.
- */
-typedef struct convertLevel
-{
- uint32 i; /* Iterates once per element, or once per pair */
- uint32 *header; /* Pointer to current container header */
- JEntry *meta; /* This level's metadata */
- char *begin; /* Pointer into convertState.buffer */
-} convertLevel;
-
-/*
- * convertState: Overall bookkeeping state for conversion
+ * convertState: a resizeable buffer used when constructing a Jsonb datum
*/
-typedef struct convertState
+typedef struct
{
- /* 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;
-
+ char *buffer;
+ int len;
+ int allocatedsz;
} convertState;
+static void fillJsonbValue(JEntry *entry, char *payload_base, JsonbValue *result);
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 Jsonb *convertToJsonb(JsonbValue *val);
+static void convertJsonbValue(convertState *buffer, JEntry *header, JsonbValue *val, int level);
+static void convertJsonbArray(convertState *buffer, JEntry *header, JsonbValue *val, int level);
+static void convertJsonbObject(convertState *buffer, JEntry *header, JsonbValue *val, int level);
+static void convertJsonbScalar(convertState *buffer, JEntry *header, JsonbValue *scalarVal);
+
+static int reserveFromBuffer(convertState *buffer, int len);
+static void appendToBuffer(convertState *buffer, char *data, int len);
+static void copyToBuffer(convertState *buffer, int offset, char *data, int len);
+static short padBufferToInt(convertState *buffer);
+
+static void iteratorFromContainer(JsonbIterator *it, JsonbContainer *container);
static bool formIterIsContainer(JsonbIterator **it, JsonbValue *val,
JEntry *ent, bool skipNested);
static JsonbIterator *freeAndGetParent(JsonbIterator *it);
@@ -91,7 +71,6 @@ static void appendElement(JsonbParseState *pstate, JsonbValue *scalarVal);
static int lengthCompareJsonbStringValue(const void *a, const void *b, void *arg);
static int lengthCompareJsonbPair(const void *a, const void *b, void *arg);
static void uniqueifyJsonbObject(JsonbValue *object);
-static void uniqueifyJsonbArray(JsonbValue *array);
/*
* Turn an in-memory JsonbValue into a Jsonb for on-disk storage.
@@ -110,7 +89,6 @@ Jsonb *
JsonbValueToJsonb(JsonbValue *val)
{
Jsonb *out;
- Size sz;
if (IsAJsonbScalar(val))
{
@@ -127,17 +105,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
{
@@ -161,7 +133,7 @@ JsonbValueToJsonb(JsonbValue *val)
* memory here.
*/
int
-compareJsonbSuperHeaderValue(JsonbSuperHeader a, JsonbSuperHeader b)
+compareJsonbContainers(JsonbContainer *a, JsonbContainer *b)
{
JsonbIterator *ita,
*itb;
@@ -288,90 +260,51 @@ 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);
+ JEntry *array = container->children;
+ int count = (container->header & JB_CMASK);
JsonbValue *result = palloc(sizeof(JsonbValue));
Assert((flags & ~(JB_FARRAY | JB_FOBJECT)) == 0);
- if (flags & JB_FARRAY & superheader)
+ if (flags & JB_FARRAY & container->header)
{
- char *data = (char *) (array + (superheader & JB_CMASK));
+ char *data = (char *) (array + (container->header & JB_CMASK));
int i;
for (i = 0; i < count; i++)
{
JEntry *e = array + i;
- if (JBE_ISNULL(*e) && key->type == jbvNull)
- {
- result->type = jbvNull;
- result->estSize = sizeof(JEntry);
- }
- else if (JBE_ISSTRING(*e) && key->type == jbvString)
- {
- result->type = jbvString;
- result->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(e, data, 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 (compareJsonbScalarValue(key, result) == 0)
+ return result;
}
- else
- continue;
-
- if (compareJsonbScalarValue(key, result) == 0)
- return result;
}
}
- else if (flags & JB_FOBJECT & superheader)
+ else if (flags & JB_FOBJECT & container->header)
{
/* Since this is an object, account for *Pairs* of Jentrys */
- char *data = (char *) (array + (superheader & JB_CMASK) * 2);
- uint32 stopLow = lowbound ? *lowbound : 0,
+ char *data = (char *) (array + (container->header & JB_CMASK) * 2);
+ uint32 stopLow = 0,
stopMiddle;
/* Object key past by caller must be a string */
@@ -395,7 +328,6 @@ findJsonbValueFromSuperHeader(JsonbSuperHeader sheader, uint32 flags,
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;
difference = lengthCompareJsonbStringValue(&candidate, key, NULL);
@@ -404,47 +336,7 @@ findJsonbValueFromSuperHeader(JsonbSuperHeader sheader, uint32 flags,
/* Found our value (from key/value pair) */
JEntry *v = entry + 1;
- if (lowbound)
- *lowbound = stopMiddle + 1;
-
- if (JBE_ISNULL(*v))
- {
- result->type = jbvNull;
- result->estSize = sizeof(JEntry);
- }
- else if (JBE_ISSTRING(*v))
- {
- result->type = jbvString;
- result->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)));
-
- 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;
- }
+ fillJsonbValue(v, data, result);
return result;
}
@@ -456,9 +348,6 @@ findJsonbValueFromSuperHeader(JsonbSuperHeader sheader, uint32 flags,
count = stopMiddle;
}
}
-
- if (lowbound)
- *lowbound = stopLow;
}
/* Not found */
@@ -467,70 +356,80 @@ findJsonbValueFromSuperHeader(JsonbSuperHeader sheader, uint32 flags,
}
/*
- * 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;
+ JEntry *e;
char *data;
+ uint32 nelements;
- result = palloc(sizeof(JsonbValue));
+ if ((container->header & JB_FARRAY) == 0)
+ elog(ERROR, "not a jsonb array");
+
+ nelements = container->header & JB_CMASK;
- if (i >= (superheader & JB_CMASK))
+ if (i >= nelements)
return NULL;
- array = (JEntry *) (sheader + sizeof(uint32));
+ e = &container->children[i];
- if (superheader & JB_FARRAY)
- {
- e = array + i;
- data = (char *) (array + (superheader & JB_CMASK));
- }
- else
- {
- elog(ERROR, "not a jsonb array");
- }
+ data = (char *) &container->children[nelements];
- if (JBE_ISNULL(*e))
+ result = palloc(sizeof(JsonbValue));
+
+ fillJsonbValue(e, data, result);
+
+ return result;
+}
+
+/*
+ * Given the JEntry header, and the base address of the data that the offset
+ * in the JEntry refers to, fill a JsonbValue.
+ *
+ * An array or object will be returned as jbvBinary, ie. it won't be
+ * expanded.
+ */
+static void
+fillJsonbValue(JEntry *entry, char *payload_base, JsonbValue *result)
+{
+ 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 = payload_base + JBE_OFF(*entry);
+ result->val.string.len = JBE_LEN(*entry);
+ 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) (payload_base + INTALIGN(JBE_OFF(*entry)));
+ }
+ 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);
+ result->val.binary.data = (JsonbContainer *) (payload_base + INTALIGN(JBE_OFF(*entry)));
+ result->val.binary.len = JBE_LEN(*entry) - (INTALIGN(JBE_OFF(*entry)) - JBE_OFF(*entry));
}
-
- return result;
}
/*
@@ -547,7 +446,8 @@ getIthJsonbValueFromSuperHeader(JsonbSuperHeader sheader, uint32 i)
* "raw scalar" pseudo array to append that.
*/
JsonbValue *
-pushJsonbValue(JsonbParseState **pstate, int seq, JsonbValue *scalarVal)
+pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
+ JsonbValue *scalarVal)
{
JsonbValue *result = NULL;
@@ -558,7 +458,6 @@ 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);
@@ -580,7 +479,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) *
@@ -602,6 +500,7 @@ pushJsonbValue(JsonbParseState **pstate, int seq, JsonbValue *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,17 +534,17 @@ pushJsonbValue(JsonbParseState **pstate, int seq, JsonbValue *scalarVal)
}
/*
- * Given a Jsonb superheader, expand to JsonbIterator to iterate over items
+ * 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);
+ iteratorFromContainer(it, container);
it->parent = NULL;
return it;
@@ -679,7 +578,7 @@ JsonbIteratorInit(JsonbSuperHeader sheader)
* or Object element/pair buffers, since their element/pair pointers are
* garbage.
*/
-int
+JsonbIteratorToken
JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
{
JsonbIterState state;
@@ -875,10 +774,9 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
Assert(rcont == WJB_KEY);
/* First, find value by key... */
- lhsVal = findJsonbValueFromSuperHeader((*val)->buffer,
- JB_FOBJECT,
- NULL,
- &vcontained);
+ lhsVal = findJsonbValueFromContainer((JsonbContainer *) (*val)->buffer,
+ JB_FOBJECT,
+ &vcontained);
if (!lhsVal)
return false;
@@ -978,10 +876,9 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
if (IsAJsonbScalar(&vcontained))
{
- if (!findJsonbValueFromSuperHeader((*val)->buffer,
- JB_FARRAY,
- NULL,
- &vcontained))
+ if (!findJsonbValueFromContainer((JsonbContainer *) (*val)->buffer,
+ JB_FARRAY,
+ &vcontained))
return false;
}
else
@@ -1057,63 +954,6 @@ 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 the hash value into an existing
* hash provided by the caller.
*
@@ -1212,331 +1052,333 @@ lexicalCompareJsonbStringValue(const void *a, const void *b)
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
+ * Functions for manipulating the resizeable buffer used by convertJsonb and
+ * its subroutines.
*/
-static Size
-convertJsonb(JsonbValue *val, Jsonb *buffer)
-{
- convertState state;
- Size len;
- /* Should not already have binary representation */
- Assert(val->type != jbvBinary);
+/*
+ * Rervere 'len' bytes, at the end of the buffer, enlarging it if necessary.
+ * Returns the offset to the reserved area. The caller is expected to copy
+ * the data to the reserved area later with copyToBuffer()
+ */
+static int
+reserveFromBuffer(convertState *buffer, int len)
+{
+ int offset;
- state.buffer = buffer;
- /* Start from superheader */
- state.ptr = VARDATA(state.buffer);
- state.levelSz = 8;
- state.allState = palloc(sizeof(convertLevel) * state.levelSz);
+ /* Make more room if needed */
+ if (buffer->len + len > buffer->allocatedsz)
+ {
+ buffer->allocatedsz *= 2;
+ buffer->buffer = repalloc(buffer->buffer, buffer->allocatedsz);
+ }
- walkJsonbValueConversion(val, &state, 0);
+ /* remember current offset */
+ offset = buffer->len;
- len = state.ptr - VARDATA(state.buffer);
+ /* reserve the space */
+ buffer->len += len;
- 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(convertState *buffer, int offset, char *data, int len)
{
- int i;
+ memcpy(buffer->buffer + offset, data, len);
+}
- check_stack_depth();
+/*
+ * A shorthand for reserveFromBuffer + copyToBuffer.
+ */
+static void
+appendToBuffer(convertState *buffer, char *data, int len)
+{
+ int offset;
- if (!val)
- return;
+ offset = reserveFromBuffer(buffer, len);
+ copyToBuffer(buffer, offset, data, len);
+}
- 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);
+/*
+ * Append padding, so that the length of the StringInfo is int-aligned.
+ * Returns the number of padding bytes appended.
+ */
+static short
+padBufferToInt(convertState *buffer)
+{
+ short padlen,
+ p;
+ int offset;
- break;
- case jbvObject:
+ padlen = INTALIGN(buffer->len) - buffer->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);
+ offset = reserveFromBuffer(buffer, padlen);
+ for (p = 0; p < padlen; p++)
+ buffer->buffer[offset + p] = 0;
- break;
- default:
- elog(ERROR, "unknown type of jsonb container");
- }
+ return padlen;
}
/*
- * walkJsonbValueConversion() worker. Add padding sufficient to int-align our
- * access to conversion buffer.
+ * Given a JsonbValue, convert to Jsonb. The result is palloc'd.
*/
-static inline
-short
-addPaddingInt(convertState *cstate)
+static Jsonb *
+convertToJsonb(JsonbValue *val)
{
- short padlen,
- p;
+ convertState buffer;
+ JEntry jentry;
+ Jsonb *res;
- padlen = INTALIGN(cstate->ptr - VARDATA(cstate->buffer)) -
- (cstate->ptr - VARDATA(cstate->buffer));
+ /* Should not already have binary representation */
+ Assert(val->type != jbvBinary);
- for (p = padlen; p > 0; p--)
- {
- *cstate->ptr = '\0';
- cstate->ptr++;
- }
+ /* Allocate an output buffer. It will be enlarged as needed */
+ buffer.buffer = palloc(128);
+ buffer.len = 0;
+ buffer.allocatedsz = 128;
- return padlen;
+ /* Make room for the varlena header */
+ reserveFromBuffer(&buffer, sizeof(VARHDRSZ));
+
+ convertJsonbValue(&buffer, &jentry, val, 0);
+
+ /*
+ * Note: the JEntry of the root is not discarded. Therefore the root
+ * JsonbContainer struct must contain enough information to tell what
+ * kind of value it is.
+ */
+
+ res = (Jsonb *) buffer.buffer;
+
+ SET_VARSIZE(res, buffer.len);
+
+ return res;
}
/*
- * walkJsonbValueConversion() worker.
+ * 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, but if
+ * it is stored in an array or an object (which is always, except for the root
+ * node), it is the caller's responsibility to adjust it with the offset
+ * within the container.
+ *
+ * If the value is an array or an object, this recurses. 'level' is only used
+ * for debugging purposes.
+
* 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).
+ * serialize and copy a scalar value into buffer.
*
- * 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).
+ * 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.
+ *
+ * It is the callers responsibility to shift the offset if this is stored
+ * in an array or object.
*/
static void
-putJsonbValueConversion(convertState *cstate, JsonbValue *val, uint32 flags,
- uint32 level)
+convertJsonbValue(convertState *buffer, JEntry *header, JsonbValue *val, int level)
{
- if (level == cstate->levelSz)
- {
- cstate->levelSz *= 2;
- cstate->allState = repalloc(cstate->allState,
- sizeof(convertLevel) * cstate->levelSz);
- }
-
- cstate->contPtr = cstate->allState + level;
+ check_stack_depth();
- if (flags & (WJB_BEGIN_ARRAY | WJB_BEGIN_OBJECT))
- {
- Assert(((flags & WJB_BEGIN_ARRAY) && val->type == jbvArray) ||
- ((flags & WJB_BEGIN_OBJECT) && val->type == jbvObject));
+ if (!val)
+ return;
- /* Initialize pointer into conversion buffer at this level */
- cstate->contPtr->begin = cstate->ptr;
+ if (IsAJsonbScalar(val) || val->type == jbvBinary)
+ 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");
+}
- addPaddingInt(cstate);
+static void
+convertJsonbArray(convertState *buffer, JEntry *pheader, JsonbValue *val, int level)
+{
+ int offset;
+ int metaoffset;
+ int i;
+ int totallen;
+ uint32 header;
- /* 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;
+ /* Initialize pointer into conversion buffer at this level */
+ offset = buffer->len;
- if (val->type == jbvArray)
- {
- *cstate->contPtr->header = val->val.array.nElems | JB_FARRAY;
- cstate->ptr += sizeof(JEntry) * val->val.array.nElems;
+ padBufferToInt(buffer);
- 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)
+ /*
+ * Construct the header Jentry, stored in the beginning of the variable-
+ * length payload.
+ */
+ header = val->val.array.nElems | JB_FARRAY;
+ if (val->val.array.rawScalar)
{
- putScalarConversion(cstate, val, level, cstate->contPtr->i);
- cstate->contPtr->i++;
+ Assert(val->val.array.nElems == 1);
+ Assert(level == 0);
+ header |= JB_FSCALAR;
}
- else if (flags & WJB_KEY)
- {
- Assert(val->type == jbvString);
- putScalarConversion(cstate, val, level, cstate->contPtr->i * 2);
- }
- else if (flags & WJB_VALUE)
+ appendToBuffer(buffer, (char *) &header, sizeof(uint32));
+ /* reserve space for the JEntries of the elements. */
+ metaoffset = reserveFromBuffer(buffer, sizeof(JEntry) * val->val.array.nElems);
+
+ totallen = 0;
+ for (i = 0; i < val->val.array.nElems; i++)
{
- putScalarConversion(cstate, val, level, cstate->contPtr->i * 2 + 1);
- cstate->contPtr->i++;
+ JsonbValue *elem = &val->val.array.elems[i];
+ int len;
+ JEntry meta;
+
+ convertJsonbValue(buffer, &meta, elem, level + 1);
+ len = meta & JENTRY_POSMASK;
+ totallen += len;
+
+ if (totallen > JENTRY_POSMASK)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("total size of jsonb array elements exceeds the maximum of %u bytes",
+ JENTRY_POSMASK)));
+
+ if (i == 0)
+ meta |= JENTRY_ISFIRST;
+ else
+ meta = (meta & ~JENTRY_POSMASK) | totallen;
+ copyToBuffer(buffer, metaoffset, (char *) &meta, sizeof(JEntry));
+ metaoffset += sizeof(JEntry);
}
- else if (flags & (WJB_END_ARRAY | WJB_END_OBJECT))
- {
- convertLevel *prevPtr; /* Prev container pointer */
- uint32 len,
- i;
- Assert(((flags & WJB_END_ARRAY) && val->type == jbvArray) ||
- ((flags & WJB_END_OBJECT) && val->type == jbvObject));
+ totallen = buffer->len - offset;
- if (level == 0)
- return;
+ /* Initialize the header of this node, in the container's JEntry array */
+ *pheader = JENTRY_ISCONTAINER | totallen;
+}
- len = cstate->ptr - (char *) cstate->contPtr->begin;
+static void
+convertJsonbObject(convertState *buffer, JEntry *pheader, JsonbValue *val, int level)
+{
+ uint32 header;
+ int offset;
+ int metaoffset;
+ int i;
+ int totallen;
- prevPtr = cstate->contPtr - 1;
+ /* Initialize pointer into conversion buffer at this level */
+ offset = buffer->len;
- if (*prevPtr->header & JB_FARRAY)
- {
- i = prevPtr->i;
+ padBufferToInt(buffer);
- prevPtr->meta[i].header = JENTRY_ISNEST;
+ /* Initialize header */
+ header = val->val.object.nPairs | JB_FOBJECT;
+ appendToBuffer(buffer, (char *) &header, sizeof(uint32));
- 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 */
+ /* reserve space for the JEntries of the keys and values */
+ metaoffset = reserveFromBuffer(buffer, sizeof(JEntry) * val->val.object.nPairs * 2);
- prevPtr->meta[i].header = JENTRY_ISNEST;
+ totallen = 0;
+ for (i = 0; i < val->val.object.nPairs; i++)
+ {
+ JsonbPair *pair = &val->val.object.pairs[i];
+ int len;
+ JEntry meta;
- prevPtr->meta[i].header |=
- (prevPtr->meta[i - 1].header & JENTRY_POSMASK) + len;
- }
- else
- {
- elog(ERROR, "invalid jsonb container type");
- }
+ /* put key */
+ convertJsonbScalar(buffer, &meta, &pair->key);
- Assert(cstate->ptr - cstate->contPtr->begin <= val->estSize);
- prevPtr->i++;
- }
- else
- {
- elog(ERROR, "unknown flag encountered during jsonb tree walk");
+ len = meta & JENTRY_POSMASK;
+ totallen += len;
+
+ if (totallen > JENTRY_POSMASK)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("total size of jsonb array elements exceeds the maximum of %u bytes",
+ JENTRY_POSMASK)));
+
+ if (i == 0)
+ meta |= JENTRY_ISFIRST;
+ else
+ meta = (meta & ~JENTRY_POSMASK) | totallen;
+ copyToBuffer(buffer, metaoffset, (char *) &meta, sizeof(JEntry));
+ metaoffset += sizeof(JEntry);
+
+ convertJsonbValue(buffer, &meta, &pair->value, level);
+ len = meta & JENTRY_POSMASK;
+ totallen += len;
+
+ if (totallen > JENTRY_POSMASK)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("total size of jsonb array elements exceeds the maximum of %u bytes",
+ JENTRY_POSMASK)));
+
+ meta = (meta & ~JENTRY_POSMASK) | totallen;
+ copyToBuffer(buffer, metaoffset, (char *) &meta, sizeof(JEntry));
+ metaoffset += sizeof(JEntry);
}
+
+ totallen = buffer->len - offset;
+
+ *pheader = JENTRY_ISCONTAINER | totallen;
}
-/*
- * 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)
+convertJsonbScalar(convertState *buffer, JEntry *jentry, JsonbValue *scalarVal)
{
int numlen;
short padlen;
- cstate->contPtr = cstate->allState + level;
-
- if (i == 0)
- cstate->contPtr->meta[0].header = JENTRY_ISFIRST;
- else
- cstate->contPtr->meta[i].header = 0;
-
switch (scalarVal->type)
{
case jbvNull:
- cstate->contPtr->meta[i].header |= JENTRY_ISNULL;
-
- if (i > 0)
- cstate->contPtr->meta[i].header |=
- cstate->contPtr->meta[i - 1].header & JENTRY_POSMASK;
+ *jentry = JENTRY_ISNULL;
break;
+
case jbvString:
- memcpy(cstate->ptr, scalarVal->val.string.val, scalarVal->val.string.len);
- cstate->ptr += scalarVal->val.string.len;
+ appendToBuffer(buffer, scalarVal->val.string.val, scalarVal->val.string.len);
- 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;
+ *jentry = scalarVal->val.string.len;
break;
+
case jbvNumeric:
numlen = VARSIZE_ANY(scalarVal->val.numeric);
- padlen = addPaddingInt(cstate);
+ padlen = padBufferToInt(buffer);
- memcpy(cstate->ptr, scalarVal->val.numeric, numlen);
- cstate->ptr += numlen;
+ appendToBuffer(buffer, (char *) scalarVal->val.numeric, numlen);
- cstate->contPtr->meta[i].header |= JENTRY_ISNUMERIC;
- if (i == 0)
- cstate->contPtr->meta[0].header |= padlen + numlen;
- else
- cstate->contPtr->meta[i].header |=
- (cstate->contPtr->meta[i - 1].header & JENTRY_POSMASK)
- + padlen + numlen;
+ *jentry = JENTRY_ISNUMERIC | (padlen + numlen);
break;
- case jbvBool:
- cstate->contPtr->meta[i].header |= (scalarVal->val.boolean) ?
- JENTRY_ISTRUE : JENTRY_ISFALSE;
- if (i > 0)
- cstate->contPtr->meta[i].header |=
- cstate->contPtr->meta[i - 1].header & JENTRY_POSMASK;
+ case jbvBool:
+ *jentry = (scalarVal->val.boolean) ?
+ JENTRY_ISBOOL_TRUE : JENTRY_ISBOOL_FALSE;
break;
+
default:
elog(ERROR, "invalid jsonb scalar type");
}
}
/*
- * Given superheader pointer into buffer, initialize iterator. Must be a
- * container type.
+ * Initialize an iterator for iterating all elements in a container.
*/
static void
-iteratorFromContainerBuf(JsonbIterator *it, JsonbSuperHeader sheader)
+iteratorFromContainer(JsonbIterator *it, JsonbContainer *container)
{
- uint32 superheader = *(uint32 *) sheader;
-
- it->containerType = superheader & (JB_FARRAY | JB_FOBJECT);
- it->nElems = superheader & JB_CMASK;
- it->buffer = sheader;
+ it->containerType = container->header & (JB_FARRAY | JB_FOBJECT);
+ it->nElems = container->header & JB_CMASK;
+ it->buffer = (char *) container;
/* Array starts just after header */
- it->meta = (JEntry *) (sheader + sizeof(uint32));
+ it->meta = container->children;
it->state = jbi_start;
switch (it->containerType)
@@ -1544,7 +1386,7 @@ iteratorFromContainerBuf(JsonbIterator *it, JsonbSuperHeader sheader)
case JB_FARRAY:
it->dataProper =
(char *) it->meta + it->nElems * sizeof(JEntry);
- it->isScalar = (superheader & JB_FSCALAR) != 0;
+ it->isScalar = (container->header & JB_FSCALAR) != 0;
/* This is either a "raw scalar", or an array */
Assert(!it->isScalar || it->nElems == 1);
break;
@@ -1584,60 +1426,21 @@ static bool
formIterIsContainer(JsonbIterator **it, JsonbValue *val, JEntry *ent,
bool skipNested)
{
- if (JBE_ISNULL(*ent))
- {
- val->type = jbvNull;
- val->estSize = sizeof(JEntry);
+ fillJsonbValue(ent, (*it)->dataProper, val);
+ if (IsAJsonbScalar(val) || skipNested)
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;
-
- return false;
- }
- else if (JBE_ISNUMERIC(*ent))
- {
- val->type = jbvNumeric;
- val->val.numeric = (Numeric) ((*it)->dataProper + INTALIGN(JBE_OFF(*ent)));
-
- val->estSize = 2 * sizeof(JEntry) + VARSIZE_ANY(val->val.numeric);
-
- return false;
- }
- else if (JBE_ISBOOL(*ent))
- {
- val->type = jbvBool;
- val->val.boolean = JBE_ISBOOL_TRUE(*ent) != 0;
- val->estSize = sizeof(JEntry);
-
- return false;
- }
- else if (skipNested)
- {
- 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);
-
- return false;
- }
else
{
/*
- * Must be container type, so setup caller's iterator to point to
+ * It's a container type, so setup caller's iterator to point to
* that, and return indication of that.
*
* Get child iterator.
*/
JsonbIterator *child = palloc(sizeof(JsonbIterator));
- iteratorFromContainerBuf(child,
- (*it)->dataProper + INTALIGN(JBE_OFF(*ent)));
+ iteratorFromContainer(child, val->val.binary.data);
child->parent = *it;
*it = child;
@@ -1697,8 +1500,6 @@ appendKey(JsonbParseState *pstate, JsonbValue *string)
object->val.object.pairs[object->val.object.nPairs].key = *string;
object->val.object.pairs[object->val.object.nPairs].order = object->val.object.nPairs;
-
- object->estSize += string->estSize;
}
/*
@@ -1713,7 +1514,6 @@ appendValue(JsonbParseState *pstate, JsonbValue *scalarVal)
Assert(object->type == jbvObject);
object->val.object.pairs[object->val.object.nPairs++].value = *scalarVal;
- object->estSize += scalarVal->estSize;
}
/*
@@ -1740,7 +1540,6 @@ appendElement(JsonbParseState *pstate, JsonbValue *scalarVal)
}
array->val.array.elems[array->val.array.nElems++] = *scalarVal;
- array->estSize += scalarVal->estSize;
}
/*
@@ -1835,11 +1634,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, NULL) != 0)
{
res++;
if (ptr != res)
@@ -1851,45 +1646,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;
- }
-}
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 6b1ce9b3a9f..b67eb6555c1 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -106,7 +106,7 @@ static inline Datum populate_recordset_worker(FunctionCallInfo fcinfo,
bool have_record_arg);
/* Worker that takes care of common setup for us */
-static JsonbValue *findJsonbValueFromSuperHeaderLen(JsonbSuperHeader sheader,
+static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
uint32 flags,
char *key,
uint32 keylen);
@@ -286,7 +286,7 @@ jsonb_object_keys(PG_FUNCTION_ARGS)
state->sent_count = 0;
state->result = palloc(state->result_size * sizeof(char *));
- it = JsonbIteratorInit(VARDATA_ANY(jb));
+ it = JsonbIteratorInit(&jb->root);
while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
{
@@ -484,7 +484,7 @@ jsonb_object_field(PG_FUNCTION_ARGS)
Assert(JB_ROOT_IS_OBJECT(jb));
- it = JsonbIteratorInit(VARDATA_ANY(jb));
+ it = JsonbIteratorInit(&jb->root);
while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
{
@@ -545,7 +545,7 @@ jsonb_object_field_text(PG_FUNCTION_ARGS)
Assert(JB_ROOT_IS_OBJECT(jb));
- it = JsonbIteratorInit(VARDATA_ANY(jb));
+ it = JsonbIteratorInit(&jb->root);
while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
{
@@ -580,7 +580,7 @@ jsonb_object_field_text(PG_FUNCTION_ARGS)
StringInfo jtext = makeStringInfo();
Jsonb *tjb = JsonbValueToJsonb(&v);
- (void) JsonbToCString(jtext, VARDATA(tjb), -1);
+ (void) JsonbToCString(jtext, &tjb->root , -1);
result = cstring_to_text_with_len(jtext->data, jtext->len);
}
PG_RETURN_TEXT_P(result);
@@ -628,7 +628,7 @@ jsonb_array_element(PG_FUNCTION_ARGS)
Assert(JB_ROOT_IS_ARRAY(jb));
- it = JsonbIteratorInit(VARDATA_ANY(jb));
+ it = JsonbIteratorInit(&jb->root);
while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
{
@@ -682,7 +682,7 @@ jsonb_array_element_text(PG_FUNCTION_ARGS)
Assert(JB_ROOT_IS_ARRAY(jb));
- it = JsonbIteratorInit(VARDATA_ANY(jb));
+ it = JsonbIteratorInit(&jb->root);
while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
{
@@ -711,7 +711,7 @@ jsonb_array_element_text(PG_FUNCTION_ARGS)
StringInfo jtext = makeStringInfo();
Jsonb *tjb = JsonbValueToJsonb(&v);
- (void) JsonbToCString(jtext, VARDATA(tjb), -1);
+ (void) JsonbToCString(jtext, &tjb->root, -1);
result = cstring_to_text_with_len(jtext->data, jtext->len);
}
PG_RETURN_TEXT_P(result);
@@ -1155,7 +1155,7 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
have_array = false;
JsonbValue *jbvp = NULL;
JsonbValue tv;
- JsonbSuperHeader superHeader;
+ JsonbContainer *container;
if (array_contains_nulls(path))
ereport(ERROR,
@@ -1170,15 +1170,15 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb))
have_array = true;
- superHeader = (JsonbSuperHeader) VARDATA(jb);
+ container = &jb->root;
for (i = 0; i < npath; i++)
{
if (have_object)
{
- jbvp = findJsonbValueFromSuperHeaderLen(superHeader,
- JB_FOBJECT,
- VARDATA_ANY(pathtext[i]),
+ jbvp = findJsonbValueFromContainerLen(container,
+ JB_FOBJECT,
+ VARDATA_ANY(pathtext[i]),
VARSIZE_ANY_EXHDR(pathtext[i]));
}
else if (have_array)
@@ -1192,7 +1192,7 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
if (*endptr != '\0' || lindex > INT_MAX || lindex < 0)
PG_RETURN_NULL();
index = (uint32) lindex;
- jbvp = getIthJsonbValueFromSuperHeader(superHeader, index);
+ jbvp = getIthJsonbValueFromContainer(container, index);
}
else
{
@@ -1210,11 +1210,11 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
if (jbvp->type == jbvBinary)
{
- JsonbIterator *it = JsonbIteratorInit(jbvp->val.binary.data);
+ JsonbIterator *it = JsonbIteratorInit((JsonbContainer *) jbvp->val.binary.data);
int r;
r = JsonbIteratorNext(&it, &tv, true);
- superHeader = (JsonbSuperHeader) jbvp->val.binary.data;
+ container = (JsonbContainer *) jbvp->val.binary.data;
have_object = r == WJB_BEGIN_OBJECT;
have_array = r == WJB_BEGIN_ARRAY;
}
@@ -1238,7 +1238,7 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
if (as_text)
{
PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
- VARDATA(res),
+ &res->root,
VARSIZE(res))));
}
else
@@ -1428,7 +1428,7 @@ each_worker_jsonb(FunctionCallInfo fcinfo, bool as_text)
ALLOCSET_DEFAULT_MAXSIZE);
- it = JsonbIteratorInit(VARDATA_ANY(jb));
+ it = JsonbIteratorInit(&jb->root);
while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
{
@@ -1477,7 +1477,7 @@ each_worker_jsonb(FunctionCallInfo fcinfo, bool as_text)
StringInfo jtext = makeStringInfo();
Jsonb *jb = JsonbValueToJsonb(&v);
- (void) JsonbToCString(jtext, VARDATA(jb), 2 * v.estSize);
+ (void) JsonbToCString(jtext, &jb->root, 0);
sv = cstring_to_text_with_len(jtext->data, jtext->len);
}
@@ -1753,7 +1753,7 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, bool as_text)
ALLOCSET_DEFAULT_MAXSIZE);
- it = JsonbIteratorInit(VARDATA_ANY(jb));
+ it = JsonbIteratorInit(&jb->root);
while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
{
@@ -1797,7 +1797,7 @@ elements_worker_jsonb(FunctionCallInfo fcinfo, bool as_text)
StringInfo jtext = makeStringInfo();
Jsonb *jb = JsonbValueToJsonb(&v);
- (void) JsonbToCString(jtext, VARDATA(jb), 2 * v.estSize);
+ (void) JsonbToCString(jtext, &jb->root, 0);
sv = cstring_to_text_with_len(jtext->data, jtext->len);
}
@@ -2219,8 +2219,8 @@ populate_record_worker(FunctionCallInfo fcinfo, bool have_record_arg)
{
char *key = NameStr(tupdesc->attrs[i]->attname);
- v = findJsonbValueFromSuperHeaderLen(VARDATA(jb), JB_FOBJECT, key,
- strlen(key));
+ v = findJsonbValueFromContainerLen(&jb->root, JB_FOBJECT, key,
+ strlen(key));
}
/*
@@ -2282,7 +2282,7 @@ populate_record_worker(FunctionCallInfo fcinfo, bool have_record_arg)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot populate with a nested object unless use_json_as_text is true")));
else if (v->type == jbvBinary)
- s = JsonbToCString(NULL, v->val.binary.data, v->val.binary.len);
+ s = JsonbToCString(NULL, (JsonbContainer *) v->val.binary.data, v->val.binary.len);
else
elog(ERROR, "invalid jsonb type");
}
@@ -2529,8 +2529,8 @@ make_row_from_rec_and_jsonb(Jsonb *element, PopulateRecordsetState *state)
key = NameStr(tupdesc->attrs[i]->attname);
- v = findJsonbValueFromSuperHeaderLen(VARDATA(element), JB_FOBJECT,
- key, strlen(key));
+ v = findJsonbValueFromContainerLen(&element->root, JB_FOBJECT,
+ key, strlen(key));
/*
* We can't just skip here if the key wasn't found since we might have
@@ -2582,7 +2582,7 @@ make_row_from_rec_and_jsonb(Jsonb *element, PopulateRecordsetState *state)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot populate with a nested object unless use_json_as_text is true")));
else if (v->type == jbvBinary)
- s = JsonbToCString(NULL, v->val.binary.data, v->val.binary.len);
+ s = JsonbToCString(NULL, (JsonbContainer *) v->val.binary.data, v->val.binary.len);
else
elog(ERROR, "invalid jsonb type");
@@ -2750,7 +2750,7 @@ populate_recordset_worker(FunctionCallInfo fcinfo, bool have_record_arg)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot call jsonb_populate_recordset on non-array")));
- it = JsonbIteratorInit(VARDATA_ANY(jb));
+ it = JsonbIteratorInit(&jb->root);
while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
{
@@ -3019,11 +3019,11 @@ populate_recordset_object_field_end(void *state, char *fname, bool isnull)
}
/*
- * findJsonbValueFromSuperHeader() wrapper that sets up JsonbValue key string.
+ * findJsonbValueFromContainer() wrapper that sets up JsonbValue key string.
*/
static JsonbValue *
-findJsonbValueFromSuperHeaderLen(JsonbSuperHeader sheader, uint32 flags,
- char *key, uint32 keylen)
+findJsonbValueFromContainerLen(JsonbContainer *container, uint32 flags,
+ char *key, uint32 keylen)
{
JsonbValue k;
@@ -3031,5 +3031,5 @@ findJsonbValueFromSuperHeaderLen(JsonbSuperHeader sheader, uint32 flags,
k.val.string.val = key;
k.val.string.len = keylen;
- return findJsonbValueFromSuperHeader(sheader, flags, NULL, &k);
+ return findJsonbValueFromContainer(container, flags, &k);
}
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index dea64ad7805..fc746c8b742 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -16,60 +16,18 @@
#include "utils/array.h"
#include "utils/numeric.h"
-/*
- * JB_CMASK is used to extract count of items
- *
- * It's not possible to get more than 2^28 items into an Jsonb.
- */
-#define JB_CMASK 0x0FFFFFFF
-
-#define JB_FSCALAR 0x10000000
-#define JB_FOBJECT 0x20000000
-#define JB_FARRAY 0x40000000
-
-/* Get information on varlena Jsonb */
-#define JB_ROOT_COUNT(jbp_) ( *(uint32*) VARDATA(jbp_) & JB_CMASK)
-#define JB_ROOT_IS_SCALAR(jbp_) ( *(uint32*) VARDATA(jbp_) & JB_FSCALAR)
-#define JB_ROOT_IS_OBJECT(jbp_) ( *(uint32*) VARDATA(jbp_) & JB_FOBJECT)
-#define JB_ROOT_IS_ARRAY(jbp_) ( *(uint32*) VARDATA(jbp_) & JB_FARRAY)
-
-/* Jentry macros */
-#define JENTRY_POSMASK 0x0FFFFFFF
-#define JENTRY_ISFIRST 0x80000000
-#define JENTRY_TYPEMASK (~(JENTRY_POSMASK | JENTRY_ISFIRST))
-#define JENTRY_ISSTRING 0x00000000
-#define JENTRY_ISNUMERIC 0x10000000
-#define JENTRY_ISNEST 0x20000000
-#define JENTRY_ISNULL 0x40000000
-#define JENTRY_ISBOOL (JENTRY_ISNUMERIC | JENTRY_ISNEST)
-#define JENTRY_ISFALSE JENTRY_ISBOOL
-#define JENTRY_ISTRUE (JENTRY_ISBOOL | 0x40000000)
-/* Note possible multiple evaluations, also access to prior array element */
-#define JBE_ISFIRST(je_) (((je_).header & JENTRY_ISFIRST) != 0)
-#define JBE_ISSTRING(je_) (((je_).header & JENTRY_TYPEMASK) == JENTRY_ISSTRING)
-#define JBE_ISNUMERIC(je_) (((je_).header & JENTRY_TYPEMASK) == JENTRY_ISNUMERIC)
-#define JBE_ISNEST(je_) (((je_).header & JENTRY_TYPEMASK) == JENTRY_ISNEST)
-#define JBE_ISNULL(je_) (((je_).header & JENTRY_TYPEMASK) == JENTRY_ISNULL)
-#define JBE_ISBOOL(je_) (((je_).header & JENTRY_TYPEMASK & JENTRY_ISBOOL) == JENTRY_ISBOOL)
-#define JBE_ISBOOL_TRUE(je_) (((je_).header & JENTRY_TYPEMASK) == JENTRY_ISTRUE)
-#define JBE_ISBOOL_FALSE(je_) (JBE_ISBOOL(je_) && !JBE_ISBOOL_TRUE(je_))
-
-/* Get offset for Jentry */
-#define JBE_ENDPOS(je_) ((je_).header & JENTRY_POSMASK)
-#define JBE_OFF(je_) (JBE_ISFIRST(je_) ? 0 : JBE_ENDPOS((&(je_))[-1]))
-#define JBE_LEN(je_) (JBE_ISFIRST(je_) ? \
- JBE_ENDPOS(je_) \
- : JBE_ENDPOS(je_) - JBE_ENDPOS((&(je_))[-1]))
-
-/* Flags indicating a stage of sequential Jsonb processing */
-#define WJB_DONE 0x000
-#define WJB_KEY 0x001
-#define WJB_VALUE 0x002
-#define WJB_ELEM 0x004
-#define WJB_BEGIN_ARRAY 0x008
-#define WJB_END_ARRAY 0x010
-#define WJB_BEGIN_OBJECT 0x020
-#define WJB_END_OBJECT 0x040
+/* Tokens used when sequentially processing a jsonb value */
+typedef enum
+{
+ WJB_DONE,
+ WJB_KEY,
+ WJB_VALUE,
+ WJB_ELEM,
+ WJB_BEGIN_ARRAY,
+ WJB_END_ARRAY,
+ WJB_BEGIN_OBJECT,
+ WJB_END_OBJECT
+} JsonbIteratorToken;
/*
* When using a GIN index for jsonb, we choose to index both keys and values.
@@ -98,7 +56,6 @@
typedef struct JsonbPair JsonbPair;
typedef struct JsonbValue JsonbValue;
-typedef char *JsonbSuperHeader;
/*
* Jsonbs are varlena objects, so must meet the varlena convention that the
@@ -109,35 +66,115 @@ typedef char *JsonbSuperHeader;
* representation. Often, JsonbValues are just shims through which a Jsonb
* buffer is accessed, but they can also be deep copied and passed around.
*
- * We have an abstraction called a "superheader". This is a pointer that
- * conventionally points to the first item after our 4-byte uncompressed
- * varlena header, from which we can read flags using bitwise operations.
+ * Jsonb is a tree structure. Each node in the tree consists of a JEntry
+ * header, and a variable-length content. The JEntry header indicates what
+ * kind of a node it is, e.g. a string or an array, and the offset and length
+ * of its variable-length portion within the container.
*
- * Frequently, we pass a superheader reference to a function, and it doesn't
- * matter if it points to just after the start of a Jsonb, or to a temp buffer.
+ * The JEntry and the content of a node are not stored physically together.
+ * Instead, the container array or object has an array that holds the JEntrys
+ * of all the child nodes, followed by their variable-length portions.
+ *
+ * The root node is an exception; it has no parent array or object that could
+ * hold its JEntry. Hence, no JEntry header is stored for the root node. It
+ * is implicitly known that the the root node must be an array or an object,
+ * so we can get away without the type indicator as long as we can distinguish
+ * the two. For that purpose, both an array and an object begins with a uint32
+ * header field, which contains an JB_FOBJECT or JB_FARRAY flag. When a naked
+ * scalar value needs to be stored as a Jsonb value, what we actually store is
+ * an array with one element, with the flags in the array's header field set
+ * to JB_FSCALAR | JB_FARRAY.
+ *
+ * To encode the length and offset of the variable-length portion of each
+ * node in a compact way, the JEntry stores only the end offset within the
+ * variable-length portion of the container node. For the first JEntry in the
+ * container's JEntry array, that equals to the length of the node data. For
+ * convenience, the JENTRY_ISFIRST flag is set. The begin offset and length
+ * of the rest of the entries can be calculated using the end offset of the
+ * previous JEntry in the array.
+ *
+ * Overall, the Jsonb struct requires 4-bytes alignment. Within the struct,
+ * the variable-length portion of some node types is aligned to a 4-byte
+ * boundary, while others are not. When alignment is needed, the padding is
+ * in the beginning of the node that requires it. For example, if a numeric
+ * node is stored after a string node, so that the numeric node begins at
+ * offset 3, the variable-length portion of the numeric node will begin with
+ * one padding byte.
*/
-typedef struct
-{
- int32 vl_len_; /* varlena header (do not touch directly!) */
- uint32 superheader;
- /* (array of JEntry follows, size determined using uint32 superheader) */
-} Jsonb;
/*
- * JEntry: there is one of these for each key _and_ value for objects. Arrays
- * have one per element.
+ * Jentry format.
+ *
+ * The least significant 28 bits store the end offset of the entry (see
+ * JBE_ENDPOS, JBE_OFF, JBE_LEN macros below). The next three bits
+ * are used to store the type of the entry. The most significant bit
+ * is set on the first entry in an array of JEntrys.
+ */
+typedef uint32 JEntry;
+
+#define JENTRY_POSMASK 0x0FFFFFFF
+#define JENTRY_TYPEMASK 0x70000000
+#define JENTRY_ISFIRST 0x80000000
+
+/* values stored in the type bits */
+#define JENTRY_ISSTRING 0x00000000
+#define JENTRY_ISNUMERIC 0x10000000
+#define JENTRY_ISCONTAINER 0x20000000 /* array or object */
+#define JENTRY_ISBOOL_FALSE 0x30000000
+#define JENTRY_ISNULL 0x40000000
+#define JENTRY_ISBOOL_TRUE 0x70000000
+
+/* Note possible multiple evaluations, also access to prior array element */
+#define JBE_ISFIRST(je_) (((je_) & JENTRY_ISFIRST) != 0)
+#define JBE_ISSTRING(je_) (((je_) & JENTRY_TYPEMASK) == JENTRY_ISSTRING)
+#define JBE_ISNUMERIC(je_) (((je_) & JENTRY_TYPEMASK) == JENTRY_ISNUMERIC)
+#define JBE_ISCONTAINER(je_) (((je_) & JENTRY_TYPEMASK) == JENTRY_ISCONTAINER)
+#define JBE_ISNULL(je_) (((je_) & JENTRY_TYPEMASK) == JENTRY_ISNULL)
+#define JBE_ISBOOL_TRUE(je_) (((je_) & JENTRY_TYPEMASK) == JENTRY_ISBOOL_TRUE)
+#define JBE_ISBOOL_FALSE(je_) (((je_) & JENTRY_TYPEMASK) == JENTRY_ISBOOL_FALSE)
+#define JBE_ISBOOL(je_) (JBE_ISBOOL_TRUE(je_) || JBE_ISBOOL_FALSE(je_))
+
+/* Get offset for Jentry */
+#define JBE_ENDPOS(je_) ((je_) & JENTRY_POSMASK)
+#define JBE_OFF(je_) (JBE_ISFIRST(je_) ? 0 : JBE_ENDPOS((&(je_))[-1]))
+#define JBE_LEN(je_) (JBE_ISFIRST(je_) ? \
+ JBE_ENDPOS(je_) \
+ : JBE_ENDPOS(je_) - JBE_ENDPOS((&(je_))[-1]))
+
+/*
+ * A jsonb array or object node, within a Jsonb Datum.
*
- * The position offset points to the _end_ so that we can get the length by
- * subtraction from the previous entry. The JENTRY_ISFIRST flag indicates if
- * there is a previous entry.
+ * An array has one child for each element. An object has two children for
+ * each key/value pair.
*/
+typedef struct JsonbContainer
+{
+ uint32 header; /* number of elements or key/value pairs, and
+ * flags */
+ JEntry children[1]; /* variable length */
+
+ /* the data for each child node follows. */
+} JsonbContainer;
+
+/* flags for the header-field in JsonbContainer */
+#define JB_CMASK 0x0FFFFFFF
+#define JB_FSCALAR 0x10000000
+#define JB_FOBJECT 0x20000000
+#define JB_FARRAY 0x40000000
+
+/* The top-level on-disk format for a jsonb datum. */
typedef struct
{
- uint32 header; /* Shares some flags with superheader */
-} JEntry;
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ JsonbContainer root;
+} Jsonb;
+
+/* convenience macros for accessing the root container in a Jsonb datum */
+#define JB_ROOT_COUNT(jbp_) ( *(uint32*) VARDATA(jbp_) & JB_CMASK)
+#define JB_ROOT_IS_SCALAR(jbp_) ( *(uint32*) VARDATA(jbp_) & JB_FSCALAR)
+#define JB_ROOT_IS_OBJECT(jbp_) ( *(uint32*) VARDATA(jbp_) & JB_FOBJECT)
+#define JB_ROOT_IS_ARRAY(jbp_) ( *(uint32*) VARDATA(jbp_) & JB_FARRAY)
-#define IsAJsonbScalar(jsonbval) ((jsonbval)->type >= jbvNull && \
- (jsonbval)->type <= jbvBool)
/*
* JsonbValue: In-memory representation of Jsonb. This is a convenient
@@ -161,8 +198,6 @@ struct JsonbValue
jbvBinary
} type; /* Influences sort order */
- int estSize; /* Estimated size of node (including subnodes) */
-
union
{
Numeric numeric;
@@ -189,11 +224,14 @@ struct JsonbValue
struct
{
int len;
- char *data;
+ JsonbContainer *data;
} binary;
} val;
};
+#define IsAJsonbScalar(jsonbval) ((jsonbval)->type >= jbvNull && \
+ (jsonbval)->type <= jbvBool)
+
/*
* Pair within an Object.
*
@@ -294,27 +332,24 @@ extern Datum gin_consistent_jsonb_hash(PG_FUNCTION_ARGS);
extern Datum gin_triconsistent_jsonb_hash(PG_FUNCTION_ARGS);
/* Support functions */
-extern int compareJsonbSuperHeaderValue(JsonbSuperHeader a,
- JsonbSuperHeader b);
-extern JsonbValue *findJsonbValueFromSuperHeader(JsonbSuperHeader sheader,
+extern int compareJsonbContainers(JsonbContainer *a, JsonbContainer *b);
+extern JsonbValue *findJsonbValueFromContainer(JsonbContainer *sheader,
uint32 flags,
- uint32 *lowbound,
JsonbValue *key);
-extern JsonbValue *getIthJsonbValueFromSuperHeader(JsonbSuperHeader sheader,
+extern JsonbValue *getIthJsonbValueFromContainer(JsonbContainer *sheader,
uint32 i);
-extern JsonbValue *pushJsonbValue(JsonbParseState **pstate, int seq,
- JsonbValue *scalarVal);
-extern JsonbIterator *JsonbIteratorInit(JsonbSuperHeader buffer);
-extern int JsonbIteratorNext(JsonbIterator **it, JsonbValue *val,
+extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,
+ JsonbIteratorToken seq, JsonbValue *scalarVal);
+extern JsonbIterator *JsonbIteratorInit(JsonbContainer *container);
+extern JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val,
bool skipNested);
extern Jsonb *JsonbValueToJsonb(JsonbValue *val);
extern bool JsonbDeepContains(JsonbIterator **val,
JsonbIterator **mContained);
-extern JsonbValue *arrayToJsonbSortedArray(ArrayType *a);
extern void JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash);
/* jsonb.c support function */
-extern char *JsonbToCString(StringInfo out, JsonbSuperHeader in,
+extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
int estimated_len);
#endif /* __JSONB_H__ */