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.
This commit is contained in:
Heikki Linnakangas 2014-05-07 23:16:19 +03:00
parent 4d155d8b08
commit 364ddc3e5c
6 changed files with 588 additions and 808 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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);
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(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)
{

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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.
*/
/*
* 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.
*
* 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
{
int32 vl_len_; /* varlena header (do not touch directly!) */
uint32 superheader;
/* (array of JEntry follows, size determined using uint32 superheader) */
JsonbContainer root;
} Jsonb;
/*
* JEntry: there is one of these for each key _and_ value for objects. Arrays
* have one per element.
*
* 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.
*/
typedef struct
{
uint32 header; /* Shares some flags with superheader */
} JEntry;
/* 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__ */