Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 38bef9e

Browse files
committed
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
1 parent def510c commit 38bef9e

File tree

4 files changed

+85
-50
lines changed

4 files changed

+85
-50
lines changed

doc/src/sgml/func.sgml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11925,10 +11925,13 @@ table2-mapping
1192511925

1192611926
<note>
1192711927
<para>
11928-
The <literal>||</literal> operator concatenates the elements at the top level of
11929-
each of its operands. It does not operate recursively. For example, if
11930-
both operands are objects with a common key field name, the value of the
11931-
field in the result will just be the value from the right hand operand.
11928+
The <literal>||</literal> operator concatenates two JSON objects by
11929+
generating an object containing the union of their keys, taking the
11930+
second object's value when there are duplicate keys. All other cases
11931+
produce a JSON array: first, any non-array input is converted into a
11932+
single-element array, and then the two arrays are concatenated.
11933+
It does not operate recursively; only the top-level array or object
11934+
structure is merged.
1193211935
</para>
1193311936
</note>
1193411937

src/backend/utils/adt/jsonfuncs.c

Lines changed: 39 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4583,36 +4583,39 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
45834583
rk1,
45844584
rk2;
45854585

4586-
r1 = rk1 = JsonbIteratorNext(it1, &v1, false);
4587-
r2 = rk2 = JsonbIteratorNext(it2, &v2, false);
4586+
rk1 = JsonbIteratorNext(it1, &v1, false);
4587+
rk2 = JsonbIteratorNext(it2, &v2, false);
45884588

45894589
/*
4590-
* Both elements are objects.
4590+
* JsonbIteratorNext reports raw scalars as if they were single-element
4591+
* arrays; hence we only need consider "object" and "array" cases here.
45914592
*/
45924593
if (rk1 == WJB_BEGIN_OBJECT && rk2 == WJB_BEGIN_OBJECT)
45934594
{
45944595
/*
4595-
* Append the all tokens from v1 to res, except last WJB_END_OBJECT
4596+
* Both inputs are objects.
4597+
*
4598+
* Append all the tokens from v1 to res, except last WJB_END_OBJECT
45964599
* (because res will not be finished yet).
45974600
*/
4598-
pushJsonbValue(state, r1, NULL);
4601+
pushJsonbValue(state, rk1, NULL);
45994602
while ((r1 = JsonbIteratorNext(it1, &v1, true)) != WJB_END_OBJECT)
46004603
pushJsonbValue(state, r1, &v1);
46014604

46024605
/*
4603-
* Append the all tokens from v2 to res, include last WJB_END_OBJECT
4604-
* (the concatenation will be completed).
4606+
* Append all the tokens from v2 to res, including last WJB_END_OBJECT
4607+
* (the concatenation will be completed). Any duplicate keys will
4608+
* automatically override the value from the first object.
46054609
*/
46064610
while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
46074611
res = pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
46084612
}
4609-
4610-
/*
4611-
* Both elements are arrays (either can be scalar).
4612-
*/
46134613
else if (rk1 == WJB_BEGIN_ARRAY && rk2 == WJB_BEGIN_ARRAY)
46144614
{
4615-
pushJsonbValue(state, r1, NULL);
4615+
/*
4616+
* Both inputs are arrays.
4617+
*/
4618+
pushJsonbValue(state, rk1, NULL);
46164619

46174620
while ((r1 = JsonbIteratorNext(it1, &v1, true)) != WJB_END_ARRAY)
46184621
{
@@ -4628,48 +4631,40 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
46284631

46294632
res = pushJsonbValue(state, WJB_END_ARRAY, NULL /* signal to sort */ );
46304633
}
4631-
/* have we got array || object or object || array? */
4632-
else if (((rk1 == WJB_BEGIN_ARRAY && !(*it1)->isScalar) && rk2 == WJB_BEGIN_OBJECT) ||
4633-
(rk1 == WJB_BEGIN_OBJECT && (rk2 == WJB_BEGIN_ARRAY && !(*it2)->isScalar)))
4634+
else if (rk1 == WJB_BEGIN_OBJECT)
46344635
{
4635-
4636-
JsonbIterator **it_array = rk1 == WJB_BEGIN_ARRAY ? it1 : it2;
4637-
JsonbIterator **it_object = rk1 == WJB_BEGIN_OBJECT ? it1 : it2;
4638-
4639-
bool prepend = (rk1 == WJB_BEGIN_OBJECT);
4636+
/*
4637+
* We have object || array.
4638+
*/
4639+
Assert(rk2 == WJB_BEGIN_ARRAY);
46404640

46414641
pushJsonbValue(state, WJB_BEGIN_ARRAY, NULL);
46424642

4643-
if (prepend)
4644-
{
4645-
pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
4646-
while ((r1 = JsonbIteratorNext(it_object, &v1, true)) != WJB_DONE)
4647-
pushJsonbValue(state, r1, r1 != WJB_END_OBJECT ? &v1 : NULL);
4648-
4649-
while ((r2 = JsonbIteratorNext(it_array, &v2, true)) != WJB_DONE)
4650-
res = pushJsonbValue(state, r2, r2 != WJB_END_ARRAY ? &v2 : NULL);
4651-
}
4652-
else
4653-
{
4654-
while ((r1 = JsonbIteratorNext(it_array, &v1, true)) != WJB_END_ARRAY)
4655-
pushJsonbValue(state, r1, &v1);
4643+
pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
4644+
while ((r1 = JsonbIteratorNext(it1, &v1, true)) != WJB_DONE)
4645+
pushJsonbValue(state, r1, r1 != WJB_END_OBJECT ? &v1 : NULL);
46564646

4657-
pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
4658-
while ((r2 = JsonbIteratorNext(it_object, &v2, true)) != WJB_DONE)
4659-
pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
4660-
4661-
res = pushJsonbValue(state, WJB_END_ARRAY, NULL);
4662-
}
4647+
while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
4648+
res = pushJsonbValue(state, r2, r2 != WJB_END_ARRAY ? &v2 : NULL);
46634649
}
46644650
else
46654651
{
46664652
/*
4667-
* This must be scalar || object or object || scalar, as that's all
4668-
* that's left. Both of these make no sense, so error out.
4653+
* We have array || object.
46694654
*/
4670-
ereport(ERROR,
4671-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4672-
errmsg("invalid concatenation of jsonb objects")));
4655+
Assert(rk1 == WJB_BEGIN_ARRAY);
4656+
Assert(rk2 == WJB_BEGIN_OBJECT);
4657+
4658+
pushJsonbValue(state, WJB_BEGIN_ARRAY, NULL);
4659+
4660+
while ((r1 = JsonbIteratorNext(it1, &v1, true)) != WJB_END_ARRAY)
4661+
pushJsonbValue(state, r1, &v1);
4662+
4663+
pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
4664+
while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
4665+
pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
4666+
4667+
res = pushJsonbValue(state, WJB_END_ARRAY, NULL);
46734668
}
46744669

