1use sqlparser::ast::{self, Select};
7
8use super::array_arm;
9use super::constraint::extract_join_spec;
10use crate::error::{Result, SqlError};
11use crate::functions::registry::FunctionRegistry;
12use crate::planner::lateral::plan::{
13 LateralJoinArgs, is_lateral_derived, lateral_alias_from_factor, plan_lateral_join,
14 subquery_from_factor,
15};
16use crate::resolver::columns::TableScope;
17use crate::types::*;
18
19pub fn plan_join_from_select(
20 select: &Select,
21 scope: &TableScope,
22 catalog: &dyn SqlCatalog,
23 functions: &FunctionRegistry,
24 temporal: crate::TemporalScope,
25) -> Result<Option<SqlPlan>> {
26 let from = &select.from[0];
27
28 let left_plan =
30 if let Some(plan) = array_arm::try_plan_relation(&from.relation, catalog, temporal)? {
31 plan
32 } else {
33 scan_for_relation(&from.relation, scope)?
34 };
35
36 let outer_alias = scan_alias_from_relation(&from.relation);
37
38 let mut current_plan = left_plan;
39
40 for join_item in &from.joins {
41 if is_lateral_derived(&join_item.relation) {
43 let lateral_alias =
44 lateral_alias_from_factor(&join_item.relation).ok_or_else(|| {
45 SqlError::Unsupported {
46 detail: "LATERAL subquery requires an alias (e.g. LATERAL (...) AS x)"
47 .into(),
48 }
49 })?;
50 let subquery = subquery_from_factor(&join_item.relation)
51 .expect("is_lateral_derived guarantees Derived variant");
52 let left_join = is_left_join_operator(&join_item.join_operator);
53 let projection = super::super::select::convert_projection(&select.projection)?;
54 return Ok(Some(plan_lateral_join(LateralJoinArgs {
55 outer_plan: current_plan,
56 outer_alias,
57 subquery,
58 lateral_alias: &lateral_alias,
59 left_join,
60 outer_projection: projection,
61 catalog,
62 temporal,
63 })?));
64 }
65
66 let right_plan = if let Some(plan) =
68 array_arm::try_plan_relation(&join_item.relation, catalog, temporal)?
69 {
70 plan
71 } else {
72 scan_for_relation(&join_item.relation, scope)?
73 };
74
75 let (join_type, mut on_keys, condition) = extract_join_spec(&join_item.join_operator)?;
76
77 let right_ids = right_side_identifiers(&join_item.relation);
83 super::constraint::orient_keys_to_sides(&mut on_keys, &right_ids);
84
85 current_plan = SqlPlan::Join {
86 left: Box::new(current_plan),
87 right: Box::new(right_plan),
88 on: on_keys,
89 join_type,
90 condition,
91 limit: None,
92 projection: Vec::new(),
93 filters: Vec::new(),
94 };
95 }
96
97 let (subquery_joins, effective_where) = if let Some(expr) = &select.selection {
98 let extraction =
99 super::super::subquery::extract_subqueries(expr, catalog, functions, temporal)?;
100 (extraction.joins, extraction.remaining_where)
101 } else {
102 (Vec::new(), None)
103 };
104
105 let projection = super::super::select::convert_projection(&select.projection)?;
106 let filters = match &effective_where {
107 Some(expr) => super::super::select::convert_where_to_filters(expr)?,
108 None => Vec::new(),
109 };
110
111 for sq in subquery_joins {
112 current_plan = SqlPlan::Join {
113 left: Box::new(current_plan),
114 right: Box::new(sq.inner_plan),
115 on: vec![(sq.outer_column, sq.inner_column)],
116 join_type: sq.join_type,
117 condition: None,
118 limit: None,
119 projection: Vec::new(),
120 filters: Vec::new(),
121 };
122 }
123
124 let group_by_non_empty = match &select.group_by {
125 ast::GroupByExpr::All(_) => true,
126 ast::GroupByExpr::Expressions(exprs, _) => !exprs.is_empty(),
127 };
128 if super::super::select::convert_projection(&select.projection).is_ok() && group_by_non_empty {
129 let aggregates = super::super::aggregate::extract_aggregates_from_projection(
130 &select.projection,
131 functions,
132 )?;
133 let group_by = super::super::aggregate::convert_group_by(&select.group_by)?;
134 let group_by_aliases =
135 super::super::aggregate::group_by_output_aliases(&select.projection, &group_by);
136 let output_order = super::super::aggregate_order::compute_output_order(
137 &select.projection,
138 &group_by,
139 functions,
140 )?;
141 let having = match &select.having {
142 Some(expr) => super::super::select::convert_where_to_filters(expr)?,
143 None => Vec::new(),
144 };
145 return Ok(Some(SqlPlan::Aggregate {
146 input: Box::new(current_plan),
147 group_by,
148 group_by_aliases,
149 output_order,
150 aggregates,
151 having,
152 limit: 10000,
153 grouping_sets: None,
154 sort_keys: Vec::new(),
155 }));
156 }
157
158 if let SqlPlan::Join {
159 projection: ref mut proj,
160 filters: ref mut filt,
161 ..
162 } = current_plan
163 {
164 *proj = projection;
165 *filt = filters;
166 }
167 Ok(Some(current_plan))
168}
169
170fn right_side_identifiers(factor: &ast::TableFactor) -> Vec<String> {
174 let mut ids = Vec::new();
175 if let Ok(Some((name, alias))) = crate::parser::normalize::table_name_from_factor(factor) {
176 if let Some(a) = alias {
177 ids.push(a);
178 }
179 ids.push(name);
180 }
181 ids
182}
183
184fn scan_alias_from_relation(factor: &ast::TableFactor) -> Option<String> {
186 match factor {
187 ast::TableFactor::Table { name, alias, .. } => alias
188 .as_ref()
189 .map(|a| crate::parser::normalize::normalize_ident(&a.name))
190 .or_else(|| crate::parser::normalize::normalize_object_name_checked(name).ok()),
191 _ => None,
192 }
193}
194
195fn is_left_join_operator(op: &ast::JoinOperator) -> bool {
197 matches!(
198 op,
199 ast::JoinOperator::Left(_) | ast::JoinOperator::LeftOuter(_)
200 )
201}
202
203fn scan_for_relation(rel: &ast::TableFactor, scope: &TableScope) -> Result<SqlPlan> {
205 let (rel_name, rel_alias) =
206 crate::parser::normalize::table_name_from_factor(rel)?.ok_or_else(|| {
207 SqlError::Unsupported {
208 detail: "non-table JOIN target".into(),
209 }
210 })?;
211 let table = scope
212 .tables
213 .values()
214 .find(|t| t.name == rel_name || t.alias.as_deref() == Some(&rel_name))
215 .ok_or_else(|| SqlError::UnknownTable {
216 name: rel_name.clone(),
217 })?;
218 Ok(SqlPlan::Scan {
219 collection: table.name.clone(),
220 alias: rel_alias.or_else(|| table.alias.clone()),
221 engine: table.info.engine,
222 filters: Vec::new(),
223 projection: Vec::new(),
224 sort_keys: Vec::new(),
225 limit: None,
226 offset: 0,
227 distinct: false,
228 window_functions: Vec::new(),
229 temporal: crate::temporal::TemporalScope::default(),
230 })
231}