Skip to content

Commit d69074c

Browse files
committed
chore: Clean up
1 parent 4e61bf4 commit d69074c

File tree

2 files changed

+126
-85
lines changed
  • datafusion

2 files changed

+126
-85
lines changed

datafusion/functions-nested/src/map.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,3 +486,129 @@ fn make_map_array_internal<O: OffsetSizeTrait>(
486486
let map_data = map_data_builder.build()?;
487487
Ok(ColumnarValue::Array(Arc::new(MapArray::from(map_data))))
488488
}
489+
490+
#[cfg(test)]
491+
mod tests {
492+
use super::*;
493+
use arrow::array::{ListArray, Int32Array, StringArray};
494+
use arrow::datatypes::Field;
495+
496+
#[test]
497+
fn test_make_map_with_null_maps() {
498+
// Test that NULL map values (entire map is NULL) are correctly handled
499+
// This test directly calls make_map_batch with a List containing NULL elements
500+
// On main branch: Would fail at line 66 with "map key cannot be null"
501+
// With fix: Correctly handles NULL maps by routing to make_map_array_internal
502+
503+
// Create a List<List<String>> where one element is NULL (representing a NULL map)
504+
// keys = [['a'], NULL, ['b']]
505+
let key_values: Vec<Option<Vec<Option<&str>>>> = vec![
506+
Some(vec![Some("a")]),
507+
None, // This is a NULL map, not a null key
508+
Some(vec![Some("b")]),
509+
];
510+
511+
let mut key_builder = arrow::array::ListBuilder::new(
512+
arrow::array::StringBuilder::new()
513+
);
514+
515+
for key_list in key_values {
516+
match key_list {
517+
Some(keys) => {
518+
for key in keys {
519+
key_builder.values().append_option(key);
520+
}
521+
key_builder.append(true);
522+
}
523+
None => {
524+
key_builder.append(false); // NULL map
525+
}
526+
}
527+
}
528+
let keys_array = Arc::new(key_builder.finish());
529+
530+
// Create corresponding values = [[1], [2], [3]]
531+
let value_values: Vec<Option<Vec<Option<i32>>>> = vec![
532+
Some(vec![Some(1)]),
533+
Some(vec![Some(2)]),
534+
Some(vec![Some(3)]),
535+
];
536+
537+
let mut value_builder = arrow::array::ListBuilder::new(
538+
arrow::array::Int32Builder::new()
539+
);
540+
541+
for value_list in value_values {
542+
match value_list {
543+
Some(values) => {
544+
for value in values {
545+
value_builder.values().append_option(value);
546+
}
547+
value_builder.append(true);
548+
}
549+
None => {
550+
value_builder.append(false);
551+
}
552+
}
553+
}
554+
let values_array = Arc::new(value_builder.finish());
555+
556+
// Call make_map_batch - this should succeed with the fix
557+
let result = make_map_batch(&[
558+
ColumnarValue::Array(keys_array),
559+
ColumnarValue::Array(values_array),
560+
]);
561+
562+
assert!(result.is_ok(), "Should handle NULL maps correctly");
563+
564+
if let Ok(ColumnarValue::Array(map_array)) = result {
565+
assert_eq!(map_array.len(), 3);
566+
assert!(map_array.is_null(1), "Second map should be NULL");
567+
assert!(!map_array.is_null(0), "First map should not be NULL");
568+
assert!(!map_array.is_null(2), "Third map should not be NULL");
569+
} else {
570+
panic!("Expected Array result");
571+
}
572+
}
573+
574+
#[test]
575+
fn test_make_map_with_null_key_within_map_should_fail() {
576+
// Test that null keys WITHIN a map are properly rejected
577+
// keys = [['a', NULL, 'b']] -- NULL here is a null key, should fail
578+
let mut key_builder = arrow::array::ListBuilder::new(
579+
arrow::array::StringBuilder::new()
580+
);
581+
582+
key_builder.values().append_value("a");
583+
key_builder.values().append_null(); // NULL key within the map
584+
key_builder.values().append_value("b");
585+
key_builder.append(true);
586+
587+
let keys_array = Arc::new(key_builder.finish());
588+
589+
// values = [[1, 2, 3]]
590+
let mut value_builder = arrow::array::ListBuilder::new(
591+
arrow::array::Int32Builder::new()
592+
);
593+
value_builder.values().append_value(1);
594+
value_builder.values().append_value(2);
595+
value_builder.values().append_value(3);
596+
value_builder.append(true);
597+
598+
let values_array = Arc::new(value_builder.finish());
599+
600+
// Call make_map_batch - this should fail
601+
let result = make_map_batch(&[
602+
ColumnarValue::Array(keys_array),
603+
ColumnarValue::Array(values_array),
604+
]);
605+
606+
assert!(result.is_err(), "Should reject null keys within maps");
607+
let err = result.unwrap_err();
608+
assert!(
609+
err.to_string().contains("cannot be null"),
610+
"Error should mention null keys, got: {}",
611+
err
612+
);
613+
}
614+
}

