mirror of
https://github.com/postgres/postgres.git
synced 2025-12-10 14:22:35 +03:00
Revise APIs for pushJsonbValue() and associated routines.
Instead of passing "JsonbParseState **" to pushJsonbValue(), pass a pointer to a JsonbInState, which will contain the parseState stack pointer as well as other useful fields. Also, instead of returning a JsonbValue pointer that is often meaningless/ignored, return the top-level JsonbValue pointer in the "result" field of the JsonbInState. This involves a lot of (mostly mechanical) edits, but I think the results are notationally cleaner and easier to understand. Certainly the business with sometimes capturing the result of pushJsonbValue() and sometimes not was bug-prone and incapable of mechanical verification. In the new arrangement, JsonbInState.result remains null until we've completed a valid sequence of pushes, so that an incorrect sequence will result in a null-pointer dereference, not mistaken use of a partial result. However, this isn't simply an exercise in prettier notation. The real reason for doing it is to provide a mechanism whereby pushJsonbValue() can be told to construct the JsonbValue tree in a context that is not CurrentMemoryContext. That happens when a non-null "outcontext" is specified in the JsonbInState. No callers exercise that option in this patch, but the next patch in the series will make use of it. I tried to improve the comments in this area too. Author: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: jian he <jian.universality@gmail.com> Reviewed-by: Chao Li <li.evan.chao@gmail.com> Discussion: https://postgr.es/m/1060917.1753202222@sss.pgh.pa.us
This commit is contained in:
@@ -1439,10 +1439,9 @@ hstore_to_jsonb(PG_FUNCTION_ARGS)
|
|||||||
int count = HS_COUNT(in);
|
int count = HS_COUNT(in);
|
||||||
char *base = STRPTR(in);
|
char *base = STRPTR(in);
|
||||||
HEntry *entries = ARRPTR(in);
|
HEntry *entries = ARRPTR(in);
|
||||||
JsonbParseState *state = NULL;
|
JsonbInState state = {0};
|
||||||
JsonbValue *res;
|
|
||||||
|
|
||||||
(void) pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
|
pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
|
||||||
|
|
||||||
for (i = 0; i < count; i++)
|
for (i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
@@ -1453,7 +1452,7 @@ hstore_to_jsonb(PG_FUNCTION_ARGS)
|
|||||||
key.val.string.len = HSTORE_KEYLEN(entries, i);
|
key.val.string.len = HSTORE_KEYLEN(entries, i);
|
||||||
key.val.string.val = HSTORE_KEY(entries, base, 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))
|
if (HSTORE_VALISNULL(entries, i))
|
||||||
{
|
{
|
||||||
@@ -1465,12 +1464,12 @@ hstore_to_jsonb(PG_FUNCTION_ARGS)
|
|||||||
val.val.string.len = HSTORE_VALLEN(entries, i);
|
val.val.string.len = HSTORE_VALLEN(entries, i);
|
||||||
val.val.string.val = HSTORE_VAL(entries, base, 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);
|
PG_FUNCTION_INFO_V1(hstore_to_jsonb_loose);
|
||||||
@@ -1482,13 +1481,12 @@ hstore_to_jsonb_loose(PG_FUNCTION_ARGS)
|
|||||||
int count = HS_COUNT(in);
|
int count = HS_COUNT(in);
|
||||||
char *base = STRPTR(in);
|
char *base = STRPTR(in);
|
||||||
HEntry *entries = ARRPTR(in);
|
HEntry *entries = ARRPTR(in);
|
||||||
JsonbParseState *state = NULL;
|
JsonbInState state = {0};
|
||||||
JsonbValue *res;
|
|
||||||
StringInfoData tmp;
|
StringInfoData tmp;
|
||||||
|
|
||||||
initStringInfo(&tmp);
|
initStringInfo(&tmp);
|
||||||
|
|
||||||
(void) pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
|
pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
|
||||||
|
|
||||||
for (i = 0; i < count; i++)
|
for (i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
@@ -1499,7 +1497,7 @@ hstore_to_jsonb_loose(PG_FUNCTION_ARGS)
|
|||||||
key.val.string.len = HSTORE_KEYLEN(entries, i);
|
key.val.string.len = HSTORE_KEYLEN(entries, i);
|
||||||
key.val.string.val = HSTORE_KEY(entries, base, 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))
|
if (HSTORE_VALISNULL(entries, i))
|
||||||
{
|
{
|
||||||
@@ -1541,10 +1539,10 @@ hstore_to_jsonb_loose(PG_FUNCTION_ARGS)
|
|||||||
val.val.string.val = HSTORE_VAL(entries, base, 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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ PG_MODULE_MAGIC_EXT(
|
|||||||
);
|
);
|
||||||
|
|
||||||
static SV *Jsonb_to_SV(JsonbContainer *jsonb);
|
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 SV *
|
||||||
@@ -127,8 +127,8 @@ Jsonb_to_SV(JsonbContainer *jsonb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static JsonbValue *
|
static void
|
||||||
AV_to_JsonbValue(AV *in, JsonbParseState **jsonb_state)
|
AV_to_JsonbValue(AV *in, JsonbInState *jsonb_state)
|
||||||
{
|
{
|
||||||
dTHX;
|
dTHX;
|
||||||
SSize_t pcount = av_len(in) + 1;
|
SSize_t pcount = av_len(in) + 1;
|
||||||
@@ -141,14 +141,14 @@ AV_to_JsonbValue(AV *in, JsonbParseState **jsonb_state)
|
|||||||
SV **value = av_fetch(in, i, FALSE);
|
SV **value = av_fetch(in, i, FALSE);
|
||||||
|
|
||||||
if (value)
|
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 *
|
static void
|
||||||
HV_to_JsonbValue(HV *obj, JsonbParseState **jsonb_state)
|
HV_to_JsonbValue(HV *obj, JsonbInState *jsonb_state)
|
||||||
{
|
{
|
||||||
dTHX;
|
dTHX;
|
||||||
JsonbValue key;
|
JsonbValue key;
|
||||||
@@ -167,14 +167,14 @@ HV_to_JsonbValue(HV *obj, JsonbParseState **jsonb_state)
|
|||||||
key.val.string.val = pnstrdup(kstr, klen);
|
key.val.string.val = pnstrdup(kstr, klen);
|
||||||
key.val.string.len = klen;
|
key.val.string.len = klen;
|
||||||
pushJsonbValue(jsonb_state, WJB_KEY, &key);
|
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 *
|
static void
|
||||||
SV_to_JsonbValue(SV *in, JsonbParseState **jsonb_state, bool is_elem)
|
SV_to_JsonbValue(SV *in, JsonbInState *jsonb_state, bool is_elem)
|
||||||
{
|
{
|
||||||
dTHX;
|
dTHX;
|
||||||
JsonbValue out; /* result */
|
JsonbValue out; /* result */
|
||||||
@@ -186,10 +186,12 @@ SV_to_JsonbValue(SV *in, JsonbParseState **jsonb_state, bool is_elem)
|
|||||||
switch (SvTYPE(in))
|
switch (SvTYPE(in))
|
||||||
{
|
{
|
||||||
case SVt_PVAV:
|
case SVt_PVAV:
|
||||||
return AV_to_JsonbValue((AV *) in, jsonb_state);
|
AV_to_JsonbValue((AV *) in, jsonb_state);
|
||||||
|
return;
|
||||||
|
|
||||||
case SVt_PVHV:
|
case SVt_PVHV:
|
||||||
return HV_to_JsonbValue((HV *) in, jsonb_state);
|
HV_to_JsonbValue((HV *) in, jsonb_state);
|
||||||
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (!SvOK(in))
|
if (!SvOK(in))
|
||||||
@@ -259,14 +261,24 @@ SV_to_JsonbValue(SV *in, JsonbParseState **jsonb_state, bool is_elem)
|
|||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("cannot transform this Perl type to jsonb")));
|
errmsg("cannot transform this Perl type to jsonb")));
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Push result into 'jsonb_state' unless it is a raw scalar. */
|
if (jsonb_state->parseState)
|
||||||
return *jsonb_state
|
{
|
||||||
? pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, &out)
|
/* We're in an array or object, so push value as element or field. */
|
||||||
: memcpy(palloc_object(JsonbValue), &out, sizeof(JsonbValue));
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -289,10 +301,9 @@ Datum
|
|||||||
plperl_to_jsonb(PG_FUNCTION_ARGS)
|
plperl_to_jsonb(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
dTHX;
|
dTHX;
|
||||||
JsonbParseState *jsonb_state = NULL;
|
|
||||||
SV *in = (SV *) PG_GETARG_POINTER(0);
|
SV *in = (SV *) PG_GETARG_POINTER(0);
|
||||||
JsonbValue *out = SV_to_JsonbValue(in, &jsonb_state, true);
|
JsonbInState jsonb_state = {0};
|
||||||
Jsonb *result = JsonbValueToJsonb(out);
|
|
||||||
|
|
||||||
PG_RETURN_JSONB_P(result);
|
SV_to_JsonbValue(in, &jsonb_state, true);
|
||||||
|
PG_RETURN_JSONB_P(JsonbValueToJsonb(jsonb_state.result));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ static PLy_elog_impl_t PLy_elog_impl_p;
|
|||||||
static PyObject *decimal_constructor;
|
static PyObject *decimal_constructor;
|
||||||
|
|
||||||
static PyObject *PLyObject_FromJsonbContainer(JsonbContainer *jsonb);
|
static PyObject *PLyObject_FromJsonbContainer(JsonbContainer *jsonb);
|
||||||
static JsonbValue *PLyObject_ToJsonbValue(PyObject *obj,
|
static void PLyObject_ToJsonbValue(PyObject *obj,
|
||||||
JsonbParseState **jsonb_state, bool is_elem);
|
JsonbInState *jsonb_state, bool is_elem);
|
||||||
|
|
||||||
typedef PyObject *(*PLyUnicode_FromStringAndSize_t)
|
typedef PyObject *(*PLyUnicode_FromStringAndSize_t)
|
||||||
(const char *s, Py_ssize_t size);
|
(const char *s, Py_ssize_t size);
|
||||||
@@ -261,12 +261,11 @@ PLyObject_FromJsonbContainer(JsonbContainer *jsonb)
|
|||||||
*
|
*
|
||||||
* Transform Python dict to JsonbValue.
|
* Transform Python dict to JsonbValue.
|
||||||
*/
|
*/
|
||||||
static JsonbValue *
|
static void
|
||||||
PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
|
PLyMapping_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state)
|
||||||
{
|
{
|
||||||
Py_ssize_t pcount;
|
Py_ssize_t pcount;
|
||||||
PyObject *volatile items;
|
PyObject *volatile items;
|
||||||
JsonbValue *volatile out;
|
|
||||||
|
|
||||||
pcount = PyMapping_Size(obj);
|
pcount = PyMapping_Size(obj);
|
||||||
items = PyMapping_Items(obj);
|
items = PyMapping_Items(obj);
|
||||||
@@ -297,19 +296,17 @@ PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
|
|||||||
PLyUnicode_ToJsonbValue(key, &jbvKey);
|
PLyUnicode_ToJsonbValue(key, &jbvKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) pushJsonbValue(jsonb_state, WJB_KEY, &jbvKey);
|
pushJsonbValue(jsonb_state, WJB_KEY, &jbvKey);
|
||||||
(void) PLyObject_ToJsonbValue(value, jsonb_state, false);
|
PLyObject_ToJsonbValue(value, jsonb_state, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
out = pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
|
pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
|
||||||
}
|
}
|
||||||
PG_FINALLY();
|
PG_FINALLY();
|
||||||
{
|
{
|
||||||
Py_DECREF(items);
|
Py_DECREF(items);
|
||||||
}
|
}
|
||||||
PG_END_TRY();
|
PG_END_TRY();
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -318,8 +315,8 @@ PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
|
|||||||
* Transform python list to JsonbValue. Expects transformed PyObject and
|
* Transform python list to JsonbValue. Expects transformed PyObject and
|
||||||
* a state required for jsonb construction.
|
* a state required for jsonb construction.
|
||||||
*/
|
*/
|
||||||
static JsonbValue *
|
static void
|
||||||
PLySequence_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
|
PLySequence_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state)
|
||||||
{
|
{
|
||||||
Py_ssize_t i;
|
Py_ssize_t i;
|
||||||
Py_ssize_t pcount;
|
Py_ssize_t pcount;
|
||||||
@@ -336,7 +333,7 @@ PLySequence_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
|
|||||||
value = PySequence_GetItem(obj, i);
|
value = PySequence_GetItem(obj, i);
|
||||||
Assert(value);
|
Assert(value);
|
||||||
|
|
||||||
(void) PLyObject_ToJsonbValue(value, jsonb_state, true);
|
PLyObject_ToJsonbValue(value, jsonb_state, true);
|
||||||
Py_XDECREF(value);
|
Py_XDECREF(value);
|
||||||
value = NULL;
|
value = NULL;
|
||||||
}
|
}
|
||||||
@@ -348,7 +345,7 @@ PLySequence_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
|
|||||||
}
|
}
|
||||||
PG_END_TRY();
|
PG_END_TRY();
|
||||||
|
|
||||||
return pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
|
pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -406,17 +403,23 @@ PLyNumber_ToJsonbValue(PyObject *obj, JsonbValue *jbvNum)
|
|||||||
*
|
*
|
||||||
* Transform python object to JsonbValue.
|
* Transform python object to JsonbValue.
|
||||||
*/
|
*/
|
||||||
static JsonbValue *
|
static void
|
||||||
PLyObject_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state, bool is_elem)
|
PLyObject_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state, bool is_elem)
|
||||||
{
|
{
|
||||||
JsonbValue *out;
|
JsonbValue *out;
|
||||||
|
|
||||||
if (!PyUnicode_Check(obj))
|
if (!PyUnicode_Check(obj))
|
||||||
{
|
{
|
||||||
if (PySequence_Check(obj))
|
if (PySequence_Check(obj))
|
||||||
return PLySequence_ToJsonbValue(obj, jsonb_state);
|
{
|
||||||
|
PLySequence_ToJsonbValue(obj, jsonb_state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
else if (PyMapping_Check(obj))
|
else if (PyMapping_Check(obj))
|
||||||
return PLyMapping_ToJsonbValue(obj, jsonb_state);
|
{
|
||||||
|
PLyMapping_ToJsonbValue(obj, jsonb_state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out = palloc_object(JsonbValue);
|
out = palloc_object(JsonbValue);
|
||||||
@@ -443,10 +446,20 @@ PLyObject_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state, bool is_ele
|
|||||||
errmsg("Python type \"%s\" cannot be transformed to jsonb",
|
errmsg("Python type \"%s\" cannot be transformed to jsonb",
|
||||||
PLyObject_AsString((PyObject *) obj->ob_type))));
|
PLyObject_AsString((PyObject *) obj->ob_type))));
|
||||||
|
|
||||||
/* Push result into 'jsonb_state' unless it is raw scalar value. */
|
if (jsonb_state->parseState)
|
||||||
return (*jsonb_state ?
|
{
|
||||||
pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, out) :
|
/* We're in an array or object, so push value as element or field. */
|
||||||
out);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -458,13 +471,11 @@ PG_FUNCTION_INFO_V1(plpython_to_jsonb);
|
|||||||
Datum
|
Datum
|
||||||
plpython_to_jsonb(PG_FUNCTION_ARGS)
|
plpython_to_jsonb(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
PyObject *obj;
|
PyObject *obj = (PyObject *) PG_GETARG_POINTER(0);
|
||||||
JsonbValue *out;
|
JsonbInState jsonb_state = {0};
|
||||||
JsonbParseState *jsonb_state = NULL;
|
|
||||||
|
|
||||||
obj = (PyObject *) PG_GETARG_POINTER(0);
|
PLyObject_ToJsonbValue(obj, &jsonb_state, true);
|
||||||
out = PLyObject_ToJsonbValue(obj, &jsonb_state, true);
|
PG_RETURN_POINTER(JsonbValueToJsonb(jsonb_state.result));
|
||||||
PG_RETURN_POINTER(JsonbValueToJsonb(out));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -25,14 +25,6 @@
|
|||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/typcache.h"
|
#include "utils/typcache.h"
|
||||||
|
|
||||||
typedef struct JsonbInState
|
|
||||||
{
|
|
||||||
JsonbParseState *parseState;
|
|
||||||
JsonbValue *res;
|
|
||||||
bool unique_keys;
|
|
||||||
Node *escontext;
|
|
||||||
} JsonbInState;
|
|
||||||
|
|
||||||
typedef struct JsonbAggState
|
typedef struct JsonbAggState
|
||||||
{
|
{
|
||||||
JsonbInState *res;
|
JsonbInState *res;
|
||||||
@@ -270,8 +262,8 @@ jsonb_from_cstring(char *json, int len, bool unique_keys, Node *escontext)
|
|||||||
if (!pg_parse_json_or_errsave(&lex, &sem, escontext))
|
if (!pg_parse_json_or_errsave(&lex, &sem, escontext))
|
||||||
return (Datum) 0;
|
return (Datum) 0;
|
||||||
|
|
||||||
/* after parsing, the item member has the composed jsonb structure */
|
/* after parsing, the result field has the composed jsonb structure */
|
||||||
PG_RETURN_POINTER(JsonbValueToJsonb(state.res));
|
PG_RETURN_POINTER(JsonbValueToJsonb(state.result));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@@ -292,7 +284,7 @@ jsonb_in_object_start(void *pstate)
|
|||||||
{
|
{
|
||||||
JsonbInState *_state = (JsonbInState *) pstate;
|
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;
|
_state->parseState->unique_keys = _state->unique_keys;
|
||||||
|
|
||||||
return JSON_SUCCESS;
|
return JSON_SUCCESS;
|
||||||
@@ -303,7 +295,7 @@ jsonb_in_object_end(void *pstate)
|
|||||||
{
|
{
|
||||||
JsonbInState *_state = (JsonbInState *) pstate;
|
JsonbInState *_state = (JsonbInState *) pstate;
|
||||||
|
|
||||||
_state->res = pushJsonbValue(&_state->parseState, WJB_END_OBJECT, NULL);
|
pushJsonbValue(_state, WJB_END_OBJECT, NULL);
|
||||||
|
|
||||||
return JSON_SUCCESS;
|
return JSON_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -313,7 +305,7 @@ jsonb_in_array_start(void *pstate)
|
|||||||
{
|
{
|
||||||
JsonbInState *_state = (JsonbInState *) pstate;
|
JsonbInState *_state = (JsonbInState *) pstate;
|
||||||
|
|
||||||
_state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_ARRAY, NULL);
|
pushJsonbValue(_state, WJB_BEGIN_ARRAY, NULL);
|
||||||
|
|
||||||
return JSON_SUCCESS;
|
return JSON_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -323,7 +315,7 @@ jsonb_in_array_end(void *pstate)
|
|||||||
{
|
{
|
||||||
JsonbInState *_state = (JsonbInState *) pstate;
|
JsonbInState *_state = (JsonbInState *) pstate;
|
||||||
|
|
||||||
_state->res = pushJsonbValue(&_state->parseState, WJB_END_ARRAY, NULL);
|
pushJsonbValue(_state, WJB_END_ARRAY, NULL);
|
||||||
|
|
||||||
return JSON_SUCCESS;
|
return JSON_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -341,7 +333,7 @@ jsonb_in_object_field_start(void *pstate, char *fname, bool isnull)
|
|||||||
return JSON_SEM_ACTION_FAILED;
|
return JSON_SEM_ACTION_FAILED;
|
||||||
v.val.string.val = fname;
|
v.val.string.val = fname;
|
||||||
|
|
||||||
_state->res = pushJsonbValue(&_state->parseState, WJB_KEY, &v);
|
pushJsonbValue(_state, WJB_KEY, &v);
|
||||||
|
|
||||||
return JSON_SUCCESS;
|
return JSON_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -435,9 +427,9 @@ jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
|
|||||||
va.val.array.rawScalar = true;
|
va.val.array.rawScalar = true;
|
||||||
va.val.array.nElems = 1;
|
va.val.array.nElems = 1;
|
||||||
|
|
||||||
_state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_ARRAY, &va);
|
pushJsonbValue(_state, WJB_BEGIN_ARRAY, &va);
|
||||||
_state->res = pushJsonbValue(&_state->parseState, WJB_ELEM, &v);
|
pushJsonbValue(_state, WJB_ELEM, &v);
|
||||||
_state->res = pushJsonbValue(&_state->parseState, WJB_END_ARRAY, NULL);
|
pushJsonbValue(_state, WJB_END_ARRAY, NULL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -446,10 +438,10 @@ jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
|
|||||||
switch (o->type)
|
switch (o->type)
|
||||||
{
|
{
|
||||||
case jbvArray:
|
case jbvArray:
|
||||||
_state->res = pushJsonbValue(&_state->parseState, WJB_ELEM, &v);
|
pushJsonbValue(_state, WJB_ELEM, &v);
|
||||||
break;
|
break;
|
||||||
case jbvObject:
|
case jbvObject:
|
||||||
_state->res = pushJsonbValue(&_state->parseState, WJB_VALUE, &v);
|
pushJsonbValue(_state, WJB_VALUE, &v);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unexpected parent of nested structure");
|
elog(ERROR, "unexpected parent of nested structure");
|
||||||
@@ -795,11 +787,9 @@ datum_to_jsonb_internal(Datum val, bool is_null, JsonbInState *result,
|
|||||||
{
|
{
|
||||||
if (type == WJB_END_ARRAY || type == WJB_END_OBJECT ||
|
if (type == WJB_END_ARRAY || type == WJB_END_OBJECT ||
|
||||||
type == WJB_BEGIN_ARRAY || type == WJB_BEGIN_OBJECT)
|
type == WJB_BEGIN_ARRAY || type == WJB_BEGIN_OBJECT)
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
pushJsonbValue(result, type, NULL);
|
||||||
type, NULL);
|
|
||||||
else
|
else
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
pushJsonbValue(result, type, &jb);
|
||||||
type, &jb);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -830,9 +820,9 @@ datum_to_jsonb_internal(Datum val, bool is_null, JsonbInState *result,
|
|||||||
va.val.array.rawScalar = true;
|
va.val.array.rawScalar = true;
|
||||||
va.val.array.nElems = 1;
|
va.val.array.nElems = 1;
|
||||||
|
|
||||||
result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, &va);
|
pushJsonbValue(result, WJB_BEGIN_ARRAY, &va);
|
||||||
result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb);
|
pushJsonbValue(result, WJB_ELEM, &jb);
|
||||||
result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL);
|
pushJsonbValue(result, WJB_END_ARRAY, NULL);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -841,10 +831,10 @@ datum_to_jsonb_internal(Datum val, bool is_null, JsonbInState *result,
|
|||||||
switch (o->type)
|
switch (o->type)
|
||||||
{
|
{
|
||||||
case jbvArray:
|
case jbvArray:
|
||||||
result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb);
|
pushJsonbValue(result, WJB_ELEM, &jb);
|
||||||
break;
|
break;
|
||||||
case jbvObject:
|
case jbvObject:
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
pushJsonbValue(result,
|
||||||
key_scalar ? WJB_KEY : WJB_VALUE,
|
key_scalar ? WJB_KEY : WJB_VALUE,
|
||||||
&jb);
|
&jb);
|
||||||
break;
|
break;
|
||||||
@@ -868,7 +858,7 @@ array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims, const Da
|
|||||||
|
|
||||||
Assert(dim < ndims);
|
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++)
|
for (i = 1; i <= dims[dim]; i++)
|
||||||
{
|
{
|
||||||
@@ -885,7 +875,7 @@ array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims, const Da
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL);
|
pushJsonbValue(result, WJB_END_ARRAY, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -914,8 +904,8 @@ array_to_jsonb_internal(Datum array, JsonbInState *result)
|
|||||||
|
|
||||||
if (nitems <= 0)
|
if (nitems <= 0)
|
||||||
{
|
{
|
||||||
result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, NULL);
|
pushJsonbValue(result, WJB_BEGIN_ARRAY, NULL);
|
||||||
result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL);
|
pushJsonbValue(result, WJB_END_ARRAY, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -962,7 +952,7 @@ composite_to_jsonb(Datum composite, JsonbInState *result)
|
|||||||
tmptup.t_data = td;
|
tmptup.t_data = td;
|
||||||
tuple = &tmptup;
|
tuple = &tmptup;
|
||||||
|
|
||||||
result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_OBJECT, NULL);
|
pushJsonbValue(result, WJB_BEGIN_OBJECT, NULL);
|
||||||
|
|
||||||
for (i = 0; i < tupdesc->natts; i++)
|
for (i = 0; i < tupdesc->natts; i++)
|
||||||
{
|
{
|
||||||
@@ -984,7 +974,7 @@ composite_to_jsonb(Datum composite, JsonbInState *result)
|
|||||||
v.val.string.len = strlen(attname);
|
v.val.string.len = strlen(attname);
|
||||||
v.val.string.val = 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);
|
val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
|
||||||
|
|
||||||
@@ -1001,7 +991,7 @@ composite_to_jsonb(Datum composite, JsonbInState *result)
|
|||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
result->res = pushJsonbValue(&result->parseState, WJB_END_OBJECT, NULL);
|
pushJsonbValue(result, WJB_END_OBJECT, NULL);
|
||||||
ReleaseTupleDesc(tupdesc);
|
ReleaseTupleDesc(tupdesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1119,7 +1109,7 @@ datum_to_jsonb(Datum val, JsonTypeCategory tcategory, Oid outfuncoid)
|
|||||||
datum_to_jsonb_internal(val, false, &result, tcategory, outfuncoid,
|
datum_to_jsonb_internal(val, false, &result, tcategory, outfuncoid,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
return JsonbPGetDatum(JsonbValueToJsonb(result.res));
|
return JsonbPGetDatum(JsonbValueToJsonb(result.result));
|
||||||
}
|
}
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
@@ -1139,7 +1129,7 @@ jsonb_build_object_worker(int nargs, const Datum *args, const bool *nulls, const
|
|||||||
|
|
||||||
memset(&result, 0, sizeof(JsonbInState));
|
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->unique_keys = unique_keys;
|
||||||
result.parseState->skip_nulls = absent_on_null;
|
result.parseState->skip_nulls = absent_on_null;
|
||||||
|
|
||||||
@@ -1166,9 +1156,9 @@ jsonb_build_object_worker(int nargs, const Datum *args, const bool *nulls, const
|
|||||||
add_jsonb(args[i + 1], nulls[i + 1], &result, types[i + 1], false);
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1201,10 +1191,10 @@ jsonb_build_object_noargs(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
memset(&result, 0, sizeof(JsonbInState));
|
memset(&result, 0, sizeof(JsonbInState));
|
||||||
|
|
||||||
(void) pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
|
pushJsonbValue(&result, WJB_BEGIN_OBJECT, NULL);
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
@@ -1216,7 +1206,7 @@ jsonb_build_array_worker(int nargs, const Datum *args, const bool *nulls, const
|
|||||||
|
|
||||||
memset(&result, 0, sizeof(JsonbInState));
|
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++)
|
for (i = 0; i < nargs; i++)
|
||||||
{
|
{
|
||||||
@@ -1226,9 +1216,9 @@ jsonb_build_array_worker(int nargs, const Datum *args, const bool *nulls, const
|
|||||||
add_jsonb(args[i], nulls[i], &result, types[i], false);
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1262,10 +1252,10 @@ jsonb_build_array_noargs(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
memset(&result, 0, sizeof(JsonbInState));
|
memset(&result, 0, sizeof(JsonbInState));
|
||||||
|
|
||||||
(void) pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
|
pushJsonbValue(&result, WJB_BEGIN_ARRAY, NULL);
|
||||||
result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
|
pushJsonbValue(&result, WJB_END_ARRAY, NULL);
|
||||||
|
|
||||||
PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
|
PG_RETURN_POINTER(JsonbValueToJsonb(result.result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1290,7 +1280,7 @@ jsonb_object(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
memset(&result, 0, sizeof(JsonbInState));
|
memset(&result, 0, sizeof(JsonbInState));
|
||||||
|
|
||||||
(void) pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
|
pushJsonbValue(&result, WJB_BEGIN_OBJECT, NULL);
|
||||||
|
|
||||||
switch (ndims)
|
switch (ndims)
|
||||||
{
|
{
|
||||||
@@ -1341,7 +1331,7 @@ jsonb_object(PG_FUNCTION_ARGS)
|
|||||||
v.val.string.len = len;
|
v.val.string.len = len;
|
||||||
v.val.string.val = str;
|
v.val.string.val = str;
|
||||||
|
|
||||||
(void) pushJsonbValue(&result.parseState, WJB_KEY, &v);
|
pushJsonbValue(&result, WJB_KEY, &v);
|
||||||
|
|
||||||
if (in_nulls[i * 2 + 1])
|
if (in_nulls[i * 2 + 1])
|
||||||
{
|
{
|
||||||
@@ -1358,16 +1348,16 @@ jsonb_object(PG_FUNCTION_ARGS)
|
|||||||
v.val.string.val = str;
|
v.val.string.val = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) pushJsonbValue(&result.parseState, WJB_VALUE, &v);
|
pushJsonbValue(&result, WJB_VALUE, &v);
|
||||||
}
|
}
|
||||||
|
|
||||||
pfree(in_datums);
|
pfree(in_datums);
|
||||||
pfree(in_nulls);
|
pfree(in_nulls);
|
||||||
|
|
||||||
close_object:
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1394,7 +1384,7 @@ jsonb_object_two_arg(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
memset(&result, 0, sizeof(JsonbInState));
|
memset(&result, 0, sizeof(JsonbInState));
|
||||||
|
|
||||||
(void) pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
|
pushJsonbValue(&result, WJB_BEGIN_OBJECT, NULL);
|
||||||
|
|
||||||
if (nkdims > 1 || nkdims != nvdims)
|
if (nkdims > 1 || nkdims != nvdims)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@@ -1431,7 +1421,7 @@ jsonb_object_two_arg(PG_FUNCTION_ARGS)
|
|||||||
v.val.string.len = len;
|
v.val.string.len = len;
|
||||||
v.val.string.val = str;
|
v.val.string.val = str;
|
||||||
|
|
||||||
(void) pushJsonbValue(&result.parseState, WJB_KEY, &v);
|
pushJsonbValue(&result, WJB_KEY, &v);
|
||||||
|
|
||||||
if (val_nulls[i])
|
if (val_nulls[i])
|
||||||
{
|
{
|
||||||
@@ -1448,7 +1438,7 @@ jsonb_object_two_arg(PG_FUNCTION_ARGS)
|
|||||||
v.val.string.val = str;
|
v.val.string.val = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) pushJsonbValue(&result.parseState, WJB_VALUE, &v);
|
pushJsonbValue(&result, WJB_VALUE, &v);
|
||||||
}
|
}
|
||||||
|
|
||||||
pfree(key_datums);
|
pfree(key_datums);
|
||||||
@@ -1457,9 +1447,9 @@ jsonb_object_two_arg(PG_FUNCTION_ARGS)
|
|||||||
pfree(val_nulls);
|
pfree(val_nulls);
|
||||||
|
|
||||||
close_object:
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1534,8 +1524,7 @@ jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
|
|||||||
state = palloc(sizeof(JsonbAggState));
|
state = palloc(sizeof(JsonbAggState));
|
||||||
result = palloc0(sizeof(JsonbInState));
|
result = palloc0(sizeof(JsonbInState));
|
||||||
state->res = result;
|
state->res = result;
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
pushJsonbValue(result, WJB_BEGIN_ARRAY, NULL);
|
||||||
WJB_BEGIN_ARRAY, NULL);
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
json_categorize_type(arg_type, true, &state->val_category,
|
json_categorize_type(arg_type, true, &state->val_category,
|
||||||
@@ -1559,7 +1548,7 @@ jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
|
|||||||
datum_to_jsonb_internal(val, PG_ARGISNULL(1), &elem, state->val_category,
|
datum_to_jsonb_internal(val, PG_ARGISNULL(1), &elem, state->val_category,
|
||||||
state->val_output_func, false);
|
state->val_output_func, false);
|
||||||
|
|
||||||
jbelem = JsonbValueToJsonb(elem.res);
|
jbelem = JsonbValueToJsonb(elem.result);
|
||||||
|
|
||||||
/* switch to the aggregate context for accumulation operations */
|
/* switch to the aggregate context for accumulation operations */
|
||||||
|
|
||||||
@@ -1575,18 +1564,15 @@ jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
|
|||||||
if (v.val.array.rawScalar)
|
if (v.val.array.rawScalar)
|
||||||
single_scalar = true;
|
single_scalar = true;
|
||||||
else
|
else
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
pushJsonbValue(result, type, NULL);
|
||||||
type, NULL);
|
|
||||||
break;
|
break;
|
||||||
case WJB_END_ARRAY:
|
case WJB_END_ARRAY:
|
||||||
if (!single_scalar)
|
if (!single_scalar)
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
pushJsonbValue(result, type, NULL);
|
||||||
type, NULL);
|
|
||||||
break;
|
break;
|
||||||
case WJB_BEGIN_OBJECT:
|
case WJB_BEGIN_OBJECT:
|
||||||
case WJB_END_OBJECT:
|
case WJB_END_OBJECT:
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
pushJsonbValue(result, type, NULL);
|
||||||
type, NULL);
|
|
||||||
break;
|
break;
|
||||||
case WJB_ELEM:
|
case WJB_ELEM:
|
||||||
case WJB_KEY:
|
case WJB_KEY:
|
||||||
@@ -1606,8 +1592,7 @@ jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
|
|||||||
DatumGetNumeric(DirectFunctionCall1(numeric_uplus,
|
DatumGetNumeric(DirectFunctionCall1(numeric_uplus,
|
||||||
NumericGetDatum(v.val.numeric)));
|
NumericGetDatum(v.val.numeric)));
|
||||||
}
|
}
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
pushJsonbValue(result, type, &v);
|
||||||
type, &v);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unknown jsonb iterator token type");
|
elog(ERROR, "unknown jsonb iterator token type");
|
||||||
@@ -1662,10 +1647,9 @@ jsonb_agg_finalfn(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
result.parseState = clone_parse_state(arg->res->parseState);
|
result.parseState = clone_parse_state(arg->res->parseState);
|
||||||
|
|
||||||
result.res = pushJsonbValue(&result.parseState,
|
pushJsonbValue(&result, WJB_END_ARRAY, NULL);
|
||||||
WJB_END_ARRAY, NULL);
|
|
||||||
|
|
||||||
out = JsonbValueToJsonb(result.res);
|
out = JsonbValueToJsonb(result.result);
|
||||||
|
|
||||||
PG_RETURN_POINTER(out);
|
PG_RETURN_POINTER(out);
|
||||||
}
|
}
|
||||||
@@ -1704,8 +1688,7 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
|||||||
state = palloc(sizeof(JsonbAggState));
|
state = palloc(sizeof(JsonbAggState));
|
||||||
result = palloc0(sizeof(JsonbInState));
|
result = palloc0(sizeof(JsonbInState));
|
||||||
state->res = result;
|
state->res = result;
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
pushJsonbValue(result, WJB_BEGIN_OBJECT, NULL);
|
||||||
WJB_BEGIN_OBJECT, NULL);
|
|
||||||
result->parseState->unique_keys = unique_keys;
|
result->parseState->unique_keys = unique_keys;
|
||||||
result->parseState->skip_nulls = absent_on_null;
|
result->parseState->skip_nulls = absent_on_null;
|
||||||
|
|
||||||
@@ -1760,7 +1743,7 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
|||||||
datum_to_jsonb_internal(val, false, &elem, state->key_category,
|
datum_to_jsonb_internal(val, false, &elem, state->key_category,
|
||||||
state->key_output_func, true);
|
state->key_output_func, true);
|
||||||
|
|
||||||
jbkey = JsonbValueToJsonb(elem.res);
|
jbkey = JsonbValueToJsonb(elem.result);
|
||||||
|
|
||||||
val = PG_ARGISNULL(2) ? (Datum) 0 : PG_GETARG_DATUM(2);
|
val = PG_ARGISNULL(2) ? (Datum) 0 : PG_GETARG_DATUM(2);
|
||||||
|
|
||||||
@@ -1769,7 +1752,7 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
|||||||
datum_to_jsonb_internal(val, PG_ARGISNULL(2), &elem, state->val_category,
|
datum_to_jsonb_internal(val, PG_ARGISNULL(2), &elem, state->val_category,
|
||||||
state->val_output_func, false);
|
state->val_output_func, false);
|
||||||
|
|
||||||
jbval = JsonbValueToJsonb(elem.res);
|
jbval = JsonbValueToJsonb(elem.result);
|
||||||
|
|
||||||
it = JsonbIteratorInit(&jbkey->root);
|
it = JsonbIteratorInit(&jbkey->root);
|
||||||
|
|
||||||
@@ -1806,14 +1789,12 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
|||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("object keys must be strings")));
|
errmsg("object keys must be strings")));
|
||||||
}
|
}
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
pushJsonbValue(result, WJB_KEY, &v);
|
||||||
WJB_KEY, &v);
|
|
||||||
|
|
||||||
if (skip)
|
if (skip)
|
||||||
{
|
{
|
||||||
v.type = jbvNull;
|
v.type = jbvNull;
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
pushJsonbValue(result, WJB_VALUE, &v);
|
||||||
WJB_VALUE, &v);
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
PG_RETURN_POINTER(state);
|
PG_RETURN_POINTER(state);
|
||||||
}
|
}
|
||||||
@@ -1845,18 +1826,15 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
|||||||
if (v.val.array.rawScalar)
|
if (v.val.array.rawScalar)
|
||||||
single_scalar = true;
|
single_scalar = true;
|
||||||
else
|
else
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
pushJsonbValue(result, type, NULL);
|
||||||
type, NULL);
|
|
||||||
break;
|
break;
|
||||||
case WJB_END_ARRAY:
|
case WJB_END_ARRAY:
|
||||||
if (!single_scalar)
|
if (!single_scalar)
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
pushJsonbValue(result, type, NULL);
|
||||||
type, NULL);
|
|
||||||
break;
|
break;
|
||||||
case WJB_BEGIN_OBJECT:
|
case WJB_BEGIN_OBJECT:
|
||||||
case WJB_END_OBJECT:
|
case WJB_END_OBJECT:
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
pushJsonbValue(result, type, NULL);
|
||||||
type, NULL);
|
|
||||||
break;
|
break;
|
||||||
case WJB_ELEM:
|
case WJB_ELEM:
|
||||||
case WJB_KEY:
|
case WJB_KEY:
|
||||||
@@ -1876,9 +1854,7 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
|||||||
DatumGetNumeric(DirectFunctionCall1(numeric_uplus,
|
DatumGetNumeric(DirectFunctionCall1(numeric_uplus,
|
||||||
NumericGetDatum(v.val.numeric)));
|
NumericGetDatum(v.val.numeric)));
|
||||||
}
|
}
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
pushJsonbValue(result, single_scalar ? WJB_VALUE : type, &v);
|
||||||
single_scalar ? WJB_VALUE : type,
|
|
||||||
&v);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unknown jsonb iterator token type");
|
elog(ERROR, "unknown jsonb iterator token type");
|
||||||
@@ -1953,10 +1929,9 @@ jsonb_object_agg_finalfn(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
result.parseState = clone_parse_state(arg->res->parseState);
|
result.parseState = clone_parse_state(arg->res->parseState);
|
||||||
|
|
||||||
result.res = pushJsonbValue(&result.parseState,
|
pushJsonbValue(&result, WJB_END_OBJECT, NULL);
|
||||||
WJB_END_OBJECT, NULL);
|
|
||||||
|
|
||||||
out = JsonbValueToJsonb(result.res);
|
out = JsonbValueToJsonb(result.result);
|
||||||
|
|
||||||
PG_RETURN_POINTER(out);
|
PG_RETURN_POINTER(out);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,13 @@
|
|||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "catalog/pg_collation.h"
|
#include "catalog/pg_collation.h"
|
||||||
|
#include "catalog/pg_type.h"
|
||||||
#include "common/hashfn.h"
|
#include "common/hashfn.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "port/pg_bitutils.h"
|
#include "port/pg_bitutils.h"
|
||||||
|
#include "utils/date.h"
|
||||||
#include "utils/datetime.h"
|
#include "utils/datetime.h"
|
||||||
|
#include "utils/datum.h"
|
||||||
#include "utils/fmgrprotos.h"
|
#include "utils/fmgrprotos.h"
|
||||||
#include "utils/json.h"
|
#include "utils/json.h"
|
||||||
#include "utils/jsonb.h"
|
#include "utils/jsonb.h"
|
||||||
@@ -54,17 +57,18 @@ static short padBufferToInt(StringInfo buffer);
|
|||||||
|
|
||||||
static JsonbIterator *iteratorFromContainer(JsonbContainer *container, JsonbIterator *parent);
|
static JsonbIterator *iteratorFromContainer(JsonbContainer *container, JsonbIterator *parent);
|
||||||
static JsonbIterator *freeAndGetParent(JsonbIterator *it);
|
static JsonbIterator *freeAndGetParent(JsonbIterator *it);
|
||||||
static JsonbParseState *pushState(JsonbParseState **pstate);
|
static JsonbParseState *pushState(JsonbInState *pstate);
|
||||||
static void appendKey(JsonbParseState *pstate, JsonbValue *string);
|
static void appendKey(JsonbInState *pstate, JsonbValue *string, bool needCopy);
|
||||||
static void appendValue(JsonbParseState *pstate, JsonbValue *scalarVal);
|
static void appendValue(JsonbInState *pstate, JsonbValue *scalarVal, bool needCopy);
|
||||||
static void appendElement(JsonbParseState *pstate, JsonbValue *scalarVal);
|
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 lengthCompareJsonbStringValue(const void *a, const void *b);
|
||||||
static int lengthCompareJsonbString(const char *val1, int len1,
|
static int lengthCompareJsonbString(const char *val1, int len1,
|
||||||
const char *val2, int len2);
|
const char *val2, int len2);
|
||||||
static int lengthCompareJsonbPair(const void *a, const void *b, void *binequal);
|
static int lengthCompareJsonbPair(const void *a, const void *b, void *binequal);
|
||||||
static void uniqueifyJsonbObject(JsonbValue *object, bool unique_keys,
|
static void uniqueifyJsonbObject(JsonbValue *object, bool unique_keys,
|
||||||
bool skip_nulls);
|
bool skip_nulls);
|
||||||
static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
|
static void pushJsonbValueScalar(JsonbInState *pstate,
|
||||||
JsonbIteratorToken seq,
|
JsonbIteratorToken seq,
|
||||||
JsonbValue *scalarVal);
|
JsonbValue *scalarVal);
|
||||||
|
|
||||||
@@ -95,9 +99,8 @@ JsonbValueToJsonb(JsonbValue *val)
|
|||||||
|
|
||||||
if (IsAJsonbScalar(val))
|
if (IsAJsonbScalar(val))
|
||||||
{
|
{
|
||||||
/* Scalar value */
|
/* Scalar value, so wrap it in an array */
|
||||||
JsonbParseState *pstate = NULL;
|
JsonbInState pstate = {0};
|
||||||
JsonbValue *res;
|
|
||||||
JsonbValue scalarArray;
|
JsonbValue scalarArray;
|
||||||
|
|
||||||
scalarArray.type = jbvArray;
|
scalarArray.type = jbvArray;
|
||||||
@@ -106,9 +109,9 @@ JsonbValueToJsonb(JsonbValue *val)
|
|||||||
|
|
||||||
pushJsonbValue(&pstate, WJB_BEGIN_ARRAY, &scalarArray);
|
pushJsonbValue(&pstate, WJB_BEGIN_ARRAY, &scalarArray);
|
||||||
pushJsonbValue(&pstate, WJB_ELEM, val);
|
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)
|
else if (val->type == jbvObject || val->type == jbvArray)
|
||||||
{
|
{
|
||||||
@@ -547,13 +550,23 @@ fillJsonbValue(JsonbContainer *container, int index,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Push JsonbValue into JsonbParseState.
|
* Push JsonbValue into JsonbInState.
|
||||||
*
|
*
|
||||||
* Used when parsing JSON tokens to form Jsonb, or when converting an in-memory
|
* Used, for example, when parsing JSON input.
|
||||||
* JsonbValue to a Jsonb.
|
|
||||||
*
|
*
|
||||||
* Initial state of *JsonbParseState is NULL, since it'll be allocated here
|
* *pstate is typically initialized to all-zeroes, except that the caller
|
||||||
* originally (caller will get JsonbParseState back by reference).
|
* 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
|
* Only sequential tokens pertaining to non-container types should pass a
|
||||||
* JsonbValue. There is one exception -- WJB_BEGIN_ARRAY callers may pass a
|
* JsonbValue. There is one exception -- WJB_BEGIN_ARRAY callers may pass a
|
||||||
@@ -562,18 +575,32 @@ fillJsonbValue(JsonbContainer *container, int index,
|
|||||||
*
|
*
|
||||||
* Values of type jbvBinary, which are rolled up arrays and objects,
|
* Values of type jbvBinary, which are rolled up arrays and objects,
|
||||||
* are unpacked before being added to the result.
|
* 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 *
|
void
|
||||||
pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
|
pushJsonbValue(JsonbInState *pstate, JsonbIteratorToken seq,
|
||||||
JsonbValue *jbval)
|
JsonbValue *jbval)
|
||||||
{
|
{
|
||||||
JsonbIterator *it;
|
JsonbIterator *it;
|
||||||
JsonbValue *res = NULL;
|
|
||||||
JsonbValue v;
|
JsonbValue v;
|
||||||
JsonbIteratorToken tok;
|
JsonbIteratorToken tok;
|
||||||
int i;
|
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);
|
pushJsonbValue(pstate, WJB_BEGIN_OBJECT, NULL);
|
||||||
for (i = 0; i < jbval->val.object.nPairs; i++)
|
for (i = 0; i < jbval->val.object.nPairs; i++)
|
||||||
@@ -581,32 +608,29 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
|
|||||||
pushJsonbValue(pstate, WJB_KEY, &jbval->val.object.pairs[i].key);
|
pushJsonbValue(pstate, WJB_KEY, &jbval->val.object.pairs[i].key);
|
||||||
pushJsonbValue(pstate, WJB_VALUE, &jbval->val.object.pairs[i].value);
|
pushJsonbValue(pstate, WJB_VALUE, &jbval->val.object.pairs[i].value);
|
||||||
}
|
}
|
||||||
|
pushJsonbValue(pstate, WJB_END_OBJECT, NULL);
|
||||||
return 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);
|
pushJsonbValue(pstate, WJB_BEGIN_ARRAY, NULL);
|
||||||
for (i = 0; i < jbval->val.array.nElems; i++)
|
for (i = 0; i < jbval->val.array.nElems; i++)
|
||||||
{
|
{
|
||||||
pushJsonbValue(pstate, WJB_ELEM, &jbval->val.array.elems[i]);
|
pushJsonbValue(pstate, WJB_ELEM, &jbval->val.array.elems[i]);
|
||||||
}
|
}
|
||||||
|
pushJsonbValue(pstate, WJB_END_ARRAY, NULL);
|
||||||
return pushJsonbValue(pstate, WJB_END_ARRAY, NULL);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!jbval || (seq != WJB_ELEM && seq != WJB_VALUE) ||
|
/* Else it must be a jbvBinary value; push its contents */
|
||||||
jbval->type != jbvBinary)
|
Assert(jbval->type == jbvBinary);
|
||||||
{
|
|
||||||
/* drop through */
|
|
||||||
return pushJsonbValueScalar(pstate, seq, jbval);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* unpack the binary and add each piece to the pstate */
|
|
||||||
it = JsonbIteratorInit(jbval->val.binary.data);
|
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);
|
tok = JsonbIteratorNext(&it, &v, true);
|
||||||
Assert(tok == WJB_BEGIN_ARRAY);
|
Assert(tok == WJB_BEGIN_ARRAY);
|
||||||
@@ -615,197 +639,290 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
|
|||||||
tok = JsonbIteratorNext(&it, &v, true);
|
tok = JsonbIteratorNext(&it, &v, true);
|
||||||
Assert(tok == WJB_ELEM);
|
Assert(tok == WJB_ELEM);
|
||||||
|
|
||||||
res = pushJsonbValueScalar(pstate, seq, &v);
|
pushJsonbValueScalar(pstate, seq, &v);
|
||||||
|
|
||||||
tok = JsonbIteratorNext(&it, &v, true);
|
tok = JsonbIteratorNext(&it, &v, true);
|
||||||
Assert(tok == WJB_END_ARRAY);
|
Assert(tok == WJB_END_ARRAY);
|
||||||
Assert(it == NULL);
|
Assert(it == NULL);
|
||||||
|
|
||||||
return res;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((tok = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
|
while ((tok = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
|
||||||
res = pushJsonbValueScalar(pstate, tok,
|
pushJsonbValueScalar(pstate, tok,
|
||||||
tok < WJB_BEGIN_ARRAY ||
|
tok < WJB_BEGIN_ARRAY ||
|
||||||
(tok == WJB_BEGIN_ARRAY &&
|
(tok == WJB_BEGIN_ARRAY &&
|
||||||
v.val.array.rawScalar) ? &v : NULL);
|
v.val.array.rawScalar) ? &v : NULL);
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do the actual pushing, with only scalar or pseudo-scalar-array values
|
* Do the actual pushing, with only scalar or pseudo-scalar-array values
|
||||||
* accepted.
|
* accepted.
|
||||||
*/
|
*/
|
||||||
static JsonbValue *
|
static void
|
||||||
pushJsonbValueScalar(JsonbParseState **pstate, JsonbIteratorToken seq,
|
pushJsonbValueScalar(JsonbInState *pstate, JsonbIteratorToken seq,
|
||||||
JsonbValue *scalarVal)
|
JsonbValue *scalarVal)
|
||||||
{
|
{
|
||||||
JsonbValue *result = NULL;
|
JsonbParseState *ppstate;
|
||||||
|
JsonbValue *val;
|
||||||
|
MemoryContext outcontext;
|
||||||
|
|
||||||
switch (seq)
|
switch (seq)
|
||||||
{
|
{
|
||||||
case WJB_BEGIN_ARRAY:
|
case WJB_BEGIN_ARRAY:
|
||||||
Assert(!scalarVal || scalarVal->val.array.rawScalar);
|
Assert(!scalarVal || scalarVal->val.array.rawScalar);
|
||||||
*pstate = pushState(pstate);
|
ppstate = pushState(pstate);
|
||||||
result = &(*pstate)->contVal;
|
val = &ppstate->contVal;
|
||||||
(*pstate)->contVal.type = jbvArray;
|
val->type = jbvArray;
|
||||||
(*pstate)->contVal.val.array.nElems = 0;
|
val->val.array.nElems = 0;
|
||||||
(*pstate)->contVal.val.array.rawScalar = (scalarVal &&
|
val->val.array.rawScalar = (scalarVal &&
|
||||||
scalarVal->val.array.rawScalar);
|
scalarVal->val.array.rawScalar);
|
||||||
if (scalarVal && scalarVal->val.array.nElems > 0)
|
if (scalarVal && scalarVal->val.array.nElems > 0)
|
||||||
{
|
{
|
||||||
/* Assume that this array is still really a scalar */
|
/* Assume that this array is still really a scalar */
|
||||||
Assert(scalarVal->type == jbvArray);
|
Assert(scalarVal->type == jbvArray);
|
||||||
(*pstate)->size = scalarVal->val.array.nElems;
|
ppstate->size = scalarVal->val.array.nElems;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(*pstate)->size = 4;
|
ppstate->size = 4; /* initial guess at array size */
|
||||||
}
|
}
|
||||||
(*pstate)->contVal.val.array.elems = palloc(sizeof(JsonbValue) *
|
outcontext = pstate->outcontext ? pstate->outcontext : CurrentMemoryContext;
|
||||||
(*pstate)->size);
|
val->val.array.elems = MemoryContextAlloc(outcontext,
|
||||||
|
sizeof(JsonbValue) *
|
||||||
|
ppstate->size);
|
||||||
break;
|
break;
|
||||||
case WJB_BEGIN_OBJECT:
|
case WJB_BEGIN_OBJECT:
|
||||||
Assert(!scalarVal);
|
Assert(!scalarVal);
|
||||||
*pstate = pushState(pstate);
|
ppstate = pushState(pstate);
|
||||||
result = &(*pstate)->contVal;
|
val = &ppstate->contVal;
|
||||||
(*pstate)->contVal.type = jbvObject;
|
val->type = jbvObject;
|
||||||
(*pstate)->contVal.val.object.nPairs = 0;
|
val->val.object.nPairs = 0;
|
||||||
(*pstate)->size = 4;
|
ppstate->size = 4; /* initial guess at object size */
|
||||||
(*pstate)->contVal.val.object.pairs = palloc(sizeof(JsonbPair) *
|
outcontext = pstate->outcontext ? pstate->outcontext : CurrentMemoryContext;
|
||||||
(*pstate)->size);
|
val->val.object.pairs = MemoryContextAlloc(outcontext,
|
||||||
|
sizeof(JsonbPair) *
|
||||||
|
ppstate->size);
|
||||||
break;
|
break;
|
||||||
case WJB_KEY:
|
case WJB_KEY:
|
||||||
Assert(scalarVal->type == jbvString);
|
Assert(scalarVal->type == jbvString);
|
||||||
appendKey(*pstate, scalarVal);
|
appendKey(pstate, scalarVal, true);
|
||||||
break;
|
break;
|
||||||
case WJB_VALUE:
|
case WJB_VALUE:
|
||||||
Assert(IsAJsonbScalar(scalarVal));
|
Assert(IsAJsonbScalar(scalarVal));
|
||||||
appendValue(*pstate, scalarVal);
|
appendValue(pstate, scalarVal, true);
|
||||||
break;
|
break;
|
||||||
case WJB_ELEM:
|
case WJB_ELEM:
|
||||||
Assert(IsAJsonbScalar(scalarVal));
|
Assert(IsAJsonbScalar(scalarVal));
|
||||||
appendElement(*pstate, scalarVal);
|
appendElement(pstate, scalarVal, true);
|
||||||
break;
|
break;
|
||||||
case WJB_END_OBJECT:
|
case WJB_END_OBJECT:
|
||||||
uniqueifyJsonbObject(&(*pstate)->contVal,
|
ppstate = pstate->parseState;
|
||||||
(*pstate)->unique_keys,
|
uniqueifyJsonbObject(&ppstate->contVal,
|
||||||
(*pstate)->skip_nulls);
|
ppstate->unique_keys,
|
||||||
|
ppstate->skip_nulls);
|
||||||
/* fall through! */
|
/* fall through! */
|
||||||
case WJB_END_ARRAY:
|
case WJB_END_ARRAY:
|
||||||
/* Steps here common to WJB_END_OBJECT case */
|
/* Steps here common to WJB_END_OBJECT case */
|
||||||
Assert(!scalarVal);
|
Assert(!scalarVal);
|
||||||
result = &(*pstate)->contVal;
|
ppstate = pstate->parseState;
|
||||||
|
val = &ppstate->contVal;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pop stack and push current array/object as value in parent
|
* 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;
|
pstate->parseState = ppstate = ppstate->next;
|
||||||
if (*pstate)
|
if (ppstate)
|
||||||
{
|
{
|
||||||
switch ((*pstate)->contVal.type)
|
switch (ppstate->contVal.type)
|
||||||
{
|
{
|
||||||
case jbvArray:
|
case jbvArray:
|
||||||
appendElement(*pstate, result);
|
appendElement(pstate, val, false);
|
||||||
break;
|
break;
|
||||||
case jbvObject:
|
case jbvObject:
|
||||||
appendValue(*pstate, result);
|
appendValue(pstate, val, false);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "invalid jsonb container type");
|
elog(ERROR, "invalid jsonb container type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
pstate->result = val;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized jsonb sequential processing token");
|
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 *
|
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->unique_keys = false;
|
||||||
ns->skip_nulls = false;
|
ns->skip_nulls = false;
|
||||||
|
|
||||||
|
pstate->parseState = ns;
|
||||||
return 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
|
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(object->type == jbvObject);
|
||||||
Assert(string->type == jbvString);
|
Assert(string->type == jbvString);
|
||||||
|
|
||||||
if (object->val.object.nPairs >= JSONB_MAX_PAIRS)
|
if (object->val.object.nPairs >= ppstate->size)
|
||||||
|
{
|
||||||
|
if (unlikely(object->val.object.nPairs >= JSONB_MAX_PAIRS))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
||||||
errmsg("number of jsonb object pairs exceeds the maximum allowed (%zu)",
|
errmsg("number of jsonb object pairs exceeds the maximum allowed (%zu)",
|
||||||
JSONB_MAX_PAIRS)));
|
JSONB_MAX_PAIRS)));
|
||||||
|
ppstate->size = Min(ppstate->size * 2, JSONB_MAX_PAIRS);
|
||||||
if (object->val.object.nPairs >= pstate->size)
|
|
||||||
{
|
|
||||||
pstate->size *= 2;
|
|
||||||
object->val.object.pairs = repalloc(object->val.object.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;
|
pair = &object->val.object.pairs[object->val.object.nPairs];
|
||||||
object->val.object.pairs[object->val.object.nPairs].order = 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
|
* pushJsonbValue() worker: Append a pair value to pstate
|
||||||
* Jsonb
|
|
||||||
*/
|
*/
|
||||||
static void
|
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);
|
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
|
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);
|
Assert(array->type == jbvArray);
|
||||||
|
|
||||||
if (array->val.array.nElems >= JSONB_MAX_ELEMS)
|
if (array->val.array.nElems >= ppstate->size)
|
||||||
|
{
|
||||||
|
if (unlikely(array->val.array.nElems >= JSONB_MAX_ELEMS))
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
||||||
errmsg("number of jsonb array elements exceeds the maximum allowed (%zu)",
|
errmsg("number of jsonb array elements exceeds the maximum allowed (%zu)",
|
||||||
JSONB_MAX_ELEMS)));
|
JSONB_MAX_ELEMS)));
|
||||||
|
ppstate->size = Min(ppstate->size * 2, JSONB_MAX_ELEMS);
|
||||||
if (array->val.array.nElems >= pstate->size)
|
|
||||||
{
|
|
||||||
pstate->size *= 2;
|
|
||||||
array->val.array.elems = repalloc(array->val.array.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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -475,18 +475,18 @@ static Datum populate_domain(DomainIOData *io, Oid typid, const char *colname,
|
|||||||
Node *escontext, bool omit_quotes);
|
Node *escontext, bool omit_quotes);
|
||||||
|
|
||||||
/* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
|
/* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
|
||||||
static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
|
static void IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
|
||||||
JsonbParseState **state);
|
JsonbInState *state);
|
||||||
static JsonbValue *setPath(JsonbIterator **it, const Datum *path_elems,
|
static void setPath(JsonbIterator **it, const Datum *path_elems,
|
||||||
const bool *path_nulls, int path_len,
|
const bool *path_nulls, int path_len,
|
||||||
JsonbParseState **st, int level, JsonbValue *newval,
|
JsonbInState *st, int level, JsonbValue *newval,
|
||||||
int op_type);
|
int op_type);
|
||||||
static void setPathObject(JsonbIterator **it, const Datum *path_elems,
|
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,
|
int level,
|
||||||
JsonbValue *newval, uint32 npairs, int op_type);
|
JsonbValue *newval, uint32 npairs, int op_type);
|
||||||
static void setPathArray(JsonbIterator **it, const Datum *path_elems,
|
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,
|
int level,
|
||||||
JsonbValue *newval, uint32 nelems, int op_type);
|
JsonbValue *newval, uint32 nelems, int op_type);
|
||||||
|
|
||||||
@@ -1679,8 +1679,7 @@ Datum
|
|||||||
jsonb_set_element(Jsonb *jb, const Datum *path, int path_len,
|
jsonb_set_element(Jsonb *jb, const Datum *path, int path_len,
|
||||||
JsonbValue *newval)
|
JsonbValue *newval)
|
||||||
{
|
{
|
||||||
JsonbValue *res;
|
JsonbInState state = {0};
|
||||||
JsonbParseState *state = NULL;
|
|
||||||
JsonbIterator *it;
|
JsonbIterator *it;
|
||||||
bool *path_nulls = palloc0(path_len * sizeof(bool));
|
bool *path_nulls = palloc0(path_len * sizeof(bool));
|
||||||
|
|
||||||
@@ -1689,17 +1688,17 @@ jsonb_set_element(Jsonb *jb, const Datum *path, int path_len,
|
|||||||
|
|
||||||
it = JsonbIteratorInit(&jb->root);
|
it = JsonbIteratorInit(&jb->root);
|
||||||
|
|
||||||
res = setPath(&it, path, path_nulls, path_len, &state, 0, newval,
|
setPath(&it, path, path_nulls, path_len, &state, 0, newval,
|
||||||
JB_PATH_CREATE | JB_PATH_FILL_GAPS |
|
JB_PATH_CREATE | JB_PATH_FILL_GAPS |
|
||||||
JB_PATH_CONSISTENT_POSITION);
|
JB_PATH_CONSISTENT_POSITION);
|
||||||
|
|
||||||
pfree(path_nulls);
|
pfree(path_nulls);
|
||||||
|
|
||||||
PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
|
PG_RETURN_JSONB_P(JsonbValueToJsonb(state.result));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
push_null_elements(JsonbParseState **ps, int num)
|
push_null_elements(JsonbInState *ps, int num)
|
||||||
{
|
{
|
||||||
JsonbValue null;
|
JsonbValue null;
|
||||||
|
|
||||||
@@ -1718,7 +1717,7 @@ push_null_elements(JsonbParseState **ps, int num)
|
|||||||
* Caller is responsible to make sure such path does not exist yet.
|
* Caller is responsible to make sure such path does not exist yet.
|
||||||
*/
|
*/
|
||||||
static void
|
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)
|
const bool *path_nulls, int path_len, JsonbValue *newval)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -1758,15 +1757,15 @@ push_path(JsonbParseState **st, int level, const Datum *path_elems,
|
|||||||
newkey.val.string.val = c;
|
newkey.val.string.val = c;
|
||||||
newkey.val.string.len = strlen(c);
|
newkey.val.string.len = strlen(c);
|
||||||
|
|
||||||
(void) pushJsonbValue(st, WJB_BEGIN_OBJECT, NULL);
|
pushJsonbValue(st, WJB_BEGIN_OBJECT, NULL);
|
||||||
(void) pushJsonbValue(st, WJB_KEY, &newkey);
|
pushJsonbValue(st, WJB_KEY, &newkey);
|
||||||
|
|
||||||
tpath[i - level] = jbvObject;
|
tpath[i - level] = jbvObject;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* integer, an array is expected */
|
/* integer, an array is expected */
|
||||||
(void) pushJsonbValue(st, WJB_BEGIN_ARRAY, NULL);
|
pushJsonbValue(st, WJB_BEGIN_ARRAY, NULL);
|
||||||
|
|
||||||
push_null_elements(st, lindex);
|
push_null_elements(st, lindex);
|
||||||
|
|
||||||
@@ -1776,11 +1775,9 @@ push_path(JsonbParseState **st, int level, const Datum *path_elems,
|
|||||||
|
|
||||||
/* Insert an actual value for either an object or array */
|
/* Insert an actual value for either an object or array */
|
||||||
if (tpath[(path_len - level) - 1] == jbvArray)
|
if (tpath[(path_len - level) - 1] == jbvArray)
|
||||||
{
|
pushJsonbValue(st, WJB_ELEM, newval);
|
||||||
(void) pushJsonbValue(st, WJB_ELEM, newval);
|
|
||||||
}
|
|
||||||
else
|
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
|
* Close everything up to the last but one level. The last one will be
|
||||||
@@ -1792,9 +1789,9 @@ push_path(JsonbParseState **st, int level, const Datum *path_elems,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
if (tpath[i - level] == jbvObject)
|
if (tpath[i - level] == jbvObject)
|
||||||
(void) pushJsonbValue(st, WJB_END_OBJECT, NULL);
|
pushJsonbValue(st, WJB_END_OBJECT, NULL);
|
||||||
else
|
else
|
||||||
(void) pushJsonbValue(st, WJB_END_ARRAY, NULL);
|
pushJsonbValue(st, WJB_END_ARRAY, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4544,8 +4541,7 @@ jsonb_strip_nulls(PG_FUNCTION_ARGS)
|
|||||||
Jsonb *jb = PG_GETARG_JSONB_P(0);
|
Jsonb *jb = PG_GETARG_JSONB_P(0);
|
||||||
bool strip_in_arrays = false;
|
bool strip_in_arrays = false;
|
||||||
JsonbIterator *it;
|
JsonbIterator *it;
|
||||||
JsonbParseState *parseState = NULL;
|
JsonbInState parseState = {0};
|
||||||
JsonbValue *res = NULL;
|
|
||||||
JsonbValue v,
|
JsonbValue v,
|
||||||
k;
|
k;
|
||||||
JsonbIteratorToken type;
|
JsonbIteratorToken type;
|
||||||
@@ -4581,7 +4577,7 @@ jsonb_strip_nulls(PG_FUNCTION_ARGS)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* otherwise, do a delayed push of the key */
|
/* 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 */
|
/* if strip_in_arrays is set, also skip null array elements */
|
||||||
@@ -4590,14 +4586,12 @@ jsonb_strip_nulls(PG_FUNCTION_ARGS)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (type == WJB_VALUE || type == WJB_ELEM)
|
if (type == WJB_VALUE || type == WJB_ELEM)
|
||||||
res = pushJsonbValue(&parseState, type, &v);
|
pushJsonbValue(&parseState, type, &v);
|
||||||
else
|
else
|
||||||
res = pushJsonbValue(&parseState, type, NULL);
|
pushJsonbValue(&parseState, type, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert(res != NULL);
|
PG_RETURN_POINTER(JsonbValueToJsonb(parseState.result));
|
||||||
|
|
||||||
PG_RETURN_POINTER(JsonbValueToJsonb(res));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -4627,8 +4621,7 @@ jsonb_concat(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Jsonb *jb1 = PG_GETARG_JSONB_P(0);
|
Jsonb *jb1 = PG_GETARG_JSONB_P(0);
|
||||||
Jsonb *jb2 = PG_GETARG_JSONB_P(1);
|
Jsonb *jb2 = PG_GETARG_JSONB_P(1);
|
||||||
JsonbParseState *state = NULL;
|
JsonbInState state = {0};
|
||||||
JsonbValue *res;
|
|
||||||
JsonbIterator *it1,
|
JsonbIterator *it1,
|
||||||
*it2;
|
*it2;
|
||||||
|
|
||||||
@@ -4649,11 +4642,9 @@ jsonb_concat(PG_FUNCTION_ARGS)
|
|||||||
it1 = JsonbIteratorInit(&jb1->root);
|
it1 = JsonbIteratorInit(&jb1->root);
|
||||||
it2 = JsonbIteratorInit(&jb2->root);
|
it2 = JsonbIteratorInit(&jb2->root);
|
||||||
|
|
||||||
res = IteratorConcat(&it1, &it2, &state);
|
IteratorConcat(&it1, &it2, &state);
|
||||||
|
|
||||||
Assert(res != NULL);
|
PG_RETURN_JSONB_P(JsonbValueToJsonb(state.result));
|
||||||
|
|
||||||
PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -4670,10 +4661,9 @@ jsonb_delete(PG_FUNCTION_ARGS)
|
|||||||
text *key = PG_GETARG_TEXT_PP(1);
|
text *key = PG_GETARG_TEXT_PP(1);
|
||||||
char *keyptr = VARDATA_ANY(key);
|
char *keyptr = VARDATA_ANY(key);
|
||||||
int keylen = VARSIZE_ANY_EXHDR(key);
|
int keylen = VARSIZE_ANY_EXHDR(key);
|
||||||
JsonbParseState *state = NULL;
|
JsonbInState pstate = {0};
|
||||||
JsonbIterator *it;
|
JsonbIterator *it;
|
||||||
JsonbValue v,
|
JsonbValue v;
|
||||||
*res = NULL;
|
|
||||||
bool skipNested = false;
|
bool skipNested = false;
|
||||||
JsonbIteratorToken r;
|
JsonbIteratorToken r;
|
||||||
|
|
||||||
@@ -4702,12 +4692,10 @@ jsonb_delete(PG_FUNCTION_ARGS)
|
|||||||
continue;
|
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(pstate.result));
|
||||||
|
|
||||||
PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -4724,10 +4712,9 @@ jsonb_delete_array(PG_FUNCTION_ARGS)
|
|||||||
Datum *keys_elems;
|
Datum *keys_elems;
|
||||||
bool *keys_nulls;
|
bool *keys_nulls;
|
||||||
int keys_len;
|
int keys_len;
|
||||||
JsonbParseState *state = NULL;
|
JsonbInState pstate = {0};
|
||||||
JsonbIterator *it;
|
JsonbIterator *it;
|
||||||
JsonbValue v,
|
JsonbValue v;
|
||||||
*res = NULL;
|
|
||||||
bool skipNested = false;
|
bool skipNested = false;
|
||||||
JsonbIteratorToken r;
|
JsonbIteratorToken r;
|
||||||
|
|
||||||
@@ -4788,12 +4775,10 @@ jsonb_delete_array(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(pstate.result));
|
||||||
|
|
||||||
PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -4808,12 +4793,11 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Jsonb *in = PG_GETARG_JSONB_P(0);
|
Jsonb *in = PG_GETARG_JSONB_P(0);
|
||||||
int idx = PG_GETARG_INT32(1);
|
int idx = PG_GETARG_INT32(1);
|
||||||
JsonbParseState *state = NULL;
|
JsonbInState pstate = {0};
|
||||||
JsonbIterator *it;
|
JsonbIterator *it;
|
||||||
uint32 i = 0,
|
uint32 i = 0,
|
||||||
n;
|
n;
|
||||||
JsonbValue v,
|
JsonbValue v;
|
||||||
*res = NULL;
|
|
||||||
JsonbIteratorToken r;
|
JsonbIteratorToken r;
|
||||||
|
|
||||||
if (JB_ROOT_IS_SCALAR(in))
|
if (JB_ROOT_IS_SCALAR(in))
|
||||||
@@ -4846,7 +4830,7 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
|
|||||||
if (idx >= n)
|
if (idx >= n)
|
||||||
PG_RETURN_JSONB_P(in);
|
PG_RETURN_JSONB_P(in);
|
||||||
|
|
||||||
pushJsonbValue(&state, r, NULL);
|
pushJsonbValue(&pstate, r, NULL);
|
||||||
|
|
||||||
while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
|
while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
|
||||||
{
|
{
|
||||||
@@ -4856,12 +4840,10 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
|
|||||||
continue;
|
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(pstate.result));
|
||||||
|
|
||||||
PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -4875,12 +4857,11 @@ jsonb_set(PG_FUNCTION_ARGS)
|
|||||||
Jsonb *newjsonb = PG_GETARG_JSONB_P(2);
|
Jsonb *newjsonb = PG_GETARG_JSONB_P(2);
|
||||||
JsonbValue newval;
|
JsonbValue newval;
|
||||||
bool create = PG_GETARG_BOOL(3);
|
bool create = PG_GETARG_BOOL(3);
|
||||||
JsonbValue *res = NULL;
|
|
||||||
Datum *path_elems;
|
Datum *path_elems;
|
||||||
bool *path_nulls;
|
bool *path_nulls;
|
||||||
int path_len;
|
int path_len;
|
||||||
JsonbIterator *it;
|
JsonbIterator *it;
|
||||||
JsonbParseState *st = NULL;
|
JsonbInState st = {0};
|
||||||
|
|
||||||
JsonbToJsonbValue(newjsonb, &newval);
|
JsonbToJsonbValue(newjsonb, &newval);
|
||||||
|
|
||||||
@@ -4904,12 +4885,10 @@ jsonb_set(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
it = JsonbIteratorInit(&in->root);
|
it = JsonbIteratorInit(&in->root);
|
||||||
|
|
||||||
res = setPath(&it, path_elems, path_nulls, path_len, &st,
|
setPath(&it, path_elems, path_nulls, path_len, &st,
|
||||||
0, &newval, create ? JB_PATH_CREATE : JB_PATH_REPLACE);
|
0, &newval, create ? JB_PATH_CREATE : JB_PATH_REPLACE);
|
||||||
|
|
||||||
Assert(res != NULL);
|
PG_RETURN_JSONB_P(JsonbValueToJsonb(st.result));
|
||||||
|
|
||||||
PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -4988,12 +4967,11 @@ jsonb_delete_path(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Jsonb *in = PG_GETARG_JSONB_P(0);
|
Jsonb *in = PG_GETARG_JSONB_P(0);
|
||||||
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
|
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
|
||||||
JsonbValue *res = NULL;
|
|
||||||
Datum *path_elems;
|
Datum *path_elems;
|
||||||
bool *path_nulls;
|
bool *path_nulls;
|
||||||
int path_len;
|
int path_len;
|
||||||
JsonbIterator *it;
|
JsonbIterator *it;
|
||||||
JsonbParseState *st = NULL;
|
JsonbInState st = {0};
|
||||||
|
|
||||||
if (ARR_NDIM(path) > 1)
|
if (ARR_NDIM(path) > 1)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@@ -5015,12 +4993,10 @@ jsonb_delete_path(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
it = JsonbIteratorInit(&in->root);
|
it = JsonbIteratorInit(&in->root);
|
||||||
|
|
||||||
res = setPath(&it, path_elems, path_nulls, path_len, &st,
|
setPath(&it, path_elems, path_nulls, path_len, &st,
|
||||||
0, NULL, JB_PATH_DELETE);
|
0, NULL, JB_PATH_DELETE);
|
||||||
|
|
||||||
Assert(res != NULL);
|
PG_RETURN_JSONB_P(JsonbValueToJsonb(st.result));
|
||||||
|
|
||||||
PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -5034,12 +5010,11 @@ jsonb_insert(PG_FUNCTION_ARGS)
|
|||||||
Jsonb *newjsonb = PG_GETARG_JSONB_P(2);
|
Jsonb *newjsonb = PG_GETARG_JSONB_P(2);
|
||||||
JsonbValue newval;
|
JsonbValue newval;
|
||||||
bool after = PG_GETARG_BOOL(3);
|
bool after = PG_GETARG_BOOL(3);
|
||||||
JsonbValue *res = NULL;
|
|
||||||
Datum *path_elems;
|
Datum *path_elems;
|
||||||
bool *path_nulls;
|
bool *path_nulls;
|
||||||
int path_len;
|
int path_len;
|
||||||
JsonbIterator *it;
|
JsonbIterator *it;
|
||||||
JsonbParseState *st = NULL;
|
JsonbInState st = {0};
|
||||||
|
|
||||||
JsonbToJsonbValue(newjsonb, &newval);
|
JsonbToJsonbValue(newjsonb, &newval);
|
||||||
|
|
||||||
@@ -5060,12 +5035,10 @@ jsonb_insert(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
it = JsonbIteratorInit(&in->root);
|
it = JsonbIteratorInit(&in->root);
|
||||||
|
|
||||||
res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, &newval,
|
setPath(&it, path_elems, path_nulls, path_len, &st, 0, &newval,
|
||||||
after ? JB_PATH_INSERT_AFTER : JB_PATH_INSERT_BEFORE);
|
after ? JB_PATH_INSERT_AFTER : JB_PATH_INSERT_BEFORE);
|
||||||
|
|
||||||
Assert(res != NULL);
|
PG_RETURN_JSONB_P(JsonbValueToJsonb(st.result));
|
||||||
|
|
||||||
PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -5075,13 +5048,12 @@ jsonb_insert(PG_FUNCTION_ARGS)
|
|||||||
* In that case we just append the content of it2 to it1 without any
|
* In that case we just append the content of it2 to it1 without any
|
||||||
* verifications.
|
* verifications.
|
||||||
*/
|
*/
|
||||||
static JsonbValue *
|
static void
|
||||||
IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
|
IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
|
||||||
JsonbParseState **state)
|
JsonbInState *state)
|
||||||
{
|
{
|
||||||
JsonbValue v1,
|
JsonbValue v1,
|
||||||
v2,
|
v2;
|
||||||
*res = NULL;
|
|
||||||
JsonbIteratorToken r1,
|
JsonbIteratorToken r1,
|
||||||
r2,
|
r2,
|
||||||
rk1,
|
rk1,
|
||||||
@@ -5112,7 +5084,7 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
|
|||||||
* automatically override the value from the first object.
|
* automatically override the value from the first object.
|
||||||
*/
|
*/
|
||||||
while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
|
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)
|
else if (rk1 == WJB_BEGIN_ARRAY && rk2 == WJB_BEGIN_ARRAY)
|
||||||
{
|
{
|
||||||
@@ -5133,7 +5105,7 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
|
|||||||
pushJsonbValue(state, WJB_ELEM, &v2);
|
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)
|
else if (rk1 == WJB_BEGIN_OBJECT)
|
||||||
{
|
{
|
||||||
@@ -5149,7 +5121,7 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
|
|||||||
pushJsonbValue(state, r1, r1 != WJB_END_OBJECT ? &v1 : NULL);
|
pushJsonbValue(state, r1, r1 != WJB_END_OBJECT ? &v1 : NULL);
|
||||||
|
|
||||||
while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
|
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
|
else
|
||||||
{
|
{
|
||||||
@@ -5168,10 +5140,8 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
|
|||||||
while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
|
while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
|
||||||
pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
|
pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
|
||||||
|
|
||||||
res = pushJsonbValue(state, WJB_END_ARRAY, NULL);
|
pushJsonbValue(state, WJB_END_ARRAY, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -5203,14 +5173,13 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
|
|||||||
* All path elements before the last must already exist
|
* All path elements before the last must already exist
|
||||||
* whatever bits in op_type are set, or nothing is done.
|
* whatever bits in op_type are set, or nothing is done.
|
||||||
*/
|
*/
|
||||||
static JsonbValue *
|
static void
|
||||||
setPath(JsonbIterator **it, const Datum *path_elems,
|
setPath(JsonbIterator **it, const Datum *path_elems,
|
||||||
const bool *path_nulls, int path_len,
|
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;
|
JsonbValue v;
|
||||||
JsonbIteratorToken r;
|
JsonbIteratorToken r;
|
||||||
JsonbValue *res;
|
|
||||||
|
|
||||||
check_stack_depth();
|
check_stack_depth();
|
||||||
|
|
||||||
@@ -5240,20 +5209,20 @@ setPath(JsonbIterator **it, const Datum *path_elems,
|
|||||||
errdetail("The path assumes key is a composite object, "
|
errdetail("The path assumes key is a composite object, "
|
||||||
"but it is a scalar value.")));
|
"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,
|
setPathArray(it, path_elems, path_nulls, path_len, st, level,
|
||||||
newval, v.val.array.nElems, op_type);
|
newval, v.val.array.nElems, op_type);
|
||||||
r = JsonbIteratorNext(it, &v, false);
|
r = JsonbIteratorNext(it, &v, false);
|
||||||
Assert(r == WJB_END_ARRAY);
|
Assert(r == WJB_END_ARRAY);
|
||||||
res = pushJsonbValue(st, r, NULL);
|
pushJsonbValue(st, r, NULL);
|
||||||
break;
|
break;
|
||||||
case WJB_BEGIN_OBJECT:
|
case WJB_BEGIN_OBJECT:
|
||||||
(void) pushJsonbValue(st, r, NULL);
|
pushJsonbValue(st, r, NULL);
|
||||||
setPathObject(it, path_elems, path_nulls, path_len, st, level,
|
setPathObject(it, path_elems, path_nulls, path_len, st, level,
|
||||||
newval, v.val.object.nPairs, op_type);
|
newval, v.val.object.nPairs, op_type);
|
||||||
r = JsonbIteratorNext(it, &v, true);
|
r = JsonbIteratorNext(it, &v, true);
|
||||||
Assert(r == WJB_END_OBJECT);
|
Assert(r == WJB_END_OBJECT);
|
||||||
res = pushJsonbValue(st, r, NULL);
|
pushJsonbValue(st, r, NULL);
|
||||||
break;
|
break;
|
||||||
case WJB_ELEM:
|
case WJB_ELEM:
|
||||||
case WJB_VALUE:
|
case WJB_VALUE:
|
||||||
@@ -5271,15 +5240,12 @@ setPath(JsonbIterator **it, const Datum *path_elems,
|
|||||||
errdetail("The path assumes key is a composite object, "
|
errdetail("The path assumes key is a composite object, "
|
||||||
"but it is a scalar value.")));
|
"but it is a scalar value.")));
|
||||||
|
|
||||||
res = pushJsonbValue(st, r, &v);
|
pushJsonbValue(st, r, &v);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized iterator result: %d", (int) r);
|
elog(ERROR, "unrecognized iterator result: %d", (int) r);
|
||||||
res = NULL; /* keep compiler quiet */
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -5287,7 +5253,7 @@ setPath(JsonbIterator **it, const Datum *path_elems,
|
|||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
setPathObject(JsonbIterator **it, const Datum *path_elems, const bool *path_nulls,
|
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)
|
JsonbValue *newval, uint32 npairs, int op_type)
|
||||||
{
|
{
|
||||||
text *pathelem = NULL;
|
text *pathelem = NULL;
|
||||||
@@ -5314,8 +5280,8 @@ setPathObject(JsonbIterator **it, const Datum *path_elems, const bool *path_null
|
|||||||
newkey.val.string.val = VARDATA_ANY(pathelem);
|
newkey.val.string.val = VARDATA_ANY(pathelem);
|
||||||
newkey.val.string.len = VARSIZE_ANY_EXHDR(pathelem);
|
newkey.val.string.len = VARSIZE_ANY_EXHDR(pathelem);
|
||||||
|
|
||||||
(void) pushJsonbValue(st, WJB_KEY, &newkey);
|
pushJsonbValue(st, WJB_KEY, &newkey);
|
||||||
(void) pushJsonbValue(st, WJB_VALUE, newval);
|
pushJsonbValue(st, WJB_VALUE, newval);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < npairs; i++)
|
for (i = 0; i < npairs; i++)
|
||||||
@@ -5347,13 +5313,13 @@ setPathObject(JsonbIterator **it, const Datum *path_elems, const bool *path_null
|
|||||||
r = JsonbIteratorNext(it, &v, true); /* skip value */
|
r = JsonbIteratorNext(it, &v, true); /* skip value */
|
||||||
if (!(op_type & JB_PATH_DELETE))
|
if (!(op_type & JB_PATH_DELETE))
|
||||||
{
|
{
|
||||||
(void) pushJsonbValue(st, WJB_KEY, &k);
|
pushJsonbValue(st, WJB_KEY, &k);
|
||||||
(void) pushJsonbValue(st, WJB_VALUE, newval);
|
pushJsonbValue(st, WJB_VALUE, newval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(void) pushJsonbValue(st, r, &k);
|
pushJsonbValue(st, r, &k);
|
||||||
setPath(it, path_elems, path_nulls, path_len,
|
setPath(it, path_elems, path_nulls, path_len,
|
||||||
st, level + 1, newval, op_type);
|
st, level + 1, newval, op_type);
|
||||||
}
|
}
|
||||||
@@ -5369,13 +5335,13 @@ setPathObject(JsonbIterator **it, const Datum *path_elems, const bool *path_null
|
|||||||
newkey.val.string.val = VARDATA_ANY(pathelem);
|
newkey.val.string.val = VARDATA_ANY(pathelem);
|
||||||
newkey.val.string.len = VARSIZE_ANY_EXHDR(pathelem);
|
newkey.val.string.len = VARSIZE_ANY_EXHDR(pathelem);
|
||||||
|
|
||||||
(void) pushJsonbValue(st, WJB_KEY, &newkey);
|
pushJsonbValue(st, WJB_KEY, &newkey);
|
||||||
(void) pushJsonbValue(st, WJB_VALUE, newval);
|
pushJsonbValue(st, WJB_VALUE, newval);
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) pushJsonbValue(st, r, &k);
|
pushJsonbValue(st, r, &k);
|
||||||
r = JsonbIteratorNext(it, &v, false);
|
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_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
|
||||||
{
|
{
|
||||||
int walking_level = 1;
|
int walking_level = 1;
|
||||||
@@ -5389,7 +5355,7 @@ setPathObject(JsonbIterator **it, const Datum *path_elems, const bool *path_null
|
|||||||
if (r == WJB_END_ARRAY || r == WJB_END_OBJECT)
|
if (r == WJB_END_ARRAY || r == WJB_END_OBJECT)
|
||||||
--walking_level;
|
--walking_level;
|
||||||
|
|
||||||
(void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
|
pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5413,7 +5379,7 @@ setPathObject(JsonbIterator **it, const Datum *path_elems, const bool *path_null
|
|||||||
newkey.val.string.val = VARDATA_ANY(pathelem);
|
newkey.val.string.val = VARDATA_ANY(pathelem);
|
||||||
newkey.val.string.len = VARSIZE_ANY_EXHDR(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);
|
push_path(st, level, path_elems, path_nulls, path_len, newval);
|
||||||
|
|
||||||
/* Result is closed with WJB_END_OBJECT outside of this function */
|
/* Result is closed with WJB_END_OBJECT outside of this function */
|
||||||
@@ -5425,7 +5391,7 @@ setPathObject(JsonbIterator **it, const Datum *path_elems, const bool *path_null
|
|||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
setPathArray(JsonbIterator **it, const Datum *path_elems, const bool *path_nulls,
|
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 *newval, uint32 nelems, int op_type)
|
||||||
{
|
{
|
||||||
JsonbValue v;
|
JsonbValue v;
|
||||||
@@ -5493,7 +5459,7 @@ setPathArray(JsonbIterator **it, const Datum *path_elems, const bool *path_nulls
|
|||||||
if (op_type & JB_PATH_FILL_GAPS && nelems == 0 && idx > 0)
|
if (op_type & JB_PATH_FILL_GAPS && nelems == 0 && idx > 0)
|
||||||
push_null_elements(st, idx);
|
push_null_elements(st, idx);
|
||||||
|
|
||||||
(void) pushJsonbValue(st, WJB_ELEM, newval);
|
pushJsonbValue(st, WJB_ELEM, newval);
|
||||||
|
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
@@ -5512,7 +5478,7 @@ setPathArray(JsonbIterator **it, const Datum *path_elems, const bool *path_nulls
|
|||||||
r = JsonbIteratorNext(it, &v, true); /* skip */
|
r = JsonbIteratorNext(it, &v, true); /* skip */
|
||||||
|
|
||||||
if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE))
|
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
|
* We should keep current value only in case of
|
||||||
@@ -5520,20 +5486,20 @@ setPathArray(JsonbIterator **it, const Datum *path_elems, const bool *path_nulls
|
|||||||
* otherwise it should be deleted or replaced
|
* otherwise it should be deleted or replaced
|
||||||
*/
|
*/
|
||||||
if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_INSERT_BEFORE))
|
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))
|
if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_REPLACE))
|
||||||
(void) pushJsonbValue(st, WJB_ELEM, newval);
|
pushJsonbValue(st, WJB_ELEM, newval);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
(void) setPath(it, path_elems, path_nulls, path_len,
|
setPath(it, path_elems, path_nulls, path_len,
|
||||||
st, level + 1, newval, op_type);
|
st, level + 1, newval, op_type);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
r = JsonbIteratorNext(it, &v, false);
|
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_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
|
||||||
{
|
{
|
||||||
@@ -5548,7 +5514,7 @@ setPathArray(JsonbIterator **it, const Datum *path_elems, const bool *path_nulls
|
|||||||
if (r == WJB_END_ARRAY || r == WJB_END_OBJECT)
|
if (r == WJB_END_ARRAY || r == WJB_END_OBJECT)
|
||||||
--walking_level;
|
--walking_level;
|
||||||
|
|
||||||
(void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
|
pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5563,7 +5529,7 @@ setPathArray(JsonbIterator **it, const Datum *path_elems, const bool *path_nulls
|
|||||||
if (op_type & JB_PATH_FILL_GAPS && idx > nelems)
|
if (op_type & JB_PATH_FILL_GAPS && idx > nelems)
|
||||||
push_null_elements(st, idx - nelems);
|
push_null_elements(st, idx - nelems);
|
||||||
|
|
||||||
(void) pushJsonbValue(st, WJB_ELEM, newval);
|
pushJsonbValue(st, WJB_ELEM, newval);
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5808,10 +5774,9 @@ transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
|
|||||||
JsonTransformStringValuesAction transform_action)
|
JsonTransformStringValuesAction transform_action)
|
||||||
{
|
{
|
||||||
JsonbIterator *it;
|
JsonbIterator *it;
|
||||||
JsonbValue v,
|
JsonbValue v;
|
||||||
*res = NULL;
|
|
||||||
JsonbIteratorToken type;
|
JsonbIteratorToken type;
|
||||||
JsonbParseState *st = NULL;
|
JsonbInState st = {0};
|
||||||
text *out;
|
text *out;
|
||||||
bool is_scalar = false;
|
bool is_scalar = false;
|
||||||
|
|
||||||
@@ -5827,20 +5792,20 @@ transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
|
|||||||
out = pg_detoast_datum_packed(out);
|
out = pg_detoast_datum_packed(out);
|
||||||
v.val.string.val = VARDATA_ANY(out);
|
v.val.string.val = VARDATA_ANY(out);
|
||||||
v.val.string.len = VARSIZE_ANY_EXHDR(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
|
else
|
||||||
{
|
{
|
||||||
res = pushJsonbValue(&st, type, (type == WJB_KEY ||
|
pushJsonbValue(&st, type, (type == WJB_KEY ||
|
||||||
type == WJB_VALUE ||
|
type == WJB_VALUE ||
|
||||||
type == WJB_ELEM) ? &v : NULL);
|
type == WJB_ELEM) ? &v : NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res->type == jbvArray)
|
if (st.result->type == jbvArray)
|
||||||
res->val.array.rawScalar = is_scalar;
|
st.result->val.array.rawScalar = is_scalar;
|
||||||
|
|
||||||
return JsonbValueToJsonb(res);
|
return JsonbValueToJsonb(st.result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -2874,8 +2874,7 @@ executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
|||||||
{
|
{
|
||||||
JsonBaseObjectInfo baseObject;
|
JsonBaseObjectInfo baseObject;
|
||||||
JsonbValue obj;
|
JsonbValue obj;
|
||||||
JsonbParseState *ps;
|
JsonbInState ps;
|
||||||
JsonbValue *keyval;
|
|
||||||
Jsonb *jsonb;
|
Jsonb *jsonb;
|
||||||
|
|
||||||
if (tok != WJB_KEY)
|
if (tok != WJB_KEY)
|
||||||
@@ -2889,7 +2888,8 @@ executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
|||||||
tok = JsonbIteratorNext(&it, &val, true);
|
tok = JsonbIteratorNext(&it, &val, true);
|
||||||
Assert(tok == WJB_VALUE);
|
Assert(tok == WJB_VALUE);
|
||||||
|
|
||||||
ps = NULL;
|
memset(&ps, 0, sizeof(ps));
|
||||||
|
|
||||||
pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
|
pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
|
||||||
|
|
||||||
pushJsonbValue(&ps, WJB_KEY, &keystr);
|
pushJsonbValue(&ps, WJB_KEY, &keystr);
|
||||||
@@ -2901,9 +2901,9 @@ executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
|||||||
pushJsonbValue(&ps, WJB_KEY, &idstr);
|
pushJsonbValue(&ps, WJB_KEY, &idstr);
|
||||||
pushJsonbValue(&ps, WJB_VALUE, &idval);
|
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);
|
JsonbInitBinary(&obj, jsonb);
|
||||||
|
|
||||||
@@ -3649,7 +3649,7 @@ getScalar(JsonbValue *scalar, enum jbvType type)
|
|||||||
static JsonbValue *
|
static JsonbValue *
|
||||||
wrapItemsInArray(const JsonValueList *items)
|
wrapItemsInArray(const JsonValueList *items)
|
||||||
{
|
{
|
||||||
JsonbParseState *ps = NULL;
|
JsonbInState ps = {0};
|
||||||
JsonValueListIterator it;
|
JsonValueListIterator it;
|
||||||
JsonbValue *jbv;
|
JsonbValue *jbv;
|
||||||
|
|
||||||
@@ -3659,7 +3659,9 @@ wrapItemsInArray(const JsonValueList *items)
|
|||||||
while ((jbv = JsonValueListNext(items, &it)))
|
while ((jbv = JsonValueListNext(items, &it)))
|
||||||
pushJsonbValue(&ps, WJB_ELEM, jbv);
|
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 */
|
/* Check if the timezone required for casting from type1 to type2 is used */
|
||||||
|
|||||||
@@ -67,8 +67,10 @@ typedef enum
|
|||||||
#define JGINFLAG_HASHED 0x10 /* OR'd into flag if value was hashed */
|
#define JGINFLAG_HASHED 0x10 /* OR'd into flag if value was hashed */
|
||||||
#define JGIN_MAXLENGTH 125 /* max length of text part before hashing */
|
#define JGIN_MAXLENGTH 125 /* max length of text part before hashing */
|
||||||
|
|
||||||
|
/* Forward struct references */
|
||||||
typedef struct JsonbPair JsonbPair;
|
typedef struct JsonbPair JsonbPair;
|
||||||
typedef struct JsonbValue JsonbValue;
|
typedef struct JsonbValue JsonbValue;
|
||||||
|
typedef struct JsonbParseState JsonbParseState;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Jsonbs are varlena objects, so must meet the varlena convention that the
|
* Jsonbs are varlena objects, so must meet the varlena convention that the
|
||||||
@@ -315,15 +317,40 @@ struct JsonbPair
|
|||||||
uint32 order; /* Pair's index in original sequence */
|
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;
|
JsonbValue *result; /* The completed value; NULL until complete */
|
||||||
Size size;
|
MemoryContext outcontext; /* The context to build it in, or NULL */
|
||||||
struct JsonbParseState *next;
|
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 unique_keys; /* Check object key uniqueness */
|
||||||
bool skip_nulls; /* Skip null object fields */
|
bool skip_nulls; /* Skip null object fields */
|
||||||
} JsonbParseState;
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* JsonbIterator holds details of the type for each iteration. It also stores a
|
* JsonbIterator holds details of the type for each iteration. It also stores a
|
||||||
@@ -404,7 +431,7 @@ extern JsonbValue *getKeyJsonValueFromContainer(JsonbContainer *container,
|
|||||||
JsonbValue *res);
|
JsonbValue *res);
|
||||||
extern JsonbValue *getIthJsonbValueFromContainer(JsonbContainer *container,
|
extern JsonbValue *getIthJsonbValueFromContainer(JsonbContainer *container,
|
||||||
uint32 i);
|
uint32 i);
|
||||||
extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,
|
extern void pushJsonbValue(JsonbInState *pstate,
|
||||||
JsonbIteratorToken seq, JsonbValue *jbval);
|
JsonbIteratorToken seq, JsonbValue *jbval);
|
||||||
extern JsonbIterator *JsonbIteratorInit(JsonbContainer *container);
|
extern JsonbIterator *JsonbIteratorInit(JsonbContainer *container);
|
||||||
extern JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val,
|
extern JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val,
|
||||||
|
|||||||
Reference in New Issue
Block a user