Commit 364ddc3e authored by Heikki Linnakangas's avatar Heikki Linnakangas

Clean up jsonb code.

The main target of this cleanup is the convertJsonb() function, but I also
touched a lot of other things that I spotted into in the process.

The new convertToJsonb() function uses an output buffer that's resized on
demand, so the code to estimate of the size of JsonbValue is removed.

The on-disk format was not changed, even though I refactored the structs
used to handle it. The term "superheader" is replaced with "container".

The jsonb_exists_any and jsonb_exists_all functions no longer sort the input
array. That was a premature optimization, the idea being that if there are
duplicates in the input array, you only need to check them once. Also,
sorting the array saves some effort in the binary search used to find a key
within an object. But there were drawbacks too: the sorting and
deduplicating obviously isn't free, and in the typical case there are no
duplicates to remove, and the gain in the binary search was minimal. Remove
all that, which makes the code simpler too.

This includes a bug-fix; the total length of the elements in a jsonb array
or object mustn't exceed 2^28. That is now checked.
parent 4d155d8b
......@@ -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;
......
......@@ -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;
......
......@@ -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)
{
......
/*-------------------------------------------------------------------------
*
* 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
......@@ -1056,63 +953,6 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
return false;
}
/*
* Convert a Postgres text array to a Jsonb array, sorted and with
* de-duplicated key elements. This is used for searching an object for items
* in the array, so we enforce that the number of strings cannot exceed
* JSONB_MAX_PAIRS.
*/
JsonbValue *
arrayToJsonbSortedArray(ArrayType *array)
{
Datum *key_datums;
bool *key_nulls;
int elem_count;
JsonbValue *result;
int i,
j;
/* Extract data for sorting */
deconstruct_array(array, TEXTOID, -1, false, 'i', &key_datums, &key_nulls,
&elem_count);
if (elem_count == 0)
return NULL;
/*
* A text array uses at least eight bytes per element, so any overflow in
* "key_count * sizeof(JsonbPair)" is small enough for palloc() to catch.
* However, credible improvements to the array format could invalidate
* that assumption. Therefore, use an explicit check rather than relying
* on palloc() to complain.
*/
if (elem_count > JSONB_MAX_PAIRS)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of array elements (%d) exceeds maximum allowed Jsonb pairs (%zu)",
elem_count, JSONB_MAX_PAIRS)));
result = palloc(sizeof(JsonbValue));
result->type = jbvArray;
result->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;
}
}
......@@ -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);
}
......@@ -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__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment