Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit38d30a1

Browse files
committed
Remove "invalid concatenation of jsonb objects" error case.
The jsonb || jsonb operator arbitrarily rejected certain combinationsof scalar and non-scalar inputs, while being willing to concatenateother combinations. This was of course quite undocumented. Ratherthan trying to document it, let's just remove the restriction,creating a uniform rule that unless we are handling an object-to-objectconcatenation, non-array inputs are converted to one-element arrays,resulting in an array-to-array concatenation. (This does not changethe 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 parentbe9c3cd commit38d30a1

File tree

4 files changed

+99
-47
lines changed

4 files changed

+99
-47
lines changed

‎doc/src/sgml/func.sgml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14736,8 +14736,12 @@ table2-mapping
1473614736
</para>
1473714737
<para>
1473814738
Concatenates two <type>jsonb</type> values.
14739-
Concatenating two objects generates an object with the union of their
14739+
Concatenating two arrays generates an array containing all the
14740+
elements of each input. Concatenating two objects generates an
14741+
object containing the union of their
1474014742
keys, taking the second object's value when there are duplicate keys.
14743+
All other cases are treated by converting a non-array input into a
14744+
single-element array, and then proceeding as for two arrays.
1474114745
Does not operate recursively: only the top-level array or object
1474214746
structure is merged.
1474314747
</para>
@@ -14748,6 +14752,22 @@ table2-mapping
1474814752
<para>
1474914753
<literal>'{"a": "b"}'::jsonb || '{"c": "d"}'::jsonb</literal>
1475014754
<returnvalue>{"a": "b", "c": "d"}</returnvalue>
14755+
</para>
14756+
<para>
14757+
<literal>'[1, 2]'::jsonb || '3'::jsonb</literal>
14758+
<returnvalue>[1, 2, 3]</returnvalue>
14759+
</para>
14760+
<para>
14761+
<literal>'{"a": "b"}'::jsonb || '42'::jsonb</literal>
14762+
<returnvalue>[{"a": "b"}, 42]</returnvalue>
14763+
</para>
14764+
<para>
14765+
To append an array to another array as a single entry, wrap it
14766+
in an additional layer of array, for example:
14767+
</para>
14768+
<para>
14769+
<literal>'[1, 2]'::jsonb || jsonb_build_array('[3, 4]'::jsonb)</literal>
14770+
<returnvalue>[1, 2, [3, 4]]</returnvalue>
1475114771
</para></entry>
1475214772
</row>
1475314773

‎src/backend/utils/adt/jsonfuncs.c

Lines changed: 39 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4687,36 +4687,39 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
46874687
rk1,
46884688
rk2;
46894689

4690-
r1=rk1=JsonbIteratorNext(it1,&v1, false);
4691-
r2=rk2=JsonbIteratorNext(it2,&v2, false);
4690+
rk1=JsonbIteratorNext(it1,&v1, false);
4691+
rk2=JsonbIteratorNext(it2,&v2, false);
46924692

