sunchao commented on code in PR #6431:
URL: https://github.com/apache/datafusion-comet/pull/6431#discussion_r4315280302
##########
native/core/src/execution/planner.rs:
##########
@@ -2541,12 +2542,21 @@ impl PhysicalPlanner {
}
/// Keep the Spark filter's metric identity when its reader is replaced
for an execution.
- fn prepare_probe_filter_for_runtime_reader(plan: Arc<SparkPlan>) ->
Arc<SparkPlan> {
+ fn prepare_probe_filter_for_runtime_reader(
Review Comment:
Rebased onto current main in b9230be2f. `prepare_probe_for_runtime_filters`
now walks the serialized Operator and SparkPlan children together, checking
plan ID, operator type, child counts, and native/Spark child identity before
reading each filter's permission. A mismatch defaults permissions to false for
that subtree while retaining the existing metric wrappers.
Added native tests for `Projection -> Filter -> Scan`, `Filter -> Filter ->
Scan` with all combinations of permissions, and mismatched metadata/tree
boundaries. Added the comment in `early.rs`: this permission controls reader
pruning and does not relax early-consumer expression barriers.
##########
docs/source/user-guide/latest/tuning/operators.md:
##########
@@ -59,6 +59,29 @@ boundaries. A shuffled hash join can still filter probe
batches after shuffle, b
its filter back to an earlier scan stage. Compare the [runtime-filter and scan
metrics](../metrics.md#hash-joins)
with the setting disabled to distinguish reduced hash-probe work from reader
I/O savings.
+### Join runtime filters through residual predicates
+
+Native join runtime filtering is enabled with
+`spark.comet.exec.join.dynamicFilter.enabled`. It can reject probe rows whose
keys
+are absent from the completed build side and use eligible Parquet readers to
prune
+row groups. By default, an intervening residual filter permits reader
propagation
+only for direct-column `IS NOT NULL` checks combined with `AND`.
+
+Set
`spark.comet.exec.join.dynamicFilter.allowDeterministicFilterPushdown=true` to
+let the join filter pass through other deterministic residual predicates, such
as
+`quantity > 0` or a Spark-generated Bloom check. The original residual and the
+exact join remain in place. For example, when the build contains only item IDs
+17 and 42, a row for item 99 can be rejected before evaluating its quantity.
+Nondeterministic predicates, limits, computed projections, and execution
boundaries
Review Comment:
Kept column-only reader traversal as a separate follow-up and opened #7113.
The docs now state that every standalone projection blocks reader attachment,
including column-only projections Spark inserts to drop residual-only columns.
The example explicitly retains `quantity` alongside `item_id`; projection
inside the scan remains supported. The follow-up is linked from the tuning
guide.
##########
spark/src/main/scala/org/apache/comet/CometConf.scala:
##########
@@ -489,6 +489,20 @@ object CometConf extends ShimCometConf {
.booleanConf
.createWithDefault(false)
+ val COMET_EXEC_JOIN_DYNAMIC_FILTER_ALLOW_DETERMINISTIC_FILTER_PUSHDOWN:
ConfigEntry[Boolean] =
Review Comment:
Updated the configuration description to say all standalone projections,
including column-only projections.
Agreed that Spark can already avoid these errors through DPP or pushed
predicates. The additional key is a compatibility and tuning choice, rather
than a claim that Spark must evaluate every residual. Existing dynamic
filtering preserves residual evaluation except direct null checks; this
traversal changes observable errors for plans without those other pruning
opportunities. I kept the separate supported knob so users can retain that
behavior while enabling dynamic filtering, accepting its versioning commitment.
The tuning guide now records that rationale, and the native division-by-zero
regression pins the behavior with the flag off/on and with an error on a
retained row.
##########
spark/src/test/scala/org/apache/comet/exec/CometJoinSuite.scala:
##########
@@ -544,14 +613,18 @@ class CometJoinSuite extends CometTestBase {
.parquet(probePath.getCanonicalPath)
.createOrReplaceTempView("dynamic_schema_probe")
withParquetTable(Seq(Tuple1(0)), "dynamic_schema_build") {
- for ((comet, dynamicFilter) <- Seq((false, false), (true, false),
(true, true))) {
+ for {
+ (comet, dynamicFilter) <- Seq((false, false), (true, false),
(true, true))
+ allowed <- Seq(false, true)
+ } {
withSQLConf(
CometConf.COMET_ENABLED.key -> comet.toString,
+
CometConf.COMET_EXEC_JOIN_DYNAMIC_FILTER_ALLOW_DETERMINISTIC_FILTER_PUSHDOWN.key
-> allowed.toString,
CometConf.COMET_EXEC_JOIN_DYNAMIC_FILTER_ENABLED.key ->
dynamicFilter.toString) {
val df = sql(
"SELECT /*+ BROADCAST(b) */ p.probe_key, p.payload " +
"FROM dynamic_schema_probe p JOIN dynamic_schema_build b "
+
- "ON p.probe_key = b._1")
+ "ON p.probe_key = b._1 WHERE pmod(p.payload, 2) >= 0")
Review Comment:
Restored the residual-free query alongside the `pmod` residual in the
schema-conversion test. Both now run for every existing
Comet/dynamic-filter/permission combination, so the original attached-reader
case remains covered when the new flag is false as well as true. Failure clues
include the permission and residual. Included in b9230be2f.
--
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]