Skip to content

Commit 0965455

Browse files
authored
Return scalar result when all inputs are constants in map and make_map (#11461)
* return scalar result when all inputs are constants. * support convert map array to scalar. * disable the const evaluate for Map type before impl its hash calculation. * add tests in map.slt. * improve error return. * fix error. * fix remove unused import. * remove duplicated testcase. * remove inline.
1 parent d01301d commit 0965455

File tree

4 files changed

+143
-7
lines changed

4 files changed

+143
-7
lines changed

datafusion/common/src/scalar/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2678,7 +2678,10 @@ impl ScalarValue {
26782678
DataType::Duration(TimeUnit::Nanosecond) => {
26792679
typed_cast!(array, index, DurationNanosecondArray, DurationNanosecond)?
26802680
}
2681-
2681+
DataType::Map(_, _) => {
2682+
let a = array.slice(index, 1);
2683+
Self::Map(Arc::new(a.as_map().to_owned()))
2684+
}
26822685
other => {
26832686
return _not_impl_err!(
26842687
"Can't create a scalar from array of type \"{other:?}\""

datafusion/functions/src/core/map.rs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,21 @@ use datafusion_common::{exec_err, internal_err, ScalarValue};
2828
use datafusion_common::{not_impl_err, Result};
2929
use datafusion_expr::{ColumnarValue, ScalarUDFImpl, Signature, Volatility};
3030

31+
/// Check if we can evaluate the expr to constant directly.
32+
///
33+
/// # Example
34+
/// ```sql
35+
/// SELECT make_map('type', 'test') from test
36+
/// ```
37+
/// We can evaluate the result of `make_map` directly.
38+
fn can_evaluate_to_const(args: &[ColumnarValue]) -> bool {
39+
args.iter()
40+
.all(|arg| matches!(arg, ColumnarValue::Scalar(_)))
41+
}
42+
3143
fn make_map(args: &[ColumnarValue]) -> Result<ColumnarValue> {
44+
let can_evaluate_to_const = can_evaluate_to_const(args);
45+
3246
let (key, value): (Vec<_>, Vec<_>) = args
3347
.chunks_exact(2)
3448
.map(|chunk| {
@@ -58,7 +72,7 @@ fn make_map(args: &[ColumnarValue]) -> Result<ColumnarValue> {
5872
Ok(value) => value,
5973
Err(e) => return internal_err!("Error concatenating values: {}", e),
6074
};
61-
make_map_batch_internal(key, value)
75+
make_map_batch_internal(key, value, can_evaluate_to_const)
6276
}
6377

6478
fn make_map_batch(args: &[ColumnarValue]) -> Result<ColumnarValue> {
@@ -68,9 +82,12 @@ fn make_map_batch(args: &[ColumnarValue]) -> Result<ColumnarValue> {
6882
args.len()
6983
);
7084
}
85+
86+
let can_evaluate_to_const = can_evaluate_to_const(args);
87+
7188
let key = get_first_array_ref(&args[0])?;
7289
let value = get_first_array_ref(&args[1])?;
73-
make_map_batch_internal(key, value)
90+
make_map_batch_internal(key, value, can_evaluate_to_const)
7491
}
7592

7693
fn get_first_array_ref(columnar_value: &ColumnarValue) -> Result<ArrayRef> {
@@ -85,7 +102,11 @@ fn get_first_array_ref(columnar_value: &ColumnarValue) -> Result<ArrayRef> {
85102
}
86103
}
87104

88-
fn make_map_batch_internal(keys: ArrayRef, values: ArrayRef) -> Result<ColumnarValue> {
105+
fn make_map_batch_internal(
106+
keys: ArrayRef,
107+
values: ArrayRef,
108+
can_evaluate_to_const: bool,
109+
) -> Result<ColumnarValue> {
89110
if keys.null_count() > 0 {
90111
return exec_err!("map key cannot be null");
91112
}
@@ -124,8 +145,13 @@ fn make_map_batch_internal(keys: ArrayRef, values: ArrayRef) -> Result<ColumnarV
124145
.add_buffer(entry_offsets_buffer)
125146
.add_child_data(entry_struct.to_data())
126147
.build()?;
148+
let map_array = Arc::new(MapArray::from(map_data));
127149

128-
Ok(ColumnarValue::Array(Arc::new(MapArray::from(map_data))))
150+
Ok(if can_evaluate_to_const {
151+
ColumnarValue::Scalar(ScalarValue::try_from_array(map_array.as_ref(), 0)?)
152+
} else {
153+
ColumnarValue::Array(map_array)
154+
})
129155
}
130156

131157
#[derive(Debug)]

datafusion/optimizer/src/simplify_expressions/expr_simplifier.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -656,12 +656,35 @@ impl<'a> ConstEvaluator<'a> {
656656
} else {
657657
// Non-ListArray
658658
match ScalarValue::try_from_array(&a, 0) {
659-
Ok(s) => ConstSimplifyResult::Simplified(s),
659+
Ok(s) => {
660+
// TODO: support the optimization for `Map` type after support impl hash for it
661+
if matches!(&s, ScalarValue::Map(_)) {
662+
ConstSimplifyResult::SimplifyRuntimeError(
663+
DataFusionError::NotImplemented("Const evaluate for Map type is still not supported".to_string()),
664+
expr,
665+
)
666+
} else {
667+
ConstSimplifyResult::Simplified(s)
668+
}
669+
}
660670
Err(err) => ConstSimplifyResult::SimplifyRuntimeError(err, expr),
661671
}
662672
}
663673
}
664-
ColumnarValue::Scalar(s) => ConstSimplifyResult::Simplified(s),
674+
ColumnarValue::Scalar(s) => {
675+
// TODO: support the optimization for `Map` type after support impl hash for it
676+
if matches!(&s, ScalarValue::Map(_)) {
677+
ConstSimplifyResult::SimplifyRuntimeError(
678+
DataFusionError::NotImplemented(
679+
"Const evaluate for Map type is still not supported"
680+
.to_string(),
681+
),
682+
expr,
683+
)
684+
} else {
685+
ConstSimplifyResult::Simplified(s)
686+
}
687+
}
665688
}
666689
}
667690
}

datafusion/sqllogictest/test_files/map.slt

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,87 @@ SELECT map(column5, column6) FROM t;
212212
# {k1:1, k2:2}
213213
# {k3: 3}
214214
# {k5: 5}
215+
216+
query ?
217+
SELECT MAKE_MAP('POST', 41, 'HEAD', 33, 'PATCH', 30, 'OPTION', 29, 'GET', 27, 'PUT', 25, 'DELETE', 24) AS method_count from t;
218+
----
219+
{POST: 41, HEAD: 33, PATCH: 30, OPTION: 29, GET: 27, PUT: 25, DELETE: 24}
220+
{POST: 41, HEAD: 33, PATCH: 30, OPTION: 29, GET: 27, PUT: 25, DELETE: 24}
221+
{POST: 41, HEAD: 33, PATCH: 30, OPTION: 29, GET: 27, PUT: 25, DELETE: 24}
222+
223+
query I
224+
SELECT MAKE_MAP('POST', 41, 'HEAD', 33)['POST'] from t;
225+
----
226+
41
227+
41
228+
41
229+
230+
query ?
231+
SELECT MAKE_MAP('POST', 41, 'HEAD', 33, 'PATCH', null) from t;
232+
----
233+
{POST: 41, HEAD: 33, PATCH: }
234+
{POST: 41, HEAD: 33, PATCH: }
235+
{POST: 41, HEAD: 33, PATCH: }
236+
237+
query ?
238+
SELECT MAKE_MAP('POST', null, 'HEAD', 33, 'PATCH', null) from t;
239+
----
240+
{POST: , HEAD: 33, PATCH: }
241+
{POST: , HEAD: 33, PATCH: }
242+
{POST: , HEAD: 33, PATCH: }
243+
244+
query ?
245+
SELECT MAKE_MAP(1, null, 2, 33, 3, null) from t;
246+
----
247+
{1: , 2: 33, 3: }
248+
{1: , 2: 33, 3: }
249+
{1: , 2: 33, 3: }
250+
251+
query ?
252+
SELECT MAKE_MAP([1,2], ['a', 'b'], [3,4], ['b']) from t;
253+
----
254+
{[1, 2]: [a, b], [3, 4]: [b]}
255+
{[1, 2]: [a, b], [3, 4]: [b]}
256+
{[1, 2]: [a, b], [3, 4]: [b]}
257+
258+
query ?
259+
SELECT MAP(['POST', 'HEAD', 'PATCH'], [41, 33, 30]) from t;
260+
----
261+
{POST: 41, HEAD: 33, PATCH: 30}
262+
{POST: 41, HEAD: 33, PATCH: 30}
263+
{POST: 41, HEAD: 33, PATCH: 30}
264+
265+
query ?
266+
SELECT MAP(['POST', 'HEAD', 'PATCH'], [41, 33, null]) from t;
267+
----
268+
{POST: 41, HEAD: 33, PATCH: }
269+
{POST: 41, HEAD: 33, PATCH: }
270+
{POST: 41, HEAD: 33, PATCH: }
271+
272+
query ?
273+
SELECT MAP([[1,2], [3,4]], ['a', 'b']) from t;
274+
----
275+
{[1, 2]: a, [3, 4]: b}
276+
{[1, 2]: a, [3, 4]: b}
277+
{[1, 2]: a, [3, 4]: b}
278+
279+
query ?
280+
SELECT MAP(make_array('POST', 'HEAD', 'PATCH'), make_array(41, 33, 30)) from t;
281+
----
282+
{POST: 41, HEAD: 33, PATCH: 30}
283+
{POST: 41, HEAD: 33, PATCH: 30}
284+
{POST: 41, HEAD: 33, PATCH: 30}
285+
286+
query ?
287+
SELECT MAP(arrow_cast(make_array('POST', 'HEAD', 'PATCH'), 'FixedSizeList(3, Utf8)'), arrow_cast(make_array(41, 33, 30), 'FixedSizeList(3, Int64)')) from t;
288+
----
289+
{POST: 41, HEAD: 33, PATCH: 30}
290+
{POST: 41, HEAD: 33, PATCH: 30}
291+
{POST: 41, HEAD: 33, PATCH: 30}
292+
293+
query ?
294+
SELECT MAP(arrow_cast(make_array('POST', 'HEAD', 'PATCH'), 'LargeList(Utf8)'), arrow_cast(make_array(41, 33, 30), 'LargeList(Int64)')) from t;
295+
----
296+
{POST: 41, HEAD: 33, PATCH: 30}
297+
{POST: 41, HEAD: 33, PATCH: 30}
298+
{POST: 41, HEAD: 33, PATCH: 30}

0 commit comments

Comments
 (0)