46934693
/*
4694-
* Both elements are objects.
4694+
* JsonbIteratorNext reports raw scalars as if they were single-element
4695+
* arrays; hence we only need consider "object" and "array" cases here.
46954696
*/
46964697
if (rk1==WJB_BEGIN_OBJECT&&rk2==WJB_BEGIN_OBJECT)
46974698
{
46984699
/*
4699-
* Append the all tokens from v1 to res, except last WJB_END_OBJECT
4700+
* Both inputs are objects.
4701+
*
4702+
* Append all the tokens from v1 to res, except last WJB_END_OBJECT
47004703
* (because res will not be finished yet).
47014704
*/
4702-
pushJsonbValue(state,r1,NULL);
4705+
pushJsonbValue(state,rk1,NULL);
47034706
while ((r1=JsonbIteratorNext(it1,&v1, true))!=WJB_END_OBJECT)
47044707
pushJsonbValue(state,r1,&v1);
47054708

47064709
/*
4707-
* Append the all tokens from v2 to res, include last WJB_END_OBJECT
4708-
* (the concatenation will be completed).
4710+
* Append all the tokens from v2 to res, including last WJB_END_OBJECT
4711+
* (the concatenation will be completed). Any duplicate keys will
4712+
* automatically override the value from the first object.
47094713
*/
47104714
while ((r2=JsonbIteratorNext(it2,&v2, true))!=WJB_DONE)
47114715
res=pushJsonbValue(state,r2,r2!=WJB_END_OBJECT ?&v2 :NULL);
47124716
}
4713-
4714-
/*
4715-
* Both elements are arrays (either can be scalar).
4716-
*/
47174717
elseif (rk1==WJB_BEGIN_ARRAY&&rk2==WJB_BEGIN_ARRAY)
47184718
{
4719-
pushJsonbValue(state,r1,NULL);
4719+
/*
4720+
* Both inputs are arrays.
4721+
*/
4722+
pushJsonbValue(state,rk1,NULL);
47204723

47214724
while ((r1=JsonbIteratorNext(it1,&v1, true))!=WJB_END_ARRAY)
47224725
{
@@ -4732,48 +4735,40 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
47324735

47334736
res=pushJsonbValue(state,WJB_END_ARRAY,NULL/* signal to sort */ );
47344737
}
4735-
/* have we got array || object or object || array? */
4736-
elseif (((rk1==WJB_BEGIN_ARRAY&& !(*it1)->isScalar)&&rk2==WJB_BEGIN_OBJECT)||
4737-
(rk1==WJB_BEGIN_OBJECT&& (rk2==WJB_BEGIN_ARRAY&& !(*it2)->isScalar)))
4738+
elseif (rk1==WJB_BEGIN_OBJECT)
47384739
{
4739-
4740-
JsonbIterator**it_array=rk1==WJB_BEGIN_ARRAY ?it1 :it2;
4741-
JsonbIterator**it_object=rk1==WJB_BEGIN_OBJECT ?it1 :it2;
4742-
4743-
boolprepend= (rk1==WJB_BEGIN_OBJECT);
4740+
/*
4741+
* We have object || array.
4742+
*/
4743+
Assert(rk2==WJB_BEGIN_ARRAY);
47444744

47454745
pushJsonbValue(state,WJB_BEGIN_ARRAY,NULL);
47464746

4747-
if (prepend)
4748-
{
4749-
pushJsonbValue(state,WJB_BEGIN_OBJECT,NULL);
4750-
while ((r1=JsonbIteratorNext(it_object,&v1, true))!=WJB_DONE)
4751-
pushJsonbValue(state,r1,r1!=WJB_END_OBJECT ?&v1 :NULL);
4752-
4753-
while ((r2=JsonbIteratorNext(it_array,&v2, true))!=WJB_DONE)
4754-
res=pushJsonbValue(state,r2,r2!=WJB_END_ARRAY ?&v2 :NULL);
4755-
}
4756-
else
4757-
{
4758-
while ((r1=JsonbIteratorNext(it_array,&v1, true))!=WJB_END_ARRAY)
4759-
pushJsonbValue(state,r1,&v1);
4747+
pushJsonbValue(state,WJB_BEGIN_OBJECT,NULL);
4748+
while ((r1=JsonbIteratorNext(it1,&v1, true))!=WJB_DONE)
4749+
pushJsonbValue(state,r1,r1!=WJB_END_OBJECT ?&v1 :NULL);
47604750

4761-
pushJsonbValue(state,WJB_BEGIN_OBJECT,NULL);
4762-
while ((r2=JsonbIteratorNext(it_object,&v2, true))!=WJB_DONE)
4763-
pushJsonbValue(state,r2,r2!=WJB_END_OBJECT ?&v2 :NULL);
4764-
4765-
res=pushJsonbValue(state,WJB_END_ARRAY,NULL);
4766-
}
4751+
while ((r2=JsonbIteratorNext(it2,&v2, true))!=WJB_DONE)
4752+
res=pushJsonbValue(state,r2,r2!=WJB_END_ARRAY ?&v2 :NULL);
47674753
}
47684754
else
47694755
{
47704756
/*
4771-
* This must be scalar || object or object || scalar, as that's all
4772-
* that's left. Both of these make no sense, so error out.
4757+
* We have array || object.
47734758
*/
4774-
ereport(ERROR,
4775-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4776-
errmsg("invalid concatenation of jsonb objects")));
4759+
Assert(rk1==WJB_BEGIN_ARRAY);
4760+
Assert(rk2==WJB_BEGIN_OBJECT);
4761+
4762+
pushJsonbValue(state,WJB_BEGIN_ARRAY,NULL);
4763+
4764+
while ((r1=JsonbIteratorNext(it1,&v1, true))!=WJB_END_ARRAY)
4765+
pushJsonbValue(state,r1,&v1);
4766+
4767+
pushJsonbValue(state,WJB_BEGIN_OBJECT,NULL);
4768+
while ((r2=JsonbIteratorNext(it2,&v2, true))!=WJB_DONE)
4769+
pushJsonbValue(state,r2,r2!=WJB_END_OBJECT ?&v2 :NULL);
4770+
4771+
res=pushJsonbValue(state,WJB_END_ARRAY,NULL);
47774772
}
47784773

47794774
returnres;

‎src/test/regress/expected/jsonb.out

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

41134113
select '"a"'::jsonb || '{"a":1}';
4114-
ERROR: invalid concatenation of jsonb objects
4114+
?column?
4115+
-----------------
4116+
["a", {"a": 1}]
4117+
(1 row)
4118+
41154119
select '{"a":1}' || '"a"'::jsonb;
4116-
ERROR: invalid concatenation of jsonb objects
4120+
?column?
4121+
-----------------
4122+
[{"a": 1}, "a"]
4123+
(1 row)
4124+
4125+
select '[3]'::jsonb || '{}'::jsonb;
4126+
?column?
4127+
----------
4128+
[3, {}]
4129+
(1 row)
4130+
4131+
select '3'::jsonb || '[]'::jsonb;
4132+
?column?
4133+
----------
4134+
[3]
4135+
(1 row)
4136+
4137+
select '3'::jsonb || '4'::jsonb;
4138+
?column?
4139+
----------
4140+
[3, 4]
4141+
(1 row)
4142+
4143+
select '3'::jsonb || '{}'::jsonb;
4144+
?column?
4145+
----------
4146+
[3, {}]
4147+
(1 row)
4148+
41174149
select '["a", "b"]'::jsonb || '{"c":1}';
41184150
?column?
41194151
----------------------

‎src/test/regress/sql/jsonb.sql

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

1059+
select'[3]'::jsonb||'{}'::jsonb;
1060+
select'3'::jsonb||'[]'::jsonb;
1061+
select'3'::jsonb||'4'::jsonb;
1062+
select'3'::jsonb||'{}'::jsonb;
1063+
10591064
select'["a", "b"]'::jsonb||'{"c":1}';
10601065
select'{"c": 1}'::jsonb||'["a", "b"]';
10611066

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp