()
| 438 | |
| 439 | #[test] |
| 440 | fn test_deeply_nested_not() -> Result<()> { |
| 441 | let schema = not_test_schema(); |
| 442 | let simplifier = PhysicalExprSimplifier::new(&schema); |
| 443 | |
| 444 | // Create a deeply nested NOT expression: NOT(NOT(NOT(...NOT(c > 5)...))) |
| 445 | // This tests that we don't get stack overflow with many nested NOTs. |
| 446 | // With recursive_protection enabled (default), this should work by |
| 447 | // automatically growing the stack as needed. |
| 448 | let inner_expr: Arc<dyn PhysicalExpr> = Arc::new(BinaryExpr::new( |
| 449 | col("c", &schema)?, |
| 450 | Operator::Gt, |
| 451 | lit(ScalarValue::Int32(Some(5))), |
| 452 | )); |
| 453 | |
| 454 | let mut expr = Arc::clone(&inner_expr); |
| 455 | // Create 200 layers of NOT to test deep recursion handling |
| 456 | for _ in 0..200 { |
| 457 | expr = Arc::new(NotExpr::new(expr)); |
| 458 | } |
| 459 | |
| 460 | // With 200 NOTs (even number), should simplify back to the original expression |
| 461 | let expected = inner_expr; |
| 462 | assert_not_simplify(&simplifier, Arc::clone(&expr), expected); |
| 463 | |
| 464 | // Manually dismantle the deep input expression to avoid Stack Overflow on Drop |
| 465 | // If we just let `expr` go out of scope, Rust's recursive Drop will blow the stack |
| 466 | // even with recursive_protection, because Drop doesn't use the #[recursive] attribute. |
| 467 | // We peel off layers one by one to avoid deep recursion in Drop. |
| 468 | while let Some(not_expr) = expr.downcast_ref::<NotExpr>() { |
| 469 | // Clone the child (Arc increment). |
| 470 | // Now child has 2 refs: one in parent, one in `child`. |
| 471 | let child = Arc::clone(not_expr.arg()); |
| 472 | |
| 473 | // Reassign `expr` to `child`. |
| 474 | // This drops the old `expr` (Parent). |
| 475 | // Parent refcount -> 0, Parent is dropped. |
| 476 | // Parent drops its reference to Child. |
| 477 | // Child refcount decrements 2 -> 1. |
| 478 | // Child is NOT dropped recursively because we still hold it in `expr` |
| 479 | expr = child; |
| 480 | } |
| 481 | |
| 482 | Ok(()) |
| 483 | } |
| 484 | |
| 485 | #[test] |
| 486 | fn test_simplify_literal_binary_expr() { |
nothing calls this directly
no test coverage detected
searching dependent graphs…