mirror of
https://github.com/postgres/postgres.git
synced 2025-12-12 02:37:31 +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);
|
||||
char *base = STRPTR(in);
|
||||
HEntry *entries = ARRPTR(in);
|
||||
JsonbParseState *state = NULL;
|
||||
JsonbValue *res;
|
||||
JsonbInState state = {0};
|
||||
|
||||
(void) pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
|
||||
pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
@@ -1453,7 +1452,7 @@ hstore_to_jsonb(PG_FUNCTION_ARGS)
|
||||
key.val.string.len = HSTORE_KEYLEN(entries, i);
|
||||
key.val.string.val = HSTORE_KEY(entries, base, i);
|
||||
|
||||
(void) pushJsonbValue(&state, WJB_KEY, &key);
|
||||
pushJsonbValue(&state, WJB_KEY, &key);
|
||||
|
||||
if (HSTORE_VALISNULL(entries, i))
|
||||
{
|
||||
@@ -1465,12 +1464,12 @@ hstore_to_jsonb(PG_FUNCTION_ARGS)
|
||||
val.val.string.len = HSTORE_VALLEN(entries, i);
|
||||
val.val.string.val = HSTORE_VAL(entries, base, i);
|
||||
}
|
||||
(void) pushJsonbValue(&state, WJB_VALUE, &val);
|
||||
pushJsonbValue(&state, WJB_VALUE, &val);
|
||||
}
|
||||
|
||||
res = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
|
||||
pushJsonbValue(&state, WJB_END_OBJECT, NULL);
|
||||
|
||||
PG_RETURN_POINTER(JsonbValueToJsonb(res));
|
||||
PG_RETURN_POINTER(JsonbValueToJsonb(state.result));
|
||||
}
|
||||
|
||||
PG_FUNCTION_INFO_V1(hstore_to_jsonb_loose);
|
||||
@@ -1482,13 +1481,12 @@ hstore_to_jsonb_loose(PG_FUNCTION_ARGS)
|
||||
int count = HS_COUNT(in);
|
||||
char *base = STRPTR(in);
|
||||
HEntry *entries = ARRPTR(in);
|
||||
JsonbParseState *state = NULL;
|
||||
JsonbValue *res;
|
||||
JsonbInState state = {0};
|
||||
StringInfoData tmp;
|
||||
|
||||
initStringInfo(&tmp);
|
||||
|
||||
(void) pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
|
||||
pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
@@ -1499,7 +1497,7 @@ hstore_to_jsonb_loose(PG_FUNCTION_ARGS)
|
||||
key.val.string.len = HSTORE_KEYLEN(entries, i);
|
||||
key.val.string.val = HSTORE_KEY(entries, base, i);
|
||||
|
||||
(void) pushJsonbValue(&state, WJB_KEY, &key);
|
||||
pushJsonbValue(&state, WJB_KEY, &key);
|
||||
|
||||
if (HSTORE_VALISNULL(entries, i))
|
||||
{
|
||||
@@ -1541,10 +1539,10 @@ hstore_to_jsonb_loose(PG_FUNCTION_ARGS)
|
||||
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 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 *
|
||||
@@ -127,8 +127,8 @@ Jsonb_to_SV(JsonbContainer *jsonb)
|
||||
}
|
||||
}
|
||||
|
||||
static JsonbValue *
|
||||
AV_to_JsonbValue(AV *in, JsonbParseState **jsonb_state)
|
||||
static void
|
||||
AV_to_JsonbValue(AV *in, JsonbInState *jsonb_state)
|
||||
{
|
||||
dTHX;
|
||||
SSize_t pcount = av_len(in) + 1;
|
||||
@@ -141,14 +141,14 @@ AV_to_JsonbValue(AV *in, JsonbParseState **jsonb_state)
|
||||
SV **value = av_fetch(in, i, FALSE);
|
||||
|
||||
if (value)
|
||||
(void) SV_to_JsonbValue(*value, jsonb_state, true);
|
||||
SV_to_JsonbValue(*value, jsonb_state, true);
|
||||
}
|
||||
|
||||
return pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
|
||||
pushJsonbValue(jsonb_state, WJB_END_ARRAY, NULL);
|
||||
}
|
||||
|
||||
static JsonbValue *
|
||||
HV_to_JsonbValue(HV *obj, JsonbParseState **jsonb_state)
|
||||
static void
|
||||
HV_to_JsonbValue(HV *obj, JsonbInState *jsonb_state)
|
||||
{
|
||||
dTHX;
|
||||
JsonbValue key;
|
||||
@@ -167,14 +167,14 @@ HV_to_JsonbValue(HV *obj, JsonbParseState **jsonb_state)
|
||||
key.val.string.val = pnstrdup(kstr, klen);
|
||||
key.val.string.len = klen;
|
||||
pushJsonbValue(jsonb_state, WJB_KEY, &key);
|
||||
(void) SV_to_JsonbValue(val, jsonb_state, false);
|
||||
SV_to_JsonbValue(val, jsonb_state, false);
|
||||
}
|
||||
|
||||
return pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
|
||||
pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
|
||||
}
|
||||
|
||||
static JsonbValue *
|
||||
SV_to_JsonbValue(SV *in, JsonbParseState **jsonb_state, bool is_elem)
|
||||
static void
|
||||
SV_to_JsonbValue(SV *in, JsonbInState *jsonb_state, bool is_elem)
|
||||
{
|
||||
dTHX;
|
||||
JsonbValue out; /* result */
|
||||
@@ -186,10 +186,12 @@ SV_to_JsonbValue(SV *in, JsonbParseState **jsonb_state, bool is_elem)
|
||||
switch (SvTYPE(in))
|
||||
{
|
||||
case SVt_PVAV:
|
||||
return AV_to_JsonbValue((AV *) in, jsonb_state);
|
||||
AV_to_JsonbValue((AV *) in, jsonb_state);
|
||||
return;
|
||||
|
||||
case SVt_PVHV:
|
||||
return HV_to_JsonbValue((HV *) in, jsonb_state);
|
||||
HV_to_JsonbValue((HV *) in, jsonb_state);
|
||||
return;
|
||||
|
||||
default:
|
||||
if (!SvOK(in))
|
||||
@@ -259,14 +261,24 @@ SV_to_JsonbValue(SV *in, JsonbParseState **jsonb_state, bool is_elem)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot transform this Perl type to jsonb")));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Push result into 'jsonb_state' unless it is a raw scalar. */
|
||||
return *jsonb_state
|
||||
? pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, &out)
|
||||
: memcpy(palloc_object(JsonbValue), &out, sizeof(JsonbValue));
|
||||
if (jsonb_state->parseState)
|
||||
{
|
||||
/* We're in an array or object, so push value as element or field. */
|
||||
pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, &out);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* We are at top level, so it's a raw scalar. If we just shove the
|
||||
* scalar value into jsonb_state->result, JsonbValueToJsonb will take
|
||||
* care of wrapping it into a dummy array.
|
||||
*/
|
||||
jsonb_state->result = palloc_object(JsonbValue);
|
||||
memcpy(jsonb_state->result, &out, sizeof(JsonbValue));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -289,10 +301,9 @@ Datum
|
||||
plperl_to_jsonb(PG_FUNCTION_ARGS)
|
||||
{
|
||||
dTHX;
|
||||
JsonbParseState *jsonb_state = NULL;
|
||||
SV *in = (SV *) PG_GETARG_POINTER(0);
|
||||
JsonbValue *out = SV_to_JsonbValue(in, &jsonb_state, true);
|
||||
Jsonb *result = JsonbValueToJsonb(out);
|
||||
JsonbInState jsonb_state = {0};
|
||||
|
||||
PG_RETURN_JSONB_P(result);
|
||||
SV_to_JsonbValue(in, &jsonb_state, true);
|
||||
PG_RETURN_JSONB_P(JsonbValueToJsonb(jsonb_state.result));
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ static PLy_elog_impl_t PLy_elog_impl_p;
|
||||
static PyObject *decimal_constructor;
|
||||
|
||||
static PyObject *PLyObject_FromJsonbContainer(JsonbContainer *jsonb);
|
||||
static JsonbValue *PLyObject_ToJsonbValue(PyObject *obj,
|
||||
JsonbParseState **jsonb_state, bool is_elem);
|
||||
static void PLyObject_ToJsonbValue(PyObject *obj,
|
||||
JsonbInState *jsonb_state, bool is_elem);
|
||||
|
||||
typedef PyObject *(*PLyUnicode_FromStringAndSize_t)
|
||||
(const char *s, Py_ssize_t size);
|
||||
@@ -261,12 +261,11 @@ PLyObject_FromJsonbContainer(JsonbContainer *jsonb)
|
||||
*
|
||||
* Transform Python dict to JsonbValue.
|
||||
*/
|
||||
static JsonbValue *
|
||||
PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
|
||||
static void
|
||||
PLyMapping_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state)
|
||||
{
|
||||
Py_ssize_t pcount;
|
||||
PyObject *volatile items;
|
||||
JsonbValue *volatile out;
|
||||
|
||||
pcount = PyMapping_Size(obj);
|
||||
items = PyMapping_Items(obj);
|
||||
@@ -297,19 +296,17 @@ PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
|
||||
PLyUnicode_ToJsonbValue(key, &jbvKey);
|
||||
}
|
||||
|
||||
(void) pushJsonbValue(jsonb_state, WJB_KEY, &jbvKey);
|
||||
(void) PLyObject_ToJsonbValue(value, jsonb_state, false);
|
||||
pushJsonbValue(jsonb_state, WJB_KEY, &jbvKey);
|
||||
PLyObject_ToJsonbValue(value, jsonb_state, false);
|
||||
}
|
||||
|
||||
out = pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
|
||||
pushJsonbValue(jsonb_state, WJB_END_OBJECT, NULL);
|
||||
}
|
||||
PG_FINALLY();
|
||||
{
|
||||
Py_DECREF(items);
|
||||
}
|
||||
PG_END_TRY();
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -318,8 +315,8 @@ PLyMapping_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
|
||||
* Transform python list to JsonbValue. Expects transformed PyObject and
|
||||
* a state required for jsonb construction.
|
||||
*/
|
||||
static JsonbValue *
|
||||
PLySequence_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
|
||||
static void
|
||||
PLySequence_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state)
|
||||
{
|
||||
Py_ssize_t i;
|
||||
Py_ssize_t pcount;
|
||||
@@ -336,7 +333,7 @@ PLySequence_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
|
||||
value = PySequence_GetItem(obj, i);
|
||||
Assert(value);
|
||||
|
||||
(void) PLyObject_ToJsonbValue(value, jsonb_state, true);
|
||||
PLyObject_ToJsonbValue(value, jsonb_state, true);
|
||||
Py_XDECREF(value);
|
||||
value = NULL;
|
||||
}
|
||||
@@ -348,7 +345,7 @@ PLySequence_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state)
|
||||
}
|
||||
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.
|
||||
*/
|
||||
static JsonbValue *
|
||||
PLyObject_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state, bool is_elem)
|
||||
static void
|
||||
PLyObject_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state, bool is_elem)
|
||||
{
|
||||
JsonbValue *out;
|
||||
|
||||
if (!PyUnicode_Check(obj))
|
||||
{
|
||||
if (PySequence_Check(obj))
|
||||
return PLySequence_ToJsonbValue(obj, jsonb_state);
|
||||
{
|
||||
PLySequence_ToJsonbValue(obj, jsonb_state);
|
||||
return;
|
||||
}
|
||||
else if (PyMapping_Check(obj))
|
||||
return PLyMapping_ToJsonbValue(obj, jsonb_state);
|
||||
{
|
||||
PLyMapping_ToJsonbValue(obj, jsonb_state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
out = palloc_object(JsonbValue);
|
||||
@@ -443,10 +446,20 @@ PLyObject_ToJsonbValue(PyObject *obj, JsonbParseState **jsonb_state, bool is_ele
|
||||
errmsg("Python type \"%s\" cannot be transformed to jsonb",
|
||||
PLyObject_AsString((PyObject *) obj->ob_type))));
|
||||
|
||||
/* Push result into 'jsonb_state' unless it is raw scalar value. */
|
||||
return (*jsonb_state ?
|
||||
pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, out) :
|
||||
out);
|
||||
if (jsonb_state->parseState)
|
||||
{
|
||||
/* We're in an array or object, so push value as element or field. */
|
||||
pushJsonbValue(jsonb_state, is_elem ? WJB_ELEM : WJB_VALUE, out);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* We are at top level, so it's a raw scalar. If we just shove the
|
||||
* scalar value into jsonb_state->result, JsonbValueToJsonb will take
|
||||
* care of wrapping it into a dummy array.
|
||||
*/
|
||||
jsonb_state->result = out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -458,13 +471,11 @@ PG_FUNCTION_INFO_V1(plpython_to_jsonb);
|
||||
Datum
|
||||
plpython_to_jsonb(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PyObject *obj;
|
||||
JsonbValue *out;
|
||||
JsonbParseState *jsonb_state = NULL;
|
||||
PyObject *obj = (PyObject *) PG_GETARG_POINTER(0);
|
||||
JsonbInState jsonb_state = {0};
|
||||
|
||||
obj = (PyObject *) PG_GETARG_POINTER(0);
|
||||
out = PLyObject_ToJsonbValue(obj, &jsonb_state, true);
|
||||
PG_RETURN_POINTER(JsonbValueToJsonb(out));
|
||||
PLyObject_ToJsonbValue(obj, &jsonb_state, true);
|
||||
PG_RETURN_POINTER(JsonbValueToJsonb(jsonb_state.result));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user