</para>
<para>
Concatenates two <type>jsonb</type> values.
- Concatenating two objects generates an object with the union of their
+ Concatenating two arrays generates an array containing all the
+ elements of each input. Concatenating two objects generates an
+ object containing the union of their
keys, taking the second object's value when there are duplicate keys.
+ All other cases are treated by converting a non-array input into a
+ single-element array, and then proceeding as for two arrays.
Does not operate recursively: only the top-level array or object
structure is merged.
</para>
<para>
<literal>'{"a": "b"}'::jsonb || '{"c": "d"}'::jsonb</literal>
<returnvalue>{"a": "b", "c": "d"}</returnvalue>
+ </para>
+ <para>
+ <literal>'[1, 2]'::jsonb || '3'::jsonb</literal>
+ <returnvalue>[1, 2, 3]</returnvalue>
+ </para>
+ <para>
+ <literal>'{"a": "b"}'::jsonb || '42'::jsonb</literal>
+ <returnvalue>[{"a": "b"}, 42]</returnvalue>
+ </para>
+ <para>
+ To append an array to another array as a single entry, wrap it
+ in an additional layer of array, for example:
+ </para>
+ <para>
+ <literal>'[1, 2]'::jsonb || jsonb_build_array('[3, 4]'::jsonb)</literal>
+ <returnvalue>[1, 2, [3, 4]]</returnvalue>
</para></entry>
</row>
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).
*/
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)
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;