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


##########
native/core/src/execution/operators/dynamic_filter_topk.rs:
##########
@@ -0,0 +1,271 @@
+// 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.
+
+//! Connect a local TopK's improving threshold to its native Parquet reader.
+
+use std::fmt::Formatter;
+use std::sync::Arc;
+
+use arrow::datatypes::DataType;
+use datafusion::common::config::ConfigOptions;
+use datafusion::common::tree_node::TreeNodeRecursion;
+use datafusion::common::{internal_err, Result, Statistics};
+use datafusion::execution::TaskContext;
+use datafusion::physical_expr::expressions::{lit, Column, 
DynamicFilterPhysicalExpr};
+use datafusion::physical_expr::PhysicalExpr;
+use 
datafusion::physical_plan::distribution_requirements::InputDistributionRequirements;
+use datafusion::physical_plan::execution_plan::CardinalityEffect;
+use datafusion::physical_plan::metrics::{ExecutionPlanMetricsSet, 
MetricBuilder, MetricsSet};
+use datafusion::physical_plan::sorts::sort::SortExec;
+use datafusion::physical_plan::statistics::{ChildStats, StatisticsArgs};
+use datafusion::physical_plan::stream::RecordBatchStreamAdapter;
+use datafusion::physical_plan::{
+    ChildrenPropertiesMode, DisplayAs, DisplayFormatType, ExecutionPlan, 
ExecutionPlanProperties,
+    PlanProperties, ReplaceChildrenOptions, SendableRecordBatchStream,
+};
+use futures::StreamExt;
+
+use super::parquet_reader_filter::try_attach_parquet_reader_filter;
+
+/// Keep an unexecuted template in the Spark plan. Each stream gets a fresh 
TopK
+/// and reader predicate, so a previous execution's threshold cannot discard 
rows
+/// in a later execution. Only metric handles outlive the stream.
+#[derive(Debug)]
+pub(crate) struct DynamicFilterTopKExec {
+    template: SortExec,
+    config: ConfigOptions,
+    metrics: ExecutionPlanMetricsSet,
+}
+
+struct RuntimeDynamicFilterTopK {
+    sort: SortExec,
+    reader_filter_attached: bool,
+}
+
+impl DynamicFilterTopKExec {
+    pub(crate) fn try_new(sort: &SortExec, config: &ConfigOptions) -> 
Result<Option<Self>> {
+        if !config.optimizer.enable_dynamic_filter_pushdown
+            || !config.optimizer.enable_topk_dynamic_filter_pushdown
+            || !matches!(sort.fetch(), Some(fetch) if fetch > 0)
+            || sort.input().output_partitioning().partition_count() != 1
+            || sort.expr().len() != 1
+        {
+            return Ok(None);
+        }
+        let key = &sort.expr()[0].expr;
+        if !key.is::<Column>()
+            || !matches!(
+                key.data_type(sort.input().schema().as_ref())?,
+                DataType::Int8 | DataType::Int16 | DataType::Int32 | 
DataType::Int64
+            )
+        {
+            return Ok(None);
+        }
+        Ok(Some(Self::new(sort, config.clone())))
+    }
+
+    fn new(sort: &SortExec, config: ConfigOptions) -> Self {
+        Self {
+            template: Self::fresh_sort(sort, Arc::clone(sort.input())),
+            config,
+            metrics: ExecutionPlanMetricsSet::new(),
+        }
+    }
+
+    fn fresh_sort(template: &SortExec, input: Arc<dyn ExecutionPlan>) -> 
SortExec {
+        SortExec::new(template.expr().clone(), input)
+            .with_preserve_partitioning(template.preserve_partitioning())
+            .with_fetch(template.fetch())
+    }
+
+    fn build_runtime_sort(&self) -> Result<RuntimeDynamicFilterTopK> {
+        let predicate = Arc::new(DynamicFilterPhysicalExpr::new(
+            vec![Arc::clone(&self.template.expr()[0].expr)],
+            lit(true),
+        ));
+        // Unfiltered Comet scans check top-level TIMESTAMP_MILLIS conversions
+        // for overflow. Pruning a later row group could suppress that error.
+        // The physical Parquet units are unknown here, so leave scans with a
+        // projected top-level timestamp unfiltered. Nested conversions already
+        // use Spark's safe-cast behavior in the schema adapter.
+        let has_timestamp = self
+            .template
+            .input()
+            .schema()
+            .fields()
+            .iter()
+            .any(|field| matches!(field.data_type(), DataType::Timestamp(_, 
_)));

Review Comment:
   ### Correctness
   
   **[P2] Include nested timestamp payloads in the pruning guard**
   
   The current base's [#5740 
change](https://github.com/apache/datafusion-comet/pull/5740) makes unfiltered 
`TIMESTAMP_MILLIS` conversions checked inside structs, arrays and maps, so the 
comment about nested safe casts is no longer true after this PR is merged. This 
predicate sees only the outer `Struct`/`List`/`Map` and still attaches the 
reader filter. For example, `SELECT k, payload FROM t ORDER BY k ASC NULLS LAST 
LIMIT 1` can have a direct INT key and `payload STRUCT<ts TIMESTAMP>`: put 
valid timestamps with `k=0..99` in the first row group and a visible 
`92233720368547758` millisecond timestamp with `k=100..199` in a later group. 
With one partition, small batches and page/row filtering disabled, the first 
group establishes `k < 0`. TopK can prune the later group before the nested 
checked conversion. It returns a row where unfiltered Comet on the current base 
and Spark raise overflow. Please inspect projected types recursively and extend 
the flat timestamp regression to whole ne
 sted payloads with filtering off/on. The recursive checked conversion is 
already present in the current CI merge's `parquet_support.rs`.
   



-- 
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