dwsmith1983 commented on code in PR #5654:
URL: https://github.com/apache/datafusion-comet/pull/5654#discussion_r4095811806
##########
native/core/src/parquet/parquet_support.rs:
##########
@@ -162,43 +169,327 @@ impl SparkParquetOptions {
/// Spark-compatible cast implementation. Defers to DataFusion's cast where
that is known
/// to be compatible, and returns an error when a not supported and not
DF-compatible cast
-/// is requested.
+/// is requested. Resolves the nested field mapping for this one value; a
per-file caller
+/// resolves once and uses [`spark_parquet_convert_with_mapping`] for every
batch.
pub fn spark_parquet_convert(
arg: ColumnarValue,
data_type: &DataType,
parquet_options: &SparkParquetOptions,
+) -> DataFusionResult<ColumnarValue> {
+ let mapping =
+ resolve_field_mapping(&arg.data_type(), data_type,
parquet_options).map_err(spark_error)?;
+ spark_parquet_convert_with_mapping(arg, data_type, &mapping,
parquet_options)
+}
+
+/// [`spark_parquet_convert`] with a mapping already resolved for the value's
type.
+pub(crate) fn spark_parquet_convert_with_mapping(
+ arg: ColumnarValue,
+ data_type: &DataType,
+ mapping: &FieldMapping,
+ parquet_options: &SparkParquetOptions,
) -> DataFusionResult<ColumnarValue> {
match arg {
- ColumnarValue::Array(array) =>
Ok(ColumnarValue::Array(parquet_convert_array(
+ ColumnarValue::Array(array) => Ok(ColumnarValue::Array(convert_array(
array,
data_type,
+ mapping,
parquet_options,
+ None,
)?)),
ColumnarValue::Scalar(scalar) => {
// Note that normally CAST(scalar) should be fold in Spark JVM
side. However, for
// some cases e.g., scalar subquery, Spark will not fold it, so we
need to handle it
// here.
let array = scalar.to_array()?;
let scalar = ScalarValue::try_from_array(
- &parquet_convert_array(array, data_type, parquet_options)?,
+ &convert_array(array, data_type, mapping, parquet_options,
None)?,
0,
)?;
Ok(ColumnarValue::Scalar(scalar))
}
}
}
-fn parquet_convert_array(
- array: ArrayRef,
+/// Wrap a [`SparkError`] the way every native operator surfaces it to the JVM.
+pub(crate) fn spark_error(error: SparkError) -> DataFusionError {
+ DataFusionError::External(Box::new(error))
+}
+
+/// Outcome of matching one requested id or name against a struct's file
fields: the last
+/// file field that matched and whether more than one did. A plain `Copy`
value, so resolving
+/// a wide struct allocates nothing per id or per name; the matched names are
only gathered
+/// when an ambiguity is reported.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) struct FieldMatch {
+ pub(crate) index: usize,
+ pub(crate) ambiguous: bool,
+}
+
+impl FieldMatch {
+ pub(crate) fn new(index: usize, ambiguous: bool) -> Self {
+ Self { index, ambiguous }
+ }
+
+ /// The first file field carrying this id or name.
+ pub(crate) fn first(index: usize) -> Self {
+ Self::new(index, false)
+ }
+
+ /// A further file field carrying the same id or name: the later index
wins, as Spark's
+ /// `toMap` does for exact names, and the entry turns ambiguous.
+ pub(crate) fn also(self, index: usize) -> Self {
+ Self::new(index, true)
+ }
+}
+
+/// Record file field `index` under `key`, keeping the entry `Copy`-sized
however many fields
+/// share the key.
+pub(crate) fn record_field_match<K: Hash + Eq>(
+ matches: &mut HashMap<K, FieldMatch>,
+ key: K,
+ index: usize,
+) {
+ matches
+ .entry(key)
+ .and_modify(|m| *m = m.also(index))
+ .or_insert_with(|| FieldMatch::first(index));
+}
+
+/// Comma-joined names of the fields carrying `id`, for the duplicate-id error
message.
+pub(crate) fn field_names_with_id(fields: &Fields, id: i32) -> String {
Review Comment:
> Now that this helper formats the list for root and nested fields alike,
could it add the brackets?
Done. `field_names_with_id` now returns the list bracketed and comma joined
the way `matchIdField` renders it, so the message Comet hands to
`foundDuplicateFieldInFieldIdLookupModeError` reads `Found duplicate field(s)
"1": [x, y] in id mapping mode` on both sides. The Rust display string dropped
its own brackets so the list is not wrapped twice, and the Rust assertions pin
`id=1 matches [x, y]` rather than only the id.
On the Scala side, `multiple id matches` and `duplicate field id inside a
struct is rejected when a requested id matches two fields` run the same read
with Comet off, take the duplicate id message Spark raises, check that it lists
the expected fields (`"1": [a, rand2]` for the root case, `"1": [x, y]` for the
struct), and assert that Comet's message equals it in full.
--
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]