Rewrites predicates that contain subqueries so that the subqueries appear in their own later predicate when possible. For example, this function rewrites this expression ```text Filter { predicates: [a = b AND EXISTS ( ) AND c = d AND ( ) = e] } ``` like so: ```text Filter { predicates: [ a = b AND c = d, EXISTS ( ), ( ) = e, ] } ``` The rewrite cause
(expr: &mut HirRelationExpr)
| 58 | /// d AND EXISTS(<subquery>)`. This can vastly reduce the cost of the |
| 59 | /// subquery, especially when the original conjunction contains join keys. |
| 60 | pub fn split_subquery_predicates(expr: &mut HirRelationExpr) -> Result<(), RecursionLimitError> { |
| 61 | fn walk_relation(expr: &mut HirRelationExpr) -> Result<(), RecursionLimitError> { |
| 62 | #[allow(deprecated)] |
| 63 | expr.visit_mut_fallible(0, &mut |expr, _| { |
| 64 | match expr { |
| 65 | HirRelationExpr::Map { scalars, .. } => { |
| 66 | for scalar in scalars { |
| 67 | walk_scalar(scalar)?; |
| 68 | } |
| 69 | } |
| 70 | HirRelationExpr::CallTable { exprs, .. } => { |
| 71 | for expr in exprs { |
| 72 | walk_scalar(expr)?; |
| 73 | } |
| 74 | } |
| 75 | HirRelationExpr::Filter { predicates, .. } => { |
| 76 | let mut subqueries = vec![]; |
| 77 | for predicate in &mut *predicates { |
| 78 | walk_scalar(predicate)?; |
| 79 | extract_conjuncted_subqueries(predicate, &mut subqueries)?; |
| 80 | } |
| 81 | // TODO(benesch): we could be smarter about the order in which |
| 82 | // we emit subqueries. At the moment we just emit in the order |
| 83 | // we discovered them, but ideally we'd emit them in an order |
| 84 | // that accounted for their cost/selectivity. E.g., low-cost, |
| 85 | // high-selectivity subqueries should go first. |
| 86 | for subquery in subqueries { |
| 87 | predicates.push(subquery); |
| 88 | } |
| 89 | } |
| 90 | _ => (), |
| 91 | } |
| 92 | Ok(()) |
| 93 | }) |
| 94 | } |
| 95 | |
| 96 | fn walk_scalar(expr: &mut HirScalarExpr) -> Result<(), RecursionLimitError> { |
| 97 | expr.try_visit_direct_subqueries_mut(&mut walk_relation) |
| 98 | } |
| 99 | |
| 100 | fn contains_subquery(expr: &HirScalarExpr) -> Result<bool, RecursionLimitError> { |
| 101 | let mut found = false; |
| 102 | expr.try_visit_direct_subqueries(|_| { |
| 103 | found = true; |
| 104 | Ok(()) |
| 105 | })?; |
| 106 | Ok(found) |
| 107 | } |
| 108 | |
| 109 | /// Extracts subqueries from a conjunction into `out`. |
| 110 | /// |
| 111 | /// For example, given an expression like |
| 112 | /// |
| 113 | /// ```text |
| 114 | /// a = b AND EXISTS (<subquery 1>) AND c = d AND (<subquery 2>) = e |
| 115 | /// ``` |
| 116 | /// |
| 117 | /// this function rewrites the expression to |