More jsonb cleanup.

Fix JSONB_MAX_ELEMS and JSONB_MAX_PAIRS macros to use CB_MASK in the
calculation. JENTRY_POSMASK happens to have the same value at the moment,
but that's just coincidental.

Refactor jsonb iterator functions, for readability.

Get rid of the JENTRY_ISFIRST flag. Whenever we handle JEntrys, we have
access to the whole array and have enough context information to know
which entry is the first. This frees up one bit in the JEntry header for
future use. While we're at it, shuffle the JEntry bits so that boolean
true and false go together, for aesthetic reasons.

Bump catalog version as this changes the on-disk format slightly.
This commit is contained in:
Heikki Linnakangas 2014-05-09 15:55:56 +03:00
parent 46dddf7673
commit d9daff0e0c
3 changed files with 139 additions and 216 deletions

View File

@ -21,20 +21,16 @@
#include "utils/memutils.h" #include "utils/memutils.h"
/* /*
* Twice as many values may be stored within pairs (for an Object) than within * Maximum number of elements in an array (or key/value pairs in an object).
* elements (for an Array), modulo the current MaxAllocSize limitation. Note * This is limited by two things: the size of the JEntry array must fit
* that JSONB_MAX_PAIRS is derived from the number of possible pairs, not * in MaxAllocSize, and the number of elements (or pairs) must fit in the bits
* values (as is the case for arrays and their elements), because we're * reserved for that in the JsonbContainer.header field.
* concerned about limitations on the representation of the number of pairs. *
* Over twice the memory is required to store n JsonbPairs as n JsonbValues. * (the total size of an array's elements is also limited by JENTRY_POSMASK,
* It only takes exactly twice as much disk space for storage, though. The * but we're not concerned about that here)
* JsonbPair (not an actual pair of values) representation is used here because
* that is what is subject to the MaxAllocSize restriction when building an
* object.
*/ */
#define JSONB_MAX_ELEMS (Min(MaxAllocSize / sizeof(JsonbValue), JENTRY_POSMASK)) #define JSONB_MAX_ELEMS (Min(MaxAllocSize / sizeof(JsonbValue), JB_CMASK))
#define JSONB_MAX_PAIRS (Min(MaxAllocSize / sizeof(JsonbPair), \ #define JSONB_MAX_PAIRS (Min(MaxAllocSize / sizeof(JsonbPair), JB_CMASK))
JENTRY_POSMASK))
/* /*
* convertState: a resizeable buffer used when constructing a Jsonb datum * convertState: a resizeable buffer used when constructing a Jsonb datum
@ -46,7 +42,8 @@ typedef struct
int allocatedsz; int allocatedsz;
} convertState; } convertState;
static void fillJsonbValue(JEntry *entry, char *payload_base, JsonbValue *result); static void fillJsonbValue(JEntry *array, int index, char *base_addr,
JsonbValue *result);
static int compareJsonbScalarValue(JsonbValue *a, JsonbValue *b); static int compareJsonbScalarValue(JsonbValue *a, JsonbValue *b);
static int lexicalCompareJsonbStringValue(const void *a, const void *b); static int lexicalCompareJsonbStringValue(const void *a, const void *b);
static Jsonb *convertToJsonb(JsonbValue *val); static Jsonb *convertToJsonb(JsonbValue *val);
@ -60,9 +57,7 @@ static void appendToBuffer(convertState *buffer, char *data, int len);
static void copyToBuffer(convertState *buffer, int offset, char *data, int len); static void copyToBuffer(convertState *buffer, int offset, char *data, int len);
static short padBufferToInt(convertState *buffer); static short padBufferToInt(convertState *buffer);
static void iteratorFromContainer(JsonbIterator *it, JsonbContainer *container); static JsonbIterator *iteratorFromContainer(JsonbContainer *container, JsonbIterator *parent);
static bool formIterIsContainer(JsonbIterator **it, JsonbValue *val,
JEntry *ent, bool skipNested);
static JsonbIterator *freeAndGetParent(JsonbIterator *it); static JsonbIterator *freeAndGetParent(JsonbIterator *it);
static JsonbParseState *pushState(JsonbParseState **pstate); static JsonbParseState *pushState(JsonbParseState **pstate);
static void appendKey(JsonbParseState *pstate, JsonbValue *scalarVal); static void appendKey(JsonbParseState *pstate, JsonbValue *scalarVal);
@ -276,7 +271,7 @@ JsonbValue *
findJsonbValueFromContainer(JsonbContainer *container, uint32 flags, findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
JsonbValue *key) JsonbValue *key)
{ {
JEntry *array = container->children; JEntry *children = container->children;
int count = (container->header & JB_CMASK); int count = (container->header & JB_CMASK);
JsonbValue *result = palloc(sizeof(JsonbValue)); JsonbValue *result = palloc(sizeof(JsonbValue));
@ -284,14 +279,12 @@ findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
if (flags & JB_FARRAY & container->header) if (flags & JB_FARRAY & container->header)
{ {
char *data = (char *) (array + (container->header & JB_CMASK)); char *base_addr = (char *) (children + count);
int i; int i;
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
JEntry *e = array + i; fillJsonbValue(children, i, base_addr, result);
fillJsonbValue(e, data, result);
if (key->type == result->type) if (key->type == result->type)
{ {
@ -303,7 +296,7 @@ findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
else if (flags & JB_FOBJECT & container->header) else if (flags & JB_FOBJECT & container->header)
{ {
/* Since this is an object, account for *Pairs* of Jentrys */ /* Since this is an object, account for *Pairs* of Jentrys */
char *data = (char *) (array + (container->header & JB_CMASK) * 2); char *base_addr = (char *) (children + count * 2);
uint32 stopLow = 0, uint32 stopLow = 0,
stopMiddle; stopMiddle;
@ -313,7 +306,7 @@ findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
/* Binary search on object/pair keys *only* */ /* Binary search on object/pair keys *only* */
while (stopLow < count) while (stopLow < count)
{ {
JEntry *entry; int index;
int difference; int difference;
JsonbValue candidate; JsonbValue candidate;
@ -323,20 +316,18 @@ findJsonbValueFromContainer(JsonbContainer *container, uint32 flags,
*/ */
stopMiddle = stopLow + (count - stopLow) / 2; stopMiddle = stopLow + (count - stopLow) / 2;
entry = array + stopMiddle * 2; index = stopMiddle * 2;
candidate.type = jbvString; candidate.type = jbvString;
candidate.val.string.val = data + JBE_OFF(*entry); candidate.val.string.val = base_addr + JBE_OFF(children, index);
candidate.val.string.len = JBE_LEN(*entry); candidate.val.string.len = JBE_LEN(children, index);
difference = lengthCompareJsonbStringValue(&candidate, key); difference = lengthCompareJsonbStringValue(&candidate, key);
if (difference == 0) if (difference == 0)
{ {
/* Found our value (from key/value pair) */ /* Found our key, return value */
JEntry *v = entry + 1; fillJsonbValue(children, index + 1, base_addr, result);
fillJsonbValue(v, data, result);
return result; return result;
} }
@ -364,71 +355,69 @@ JsonbValue *
getIthJsonbValueFromContainer(JsonbContainer *container, uint32 i) getIthJsonbValueFromContainer(JsonbContainer *container, uint32 i)
{ {
JsonbValue *result; JsonbValue *result;
JEntry *e; char *base_addr;
char *data;
uint32 nelements; uint32 nelements;
if ((container->header & JB_FARRAY) == 0) if ((container->header & JB_FARRAY) == 0)
elog(ERROR, "not a jsonb array"); elog(ERROR, "not a jsonb array");
nelements = container->header & JB_CMASK; nelements = container->header & JB_CMASK;
base_addr = (char *) &container->children[nelements];
if (i >= nelements) if (i >= nelements)
return NULL; return NULL;
e = &container->children[i];
data = (char *) &container->children[nelements];
result = palloc(sizeof(JsonbValue)); result = palloc(sizeof(JsonbValue));
fillJsonbValue(e, data, result); fillJsonbValue(container->children, i, base_addr, result);
return result; return result;
} }
/* /*
* Given the JEntry header, and the base address of the data that the offset * A helper function to fill in a JsonbValue to represent an element of an
* in the JEntry refers to, fill a JsonbValue. * array, or a key or value of an object.
* *
* An array or object will be returned as jbvBinary, ie. it won't be * A nested array or object will be returned as jbvBinary, ie. it won't be
* expanded. * expanded.
*/ */
static void static void
fillJsonbValue(JEntry *entry, char *payload_base, JsonbValue *result) fillJsonbValue(JEntry *children, int index, char *base_addr, JsonbValue *result)
{ {
if (JBE_ISNULL(*entry)) JEntry entry = children[index];
if (JBE_ISNULL(entry))
{ {
result->type = jbvNull; result->type = jbvNull;
} }
else if (JBE_ISSTRING(*entry)) else if (JBE_ISSTRING(entry))
{ {
result->type = jbvString; result->type = jbvString;
result->val.string.val = payload_base + JBE_OFF(*entry); result->val.string.val = base_addr + JBE_OFF(children, index);
result->val.string.len = JBE_LEN(*entry); result->val.string.len = JBE_LEN(children, index);
Assert(result->val.string.len >= 0); Assert(result->val.string.len >= 0);
} }
else if (JBE_ISNUMERIC(*entry)) else if (JBE_ISNUMERIC(entry))
{ {
result->type = jbvNumeric; result->type = jbvNumeric;
result->val.numeric = (Numeric) (payload_base + INTALIGN(JBE_OFF(*entry))); result->val.numeric = (Numeric) (base_addr + INTALIGN(JBE_OFF(children, index)));
} }
else if (JBE_ISBOOL_TRUE(*entry)) else if (JBE_ISBOOL_TRUE(entry))
{ {
result->type = jbvBool; result->type = jbvBool;
result->val.boolean = true; result->val.boolean = true;
} }
else if (JBE_ISBOOL_FALSE(*entry)) else if (JBE_ISBOOL_FALSE(entry))
{ {
result->type = jbvBool; result->type = jbvBool;
result->val.boolean = false; result->val.boolean = false;
} }
else else
{ {
Assert(JBE_ISCONTAINER(*entry)); Assert(JBE_ISCONTAINER(entry));
result->type = jbvBinary; result->type = jbvBinary;
result->val.binary.data = (JsonbContainer *) (payload_base + INTALIGN(JBE_OFF(*entry))); result->val.binary.data = (JsonbContainer *) (base_addr + INTALIGN(JBE_OFF(children, index)));
result->val.binary.len = JBE_LEN(*entry) - (INTALIGN(JBE_OFF(*entry)) - JBE_OFF(*entry)); result->val.binary.len = JBE_LEN(children, index) - (INTALIGN(JBE_OFF(children, index)) - JBE_OFF(children, index));
} }
} }
@ -622,12 +611,7 @@ appendElement(JsonbParseState *pstate, JsonbValue *scalarVal)
JsonbIterator * JsonbIterator *
JsonbIteratorInit(JsonbContainer *container) JsonbIteratorInit(JsonbContainer *container)
{ {
JsonbIterator *it = palloc(sizeof(JsonbIterator)); return iteratorFromContainer(container, NULL);
iteratorFromContainer(it, container);
it->parent = NULL;
return it;
} }
/* /*
@ -661,21 +645,19 @@ JsonbIteratorInit(JsonbContainer *container)
JsonbIteratorToken JsonbIteratorToken
JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested) JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
{ {
JsonbIterState state;
/* Guard against stack overflow due to overly complex Jsonb */
check_stack_depth();
/* Recursive caller may have original caller's iterator */
if (*it == NULL) if (*it == NULL)
return WJB_DONE; return WJB_DONE;
state = (*it)->state; /*
* When stepping into a nested container, we jump back here to start
if ((*it)->containerType == JB_FARRAY) * processing the child. We will not recurse further in one call, because
* processing the child will always begin in JBI_ARRAY_START or
* JBI_OBJECT_START state.
*/
recurse:
switch ((*it)->state)
{ {
if (state == jbi_start) case JBI_ARRAY_START:
{
/* Set v to array on first array call */ /* Set v to array on first array call */
val->type = jbvArray; val->type = jbvArray;
val->val.array.nElems = (*it)->nElems; val->val.array.nElems = (*it)->nElems;
@ -687,11 +669,10 @@ JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
val->val.array.rawScalar = (*it)->isScalar; val->val.array.rawScalar = (*it)->isScalar;
(*it)->i = 0; (*it)->i = 0;
/* Set state for next call */ /* Set state for next call */
(*it)->state = jbi_elem; (*it)->state = JBI_ARRAY_ELEM;
return WJB_BEGIN_ARRAY; return WJB_BEGIN_ARRAY;
}
else if (state == jbi_elem) case JBI_ARRAY_ELEM:
{
if ((*it)->i >= (*it)->nElems) if ((*it)->i >= (*it)->nElems)
{ {
/* /*
@ -703,27 +684,25 @@ JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
*it = freeAndGetParent(*it); *it = freeAndGetParent(*it);
return WJB_END_ARRAY; return WJB_END_ARRAY;
} }
else if (formIterIsContainer(it, val, &(*it)->meta[(*it)->i++],
skipNested)) fillJsonbValue((*it)->children, (*it)->i++, (*it)->dataProper, val);
if (!IsAJsonbScalar(val) && !skipNested)
{ {
/* /* Recurse into container. */
* New child iterator acquired within formIterIsContainer. *it = iteratorFromContainer(val->val.binary.data, *it);
* Recurse into container. Don't directly return jbvBinary goto recurse;
* value to top-level client.
*/
return JsonbIteratorNext(it, val, skipNested);
} }
else else
{ {
/* Scalar item in array */ /*
* Scalar item in array (or a container and caller didn't
* want us to recurse into it.
*/
return WJB_ELEM; return WJB_ELEM;
} }
}
} case JBI_OBJECT_START:
else if ((*it)->containerType == JB_FOBJECT)
{
if (state == jbi_start)
{
/* Set v to object on first object call */ /* Set v to object on first object call */
val->type = jbvObject; val->type = jbvObject;
val->val.object.nPairs = (*it)->nElems; val->val.object.nPairs = (*it)->nElems;
@ -734,11 +713,10 @@ JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
*/ */
(*it)->i = 0; (*it)->i = 0;
/* Set state for next call */ /* Set state for next call */
(*it)->state = jbi_key; (*it)->state = JBI_OBJECT_KEY;
return WJB_BEGIN_OBJECT; return WJB_BEGIN_OBJECT;
}
else if (state == jbi_key) case JBI_OBJECT_KEY:
{
if ((*it)->i >= (*it)->nElems) if ((*it)->i >= (*it)->nElems)
{ {
/* /*
@ -752,37 +730,35 @@ JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
} }
else else
{ {
/* /* Return key of a key/value pair. */
* Return binary item key (ensured by setting skipNested to fillJsonbValue((*it)->children, (*it)->i * 2, (*it)->dataProper, val);
* false directly). No child iterator, no further recursion. if (val->type != jbvString)
* When control reaches here, it's probably from a recursive elog(ERROR, "unexpected jsonb type as object key");
* call.
*/
if (formIterIsContainer(it, val, &(*it)->meta[(*it)->i * 2], false))
elog(ERROR, "unexpected container as object key");
Assert(val->type == jbvString);
/* Set state for next call */ /* Set state for next call */
(*it)->state = jbi_value; (*it)->state = JBI_OBJECT_VALUE;
return WJB_KEY; return WJB_KEY;
} }
}
else if (state == jbi_value) case JBI_OBJECT_VALUE:
{
/* Set state for next call */ /* Set state for next call */
(*it)->state = jbi_key; (*it)->state = JBI_OBJECT_KEY;
fillJsonbValue((*it)->children, ((*it)->i++) * 2 + 1,
(*it)->dataProper, val);
/* /*
* Value may be a container, in which case we recurse with new, * Value may be a container, in which case we recurse with new,
* child iterator. If it is, don't bother !skipNested callers * child iterator (unless the caller asked not to, by passing
* with dealing with the jbvBinary representation. * skipNested).
*/ */
if (formIterIsContainer(it, val, &(*it)->meta[((*it)->i++) * 2 + 1], if (!IsAJsonbScalar(val) && !skipNested)
skipNested)) {
return JsonbIteratorNext(it, val, skipNested); *it = iteratorFromContainer(val->val.binary.data, *it);
goto recurse;
}
else else
return WJB_VALUE; return WJB_VALUE;
}
} }
elog(ERROR, "invalid iterator state"); elog(ERROR, "invalid iterator state");
@ -792,26 +768,31 @@ JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
/* /*
* Initialize an iterator for iterating all elements in a container. * Initialize an iterator for iterating all elements in a container.
*/ */
static void static JsonbIterator *
iteratorFromContainer(JsonbIterator *it, JsonbContainer *container) iteratorFromContainer(JsonbContainer *container, JsonbIterator *parent)
{ {
it->containerType = container->header & (JB_FARRAY | JB_FOBJECT); JsonbIterator *it;
it = palloc(sizeof(JsonbIterator));
it->container = container;
it->parent = parent;
it->nElems = container->header & JB_CMASK; it->nElems = container->header & JB_CMASK;
it->buffer = (char *) container;
/* Array starts just after header */ /* Array starts just after header */
it->meta = container->children; it->children = container->children;
it->state = jbi_start;
switch (it->containerType) switch (container->header & (JB_FARRAY | JB_FOBJECT))
{ {
case JB_FARRAY: case JB_FARRAY:
it->dataProper = it->dataProper =
(char *) it->meta + it->nElems * sizeof(JEntry); (char *) it->children + it->nElems * sizeof(JEntry);
it->isScalar = (container->header & JB_FSCALAR) != 0; it->isScalar = (container->header & JB_FSCALAR) != 0;
/* This is either a "raw scalar", or an array */ /* This is either a "raw scalar", or an array */
Assert(!it->isScalar || it->nElems == 1); Assert(!it->isScalar || it->nElems == 1);
it->state = JBI_ARRAY_START;
break; break;
case JB_FOBJECT: case JB_FOBJECT:
/* /*
@ -819,56 +800,15 @@ iteratorFromContainer(JsonbIterator *it, JsonbContainer *container)
* Each key and each value contain Jentry metadata just the same. * Each key and each value contain Jentry metadata just the same.
*/ */
it->dataProper = it->dataProper =
(char *) it->meta + it->nElems * sizeof(JEntry) * 2; (char *) it->children + it->nElems * sizeof(JEntry) * 2;
it->state = JBI_OBJECT_START;
break; break;
default: default:
elog(ERROR, "unknown type of jsonb container"); elog(ERROR, "unknown type of jsonb container");
} }
}
/* return it;
* JsonbIteratorNext() worker
*
* Returns bool indicating if v was a non-jbvBinary container, and thus if
* further recursion is required by caller (according to its skipNested
* preference). If it is required, we set the caller's iterator for further
* recursion into the nested value. If we're going to skip nested items, just
* set v to a jbvBinary value, but don't set caller's iterator.
*
* Unlike with containers (either in this function or in any
* JsonbIteratorNext() infrastructure), we fully convert from what is
* ultimately a Jsonb on-disk representation, to a JsonbValue in-memory
* representation (for scalar values only). JsonbIteratorNext() initializes
* container Jsonbvalues, but without a sane private buffer. For scalar values
* it has to be done for real (even if we don't actually allocate more memory
* to do this. The point is that our JsonbValues scalars can be passed around
* anywhere).
*/
static bool
formIterIsContainer(JsonbIterator **it, JsonbValue *val, JEntry *ent,
bool skipNested)
{
fillJsonbValue(ent, (*it)->dataProper, val);
if (IsAJsonbScalar(val) || skipNested)
return false;
else
{
/*
* 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));
iteratorFromContainer(child, val->val.binary.data);
child->parent = *it;
*it = child;
return true;
}
} }
/* /*
@ -949,7 +889,7 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
Assert(rcont == WJB_KEY); Assert(rcont == WJB_KEY);
/* First, find value by key... */ /* First, find value by key... */
lhsVal = findJsonbValueFromContainer((JsonbContainer *) (*val)->buffer, lhsVal = findJsonbValueFromContainer((*val)->container,
JB_FOBJECT, JB_FOBJECT,
&vcontained); &vcontained);
@ -1051,7 +991,7 @@ JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained)
if (IsAJsonbScalar(&vcontained)) if (IsAJsonbScalar(&vcontained))
{ {
if (!findJsonbValueFromContainer((JsonbContainer *) (*val)->buffer, if (!findJsonbValueFromContainer((*val)->container,
JB_FARRAY, JB_FARRAY,
&vcontained)) &vcontained))
return false; return false;
@ -1325,7 +1265,7 @@ convertToJsonb(JsonbValue *val)
convertJsonbValue(&buffer, &jentry, val, 0); convertJsonbValue(&buffer, &jentry, val, 0);
/* /*
* Note: the JEntry of the root is not discarded. Therefore the root * Note: the JEntry of the root is discarded. Therefore the root
* JsonbContainer struct must contain enough information to tell what * JsonbContainer struct must contain enough information to tell what
* kind of value it is. * kind of value it is.
*/ */
@ -1341,23 +1281,12 @@ convertToJsonb(JsonbValue *val)
* Subroutine of convertJsonb: serialize a single JsonbValue into buffer. * Subroutine of convertJsonb: serialize a single JsonbValue into buffer.
* *
* The JEntry header for this node is returned in *header. It is filled in * The JEntry header for this node is returned in *header. It is filled in
* with the length of this value, but if * with the length of this value, but if it is stored in an array or an
* it is stored in an array or an object (which is always, except for the root * object (which is always, except for the root node), it is the caller's
* node), it is the caller's responsibility to adjust it with the offset * responsibility to adjust it with the offset within the container.
* within the container.
* *
* If the value is an array or an object, this recurses. 'level' is only used * If the value is an array or an object, this recurses. 'level' is only used
* for debugging purposes. * for debugging purposes.
* 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.
*
* It is the callers responsibility to shift the offset if this is stored
* in an array or object.
*/ */
static void static void
convertJsonbValue(convertState *buffer, JEntry *header, JsonbValue *val, int level) convertJsonbValue(convertState *buffer, JEntry *header, JsonbValue *val, int level)
@ -1424,9 +1353,7 @@ convertJsonbArray(convertState *buffer, JEntry *pheader, JsonbValue *val, int le
errmsg("total size of jsonb array elements exceeds the maximum of %u bytes", errmsg("total size of jsonb array elements exceeds the maximum of %u bytes",
JENTRY_POSMASK))); JENTRY_POSMASK)));
if (i == 0) if (i > 0)
meta |= JENTRY_ISFIRST;
else
meta = (meta & ~JENTRY_POSMASK) | totallen; meta = (meta & ~JENTRY_POSMASK) | totallen;
copyToBuffer(buffer, metaoffset, (char *) &meta, sizeof(JEntry)); copyToBuffer(buffer, metaoffset, (char *) &meta, sizeof(JEntry));
metaoffset += sizeof(JEntry); metaoffset += sizeof(JEntry);
@ -1478,9 +1405,7 @@ convertJsonbObject(convertState *buffer, JEntry *pheader, JsonbValue *val, int l
errmsg("total size of jsonb array elements exceeds the maximum of %u bytes", errmsg("total size of jsonb array elements exceeds the maximum of %u bytes",
JENTRY_POSMASK))); JENTRY_POSMASK)));
if (i == 0) if (i > 0)
meta |= JENTRY_ISFIRST;
else
meta = (meta & ~JENTRY_POSMASK) | totallen; meta = (meta & ~JENTRY_POSMASK) | totallen;
copyToBuffer(buffer, metaoffset, (char *) &meta, sizeof(JEntry)); copyToBuffer(buffer, metaoffset, (char *) &meta, sizeof(JEntry));
metaoffset += sizeof(JEntry); metaoffset += sizeof(JEntry);

View File

@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 201405091 #define CATALOG_VERSION_NO 201405092
#endif #endif

View File

@ -130,17 +130,16 @@ typedef uint32 JEntry;
#define JENTRY_POSMASK 0x0FFFFFFF #define JENTRY_POSMASK 0x0FFFFFFF
#define JENTRY_TYPEMASK 0x70000000 #define JENTRY_TYPEMASK 0x70000000
#define JENTRY_ISFIRST 0x80000000
/* values stored in the type bits */ /* values stored in the type bits */
#define JENTRY_ISSTRING 0x00000000 #define JENTRY_ISSTRING 0x00000000
#define JENTRY_ISNUMERIC 0x10000000 #define JENTRY_ISNUMERIC 0x10000000
#define JENTRY_ISCONTAINER 0x20000000 /* array or object */ #define JENTRY_ISBOOL_FALSE 0x20000000
#define JENTRY_ISBOOL_FALSE 0x30000000 #define JENTRY_ISBOOL_TRUE 0x30000000
#define JENTRY_ISNULL 0x40000000 #define JENTRY_ISNULL 0x40000000
#define JENTRY_ISBOOL_TRUE 0x70000000 #define JENTRY_ISCONTAINER 0x50000000 /* array or object */
/* Note possible multiple evaluations, also access to prior array element */ /* Note possible multiple evaluations */
#define JBE_ISFIRST(je_) (((je_) & JENTRY_ISFIRST) != 0) #define JBE_ISFIRST(je_) (((je_) & JENTRY_ISFIRST) != 0)
#define JBE_ISSTRING(je_) (((je_) & JENTRY_TYPEMASK) == JENTRY_ISSTRING) #define JBE_ISSTRING(je_) (((je_) & JENTRY_TYPEMASK) == JENTRY_ISSTRING)
#define JBE_ISNUMERIC(je_) (((je_) & JENTRY_TYPEMASK) == JENTRY_ISNUMERIC) #define JBE_ISNUMERIC(je_) (((je_) & JENTRY_TYPEMASK) == JENTRY_ISNUMERIC)
@ -150,12 +149,14 @@ typedef uint32 JEntry;
#define JBE_ISBOOL_FALSE(je_) (((je_) & JENTRY_TYPEMASK) == JENTRY_ISBOOL_FALSE) #define JBE_ISBOOL_FALSE(je_) (((je_) & JENTRY_TYPEMASK) == JENTRY_ISBOOL_FALSE)
#define JBE_ISBOOL(je_) (JBE_ISBOOL_TRUE(je_) || JBE_ISBOOL_FALSE(je_)) #define JBE_ISBOOL(je_) (JBE_ISBOOL_TRUE(je_) || JBE_ISBOOL_FALSE(je_))
/* Get offset for Jentry */ /*
* Macros for getting the offset and length of an element. Note multiple
* evaluations and access to prior array element.
*/
#define JBE_ENDPOS(je_) ((je_) & JENTRY_POSMASK) #define JBE_ENDPOS(je_) ((je_) & JENTRY_POSMASK)
#define JBE_OFF(je_) (JBE_ISFIRST(je_) ? 0 : JBE_ENDPOS((&(je_))[-1])) #define JBE_OFF(ja, i) ((i) == 0 ? 0 : JBE_ENDPOS((ja)[i - 1]))
#define JBE_LEN(je_) (JBE_ISFIRST(je_) ? \ #define JBE_LEN(ja, i) ((i) == 0 ? JBE_ENDPOS((ja)[i]) \
JBE_ENDPOS(je_) \ : JBE_ENDPOS((ja)[i]) - JBE_ENDPOS((ja)[i - 1]))
: JBE_ENDPOS(je_) - JBE_ENDPOS((&(je_))[-1]))
/* /*
* A jsonb array or object node, within a Jsonb Datum. * A jsonb array or object node, within a Jsonb Datum.
@ -241,7 +242,7 @@ struct JsonbValue
{ {
int len; int len;
JsonbContainer *data; JsonbContainer *data;
} binary; } binary; /* Array or object, in on-disk format */
} val; } val;
}; };
@ -277,32 +278,29 @@ typedef struct JsonbParseState
*/ */
typedef enum typedef enum
{ {
jbi_start = 0x0, JBI_ARRAY_START,
jbi_key, JBI_ARRAY_ELEM,
jbi_value, JBI_OBJECT_START,
jbi_elem JBI_OBJECT_KEY,
JBI_OBJECT_VALUE
} JsonbIterState; } JsonbIterState;
typedef struct JsonbIterator typedef struct JsonbIterator
{ {
/* Jsonb varlena buffer (may or may not be root) */ /* Container being iterated */
char *buffer; JsonbContainer *container;
uint32 nElems; /* Number of elements in children array (will be
/* Current value */
uint32 containerType; /* Never of value JB_FSCALAR, since scalars
* will appear in pseudo-arrays */
uint32 nElems; /* Number of elements in metaArray (will be
* nPairs for objects) */ * nPairs for objects) */
bool isScalar; /* Pseudo-array scalar value? */ bool isScalar; /* Pseudo-array scalar value? */
JEntry *meta; JEntry *children;
/* Current item in buffer (up to nElems, but must * 2 for objects) */ /* Current item in buffer (up to nElems, but must * 2 for objects) */
int i; int i;
/* /*
* Data proper. Note that this points just past end of "meta" array. We * Data proper. This points just past end of children array.
* use its metadata (Jentrys) with JBE_OFF() macro to find appropriate * We use the JBE_OFF() macro on the Jentrys to find offsets of each
* offsets into this array. * child in this area.
*/ */
char *dataProper; char *dataProper;