This is an automated email from the ASF dual-hosted git repository.

agrove pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-comet.git


The following commit(s) were added to refs/heads/main by this push:
     new 698c1b28 fix: unwrap dictionaries in CreateNamedStruct (#754)
698c1b28 is described below

commit 698c1b282be98bb87563e511f5c5739b51de5068
Author: Andy Grove <[email protected]>
AuthorDate: Fri Aug 2 11:47:58 2024 -0600

    fix: unwrap dictionaries in CreateNamedStruct (#754)
---
 .../datafusion/expressions/create_named_struct.rs  | 78 ++++++++++++++++++++--
 .../org/apache/comet/CometExpressionSuite.scala    | 16 +++++
 2 files changed, 87 insertions(+), 7 deletions(-)

diff --git 
a/native/core/src/execution/datafusion/expressions/create_named_struct.rs 
b/native/core/src/execution/datafusion/expressions/create_named_struct.rs
index 17d763cc..c06aacf1 100644
--- a/native/core/src/execution/datafusion/expressions/create_named_struct.rs
+++ b/native/core/src/execution/datafusion/expressions/create_named_struct.rs
@@ -15,6 +15,14 @@
 // specific language governing permissions and limitations
 // under the License.
 
+use arrow::compute::take;
+use arrow::record_batch::RecordBatch;
+use arrow_array::types::Int32Type;
+use arrow_array::{Array, DictionaryArray, StructArray};
+use arrow_schema::{DataType, Schema};
+use datafusion::logical_expr::ColumnarValue;
+use datafusion_common::{DataFusionError, Result as DataFusionResult};
+use datafusion_physical_expr::PhysicalExpr;
 use std::{
     any::Any,
     fmt::{Display, Formatter},
@@ -22,13 +30,6 @@ use std::{
     sync::Arc,
 };
 
-use arrow::record_batch::RecordBatch;
-use arrow_array::StructArray;
-use arrow_schema::{DataType, Schema};
-use datafusion::logical_expr::ColumnarValue;
-use datafusion_common::{DataFusionError, Result as DataFusionResult};
-use datafusion_physical_expr::PhysicalExpr;
-
 use crate::execution::datafusion::expressions::utils::down_cast_any_ref;
 
 #[derive(Debug, Hash)]
@@ -63,6 +64,21 @@ impl PhysicalExpr for CreateNamedStruct {
             .map(|expr| expr.evaluate(batch))
             .collect::<datafusion_common::Result<Vec<_>>>()?;
         let arrays = ColumnarValue::values_to_arrays(&values)?;
+        // TODO it would be more efficient if we could preserve dictionaries 
within the
+        // struct array but for now we unwrap them to avoid runtime errors
+        // https://github.com/apache/datafusion-comet/issues/755
+        let arrays = arrays
+            .iter()
+            .map(|array| {
+                if let Some(dict_array) =
+                    array.as_any().downcast_ref::<DictionaryArray<Int32Type>>()
+                {
+                    take(dict_array.values().as_ref(), dict_array.keys(), None)
+                } else {
+                    Ok(Arc::clone(array))
+                }
+            })
+            .collect::<Result<Vec<_>, _>>()?;
         let fields = match &self.data_type {
             DataType::Struct(fields) => fields,
             _ => {
@@ -125,3 +141,51 @@ impl PartialEq<dyn Any> for CreateNamedStruct {
             .unwrap_or(false)
     }
 }
+
+#[cfg(test)]
+mod test {
+    use super::CreateNamedStruct;
+    use arrow_array::{Array, DictionaryArray, Int32Array, RecordBatch, 
StringArray};
+    use arrow_schema::{DataType, Field, Fields, Schema};
+    use datafusion_common::Result;
+    use datafusion_expr::ColumnarValue;
+    use datafusion_physical_expr_common::expressions::column::Column;
+    use datafusion_physical_expr_common::physical_expr::PhysicalExpr;
+    use std::sync::Arc;
+
+    #[test]
+    fn test_create_struct_from_dict_encoded_i32() -> Result<()> {
+        let keys = Int32Array::from(vec![0, 1, 2]);
+        let values = Int32Array::from(vec![0, 111, 233]);
+        let dict = DictionaryArray::try_new(keys, Arc::new(values))?;
+        let data_type = DataType::Dictionary(Box::new(DataType::Int32), 
Box::new(DataType::Int32));
+        let schema = Schema::new(vec![Field::new("a", data_type, false)]);
+        let batch = RecordBatch::try_new(Arc::new(schema), 
vec![Arc::new(dict)])?;
+        let data_type =
+            DataType::Struct(Fields::from(vec![Field::new("a", 
DataType::Int32, false)]));
+        let x = CreateNamedStruct::new(vec![Arc::new(Column::new("a", 0))], 
data_type);
+        let ColumnarValue::Array(x) = x.evaluate(&batch)? else {
+            unreachable!()
+        };
+        assert_eq!(3, x.len());
+        Ok(())
+    }
+
+    #[test]
+    fn test_create_struct_from_dict_encoded_string() -> Result<()> {
+        let keys = Int32Array::from(vec![0, 1, 2]);
+        let values = StringArray::from(vec!["a".to_string(), "b".to_string(), 
"c".to_string()]);
+        let dict = DictionaryArray::try_new(keys, Arc::new(values))?;
+        let data_type = DataType::Dictionary(Box::new(DataType::Int32), 
Box::new(DataType::Utf8));
+        let schema = Schema::new(vec![Field::new("a", data_type, false)]);
+        let batch = RecordBatch::try_new(Arc::new(schema), 
vec![Arc::new(dict)])?;
+        let data_type =
+            DataType::Struct(Fields::from(vec![Field::new("a", DataType::Utf8, 
false)]));
+        let x = CreateNamedStruct::new(vec![Arc::new(Column::new("a", 0))], 
data_type);
+        let ColumnarValue::Array(x) = x.evaluate(&batch)? else {
+            unreachable!()
+        };
+        assert_eq!(3, x.len());
+        Ok(())
+    }
+}
diff --git a/spark/src/test/scala/org/apache/comet/CometExpressionSuite.scala 
b/spark/src/test/scala/org/apache/comet/CometExpressionSuite.scala
index dd02be4f..f169538a 100644
--- a/spark/src/test/scala/org/apache/comet/CometExpressionSuite.scala
+++ b/spark/src/test/scala/org/apache/comet/CometExpressionSuite.scala
@@ -1887,4 +1887,20 @@ class CometExpressionSuite extends CometTestBase with 
AdaptiveSparkPlanHelper {
       }
     }
   }
+
+  test("struct and named_struct with dictionary") {
+    Seq(true, false).foreach { dictionaryEnabled =>
+      withParquetTable(
+        (0 until 100).map(i =>
+          (
+            i,
+            if (i % 2 == 0) { "even" }
+            else { "odd" })),
+        "tbl",
+        withDictionary = dictionaryEnabled) {
+        checkSparkAnswerAndOperator("SELECT struct(_1, _2) FROM tbl")
+        checkSparkAnswerAndOperator("SELECT named_struct('a', _1, 'b', _2) 
FROM tbl")
+      }
+    }
+  }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to