int count = HS_COUNT(in);
char *base = STRPTR(in);
HEntry *entries = ARRPTR(in);
- JsonbParseState *state = NULL;
- JsonbValue *res;
+ JsonbInState state = {0};
- (void) pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
for (i = 0; i < count; i++)
{
key.val.string.len = HSTORE_KEYLEN(entries, i);
key.val.string.val = HSTORE_KEY(entries, base, i);
- (void) pushJsonbValue(&state, WJB_KEY, &key);
+ pushJsonbValue(&state, WJB_KEY, &key);
if (HSTORE_VALISNULL(entries, i))
{
val.val.string.len = HSTORE_VALLEN(entries, i);
val.val.string.val = HSTORE_VAL(entries, base, i);
}
- (void) pushJsonbValue(&state, WJB_VALUE, &val);
+ pushJsonbValue(&state, WJB_VALUE, &val);
}
- res = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
- PG_RETURN_POINTER(JsonbValueToJsonb(res));
+ PG_RETURN_POINTER(JsonbValueToJsonb(state.result));
}
PG_FUNCTION_INFO_V1(hstore_to_jsonb_loose);
int count = HS_COUNT(in);
char *base = STRPTR(in);
HEntry *entries = ARRPTR(in);
- JsonbParseState *state = NULL;
- JsonbValue *res;
+ JsonbInState state = {0};
StringInfoData tmp;
initStringInfo(&tmp);
- (void) pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
+ pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
for (i = 0; i < count; i++)
{
key.val.string.len = HSTORE_KEYLEN(entries, i);
key.val.string.val = HSTORE_KEY(entries, base, i);
- (void) pushJsonbValue(&state, WJB_KEY, &key);
+ pushJsonbValue(&state, WJB_KEY, &key);
if (HSTORE_VALISNULL(entries, i))
{
val.val.string.val = HSTORE_VAL(entries, base, i);
}
}
- (void) pushJsonbValue(&state, WJB_VALUE, &val);
+ pushJsonbValue(&state, WJB_VALUE, &val);
}
- res = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
+ pushJsonbValue(&state, WJB_END_OBJECT, NULL);
- PG_RETURN_POINTER(JsonbValueToJsonb(res));
+ PG_RETURN_POINTER(JsonbValueToJsonb(state.result));
}
);
static SV *Jsonb_to_SV(JsonbContainer *jsonb);
-static JsonbValue *SV_to_JsonbValue(SV *obj, JsonbParseState **ps, bool is_elem);
+static void SV_to_JsonbValue(SV *obj, JsonbInState *ps, bool is_elem);
static SV *
}
}
-static JsonbValue *
-AV_to_JsonbValue(AV *in, JsonbParseState **jsonb_state)
+static void
+AV_to_JsonbValue(AV *in, JsonbInState *jsonb_state)
{
dTHX;
SSize_t pcount = av_len(in) + 1;
SV **value = av_fetch(in, i, FALSE);
if (value)
- (void) SV_to_JsonbValue(*value, jsonb_state, true);
+ SV_to_JsonbValue(*value, jsonb_state, true);
}
- return pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
+ pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
}
-static JsonbValue *
-HV_to_JsonbValue(HV *obj, JsonbParseState **jsonb_state)
+static void
+HV_to_JsonbValue(HV *obj, JsonbInState *jsonb_state)
{
dTHX;
JsonbValue key;
key.val.string.val = pnstrdup(kstr, klen);
key.val.string.len = klen;
pushJsonbValue(jsonb_state, WJB_KEY, &key);
- (void) SV_to_JsonbValue(val, jsonb_state, false);
+ SV_to_JsonbValue(val, jsonb_state, false);
}
- return pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
+ pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
}
-static JsonbValue *
-SV_to_JsonbValue(SV *in, JsonbParseState **jsonb_state, bool is_elem)
+static void
+SV_to_JsonbValue(SV *in, JsonbInState *jsonb_state, bool is_elem)
{
dTHX;
JsonbValue out; /* result */
switch (SvTYPE(in))
{
case SVt_PVAV:
- return AV_to_JsonbValue((AV *) in, jsonb_state);
+ AV_to_JsonbValue((AV *) in, jsonb_state);
+ return;
case SVt_PVHV:
- return HV_to_JsonbValue((HV *) in, jsonb_state);
+ HV_to_JsonbValue((HV *) in, jsonb_state);
+ return;
default:
if (!SvOK(in))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot transform this Perl type to jsonb")));
- return NULL;
}
}
- /* Push result into 'jsonb_state' unless it is a raw scalar. */
- return *jsonb_state
- ? pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, &out)
- : memcpy(palloc_object(JsonbValue), &out, sizeof(JsonbValue));
+ if (jsonb_state->parseState)
+ {
+ /* We're in an array or object, so push value as element or field. */
+ pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, &out);
+ }
+ else
+ {
+ /*
+ * We are at top level, so it's a raw scalar. If we just shove the
+ * scalar value into jsonb_state->result, JsonbValueToJsonb will take
+ * care of wrapping it into a dummy array.
+ */
+ jsonb_state->result = palloc_object(JsonbValue);
+ memcpy(jsonb_state->result, &out, sizeof(JsonbValue));
+ }
}
plperl_to_jsonb(PG_FUNCTION_ARGS)
{
dTHX;
- JsonbParseState *jsonb_state = NULL;
SV *in = (SV *) PG_GETARG_POINTER(0);
- JsonbValue *out = SV_to_JsonbValue(in, &jsonb_state, true);
- Jsonb *result = JsonbValueToJsonb(out);
+ JsonbInState jsonb_state = {0};
- PG_RETURN_JSONB_P(result);
+ SV_to_JsonbValue(in, &jsonb_state, true);
+ PG_RETURN_JSONB_P(JsonbValueToJsonb(jsonb_state.result));
}
static PyObject *decimal_constructor;
static PyObject *PLyObject_FromJsonbContainer(JsonbContainer *jsonb);
-static JsonbValue *PLyObject_ToJsonbValue(PyObject *obj,
- JsonbParseState **jsonb_state, bool is_elem);
+static void PLyObject_ToJsonbValue(PyObject *obj,
+ JsonbInState *jsonb_state, bool is_elem);
typedef PyObject *(*PLyUnicode_FromStringAndSize_t)
(const char *s, Py_ssize_t size);
*
* Transform Python dict to JsonbValue.
*/
-static JsonbValue *
-PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
+static void
+PLyMapping_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state)
{
Py_ssize_t pcount;
PyObject *volatile items;
- JsonbValue *volatile out;
pcount = PyMapping_Size(obj);
items = PyMapping_Items(obj);
PLyUnicode_ToJsonbValue(key, &jbvKey);
}
- (void) pushJsonbValue(jsonb_state, WJB_KEY, &jbvKey);
- (void) PLyObject_ToJsonbValue(value, jsonb_state, false);
+ pushJsonbValue(jsonb_state, WJB_KEY, &jbvKey);
+ PLyObject_ToJsonbValue(value, jsonb_state, false);
}
- out = pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
+ pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
}
PG_FINALLY();
{
Py_DECREF(items);
}
PG_END_TRY();
-
- return out;
}
/*
* Transform python list to JsonbValue. Expects transformed PyObject and
* a state required for jsonb construction.
*/
-static JsonbValue *
-PLySequence_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
+static void
+PLySequence_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state)
{
Py_ssize_t i;
Py_ssize_t pcount;
value = PySequence_GetItem(obj, i);
Assert(value);
- (void) PLyObject_ToJsonbValue(value, jsonb_state, true);
+ PLyObject_ToJsonbValue(value, jsonb_state, true);
Py_XDECREF(value);
value = NULL;
}
}
PG_END_TRY();
- return pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
+ pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
}
/*
*
* Transform python object to JsonbValue.
*/
-static JsonbValue *
-PLyObject_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state, bool is_elem)
+static void
+PLyObject_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state, bool is_elem)
{
JsonbValue *out;
if (!PyUnicode_Check(obj))
{
if (PySequence_Check(obj))
- return PLySequence_ToJsonbValue(obj, jsonb_state);
+ {
+ PLySequence_ToJsonbValue(obj, jsonb_state);
+ return;
+ }
else if (PyMapping_Check(obj))
- return PLyMapping_ToJsonbValue(obj, jsonb_state);
+ {
+ PLyMapping_ToJsonbValue(obj, jsonb_state);
+ return;
+ }
}
out = palloc_object(JsonbValue);
errmsg("Python type \"%s\" cannot be transformed to jsonb",
PLyObject_AsString((PyObject *) obj->ob_type))));
- /* Push result into 'jsonb_state' unless it is raw scalar value. */
- return (*jsonb_state ?
- pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, out) :
- out);
+ if (jsonb_state->parseState)
+ {
+ /* We're in an array or object, so push value as element or field. */
+ pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, out);
+ }
+ else
+ {
+ /*
+ * We are at top level, so it's a raw scalar. If we just shove the
+ * scalar value into jsonb_state->result, JsonbValueToJsonb will take
+ * care of wrapping it into a dummy array.
+ */
+ jsonb_state->result = out;
+ }
}
/*
Datum
plpython_to_jsonb(PG_FUNCTION_ARGS)
{
- PyObject *obj;
- JsonbValue *out;
- JsonbParseState *jsonb_state = NULL;
+ PyObject *obj = (PyObject *) PG_GETARG_POINTER(0);
+ JsonbInState jsonb_state = {0};
- obj = (PyObject *) PG_GETARG_POINTER(0);
- out = PLyObject_ToJsonbValue(obj, &jsonb_state, true);
- PG_RETURN_POINTER(JsonbValueToJsonb(out));
+ PLyObject_ToJsonbValue(obj, &jsonb_state, true);
+ PG_RETURN_POINTER(JsonbValueToJsonb(jsonb_state.result));
}
/*
#include "utils/lsyscache.h"
#include "utils/typcache.h"
-typedef struct JsonbInState
-{
- JsonbParseState *parseState;
- JsonbValue *res;
- bool unique_keys;
- Node *escontext;
-} JsonbInState;
-
typedef struct JsonbAggState
{
JsonbInState *res;
if (!pg_parse_json_or_errsave(&lex, &sem, escontext))
return (Datum) 0;
- /* after parsing, the item member has the composed jsonb structure */
- PG_RETURN_POINTER(JsonbValueToJsonb(state.res));
+ /* after parsing, the result field has the composed jsonb structure */
+ PG_RETURN_POINTER(JsonbValueToJsonb(state.result));
}
static bool
{
JsonbInState *_state = (JsonbInState *) pstate;
- _state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_OBJECT, NULL);
+ pushJsonbValue(_state, WJB_BEGIN_OBJECT, NULL);
_state->parseState->unique_keys = _state->unique_keys;
return JSON_SUCCESS;
{
JsonbInState *_state = (JsonbInState *) pstate;
- _state->res = pushJsonbValue(&_state->parseState, WJB_END_OBJECT, NULL);
+ pushJsonbValue(_state, WJB_END_OBJECT, NULL);
return JSON_SUCCESS;
}
{
JsonbInState *_state = (JsonbInState *) pstate;
- _state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_ARRAY, NULL);
+ pushJsonbValue(_state, WJB_BEGIN_ARRAY, NULL);
return JSON_SUCCESS;
}
{
JsonbInState *_state = (JsonbInState *) pstate;
- _state->res = pushJsonbValue(&_state->parseState, WJB_END_ARRAY, NULL);
+ pushJsonbValue(_state, WJB_END_ARRAY, NULL);
return JSON_SUCCESS;
}
return JSON_SEM_ACTION_FAILED;
v.val.string.val = fname;
- _state->res = pushJsonbValue(&_state->parseState, WJB_KEY, &v);
+ pushJsonbValue(_state, WJB_KEY, &v);
return JSON_SUCCESS;
}
va.val.array.rawScalar = true;
va.val.array.nElems = 1;
- _state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_ARRAY, &va);
- _state->res = pushJsonbValue(&_state->parseState, WJB_ELEM, &v);
- _state->res = pushJsonbValue(&_state->parseState, WJB_END_ARRAY, NULL);
+ pushJsonbValue(_state, WJB_BEGIN_ARRAY, &va);
+ pushJsonbValue(_state, WJB_ELEM, &v);
+ pushJsonbValue(_state, WJB_END_ARRAY, NULL);
}
else
{
switch (o->type)
{
case jbvArray:
- _state->res = pushJsonbValue(&_state->parseState, WJB_ELEM, &v);
+ pushJsonbValue(_state, WJB_ELEM, &v);
break;
case jbvObject:
- _state->res = pushJsonbValue(&_state->parseState, WJB_VALUE, &v);
+ pushJsonbValue(_state, WJB_VALUE, &v);
break;
default:
elog(ERROR, "unexpected parent of nested structure");
{
if (type == WJB_END_ARRAY || type == WJB_END_OBJECT ||
type == WJB_BEGIN_ARRAY || type == WJB_BEGIN_OBJECT)
- result->res = pushJsonbValue(&result->parseState,
- type, NULL);
+ pushJsonbValue(result, type, NULL);
else
- result->res = pushJsonbValue(&result->parseState,
- type, &jb);
+ pushJsonbValue(result, type, &jb);
}
}
}
va.val.array.rawScalar = true;
va.val.array.nElems = 1;
- result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, &va);
- result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb);
- result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL);
+ pushJsonbValue(result, WJB_BEGIN_ARRAY, &va);
+ pushJsonbValue(result, WJB_ELEM, &jb);
+ pushJsonbValue(result, WJB_END_ARRAY, NULL);
}
else
{
switch (o->type)
{
case jbvArray:
- result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb);
+ pushJsonbValue(result, WJB_ELEM, &jb);
break;
case jbvObject:
- result->res = pushJsonbValue(&result->parseState,
- key_scalar ? WJB_KEY : WJB_VALUE,
- &jb);
+ pushJsonbValue(result,
+ key_scalar ? WJB_KEY : WJB_VALUE,
+ &jb);
break;
default:
elog(ERROR, "unexpected parent of nested structure");
Assert(dim < ndims);
- result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, NULL);
+ pushJsonbValue(result, WJB_BEGIN_ARRAY, NULL);
for (i = 1; i <= dims[dim]; i++)
{
}
}
- result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL);
+ pushJsonbValue(result, WJB_END_ARRAY, NULL);
}
/*
if (nitems <= 0)
{
- result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, NULL);
- result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL);
+ pushJsonbValue(result, WJB_BEGIN_ARRAY, NULL);
+ pushJsonbValue(result, WJB_END_ARRAY, NULL);
return;
}
tmptup.t_data = td;
tuple = &tmptup;
- result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_OBJECT, NULL);
+ pushJsonbValue(result, WJB_BEGIN_OBJECT, NULL);
for (i = 0; i < tupdesc->natts; i++)
{
v.val.string.len = strlen(attname);
v.val.string.val = attname;
- result->res = pushJsonbValue(&result->parseState, WJB_KEY, &v);
+ pushJsonbValue(result, WJB_KEY, &v);
val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
false);
}
- result->res = pushJsonbValue(&result->parseState, WJB_END_OBJECT, NULL);
+ pushJsonbValue(result, WJB_END_OBJECT, NULL);
ReleaseTupleDesc(tupdesc);
}
datum_to_jsonb_internal(val, false, &result, tcategory, outfuncoid,
false);
- return JsonbPGetDatum(JsonbValueToJsonb(result.res));
+ return JsonbPGetDatum(JsonbValueToJsonb(result.result));
}
Datum
memset(&result, 0, sizeof(JsonbInState));
- result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
+ pushJsonbValue(&result, WJB_BEGIN_OBJECT, NULL);
result.parseState->unique_keys = unique_keys;
result.parseState->skip_nulls = absent_on_null;
add_jsonb(args[i + 1], nulls[i + 1], &result, types[i + 1], false);
}
- result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
+ pushJsonbValue(&result, WJB_END_OBJECT, NULL);
- return JsonbPGetDatum(JsonbValueToJsonb(result.res));
+ return JsonbPGetDatum(JsonbValueToJsonb(result.result));
}
/*
memset(&result, 0, sizeof(JsonbInState));
- (void) pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
- result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
+ pushJsonbValue(&result, WJB_BEGIN_OBJECT, NULL);
+ pushJsonbValue(&result, WJB_END_OBJECT, NULL);
- PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ PG_RETURN_POINTER(JsonbValueToJsonb(result.result));
}
Datum
memset(&result, 0, sizeof(JsonbInState));
- result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
+ pushJsonbValue(&result, WJB_BEGIN_ARRAY, NULL);
for (i = 0; i < nargs; i++)
{
add_jsonb(args[i], nulls[i], &result, types[i], false);
}
- result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
+ pushJsonbValue(&result, WJB_END_ARRAY, NULL);
- return JsonbPGetDatum(JsonbValueToJsonb(result.res));
+ return JsonbPGetDatum(JsonbValueToJsonb(result.result));
}
/*
memset(&result, 0, sizeof(JsonbInState));
- (void) pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
- result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
+ pushJsonbValue(&result, WJB_BEGIN_ARRAY, NULL);
+ pushJsonbValue(&result, WJB_END_ARRAY, NULL);
- PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ PG_RETURN_POINTER(JsonbValueToJsonb(result.result));
}
memset(&result, 0, sizeof(JsonbInState));
- (void) pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
+ pushJsonbValue(&result, WJB_BEGIN_OBJECT, NULL);
switch (ndims)
{
v.val.string.len = len;
v.val.string.val = str;
- (void) pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ pushJsonbValue(&result, WJB_KEY, &v);
if (in_nulls[i * 2 + 1])
{
v.val.string.val = str;
}
- (void) pushJsonbValue(&result.parseState, WJB_VALUE, &v);
+ pushJsonbValue(&result, WJB_VALUE, &v);
}
pfree(in_datums);
pfree(in_nulls);
close_object:
- result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
+ pushJsonbValue(&result, WJB_END_OBJECT, NULL);
- PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ PG_RETURN_POINTER(JsonbValueToJsonb(result.result));
}
/*
memset(&result, 0, sizeof(JsonbInState));
- (void) pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
+ pushJsonbValue(&result, WJB_BEGIN_OBJECT, NULL);
if (nkdims > 1 || nkdims != nvdims)
ereport(ERROR,
v.val.string.len = len;
v.val.string.val = str;
- (void) pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ pushJsonbValue(&result, WJB_KEY, &v);
if (val_nulls[i])
{
v.val.string.val = str;
}
- (void) pushJsonbValue(&result.parseState, WJB_VALUE, &v);
+ pushJsonbValue(&result, WJB_VALUE, &v);
}
pfree(key_datums);
pfree(val_nulls);
close_object:
- result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
+ pushJsonbValue(&result, WJB_END_OBJECT, NULL);
- PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ PG_RETURN_POINTER(JsonbValueToJsonb(result.result));
}
state = palloc(sizeof(JsonbAggState));
result = palloc0(sizeof(JsonbInState));
state->res = result;
- result->res = pushJsonbValue(&result->parseState,
- WJB_BEGIN_ARRAY, NULL);
+ pushJsonbValue(result, WJB_BEGIN_ARRAY, NULL);
MemoryContextSwitchTo(oldcontext);
json_categorize_type(arg_type, true, &state->val_category,
datum_to_jsonb_internal(val, PG_ARGISNULL(1), &elem, state->val_category,
state->val_output_func, false);
- jbelem = JsonbValueToJsonb(elem.res);
+ jbelem = JsonbValueToJsonb(elem.result);
/* switch to the aggregate context for accumulation operations */
if (v.val.array.rawScalar)
single_scalar = true;
else
- result->res = pushJsonbValue(&result->parseState,
- type, NULL);
+ pushJsonbValue(result, type, NULL);
break;
case WJB_END_ARRAY:
if (!single_scalar)
- result->res = pushJsonbValue(&result->parseState,
- type, NULL);
+ pushJsonbValue(result, type, NULL);
break;
case WJB_BEGIN_OBJECT:
case WJB_END_OBJECT:
- result->res = pushJsonbValue(&result->parseState,
- type, NULL);
+ pushJsonbValue(result, type, NULL);
break;
case WJB_ELEM:
case WJB_KEY:
DatumGetNumeric(DirectFunctionCall1(numeric_uplus,
NumericGetDatum(v.val.numeric)));
}
- result->res = pushJsonbValue(&result->parseState,
- type, &v);
+ pushJsonbValue(result, type, &v);
break;
default:
elog(ERROR, "unknown jsonb iterator token type");
result.parseState = clone_parse_state(arg->res->parseState);
- result.res = pushJsonbValue(&result.parseState,
- WJB_END_ARRAY, NULL);
+ pushJsonbValue(&result, WJB_END_ARRAY, NULL);
- out = JsonbValueToJsonb(result.res);
+ out = JsonbValueToJsonb(result.result);
PG_RETURN_POINTER(out);
}
state = palloc(sizeof(JsonbAggState));
result = palloc0(sizeof(JsonbInState));
state->res = result;
- result->res = pushJsonbValue(&result->parseState,
- WJB_BEGIN_OBJECT, NULL);
+ pushJsonbValue(result, WJB_BEGIN_OBJECT, NULL);
result->parseState->unique_keys = unique_keys;
result->parseState->skip_nulls = absent_on_null;
datum_to_jsonb_internal(val, false, &elem, state->key_category,
state->key_output_func, true);
- jbkey = JsonbValueToJsonb(elem.res);
+ jbkey = JsonbValueToJsonb(elem.result);
val = PG_ARGISNULL(2) ? (Datum) 0 : PG_GETARG_DATUM(2);
datum_to_jsonb_internal(val, PG_ARGISNULL(2), &elem, state->val_category,
state->val_output_func, false);
- jbval = JsonbValueToJsonb(elem.res);
+ jbval = JsonbValueToJsonb(elem.result);
it = JsonbIteratorInit(&jbkey->root);
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("object keys must be strings")));
}
- result->res = pushJsonbValue(&result->parseState,
- WJB_KEY, &v);
+ pushJsonbValue(result, WJB_KEY, &v);
if (skip)
{
v.type = jbvNull;
- result->res = pushJsonbValue(&result->parseState,
- WJB_VALUE, &v);
+ pushJsonbValue(result, WJB_VALUE, &v);
MemoryContextSwitchTo(oldcontext);
PG_RETURN_POINTER(state);
}
if (v.val.array.rawScalar)
single_scalar = true;
else
- result->res = pushJsonbValue(&result->parseState,
- type, NULL);
+ pushJsonbValue(result, type, NULL);
break;
case WJB_END_ARRAY:
if (!single_scalar)
- result->res = pushJsonbValue(&result->parseState,
- type, NULL);
+ pushJsonbValue(result, type, NULL);
break;
case WJB_BEGIN_OBJECT:
case WJB_END_OBJECT:
- result->res = pushJsonbValue(&result->parseState,
- type, NULL);
+ pushJsonbValue(result, type, NULL);
break;
case WJB_ELEM:
case WJB_KEY:
DatumGetNumeric(DirectFunctionCall1(numeric_uplus,
NumericGetDatum(v.val.numeric)));
}
- result->res = pushJsonbValue(&result->parseState,
- single_scalar ? WJB_VALUE : type,
- &v);
+ pushJsonbValue(result, single_scalar ? WJB_VALUE : type, &v);
break;
default:
elog(ERROR, "unknown jsonb iterator token type");
result.parseState = clone_parse_state(arg->res->parseState);
- result.res = pushJsonbValue(&result.parseState,
- WJB_END_OBJECT, NULL);
+ pushJsonbValue(&result, WJB_END_OBJECT, NULL);
- out = JsonbValueToJsonb(result.res);
+ out = JsonbValueToJsonb(result.result);
PG_RETURN_POINTER(out);
}
#include "postgres.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_type.h"
#include "common/hashfn.h"
#include "miscadmin.h"
#include "port/pg_bitutils.h"
+#include "utils/date.h"
#include "utils/datetime.h"
+#include "utils/datum.h"
#include "utils/fmgrprotos.h"
#include "utils/json.h"
#include "utils/jsonb.h"
static JsonbIterator *iteratorFromContainer(JsonbContainer *container, JsonbIterator *parent);
static JsonbIterator *freeAndGetParent(JsonbIterator *it);
-static JsonbParseState *pushState(JsonbParseState **pstate);
-static void appendKey(JsonbParseState *pstate, JsonbValue *string);
-static void appendValue(JsonbParseState *pstate, JsonbValue *scalarVal);
-static void appendElement(JsonbParseState *pstate, JsonbValue *scalarVal);
+static JsonbParseState *pushState(JsonbInState *pstate);
+static void appendKey(JsonbInState *pstate, JsonbValue *string, bool needCopy);
+static void appendValue(JsonbInState *pstate, JsonbValue *scalarVal, bool needCopy);
+static void appendElement(JsonbInState *pstate, JsonbValue *scalarVal, bool needCopy);
+static void copyScalarSubstructure(JsonbValue *v, MemoryContext outcontext);
static int lengthCompareJsonbStringValue(const void *a, const void *b);
static int lengthCompareJsonbString(const char *val1, int len1,
const char *val2, int len2);
static int lengthCompareJsonbPair(const void *a, const void *b, void *binequal);
static void uniqueifyJsonbObject(JsonbValue *object, bool unique_keys,
bool skip_nulls);
-static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
- JsonbIteratorToken seq,
- JsonbValue *scalarVal);
+static void pushJsonbValueScalar(JsonbInState *pstate,
+ JsonbIteratorToken seq,
+ JsonbValue *scalarVal);
void
JsonbToJsonbValue(Jsonb *jsonb, JsonbValue *val)
if (IsAJsonbScalar(val))
{
- /* Scalar value */
- JsonbParseState *pstate = NULL;
- JsonbValue *res;
+ /* Scalar value, so wrap it in an array */
+ JsonbInState pstate = {0};
JsonbValue scalarArray;
scalarArray.type = jbvArray;
pushJsonbValue(&pstate, WJB_BEGIN_ARRAY, &scalarArray);
pushJsonbValue(&pstate, WJB_ELEM, val);
- res = pushJsonbValue(&pstate, WJB_END_ARRAY, NULL);
+ pushJsonbValue(&pstate, WJB_END_ARRAY, NULL);
- out = convertToJsonb(res);
+ out = convertToJsonb(pstate.result);
}
else if (val->type == jbvObject || val->type == jbvArray)
{
}
/*
- * Push JsonbValue into JsonbParseState.
+ * Push JsonbValue into JsonbInState.
*
- * Used when parsing JSON tokens to form Jsonb, or when converting an in-memory
- * JsonbValue to a Jsonb.
+ * Used, for example, when parsing JSON input.
*
- * Initial state of *JsonbParseState is NULL, since it'll be allocated here
- * originally (caller will get JsonbParseState back by reference).
+ * *pstate is typically initialized to all-zeroes, except that the caller
+ * may provide outcontext and/or escontext. (escontext is ignored by this
+ * function and its subroutines, however.)
+ *
+ * "seq" tells what is being pushed (start/end of array or object, key,
+ * value, etc). WJB_DONE is not used here, but the other values of
+ * JsonbIteratorToken are. We assume the caller passes a valid sequence
+ * of values.
+ *
+ * The passed "jbval" is typically transient storage, such as a local variable.
+ * We will copy it into the outcontext (CurrentMemoryContext by default).
+ * If outcontext isn't NULL, we will also make copies of any pass-by-reference
+ * scalar values.
*
* Only sequential tokens pertaining to non-container types should pass a
* JsonbValue. There is one exception -- WJB_BEGIN_ARRAY callers may pass a
*
* Values of type jbvBinary, which are rolled up arrays and objects,
* are unpacked before being added to the result.
+ *
+ * At the end of construction of a JsonbValue, pstate->result will reference
+ * the top-level JsonbValue object.
*/
-JsonbValue *
-pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
+void
+pushJsonbValue(JsonbInState *pstate, JsonbIteratorToken seq,
JsonbValue *jbval)
{
JsonbIterator *it;
- JsonbValue *res = NULL;
JsonbValue v;
JsonbIteratorToken tok;
int i;
- if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvObject)
+ /*
+ * pushJsonbValueScalar handles all cases not involving pushing a
+ * container object as an ELEM or VALUE.
+ */
+ if (!jbval || IsAJsonbScalar(jbval) ||
+ (seq != WJB_ELEM && seq != WJB_VALUE))
+ {
+ pushJsonbValueScalar(pstate, seq, jbval);
+ return;
+ }
+
+ /* If an object or array is pushed, recursively push its contents */
+ if (jbval->type == jbvObject)
{
pushJsonbValue(pstate, WJB_BEGIN_OBJECT, NULL);
for (i = 0; i < jbval->val.object.nPairs; i++)
pushJsonbValue(pstate, WJB_KEY, &jbval->val.object.pairs[i].key);
pushJsonbValue(pstate, WJB_VALUE, &jbval->val.object.pairs[i].value);
}
-
- return pushJsonbValue(pstate, WJB_END_OBJECT, NULL);
+ pushJsonbValue(pstate, WJB_END_OBJECT, NULL);
+ return;
}
- if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvArray)
+ if (jbval->type == jbvArray)
{
pushJsonbValue(pstate, WJB_BEGIN_ARRAY, NULL);
for (i = 0; i < jbval->val.array.nElems; i++)
{
pushJsonbValue(pstate, WJB_ELEM, &jbval->val.array.elems[i]);
}
-
- return pushJsonbValue(pstate, WJB_END_ARRAY, NULL);
+ pushJsonbValue(pstate, WJB_END_ARRAY, NULL);
+ return;
}
- if (!jbval || (seq != WJB_ELEM && seq != WJB_VALUE) ||
- jbval->type != jbvBinary)
- {
- /* drop through */
- return pushJsonbValueScalar(pstate, seq, jbval);
- }
+ /* Else it must be a jbvBinary value; push its contents */
+ Assert(jbval->type == jbvBinary);
- /* unpack the binary and add each piece to the pstate */
it = JsonbIteratorInit(jbval->val.binary.data);
- if ((jbval->val.binary.data->header & JB_FSCALAR) && *pstate)
+ /* ... with a special case for pushing a raw scalar */
+ if ((jbval->val.binary.data->header & JB_FSCALAR) &&
+ pstate->parseState != NULL)
{
tok = JsonbIteratorNext(&it, &v, true);
Assert(tok == WJB_BEGIN_ARRAY);
tok = JsonbIteratorNext(&it, &v, true);
Assert(tok == WJB_ELEM);
- res = pushJsonbValueScalar(pstate, seq, &v);
+ pushJsonbValueScalar(pstate, seq, &v);
tok = JsonbIteratorNext(&it, &v, true);
Assert(tok == WJB_END_ARRAY);
Assert(it == NULL);
- return res;
+ return;
}
while ((tok = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
- res = pushJsonbValueScalar(pstate, tok,
- tok < WJB_BEGIN_ARRAY ||
- (tok == WJB_BEGIN_ARRAY &&
- v.val.array.rawScalar) ? &v : NULL);
-
- return res;
+ pushJsonbValueScalar(pstate, tok,
+ tok < WJB_BEGIN_ARRAY ||
+ (tok == WJB_BEGIN_ARRAY &&
+ v.val.array.rawScalar) ? &v : NULL);
}
/*
* Do the actual pushing, with only scalar or pseudo-scalar-array values
* accepted.
*/
-static JsonbValue *
-pushJsonbValueScalar(JsonbParseState **pstate, JsonbIteratorToken seq,
+static void
+pushJsonbValueScalar(JsonbInState *pstate, JsonbIteratorToken seq,
JsonbValue *scalarVal)
{
- JsonbValue *result = NULL;
+ JsonbParseState *ppstate;
+ JsonbValue *val;
+ MemoryContext outcontext;
switch (seq)
{
case WJB_BEGIN_ARRAY:
Assert(!scalarVal || scalarVal->val.array.rawScalar);
- *pstate = pushState(pstate);
- result = &(*pstate)->contVal;
- (*pstate)->contVal.type = jbvArray;
- (*pstate)->contVal.val.array.nElems = 0;
- (*pstate)->contVal.val.array.rawScalar = (scalarVal &&
- scalarVal->val.array.rawScalar);
+ ppstate = pushState(pstate);
+ val = &ppstate->contVal;
+ val->type = jbvArray;
+ val->val.array.nElems = 0;
+ val->val.array.rawScalar = (scalarVal &&
+ scalarVal->val.array.rawScalar);
if (scalarVal && scalarVal->val.array.nElems > 0)
{
/* Assume that this array is still really a scalar */
Assert(scalarVal->type == jbvArray);
- (*pstate)->size = scalarVal->val.array.nElems;
+ ppstate->size = scalarVal->val.array.nElems;
}
else
{
- (*pstate)->size = 4;
+ ppstate->size = 4; /* initial guess at array size */
}
- (*pstate)->contVal.val.array.elems = palloc(sizeof(JsonbValue) *
- (*pstate)->size);
+ outcontext = pstate->outcontext ? pstate->outcontext : CurrentMemoryContext;
+ val->val.array.elems = MemoryContextAlloc(outcontext,
+ sizeof(JsonbValue) *
+ ppstate->size);
break;
case WJB_BEGIN_OBJECT:
Assert(!scalarVal);
- *pstate = pushState(pstate);
- result = &(*pstate)->contVal;
- (*pstate)->contVal.type = jbvObject;
- (*pstate)->contVal.val.object.nPairs = 0;
- (*pstate)->size = 4;
- (*pstate)->contVal.val.object.pairs = palloc(sizeof(JsonbPair) *
- (*pstate)->size);
+ ppstate = pushState(pstate);
+ val = &ppstate->contVal;
+ val->type = jbvObject;
+ val->val.object.nPairs = 0;
+ ppstate->size = 4; /* initial guess at object size */
+ outcontext = pstate->outcontext ? pstate->outcontext : CurrentMemoryContext;
+ val->val.object.pairs = MemoryContextAlloc(outcontext,
+ sizeof(JsonbPair) *
+ ppstate->size);
break;
case WJB_KEY:
Assert(scalarVal->type == jbvString);
- appendKey(*pstate, scalarVal);
+ appendKey(pstate, scalarVal, true);
break;
case WJB_VALUE:
Assert(IsAJsonbScalar(scalarVal));
- appendValue(*pstate, scalarVal);
+ appendValue(pstate, scalarVal, true);
break;
case WJB_ELEM:
Assert(IsAJsonbScalar(scalarVal));
- appendElement(*pstate, scalarVal);
+ appendElement(pstate, scalarVal, true);
break;
case WJB_END_OBJECT:
- uniqueifyJsonbObject(&(*pstate)->contVal,
- (*pstate)->unique_keys,
- (*pstate)->skip_nulls);
+ ppstate = pstate->parseState;
+ uniqueifyJsonbObject(&ppstate->contVal,
+ ppstate->unique_keys,
+ ppstate->skip_nulls);
/* fall through! */
case WJB_END_ARRAY:
/* Steps here common to WJB_END_OBJECT case */
Assert(!scalarVal);
- result = &(*pstate)->contVal;
+ ppstate = pstate->parseState;
+ val = &ppstate->contVal;
/*
* Pop stack and push current array/object as value in parent
- * array/object
+ * array/object, or return it as the final result. We don't need
+ * to re-copy any scalars that are in the data structure.
*/
- *pstate = (*pstate)->next;
- if (*pstate)
+ pstate->parseState = ppstate = ppstate->next;
+ if (ppstate)
{
- switch ((*pstate)->contVal.type)
+ switch (ppstate->contVal.type)
{
case jbvArray:
- appendElement(*pstate, result);
+ appendElement(pstate, val, false);
break;
case jbvObject:
- appendValue(*pstate, result);
+ appendValue(pstate, val, false);
break;
default:
elog(ERROR, "invalid jsonb container type");
}
}
+ else
+ pstate->result = val;
break;
default:
elog(ERROR, "unrecognized jsonb sequential processing token");
}
-
- return result;
}
/*
- * pushJsonbValue() worker: Iteration-like forming of Jsonb
+ * Push a new JsonbParseState onto the JsonbInState's stack
+ *
+ * As a notational convenience, the new state's address is returned.
+ * The caller must initialize the new state's contVal and size fields.
*/
static JsonbParseState *
-pushState(JsonbParseState **pstate)
+pushState(JsonbInState *pstate)
{
- JsonbParseState *ns = palloc(sizeof(JsonbParseState));
+ MemoryContext outcontext = pstate->outcontext ? pstate->outcontext : CurrentMemoryContext;
+ JsonbParseState *ns = MemoryContextAlloc(outcontext,
+ sizeof(JsonbParseState));
- ns->next = *pstate;
+ ns->next = pstate->parseState;
+ /* This module never changes these fields, but callers can: */
ns->unique_keys = false;
ns->skip_nulls = false;
+ pstate->parseState = ns;
return ns;
}
/*
- * pushJsonbValue() worker: Append a pair key to state when generating a Jsonb
+ * pushJsonbValue() worker: Append a pair key to pstate
*/
static void
-appendKey(JsonbParseState *pstate, JsonbValue *string)
+appendKey(JsonbInState *pstate, JsonbValue *string, bool needCopy)
{
- JsonbValue *object = &pstate->contVal;
+ JsonbParseState *ppstate = pstate->parseState;
+ JsonbValue *object = &ppstate->contVal;
+ JsonbPair *pair;
Assert(object->type == jbvObject);
Assert(string->type == jbvString);
- if (object->val.object.nPairs >= JSONB_MAX_PAIRS)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of jsonb object pairs exceeds the maximum allowed (%zu)",
- JSONB_MAX_PAIRS)));
-
- if (object->val.object.nPairs >= pstate->size)
+ if (object->val.object.nPairs >= ppstate->size)
{
- pstate->size *= 2;
+ if (unlikely(object->val.object.nPairs >= JSONB_MAX_PAIRS))
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of jsonb object pairs exceeds the maximum allowed (%zu)",
+ JSONB_MAX_PAIRS)));
+ ppstate->size = Min(ppstate->size * 2, JSONB_MAX_PAIRS);
object->val.object.pairs = repalloc(object->val.object.pairs,
- sizeof(JsonbPair) * pstate->size);
+ sizeof(JsonbPair) * ppstate->size);
}
- object->val.object.pairs[object->val.object.nPairs].key = *string;
- object->val.object.pairs[object->val.object.nPairs].order = object->val.object.nPairs;
+ pair = &object->val.object.pairs[object->val.object.nPairs];
+ pair->key = *string;
+ pair->order = object->val.object.nPairs;
+
+ if (needCopy)
+ copyScalarSubstructure(&pair->key, pstate->outcontext);
}
/*
- * pushJsonbValue() worker: Append a pair value to state when generating a
- * Jsonb
+ * pushJsonbValue() worker: Append a pair value to pstate
*/
static void
-appendValue(JsonbParseState *pstate, JsonbValue *scalarVal)
+appendValue(JsonbInState *pstate, JsonbValue *scalarVal, bool needCopy)
{
- JsonbValue *object = &pstate->contVal;
+ JsonbValue *object = &pstate->parseState->contVal;
+ JsonbPair *pair;
Assert(object->type == jbvObject);
- object->val.object.pairs[object->val.object.nPairs++].value = *scalarVal;
+ pair = &object->val.object.pairs[object->val.object.nPairs];
+ pair->value = *scalarVal;
+ object->val.object.nPairs++;
+
+ if (needCopy)
+ copyScalarSubstructure(&pair->value, pstate->outcontext);
}
/*
- * pushJsonbValue() worker: Append an element to state when generating a Jsonb
+ * pushJsonbValue() worker: Append an array element to pstate
*/
static void
-appendElement(JsonbParseState *pstate, JsonbValue *scalarVal)
+appendElement(JsonbInState *pstate, JsonbValue *scalarVal, bool needCopy)
{
- JsonbValue *array = &pstate->contVal;
+ JsonbParseState *ppstate = pstate->parseState;
+ JsonbValue *array = &ppstate->contVal;
+ JsonbValue *elem;
Assert(array->type == jbvArray);
- if (array->val.array.nElems >= JSONB_MAX_ELEMS)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of jsonb array elements exceeds the maximum allowed (%zu)",
- JSONB_MAX_ELEMS)));
-
- if (array->val.array.nElems >= pstate->size)
+ if (array->val.array.nElems >= ppstate->size)
{
- pstate->size *= 2;
+ if (unlikely(array->val.array.nElems >= JSONB_MAX_ELEMS))
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of jsonb array elements exceeds the maximum allowed (%zu)",
+ JSONB_MAX_ELEMS)));
+ ppstate->size = Min(ppstate->size * 2, JSONB_MAX_ELEMS);
array->val.array.elems = repalloc(array->val.array.elems,
- sizeof(JsonbValue) * pstate->size);
+ sizeof(JsonbValue) * ppstate->size);
}
- array->val.array.elems[array->val.array.nElems++] = *scalarVal;
+ elem = &array->val.array.elems[array->val.array.nElems];
+ *elem = *scalarVal;
+ array->val.array.nElems++;
+
+ if (needCopy)
+ copyScalarSubstructure(elem, pstate->outcontext);
+}
+
+/*
+ * Copy any infrastructure of a scalar JsonbValue into the outcontext,
+ * adjusting the pointer(s) in *v.
+ *
+ * We need not deal with containers here, as the routines above ensure
+ * that they are built fresh.
+ */
+static void
+copyScalarSubstructure(JsonbValue *v, MemoryContext outcontext)
+{
+ MemoryContext oldcontext;
+
+ /* Nothing to do if caller did not specify an outcontext */
+ if (outcontext == NULL)
+ return;
+ switch (v->type)
+ {
+ case jbvNull:
+ case jbvBool:
+ /* pass-by-value, nothing to do */
+ break;
+ case jbvString:
+ {
+ char *buf = MemoryContextAlloc(outcontext,
+ v->val.string.len);
+
+ memcpy(buf, v->val.string.val, v->val.string.len);
+ v->val.string.val = buf;
+ }
+ break;
+ case jbvNumeric:
+ oldcontext = MemoryContextSwitchTo(outcontext);
+ v->val.numeric =
+ DatumGetNumeric(datumCopy(NumericGetDatum(v->val.numeric),
+ false, -1));
+ MemoryContextSwitchTo(oldcontext);
+ break;
+ case jbvDatetime:
+ switch (v->val.datetime.typid)
+ {
+ case DATEOID:
+ case TIMEOID:
+ case TIMESTAMPOID:
+ case TIMESTAMPTZOID:
+ /* pass-by-value, nothing to do */
+ break;
+ case TIMETZOID:
+ /* pass-by-reference */
+ oldcontext = MemoryContextSwitchTo(outcontext);
+ v->val.datetime.value = datumCopy(v->val.datetime.value,
+ false, TIMETZ_TYPLEN);
+ MemoryContextSwitchTo(oldcontext);
+ break;
+ default:
+ elog(ERROR, "unexpected jsonb datetime type oid %u",
+ v->val.datetime.typid);
+ }
+ break;
+ default:
+ elog(ERROR, "invalid jsonb scalar type");
+ }
}
/*
Node *escontext, bool omit_quotes);
/* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
-static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
- JsonbParseState **state);
-static JsonbValue *setPath(JsonbIterator **it, const Datum *path_elems,
- const bool *path_nulls, int path_len,
- JsonbParseState **st, int level, JsonbValue *newval,
- int op_type);
+static void IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
+ JsonbInState *state);
+static void setPath(JsonbIterator **it, const Datum *path_elems,
+ const bool *path_nulls, int path_len,
+ JsonbInState *st, int level, JsonbValue *newval,
+ int op_type);
static void setPathObject(JsonbIterator **it, const Datum *path_elems,
- const bool *path_nulls, int path_len, JsonbParseState **st,
+ const bool *path_nulls, int path_len, JsonbInState *st,
int level,
JsonbValue *newval, uint32 npairs, int op_type);
static void setPathArray(JsonbIterator **it, const Datum *path_elems,
- const bool *path_nulls, int path_len, JsonbParseState **st,
+ const bool *path_nulls, int path_len, JsonbInState *st,
int level,
JsonbValue *newval, uint32 nelems, int op_type);
jsonb_set_element(Jsonb *jb, const Datum *path, int path_len,
JsonbValue *newval)
{
- JsonbValue *res;
- JsonbParseState *state = NULL;
+ JsonbInState state = {0};
JsonbIterator *it;
bool *path_nulls = palloc0(path_len * sizeof(bool));
it = JsonbIteratorInit(&jb->root);
- res = setPath(&it, path, path_nulls, path_len, &state, 0, newval,
- JB_PATH_CREATE | JB_PATH_FILL_GAPS |
- JB_PATH_CONSISTENT_POSITION);
+ setPath(&it, path, path_nulls, path_len, &state, 0, newval,
+ JB_PATH_CREATE | JB_PATH_FILL_GAPS |
+ JB_PATH_CONSISTENT_POSITION);
pfree(path_nulls);
- PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
+ PG_RETURN_JSONB_P(JsonbValueToJsonb(state.result));
}
static void
-push_null_elements(JsonbParseState **ps, int num)
+push_null_elements(JsonbInState *ps, int num)
{
JsonbValue null;
* Caller is responsible to make sure such path does not exist yet.
*/
static void
-push_path(JsonbParseState **st, int level, const Datum *path_elems,
+push_path(JsonbInState *st, int level, const Datum *path_elems,
const bool *path_nulls, int path_len, JsonbValue *newval)
{
/*
newkey.val.string.val = c;
newkey.val.string.len = strlen(c);
- (void) pushJsonbValue(st, WJB_BEGIN_OBJECT, NULL);
- (void) pushJsonbValue(st, WJB_KEY, &newkey);
+ pushJsonbValue(st, WJB_BEGIN_OBJECT, NULL);
+ pushJsonbValue(st, WJB_KEY, &newkey);
tpath[i - level] = jbvObject;
}
else
{
/* integer, an array is expected */
- (void) pushJsonbValue(st, WJB_BEGIN_ARRAY, NULL);
+ pushJsonbValue(st, WJB_BEGIN_ARRAY, NULL);
push_null_elements(st, lindex);
/* Insert an actual value for either an object or array */
if (tpath[(path_len - level) - 1] == jbvArray)
- {
- (void) pushJsonbValue(st, WJB_ELEM, newval);
- }
+ pushJsonbValue(st, WJB_ELEM, newval);
else
- (void) pushJsonbValue(st, WJB_VALUE, newval);
+ pushJsonbValue(st, WJB_VALUE, newval);
/*
* Close everything up to the last but one level. The last one will be
break;
if (tpath[i - level] == jbvObject)
- (void) pushJsonbValue(st, WJB_END_OBJECT, NULL);
+ pushJsonbValue(st, WJB_END_OBJECT, NULL);
else
- (void) pushJsonbValue(st, WJB_END_ARRAY, NULL);
+ pushJsonbValue(st, WJB_END_ARRAY, NULL);
}
}
Jsonb *jb = PG_GETARG_JSONB_P(0);
bool strip_in_arrays = false;
JsonbIterator *it;
- JsonbParseState *parseState = NULL;
- JsonbValue *res = NULL;
+ JsonbInState parseState = {0};
JsonbValue v,
k;
JsonbIteratorToken type;
continue;
/* otherwise, do a delayed push of the key */
- (void) pushJsonbValue(&parseState, WJB_KEY, &k);
+ pushJsonbValue(&parseState, WJB_KEY, &k);
}
/* if strip_in_arrays is set, also skip null array elements */
continue;
if (type == WJB_VALUE || type == WJB_ELEM)
- res = pushJsonbValue(&parseState, type, &v);
+ pushJsonbValue(&parseState, type, &v);
else
- res = pushJsonbValue(&parseState, type, NULL);
+ pushJsonbValue(&parseState, type, NULL);
}
- Assert(res != NULL);
-
- PG_RETURN_POINTER(JsonbValueToJsonb(res));
+ PG_RETURN_POINTER(JsonbValueToJsonb(parseState.result));
}
/*
{
Jsonb *jb1 = PG_GETARG_JSONB_P(0);
Jsonb *jb2 = PG_GETARG_JSONB_P(1);
- JsonbParseState *state = NULL;
- JsonbValue *res;
+ JsonbInState state = {0};
JsonbIterator *it1,
*it2;
it1 = JsonbIteratorInit(&jb1->root);
it2 = JsonbIteratorInit(&jb2->root);
- res = IteratorConcat(&it1, &it2, &state);
-
- Assert(res != NULL);
+ IteratorConcat(&it1, &it2, &state);
- PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
+ PG_RETURN_JSONB_P(JsonbValueToJsonb(state.result));
}
text *key = PG_GETARG_TEXT_PP(1);
char *keyptr = VARDATA_ANY(key);
int keylen = VARSIZE_ANY_EXHDR(key);
- JsonbParseState *state = NULL;
+ JsonbInState pstate = {0};
JsonbIterator *it;
- JsonbValue v,
- *res = NULL;
+ JsonbValue v;
bool skipNested = false;
JsonbIteratorToken r;
continue;
}
- res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
+ pushJsonbValue(&pstate, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
}
- Assert(res != NULL);
-
- PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
+ PG_RETURN_JSONB_P(JsonbValueToJsonb(pstate.result));
}
/*
Datum *keys_elems;
bool *keys_nulls;
int keys_len;
- JsonbParseState *state = NULL;
+ JsonbInState pstate = {0};
JsonbIterator *it;
- JsonbValue v,
- *res = NULL;
+ JsonbValue v;
bool skipNested = false;
JsonbIteratorToken r;
}
}
- res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
+ pushJsonbValue(&pstate, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
}
- Assert(res != NULL);
-
- PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
+ PG_RETURN_JSONB_P(JsonbValueToJsonb(pstate.result));
}
/*
{
Jsonb *in = PG_GETARG_JSONB_P(0);
int idx = PG_GETARG_INT32(1);
- JsonbParseState *state = NULL;
+ JsonbInState pstate = {0};
JsonbIterator *it;
uint32 i = 0,
n;
- JsonbValue v,
- *res = NULL;
+ JsonbValue v;
JsonbIteratorToken r;
if (JB_ROOT_IS_SCALAR(in))
if (idx >= n)
PG_RETURN_JSONB_P(in);
- pushJsonbValue(&state, r, NULL);
+ pushJsonbValue(&pstate, r, NULL);
while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
{
continue;
}
- res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
+ pushJsonbValue(&pstate, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
}
- Assert(res != NULL);
-
- PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
+ PG_RETURN_JSONB_P(JsonbValueToJsonb(pstate.result));
}
/*
Jsonb *newjsonb = PG_GETARG_JSONB_P(2);
JsonbValue newval;
bool create = PG_GETARG_BOOL(3);
- JsonbValue *res = NULL;
Datum *path_elems;
bool *path_nulls;
int path_len;
JsonbIterator *it;
- JsonbParseState *st = NULL;
+ JsonbInState st = {0};
JsonbToJsonbValue(newjsonb, &newval);
it = JsonbIteratorInit(&in->root);
- res = setPath(&it, path_elems, path_nulls, path_len, &st,
- 0, &newval, create ? JB_PATH_CREATE : JB_PATH_REPLACE);
-
- Assert(res != NULL);
+ setPath(&it, path_elems, path_nulls, path_len, &st,
+ 0, &newval, create ? JB_PATH_CREATE : JB_PATH_REPLACE);
- PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
+ PG_RETURN_JSONB_P(JsonbValueToJsonb(st.result));
}
{
Jsonb *in = PG_GETARG_JSONB_P(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- JsonbValue *res = NULL;
Datum *path_elems;
bool *path_nulls;
int path_len;
JsonbIterator *it;
- JsonbParseState *st = NULL;
+ JsonbInState st = {0};
if (ARR_NDIM(path) > 1)
ereport(ERROR,
it = JsonbIteratorInit(&in->root);
- res = setPath(&it, path_elems, path_nulls, path_len, &st,
- 0, NULL, JB_PATH_DELETE);
+ setPath(&it, path_elems, path_nulls, path_len, &st,
+ 0, NULL, JB_PATH_DELETE);
- Assert(res != NULL);
-
- PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
+ PG_RETURN_JSONB_P(JsonbValueToJsonb(st.result));
}
/*
Jsonb *newjsonb = PG_GETARG_JSONB_P(2);
JsonbValue newval;
bool after = PG_GETARG_BOOL(3);
- JsonbValue *res = NULL;
Datum *path_elems;
bool *path_nulls;
int path_len;
JsonbIterator *it;
- JsonbParseState *st = NULL;
+ JsonbInState st = {0};
JsonbToJsonbValue(newjsonb, &newval);
it = JsonbIteratorInit(&in->root);
- res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, &newval,
- after ? JB_PATH_INSERT_AFTER : JB_PATH_INSERT_BEFORE);
-
- Assert(res != NULL);
+ setPath(&it, path_elems, path_nulls, path_len, &st, 0, &newval,
+ after ? JB_PATH_INSERT_AFTER : JB_PATH_INSERT_BEFORE);
- PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
+ PG_RETURN_JSONB_P(JsonbValueToJsonb(st.result));
}
/*
* In that case we just append the content of it2 to it1 without any
* verifications.
*/
-static JsonbValue *
+static void
IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
- JsonbParseState **state)
+ JsonbInState *state)
{
JsonbValue v1,
- v2,
- *res = NULL;
+ v2;
JsonbIteratorToken r1,
r2,
rk1,
* automatically override the value from the first object.
*/
while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
- res = pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
+ pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
}
else if (rk1 == WJB_BEGIN_ARRAY && rk2 == WJB_BEGIN_ARRAY)
{
pushJsonbValue(state, WJB_ELEM, &v2);
}
- res = pushJsonbValue(state, WJB_END_ARRAY, NULL /* signal to sort */ );
+ pushJsonbValue(state, WJB_END_ARRAY, NULL /* signal to sort */ );
}
else if (rk1 == WJB_BEGIN_OBJECT)
{
pushJsonbValue(state, r1, r1 != WJB_END_OBJECT ? &v1 : NULL);
while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
- res = pushJsonbValue(state, r2, r2 != WJB_END_ARRAY ? &v2 : NULL);
+ pushJsonbValue(state, r2, r2 != WJB_END_ARRAY ? &v2 : NULL);
}
else
{
while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
- res = pushJsonbValue(state, WJB_END_ARRAY, NULL);
+ pushJsonbValue(state, WJB_END_ARRAY, NULL);
}
-
- return res;
}
/*
* All path elements before the last must already exist
* whatever bits in op_type are set, or nothing is done.
*/
-static JsonbValue *
+static void
setPath(JsonbIterator **it, const Datum *path_elems,
const bool *path_nulls, int path_len,
- JsonbParseState **st, int level, JsonbValue *newval, int op_type)
+ JsonbInState *st, int level, JsonbValue *newval, int op_type)
{
JsonbValue v;
JsonbIteratorToken r;
- JsonbValue *res;
check_stack_depth();
errdetail("The path assumes key is a composite object, "
"but it is a scalar value.")));
- (void) pushJsonbValue(st, r, NULL);
+ pushJsonbValue(st, r, NULL);
setPathArray(it, path_elems, path_nulls, path_len, st, level,
newval, v.val.array.nElems, op_type);
r = JsonbIteratorNext(it, &v, false);
Assert(r == WJB_END_ARRAY);
- res = pushJsonbValue(st, r, NULL);
+ pushJsonbValue(st, r, NULL);
break;
case WJB_BEGIN_OBJECT:
- (void) pushJsonbValue(st, r, NULL);
+ pushJsonbValue(st, r, NULL);
setPathObject(it, path_elems, path_nulls, path_len, st, level,
newval, v.val.object.nPairs, op_type);
r = JsonbIteratorNext(it, &v, true);
Assert(r == WJB_END_OBJECT);
- res = pushJsonbValue(st, r, NULL);
+ pushJsonbValue(st, r, NULL);
break;
case WJB_ELEM:
case WJB_VALUE:
errdetail("The path assumes key is a composite object, "
"but it is a scalar value.")));
- res = pushJsonbValue(st, r, &v);
+ pushJsonbValue(st, r, &v);
break;
default:
elog(ERROR, "unrecognized iterator result: %d", (int) r);
- res = NULL; /* keep compiler quiet */
break;
}
-
- return res;
}
/*
*/
static void
setPathObject(JsonbIterator **it, const Datum *path_elems, const bool *path_nulls,
- int path_len, JsonbParseState **st, int level,
+ int path_len, JsonbInState *st, int level,
JsonbValue *newval, uint32 npairs, int op_type)
{
text *pathelem = NULL;
newkey.val.string.val = VARDATA_ANY(pathelem);
newkey.val.string.len = VARSIZE_ANY_EXHDR(pathelem);
- (void) pushJsonbValue(st, WJB_KEY, &newkey);
- (void) pushJsonbValue(st, WJB_VALUE, newval);
+ pushJsonbValue(st, WJB_KEY, &newkey);
+ pushJsonbValue(st, WJB_VALUE, newval);
}
for (i = 0; i < npairs; i++)
r = JsonbIteratorNext(it, &v, true); /* skip value */
if (!(op_type & JB_PATH_DELETE))
{
- (void) pushJsonbValue(st, WJB_KEY, &k);
- (void) pushJsonbValue(st, WJB_VALUE, newval);
+ pushJsonbValue(st, WJB_KEY, &k);
+ pushJsonbValue(st, WJB_VALUE, newval);
}
}
else
{
- (void) pushJsonbValue(st, r, &k);
+ pushJsonbValue(st, r, &k);
setPath(it, path_elems, path_nulls, path_len,
st, level + 1, newval, op_type);
}
newkey.val.string.val = VARDATA_ANY(pathelem);
newkey.val.string.len = VARSIZE_ANY_EXHDR(pathelem);
- (void) pushJsonbValue(st, WJB_KEY, &newkey);
- (void) pushJsonbValue(st, WJB_VALUE, newval);
+ pushJsonbValue(st, WJB_KEY, &newkey);
+ pushJsonbValue(st, WJB_VALUE, newval);
}
- (void) pushJsonbValue(st, r, &k);
+ pushJsonbValue(st, r, &k);
r = JsonbIteratorNext(it, &v, false);
- (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
+ pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
{
int walking_level = 1;
if (r == WJB_END_ARRAY || r == WJB_END_OBJECT)
--walking_level;
- (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
+ pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
}
}
}
newkey.val.string.val = VARDATA_ANY(pathelem);
newkey.val.string.len = VARSIZE_ANY_EXHDR(pathelem);
- (void) pushJsonbValue(st, WJB_KEY, &newkey);
+ pushJsonbValue(st, WJB_KEY, &newkey);
push_path(st, level, path_elems, path_nulls, path_len, newval);
/* Result is closed with WJB_END_OBJECT outside of this function */
*/
static void
setPathArray(JsonbIterator **it, const Datum *path_elems, const bool *path_nulls,
- int path_len, JsonbParseState **st, int level,
+ int path_len, JsonbInState *st, int level,
JsonbValue *newval, uint32 nelems, int op_type)
{
JsonbValue v;
if (op_type & JB_PATH_FILL_GAPS && nelems == 0 && idx > 0)
push_null_elements(st, idx);
- (void) pushJsonbValue(st, WJB_ELEM, newval);
+ pushJsonbValue(st, WJB_ELEM, newval);
done = true;
}
r = JsonbIteratorNext(it, &v, true); /* skip */
if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE))
- (void) pushJsonbValue(st, WJB_ELEM, newval);
+ pushJsonbValue(st, WJB_ELEM, newval);
/*
* We should keep current value only in case of
* otherwise it should be deleted or replaced
*/
if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_INSERT_BEFORE))
- (void) pushJsonbValue(st, r, &v);
+ pushJsonbValue(st, r, &v);
if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_REPLACE))
- (void) pushJsonbValue(st, WJB_ELEM, newval);
+ pushJsonbValue(st, WJB_ELEM, newval);
}
else
- (void) setPath(it, path_elems, path_nulls, path_len,
- st, level + 1, newval, op_type);
+ setPath(it, path_elems, path_nulls, path_len,
+ st, level + 1, newval, op_type);
}
else
{
r = JsonbIteratorNext(it, &v, false);
- (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
+ pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
{
if (r == WJB_END_ARRAY || r == WJB_END_OBJECT)
--walking_level;
- (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
+ pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
}
}
}
if (op_type & JB_PATH_FILL_GAPS && idx > nelems)
push_null_elements(st, idx - nelems);
- (void) pushJsonbValue(st, WJB_ELEM, newval);
+ pushJsonbValue(st, WJB_ELEM, newval);
done = true;
}
JsonTransformStringValuesAction transform_action)
{
JsonbIterator *it;
- JsonbValue v,
- *res = NULL;
+ JsonbValue v;
JsonbIteratorToken type;
- JsonbParseState *st = NULL;
+ JsonbInState st = {0};
text *out;
bool is_scalar = false;
out = pg_detoast_datum_packed(out);
v.val.string.val = VARDATA_ANY(out);
v.val.string.len = VARSIZE_ANY_EXHDR(out);
- res = pushJsonbValue(&st, type, type < WJB_BEGIN_ARRAY ? &v : NULL);
+ pushJsonbValue(&st, type, type < WJB_BEGIN_ARRAY ? &v : NULL);
}
else
{
- res = pushJsonbValue(&st, type, (type == WJB_KEY ||
- type == WJB_VALUE ||
- type == WJB_ELEM) ? &v : NULL);
+ pushJsonbValue(&st, type, (type == WJB_KEY ||
+ type == WJB_VALUE ||
+ type == WJB_ELEM) ? &v : NULL);
}
}
- if (res->type == jbvArray)
- res->val.array.rawScalar = is_scalar;
+ if (st.result->type == jbvArray)
+ st.result->val.array.rawScalar = is_scalar;
- return JsonbValueToJsonb(res);
+ return JsonbValueToJsonb(st.result);
}
/*
{
JsonBaseObjectInfo baseObject;
JsonbValue obj;
- JsonbParseState *ps;
- JsonbValue *keyval;
+ JsonbInState ps;
Jsonb *jsonb;
if (tok != WJB_KEY)
tok = JsonbIteratorNext(&it, &val, true);
Assert(tok == WJB_VALUE);
- ps = NULL;
+ memset(&ps, 0, sizeof(ps));
+
pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
pushJsonbValue(&ps, WJB_KEY, &keystr);
pushJsonbValue(&ps, WJB_KEY, &idstr);
pushJsonbValue(&ps, WJB_VALUE, &idval);
- keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
+ pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
- jsonb = JsonbValueToJsonb(keyval);
+ jsonb = JsonbValueToJsonb(ps.result);
JsonbInitBinary(&obj, jsonb);
static JsonbValue *
wrapItemsInArray(const JsonValueList *items)
{
- JsonbParseState *ps = NULL;
+ JsonbInState ps = {0};
JsonValueListIterator it;
JsonbValue *jbv;
while ((jbv = JsonValueListNext(items, &it)))
pushJsonbValue(&ps, WJB_ELEM, jbv);
- return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
+ pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
+
+ return ps.result;
}
/* Check if the timezone required for casting from type1 to type2 is used */
#define JGINFLAG_HASHED 0x10 /* OR'd into flag if value was hashed */
#define JGIN_MAXLENGTH 125 /* max length of text part before hashing */
+/* Forward struct references */
typedef struct JsonbPair JsonbPair;
typedef struct JsonbValue JsonbValue;
+typedef struct JsonbParseState JsonbParseState;
/*
* Jsonbs are varlena objects, so must meet the varlena convention that the
uint32 order; /* Pair's index in original sequence */
};
-/* Conversion state used when parsing Jsonb from text, or for type coercion */
-typedef struct JsonbParseState
+/*
+ * State used while constructing or manipulating a JsonbValue.
+ * For example, when parsing Jsonb from text, we construct a JsonbValue
+ * data structure and then flatten that into the Jsonb on-disk format.
+ * JsonbValues are also useful in aggregation and type coercion.
+ *
+ * Callers providing a JsonbInState must initialize it to zeroes/nulls,
+ * except for optionally setting outcontext (if that's left NULL,
+ * CurrentMemoryContext is used) and escontext (if that's left NULL,
+ * parsing errors are thrown via ereport).
+ */
+typedef struct JsonbInState
{
- JsonbValue contVal;
- Size size;
- struct JsonbParseState *next;
+ JsonbValue *result; /* The completed value; NULL until complete */
+ MemoryContext outcontext; /* The context to build it in, or NULL */
+ struct Node *escontext; /* Optional soft-error-reporting context */
+ /* Remaining fields should be treated as private to jsonb.c/jsonb_util.c */
+ JsonbParseState *parseState; /* Stack of parsing contexts */
+ bool unique_keys; /* Check object key uniqueness */
+} JsonbInState;
+
+/*
+ * Parsing context for one level of Jsonb array or object nesting.
+ * The contVal will be part of the constructed JsonbValue tree,
+ * but the other fields are just transient state.
+ */
+struct JsonbParseState
+{
+ JsonbValue contVal; /* An array or object JsonbValue */
+ Size size; /* Allocated length of array or object */
+ JsonbParseState *next; /* Link to next outer level, if any */
bool unique_keys; /* Check object key uniqueness */
bool skip_nulls; /* Skip null object fields */
-} JsonbParseState;
+};
/*
* JsonbIterator holds details of the type for each iteration. It also stores a
JsonbValue *res);
extern JsonbValue *getIthJsonbValueFromContainer(JsonbContainer *container,
uint32 i);
-extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,
- JsonbIteratorToken seq, JsonbValue *jbval);
+extern void pushJsonbValue(JsonbInState *pstate,
+ JsonbIteratorToken seq, JsonbValue *jbval);
extern JsonbIterator *JsonbIteratorInit(JsonbContainer *container);
extern JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val,
bool skipNested);