sunchao commented on code in PR #5407:
URL: https://github.com/apache/datafusion-comet/pull/5407#discussion_r3865538964


##########
native/core/src/parquet/cast_column/variant.rs:
##########
@@ -0,0 +1,1122 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+use arrow::{
+    array::{Array, ArrayRef, AsArray, BinaryArray, BinaryBuilder, 
ListLikeArray, StructArray},
+    buffer::NullBuffer,
+    compute::{cast, cast_with_options},
+    datatypes::{DataType, FieldRef, TimeUnit},
+    error::ArrowError,
+};
+use datafusion::common::{
+    format::DEFAULT_CAST_OPTIONS, DataFusionError, Result as DataFusionResult,
+};
+use parquet::variant::{
+    unshred_variant, BorrowedShreddingState, ListBuilder, MetadataBuilder, 
ObjectBuilder,
+    ParentState, ReadOnlyMetadataBuilder, ValueBuilder, Variant, VariantArray, 
VariantBuilder,
+    VariantDecimal4, VariantDecimal8, VariantMetadata, WritableMetadataBuilder,
+};
+use std::{
+    collections::HashSet,
+    panic::{catch_unwind, AssertUnwindSafe},
+    sync::Arc,
+};
+
+pub(super) fn normalize_variant_array(
+    array: &ArrayRef,
+    target_field: &FieldRef,
+) -> DataFusionResult<ArrayRef> {
+    let DataType::Struct(fields) = target_field.data_type() else {
+        return Err(DataFusionError::Execution(
+            "Variant extension field must use Struct storage".to_string(),
+        ));
+    };
+    if fields.len() != 2
+        || fields[0].name() != "value"
+        || fields[1].name() != "metadata"
+        || fields
+            .iter()
+            .any(|field| field.data_type() != &DataType::Binary)
+    {
+        return Err(DataFusionError::Execution(
+            "Variant output must contain Binary children [value, 
metadata]".to_string(),
+        ));
+    }
+
+    let array = decode_variant_metadata_dictionary(array)?;
+    let array = normalize_variant_typed_value(&array)?;
+    let variant = VariantArray::try_new(array.as_ref())?;
+    let was_shredded = variant.typed_value_field().is_some();
+    let unshredded = unshred_variant_for_spark(&variant)?;
+    let value = unshredded.value_field().ok_or_else(|| {
+        DataFusionError::Execution("Unshredded Variant is missing its value 
field".to_string())
+    })?;
+    let value = cast(value.as_ref(), &DataType::Binary)?;
+    let metadata = cast(unshredded.metadata_field().as_ref(), 
&DataType::Binary)?;
+    let (value, metadata) = if was_shredded {
+        rebuild_shredded_variant_for_spark(&variant, &value, &metadata, 
unshredded.inner().nulls())?
+    } else {
+        let value = reorder_variant_values(
+            &value,
+            &metadata,
+            unshredded.inner().nulls(),
+            VariantObjectKeyOrder::SparkUtf16,
+            false,
+        )?;
+        (value, metadata)
+    };
+    let output = StructArray::try_new(
+        fields.clone(),
+        vec![value, metadata],
+        unshredded.inner().nulls().cloned(),
+    )?;
+    Ok(Arc::new(output))
+}
+
+fn unshred_variant_for_spark(variant: &VariantArray) -> 
DataFusionResult<VariantArray> {
+    let first =
+        prepare_variant_for_unshredding(variant).and_then(|array| 
Ok(unshred_variant(&array)?));
+    let first_error = match first {
+        Ok(array) => return Ok(array),
+        Err(error) => error,
+    };
+    let Some(variant) = canonicalize_spark_empty_key_metadata(variant)? else {
+        return Err(first_error);
+    };
+    let variant = prepare_variant_for_unshredding(&variant)?;
+    Ok(unshred_variant(&variant)?)
+}
+
+fn normalize_variant_type(data_type: &DataType) -> Option<DataType> {
+    fn normalize_field(field: &FieldRef) -> Option<FieldRef> {
+        normalize_variant_type(field.data_type())
+            .map(|data_type| 
Arc::new(field.as_ref().clone().with_data_type(data_type)))
+    }
+
+    match data_type {
+        DataType::UInt8 => Some(DataType::Int16),
+        DataType::UInt16 => Some(DataType::Int32),
+        DataType::UInt32 => Some(DataType::Int64),
+        DataType::Timestamp(TimeUnit::Millisecond, timezone) => {

Review Comment:
   [P2] Preserve integer semantics for nanosAsLong Variant children
   
   This normalizer leaves nanosecond timestamps unchanged, but with 
`spark.sql.legacy.parquet.nanosAsLong=true`, Spark's vectorized Parquet reader 
interprets those children as integers. On Spark 4.0.4, with 
`spark.sql.variant.allowReadingShredded=true`, a whole-Variant read of 
`typed_value: INT64 TIMESTAMP(NANOS,true)` containing `1704067200123000000` 
returns that integer without Comet; this head, with `CometNativeScanExec` 
asserted in the executed plan, instead returns a timestamp (`2024-01-01 
00:00:00.123` when cast to STRING; `schema_of_variant` reports `TIMESTAMP` 
instead of Spark's `BIGINT`). The fixture has no embedded `ARROW:schema`. A 
non-microsecond-aligned value, `1704067200123456789`, instead fails with 
`UNKNOWN_PRIMITIVE_TYPE_IN_VARIANT` (type 18; the NTZ case produces 19). Please 
preserve the configured raw-Int64 interpretation before Variant construction, 
or fall back for these inputs. Merely converting nanos to micros would still 
change the Variant value type. The pass
 ing baseline here is Spark's vectorized reader; its row reader rejects the 
fixture.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to