aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/jsonfuncs.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2020-12-21 13:11:29 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2020-12-21 13:11:50 -0500
commitff5d5611c01f60525c30b2c3ebc16d05edb7956d (patch)
treefdba3512f73c4805109f295ad0208846b7fb94ed /src/backend/utils/adt/jsonfuncs.c
parent86b7cca72d4d0a4e043fac0a2cdd56218ff2f258 (diff)
downloadpostgresql-ff5d5611c01f60525c30b2c3ebc16d05edb7956d.tar.gz
postgresql-ff5d5611c01f60525c30b2c3ebc16d05edb7956d.zip
Remove "invalid concatenation of jsonb objects" error case.
The jsonb || jsonb operator arbitrarily rejected certain combinations of scalar and non-scalar inputs, while being willing to concatenate other combinations. This was of course quite undocumented. Rather than trying to document it, let's just remove the restriction, creating a uniform rule that unless we are handling an object-to-object concatenation, non-array inputs are converted to one-element arrays, resulting in an array-to-array concatenation. (This does not change the behavior for any case that didn't throw an error before.) Per complaint from Joel Jacobson. Back-patch to all supported branches. Discussion: https://postgr.es/m/163099.1608312033@sss.pgh.pa.us
Diffstat (limited to 'src/backend/utils/adt/jsonfuncs.c')
-rw-r--r--src/backend/utils/adt/jsonfuncs.c71
1 files changed, 34 insertions, 37 deletions
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 7a254150780..69100feab7c 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -4690,11 +4690,14 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
rk2 = JsonbIteratorNext(it2, &v2, false);
/*
- * Both elements are objects.
+ * JsonbIteratorNext reports raw scalars as if they were single-element
+ * arrays; hence we only need consider "object" and "array" cases here.
*/
if (rk1 == WJB_BEGIN_OBJECT && rk2 == WJB_BEGIN_OBJECT)
{
/*
+ * Both inputs are objects.
+ *
* Append all the tokens from v1 to res, except last WJB_END_OBJECT
* (because res will not be finished yet).
*/
@@ -4703,18 +4706,18 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
pushJsonbValue(state, r1, &v1);
/*
- * Append all the tokens from v2 to res, include last WJB_END_OBJECT
- * (the concatenation will be completed).
+ * Append all the tokens from v2 to res, including last WJB_END_OBJECT
+ * (the concatenation will be completed). Any duplicate keys will
+ * automatically override the value from the first object.
*/
while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
res = pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
}
-
- /*
- * Both elements are arrays (either can be scalar).
- */
else if (rk1 == WJB_BEGIN_ARRAY && rk2 == WJB_BEGIN_ARRAY)
{
+ /*
+ * Both inputs are arrays.
+ */
pushJsonbValue(state, rk1, NULL);
while ((r1 = JsonbIteratorNext(it1, &v1, true)) != WJB_END_ARRAY)
@@ -4731,46 +4734,40 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
res = pushJsonbValue(state, WJB_END_ARRAY, NULL /* signal to sort */ );
}
- /* have we got array || object or object || array? */
- else if (((rk1 == WJB_BEGIN_ARRAY && !(*it1)->isScalar) && rk2 == WJB_BEGIN_OBJECT) ||
- (rk1 == WJB_BEGIN_OBJECT && (rk2 == WJB_BEGIN_ARRAY && !(*it2)->isScalar)))
+ else if (rk1 == WJB_BEGIN_OBJECT)
{
- JsonbIterator **it_array = rk1 == WJB_BEGIN_ARRAY ? it1 : it2;
- JsonbIterator **it_object = rk1 == WJB_BEGIN_OBJECT ? it1 : it2;
- bool prepend = (rk1 == WJB_BEGIN_OBJECT);
+ /*
+ * We have object || array.
+ */
+ Assert(rk2 == WJB_BEGIN_ARRAY);
pushJsonbValue(state, WJB_BEGIN_ARRAY, NULL);
- if (prepend)
- {
- pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
- while ((r1 = JsonbIteratorNext(it_object, &v1, true)) != WJB_DONE)
- pushJsonbValue(state, r1, r1 != WJB_END_OBJECT ? &v1 : NULL);
-
- while ((r2 = JsonbIteratorNext(it_array, &v2, true)) != WJB_DONE)
- res = pushJsonbValue(state, r2, r2 != WJB_END_ARRAY ? &v2 : NULL);
- }
- else
- {
- while ((r1 = JsonbIteratorNext(it_array, &v1, true)) != WJB_END_ARRAY)
- pushJsonbValue(state, r1, &v1);
-
- pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
- while ((r2 = JsonbIteratorNext(it_object, &v2, true)) != WJB_DONE)
- pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
+ pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
+ while ((r1 = JsonbIteratorNext(it1, &v1, true)) != WJB_DONE)
+ pushJsonbValue(state, r1, r1 != WJB_END_OBJECT ? &v1 : NULL);
- res = pushJsonbValue(state, WJB_END_ARRAY, NULL);
- }
+ while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
+ res = pushJsonbValue(state, r2, r2 != WJB_END_ARRAY ? &v2 : NULL);
}
else
{
/*
- * This must be scalar || object or object || scalar, as that's all
- * that's left. Both of these make no sense, so error out.
+ * We have array || object.
*/
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid concatenation of jsonb objects")));
+ Assert(rk1 == WJB_BEGIN_ARRAY);
+ Assert(rk2 == WJB_BEGIN_OBJECT);
+
+ pushJsonbValue(state, WJB_BEGIN_ARRAY, NULL);
+
+ while ((r1 = JsonbIteratorNext(it1, &v1, true)) != WJB_END_ARRAY)
+ pushJsonbValue(state, r1, &v1);
+
+ pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
+ while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
+ pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
+
+ res = pushJsonbValue(state, WJB_END_ARRAY, NULL);
}
return res;