diff --git a/vortex-file/src/file.rs b/vortex-file/src/file.rs index ded986f6210..23e2114b1c3 100644 --- a/vortex-file/src/file.rs +++ b/vortex-file/src/file.rs @@ -247,7 +247,13 @@ impl VortexFile { let mut ctx = self.session.create_execution_ctx(); Ok(match applied.execute::(&mut ctx)? { Columnar::Constant(s) => s.scalar().as_bool().value() == Some(true), - Columnar::Canonical(_) => false, + Columnar::Canonical(c) => { + c.into_array() + .execute_scalar(0, &mut ctx)? + .as_bool() + .value() + == Some(true) + } }) } diff --git a/vortex-file/src/tests.rs b/vortex-file/src/tests.rs index 2ba10d96684..e320cf2e9d9 100644 --- a/vortex-file/src/tests.rs +++ b/vortex-file/src/tests.rs @@ -35,6 +35,7 @@ use vortex_array::dtype::PType::I32; use vortex_array::dtype::StructFields; use vortex_array::expr::and; use vortex_array::expr::cast; +use vortex_array::expr::col; use vortex_array::expr::eq; use vortex_array::expr::get_item; use vortex_array::expr::gt; @@ -1953,3 +1954,39 @@ async fn test_segment_ordering_zonemaps_after_data() -> VortexResult<()> { Ok(()) } + +#[tokio::test] +#[cfg_attr(miri, ignore)] +async fn test_can_prune_composite_predicates() -> VortexResult<()> { + // Regression test for `can_prune` after `ScalarFnConstantRule` was removed + // (#7575): composite falsification trees no longer constant-fold during + // execution, so `can_prune` must read the one-row evaluated result instead + // of requiring a `Columnar::Constant`. `Eq` is affected too: its + // falsification is internally `or(min > lit, lit > max)`. + let st = StructArray::from_fields(&[ + ("age", buffer![15i32, 18, 22, 25].into_array()), + ("price", buffer![120i32, 130, 140, 150].into_array()), + ])?; + let mut buf = ByteBufferMut::empty(); + SESSION + .write_options() + .write(&mut buf, st.into_array().to_array_stream()) + .await?; + let file = SESSION.open_options().open_buffer(buf)?; + + // Bare comparisons: falsified directly by min/max stats. + assert!(file.can_prune(>(col("age"), lit(30)))?); + assert!(file.can_prune(<(col("price"), lit(100)))?); + + // Composite predicates whose falsifications are boolean trees. + assert!(file.can_prune(&and(gt(col("age"), lit(30)), lt(col("price"), lit(100))))?); + assert!(file.can_prune(&or(gt(col("age"), lit(30)), lt(col("age"), lit(10))))?); + assert!(file.can_prune(&eq(col("age"), lit(5)))?); + + // Non-falsifiable controls: rows may match, so pruning must refuse. + assert!(!file.can_prune(>(col("age"), lit(20)))?); + assert!(!file.can_prune(&eq(col("age"), lit(18)))?); + assert!(!file.can_prune(&and(gt(col("age"), lit(20)), gt(col("price"), lit(100))))?); + + Ok(()) +}