1
0
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:
Tom Lane
2025-12-07 11:46:49 -05:00
parent 3628af4210
commit 0986e95161
8 changed files with 528 additions and 422 deletions

View File

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