From f68c97e72f8317742c4b89b2bab5f1a65504ca86 Mon Sep 17 00:00:00 2001 From: RK Date: Thu, 9 May 2024 15:56:16 +0530 Subject: [PATCH] feat(function): Implement map_cat function (#15348) * Implement map_cat function Signed-off-by: shamb0 * Implement map_cat function Signed-off-by: shamb0 * Implement map_cat function Signed-off-by: shamb0 * feat: implement map functions using MapType * merge to upstream main updates * Refactor map.rs function module based on code review feedback Signed-off-by: shamb0 * Refactor map.rs function module based on code review feedback Signed-off-by: shamb0 * Refactor map.rs function module based on code review feedback Signed-off-by: shamb0 * Refactor map.rs function module based on code review feedback Signed-off-by: shamb0 * Refactor map.rs function module based on code review feedback Signed-off-by: shamb0 * Refactor map.rs function module based on code review feedback Signed-off-by: shamb0 * Refactor map.rs function module based on code review feedback Signed-off-by: shamb0 * Make CI HappyOF Signed-off-by: shamb0 * Refactor map.rs function module based on code review feedback Signed-off-by: shamb0 * Refactor map.rs function module based on code review feedback Signed-off-by: shamb0 * Refactor map.rs function module based on code review feedback Signed-off-by: shamb0 * Refactor map.rs function module based on code review feedback Signed-off-by: shamb0 --------- Signed-off-by: shamb0 --- src/query/functions/src/scalars/map.rs | 57 ++++++++ src/query/functions/tests/it/scalars/map.rs | 66 +++++++++ .../it/scalars/testdata/function_list.txt | 4 + .../tests/it/scalars/testdata/map.txt | 135 ++++++++++++++++++ .../query/functions/02_0074_function_map.test | 86 +++++++++++ 5 files changed, 348 insertions(+) diff --git a/src/query/functions/src/scalars/map.rs b/src/query/functions/src/scalars/map.rs index 4dbc2049921b..5672a95c56f3 100644 --- a/src/query/functions/src/scalars/map.rs +++ b/src/query/functions/src/scalars/map.rs @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::collections::HashSet; use std::hash::Hash; use databend_common_expression::types::nullable::NullableDomain; +use databend_common_expression::types::ArgType; use databend_common_expression::types::ArrayType; use databend_common_expression::types::EmptyArrayType; use databend_common_expression::types::EmptyMapType; @@ -159,6 +161,61 @@ pub fn register(registry: &mut FunctionRegistry) { ), ); + registry.register_2_arg::( + "map_cat", + |_, _, _| FunctionDomain::Full, + |_, _, _| (), + ); + + registry.register_passthrough_nullable_2_arg( + "map_cat", + |_, domain1, domain2| { + FunctionDomain::Domain(match (domain1, domain2) { + (Some((key_domain1, val_domain1)), Some((key_domain2, val_domain2))) => Some(( + key_domain1.merge(key_domain2), + val_domain1.merge(val_domain2), + )), + (Some(domain1), None) => Some(domain1).cloned(), + (None, Some(domain2)) => Some(domain2).cloned(), + (None, None) => None, + }) + }, + vectorize_with_builder_2_arg::< + MapType, GenericType<1>>, + MapType, GenericType<1>>, + MapType, GenericType<1>>, + >(|lhs, rhs, output_map, ctx| { + if let Some(validity) = &ctx.validity { + if !validity.get_bit(output_map.len()) { + output_map.push_default(); + return; + } + } + + let mut concatenated_map_builder = + ArrayType::create_builder(lhs.len() + rhs.len(), ctx.generics); + let mut detect_dup_keys = HashSet::new(); + + for (lhs_key, lhs_value) in lhs.iter() { + if let Some((_, rhs_value)) = rhs.iter().find(|(rhs_key, _)| lhs_key == *rhs_key) { + detect_dup_keys.insert(lhs_key.clone()); + concatenated_map_builder.put_item((lhs_key.clone(), rhs_value.clone())); + } else { + concatenated_map_builder.put_item((lhs_key.clone(), lhs_value.clone())); + } + } + + for (rhs_key, rhs_value) in rhs.iter() { + if !detect_dup_keys.contains(&rhs_key) { + concatenated_map_builder.put_item((rhs_key, rhs_value)); + } + } + + concatenated_map_builder.commit_row(); + output_map.append_column(&concatenated_map_builder.build()); + }), + ); + registry.register_1_arg_core::, _, _>( "map_size", |_, _| FunctionDomain::Domain(SimpleDomain { min: 0, max: 0 }), diff --git a/src/query/functions/tests/it/scalars/map.rs b/src/query/functions/tests/it/scalars/map.rs index 5d408a063c14..92668272575f 100644 --- a/src/query/functions/tests/it/scalars/map.rs +++ b/src/query/functions/tests/it/scalars/map.rs @@ -30,6 +30,72 @@ fn test_map() { test_map_keys(file); test_map_values(file); test_map_size(file); + test_map_cat(file); +} + +fn test_map_cat(file: &mut impl Write) { + // Empty Inputs:: tests behavior with empty input maps + run_ast(file, "map_cat({}, {})", &[]); + run_ast(file, "map_cat({}, {'k1': 'v1'})", &[]); + run_ast(file, "map_cat({'k1': 'v1'}, {})", &[]); + + // Basic Functionality:: evaluates core functionality + let columns = [ + ("a_col", StringType::from_data(vec!["a_k1", "a_k2", "a_k3"])), + ("b_col", StringType::from_data(vec!["b_k1", "b_k2", "b_k3"])), + ("c_col", StringType::from_data(vec!["c_k1", "c_k2", "c_k3"])), + ("d_col", StringType::from_data(vec!["aaa1", "aaa2", "aaa3"])), + ("e_col", StringType::from_data(vec!["bbb1", "bbb2", "bbb3"])), + ("f_col", StringType::from_data(vec!["ccc1", "ccc2", "ccc3"])), + ]; + + run_ast( + file, + "map_cat(map([a_col, b_col], [d_col, e_col]), map([c_col], [f_col]))", + &columns, + ); + + run_ast(file, "map_cat({'k1':'v1','k2':'v2'}, {'k1':'abc'})", &[]); + + // Duplicate Keys:: assesses handling of duplicate keys + let columns = [ + ("a_col", StringType::from_data(vec!["a_k1", "a_k2", "c_k3"])), + ("b_col", StringType::from_data(vec!["b_k1", "c_k2", "b_k3"])), + ("c_col", StringType::from_data(vec!["c_k1", "c_k2", "c_k3"])), + ("d_col", StringType::from_data(vec!["aaa1", "aaa2", "aaa3"])), + ("e_col", StringType::from_data(vec!["bbb1", "bbb2", "bbb3"])), + ("f_col", StringType::from_data(vec!["ccc1", "ccc2", "ccc3"])), + ]; + + run_ast( + file, + "map_cat(map([a_col, b_col], [d_col, e_col]), map([c_col], [f_col]))", + &columns, + ); + + // Map Size Variation:: tests behavior with different map sizes + run_ast(file, "map_cat({'k1': 'v1', 'k2': 'v2'}, {'k3': 'v3'})", &[]); + run_ast(file, "map_cat({'k1': 'v1'}, {'k2': 'v2', 'k3': 'v3'})", &[]); + + // Null Values:: validates behavior for null values + run_ast( + file, + "map_cat({'k1': 'v1', 'k2': NULL}, {'k2': 'v2', 'k3': NULL})", + &[], + ); + + // Nested Maps:: examines recursive merging capabilities + run_ast( + file, + "map_cat({'k1': {'nk1': 'nv1'}, 'k2': {'nk2': 'nv2'}}, {'k2': {'nk3': 'nv3'}, 'k3': {'nk4': 'nv4'}})", + &[], + ); + + run_ast( + file, + "map_cat({'k1': {'nk1': 'nv1'}, 'k2': {'nk2': 'nv2'}}, {'k1': {'nk1': 'new_nv1'}, 'k2': {'nk3': 'nv3'}})", + &[], + ); } fn test_create(file: &mut impl Write) { diff --git a/src/query/functions/tests/it/scalars/testdata/function_list.txt b/src/query/functions/tests/it/scalars/testdata/function_list.txt index 278fe8cf4d31..0d1337bf65f6 100644 --- a/src/query/functions/tests/it/scalars/testdata/function_list.txt +++ b/src/query/functions/tests/it/scalars/testdata/function_list.txt @@ -2436,6 +2436,10 @@ Functions overloads: 1 map(Array(Nothing) NULL, Array(Nothing) NULL) :: Map(Nothing) NULL 2 map(Array(T0), Array(T1)) :: Map(T0, T1) 3 map(Array(T0) NULL, Array(T1) NULL) :: Map(T0, T1) NULL +0 map_cat(Map(Nothing), Map(Nothing)) :: Map(Nothing) +1 map_cat(Map(Nothing) NULL, Map(Nothing) NULL) :: Map(Nothing) NULL +2 map_cat(Map(T0, T1), Map(T0, T1)) :: Map(T0, T1) +3 map_cat(Map(T0, T1) NULL, Map(T0, T1) NULL) :: Map(T0, T1) NULL 0 map_keys(Map(Nothing)) :: Array(Nothing) 1 map_keys(Map(T0, T1)) :: Array(T0) 2 map_keys(Map(T0, T1) NULL) :: Array(T0) NULL diff --git a/src/query/functions/tests/it/scalars/testdata/map.txt b/src/query/functions/tests/it/scalars/testdata/map.txt index f06cbf03ae85..770348ecea25 100644 --- a/src/query/functions/tests/it/scalars/testdata/map.txt +++ b/src/query/functions/tests/it/scalars/testdata/map.txt @@ -392,3 +392,138 @@ evaluation (internal): +--------+-----------------------------------------------------------------------------------------------------------------+ +ast : map_cat({}, {}) +raw expr : map_cat(map(array(), array()), map(array(), array())) +checked expr : map_cat(map(array<>(), array<>()), map(array<>(), array<>())) +optimized expr : {} :: Map(Nothing) +output type : Map(Nothing) +output domain : {} +output : {} + + +ast : map_cat({}, {'k1': 'v1'}) +raw expr : map_cat(map(array(), array()), map(array('k1'), array('v1'))) +checked expr : map_cat(CAST(map(array<>(), array<>()) AS Map(String, String)), map(array("k1"), array("v1"))) +optimized expr : {"k1":"v1"} +output type : Map(String, String) +output domain : {[{"k1"..="k1"}], [{"v1"..="v1"}]} +output : {'k1':'v1'} + + +ast : map_cat({'k1': 'v1'}, {}) +raw expr : map_cat(map(array('k1'), array('v1')), map(array(), array())) +checked expr : map_cat(map(array("k1"), array("v1")), CAST(map(array<>(), array<>()) AS Map(String, String))) +optimized expr : {"k1":"v1"} +output type : Map(String, String) +output domain : {[{"k1"..="k1"}], [{"v1"..="v1"}]} +output : {'k1':'v1'} + + +ast : map_cat(map([a_col, b_col], [d_col, e_col]), map([c_col], [f_col])) +raw expr : map_cat(map(array(a_col::String, b_col::String), array(d_col::String, e_col::String)), map(array(c_col::String), array(f_col::String))) +checked expr : map_cat(map(array(a_col, b_col), array(d_col, e_col)), map(array(c_col), array(f_col))) +evaluation: ++--------+-------------------+-------------------+-------------------+-------------------+-------------------+-------------------+-----------------------------------------------+ +| | a_col | b_col | c_col | d_col | e_col | f_col | Output | ++--------+-------------------+-------------------+-------------------+-------------------+-------------------+-------------------+-----------------------------------------------+ +| Type | String | String | String | String | String | String | Map(String, String) | +| Domain | {"a_k1"..="a_k3"} | {"b_k1"..="b_k3"} | {"c_k1"..="c_k3"} | {"aaa1"..="aaa3"} | {"bbb1"..="bbb3"} | {"ccc1"..="ccc3"} | Unknown | +| Row 0 | 'a_k1' | 'b_k1' | 'c_k1' | 'aaa1' | 'bbb1' | 'ccc1' | {'a_k1':'aaa1', 'b_k1':'bbb1', 'c_k1':'ccc1'} | +| Row 1 | 'a_k2' | 'b_k2' | 'c_k2' | 'aaa2' | 'bbb2' | 'ccc2' | {'a_k2':'aaa2', 'b_k2':'bbb2', 'c_k2':'ccc2'} | +| Row 2 | 'a_k3' | 'b_k3' | 'c_k3' | 'aaa3' | 'bbb3' | 'ccc3' | {'a_k3':'aaa3', 'b_k3':'bbb3', 'c_k3':'ccc3'} | ++--------+-------------------+-------------------+-------------------+-------------------+-------------------+-------------------+-----------------------------------------------+ +evaluation (internal): ++--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Column | Data | ++--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| a_col | StringColumn { data: 0x615f6b31615f6b32615f6b33, offsets: [0, 4, 8, 12] } | +| b_col | StringColumn { data: 0x625f6b31625f6b32625f6b33, offsets: [0, 4, 8, 12] } | +| c_col | StringColumn { data: 0x635f6b31635f6b32635f6b33, offsets: [0, 4, 8, 12] } | +| d_col | StringColumn { data: 0x616161316161613261616133, offsets: [0, 4, 8, 12] } | +| e_col | StringColumn { data: 0x626262316262623262626233, offsets: [0, 4, 8, 12] } | +| f_col | StringColumn { data: 0x636363316363633263636333, offsets: [0, 4, 8, 12] } | +| Output | ArrayColumn { values: Tuple([StringColumn { data: 0x615f6b31625f6b31635f6b31615f6b32625f6b32635f6b32615f6b33625f6b33635f6b33, offsets: [0, 4, 8, 12, 16, 20, 24, 28, 32, 36] }, StringColumn { data: 0x616161316262623163636331616161326262623263636332616161336262623363636333, offsets: [0, 4, 8, 12, 16, 20, 24, 28, 32, 36] }]), offsets: [0, 3, 6, 9] } | ++--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + + +ast : map_cat({'k1':'v1','k2':'v2'}, {'k1':'abc'}) +raw expr : map_cat(map(array('k1', 'k2'), array('v1', 'v2')), map(array('k1'), array('abc'))) +checked expr : map_cat(map(array("k1", "k2"), array("v1", "v2")), map(array("k1"), array("abc"))) +optimized expr : {"k1":"abc", "k2":"v2"} +output type : Map(String, String) +output domain : {[{"k1"..="k2"}], [{"abc"..="v2"}]} +output : {'k1':'abc', 'k2':'v2'} + + +ast : map_cat(map([a_col, b_col], [d_col, e_col]), map([c_col], [f_col])) +raw expr : map_cat(map(array(a_col::String, b_col::String), array(d_col::String, e_col::String)), map(array(c_col::String), array(f_col::String))) +checked expr : map_cat(map(array(a_col, b_col), array(d_col, e_col)), map(array(c_col), array(f_col))) +evaluation: ++--------+-------------------+-------------------+-------------------+-------------------+-------------------+-------------------+-----------------------------------------------+ +| | a_col | b_col | c_col | d_col | e_col | f_col | Output | ++--------+-------------------+-------------------+-------------------+-------------------+-------------------+-------------------+-----------------------------------------------+ +| Type | String | String | String | String | String | String | Map(String, String) | +| Domain | {"a_k1"..="c_k3"} | {"b_k1"..="c_k2"} | {"c_k1"..="c_k3"} | {"aaa1"..="aaa3"} | {"bbb1"..="bbb3"} | {"ccc1"..="ccc3"} | Unknown | +| Row 0 | 'a_k1' | 'b_k1' | 'c_k1' | 'aaa1' | 'bbb1' | 'ccc1' | {'a_k1':'aaa1', 'b_k1':'bbb1', 'c_k1':'ccc1'} | +| Row 1 | 'a_k2' | 'c_k2' | 'c_k2' | 'aaa2' | 'bbb2' | 'ccc2' | {'a_k2':'aaa2', 'c_k2':'ccc2'} | +| Row 2 | 'c_k3' | 'b_k3' | 'c_k3' | 'aaa3' | 'bbb3' | 'ccc3' | {'c_k3':'ccc3', 'b_k3':'bbb3'} | ++--------+-------------------+-------------------+-------------------+-------------------+-------------------+-------------------+-----------------------------------------------+ +evaluation (internal): ++--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Column | Data | ++--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| a_col | StringColumn { data: 0x615f6b31615f6b32635f6b33, offsets: [0, 4, 8, 12] } | +| b_col | StringColumn { data: 0x625f6b31635f6b32625f6b33, offsets: [0, 4, 8, 12] } | +| c_col | StringColumn { data: 0x635f6b31635f6b32635f6b33, offsets: [0, 4, 8, 12] } | +| d_col | StringColumn { data: 0x616161316161613261616133, offsets: [0, 4, 8, 12] } | +| e_col | StringColumn { data: 0x626262316262623262626233, offsets: [0, 4, 8, 12] } | +| f_col | StringColumn { data: 0x636363316363633263636333, offsets: [0, 4, 8, 12] } | +| Output | ArrayColumn { values: Tuple([StringColumn { data: 0x615f6b31625f6b31635f6b31615f6b32635f6b32635f6b33625f6b33, offsets: [0, 4, 8, 12, 16, 20, 24, 28] }, StringColumn { data: 0x61616131626262316363633161616132636363326363633362626233, offsets: [0, 4, 8, 12, 16, 20, 24, 28] }]), offsets: [0, 3, 5, 7] } | ++--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + + +ast : map_cat({'k1': 'v1', 'k2': 'v2'}, {'k3': 'v3'}) +raw expr : map_cat(map(array('k1', 'k2'), array('v1', 'v2')), map(array('k3'), array('v3'))) +checked expr : map_cat(map(array("k1", "k2"), array("v1", "v2")), map(array("k3"), array("v3"))) +optimized expr : {"k1":"v1", "k2":"v2", "k3":"v3"} +output type : Map(String, String) +output domain : {[{"k1"..="k3"}], [{"v1"..="v3"}]} +output : {'k1':'v1', 'k2':'v2', 'k3':'v3'} + + +ast : map_cat({'k1': 'v1'}, {'k2': 'v2', 'k3': 'v3'}) +raw expr : map_cat(map(array('k1'), array('v1')), map(array('k2', 'k3'), array('v2', 'v3'))) +checked expr : map_cat(map(array("k1"), array("v1")), map(array("k2", "k3"), array("v2", "v3"))) +optimized expr : {"k1":"v1", "k2":"v2", "k3":"v3"} +output type : Map(String, String) +output domain : {[{"k1"..="k3"}], [{"v1"..="v3"}]} +output : {'k1':'v1', 'k2':'v2', 'k3':'v3'} + + +ast : map_cat({'k1': 'v1', 'k2': NULL}, {'k2': 'v2', 'k3': NULL}) +raw expr : map_cat(map(array('k1', 'k2'), array('v1', NULL)), map(array('k2', 'k3'), array('v2', NULL))) +checked expr : map_cat(map(array("k1", "k2"), array(CAST("v1" AS String NULL), CAST(NULL AS String NULL))), map(array("k2", "k3"), array(CAST("v2" AS String NULL), CAST(NULL AS String NULL)))) +optimized expr : {"k1":"v1", "k2":"v2", "k3":NULL} +output type : Map(String, String NULL) +output domain : {[{"k1"..="k3"}], [{""..="v2"} ∪ {NULL}]} +output : {'k1':'v1', 'k2':'v2', 'k3':NULL} + + +ast : map_cat({'k1': {'nk1': 'nv1'}, 'k2': {'nk2': 'nv2'}}, {'k2': {'nk3': 'nv3'}, 'k3': {'nk4': 'nv4'}}) +raw expr : map_cat(map(array('k1', 'k2'), array(map(array('nk1'), array('nv1')), map(array('nk2'), array('nv2')))), map(array('k2', 'k3'), array(map(array('nk3'), array('nv3')), map(array('nk4'), array('nv4'))))) +checked expr : map_cat(map(array("k1", "k2"), array(map(array("nk1"), array("nv1")), map(array("nk2"), array("nv2")))), map(array("k2", "k3"), array(map(array("nk3"), array("nv3")), map(array("nk4"), array("nv4"))))) +optimized expr : {"k1":{"nk1":"nv1"}, "k2":{"nk3":"nv3"}, "k3":{"nk4":"nv4"}} +output type : Map(String, Map(String, String)) +output domain : {[{"k1"..="k3"}], [{[{"nk1"..="nk4"}], [{"nv1"..="nv4"}]}]} +output : {'k1':{'nk1':'nv1'}, 'k2':{'nk3':'nv3'}, 'k3':{'nk4':'nv4'}} + + +ast : map_cat({'k1': {'nk1': 'nv1'}, 'k2': {'nk2': 'nv2'}}, {'k1': {'nk1': 'new_nv1'}, 'k2': {'nk3': 'nv3'}}) +raw expr : map_cat(map(array('k1', 'k2'), array(map(array('nk1'), array('nv1')), map(array('nk2'), array('nv2')))), map(array('k1', 'k2'), array(map(array('nk1'), array('new_nv1')), map(array('nk3'), array('nv3'))))) +checked expr : map_cat(map(array("k1", "k2"), array(map(array("nk1"), array("nv1")), map(array("nk2"), array("nv2")))), map(array("k1", "k2"), array(map(array("nk1"), array("new_nv1")), map(array("nk3"), array("nv3"))))) +optimized expr : {"k1":{"nk1":"new_nv1"}, "k2":{"nk3":"nv3"}} +output type : Map(String, Map(String, String)) +output domain : {[{"k1"..="k2"}], [{[{"nk1"..="nk3"}], [{"new_nv1"..="nv3"}]}]} +output : {'k1':{'nk1':'new_nv1'}, 'k2':{'nk3':'nv3'}} + + diff --git a/tests/sqllogictests/suites/query/functions/02_0074_function_map.test b/tests/sqllogictests/suites/query/functions/02_0074_function_map.test index 92fb16b14367..762b2dda6b5c 100644 --- a/tests/sqllogictests/suites/query/functions/02_0074_function_map.test +++ b/tests/sqllogictests/suites/query/functions/02_0074_function_map.test @@ -49,5 +49,91 @@ select map_values(col1), map_values(col2) from t ['v5','v6'] [40,NULL,50] [] NULL +statement ok +drop table if exists map_cat_test all + +statement ok +CREATE TABLE map_cat_test(s_no Int, p_prof_name Map(String, String) Not Null, p_prof_dob Map(String, String) Not Null); + +statement ok +INSERT INTO map_cat_test VALUES (1, {'name_r11': 'John Smith', 'name_r12': 'Emily Johnson'}, {'dob_11': '1988-07-12', 'dob_12': '1995-03-21'}), +(2, {'name_r21': 'Michael Brown', 'name_22': 'Sarah Davis'}, {'dob_21': '1978-08-17', 'dob_22': '1989-07-19'}), +(3, {'name_r31': 'David Wilson', 'name_32': 'white rose'}, {'dob_31': '1998-09-02', 'dob_32': '1992-05-18'}); + +query +SELECT * FROM map_cat_test LIMIT 3; +---- +1 {'name_r11':'John Smith','name_r12':'Emily Johnson'} {'dob_11':'1988-07-12','dob_12':'1995-03-21'} +2 {'name_r21':'Michael Brown','name_22':'Sarah Davis'} {'dob_21':'1978-08-17','dob_22':'1989-07-19'} +3 {'name_r31':'David Wilson','name_32':'white rose'} {'dob_31':'1998-09-02','dob_32':'1992-05-18'} + +query +SELECT + s_no, + MAP_CAT(p_prof_name, p_prof_dob) AS concatenated_map +FROM + map_cat_test; +---- +1 {'name_r11':'John Smith','name_r12':'Emily Johnson','dob_11':'1988-07-12','dob_12':'1995-03-21'} +2 {'name_r21':'Michael Brown','name_22':'Sarah Davis','dob_21':'1978-08-17','dob_22':'1989-07-19'} +3 {'name_r31':'David Wilson','name_32':'white rose','dob_31':'1998-09-02','dob_32':'1992-05-18'} + + +# Test empty maps +query +SELECT MAP_CAT({}, {}) +---- +{} + +query +SELECT MAP_CAT({}, {'k1': 'v1'}) +---- +{'k1':'v1'} + +query +SELECT MAP_CAT({'k1': 'v1'}, {}) +---- +{'k1':'v1'} + +# Test deduplication +query +SELECT MAP_CAT({'k1': 'v1', 'k2': 'v2'}, {'k1': 'abc'}) +---- +{'k1':'abc','k2':'v2'} + +# Test different map sizes +query +SELECT MAP_CAT({'k1': 'v1', 'k2': 'v2'}, {'k3': 'v3'}) +---- +{'k1':'v1','k2':'v2','k3':'v3'} + +query +SELECT MAP_CAT({'k1': 'v1'}, {'k2': 'v2', 'k3': 'v3'}) +---- +{'k1':'v1','k2':'v2','k3':'v3'} + +# Test NULL values +query +SELECT MAP_CAT({'k1': 'v1', 'k2': NULL}, {'k2': 'v2', 'k3': NULL}) +---- +{'k1':'v1','k2':'v2','k3':NULL} + +# Test nested maps +query +SELECT MAP_CAT( + {'k1': {'nk1': 'nv1'}, 'k2': {'nk2': 'nv2'}}, + {'k2': {'nk3': 'nv3'}, 'k3': {'nk4': 'nv4'}} +) +---- +{'k1':{'nk1':'nv1'},'k2':{'nk3':'nv3'},'k3':{'nk4':'nv4'}} + +query +SELECT MAP_CAT( + {'k1': {'nk1': 'nv1'}, 'k2': {'nk2': 'nv2'}}, + {'k3': {'nk1': 'new_nv1'}, 'k4': {'nk3': 'nv3'}} +) +---- +{'k1':{'nk1':'nv1'},'k2':{'nk2':'nv2'},'k3':{'nk1':'new_nv1'},'k4':{'nk3':'nv3'}} + statement ok DROP DATABASE map_func_test