kosiew commented on code in PR #25790:
URL: https://github.com/apache/datafusion/pull/25790#discussion_r4334343675


##########
datafusion/physical-expr/src/intervals/utils.rs:
##########
@@ -36,19 +36,47 @@ use datafusion_expr::interval_arithmetic::Interval;
 /// will relax as more types of `PhysicalExpr`s and `Operator`s are supported.
 /// Currently, [`CastExpr`], [`NegativeExpr`], [`BinaryExpr`], [`Column`] and 
[`Literal`] are supported.
 pub fn check_support(expr: &Arc<dyn PhysicalExpr>, schema: &SchemaRef) -> bool 
{
+    check_support_with(expr, schema, is_datatype_supported)
+}
+
+/// Like [`check_support`], but additionally accepts decimal types. This is
+/// used when estimating filter statistics, where decimal predicates can be
+/// analyzed with interval arithmetic. Other users of [`check_support`], such
+/// as symmetric hash join pruning, do not support decimals yet.
+pub fn check_statistics_support(
+    expr: &Arc<dyn PhysicalExpr>,
+    schema: &SchemaRef,
+) -> bool {
+    check_support_with(expr, schema, |data_type| {
+        is_datatype_supported(data_type)
+            || matches!(

Review Comment:
   Decimal arithmetic can propagate bounds with the wrong source type. For `a: 
Decimal128(15,2)` with statistics `[1.00,10.00]`, `a * Decimal128(2.00,15,2) > 
Decimal128(5.0000,31,4)` makes `FilterExec::try_new` fail with `Selectivity is 
out of limit: 8324028856.826859` because inverse propagation retains scale 4; 
`a - 2.00 > 5.00` also publishes Decimal128(17,2) bounds for a Decimal128(15,2) 
column. Please restore source-typed bounds with conservative outward rounding, 
or exclude decimal arithmetic from statistics eligibility. Clamping selectivity 
alone is insufficient. Add regressions for multiplication 
construction/selectivity and subtraction min/max types.



##########
datafusion/physical-expr/src/expressions/cast.rs:
##########
@@ -953,6 +980,44 @@ mod tests {
         Ok(())
     }
 
+    #[test]

Review Comment:
   Optional follow-up: consider representative containment tests for negative 
scales, cross-width widening, and bounds near precision limits. The exhaustive 
test covers only Decimal128 scale 2 to 3 near zero, while the Decimal32 to 
Decimal128 case currently checks eligibility rather than boundary propagation.



##########
datafusion/physical-plan/src/filter.rs:
##########
@@ -2050,6 +2050,124 @@ mod tests {
         Ok(())
     }
 
+    #[tokio::test]
+    async fn test_filter_statistics_decimal_expr() -> Result<()> {
+        // Table:
+        //      a: Decimal128(15, 2), min=0.00, max=0.10
+        let decimal = |v: i128| ScalarValue::Decimal128(Some(v), 15, 2);
+        let schema =
+            Schema::new(vec![Field::new("a", DataType::Decimal128(15, 2), 
false)]);
+        let input = Arc::new(StatisticsExec::new(
+            Statistics {
+                num_rows: Precision::Inexact(1100),
+                total_byte_size: Precision::Absent,
+                column_statistics: vec![ColumnStatistics {
+                    min_value: Precision::Inexact(decimal(0)),
+                    max_value: Precision::Inexact(decimal(10)),
+                    ..Default::default()
+                }],
+            },
+            schema.clone(),
+        ));
+
+        // a >= 0.05 AND a < 0.08
+        let predicate: Arc<dyn PhysicalExpr> = binary(
+            binary(col("a", &schema)?, Operator::GtEq, lit(decimal(5)), 
&schema)?,
+            Operator::And,
+            binary(col("a", &schema)?, Operator::Lt, lit(decimal(8)), 
&schema)?,
+            &schema,
+        )?;
+
+        // WHERE a >= 0.05 AND a < 0.08
+        let filter: Arc<dyn ExecutionPlan> =
+            Arc::new(FilterExec::try_new(predicate, input)?);
+
+        // The strict bound tightens to 0.07, so [0.05, 0.07] keeps 3 of the 11
+        // representable values in [0.00, 0.10].
+        let statistics =
+            StatisticsContext::new().compute(filter.as_ref(), 
&StatisticsArgs::new())?;
+        assert_eq!(statistics.num_rows, Precision::Inexact(300));
+        assert_eq!(
+            statistics.column_statistics,
+            vec![ColumnStatistics {
+                null_count: Precision::Exact(0),
+                min_value: Precision::Inexact(decimal(5)),
+                max_value: Precision::Inexact(decimal(7)),
+                ..Default::default()
+            }]
+        );
+
+        Ok(())
+    }
+
+    #[tokio::test]
+    async fn test_filter_statistics_decimal_mixed_scale_expr() -> Result<()> {
+        // Table:
+        //      a: Decimal128(15, 2), min=0.00, max=0.10
+        let schema =
+            Schema::new(vec![Field::new("a", DataType::Decimal128(15, 2), 
false)]);
+        let input = Arc::new(StatisticsExec::new(
+            Statistics {

Review Comment:
   Optional follow-up: consider a SQLLogicTest for a Q6-style decimal range 
with ordinary fractional literals and known min/max statistics, asserting 
EXPLAIN estimates and query results. The current mixed-scale test constructs 
the cast explicitly, so it does not cover SQL coercion through physical 
expression construction to statistics estimation.



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