risingwave_expr_impl/scalar/jsonb_concat.rs
1// Copyright 2025 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use jsonbb::{Value, ValueRef};
16use risingwave_common::types::{JsonbRef, JsonbVal};
17use risingwave_expr::function;
18
19/// Concatenates the two jsonbs.
20///
21/// Examples:
22///
23/// ```slt
24/// # concat
25/// query T
26/// SELECT '[1,2]'::jsonb || '[3,4]'::jsonb;
27/// ----
28/// [1, 2, 3, 4]
29///
30/// query T
31/// SELECT '{"a": 1}'::jsonb || '{"b": 2}'::jsonb;
32/// ----
33/// {"a": 1, "b": 2}
34///
35/// query T
36/// SELECT '[1,2]'::jsonb || '{"a": 1}'::jsonb;
37/// ----
38/// [1, 2, {"a": 1}]
39///
40/// query T
41/// SELECT '1'::jsonb || '2'::jsonb;
42/// ----
43/// [1, 2]
44///
45/// query T
46/// SELECT '[1,2]'::jsonb || 'null'::jsonb;
47/// ----
48/// [1, 2, null]
49///
50/// query T
51/// SELECT 'null'::jsonb || '[1,2]'::jsonb;
52/// ----
53/// [null, 1, 2]
54///
55/// query T
56/// SELECT 'null'::jsonb || '1'::jsonb;
57/// ----
58/// [null, 1]
59/// ```
60#[function("jsonb_concat(jsonb, jsonb) -> jsonb")]
61pub fn jsonb_concat(left: JsonbRef<'_>, right: JsonbRef<'_>) -> JsonbVal {
62 match (left.into(), right.into()) {
63 // left and right are object based.
64 // This would have left:{'a':1}, right:{'b':2} -> {'a':1,'b':2}
65 (ValueRef::Object(left), ValueRef::Object(right)) => {
66 JsonbVal::from(Value::object(left.iter().chain(right.iter())))
67 }
68
69 // left and right are array-based.
70 // This would merge both arrays into one array.
71 // This would have left:[1,2], right:[3,4] -> [1,2,3,4]
72 (ValueRef::Array(left), ValueRef::Array(right)) => {
73 JsonbVal::from(Value::array(left.iter().chain(right.iter())))
74 }
75
76 // One operand is an array, and the other is a single element.
77 // This would insert the non-array value as another element into the array
78 // Eg left:[1,2] right: {'a':1} -> [1,2,{'a':1}]
79 (ValueRef::Array(left), value) => JsonbVal::from(Value::array(left.iter().chain([value]))),
80
81 // One operand is an array, and the other is a single element.
82 // This would insert the non-array value as another element into the array
83 // Eg left:{'a':1} right:[1,2] -> [{'a':1},1,2]
84 (value, ValueRef::Array(right)) => {
85 JsonbVal::from(Value::array([value].into_iter().chain(right.iter())))
86 }
87
88 // Both are non-array inputs.
89 // Both elements would be placed together in an array
90 // Eg left:1 right: 2 -> [1,2]
91 (left, right) => JsonbVal::from(Value::array([left, right])),
92 }
93}