datafusion_substrait/logical_plan/consumer/expr/
nested.rs1use crate::logical_plan::consumer::SubstraitConsumer;
19use datafusion::common::{DFSchema, not_impl_err, substrait_err};
20use datafusion::execution::FunctionRegistry;
21use datafusion::logical_expr::Expr;
22use substrait::proto::expression::Nested;
23use substrait::proto::expression::nested::NestedType;
24
25pub async fn from_nested(
32 consumer: &impl SubstraitConsumer,
33 nested: &Nested,
34 input_schema: &DFSchema,
35) -> datafusion::common::Result<Expr> {
36 let Some(nested_type) = &nested.nested_type else {
37 return substrait_err!("Nested expression requires a nested_type");
38 };
39
40 match nested_type {
41 NestedType::List(list) => {
42 if list.values.is_empty() {
43 return substrait_err!(
44 "Empty Nested lists are not supported; use Literal.empty_list instead"
45 );
46 }
47
48 let mut args = Vec::with_capacity(list.values.len());
49 for value in &list.values {
50 args.push(consumer.consume_expression(value, input_schema).await?);
51 }
52
53 let make_array_udf = consumer.get_function_registry().udf("make_array")?;
54 Ok(Expr::ScalarFunction(
55 datafusion::logical_expr::expr::ScalarFunction::new_udf(
56 make_array_udf,
57 args,
58 ),
59 ))
60 }
61 NestedType::Struct(_) => {
62 not_impl_err!("Nested struct expressions are not yet supported")
63 }
64 NestedType::Map(_) => {
65 not_impl_err!("Nested map expressions are not yet supported")
66 }
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use crate::logical_plan::consumer::utils::tests::test_consumer;
74 use substrait::proto::expression::Literal;
75 use substrait::proto::expression::nested::List;
76 use substrait::proto::{self, Expression};
77
78 fn make_i64_literal(value: i64) -> Expression {
79 Expression {
80 rex_type: Some(proto::expression::RexType::Literal(Literal {
81 nullable: false,
82 type_variation_reference: 0,
83 literal_type: Some(proto::expression::literal::LiteralType::I64(value)),
84 })),
85 }
86 }
87
88 #[tokio::test]
89 async fn nested_list_with_literals() -> datafusion::common::Result<()> {
90 let consumer = test_consumer();
91 let schema = DFSchema::empty();
92 let nested = Nested {
93 nullable: false,
94 type_variation_reference: 0,
95 nested_type: Some(NestedType::List(List {
96 values: vec![
97 make_i64_literal(1),
98 make_i64_literal(2),
99 make_i64_literal(3),
100 ],
101 })),
102 };
103
104 let expr = from_nested(&consumer, &nested, &schema).await?;
105 assert_eq!(
106 format!("{expr}"),
107 "make_array(Int64(1), Int64(2), Int64(3))"
108 );
109
110 Ok(())
111 }
112
113 #[tokio::test]
114 async fn nested_list_empty_rejected() -> datafusion::common::Result<()> {
115 let consumer = test_consumer();
116 let schema = DFSchema::empty();
117 let nested = Nested {
118 nullable: true,
119 type_variation_reference: 0,
120 nested_type: Some(NestedType::List(List { values: vec![] })),
121 };
122
123 let result = from_nested(&consumer, &nested, &schema).await;
124 assert!(result.is_err());
125 assert!(
126 result
127 .unwrap_err()
128 .to_string()
129 .contains("Empty Nested lists are not supported")
130 );
131
132 Ok(())
133 }
134
135 #[tokio::test]
136 async fn nested_missing_type() -> datafusion::common::Result<()> {
137 let consumer = test_consumer();
138 let schema = DFSchema::empty();
139 let nested = Nested {
140 nullable: false,
141 type_variation_reference: 0,
142 nested_type: None,
143 };
144
145 let result = from_nested(&consumer, &nested, &schema).await;
146 assert!(result.is_err());
147 assert!(result.unwrap_err().to_string().contains("nested_type"));
148
149 Ok(())
150 }
151}