46754670
return res;

src/test/regress/expected/jsonb.out

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4107,9 +4107,41 @@ select '{"a":"b"}'::jsonb || '[]'::jsonb;
41074107
(1 row)
41084108

41094109
select '"a"'::jsonb || '{"a":1}';
4110-
ERROR: invalid concatenation of jsonb objects
4110+
?column?
4111+
-----------------
4112+
["a", {"a": 1}]
4113+
(1 row)
4114+
41114115
select '{"a":1}' || '"a"'::jsonb;
4112-
ERROR: invalid concatenation of jsonb objects
4116+
?column?
4117+
-----------------
4118+
[{"a": 1}, "a"]
4119+
(1 row)
4120+
4121+
select '[3]'::jsonb || '{}'::jsonb;
4122+
?column?
4123+
----------
4124+
[3, {}]
4125+
(1 row)
4126+
4127+
select '3'::jsonb || '[]'::jsonb;
4128+
?column?
4129+
----------
4130+
[3]
4131+
(1 row)
4132+
4133+
select '3'::jsonb || '4'::jsonb;
4134+
?column?
4135+
----------
4136+
[3, 4]
4137+
(1 row)
4138+
4139+
select '3'::jsonb || '{}'::jsonb;
4140+
?column?
4141+
----------
4142+
[3, {}]
4143+
(1 row)
4144+
41134145
select '["a", "b"]'::jsonb || '{"c":1}';
41144146
?column?
41154147
----------------------

src/test/regress/sql/jsonb.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,11 @@ select '{"a":"b"}'::jsonb || '[]'::jsonb;
10521052
select '"a"'::jsonb || '{"a":1}';
10531053
select '{"a":1}' || '"a"'::jsonb;
10541054

1055+
select '[3]'::jsonb || '{}'::jsonb;
1056+
select '3'::jsonb || '[]'::jsonb;
1057+
select '3'::jsonb || '4'::jsonb;
1058+
select '3'::jsonb || '{}'::jsonb;
1059+
10551060
select '["a", "b"]'::jsonb || '{"c":1}';
10561061
select '{"c": 1}'::jsonb || '["a", "b"]';
10571062

0 commit comments

Comments
 (0)