datafusion/sqllogictest/test_files/map.slt

Lines changed: 0 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -854,88 +854,3 @@ NULL
854854

855855
statement ok
856856
drop table tt;
857-
858-
# Test for issue with NULL map values
859-
# Memory table tests
860-
statement ok
861-
CREATE TABLE test_null_map AS VALUES
862-
('1', MAP {'a': 'ab'}),
863-
('2', NULL),
864-
('3', MAP {'b': 'cd'});
865-
866-
query T?
867-
SELECT * FROM test_null_map ORDER BY column1;
868-
----
869-
1 {a: ab}
870-
2 NULL
871-
3 {b: cd}
872-
873-
query ?
874-
SELECT map_keys(column2) FROM test_null_map ORDER BY column1;
875-
----
876-
[a]
877-
NULL
878-
[b]
879-
880-
query ?
881-
SELECT map_values(column2) FROM test_null_map ORDER BY column1;
882-
----
883-
[ab]
884-
NULL
885-
[cd]
886-
887-
# Test with map_entries (this internally calls make_map)
888-
query ?
889-
SELECT map_entries(column2) FROM test_null_map ORDER BY column1;
890-
----
891-
[{key: a, value: ab}]
892-
NULL
893-
[{key: b, value: cd}]
894-
895-
statement ok
896-
DROP TABLE test_null_map;
897-
898-
# Parquet storage tests
899-
# This reproduces the issue where NULL map values cause "map key cannot be null" error
900-
statement ok
901-
CREATE TABLE test_map_parquet_mixed_temp AS VALUES
902-
('1', MAP {'a': 'ab'}),
903-
('2', NULL),
904-
('3', MAP {'c': 'cd'});
905-
906-
statement ok
907-
COPY test_map_parquet_mixed_temp TO 'test_files/scratch/test_map_parquet_mixed.parquet' STORED AS PARQUET;
908-
909-
statement ok
910-
DROP TABLE test_map_parquet_mixed_temp;
911-
912-
statement ok
913-
CREATE EXTERNAL TABLE test_map_parquet_mixed
914-
STORED AS PARQUET
915-
LOCATION 'test_files/scratch/test_map_parquet_mixed.parquet';
916-
917-
query T?
918-
SELECT * FROM test_map_parquet_mixed ORDER BY column1;
919-
----
920-
1 {a: ab}
921-
2 NULL
922-
3 {c: cd}
923-
924-
# Test map operations on NULL maps from Parquet
925-
query ?
926-
SELECT map_keys(column2) FROM test_map_parquet_mixed ORDER BY column1;
927-
----
928-
[a]
929-
NULL
930-
[c]
931-
932-
query ?
933-
SELECT map_values(column2) FROM test_map_parquet_mixed ORDER BY column1;
934-
----
935-
[ab]
936-
NULL
937-
[cd]
938-
939-
statement ok
940-
DROP TABLE test_map_parquet_mixed;
941-

0 commit comments

Comments
 (0)