1use rudb_catalog::{Catalog, Entry, QualifiedName, same_name};
16use rudb_common::{Error, Field, LogicalType, Result, Value};
17use rudb_functions::{
18 Columns, TableFunction, csv_fields, files, is_file, is_pattern, parquet_fields, resolve,
19 resolve_table,
20};
21use rudb_parse::ast::{self, Ast, Distinct, LiteralKind, Nulls, Order, Quantifier, SetOp};
22use rudb_parse::{NONE, parse_ast};
23use rudb_plan::{ColumnBinding, Expr, ExprRef, JoinKind, Node, NodeRef, Plan, SetOpKind, SortKey};
24
25use crate::expr::{describe, has_aggregate};
26use crate::parameters::Parameters;
27use crate::scope::{Scope, Visible};
28
29pub fn bind(ast: &Ast, catalog: &Catalog) -> Result<Plan> {
36 bind_with(ast, catalog, &Parameters::new())
37}
38
39pub fn bind_with(ast: &Ast, catalog: &Catalog, parameters: &Parameters) -> Result<Plan> {
45 let query = match ast.statements.as_slice() {
46 [ast::Statement::Query(query)] => *query,
47 [] => return Err(Error::binder("no statement to bind")),
48 _ => return Err(Error::not_implemented("a script of more than one statement")),
49 };
50 let mut binder = Binder::with(catalog, parameters);
51 let (root, _) = binder.bind_query(ast, query)?;
52 let mut plan = binder.into_plan();
53 plan.set_root(root);
54 plan.validate()?;
55 Ok(plan)
56}
57
58pub fn bind_sql(query: &str, catalog: &Catalog) -> Result<Plan> {
64 let ast = parse_ast(query)?;
65 bind(&ast, catalog)
66}
67
68#[derive(Debug)]
70pub(crate) struct Aggregation {
71 pub(crate) index: u32,
73 pub(crate) groups: Vec<ExprRef>,
75 pub(crate) aggregates: Vec<ExprRef>,
77}
78
79#[derive(Debug)]
81pub(crate) struct Binder<'a> {
82 catalog: &'a Catalog,
83 pub(crate) parameters: &'a Parameters,
85 plan: Plan,
86 next_index: u32,
87 pub(crate) aggregation: Option<Aggregation>,
89 pub(crate) in_aggregate: bool,
91 pub(crate) clause: &'static str,
93 expanding: Vec<String>,
95}
96
97impl<'a> Binder<'a> {
98 pub(crate) fn with(catalog: &'a Catalog, parameters: &'a Parameters) -> Self {
99 Self {
100 catalog,
101 parameters,
102 plan: Plan::new(),
103 next_index: 0,
104 aggregation: None,
105 in_aggregate: false,
106 clause: "SELECT clause",
107 expanding: Vec::new(),
108 }
109 }
110
111 pub(crate) fn plan(&self) -> &Plan {
112 &self.plan
113 }
114
115 pub(crate) fn plan_mut(&mut self) -> &mut Plan {
116 &mut self.plan
117 }
118
119 pub(crate) fn into_plan(self) -> Plan {
120 self.plan
121 }
122
123 pub(crate) fn fresh_index(&mut self) -> u32 {
125 let index = self.next_index;
126 self.next_index += 1;
127 index
128 }
129
130 fn column(&mut self, index: u32, position: usize, ty: LogicalType) -> ExprRef {
132 let binding = ColumnBinding::new(index, position as u32);
133 self.plan.add_expr(Expr::Column(binding), ty)
134 }
135
136 pub(crate) fn bind_query(
139 &mut self,
140 ast: &Ast,
141 query: ast::QueryRef,
142 ) -> Result<(NodeRef, Scope)> {
143 let written = ast.query(query);
144 match written.body {
145 ast::QueryBody::Select(select) => self.bind_select(ast, select, &written),
146 ast::QueryBody::SetOp { op, quantifier, by_name, left, right } => {
147 if by_name {
148 return Err(Error::not_implemented("UNION BY NAME"));
149 }
150 self.bind_set_op(ast, &written, op, quantifier, left, right)
151 }
152 ast::QueryBody::Values(rows) => self.bind_values(ast, &written, rows),
153 }
154 }
155
156 fn bind_values(
163 &mut self,
164 ast: &Ast,
165 query: &ast::Query,
166 rows: ast::Slice,
167 ) -> Result<(NodeRef, Scope)> {
168 let written = ast.rows(rows).to_vec();
169 let Some(first) = written.first() else {
170 return Err(Error::binder("VALUES needs at least one row"));
171 };
172 let width = first.len as usize;
173 for (at, row) in written.iter().enumerate() {
174 if row.len as usize != width {
175 return Err(Error::binder(format!(
176 "VALUES lists must all be the same length, expected {width} columns but row {} has {}",
177 at + 1,
178 row.len
179 )));
180 }
181 }
182 let empty = Scope::empty();
184 let previous = std::mem::replace(&mut self.clause, "VALUES clause");
185 let mut bound: Vec<Vec<ExprRef>> = Vec::with_capacity(written.len());
186 for row in &written {
187 let mut items = Vec::with_capacity(width);
188 for &expr in ast.expr_list(*row) {
189 items.push(self.bind_expr(ast, expr, &empty)?);
190 }
191 bound.push(items);
192 }
193 self.clause = previous;
194 let mut types = Vec::with_capacity(width);
195 for at in 0..width {
196 let mut ty = self.plan.expr_type(bound[0][at]).clone();
197 for row in &bound[1..] {
198 let other = self.plan.expr_type(row[at]).clone();
199 ty = ty.promote(&other).ok_or_else(|| {
200 Error::binder(format!(
201 "Cannot combine a value of type {ty} with a value of type {other} in column {} of a VALUES",
202 at + 1
203 ))
204 })?;
205 }
206 types.push(ty);
207 }
208 let mut slices = Vec::with_capacity(bound.len());
209 for row in &bound {
210 let items: Vec<ExprRef> =
211 row.iter().zip(&types).map(|(&expr, ty)| self.cast_to(expr, ty)).collect();
212 slices.push(self.plan.add_expr_list(&items));
213 }
214 let rows = self.plan.add_rows(&slices);
215 let fields: Vec<Field> = types
216 .iter()
217 .enumerate()
218 .map(|(at, ty)| Field::new(format!("col{at}"), ty.clone()))
219 .collect();
220 let columns = self.plan.add_fields(&fields);
221 let index = self.fresh_index();
222 let mut node = self.plan.add_node(Node::Values { index, columns, rows });
223 let mut scope = Scope::empty();
224 for (at, field) in fields.iter().enumerate() {
225 scope.push(Visible {
226 table: String::new(),
227 name: field.name.clone(),
228 binding: ColumnBinding::new(index, at as u32),
229 ty: field.ty.clone(),
230 });
231 }
232 let keys = self.sort_keys(ast, query, &scope, &[])?;
233 if !keys.is_empty() {
234 let keys = self.plan.add_sort_keys(&keys);
235 node = self.plan.add_node(Node::Sort { input: node, keys });
236 }
237 node = self.apply_limit(ast, query, node)?;
238 Ok((node, scope))
239 }
240
241 fn bind_set_op(
242 &mut self,
243 ast: &Ast,
244 query: &ast::Query,
245 op: SetOp,
246 quantifier: Quantifier,
247 left: ast::QueryRef,
248 right: ast::QueryRef,
249 ) -> Result<(NodeRef, Scope)> {
250 let (left_node, left_scope) = self.bind_query(ast, left)?;
251 let (right_node, right_scope) = self.bind_query(ast, right)?;
252 if left_scope.len() != right_scope.len() {
253 return Err(Error::binder(format!(
254 "Set operations can only apply to expressions with the same number of result columns, but left side has {} and right side has {}",
255 left_scope.len(),
256 right_scope.len()
257 )));
258 }
259 let mut types = Vec::with_capacity(left_scope.len());
261 for (left, right) in left_scope.columns.iter().zip(&right_scope.columns) {
262 let common = left.ty.promote(&right.ty).ok_or_else(|| {
263 Error::binder(format!(
264 "Cannot combine a column of type {} with a column of type {} in a set operation",
265 left.ty, right.ty
266 ))
267 })?;
268 types.push(common);
269 }
270 let left_node = self.conform(left_node, &left_scope, &types);
271 let right_node = self.conform(right_node, &right_scope, &types);
272 let index = self.fresh_index();
273 let kind = match op {
274 SetOp::Union => SetOpKind::Union,
275 SetOp::Except => SetOpKind::Except,
276 SetOp::Intersect => SetOpKind::Intersect,
277 };
278 let all = quantifier == Quantifier::All;
281 let mut node = self.plan.add_node(Node::SetOp {
282 left: left_node,
283 right: right_node,
284 kind,
285 all,
286 index,
287 });
288 let mut scope = Scope::empty();
289 for (at, (column, ty)) in left_scope.columns.iter().zip(&types).enumerate() {
290 scope.push(Visible {
291 table: String::new(),
292 name: column.name.clone(),
293 binding: ColumnBinding::new(index, at as u32),
294 ty: ty.clone(),
295 });
296 }
297 let keys = self.sort_keys(ast, query, &scope, &[])?;
301 if !keys.is_empty() {
302 let keys = self.plan.add_sort_keys(&keys);
303 node = self.plan.add_node(Node::Sort { input: node, keys });
304 }
305 node = self.apply_limit(ast, query, node)?;
306 Ok((node, scope))
307 }
308
309 fn conform(&mut self, node: NodeRef, scope: &Scope, types: &[LogicalType]) -> NodeRef {
311 if scope.columns.iter().zip(types).all(|(column, ty)| &column.ty == ty) {
312 return node;
313 }
314 let index = self.fresh_index();
315 let mut exprs = Vec::with_capacity(types.len());
316 let mut names = Vec::with_capacity(types.len());
317 for (column, ty) in scope.columns.iter().zip(types) {
318 let expr = self.plan.add_expr(Expr::Column(column.binding), column.ty.clone());
319 exprs.push(self.cast_to(expr, ty));
320 names.push(self.plan.intern(&column.name));
321 }
322 let exprs = self.plan.add_expr_list(&exprs);
323 let names = self.plan.add_name_list(&names);
324 self.plan.add_node(Node::Project { input: node, index, exprs, names })
325 }
326
327 fn bind_select(
330 &mut self,
331 ast: &Ast,
332 select: ast::SelectRef,
333 query: &ast::Query,
334 ) -> Result<(NodeRef, Scope)> {
335 let written = ast.select(select);
336 let (mut node, input) = self.bind_from(ast, written.from)?;
337
338 if written.filter != NONE {
339 self.clause = "WHERE clause";
340 let predicate = self.bind_expr(ast, written.filter, &input)?;
341 let predicate = self.as_boolean(predicate, "WHERE")?;
342 node = self.plan.add_node(Node::Filter { input: node, predicate });
343 }
344
345 let targets = ast.target_list(written.targets).to_vec();
346 if targets.is_empty() {
347 return Err(Error::binder("a SELECT needs at least one expression to select"));
348 }
349
350 let group_items = self.group_items(ast, &written, &targets)?;
351 let aggregating = !group_items.is_empty()
352 || written.having != NONE
353 || targets.iter().any(|target| has_aggregate(ast, target.expr));
354 if aggregating {
355 self.clause = "GROUP BY clause";
356 let mut groups = Vec::with_capacity(group_items.len());
357 for item in &group_items {
358 groups.push(self.bind_expr(ast, *item, &input)?);
359 }
360 let index = self.fresh_index();
361 self.aggregation = Some(Aggregation { index, groups, aggregates: Vec::new() });
362 }
363
364 self.clause = "SELECT clause";
365 let (mut exprs, mut names) = self.bind_targets(ast, &targets, &input)?;
366 let visible = exprs.len();
367
368 let mut having = None;
369 if written.having != NONE {
370 self.clause = "HAVING clause";
371 let predicate = self.bind_expr(ast, written.having, &input)?;
372 let predicate = self.over_aggregate(predicate, &input)?;
373 having = Some(self.as_boolean(predicate, "HAVING")?);
374 }
375
376 let project = self.fresh_index();
379 let mut output = Scope::empty();
380 for (at, (expr, name)) in exprs.iter().zip(&names).enumerate() {
381 output.push(Visible {
382 table: String::new(),
383 name: name.clone(),
384 binding: ColumnBinding::new(project, at as u32),
385 ty: self.plan.expr_type(*expr).clone(),
386 });
387 }
388
389 self.clause = "ORDER BY clause";
390 let mut extra = Vec::new();
391 let keys = self.select_sort_keys(
392 ast, query, &input, &output, project, &mut exprs, &mut names, &mut extra,
393 )?;
394 if !extra.is_empty() && written.distinct != Distinct::No {
395 return Err(Error::binder(
396 "For SELECT DISTINCT, ORDER BY expressions must appear in the select list",
397 ));
398 }
399 let on = self.distinct_on(ast, written.distinct, &output)?;
400
401 if let Some(aggregation) = self.aggregation.take() {
402 let index = aggregation.index;
403 let groups = self.plan.add_expr_list(&aggregation.groups);
404 let aggregates = self.plan.add_expr_list(&aggregation.aggregates);
405 node = self.plan.add_node(Node::Aggregate { input: node, index, groups, aggregates });
406 }
407 if let Some(predicate) = having {
408 node = self.plan.add_node(Node::Filter { input: node, predicate });
409 }
410
411 let interned: Vec<u32> = names.iter().map(|name| self.plan.intern(name)).collect();
412 let exprs_slice = self.plan.add_expr_list(&exprs);
413 let names_slice = self.plan.add_name_list(&interned);
414 node = self.plan.add_node(Node::Project {
415 input: node,
416 index: project,
417 exprs: exprs_slice,
418 names: names_slice,
419 });
420
421 if written.distinct != Distinct::No {
422 let on = self.plan.add_expr_list(&on);
423 node = self.plan.add_node(Node::Distinct { input: node, on });
424 }
425 if !keys.is_empty() {
426 let keys = self.plan.add_sort_keys(&keys);
427 node = self.plan.add_node(Node::Sort { input: node, keys });
428 }
429 node = self.apply_limit(ast, query, node)?;
430
431 if extra.is_empty() {
432 output.columns.truncate(visible);
433 return Ok((node, output));
434 }
435 let index = self.fresh_index();
438 let mut kept = Vec::with_capacity(visible);
439 let mut kept_names = Vec::with_capacity(visible);
440 let mut scope = Scope::empty();
441 for (at, name) in names.iter().enumerate().take(visible) {
442 let ty = output.columns[at].ty.clone();
443 kept.push(self.column(project, at, ty.clone()));
444 kept_names.push(self.plan.intern(name));
445 scope.push(Visible {
446 table: String::new(),
447 name: name.clone(),
448 binding: ColumnBinding::new(index, at as u32),
449 ty,
450 });
451 }
452 let exprs = self.plan.add_expr_list(&kept);
453 let names = self.plan.add_name_list(&kept_names);
454 node = self.plan.add_node(Node::Project { input: node, index, exprs, names });
455 Ok((node, scope))
456 }
457
458 fn bind_targets(
460 &mut self,
461 ast: &Ast,
462 targets: &[ast::Target],
463 input: &Scope,
464 ) -> Result<(Vec<ExprRef>, Vec<String>)> {
465 let mut exprs = Vec::with_capacity(targets.len());
466 let mut names = Vec::with_capacity(targets.len());
467 for target in targets {
468 if let ast::Expr::Star { qualifier } = ast.expr(target.expr) {
469 let table = ast.name(qualifier).last().map(str::to_string);
470 let expanded: Vec<Visible> =
471 input.star(table.as_deref())?.into_iter().cloned().collect();
472 for column in expanded {
473 let expr = self.plan.add_expr(Expr::Column(column.binding), column.ty);
474 exprs.push(self.over_aggregate(expr, input)?);
475 names.push(column.name);
476 }
477 continue;
478 }
479 let expr = self.bind_expr(ast, target.expr, input)?;
480 exprs.push(self.over_aggregate(expr, input)?);
481 names.push(if target.alias == NONE {
482 self.output_name(ast, target.expr, input)
483 } else {
484 ast.string(target.alias).to_string()
485 });
486 }
487 Ok((exprs, names))
488 }
489
490 fn output_name(&self, ast: &Ast, target: ast::ExprRef, input: &Scope) -> String {
496 if let ast::Expr::Column { name } = ast.expr(target) {
497 let parts: Vec<&str> = ast.name(name).collect();
498 if let Ok(found) = input.resolve(&parts) {
499 return found.name.clone();
500 }
501 }
502 describe(ast, target)
503 }
504
505 fn group_items(
507 &self,
508 ast: &Ast,
509 select: &ast::Select,
510 targets: &[ast::Target],
511 ) -> Result<Vec<ast::ExprRef>> {
512 if select.group_by_all {
513 return Ok(targets
516 .iter()
517 .filter(|target| !has_aggregate(ast, target.expr))
518 .map(|target| target.expr)
519 .collect());
520 }
521 let mut items = Vec::new();
522 for &item in ast.expr_list(select.group_by) {
523 items.push(self.output_reference(ast, item, targets, "GROUP BY")?.unwrap_or(item));
524 }
525 Ok(items)
526 }
527
528 fn output_reference(
530 &self,
531 ast: &Ast,
532 item: ast::ExprRef,
533 targets: &[ast::Target],
534 clause: &str,
535 ) -> Result<Option<ast::ExprRef>> {
536 match ast.expr(item) {
537 ast::Expr::Literal { kind: LiteralKind::Number, text } => {
538 let written = ast.string(text);
539 let position: usize = written.parse().map_err(|_| {
540 Error::binder(format!("{clause} term {written} is not a column"))
541 })?;
542 if position == 0 || position > targets.len() {
543 return Err(Error::binder(format!(
544 "{clause} term out of range - should be between 1 and {}",
545 targets.len()
546 )));
547 }
548 Ok(Some(targets[position - 1].expr))
549 }
550 ast::Expr::Column { name } => {
551 let parts: Vec<&str> = ast.name(name).collect();
552 let [written] = parts.as_slice() else { return Ok(None) };
553 let mut found = None;
554 for target in targets {
555 if target.alias != NONE && same_name(ast.string(target.alias), written) {
556 if found.is_some() {
557 return Ok(None);
558 }
559 found = Some(target.expr);
560 }
561 }
562 Ok(found)
563 }
564 _ => Ok(None),
565 }
566 }
567
568 #[allow(clippy::too_many_arguments)]
572 fn select_sort_keys(
573 &mut self,
574 ast: &Ast,
575 query: &ast::Query,
576 input: &Scope,
577 output: &Scope,
578 project: u32,
579 exprs: &mut Vec<ExprRef>,
580 names: &mut Vec<String>,
581 extra: &mut Vec<usize>,
582 ) -> Result<Vec<SortKey>> {
583 if query.order_by_all {
584 return Ok(self.every_column(output));
585 }
586 let items = ast.order_list(query.order_by).to_vec();
587 let mut keys = Vec::with_capacity(items.len());
588 for item in items {
589 let position = match self.output_position(ast, item.expr, output)? {
590 Some(position) => position,
591 None => {
592 let bound = self.bind_expr(ast, item.expr, input)?;
593 let bound = self.over_aggregate(bound, input)?;
594 match exprs.iter().position(|&held| self.same_expr(held, bound)) {
595 Some(position) => position,
596 None => {
597 exprs.push(bound);
598 names.push(describe(ast, item.expr));
599 extra.push(exprs.len() - 1);
600 exprs.len() - 1
601 }
602 }
603 }
604 };
605 let ty = self.plan.expr_type(exprs[position]).clone();
606 let expr = self.column(project, position, ty);
607 keys.push(sort_key(expr, item));
608 }
609 Ok(keys)
610 }
611
612 fn sort_keys(
614 &mut self,
615 ast: &Ast,
616 query: &ast::Query,
617 output: &Scope,
618 targets: &[ast::Target],
619 ) -> Result<Vec<SortKey>> {
620 if query.order_by_all {
621 return Ok(self.every_column(output));
622 }
623 let items = ast.order_list(query.order_by).to_vec();
624 let mut keys = Vec::with_capacity(items.len());
625 for item in items {
626 let expr = match self.output_position(ast, item.expr, output)? {
627 Some(position) => {
628 let column = &output.columns[position];
629 let (binding, ty) = (column.binding, column.ty.clone());
630 self.plan.add_expr(Expr::Column(binding), ty)
631 }
632 None => {
633 let _ = targets;
634 self.bind_expr(ast, item.expr, output)?
635 }
636 };
637 keys.push(sort_key(expr, item));
638 }
639 Ok(keys)
640 }
641
642 fn every_column(&mut self, output: &Scope) -> Vec<SortKey> {
643 let columns: Vec<(ColumnBinding, LogicalType)> =
644 output.columns.iter().map(|column| (column.binding, column.ty.clone())).collect();
645 columns
646 .into_iter()
647 .map(|(binding, ty)| {
648 let expr = self.plan.add_expr(Expr::Column(binding), ty);
649 SortKey { expr, descending: false, nulls_first: false }
650 })
651 .collect()
652 }
653
654 fn output_position(
656 &self,
657 ast: &Ast,
658 item: ast::ExprRef,
659 output: &Scope,
660 ) -> Result<Option<usize>> {
661 match ast.expr(item) {
662 ast::Expr::Literal { kind: LiteralKind::Number, text } => {
663 let written = ast.string(text);
664 if written.contains(['.', 'e', 'E']) {
665 return Ok(None);
666 }
667 let position: usize = written.parse().map_err(|_| {
668 Error::binder(format!("ORDER BY term {written} is not a column"))
669 })?;
670 if position == 0 || position > output.len() {
671 return Err(Error::binder(format!(
672 "ORDER BY term out of range - should be between 1 and {}",
673 output.len()
674 )));
675 }
676 Ok(Some(position - 1))
677 }
678 ast::Expr::Column { name } => {
679 let parts: Vec<&str> = ast.name(name).collect();
680 let [written] = parts.as_slice() else { return Ok(None) };
681 Ok(output.position_of(None, written))
682 }
683 _ => Ok(None),
684 }
685 }
686
687 fn distinct_on(
689 &mut self,
690 ast: &Ast,
691 distinct: Distinct,
692 output: &Scope,
693 ) -> Result<Vec<ExprRef>> {
694 let Distinct::On(items) = distinct else {
695 return Ok(Vec::new());
696 };
697 let items = ast.expr_list(items).to_vec();
698 let mut on = Vec::with_capacity(items.len());
699 for item in items {
700 let Some(position) = self.output_position(ast, item, output)? else {
701 return Err(Error::not_implemented(
702 "DISTINCT ON an expression that is not in the select list",
703 ));
704 };
705 let column = &output.columns[position];
706 let (binding, ty) = (column.binding, column.ty.clone());
707 on.push(self.plan.add_expr(Expr::Column(binding), ty));
708 }
709 Ok(on)
710 }
711
712 fn apply_limit(&mut self, ast: &Ast, query: &ast::Query, input: NodeRef) -> Result<NodeRef> {
713 if query.limit_percent {
714 return Err(Error::not_implemented("LIMIT with a percentage"));
715 }
716 let count = self.constant_count(ast, query.limit, "LIMIT")?;
717 let offset = self.constant_count(ast, query.offset, "OFFSET")?.unwrap_or(0);
718 if count.is_none() && offset == 0 {
719 return Ok(input);
720 }
721 Ok(self.plan.add_node(Node::Limit { input, count, offset }))
722 }
723
724 fn constant_count(
726 &mut self,
727 ast: &Ast,
728 written: ast::ExprRef,
729 clause: &str,
730 ) -> Result<Option<u64>> {
731 if written == NONE {
732 return Ok(None);
733 }
734 self.clause = "LIMIT clause";
735 let scope = Scope::empty();
736 let bound = self.bind_expr(ast, written, &scope)?;
737 let Expr::Constant(value) = *self.plan.expr(bound) else {
738 return Err(Error::not_implemented(format!("a {clause} that is not a constant")));
739 };
740 let count = match self.plan.value(value) {
741 Value::Null => return Ok(None),
742 Value::TinyInt(count) => i128::from(*count),
743 Value::SmallInt(count) => i128::from(*count),
744 Value::Integer(count) => i128::from(*count),
745 Value::BigInt(count) => i128::from(*count),
746 Value::HugeInt(count) => *count,
747 other => {
748 return Err(Error::binder(format!(
749 "{clause} takes a whole number of rows, not a value of type {}",
750 other.logical_type()
751 )));
752 }
753 };
754 u64::try_from(count)
755 .map(Some)
756 .map_err(|_| Error::binder(format!("{clause} must not be negative")))
757 }
758
759 fn bind_from(&mut self, ast: &Ast, from: ast::Slice) -> Result<(NodeRef, Scope)> {
762 let sources = ast.source_list(from).to_vec();
763 let Some((first, rest)) = sources.split_first() else {
764 return Ok((self.plan.add_node(Node::Dummy), Scope::empty()));
767 };
768 let (mut node, mut scope) = self.bind_source(ast, *first)?;
769 for source in rest {
770 let (right, right_scope) = self.bind_source(ast, *source)?;
771 node = self.plan.add_node(Node::CrossProduct { left: node, right });
772 scope = scope.concat(right_scope);
773 }
774 Ok((node, scope))
775 }
776
777 fn bind_source(&mut self, ast: &Ast, source: ast::SourceRef) -> Result<(NodeRef, Scope)> {
778 match ast.source(source) {
779 ast::Source::Table { name, alias, columns } => {
780 self.bind_table(ast, name, alias, columns)
781 }
782 ast::Source::Function { name, args, alias, columns } => {
783 self.bind_table_function(ast, name, args, alias, columns)
784 }
785 ast::Source::Subquery { query, alias, columns } => {
786 let (node, mut scope) = self.bind_query(ast, query)?;
787 let label = if alias == NONE {
788 "unnamed_subquery".to_string()
789 } else {
790 ast.string(alias).to_string()
791 };
792 scope.relabel(&label);
793 if !columns.is_empty() {
794 let names: Vec<&str> = ast.name(columns).collect();
795 scope.rename(&names, &label)?;
796 }
797 Ok((node, scope))
798 }
799 ast::Source::Values { rows, alias, columns } => {
800 let bare = ast::Query::bare(ast::QueryBody::Values(rows));
801 let (node, mut scope) = self.bind_values(ast, &bare, rows)?;
802 let label =
803 if alias == NONE { String::new() } else { ast.string(alias).to_string() };
804 scope.relabel(&label);
805 if !columns.is_empty() {
806 let names: Vec<&str> = ast.name(columns).collect();
807 scope.rename(&names, &label)?;
808 }
809 Ok((node, scope))
810 }
811 ast::Source::Join { left, right, kind, natural, on, using } => {
812 self.bind_join(ast, left, right, kind, natural, on, using)
813 }
814 }
815 }
816
817 fn bind_table(
818 &mut self,
819 ast: &Ast,
820 name: ast::Slice,
821 alias: ast::StrRef,
822 columns: ast::Slice,
823 ) -> Result<(NodeRef, Scope)> {
824 let parts: Vec<&str> = ast.name(name).collect();
825 let catalog = self.catalog;
826 let resolved = match catalog.resolve(&parts) {
829 Ok(resolved) => resolved,
830 Err(missing) => {
831 return self.bind_replacement_scan(ast, &parts, alias, columns, missing);
832 }
833 };
834 if catalog.entry(&resolved)? == Entry::View {
835 return self.bind_view(ast, &resolved, alias, columns);
836 }
837 let table = catalog.table(&resolved)?;
838 let fields: Vec<Field> = table.columns().to_vec();
839 let label =
840 if alias == NONE { resolved.table.clone() } else { ast.string(alias).to_string() };
841 let index = self.fresh_index();
842 let mut scope = Scope::empty();
843 for (at, field) in fields.iter().enumerate() {
844 scope.push(Visible {
845 table: label.clone(),
846 name: field.name.clone(),
847 binding: ColumnBinding::new(index, at as u32),
848 ty: field.ty.clone(),
849 });
850 }
851 if !columns.is_empty() {
852 let names: Vec<&str> = ast.name(columns).collect();
853 scope.rename(&names, &label)?;
854 }
855 let catalog_name = self.plan.intern(&resolved.catalog);
856 let schema = self.plan.intern(&resolved.schema);
857 let table_name = self.plan.intern(&resolved.table);
858 let alias = self.plan.intern(&label);
859 let columns = self.plan.add_fields(&fields);
860 let node = self.plan.add_node(Node::Get {
861 catalog: catalog_name,
862 schema,
863 table: table_name,
864 alias,
865 index,
866 columns,
867 });
868 Ok((node, scope))
869 }
870
871 fn bind_view(
883 &mut self,
884 ast: &Ast,
885 name: &QualifiedName,
886 alias: ast::StrRef,
887 columns: ast::Slice,
888 ) -> Result<(NodeRef, Scope)> {
889 let view = self.catalog.view(name)?;
890 let full = name.to_string();
891 if self.expanding.contains(&full) {
892 return Err(Error::binder(format!(
893 "infinite recursion detected: attempting to recursively bind view \"{}\"",
894 name.table
895 )));
896 }
897 let body = parse_ast(view.sql())?;
898 let query = match body.statements.as_slice() {
899 [ast::Statement::Query(query)] => *query,
900 _ => return Err(Error::binder(format!("view \"{}\" is not a query", name.table))),
903 };
904 self.expanding.push(full);
905 let bound = self.bind_query(&body, query);
906 self.expanding.pop();
907 let (node, mut scope) = bound?;
908
909 let aliases: Vec<&str> = view.aliases().iter().map(String::as_str).collect();
910 if !aliases.is_empty() {
911 scope.rename(&aliases, "unnamed_subquery")?;
912 }
913 let label = if alias == NONE { name.table.clone() } else { ast.string(alias).to_string() };
914 scope.relabel(&label);
915 if !columns.is_empty() {
916 let names: Vec<&str> = ast.name(columns).collect();
917 scope.rename(&names, &label)?;
918 }
919 Ok((node, scope))
920 }
921
922 fn bind_table_function(
930 &mut self,
931 ast: &Ast,
932 name: ast::Slice,
933 args: ast::Slice,
934 alias: ast::StrRef,
935 columns: ast::Slice,
936 ) -> Result<(NodeRef, Scope)> {
937 let parts: Vec<&str> = ast.name(name).collect();
938 let function_name = *parts.last().unwrap_or(&"");
942 if let Some(schema) = parts.iter().rev().nth(1) {
943 if !schema.eq_ignore_ascii_case("main") && !schema.eq_ignore_ascii_case("system") {
944 return Err(Error::catalog(format!(
945 "Table Function with name {} does not exist!",
946 parts.join(".")
947 )));
948 }
949 }
950 if TableFunction::lookup(function_name).is_none() {
954 return Err(Error::catalog(format!(
955 "Table Function with name {function_name} does not exist!"
956 )));
957 }
958 let written = ast.expr_list(args).to_vec();
959 let empty = Scope::empty();
960 let previous = std::mem::replace(&mut self.clause, "table function arguments");
961 let mut bound = Vec::with_capacity(written.len());
962 for expr in written {
963 bound.push(self.bind_expr(ast, expr, &empty)?);
964 }
965 self.clause = previous;
966
967 let given: Vec<LogicalType> =
970 bound.iter().map(|&expr| self.plan.expr_type(expr).clone()).collect();
971 let resolved = resolve_table(function_name, &given)?;
972 let mut cast: Vec<ExprRef> = bound
973 .iter()
974 .zip(&resolved.arguments)
975 .map(|(&expr, ty)| self.cast_to(expr, ty))
976 .collect();
977
978 let fields = match resolved.columns {
979 Columns::Fixed(fields) => fields,
980 columns => {
981 let paths = self.file_paths(cast[0], resolved.function.name())?;
986 let first = paths.first().map_or("", String::as_str);
987 let fields = match columns {
988 Columns::Csv => csv_fields(&paths)?,
991 _ => parquet_fields(first)?,
992 };
993 cast = paths.iter().map(|path| self.path_constant(path)).collect();
994 fields
995 }
996 };
997 let label = if alias == NONE {
998 resolved.function.name().to_string()
999 } else {
1000 ast.string(alias).to_string()
1001 };
1002 let names: Vec<&str> = ast.name(columns).collect();
1003 self.table_function_source(resolved.function, &cast, fields, &label, &names)
1004 }
1005
1006 fn bind_replacement_scan(
1017 &mut self,
1018 ast: &Ast,
1019 parts: &[&str],
1020 alias: ast::StrRef,
1021 columns: ast::Slice,
1022 missing: Error,
1023 ) -> Result<(NodeRef, Scope)> {
1024 let [path] = parts else { return Err(missing) };
1025 let path = *path;
1026 let extension = path.rsplit_once('.').map(|(_, after)| after).unwrap_or_default();
1027 let Some(function) = Self::reader_for(extension) else {
1028 if is_file(path) {
1029 return Err(Error::binder(format!(
1034 "No extension found that is capable of reading the file \"{path}\"\n* If this \
1035 file is a supported file format you can explicitly use the reader functions, \
1036 such as read_csv, read_json or read_parquet"
1037 )));
1038 }
1039 return Err(missing);
1040 };
1041 let paths = files(path)?;
1046 let first = paths.first().map_or("", String::as_str);
1047 let fields = match function {
1048 TableFunction::ReadParquet => parquet_fields(first)?,
1049 _ => csv_fields(&paths)?,
1050 };
1051 let label = if alias == NONE {
1057 if is_pattern(path) {
1058 path.to_string()
1059 } else {
1060 let file = path.rsplit_once('/').map_or(path, |(_, file)| file);
1061 file.rsplit_once('.').map_or(file, |(stem, _)| stem).to_string()
1062 }
1063 } else {
1064 ast.string(alias).to_string()
1065 };
1066 let arguments: Vec<ExprRef> = paths.iter().map(|path| self.path_constant(path)).collect();
1067 let names: Vec<&str> = ast.name(columns).collect();
1068 self.table_function_source(function, &arguments, fields, &label, &names)
1069 }
1070
1071 fn path_constant(&mut self, path: &str) -> ExprRef {
1073 let value = self.plan.add_value(Value::Varchar(path.to_string()));
1074 self.plan.add_expr(Expr::Constant(value), LogicalType::Varchar)
1075 }
1076
1077 fn reader_for(extension: &str) -> Option<TableFunction> {
1084 if extension.eq_ignore_ascii_case("parquet") {
1085 return Some(TableFunction::ReadParquet);
1086 }
1087 if extension.eq_ignore_ascii_case("csv") || extension.eq_ignore_ascii_case("tsv") {
1088 return Some(TableFunction::ReadCsv);
1089 }
1090 None
1091 }
1092
1093 fn table_function_source(
1098 &mut self,
1099 function: TableFunction,
1100 args: &[ExprRef],
1101 fields: Vec<Field>,
1102 label: &str,
1103 names: &[&str],
1104 ) -> Result<(NodeRef, Scope)> {
1105 let index = self.fresh_index();
1106 let mut scope = Scope::empty();
1107 for (at, field) in fields.iter().enumerate() {
1108 scope.push(Visible {
1109 table: label.to_string(),
1110 name: field.name.clone(),
1111 binding: ColumnBinding::new(index, at as u32),
1112 ty: field.ty.clone(),
1113 });
1114 }
1115 if !names.is_empty() {
1116 scope.rename(names, label)?;
1117 }
1118 let function = self.plan.intern(function.name());
1119 let args = self.plan.add_expr_list(args);
1120 let columns = self.plan.add_fields(&fields);
1121 let node = self.plan.add_node(Node::TableFunction { index, function, args, columns });
1122 Ok((node, scope))
1123 }
1124
1125 fn file_paths(&self, expr: ExprRef, name: &str) -> Result<Vec<String>> {
1132 let mut paths = Vec::new();
1133 for pattern in self.file_patterns(expr, name)? {
1134 paths.extend(files(&pattern)?);
1135 }
1136 Ok(paths)
1137 }
1138
1139 fn file_patterns(&self, expr: ExprRef, name: &str) -> Result<Vec<String>> {
1151 let Expr::Constant(reference) = *self.plan.expr(expr) else {
1152 return Err(Error::not_implemented(
1153 "a table function file name that is not a constant",
1154 ));
1155 };
1156 match self.plan.value(reference) {
1157 Value::Varchar(path) => Ok(vec![path.clone()]),
1158 Value::Null => Err(Error::parser(format!("{name} cannot take NULL list as parameter"))),
1160 Value::List { values, .. } => values
1161 .iter()
1162 .map(|value| match value {
1163 Value::Varchar(path) => Ok(path.clone()),
1164 _ => Err(Error::parser(format!(
1165 "{name} reader cannot take NULL input as parameter"
1166 ))),
1167 })
1168 .collect(),
1169 other => {
1170 Err(Error::internal(format!("a file name bound as VARCHAR arrived as {other}")))
1171 }
1172 }
1173 }
1174
1175 #[allow(clippy::too_many_arguments)]
1176 fn bind_join(
1177 &mut self,
1178 ast: &Ast,
1179 left: ast::SourceRef,
1180 right: ast::SourceRef,
1181 kind: ast::JoinKind,
1182 natural: bool,
1183 on: ast::ExprRef,
1184 using: ast::Slice,
1185 ) -> Result<(NodeRef, Scope)> {
1186 let (left_node, left_scope) = self.bind_source(ast, left)?;
1187 let (right_node, right_scope) = self.bind_source(ast, right)?;
1188 let split = left_scope.len();
1189 let mut scope = left_scope.concat(right_scope);
1190
1191 let merged: Vec<String> = if natural {
1194 let mut names = Vec::new();
1195 for (at, column) in scope.columns.iter().enumerate().take(split) {
1196 if scope.columns[split..].iter().any(|right| same_name(&right.name, &column.name))
1197 && !names.iter().any(|held: &String| same_name(held, &column.name))
1198 {
1199 let _ = at;
1200 names.push(column.name.clone());
1201 }
1202 }
1203 names
1204 } else {
1205 ast.name(using).map(str::to_string).collect()
1206 };
1207
1208 let mut conditions = Vec::new();
1209 let mut dropped = Vec::new();
1210 for name in &merged {
1211 let left_at = scope.columns[..split]
1212 .iter()
1213 .position(|column| same_name(&column.name, name))
1214 .ok_or_else(|| {
1215 Error::binder(format!(
1216 "column \"{name}\" specified in USING clause does not exist in left table"
1217 ))
1218 })?;
1219 let right_at = scope.columns[split..]
1220 .iter()
1221 .position(|column| same_name(&column.name, name))
1222 .map(|at| at + split)
1223 .ok_or_else(|| {
1224 Error::binder(format!(
1225 "column \"{name}\" specified in USING clause does not exist in right table"
1226 ))
1227 })?;
1228 let left_column = &scope.columns[left_at];
1229 let (left_binding, left_type) = (left_column.binding, left_column.ty.clone());
1230 let right_column = &scope.columns[right_at];
1231 let (right_binding, right_type) = (right_column.binding, right_column.ty.clone());
1232 let left_expr = self.plan.add_expr(Expr::Column(left_binding), left_type);
1233 let right_expr = self.plan.add_expr(Expr::Column(right_binding), right_type);
1234 conditions.push(self.compare(rudb_plan::CompareOp::Equal, left_expr, right_expr)?);
1235 dropped.push(right_at);
1236 }
1237 dropped.sort_unstable();
1240 for at in dropped.into_iter().rev() {
1241 scope.remove(at);
1242 }
1243
1244 if on != NONE {
1245 if !merged.is_empty() {
1246 return Err(Error::binder("a join cannot have both ON and USING"));
1247 }
1248 self.clause = "JOIN condition";
1249 let predicate = self.bind_expr(ast, on, &scope)?;
1250 conditions.push(self.as_boolean(predicate, "JOIN")?);
1251 }
1252
1253 if kind == ast::JoinKind::Cross {
1254 if !conditions.is_empty() {
1255 return Err(Error::binder("a CROSS JOIN cannot have a condition"));
1256 }
1257 let node =
1258 self.plan.add_node(Node::CrossProduct { left: left_node, right: right_node });
1259 return Ok((node, scope));
1260 }
1261 if conditions.is_empty() && kind == ast::JoinKind::Inner {
1262 let node =
1263 self.plan.add_node(Node::CrossProduct { left: left_node, right: right_node });
1264 return Ok((node, scope));
1265 }
1266 let kind = match kind {
1267 ast::JoinKind::Inner | ast::JoinKind::Cross => JoinKind::Inner,
1268 ast::JoinKind::Left => JoinKind::Left,
1269 ast::JoinKind::Right => JoinKind::Right,
1270 ast::JoinKind::Full => JoinKind::Full,
1271 ast::JoinKind::Semi => JoinKind::Semi,
1272 ast::JoinKind::Anti => JoinKind::Anti,
1273 ast::JoinKind::Positional => JoinKind::Positional,
1274 };
1275 let conditions = self.plan.add_expr_list(&conditions);
1276 let node =
1277 self.plan.add_node(Node::Join { left: left_node, right: right_node, kind, conditions });
1278 Ok((node, scope))
1279 }
1280
1281 pub(crate) fn bind_aggregate(
1285 &mut self,
1286 ast: &Ast,
1287 name: &str,
1288 args: &[ast::ExprRef],
1289 distinct: bool,
1290 scope: &Scope,
1291 ) -> Result<ExprRef> {
1292 if self.in_aggregate {
1293 return Err(Error::binder(format!(
1294 "aggregate function calls cannot be nested, and {name}() is inside one"
1295 )));
1296 }
1297 if self.aggregation.is_none() {
1298 return Err(Error::binder(format!(
1299 "aggregate function calls cannot be used in the {}",
1300 self.clause
1301 )));
1302 }
1303 self.in_aggregate = true;
1304 let mut bound = Vec::with_capacity(args.len());
1305 let mut failure = None;
1306 for &arg in args {
1307 match self.bind_expr(ast, arg, scope) {
1308 Ok(expr) => bound.push(expr),
1309 Err(error) => {
1310 failure = Some(error);
1311 break;
1312 }
1313 }
1314 }
1315 self.in_aggregate = false;
1316 if let Some(error) = failure {
1317 return Err(error);
1318 }
1319
1320 let types: Vec<LogicalType> =
1321 bound.iter().map(|&arg| self.plan.expr_type(arg).clone()).collect();
1322 let resolved = resolve(name, &types)?;
1323 let mut cast = Vec::with_capacity(bound.len());
1324 for (arg, wanted) in bound.iter().zip(&resolved.arguments) {
1325 cast.push(self.cast_to(*arg, wanted));
1326 }
1327 let args = self.plan.add_expr_list(&cast);
1328 let name = self.plan.intern(resolved.name);
1329 let ty = resolved.returns;
1330 let call =
1331 self.plan.add_expr(Expr::Aggregate { name, args, distinct, filter: None }, ty.clone());
1332
1333 let existing = self.aggregation.as_ref().map(|held| held.aggregates.clone());
1336 let existing = existing.unwrap_or_default();
1337 let at = match existing.iter().position(|&held| self.same_expr(held, call)) {
1338 Some(at) => at,
1339 None => {
1340 let aggregation = self.aggregation.as_mut().expect("checked above");
1341 aggregation.aggregates.push(call);
1342 aggregation.aggregates.len() - 1
1343 }
1344 };
1345 let aggregation = self.aggregation.as_ref().expect("checked above");
1346 let (index, groups) = (aggregation.index, aggregation.groups.len());
1347 Ok(self.column(index, groups + at, ty))
1348 }
1349
1350 pub(crate) fn over_aggregate(&mut self, expr: ExprRef, scope: &Scope) -> Result<ExprRef> {
1356 let Some(aggregation) = self.aggregation.as_ref() else {
1357 return Ok(expr);
1358 };
1359 let index = aggregation.index;
1360 let groups = aggregation.groups.clone();
1361 for (at, group) in groups.iter().enumerate() {
1362 if self.same_expr(expr, *group) {
1363 let ty = self.plan.expr_type(*group).clone();
1364 return Ok(self.column(index, at, ty));
1365 }
1366 }
1367 let ty = self.plan.expr_type(expr).clone();
1368 match self.plan.expr(expr).clone() {
1369 Expr::Column(binding) if binding.table == index => Ok(expr),
1370 Expr::Column(binding) => {
1371 let name =
1372 scope.columns.iter().find(|column| column.binding == binding).map_or_else(
1373 || "a column".to_string(),
1374 |column| format!("\"{}\"", column.name),
1375 );
1376 Err(Error::binder(format!(
1377 "column {name} must appear in the GROUP BY clause or must be part of an aggregate function"
1378 )))
1379 }
1380 Expr::Constant(_) | Expr::Aggregate { .. } => Ok(expr),
1381 Expr::Cast { input, try_cast } => {
1382 let input = self.over_aggregate(input, scope)?;
1383 Ok(self.plan.add_expr(Expr::Cast { input, try_cast }, ty))
1384 }
1385 Expr::Compare { op, left, right } => {
1386 let left = self.over_aggregate(left, scope)?;
1387 let right = self.over_aggregate(right, scope)?;
1388 Ok(self.plan.add_expr(Expr::Compare { op, left, right }, ty))
1389 }
1390 Expr::Conjunction { op, children } => {
1391 let written = self.plan.expr_list(children).to_vec();
1392 let mut rewritten = Vec::with_capacity(written.len());
1393 for child in written {
1394 rewritten.push(self.over_aggregate(child, scope)?);
1395 }
1396 let children = self.plan.add_expr_list(&rewritten);
1397 Ok(self.plan.add_expr(Expr::Conjunction { op, children }, ty))
1398 }
1399 Expr::Function { name, args } => {
1400 let written = self.plan.expr_list(args).to_vec();
1401 let mut rewritten = Vec::with_capacity(written.len());
1402 for arg in written {
1403 rewritten.push(self.over_aggregate(arg, scope)?);
1404 }
1405 let args = self.plan.add_expr_list(&rewritten);
1406 Ok(self.plan.add_expr(Expr::Function { name, args }, ty))
1407 }
1408 Expr::Case { arms, otherwise } => {
1409 let written = self.plan.arm_list(arms).to_vec();
1410 let mut rewritten = Vec::with_capacity(written.len());
1411 for arm in written {
1412 let when = self.over_aggregate(arm.when, scope)?;
1413 let then = self.over_aggregate(arm.then, scope)?;
1414 rewritten.push(rudb_plan::Arm { when, then });
1415 }
1416 let otherwise = match otherwise {
1417 Some(expr) => Some(self.over_aggregate(expr, scope)?),
1418 None => None,
1419 };
1420 let arms = self.plan.add_arms(&rewritten);
1421 Ok(self.plan.add_expr(Expr::Case { arms, otherwise }, ty))
1422 }
1423 }
1424 }
1425
1426 pub(crate) fn same_expr(&self, left: ExprRef, right: ExprRef) -> bool {
1428 same_expr(&self.plan, left, right)
1429 }
1430}
1431
1432fn sort_key(expr: ExprRef, item: ast::OrderItem) -> SortKey {
1438 let descending = item.order == Order::Descending;
1439 let nulls_first = match item.nulls {
1440 Nulls::First => true,
1441 Nulls::Last => false,
1442 Nulls::Unstated => descending,
1443 };
1444 SortKey { expr, descending, nulls_first }
1445}
1446
1447fn same_expr(plan: &Plan, left: ExprRef, right: ExprRef) -> bool {
1449 if left == right {
1450 return true;
1451 }
1452 if plan.expr_type(left) != plan.expr_type(right) {
1453 return false;
1454 }
1455 let lists = |left, right| {
1456 let left: &[ExprRef] = plan.expr_list(left);
1457 let right: &[ExprRef] = plan.expr_list(right);
1458 left.len() == right.len()
1459 && left.iter().zip(right).all(|(&left, &right)| same_expr(plan, left, right))
1460 };
1461 match (plan.expr(left), plan.expr(right)) {
1462 (Expr::Column(left), Expr::Column(right)) => left == right,
1463 (Expr::Constant(left), Expr::Constant(right)) => plan.value(*left) == plan.value(*right),
1464 (
1465 Expr::Cast { input: left, try_cast: left_try },
1466 Expr::Cast { input: right, try_cast: right_try },
1467 ) => left_try == right_try && same_expr(plan, *left, *right),
1468 (
1469 Expr::Compare { op: left_op, left: left_a, right: left_b },
1470 Expr::Compare { op: right_op, left: right_a, right: right_b },
1471 ) => {
1472 left_op == right_op
1473 && same_expr(plan, *left_a, *right_a)
1474 && same_expr(plan, *left_b, *right_b)
1475 }
1476 (
1477 Expr::Conjunction { op: left_op, children: left_children },
1478 Expr::Conjunction { op: right_op, children: right_children },
1479 ) => left_op == right_op && lists(*left_children, *right_children),
1480 (
1481 Expr::Function { name: left_name, args: left_args },
1482 Expr::Function { name: right_name, args: right_args },
1483 ) => plan.string(*left_name) == plan.string(*right_name) && lists(*left_args, *right_args),
1484 (
1485 Expr::Aggregate {
1486 name: left_name,
1487 args: left_args,
1488 distinct: left_distinct,
1489 filter: left_filter,
1490 },
1491 Expr::Aggregate {
1492 name: right_name,
1493 args: right_args,
1494 distinct: right_distinct,
1495 filter: right_filter,
1496 },
1497 ) => {
1498 plan.string(*left_name) == plan.string(*right_name)
1499 && left_distinct == right_distinct
1500 && match (left_filter, right_filter) {
1501 (None, None) => true,
1502 (Some(left), Some(right)) => same_expr(plan, *left, *right),
1503 _ => false,
1504 }
1505 && lists(*left_args, *right_args)
1506 }
1507 (
1508 Expr::Case { arms: left_arms, otherwise: left_otherwise },
1509 Expr::Case { arms: right_arms, otherwise: right_otherwise },
1510 ) => {
1511 let left_arms = plan.arm_list(*left_arms);
1512 let right_arms = plan.arm_list(*right_arms);
1513 left_arms.len() == right_arms.len()
1514 && left_arms.iter().zip(right_arms).all(|(left, right)| {
1515 same_expr(plan, left.when, right.when) && same_expr(plan, left.then, right.then)
1516 })
1517 && match (left_otherwise, right_otherwise) {
1518 (None, None) => true,
1519 (Some(left), Some(right)) => same_expr(plan, *left, *right),
1520 _ => false,
1521 }
1522 }
1523 _ => false,
1524 }
1525}