(
outer_plan: SqlPlan,
outer_alias: Option<String>,
subquery: &ast::Query,
lateral_alias: &str,
left_join: bool,
outer_projection: Vec<Projection>,
catalog: &dyn SqlCatalog
| 31 | /// `outer_projection` — SELECT list projection to apply after the lateral. |
| 32 | #[allow(clippy::too_many_arguments)] |
| 33 | pub fn plan_lateral_join( |
| 34 | outer_plan: SqlPlan, |
| 35 | outer_alias: Option<String>, |
| 36 | subquery: &ast::Query, |
| 37 | lateral_alias: &str, |
| 38 | left_join: bool, |
| 39 | outer_projection: Vec<Projection>, |
| 40 | catalog: &dyn SqlCatalog, |
| 41 | functions: &FunctionRegistry, |
| 42 | temporal: TemporalScope, |
| 43 | ) -> Result<SqlPlan> { |
| 44 | let select = match subquery.body.as_ref() { |
| 45 | sqlparser::ast::SetExpr::Select(s) => s, |
| 46 | _ => { |
| 47 | return Err(SqlError::Unsupported { |
| 48 | detail: "LATERAL subquery body must be a SELECT".into(), |
| 49 | }); |
| 50 | } |
| 51 | }; |
| 52 | |
| 53 | let outer_alias_str = outer_alias.as_deref().unwrap_or("").to_string(); |
| 54 | |
| 55 | let analysis = analyse_lateral_where(subquery, &outer_alias_str); |
| 56 | |
| 57 | // Determine if this is the equi-correlated + TopK shape: |
| 58 | // - At least one equi-key correlation. |
| 59 | // - A LIMIT k on the subquery. |
| 60 | // - No non-equi correlations (those require LateralLoop). |
| 61 | let has_equi = !analysis.equi_keys.is_empty(); |
| 62 | let inner_limit = limit_from_query(subquery); |
| 63 | let is_top_k = has_equi && inner_limit.is_some() && analysis.non_equi.is_empty(); |
| 64 | |
| 65 | if is_top_k { |
| 66 | plan_lateral_top_k( |
| 67 | outer_plan, |
| 68 | outer_alias, |
| 69 | select, |
| 70 | subquery, |
| 71 | analysis.equi_keys, |
| 72 | inner_limit.expect("checked above"), |
| 73 | lateral_alias, |
| 74 | left_join, |
| 75 | outer_projection, |
| 76 | ) |
| 77 | } else if has_equi && analysis.non_equi.is_empty() { |
| 78 | // Equi-correlated, no LIMIT: rewrite as a regular hash join. |
| 79 | let inner_plan = |
| 80 | crate::planner::select::plan_query(subquery, catalog, functions, temporal)?; |
| 81 | let equi_on: Vec<(String, String)> = analysis |
| 82 | .equi_keys |
| 83 | .into_iter() |
| 84 | .map(|c| (c.outer_col, c.inner_col)) |
| 85 | .collect(); |
| 86 | Ok(SqlPlan::Join { |
| 87 | left: Box::new(outer_plan), |
| 88 | right: Box::new(inner_plan), |
| 89 | on: equi_on, |
| 90 | join_type: if left_join { |
no test coverage detected