#define JBE_ISBOOL_FALSE(he_) (JBE_ISBOOL(he_) && !JBE_ISBOOL_TRUE(he_))
/*
- * State used while rolling up an arbitrary JsonbValue into a varlena/Jsonb
- * value
+ * State used while converting an arbitrary JsonbValue into a Jsonb value
+ * (4-byte varlena uncompressed representation of a Jsonb)
*/
-typedef struct CompressState
+typedef struct ConvertState
{
/* Preallocated buffer in which to form varlena/Jsonb value */
- char *buffer;
- JsonbSuperHeader ptr;
+ Jsonb *buffer;
+ /* Pointer into buffer */
+ char *ptr;
struct
{
uint32 i;
uint32 *header;
- JEntry *array;
+ JEntry *metaArray;
char *begin;
- } *levelstate, *lptr, *pptr;
+ } *levelstate,
+ *curlptr,
+ *prvlptr;
- uint32 maxlevel;
+ /* Current size of buffer holding levelstate */
+ Size levelSz;
-} CompressState;
+} ConvertState;
static int lexicalCompareJsonbStringValue(const void *a, const void *b);
-static uint32 compressJsonb(JsonbValue * v, JsonbSuperHeader sheader);
-static void walkUncompressedJsonb(JsonbValue * value, CompressState * state,
- uint32 nestlevel);
+static Size convertJsonb(JsonbValue * v, Jsonb* buffer);
+static void walkJsonbValueConversion(JsonbValue * value, ConvertState * state,
+ uint32 nestlevel);
+static void putJsonbValueConversion(ConvertState * state, JsonbValue * value,
+ uint32 flags, uint32 level);
+static void putStringConversion(ConvertState * state, JsonbValue * value,
+ uint32 level, uint32 i);
static void parseBuffer(JsonbIterator * it, char *buffer);
static bool formAnswer(JsonbIterator ** it, JsonbValue * v, JEntry * e,
bool skipNested);
static JsonbIterator *up(JsonbIterator * it);
-static void compressJsonbValue(CompressState * state, JsonbValue * value,
- uint32 flags, uint32 level);
-static void putJEntryString(CompressState * state, JsonbValue * value,
- uint32 level, uint32 i);
static ToJsonbState *pushState(ToJsonbState ** state);
static void appendKey(ToJsonbState * state, JsonbValue * v);
static void appendValue(ToJsonbState * state, JsonbValue * v);
/* Scalar value */
ToJsonbState *state = NULL;
JsonbValue *res;
- uint32 sz;
+ Size sz;
JsonbValue scalarArray;
scalarArray.type = jbvArray;
res = pushJsonbValue(&state, WJB_END_ARRAY, NULL);
out = palloc(VARHDRSZ + res->size);
- sz = compressJsonb(res, VARDATA(out));
+ sz = convertJsonb(res, out);
Assert(sz <= res->size);
SET_VARSIZE(out, sz + VARHDRSZ);
}
uint32 sz;
out = palloc(VARHDRSZ + v->size);
- sz = compressJsonb(v, VARDATA(out));
+ sz = convertJsonb(v, out);
Assert(sz <= v->size);
SET_VARSIZE(out, VARHDRSZ + sz);
}
}
/*
- * puts JsonbValue tree into a preallocated buffer
+ * Put JsonbValue tree into a preallocated buffer Jsonb buffer
*/
-static uint32
-compressJsonb(JsonbValue * v, char *buffer)
+static Size
+convertJsonb(JsonbValue * v, Jsonb *buffer)
{
uint32 l = 0;
- CompressState state;
+ ConvertState state;
/* Should not already have binary representation */
Assert(v->type != jbvBinary);
- state.buffer = state.ptr = buffer;
- state.maxlevel = 8;
- state.levelstate = palloc(sizeof(*state.levelstate) * state.maxlevel);
+ state.buffer = buffer;
+ /* Start from superheader */
+ state.ptr = VARDATA(state.buffer);
+ state.levelSz = 8;
+ state.levelstate = palloc(sizeof(*state.levelstate) * state.levelSz);
- walkUncompressedJsonb(v, &state, 0);
+ walkJsonbValueConversion(v, &state, 0);
- l = state.ptr - buffer;
+ l = state.ptr - VARDATA(state.buffer);
Assert(l <= v->size);
return l;
}
/*
- * Walk the tree representation of jsonb
+ * Walk the tree representation of Jsonb, as part of the process of converting
+ * a JsonbValue to a Jsonb
*/
static void
-walkUncompressedJsonb(JsonbValue * value, CompressState * state,
- uint32 nestlevel)
+walkJsonbValueConversion(JsonbValue * value, ConvertState * state,
+ uint32 nestlevel)
{
int i;
switch (value->type)
{
case jbvArray:
- compressJsonbValue(state, value, WJB_BEGIN_ARRAY, nestlevel);
+ putJsonbValueConversion(state, value, WJB_BEGIN_ARRAY, nestlevel);
for (i = 0; i < value->array.nElems; i++)
{
if ((value->array.elems[i].type >= jbvNull &&
value->array.elems[i].type < jbvArray) ||
value->array.elems[i].type == jbvBinary)
- compressJsonbValue(state, value->array.elems + i, WJB_ELEM, nestlevel);
+ putJsonbValueConversion(state, value->array.elems + i,
+ WJB_ELEM, nestlevel);
else
- walkUncompressedJsonb(value->array.elems + i, state,
+ walkJsonbValueConversion(value->array.elems + i, state,
nestlevel + 1);
}
- compressJsonbValue(state, value, WJB_END_ARRAY, nestlevel);
+ putJsonbValueConversion(state, value, WJB_END_ARRAY, nestlevel);
break;
case jbvObject:
- compressJsonbValue(state, value, WJB_BEGIN_OBJECT, nestlevel);
+ putJsonbValueConversion(state, value, WJB_BEGIN_OBJECT, nestlevel);
for (i = 0; i < value->object.nPairs; i++)
{
- compressJsonbValue(state, &value->object.pairs[i].key, WJB_KEY, nestlevel);
+ putJsonbValueConversion(state, &value->object.pairs[i].key,
+ WJB_KEY, nestlevel);
if ((value->object.pairs[i].value.type >= jbvNull &&
value->object.pairs[i].value.type < jbvArray) ||
value->object.pairs[i].value.type == jbvBinary)
- compressJsonbValue(state, &value->object.pairs[i].value,
+ putJsonbValueConversion(state,
+ &value->object.pairs[i].value,
WJB_VALUE, nestlevel);
else
- walkUncompressedJsonb(&value->object.pairs[i].value,
+ walkJsonbValueConversion(&value->object.pairs[i].value,
state, nestlevel + 1);
}
- compressJsonbValue(state, value, WJB_END_OBJECT, nestlevel);
+ putJsonbValueConversion(state, value, WJB_END_OBJECT, nestlevel);
break;
default:
elog(ERROR, "unknown type of jsonb container");
}
/*
- * Iteration over binary jsonb
- */
-
-/*
- * Initialize iterator from superheader
- */
-static void
-parseBuffer(JsonbIterator * it, JsonbSuperHeader sheader)
-{
- uint32 superheader = *(uint32 *) sheader;
-
- it->containerType = superheader & (JB_FLAG_ARRAY | JB_FLAG_OBJECT);
- it->nElems = superheader & JB_COUNT_MASK;
- it->buffer = sheader;
-
- /* Array starts just after header */
- it->metaArray = (JEntry *) (sheader + sizeof(uint32));
- it->state = jbi_start;
-
- switch (it->containerType)
- {
- case JB_FLAG_ARRAY:
- it->dataProper =
- (char *) it->metaArray + it->nElems * sizeof(JEntry);
- it->isScalar = (superheader & JB_FLAG_SCALAR) != 0;
- /* This is either a "raw scalar", or an array */
- Assert(!it->isScalar || it->nElems == 1);
- break;
- case JB_FLAG_OBJECT:
- /*
- * Offset reflects that nElems indicates JsonbPairs in an object
- *
- * Each key and each value have Jentry metadata.
- */
- it->dataProper =
- (char *) it->metaArray + it->nElems * sizeof(JEntry) * 2;
- break;
- default:
- elog(ERROR, "unknown type of jsonb container");
- }
-}
-
-static bool
-formAnswer(JsonbIterator ** it, JsonbValue * v, JEntry * e, bool skipNested)
-{
- if (JBE_ISSTRING(*e))
- {
- v->type = jbvString;
- v->string.val = (*it)->dataProper + JBE_OFF(*e);
- v->string.len = JBE_LEN(*e);
- v->size = sizeof(JEntry) + v->string.len;
-
- return false;
- }
- else if (JBE_ISBOOL(*e))
- {
- v->type = jbvBool;
- v->boolean = JBE_ISBOOL_TRUE(*e) != 0;
- v->size = sizeof(JEntry);
-
- return false;
- }
- else if (JBE_ISNUMERIC(*e))
- {
- v->type = jbvNumeric;
- v->numeric = (Numeric) ((*it)->dataProper + INTALIGN(JBE_OFF(*e)));
-
- v->size = 2 * sizeof(JEntry) + VARSIZE_ANY(v->numeric);
-
- return false;
- }
- else if (JBE_ISNULL(*e))
- {
- v->type = jbvNull;
- v->size = sizeof(JEntry);
-
- return false;
- }
- else if (skipNested)
- {
- v->type = jbvBinary;
- v->binary.data = (*it)->dataProper + INTALIGN(JBE_OFF(*e));
- v->binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
- v->size = v->binary.len + 2 * sizeof(JEntry);
-
- return false;
- }
- else
- {
- JsonbIterator *nit = palloc(sizeof(*nit));
-
- parseBuffer(nit, (*it)->dataProper + INTALIGN(JBE_OFF(*e)));
- nit->next = *it;
- *it = nit;
-
- return true;
- }
-}
-
-static JsonbIterator *
-up(JsonbIterator * it)
-{
- JsonbIterator *v = it->next;
-
- pfree(it);
-
- return v;
-}
-
-/*
- * Transformation from tree to binary representation of jsonb
+ * As part of the process of converting an arbitrary JsonbValue to a Jsonb,
+ * copy an arbitrary individual JsonbValue. This function may copy over any
+ * type of value, even containers (Objects/arrays). However, it is not
+ * responsible for recursive aspects of walking the tree (so only top-level
+ * Object/array details are handled). No details about their
+ * keys/values/elements are touched. The function is called separately for the
+ * start of an Object/Array, and the end.
+ *
+ * This is a worker function for walkJsonbValueConversion.
*/
-#define curLevelState (state->lptr)
-#define prevLevelState (state->pptr)
-
static void
-compressJsonbValue(CompressState * state, JsonbValue * value, uint32 flags,
- uint32 level)
+putJsonbValueConversion(ConvertState * state, JsonbValue * value, uint32 flags,
+ uint32 level)
{
- if (level == state->maxlevel)
+ if (level == state->levelSz)
{
- state->maxlevel *= 2;
+ state->levelSz *= 2;
state->levelstate = repalloc(state->levelstate,
- sizeof(*state->levelstate) * state->maxlevel);
+ sizeof(*state->levelstate) * state->levelSz);
}
- curLevelState = state->levelstate + level;
+ state->curlptr = state->levelstate + level;
if (flags & (WJB_BEGIN_ARRAY | WJB_BEGIN_OBJECT))
{
Assert(((flags & WJB_BEGIN_ARRAY) && value->type == jbvArray) ||
((flags & WJB_BEGIN_OBJECT) && value->type == jbvObject));
- curLevelState->begin = state->ptr;
+ state->curlptr->begin = state->ptr;
+
+ padlen = INTALIGN(state->ptr - VARDATA(state->buffer)) -
+ (state->ptr - VARDATA(state->buffer));
- padlen = INTALIGN(state->ptr - state->buffer) - (state->ptr -
- state->buffer);
/*
* Add padding as necessary
*/
state->ptr++;
}
- curLevelState->header = (uint32 *) state->ptr;
- state->ptr += sizeof(*curLevelState->header);
+ state->curlptr->header = (uint32 *) state->ptr;
+ /* Advance past header */
+ state->ptr += sizeof(* state->curlptr->header);
- curLevelState->array = (JEntry *) state->ptr;
- curLevelState->i = 0;
+ state->curlptr->metaArray = (JEntry *) state->ptr;
+ state->curlptr->i = 0;
if (value->type == jbvArray)
{
- *curLevelState->header = value->array.nElems | JB_FLAG_ARRAY;
+ *state->curlptr->header = value->array.nElems | JB_FLAG_ARRAY;
state->ptr += sizeof(JEntry) * value->array.nElems;
if (value->array.scalar)
{
Assert(value->array.nElems == 1);
Assert(level == 0);
- *curLevelState->header |= JB_FLAG_SCALAR;
+ *state->curlptr->header |= JB_FLAG_SCALAR;
}
}
else
{
- *curLevelState->header = value->object.nPairs | JB_FLAG_OBJECT;
+ *state->curlptr->header = value->object.nPairs | JB_FLAG_OBJECT;
state->ptr += sizeof(JEntry) * value->object.nPairs * 2;
}
}
else if (flags & WJB_ELEM)
{
- putJEntryString(state, value, level, curLevelState->i);
- curLevelState->i++;
+ putStringConversion(state, value, level, state->curlptr->i);
+ state->curlptr->i++;
}
else if (flags & WJB_KEY)
{
Assert(value->type == jbvString);
- putJEntryString(state, value, level, curLevelState->i * 2);
+ putStringConversion(state, value, level, state->curlptr->i * 2);
}
else if (flags & WJB_VALUE)
{
- putJEntryString(state, value, level, curLevelState->i * 2 + 1);
- curLevelState->i++;
+ putStringConversion(state, value, level, state->curlptr->i * 2 + 1);
+ state->curlptr->i++;
}
else if (flags & (WJB_END_ARRAY | WJB_END_OBJECT))
{
Assert(((flags & WJB_END_ARRAY) && value->type == jbvArray) ||
((flags & WJB_END_OBJECT) && value->type == jbvObject));
+
if (level == 0)
return;
- len = state->ptr - (char *) curLevelState->begin;
+ len = state->ptr - (char *) state->curlptr->begin;
- prevLevelState = curLevelState - 1;
+ state->prvlptr = state->curlptr - 1;
- if (*prevLevelState->header & JB_FLAG_ARRAY)
+ if (*state->prvlptr->header & JB_FLAG_ARRAY)
{
- i = prevLevelState->i;
+ i = state->prvlptr->i;
- prevLevelState->array[i].header = JENTRY_ISNEST;
+ state->prvlptr->metaArray[i].header = JENTRY_ISNEST;
if (i == 0)
- prevLevelState->array[0].header |= JENTRY_ISFIRST | len;
+ state->prvlptr->metaArray[0].header |= JENTRY_ISFIRST | len;
else
- prevLevelState->array[i].header |=
- (prevLevelState->array[i - 1].header & JENTRY_POSMASK) + len;
+ state->prvlptr->metaArray[i].header |=
+ (state->prvlptr->metaArray[i - 1].header & JENTRY_POSMASK) + len;
}
- else if (*prevLevelState->header & JB_FLAG_OBJECT)
+ else if (*state->prvlptr->header & JB_FLAG_OBJECT)
{
- i = 2 * prevLevelState->i + 1; /* VALUE, not a KEY */
+ i = 2 * state->prvlptr->i + 1; /* Value, not key */
- prevLevelState->array[i].header = JENTRY_ISNEST;
+ state->prvlptr->metaArray[i].header = JENTRY_ISNEST;
- prevLevelState->array[i].header |=
- (prevLevelState->array[i - 1].header & JENTRY_POSMASK) + len;
+ state->prvlptr->metaArray[i].header |=
+ (state->prvlptr->metaArray[i - 1].header & JENTRY_POSMASK) + len;
}
else
{
elog(ERROR, "invalid jsonb container type");
}
- Assert(state->ptr - curLevelState->begin <= value->size);
- prevLevelState->i++;
+ Assert(state->ptr - state->curlptr->begin <= value->size);
+ state->prvlptr->i++;
}
else
{
- elog(ERROR, "unknown flag in tree walk");
+ elog(ERROR, "unknown flag encountered during jsonb tree walk");
}
}
+/*
+ * As part of the process of converting an arbitrary JsonbValue to a Jsonb,
+ * copy a string.
+ *
+ * This is a worker function for putJsonbValueConversion(), handling aspects of
+ * copying strings in respect of all scalar values. It handles the details
+ * with regard to Jentry meta-data within convert state.
+ */
static void
-putJEntryString(CompressState * state, JsonbValue * value, uint32 level,
- uint32 i)
+putStringConversion(ConvertState * state, JsonbValue * value,
+ uint32 level, uint32 i)
{
short p, padlen;
- curLevelState = state->levelstate + level;
+ state->curlptr = state->levelstate + level;
if (i == 0)
- curLevelState->array[0].header = JENTRY_ISFIRST;
+ state->curlptr->metaArray[0].header = JENTRY_ISFIRST;
else
- curLevelState->array[i].header = 0;
+ state->curlptr->metaArray[i].header = 0;
switch (value->type)
{
case jbvNull:
- curLevelState->array[i].header |= JENTRY_ISNULL;
+ state->curlptr->metaArray[i].header |= JENTRY_ISNULL;
if (i > 0)
- curLevelState->array[i].header |=
- curLevelState->array[i - 1].header & JENTRY_POSMASK;
+ state->curlptr->metaArray[i].header |=
+ state->curlptr->metaArray[i - 1].header & JENTRY_POSMASK;
break;
case jbvString:
memcpy(state->ptr, value->string.val, value->string.len);
state->ptr += value->string.len;
if (i == 0)
- curLevelState->array[i].header |= value->string.len;
+ state->curlptr->metaArray[i].header |= value->string.len;
else
- curLevelState->array[i].header |=
- (curLevelState->array[i - 1].header & JENTRY_POSMASK) +
+ state->curlptr->metaArray[i].header |=
+ (state->curlptr->metaArray[i - 1].header & JENTRY_POSMASK) +
value->string.len;
break;
case jbvBool:
- curLevelState->array[i].header |= (value->boolean) ?
+ state->curlptr->metaArray[i].header |= (value->boolean) ?
JENTRY_ISTRUE : JENTRY_ISFALSE;
if (i > 0)
- curLevelState->array[i].header |=
- curLevelState->array[i - 1].header & JENTRY_POSMASK;
+ state->curlptr->metaArray[i].header |=
+ state->curlptr->metaArray[i - 1].header & JENTRY_POSMASK;
break;
case jbvNumeric:
{
int numlen = VARSIZE_ANY(value->numeric);
- padlen = INTALIGN(state->ptr - state->buffer) -
- (state->ptr - state->buffer);
+ padlen = INTALIGN(state->ptr - VARDATA(state->buffer)) -
+ (state->ptr - VARDATA(state->buffer));
/*
* Add padding as necessary
memcpy(state->ptr, value->numeric, numlen);
state->ptr += numlen;
- curLevelState->array[i].header |= JENTRY_ISNUMERIC;
+ state->curlptr->metaArray[i].header |= JENTRY_ISNUMERIC;
if (i == 0)
- curLevelState->array[i].header |= padlen + numlen;
+ state->curlptr->metaArray[i].header |= padlen + numlen;
else
- curLevelState->array[i].header |=
- (curLevelState->array[i - 1].header & JENTRY_POSMASK) +
+ state->curlptr->metaArray[i].header |=
+ (state->curlptr->metaArray[i - 1].header & JENTRY_POSMASK) +
padlen + numlen;
break;
}
case jbvBinary:
{
- padlen = INTALIGN(state->ptr - state->buffer) - (state->ptr -
- state->buffer);
+ padlen = INTALIGN(state->ptr - VARDATA(state->buffer)) -
+ (state->ptr - VARDATA(state->buffer));
+
/*
* Add padding as necessary
*/
memcpy(state->ptr, value->binary.data, value->binary.len);
state->ptr += value->binary.len;
- curLevelState->array[i].header |= JENTRY_ISNEST;
+ state->curlptr->metaArray[i].header |= JENTRY_ISNEST;
if (i == 0)
- curLevelState->array[i].header |= value->binary.len + padlen;
+ state->curlptr->metaArray[i].header |= value->binary.len + padlen;
else
- curLevelState->array[i].header |=
- (curLevelState->array[i - 1].header & JENTRY_POSMASK) +
+ state->curlptr->metaArray[i].header |=
+ (state->curlptr->metaArray[i - 1].header & JENTRY_POSMASK) +
value->binary.len + padlen;
}
break;
}
}
+/*
+ * Iteration over binary jsonb
+ */
+
+/*
+ * Initialize iterator from superheader
+ */
+static void
+parseBuffer(JsonbIterator * it, JsonbSuperHeader sheader)
+{
+ uint32 superheader = *(uint32 *) sheader;
+
+ it->containerType = superheader & (JB_FLAG_ARRAY | JB_FLAG_OBJECT);
+ it->nElems = superheader & JB_COUNT_MASK;
+ it->buffer = sheader;
+
+ /* Array starts just after header */
+ it->metaArray = (JEntry *) (sheader + sizeof(uint32));
+ it->state = jbi_start;
+
+ switch (it->containerType)
+ {
+ case JB_FLAG_ARRAY:
+ it->dataProper =
+ (char *) it->metaArray + it->nElems * sizeof(JEntry);
+ it->isScalar = (superheader & JB_FLAG_SCALAR) != 0;
+ /* This is either a "raw scalar", or an array */
+ Assert(!it->isScalar || it->nElems == 1);
+ break;
+ case JB_FLAG_OBJECT:
+ /*
+ * Offset reflects that nElems indicates JsonbPairs in an object
+ *
+ * Each key and each value have Jentry metadata.
+ */
+ it->dataProper =
+ (char *) it->metaArray + it->nElems * sizeof(JEntry) * 2;
+ break;
+ default:
+ elog(ERROR, "unknown type of jsonb container");
+ }
+}
+
+static bool
+formAnswer(JsonbIterator ** it, JsonbValue * v, JEntry * e, bool skipNested)
+{
+ if (JBE_ISSTRING(*e))
+ {
+ v->type = jbvString;
+ v->string.val = (*it)->dataProper + JBE_OFF(*e);
+ v->string.len = JBE_LEN(*e);
+ v->size = sizeof(JEntry) + v->string.len;
+
+ return false;
+ }
+ else if (JBE_ISBOOL(*e))
+ {
+ v->type = jbvBool;
+ v->boolean = JBE_ISBOOL_TRUE(*e) != 0;
+ v->size = sizeof(JEntry);
+
+ return false;
+ }
+ else if (JBE_ISNUMERIC(*e))
+ {
+ v->type = jbvNumeric;
+ v->numeric = (Numeric) ((*it)->dataProper + INTALIGN(JBE_OFF(*e)));
+
+ v->size = 2 * sizeof(JEntry) + VARSIZE_ANY(v->numeric);
+
+ return false;
+ }
+ else if (JBE_ISNULL(*e))
+ {
+ v->type = jbvNull;
+ v->size = sizeof(JEntry);
+
+ return false;
+ }
+ else if (skipNested)
+ {
+ v->type = jbvBinary;
+ v->binary.data = (*it)->dataProper + INTALIGN(JBE_OFF(*e));
+ v->binary.len = JBE_LEN(*e) - (INTALIGN(JBE_OFF(*e)) - JBE_OFF(*e));
+ v->size = v->binary.len + 2 * sizeof(JEntry);
+
+ return false;
+ }
+ else
+ {
+ JsonbIterator *nit = palloc(sizeof(*nit));
+
+ parseBuffer(nit, (*it)->dataProper + INTALIGN(JBE_OFF(*e)));
+ nit->next = *it;
+ *it = nit;
+
+ return true;
+ }
+}
+
+static JsonbIterator *
+up(JsonbIterator * it)
+{
+ JsonbIterator *v = it->next;
+
+ pfree(it);
+
+ return v;
+}
+
/*
* Iteration-like forming jsonb
*/