Skip to main content

rudb_bind/
binder.rs

1//! From an `Ast` to a `Plan`.
2//!
3//! The binder walks the written query once, in the order the operators end up in rather than the
4//! order the clauses are written in, which is `FROM`, `WHERE`, `GROUP BY`, `HAVING`, `SELECT`,
5//! `DISTINCT`, `ORDER BY`, `LIMIT`. That order is not a stylistic choice: it is the reason `WHERE`
6//! cannot see an output alias and `HAVING` cannot see a column that was not grouped, and doing it
7//! in any other order means special casing both of those instead of getting them for free.
8//!
9//! Two things leave here settled that nothing downstream reconsiders. Every column is a table index
10//! and a position rather than a name, so the optimizer never has to ask which `id` a name meant.
11//! And every expression has a type, with the casts that make the types line up already written into
12//! the plan as [`Expr::Cast`] nodes, so an executor never has to decide what a comparison between
13//! an `INTEGER` and a `BIGINT` does.
14
15use rudb_catalog::{Catalog, Entry, QualifiedName, same_name};
16use rudb_common::{Error, Field, LogicalType, Result, Value};
17use rudb_functions::{
18    Columns, Given, TableFunction, csv_fields, csv_given, files, is_file, is_pattern,
19    parquet_fields, resolve, 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
29/// Binds a parsed statement against a catalog.
30///
31/// # Errors
32///
33/// If the script does not hold exactly one statement, if a name does not resolve, if a type does
34/// not work out, or if the query uses something M0 does not bind yet.
35pub fn bind(ast: &Ast, catalog: &Catalog) -> Result<Plan> {
36    bind_with(ast, catalog, &Parameters::new())
37}
38
39/// Binds a parsed query against a catalog, with values for its parameters.
40///
41/// # Errors
42///
43/// Everything [`bind`] reports, plus an error for a parameter that was given no value.
44pub 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        // One statement that is not a query is its own answer. Reporting it as a script of several
49        // reads as a count being wrong, and the count is right.
50        [_] => return Err(Error::not_implemented("a statement that is not a query")),
51        _ => return Err(Error::not_implemented("a script of more than one statement")),
52    };
53    let mut binder = Binder::with(catalog, parameters);
54    let (root, _) = binder.bind_query(ast, query)?;
55    let mut plan = binder.into_plan();
56    plan.set_root(root);
57    plan.validate()?;
58    Ok(plan)
59}
60
61/// Parses and binds one query, which is the whole front end in one call.
62///
63/// # Errors
64///
65/// Anything the parser or the binder reports.
66pub fn bind_sql(query: &str, catalog: &Catalog) -> Result<Plan> {
67    let ast = parse_ast(query)?;
68    bind(&ast, catalog)
69}
70
71/// What an aggregating select block has decided so far.
72#[derive(Debug)]
73pub(crate) struct Aggregation {
74    /// The table index the aggregate's output binds against.
75    pub(crate) index: u32,
76    /// The group expressions, over the input, which are the first output columns.
77    pub(crate) groups: Vec<ExprRef>,
78    /// The aggregate calls found so far, which follow the groups in the output.
79    pub(crate) aggregates: Vec<ExprRef>,
80}
81
82/// The state one binding run carries.
83#[derive(Debug)]
84pub(crate) struct Binder<'a> {
85    catalog: &'a Catalog,
86    /// What the parameters were given, empty for a statement that is not prepared.
87    pub(crate) parameters: &'a Parameters,
88    plan: Plan,
89    next_index: u32,
90    /// Set while a select block aggregates, which changes what a bare column means.
91    pub(crate) aggregation: Option<Aggregation>,
92    /// Set while an aggregate's own arguments are being bound, so nesting is caught.
93    pub(crate) in_aggregate: bool,
94    /// Where we are, for an error message that says which clause the writer should look at.
95    pub(crate) clause: &'static str,
96    /// The views whose bodies are open on the stack, which is what catches a cycle.
97    expanding: Vec<String>,
98}
99
100impl<'a> Binder<'a> {
101    pub(crate) fn with(catalog: &'a Catalog, parameters: &'a Parameters) -> Self {
102        Self {
103            catalog,
104            parameters,
105            plan: Plan::new(),
106            next_index: 0,
107            aggregation: None,
108            in_aggregate: false,
109            clause: "SELECT clause",
110            expanding: Vec::new(),
111        }
112    }
113
114    pub(crate) fn plan(&self) -> &Plan {
115        &self.plan
116    }
117
118    pub(crate) fn plan_mut(&mut self) -> &mut Plan {
119        &mut self.plan
120    }
121
122    pub(crate) fn into_plan(self) -> Plan {
123        self.plan
124    }
125
126    /// A table index nothing else has.
127    pub(crate) fn fresh_index(&mut self) -> u32 {
128        let index = self.next_index;
129        self.next_index += 1;
130        index
131    }
132
133    /// A reference to one column of an operator's output.
134    fn column(&mut self, index: u32, position: usize, ty: LogicalType) -> ExprRef {
135        let binding = ColumnBinding::new(index, position as u32);
136        self.plan.add_expr(Expr::Column(binding), ty)
137    }
138
139    // ---------------------------------------------------------------- queries
140
141    pub(crate) fn bind_query(
142        &mut self,
143        ast: &Ast,
144        query: ast::QueryRef,
145    ) -> Result<(NodeRef, Scope)> {
146        let written = ast.query(query);
147        match written.body {
148            ast::QueryBody::Select(select) => self.bind_select(ast, select, &written),
149            ast::QueryBody::SetOp { op, quantifier, by_name, left, right } => {
150                if by_name {
151                    return Err(Error::not_implemented("UNION BY NAME"));
152                }
153                self.bind_set_op(ast, &written, op, quantifier, left, right)
154            }
155            ast::QueryBody::Values(rows) => self.bind_values(ast, &written, rows),
156            ast::QueryBody::Describe(inner) => self.bind_describe(ast, &written, inner),
157        }
158    }
159
160    /// `DESCRIBE <query>`, which is six VARCHAR columns saying what the query returns.
161    ///
162    /// The query is bound and never run, because binding is the whole of the answer: the names and
163    /// the types of a query's columns are settled by the time the binder is done with it, so the
164    /// rows of a describe are a constant from there on. That is why this comes out as a `VALUES`
165    /// whose rows were computed here rather than as an operator of its own, and it is what makes
166    /// `SELECT column_name FROM (DESCRIBE ...) WHERE ...` an ordinary query over an ordinary
167    /// relation with no special case above it.
168    ///
169    /// The six columns, their order and their types are the reference binary's. `key`, `default`
170    /// and `extra` are null for everything this engine can declare, since `PRIMARY KEY`, `UNIQUE`
171    /// and `DEFAULT` are all refused by `CREATE TABLE` today and there is nothing for the first two
172    /// to hold, and `extra` is empty upstream as well on every table it was asked about. They are
173    /// here rather than left out because the width of a result is part of the result, and a program
174    /// that reads the fifth column has to find one.
175    fn bind_describe(
176        &mut self,
177        ast: &Ast,
178        query: &ast::Query,
179        inner: ast::QueryRef,
180    ) -> Result<(NodeRef, Scope)> {
181        let (_, described) = self.bind_query(ast, inner)?;
182        let fields: Vec<Field> = ["column_name", "column_type", "null", "key", "default", "extra"]
183            .iter()
184            .map(|name| Field::new(*name, LogicalType::Varchar))
185            .collect();
186        let mut slices = Vec::with_capacity(described.columns.len());
187        for column in described.columns.clone() {
188            // `NO` and `YES` and not a boolean, because the column is VARCHAR upstream and a
189            // client that prints the result has to get the same four or three characters.
190            let written = [
191                column.name.clone(),
192                column.ty.to_string(),
193                if column.not_null { "NO" } else { "YES" }.to_owned(),
194            ];
195            let mut items: Vec<ExprRef> = written
196                .into_iter()
197                .map(|text| self.plan.add_constant(Value::Varchar(text)))
198                .collect();
199            for _ in 0..3 {
200                let empty = self.plan.add_constant(Value::Null);
201                items.push(self.cast_to(empty, &LogicalType::Varchar));
202            }
203            slices.push(self.plan.add_expr_list(&items));
204        }
205        let rows = self.plan.add_rows(&slices);
206        let columns = self.plan.add_fields(&fields);
207        let index = self.fresh_index();
208        let mut node = self.plan.add_node(Node::Values { index, columns, rows });
209        let mut scope = Scope::empty();
210        for (at, field) in fields.iter().enumerate() {
211            scope.push(Visible {
212                table: String::new(),
213                name: field.name.clone(),
214                binding: ColumnBinding::new(index, at as u32),
215                ty: field.ty.clone(),
216                not_null: false,
217            });
218        }
219        let keys = self.sort_keys(ast, query, &scope, &[])?;
220        if !keys.is_empty() {
221            let keys = self.plan.add_sort_keys(&keys);
222            node = self.plan.add_node(Node::Sort { input: node, keys });
223        }
224        node = self.apply_limit(ast, query, node)?;
225        Ok((node, scope))
226    }
227
228    /// Whether a projected expression is a column passed straight through from below.
229    ///
230    /// Only `DESCRIBE` asks, and only to decide whether the `null` column says `NO`. Anything that
231    /// is computed is nullable however strict its inputs were, which is both the safe reading and
232    /// the one the reference binary gives.
233    fn passes_through(&self, expr: ExprRef, input: &Scope) -> bool {
234        let Expr::Column(binding) = *self.plan.expr(expr) else { return false };
235        input.columns.iter().any(|column| column.binding == binding && column.not_null)
236    }
237
238    /// `VALUES (1, 'a'), (2, 'b')`, as a query in its own right.
239    ///
240    /// The column names are `col0`, `col1` and so on, which is what DuckDB calls them, and the
241    /// column types are what every row in that position promotes to. Promotion is the same rule a
242    /// set operation uses, and for the same reason: a column has one type and the rows have to
243    /// agree on it before anything downstream can read the column.
244    fn bind_values(
245        &mut self,
246        ast: &Ast,
247        query: &ast::Query,
248        rows: ast::Slice,
249    ) -> Result<(NodeRef, Scope)> {
250        let written = ast.rows(rows).to_vec();
251        let Some(first) = written.first() else {
252            return Err(Error::binder("VALUES needs at least one row"));
253        };
254        let width = first.len as usize;
255        for (at, row) in written.iter().enumerate() {
256            if row.len as usize != width {
257                return Err(Error::binder(format!(
258                    "VALUES lists must all be the same length, expected {width} columns but row {} has {}",
259                    at + 1,
260                    row.len
261                )));
262            }
263        }
264        // A row of a `VALUES` cannot see a column, because there is nothing under it to see.
265        let empty = Scope::empty();
266        let previous = std::mem::replace(&mut self.clause, "VALUES clause");
267        let mut bound: Vec<Vec<ExprRef>> = Vec::with_capacity(written.len());
268        for row in &written {
269            let mut items = Vec::with_capacity(width);
270            for &expr in ast.expr_list(*row) {
271                items.push(self.bind_expr(ast, expr, &empty)?);
272            }
273            bound.push(items);
274        }
275        self.clause = previous;
276        let mut types = Vec::with_capacity(width);
277        for at in 0..width {
278            let mut ty = self.plan.expr_type(bound[0][at]).clone();
279            for row in &bound[1..] {
280                let other = self.plan.expr_type(row[at]).clone();
281                ty = ty.promote(&other).ok_or_else(|| {
282                    Error::binder(format!(
283                        "Cannot combine a value of type {ty} with a value of type {other} in column {} of a VALUES",
284                        at + 1
285                    ))
286                })?;
287            }
288            types.push(ty);
289        }
290        let mut slices = Vec::with_capacity(bound.len());
291        for row in &bound {
292            let items: Vec<ExprRef> =
293                row.iter().zip(&types).map(|(&expr, ty)| self.cast_to(expr, ty)).collect();
294            slices.push(self.plan.add_expr_list(&items));
295        }
296        let rows = self.plan.add_rows(&slices);
297        let fields: Vec<Field> = types
298            .iter()
299            .enumerate()
300            .map(|(at, ty)| Field::new(format!("col{at}"), ty.clone()))
301            .collect();
302        let columns = self.plan.add_fields(&fields);
303        let index = self.fresh_index();
304        let mut node = self.plan.add_node(Node::Values { index, columns, rows });
305        let mut scope = Scope::empty();
306        for (at, field) in fields.iter().enumerate() {
307            scope.push(Visible {
308                table: String::new(),
309                name: field.name.clone(),
310                binding: ColumnBinding::new(index, at as u32),
311                ty: field.ty.clone(),
312                not_null: false,
313            });
314        }
315        let keys = self.sort_keys(ast, query, &scope, &[])?;
316        if !keys.is_empty() {
317            let keys = self.plan.add_sort_keys(&keys);
318            node = self.plan.add_node(Node::Sort { input: node, keys });
319        }
320        node = self.apply_limit(ast, query, node)?;
321        Ok((node, scope))
322    }
323
324    fn bind_set_op(
325        &mut self,
326        ast: &Ast,
327        query: &ast::Query,
328        op: SetOp,
329        quantifier: Quantifier,
330        left: ast::QueryRef,
331        right: ast::QueryRef,
332    ) -> Result<(NodeRef, Scope)> {
333        let (left_node, left_scope) = self.bind_query(ast, left)?;
334        let (right_node, right_scope) = self.bind_query(ast, right)?;
335        if left_scope.len() != right_scope.len() {
336            return Err(Error::binder(format!(
337                "Set operations can only apply to expressions with the same number of result columns, but left side has {} and right side has {}",
338                left_scope.len(),
339                right_scope.len()
340            )));
341        }
342        // Both sides have to hand back one set of types, so each column meets the other side's.
343        let mut types = Vec::with_capacity(left_scope.len());
344        for (left, right) in left_scope.columns.iter().zip(&right_scope.columns) {
345            let common = left.ty.promote(&right.ty).ok_or_else(|| {
346                Error::binder(format!(
347                    "Cannot combine a column of type {} with a column of type {} in a set operation",
348                    left.ty, right.ty
349                ))
350            })?;
351            types.push(common);
352        }
353        let left_node = self.conform(left_node, &left_scope, &types);
354        let right_node = self.conform(right_node, &right_scope, &types);
355        let index = self.fresh_index();
356        let kind = match op {
357            SetOp::Union => SetOpKind::Union,
358            SetOp::Except => SetOpKind::Except,
359            SetOp::Intersect => SetOpKind::Intersect,
360        };
361        // UNION alone removes duplicates and UNION ALL keeps them, which is the one place the
362        // unwritten quantifier and ALL disagree.
363        let all = quantifier == Quantifier::All;
364        let mut node = self.plan.add_node(Node::SetOp {
365            left: left_node,
366            right: right_node,
367            kind,
368            all,
369            index,
370        });
371        let mut scope = Scope::empty();
372        for (at, (column, ty)) in left_scope.columns.iter().zip(&types).enumerate() {
373            scope.push(Visible {
374                table: String::new(),
375                name: column.name.clone(),
376                binding: ColumnBinding::new(index, at as u32),
377                ty: ty.clone(),
378                // A column of a set operation is nullable whatever the two sides were, because a
379                // column that refuses nulls on one side and takes them on the other takes them.
380                not_null: false,
381            });
382        }
383        // Above a set operation there is nothing but the output columns, so an ORDER BY term is
384        // either a position, an output name, or an expression over the output, and never needs a
385        // column projected for it that the query did not ask for.
386        let keys = self.sort_keys(ast, query, &scope, &[])?;
387        if !keys.is_empty() {
388            let keys = self.plan.add_sort_keys(&keys);
389            node = self.plan.add_node(Node::Sort { input: node, keys });
390        }
391        node = self.apply_limit(ast, query, node)?;
392        Ok((node, scope))
393    }
394
395    /// Projects one side of a set operation so that its columns have the agreed types.
396    fn conform(&mut self, node: NodeRef, scope: &Scope, types: &[LogicalType]) -> NodeRef {
397        if scope.columns.iter().zip(types).all(|(column, ty)| &column.ty == ty) {
398            return node;
399        }
400        let index = self.fresh_index();
401        let mut exprs = Vec::with_capacity(types.len());
402        let mut names = Vec::with_capacity(types.len());
403        for (column, ty) in scope.columns.iter().zip(types) {
404            let expr = self.plan.add_expr(Expr::Column(column.binding), column.ty.clone());
405            exprs.push(self.cast_to(expr, ty));
406            names.push(self.plan.intern(&column.name));
407        }
408        let exprs = self.plan.add_expr_list(&exprs);
409        let names = self.plan.add_name_list(&names);
410        self.plan.add_node(Node::Project { input: node, index, exprs, names })
411    }
412
413    // ----------------------------------------------------------------- select
414
415    fn bind_select(
416        &mut self,
417        ast: &Ast,
418        select: ast::SelectRef,
419        query: &ast::Query,
420    ) -> Result<(NodeRef, Scope)> {
421        let written = ast.select(select);
422        let (mut node, input) = self.bind_from(ast, written.from)?;
423
424        if written.filter != NONE {
425            self.clause = "WHERE clause";
426            let predicate = self.bind_expr(ast, written.filter, &input)?;
427            let predicate = self.as_boolean(predicate, "WHERE")?;
428            node = self.plan.add_node(Node::Filter { input: node, predicate });
429        }
430
431        let targets = ast.target_list(written.targets).to_vec();
432        if targets.is_empty() {
433            return Err(Error::binder("a SELECT needs at least one expression to select"));
434        }
435
436        let group_items = self.group_items(ast, &written, &targets)?;
437        let aggregating = !group_items.is_empty()
438            || written.having != NONE
439            || targets.iter().any(|target| has_aggregate(ast, target.expr));
440        if aggregating {
441            self.clause = "GROUP BY clause";
442            let mut groups = Vec::with_capacity(group_items.len());
443            for item in &group_items {
444                groups.push(self.bind_expr(ast, *item, &input)?);
445            }
446            let index = self.fresh_index();
447            self.aggregation = Some(Aggregation { index, groups, aggregates: Vec::new() });
448        }
449
450        self.clause = "SELECT clause";
451        let (mut exprs, mut names) = self.bind_targets(ast, &targets, &input)?;
452        let visible = exprs.len();
453
454        let mut having = None;
455        if written.having != NONE {
456            self.clause = "HAVING clause";
457            let predicate = self.bind_expr(ast, written.having, &input)?;
458            let predicate = self.over_aggregate(predicate, &input)?;
459            having = Some(self.as_boolean(predicate, "HAVING")?);
460        }
461
462        // The projection's index has to exist before the sort keys are built, because a key is a
463        // reference to a projected column even when the expression it sorts on is not selected.
464        let project = self.fresh_index();
465        let mut output = Scope::empty();
466        for (at, (expr, name)) in exprs.iter().zip(&names).enumerate() {
467            output.push(Visible {
468                table: String::new(),
469                name: name.clone(),
470                binding: ColumnBinding::new(project, at as u32),
471                ty: self.plan.expr_type(*expr).clone(),
472                not_null: self.passes_through(*expr, &input),
473            });
474        }
475
476        self.clause = "ORDER BY clause";
477        let mut extra = Vec::new();
478        let keys = self.select_sort_keys(
479            ast, query, &input, &output, project, &mut exprs, &mut names, &mut extra,
480        )?;
481        if !extra.is_empty() && written.distinct != Distinct::No {
482            return Err(Error::binder(
483                "For SELECT DISTINCT, ORDER BY expressions must appear in the select list",
484            ));
485        }
486        let on = self.distinct_on(ast, written.distinct, &output)?;
487
488        if let Some(aggregation) = self.aggregation.take() {
489            let index = aggregation.index;
490            let groups = self.plan.add_expr_list(&aggregation.groups);
491            let aggregates = self.plan.add_expr_list(&aggregation.aggregates);
492            node = self.plan.add_node(Node::Aggregate { input: node, index, groups, aggregates });
493        }
494        if let Some(predicate) = having {
495            node = self.plan.add_node(Node::Filter { input: node, predicate });
496        }
497
498        let interned: Vec<u32> = names.iter().map(|name| self.plan.intern(name)).collect();
499        let exprs_slice = self.plan.add_expr_list(&exprs);
500        let names_slice = self.plan.add_name_list(&interned);
501        node = self.plan.add_node(Node::Project {
502            input: node,
503            index: project,
504            exprs: exprs_slice,
505            names: names_slice,
506        });
507
508        if written.distinct != Distinct::No {
509            let on = self.plan.add_expr_list(&on);
510            node = self.plan.add_node(Node::Distinct { input: node, on });
511        }
512        if !keys.is_empty() {
513            let keys = self.plan.add_sort_keys(&keys);
514            node = self.plan.add_node(Node::Sort { input: node, keys });
515        }
516        node = self.apply_limit(ast, query, node)?;
517
518        if extra.is_empty() {
519            output.columns.truncate(visible);
520            return Ok((node, output));
521        }
522        // An expression sorted on but not selected was carried this far to make the sort possible,
523        // and now it goes, because the query did not ask for it.
524        let index = self.fresh_index();
525        let mut kept = Vec::with_capacity(visible);
526        let mut kept_names = Vec::with_capacity(visible);
527        let mut scope = Scope::empty();
528        for (at, name) in names.iter().enumerate().take(visible) {
529            let ty = output.columns[at].ty.clone();
530            kept.push(self.column(project, at, ty.clone()));
531            kept_names.push(self.plan.intern(name));
532            scope.push(Visible {
533                table: String::new(),
534                name: name.clone(),
535                binding: ColumnBinding::new(index, at as u32),
536                ty,
537                not_null: output.columns[at].not_null,
538            });
539        }
540        let exprs = self.plan.add_expr_list(&kept);
541        let names = self.plan.add_name_list(&kept_names);
542        node = self.plan.add_node(Node::Project { input: node, index, exprs, names });
543        Ok((node, scope))
544    }
545
546    /// Binds the target list, expanding every star into the columns it stands for.
547    fn bind_targets(
548        &mut self,
549        ast: &Ast,
550        targets: &[ast::Target],
551        input: &Scope,
552    ) -> Result<(Vec<ExprRef>, Vec<String>)> {
553        let mut exprs = Vec::with_capacity(targets.len());
554        let mut names = Vec::with_capacity(targets.len());
555        for target in targets {
556            if let ast::Expr::Star { qualifier, replacements } = ast.expr(target.expr) {
557                let table = ast.name(qualifier).last().map(str::to_string);
558                let expanded: Vec<Visible> =
559                    input.star(table.as_deref())?.into_iter().cloned().collect();
560                let replacements = ast.target_list(replacements).to_vec();
561                let mut used = vec![false; replacements.len()];
562                for column in expanded {
563                    let found = replacements.iter().zip(&mut used).find(|(replacement, _)| {
564                        same_name(ast.string(replacement.alias), &column.name)
565                    });
566                    // The replacement takes the column's place and its position, and it is named the
567                    // way the replace list spells it rather than the way the table does. That only
568                    // shows when the two differ in case, and `AS EventDate` over a column called
569                    // `eventdate` is exactly the case that shows it.
570                    let (expr, name) = match found {
571                        Some((replacement, used)) => {
572                            *used = true;
573                            let expr = self.bind_expr(ast, replacement.expr, input)?;
574                            (expr, ast.string(replacement.alias).to_string())
575                        }
576                        None => (
577                            self.plan.add_expr(Expr::Column(column.binding), column.ty),
578                            column.name,
579                        ),
580                    };
581                    exprs.push(self.over_aggregate(expr, input)?);
582                    names.push(name);
583                }
584                // A replace list that named something the star did not stand for is a mistake and
585                // not a no op, and it is caught here because this is the first point at which the
586                // set of names the star stands for is known.
587                if let Some((replacement, _)) =
588                    replacements.iter().zip(&used).find(|(_, used)| !**used)
589                {
590                    return Err(missing_replacement(ast.string(replacement.alias), input));
591                }
592                continue;
593            }
594            let expr = self.bind_expr(ast, target.expr, input)?;
595            exprs.push(self.over_aggregate(expr, input)?);
596            names.push(if target.alias == NONE {
597                self.output_name(ast, target.expr, input)
598            } else {
599                ast.string(target.alias).to_string()
600            });
601        }
602        Ok((exprs, names))
603    }
604
605    /// The name an unaliased target gets.
606    ///
607    /// A bare column keeps the spelling the table was created with rather than the spelling the
608    /// query used, so `SELECT USERID FROM hits` has a column called `UserID`. Identifiers match
609    /// without regard to case and the catalog is the one that holds the case.
610    fn output_name(&self, ast: &Ast, target: ast::ExprRef, input: &Scope) -> String {
611        if let ast::Expr::Column { name } = ast.expr(target) {
612            let parts: Vec<&str> = ast.name(name).collect();
613            if let Ok(found) = input.resolve(&parts) {
614                return found.name.clone();
615            }
616        }
617        describe(ast, target)
618    }
619
620    /// The expressions a `GROUP BY` clause names, with positions and output aliases followed.
621    fn group_items(
622        &self,
623        ast: &Ast,
624        select: &ast::Select,
625        targets: &[ast::Target],
626    ) -> Result<Vec<ast::ExprRef>> {
627        if select.group_by_all {
628            // GROUP BY ALL means every target that is not itself an aggregate, which is the set
629            // that would otherwise have to be written out again by hand.
630            return Ok(targets
631                .iter()
632                .filter(|target| !has_aggregate(ast, target.expr))
633                .map(|target| target.expr)
634                .collect());
635        }
636        let mut items = Vec::new();
637        for &item in ast.expr_list(select.group_by) {
638            items.push(self.output_reference(ast, item, targets, "GROUP BY")?.unwrap_or(item));
639        }
640        Ok(items)
641    }
642
643    /// The target a `GROUP BY` or `ORDER BY` term names, when it names one by position or alias.
644    fn output_reference(
645        &self,
646        ast: &Ast,
647        item: ast::ExprRef,
648        targets: &[ast::Target],
649        clause: &str,
650    ) -> Result<Option<ast::ExprRef>> {
651        match ast.expr(item) {
652            ast::Expr::Literal { kind: LiteralKind::Number, text } => {
653                let written = ast.string(text);
654                let position: usize = written.parse().map_err(|_| {
655                    Error::binder(format!("{clause} term {written} is not a column"))
656                })?;
657                if position == 0 || position > targets.len() {
658                    return Err(Error::binder(format!(
659                        "{clause} term out of range - should be between 1 and {}",
660                        targets.len()
661                    )));
662                }
663                Ok(Some(targets[position - 1].expr))
664            }
665            ast::Expr::Column { name } => {
666                let parts: Vec<&str> = ast.name(name).collect();
667                let [written] = parts.as_slice() else { return Ok(None) };
668                let mut found = None;
669                for target in targets {
670                    if target.alias != NONE && same_name(ast.string(target.alias), written) {
671                        if found.is_some() {
672                            return Ok(None);
673                        }
674                        found = Some(target.expr);
675                    }
676                }
677                Ok(found)
678            }
679            _ => Ok(None),
680        }
681    }
682
683    // -------------------------------------------------------------- modifiers
684
685    /// Sort keys for a select, projecting anything sorted on that is not already selected.
686    #[allow(clippy::too_many_arguments)]
687    fn select_sort_keys(
688        &mut self,
689        ast: &Ast,
690        query: &ast::Query,
691        input: &Scope,
692        output: &Scope,
693        project: u32,
694        exprs: &mut Vec<ExprRef>,
695        names: &mut Vec<String>,
696        extra: &mut Vec<usize>,
697    ) -> Result<Vec<SortKey>> {
698        if query.order_by_all {
699            return Ok(self.every_column(output));
700        }
701        let items = ast.order_list(query.order_by).to_vec();
702        let mut keys = Vec::with_capacity(items.len());
703        for item in items {
704            let position = match self.output_position(ast, item.expr, output)? {
705                Some(position) => position,
706                None => {
707                    let bound = self.bind_expr(ast, item.expr, input)?;
708                    let bound = self.over_aggregate(bound, input)?;
709                    match exprs.iter().position(|&held| self.same_expr(held, bound)) {
710                        Some(position) => position,
711                        None => {
712                            exprs.push(bound);
713                            names.push(describe(ast, item.expr));
714                            extra.push(exprs.len() - 1);
715                            exprs.len() - 1
716                        }
717                    }
718                }
719            };
720            let ty = self.plan.expr_type(exprs[position]).clone();
721            let expr = self.column(project, position, ty);
722            keys.push(sort_key(expr, item));
723        }
724        Ok(keys)
725    }
726
727    /// Sort keys over an output that has nothing behind it to project, which is a set operation.
728    fn sort_keys(
729        &mut self,
730        ast: &Ast,
731        query: &ast::Query,
732        output: &Scope,
733        targets: &[ast::Target],
734    ) -> Result<Vec<SortKey>> {
735        if query.order_by_all {
736            return Ok(self.every_column(output));
737        }
738        let items = ast.order_list(query.order_by).to_vec();
739        let mut keys = Vec::with_capacity(items.len());
740        for item in items {
741            let expr = match self.output_position(ast, item.expr, output)? {
742                Some(position) => {
743                    let column = &output.columns[position];
744                    let (binding, ty) = (column.binding, column.ty.clone());
745                    self.plan.add_expr(Expr::Column(binding), ty)
746                }
747                None => {
748                    let _ = targets;
749                    self.bind_expr(ast, item.expr, output)?
750                }
751            };
752            keys.push(sort_key(expr, item));
753        }
754        Ok(keys)
755    }
756
757    fn every_column(&mut self, output: &Scope) -> Vec<SortKey> {
758        let columns: Vec<(ColumnBinding, LogicalType)> =
759            output.columns.iter().map(|column| (column.binding, column.ty.clone())).collect();
760        columns
761            .into_iter()
762            .map(|(binding, ty)| {
763                let expr = self.plan.add_expr(Expr::Column(binding), ty);
764                SortKey { expr, descending: false, nulls_first: false }
765            })
766            .collect()
767    }
768
769    /// Which output column a term names, by position or by name.
770    fn output_position(
771        &self,
772        ast: &Ast,
773        item: ast::ExprRef,
774        output: &Scope,
775    ) -> Result<Option<usize>> {
776        match ast.expr(item) {
777            ast::Expr::Literal { kind: LiteralKind::Number, text } => {
778                let written = ast.string(text);
779                if written.contains(['.', 'e', 'E']) {
780                    return Ok(None);
781                }
782                let position: usize = written.parse().map_err(|_| {
783                    Error::binder(format!("ORDER BY term {written} is not a column"))
784                })?;
785                if position == 0 || position > output.len() {
786                    return Err(Error::binder(format!(
787                        "ORDER BY term out of range - should be between 1 and {}",
788                        output.len()
789                    )));
790                }
791                Ok(Some(position - 1))
792            }
793            ast::Expr::Column { name } => {
794                let parts: Vec<&str> = ast.name(name).collect();
795                let [written] = parts.as_slice() else { return Ok(None) };
796                Ok(output.position_of(None, written))
797            }
798            _ => Ok(None),
799        }
800    }
801
802    /// The expressions a `DISTINCT ON` names, which have to be columns of the output.
803    fn distinct_on(
804        &mut self,
805        ast: &Ast,
806        distinct: Distinct,
807        output: &Scope,
808    ) -> Result<Vec<ExprRef>> {
809        let Distinct::On(items) = distinct else {
810            return Ok(Vec::new());
811        };
812        let items = ast.expr_list(items).to_vec();
813        let mut on = Vec::with_capacity(items.len());
814        for item in items {
815            let Some(position) = self.output_position(ast, item, output)? else {
816                return Err(Error::not_implemented(
817                    "DISTINCT ON an expression that is not in the select list",
818                ));
819            };
820            let column = &output.columns[position];
821            let (binding, ty) = (column.binding, column.ty.clone());
822            on.push(self.plan.add_expr(Expr::Column(binding), ty));
823        }
824        Ok(on)
825    }
826
827    fn apply_limit(&mut self, ast: &Ast, query: &ast::Query, input: NodeRef) -> Result<NodeRef> {
828        if query.limit_percent {
829            return Err(Error::not_implemented("LIMIT with a percentage"));
830        }
831        let count = self.constant_count(ast, query.limit, "LIMIT")?;
832        let offset = self.constant_count(ast, query.offset, "OFFSET")?.unwrap_or(0);
833        if count.is_none() && offset == 0 {
834            return Ok(input);
835        }
836        Ok(self.plan.add_node(Node::Limit { input, count, offset }))
837    }
838
839    /// The row count a `LIMIT` or an `OFFSET` names, which has to be a constant.
840    fn constant_count(
841        &mut self,
842        ast: &Ast,
843        written: ast::ExprRef,
844        clause: &str,
845    ) -> Result<Option<u64>> {
846        if written == NONE {
847            return Ok(None);
848        }
849        self.clause = "LIMIT clause";
850        let scope = Scope::empty();
851        let bound = self.bind_expr(ast, written, &scope)?;
852        let Expr::Constant(value) = *self.plan.expr(bound) else {
853            return Err(Error::not_implemented(format!("a {clause} that is not a constant")));
854        };
855        let count = match self.plan.value(value) {
856            Value::Null => return Ok(None),
857            Value::TinyInt(count) => i128::from(*count),
858            Value::SmallInt(count) => i128::from(*count),
859            Value::Integer(count) => i128::from(*count),
860            Value::BigInt(count) => i128::from(*count),
861            Value::HugeInt(count) => *count,
862            other => {
863                return Err(Error::binder(format!(
864                    "{clause} takes a whole number of rows, not a value of type {}",
865                    other.logical_type()
866                )));
867            }
868        };
869        u64::try_from(count)
870            .map(Some)
871            .map_err(|_| Error::binder(format!("{clause} must not be negative")))
872    }
873
874    // ------------------------------------------------------------------- from
875
876    fn bind_from(&mut self, ast: &Ast, from: ast::Slice) -> Result<(NodeRef, Scope)> {
877        let sources = ast.source_list(from).to_vec();
878        let Some((first, rest)) = sources.split_first() else {
879            // No FROM clause is one row of no columns, which is what SELECT 1 sits on. Not an
880            // empty table: an empty table would make SELECT 1 return nothing.
881            return Ok((self.plan.add_node(Node::Dummy), Scope::empty()));
882        };
883        let (mut node, mut scope) = self.bind_source(ast, *first)?;
884        for source in rest {
885            let (right, right_scope) = self.bind_source(ast, *source)?;
886            node = self.plan.add_node(Node::CrossProduct { left: node, right });
887            scope = scope.concat(right_scope);
888        }
889        Ok((node, scope))
890    }
891
892    fn bind_source(&mut self, ast: &Ast, source: ast::SourceRef) -> Result<(NodeRef, Scope)> {
893        match ast.source(source) {
894            ast::Source::Table { name, alias, columns } => {
895                self.bind_table(ast, name, alias, columns)
896            }
897            ast::Source::Function { name, args, alias, columns } => {
898                self.bind_table_function(ast, name, args, alias, columns)
899            }
900            ast::Source::Subquery { query, alias, columns } => {
901                let (node, mut scope) = self.bind_query(ast, query)?;
902                let label = if alias == NONE {
903                    "unnamed_subquery".to_string()
904                } else {
905                    ast.string(alias).to_string()
906                };
907                scope.relabel(&label);
908                if !columns.is_empty() {
909                    let names: Vec<&str> = ast.name(columns).collect();
910                    scope.rename(&names, &label)?;
911                }
912                Ok((node, scope))
913            }
914            ast::Source::Values { rows, alias, columns } => {
915                let bare = ast::Query::bare(ast::QueryBody::Values(rows));
916                let (node, mut scope) = self.bind_values(ast, &bare, rows)?;
917                let label =
918                    if alias == NONE { String::new() } else { ast.string(alias).to_string() };
919                scope.relabel(&label);
920                if !columns.is_empty() {
921                    let names: Vec<&str> = ast.name(columns).collect();
922                    scope.rename(&names, &label)?;
923                }
924                Ok((node, scope))
925            }
926            ast::Source::Join { left, right, kind, natural, on, using } => {
927                self.bind_join(ast, left, right, kind, natural, on, using)
928            }
929        }
930    }
931
932    fn bind_table(
933        &mut self,
934        ast: &Ast,
935        name: ast::Slice,
936        alias: ast::StrRef,
937        columns: ast::Slice,
938    ) -> Result<(NodeRef, Scope)> {
939        let parts: Vec<&str> = ast.name(name).collect();
940        let catalog = self.catalog;
941        // The catalog is asked first and the file is the fallback, which is the order DuckDB uses:
942        // a table really called `mixed.parquet` wins over a file of that name sitting next to it.
943        let resolved = match catalog.resolve(&parts) {
944            Ok(resolved) => resolved,
945            Err(missing) => {
946                return self.bind_replacement_scan(ast, &parts, alias, columns, missing);
947            }
948        };
949        if catalog.entry(&resolved)? == Entry::View {
950            return self.bind_view(ast, &resolved, alias, columns);
951        }
952        let table = catalog.table(&resolved)?;
953        let fields: Vec<Field> = table.columns().to_vec();
954        let label =
955            if alias == NONE { resolved.table.clone() } else { ast.string(alias).to_string() };
956        let index = self.fresh_index();
957        let mut scope = Scope::empty();
958        for (at, field) in fields.iter().enumerate() {
959            scope.push(Visible {
960                table: label.clone(),
961                name: field.name.clone(),
962                binding: ColumnBinding::new(index, at as u32),
963                ty: field.ty.clone(),
964                not_null: field.not_null,
965            });
966        }
967        if !columns.is_empty() {
968            let names: Vec<&str> = ast.name(columns).collect();
969            scope.rename(&names, &label)?;
970        }
971        let catalog_name = self.plan.intern(&resolved.catalog);
972        let schema = self.plan.intern(&resolved.schema);
973        let table_name = self.plan.intern(&resolved.table);
974        let alias = self.plan.intern(&label);
975        let columns = self.plan.add_fields(&fields);
976        let node = self.plan.add_node(Node::Get {
977            catalog: catalog_name,
978            schema,
979            table: table_name,
980            alias,
981            index,
982            columns,
983        });
984        Ok((node, scope))
985    }
986
987    /// A view where a table goes, which is the body bound again right here.
988    ///
989    /// Inline and not behind a node. The view is gone by the time the plan exists, so everything
990    /// downstream sees the query somebody would have written by hand, and the column pruning that
991    /// makes `SELECT COUNT(*) FROM 'hits.parquet'` read no columns at all keeps working through
992    /// `FROM hits`. A `Node::View` would be a barrier with nothing on the other side of it.
993    ///
994    /// The scope this builds is a subquery's, right down to the name in the error message. duckdb
995    /// v1.5.1 reports a view whose column list has gone stale as `table "unnamed_subquery" has 1
996    /// columns available but 2 columns specified`, which is the sentence its subquery alias rule
997    /// produces, so a view there is a subquery with the view's name written over it afterwards.
998    fn bind_view(
999        &mut self,
1000        ast: &Ast,
1001        name: &QualifiedName,
1002        alias: ast::StrRef,
1003        columns: ast::Slice,
1004    ) -> Result<(NodeRef, Scope)> {
1005        let view = self.catalog.view(name)?;
1006        let full = name.to_string();
1007        if self.expanding.contains(&full) {
1008            // Two quotes each side, which is what the binary prints. It quotes the name on the way
1009            // in and then formats the quoted name into a quoted slot, so a view called `a` comes
1010            // back as `""a""`. That is upstream's wart and copying it is the whole job here.
1011            return Err(Error::binder(format!(
1012                "infinite recursion detected: attempting to recursively bind view \"\"{}\"\"",
1013                name.table
1014            )));
1015        }
1016        let body = parse_ast(view.sql())?;
1017        let query = match body.statements.as_slice() {
1018            [ast::Statement::Query(query)] => *query,
1019            // Only a query can have got past the binder at creation, so this is a view the catalog
1020            // was handed some other way rather than anything a statement can produce.
1021            _ => return Err(Error::binder(format!("view \"{}\" is not a query", name.table))),
1022        };
1023        self.expanding.push(full);
1024        let bound = self.bind_query(&body, query);
1025        self.expanding.pop();
1026        let (node, mut scope) = bound?;
1027
1028        let aliases: Vec<&str> = view.aliases().iter().map(String::as_str).collect();
1029        if !aliases.is_empty() {
1030            scope.rename(&aliases, "unnamed_subquery")?;
1031        }
1032        let label = if alias == NONE { name.table.clone() } else { ast.string(alias).to_string() };
1033        scope.relabel(&label);
1034        if !columns.is_empty() {
1035            let names: Vec<&str> = ast.name(columns).collect();
1036            scope.rename(&names, &label)?;
1037        }
1038        Ok((node, scope))
1039    }
1040
1041    /// A function call where a table goes, such as `range(10)`.
1042    ///
1043    /// The arguments are bound against an empty scope. A table function that can see the row on its
1044    /// left is `LATERAL`, and this is not it, so a column name in here is not resolved against
1045    /// whatever happens to be to the left in the `FROM` list. Letting it would mean `FROM t,
1046    /// range(t.n)` quietly binding to something whose meaning depends on the order the sources were
1047    /// written in.
1048    fn bind_table_function(
1049        &mut self,
1050        ast: &Ast,
1051        name: ast::Slice,
1052        args: ast::Slice,
1053        alias: ast::StrRef,
1054        columns: ast::Slice,
1055    ) -> Result<(NodeRef, Scope)> {
1056        let parts: Vec<&str> = ast.name(name).collect();
1057        // A qualified call names a schema, and the two schemas that exist are the ones every
1058        // built-in lives in. Anything else is a name that has to fail rather than fall through to
1059        // the unqualified lookup and be found somewhere it was not asked for.
1060        let function_name = *parts.last().unwrap_or(&"");
1061        if let Some(schema) = parts.iter().rev().nth(1) {
1062            if !schema.eq_ignore_ascii_case("main") && !schema.eq_ignore_ascii_case("system") {
1063                return Err(Error::catalog(format!(
1064                    "Table Function with name {} does not exist!",
1065                    parts.join(".")
1066                )));
1067            }
1068        }
1069        // The name is looked up before the arguments are bound so that a call of something that is
1070        // not a table function says that, rather than reporting whatever is wrong with the
1071        // arguments of a function that was never going to exist.
1072        let Some(called) = TableFunction::lookup(function_name) else {
1073            return Err(Error::catalog(format!(
1074                "Table Function with name {function_name} does not exist!"
1075            )));
1076        };
1077        let written = ast.target_list(args).to_vec();
1078        let empty = Scope::empty();
1079        let previous = std::mem::replace(&mut self.clause, "table function arguments");
1080        let mut bound = Vec::new();
1081        let mut written_options = Vec::new();
1082        for argument in written {
1083            let expr = self.bind_expr(ast, argument.expr, &empty)?;
1084            if argument.alias == NONE {
1085                bound.push(expr);
1086            } else {
1087                let name = ast.string(argument.alias).to_string();
1088                let (parameter, value) = self.named_argument(called, &name, expr)?;
1089                written_options.push((parameter, value, expr));
1090            }
1091        }
1092        self.clause = previous;
1093        let options = Options::of(&written_options)?;
1094
1095        // The types are what resolve the call, not the count, because `read_parquet(3)` is a
1096        // different answer from `read_parquet('3')` and only the types tell them apart.
1097        let given: Vec<LogicalType> =
1098            bound.iter().map(|&expr| self.plan.expr_type(expr).clone()).collect();
1099        let resolved = resolve_table(function_name, &given)?;
1100        let mut cast: Vec<ExprRef> = bound
1101            .iter()
1102            .zip(&resolved.arguments)
1103            .map(|(&expr, ty)| self.cast_to(expr, ty))
1104            .collect();
1105
1106        let fields = match resolved.columns {
1107            Columns::Fixed(fields) => fields,
1108            columns => {
1109                // The one argument is a pattern, and what replaces it is one constant per file it
1110                // matched. The executor is handed names rather than a pattern, so it never walks a
1111                // directory and the answer cannot change between binding a prepared statement and
1112                // running it, which is the same reason the schema is settled here.
1113                let paths = self.file_paths(cast[0], resolved.function.name())?;
1114                let first = paths.first().map_or("", String::as_str);
1115                let mut fields = match columns {
1116                    // Parquet takes the first file's footer as the answer and CSV sniffs all of
1117                    // them, which is not a choice made here. See `csv_fields`.
1118                    Columns::Csv => csv_fields(&paths, options.given)?,
1119                    _ => parquet_fields(first)?,
1120                };
1121                if options.all_varchar {
1122                    // The sniffer still ran, because the names come out of the same pass over the
1123                    // front of the file and only the types are being overruled. The executor reads
1124                    // the text as VARCHAR because this is the schema it is told to read into, which
1125                    // is the same road a file in a glob takes when the set is wider than the file.
1126                    for field in &mut fields {
1127                        field.ty = LogicalType::Varchar;
1128                    }
1129                }
1130                if options.binary_as_string {
1131                    // A byte array column with no annotation on it is a BLOB, and this is the caller
1132                    // saying that the file's writer meant text. The reader already holds both in the
1133                    // same string column and already validates the bytes, so the whole of the option
1134                    // is what the column is called from here on.
1135                    for field in &mut fields {
1136                        if field.ty == LogicalType::Blob {
1137                            field.ty = LogicalType::Varchar;
1138                        }
1139                    }
1140                }
1141                cast = paths.iter().map(|path| self.path_constant(path)).collect();
1142                fields
1143            }
1144        };
1145        let label = if alias == NONE {
1146            resolved.function.name().to_string()
1147        } else {
1148            ast.string(alias).to_string()
1149        };
1150        let names: Vec<&str> = ast.name(columns).collect();
1151        self.table_function_source(
1152            resolved.function,
1153            &cast,
1154            &written_options,
1155            fields,
1156            &label,
1157            &names,
1158        )
1159    }
1160
1161    /// One named parameter of a table function call, folded into what the call was given.
1162    ///
1163    /// The value has to be a constant of the type the parameter wants. It has to be constant
1164    /// because an option can decide what the columns are and the columns are settled here, and it
1165    /// has to be already of the type because there is no constant folding in front of the binder
1166    /// yet. DuckDB folds first, so `binary_as_string=1` and `binary_as_string='yes'` are both true
1167    /// there and both are turned away here, which is a gap that closes on its own the day the
1168    /// optimizer runs before the plan is finished. `binary_as_string=True` is what the ClickBench
1169    /// entry writes and is what has to work.
1170    ///
1171    /// A name that is not a parameter of this function is the binary's sentence followed by what it
1172    /// could have been. The binary puts the candidates on their own indented lines and this puts
1173    /// them on the same line, because an error is one line here.
1174    fn named_argument(
1175        &mut self,
1176        function: TableFunction,
1177        name: &str,
1178        expr: ExprRef,
1179    ) -> Result<(&'static str, Value)> {
1180        let known = function
1181            .parameters()
1182            .iter()
1183            .find(|(parameter, _)| parameter.eq_ignore_ascii_case(name));
1184        let Some((parameter, wanted)) = known else {
1185            let candidates: Vec<String> = function
1186                .parameters()
1187                .iter()
1188                .map(|(parameter, ty)| format!("    {parameter} {ty}"))
1189                .collect();
1190            return Err(Error::binder(format!(
1191                "Invalid named parameter \"{name}\" for function {}\nCandidates:\n{}\n",
1192                function.name(),
1193                candidates.join("\n")
1194            )));
1195        };
1196        let Expr::Constant(reference) = *self.plan.expr(expr) else {
1197            return Err(Error::not_implemented(format!(
1198                "the named parameter {parameter} with a value that is not a constant"
1199            )));
1200        };
1201        let value = self.plan.value(reference).clone();
1202        if value == Value::Null {
1203            return Err(Error::binder(null_parameter(function, parameter)));
1204        }
1205        let given = self.plan.expr_type(expr).clone();
1206        if given != *wanted {
1207            return Err(Error::not_implemented(format!(
1208                "the named parameter {parameter} given a {given} where a {wanted} was wanted"
1209            )));
1210        }
1211        Ok((parameter, value))
1212    }
1213
1214    /// A file where a table name goes, which is what DuckDB calls a replacement scan.
1215    ///
1216    /// `SELECT * FROM 'hits.parquet'` is how most DuckDB queries in the wild are written, ClickBench
1217    /// among them, so this is not sugar over `read_parquet` so much as the spelling people use. The
1218    /// catalog has already been asked and has already said no, and `missing` is what it said, so a
1219    /// name that is not a file comes back with the catalog's own answer rather than with a complaint
1220    /// about files.
1221    ///
1222    /// Only a single unqualified name is a candidate. A qualified one names a schema and a schema
1223    /// that does not exist is not a path.
1224    fn bind_replacement_scan(
1225        &mut self,
1226        ast: &Ast,
1227        parts: &[&str],
1228        alias: ast::StrRef,
1229        columns: ast::Slice,
1230        missing: Error,
1231    ) -> Result<(NodeRef, Scope)> {
1232        let [path] = parts else { return Err(missing) };
1233        let path = *path;
1234        let extension = path.rsplit_once('.').map(|(_, after)| after).unwrap_or_default();
1235        let Some(function) = Self::reader_for(extension) else {
1236            if is_file(path) {
1237                // A file that is really there and that nothing here can read is a different mistake
1238                // from a name that is not a file, and DuckDB says so with both lines, the second of
1239                // which is the way out. A file with no dot in it lands here too, which is why the
1240                // test is on the extension having a reader rather than on there being an extension.
1241                return Err(Error::binder(format!(
1242                    "No extension found that is capable of reading the file \"{path}\"\n* If this \
1243                     file is a supported file format you can explicitly use the reader functions, \
1244                     such as read_csv, read_json or read_parquet"
1245                )));
1246            }
1247            return Err(missing);
1248        };
1249        // The pattern is expanded before it is known to match anything, so a name that ends in .csv
1250        // and is not there gives the reader's own message rather than the catalog's. That is
1251        // DuckDB's order and it is the helpful one: somebody who wrote a file name wants to hear
1252        // about the file.
1253        let paths = files(path)?;
1254        let first = paths.first().map_or("", String::as_str);
1255        let fields = match function {
1256            TableFunction::ReadParquet => parquet_fields(first)?,
1257            _ => csv_fields(&paths, Given::default())?,
1258        };
1259        // The name the columns answer to is the file's stem, so `SELECT mixed.a FROM
1260        // 'data/mixed.parquet'` works. That is DuckDB's choice and it is the useful one, since the
1261        // alternative is a table name with a dot and a slash in it that nothing can write. A pattern
1262        // keeps the whole of what was written instead, which is DuckDB's choice too and was
1263        // measured: there is no stem to take when the name stands for a directory full of files.
1264        let label = if alias == NONE {
1265            if is_pattern(path) {
1266                path.to_string()
1267            } else {
1268                let file = path.rsplit_once('/').map_or(path, |(_, file)| file);
1269                file.rsplit_once('.').map_or(file, |(stem, _)| stem).to_string()
1270            }
1271        } else {
1272            ast.string(alias).to_string()
1273        };
1274        let arguments: Vec<ExprRef> = paths.iter().map(|path| self.path_constant(path)).collect();
1275        let names: Vec<&str> = ast.name(columns).collect();
1276        self.table_function_source(function, &arguments, &[], fields, &label, &names)
1277    }
1278
1279    /// One file name, as a constant expression in the plan.
1280    fn path_constant(&mut self, path: &str) -> ExprRef {
1281        let value = self.plan.add_value(Value::Varchar(path.to_string()));
1282        self.plan.add_expr(Expr::Constant(value), LogicalType::Varchar)
1283    }
1284
1285    /// The table function a file with this extension is read by, and `None` for one nothing reads.
1286    ///
1287    /// Both spellings of a tab separated file go to the CSV reader, which is not a shortcut: the
1288    /// extension picks the reader and the reader sniffs the punctuation, so a `.tsv` file that holds
1289    /// commas is read as commas. That was measured rather than assumed. The comparison ignores case
1290    /// because `UP.CSV` reads in duckdb v1.4.1.
1291    fn reader_for(extension: &str) -> Option<TableFunction> {
1292        if extension.eq_ignore_ascii_case("parquet") {
1293            return Some(TableFunction::ReadParquet);
1294        }
1295        if extension.eq_ignore_ascii_case("csv") || extension.eq_ignore_ascii_case("tsv") {
1296            return Some(TableFunction::ReadCsv);
1297        }
1298        None
1299    }
1300
1301    /// The node and the scope of a table function call whose arguments and columns are settled.
1302    ///
1303    /// The half a written out call shares with a replacement scan, which is everything after the
1304    /// question of what the file is called has been answered one way or the other.
1305    fn table_function_source(
1306        &mut self,
1307        function: TableFunction,
1308        args: &[ExprRef],
1309        written: &[(&'static str, Value, ExprRef)],
1310        fields: Vec<Field>,
1311        label: &str,
1312        names: &[&str],
1313    ) -> Result<(NodeRef, Scope)> {
1314        let index = self.fresh_index();
1315        let mut scope = Scope::empty();
1316        for (at, field) in fields.iter().enumerate() {
1317            scope.push(Visible {
1318                table: label.to_string(),
1319                name: field.name.clone(),
1320                binding: ColumnBinding::new(index, at as u32),
1321                ty: field.ty.clone(),
1322                // A reader takes what the file has, and no file format this reads says a column
1323                // cannot be null. The reference binary answers YES for every column of a Parquet.
1324                not_null: false,
1325            });
1326        }
1327        if !names.is_empty() {
1328            scope.rename(names, label)?;
1329        }
1330        let function = self.plan.intern(function.name());
1331        let args = self.plan.add_expr_list(args);
1332        let named: Vec<u32> =
1333            written.iter().map(|(parameter, _, _)| self.plan.intern(parameter)).collect();
1334        let settings: Vec<ExprRef> = written.iter().map(|(_, _, expr)| *expr).collect();
1335        let options = self.plan.add_name_list(&named);
1336        let settings = self.plan.add_expr_list(&settings);
1337        let columns = self.plan.add_fields(&fields);
1338        let node = self.plan.add_node(Node::TableFunction {
1339            index,
1340            function,
1341            args,
1342            options,
1343            settings,
1344            columns,
1345        });
1346        Ok((node, scope))
1347    }
1348
1349    /// Every file a table function's file argument names, in the order they were written.
1350    ///
1351    /// Each pattern has to find at least one file of its own, which is DuckDB's rule and is why
1352    /// this expands one at a time rather than gathering everything and looking at the total. A
1353    /// list keeps its written order and its duplicates, so a file named twice is read twice, which
1354    /// was measured: the sort and the dedup belong to one pattern rather than to the list.
1355    fn file_paths(&self, expr: ExprRef, name: &str) -> Result<Vec<String>> {
1356        let mut paths = Vec::new();
1357        for pattern in self.file_patterns(expr, name)? {
1358            paths.extend(files(&pattern)?);
1359        }
1360        Ok(paths)
1361    }
1362
1363    /// The patterns a table function argument names, which have to be constants.
1364    ///
1365    /// A table function that reads a file is resolved by opening the file, and that happens here
1366    /// rather than when the query runs, because the rest of the statement cannot bind until the
1367    /// column names are known. So the path has to be something this binder can work out without
1368    /// running anything, and a literal is that. DuckDB folds a constant expression first, so
1369    /// `read_parquet('a' || '.parquet')` works there, and folding is M1 work that this will pick up
1370    /// for free once the optimizer runs before the plan is finished rather than after.
1371    ///
1372    /// One string is one pattern and a list is one pattern an item, which is DuckDB's pair of
1373    /// overloads. A null is a different sentence in each of them, both of them measured.
1374    fn file_patterns(&self, expr: ExprRef, name: &str) -> Result<Vec<String>> {
1375        let Expr::Constant(reference) = *self.plan.expr(expr) else {
1376            return Err(Error::not_implemented(
1377                "a table function file name that is not a constant",
1378            ));
1379        };
1380        match self.plan.value(reference) {
1381            Value::Varchar(path) => Ok(vec![path.clone()]),
1382            // DuckDB's own wording, which says list because its other overload takes one.
1383            Value::Null => Err(Error::parser(format!("{name} cannot take NULL list as parameter"))),
1384            Value::List { values, .. } => values
1385                .iter()
1386                .map(|value| match value {
1387                    Value::Varchar(path) => Ok(path.clone()),
1388                    _ => Err(Error::parser(format!(
1389                        "{name} reader cannot take NULL input as parameter"
1390                    ))),
1391                })
1392                .collect(),
1393            other => {
1394                Err(Error::internal(format!("a file name bound as VARCHAR arrived as {other}")))
1395            }
1396        }
1397    }
1398
1399    #[allow(clippy::too_many_arguments)]
1400    fn bind_join(
1401        &mut self,
1402        ast: &Ast,
1403        left: ast::SourceRef,
1404        right: ast::SourceRef,
1405        kind: ast::JoinKind,
1406        natural: bool,
1407        on: ast::ExprRef,
1408        using: ast::Slice,
1409    ) -> Result<(NodeRef, Scope)> {
1410        let (left_node, left_scope) = self.bind_source(ast, left)?;
1411        let (right_node, right_scope) = self.bind_source(ast, right)?;
1412        let split = left_scope.len();
1413        let mut scope = left_scope.concat(right_scope);
1414
1415        // NATURAL is USING over whatever both sides happen to call the same thing, which is why it
1416        // is resolved here and never reaches the plan as its own idea.
1417        let merged: Vec<String> = if natural {
1418            let mut names = Vec::new();
1419            for (at, column) in scope.columns.iter().enumerate().take(split) {
1420                if scope.columns[split..].iter().any(|right| same_name(&right.name, &column.name))
1421                    && !names.iter().any(|held: &String| same_name(held, &column.name))
1422                {
1423                    let _ = at;
1424                    names.push(column.name.clone());
1425                }
1426            }
1427            names
1428        } else {
1429            ast.name(using).map(str::to_string).collect()
1430        };
1431
1432        let mut conditions = Vec::new();
1433        let mut dropped = Vec::new();
1434        for name in &merged {
1435            let left_at = scope.columns[..split]
1436                .iter()
1437                .position(|column| same_name(&column.name, name))
1438                .ok_or_else(|| {
1439                    Error::binder(format!(
1440                        "column \"{name}\" specified in USING clause does not exist in left table"
1441                    ))
1442                })?;
1443            let right_at = scope.columns[split..]
1444                .iter()
1445                .position(|column| same_name(&column.name, name))
1446                .map(|at| at + split)
1447                .ok_or_else(|| {
1448                    Error::binder(format!(
1449                        "column \"{name}\" specified in USING clause does not exist in right table"
1450                    ))
1451                })?;
1452            let left_column = &scope.columns[left_at];
1453            let (left_binding, left_type) = (left_column.binding, left_column.ty.clone());
1454            let right_column = &scope.columns[right_at];
1455            let (right_binding, right_type) = (right_column.binding, right_column.ty.clone());
1456            let left_expr = self.plan.add_expr(Expr::Column(left_binding), left_type);
1457            let right_expr = self.plan.add_expr(Expr::Column(right_binding), right_type);
1458            conditions.push(self.compare(rudb_plan::CompareOp::Equal, left_expr, right_expr)?);
1459            dropped.push(right_at);
1460        }
1461        // A joined-on column appears once, so the right side's copy goes. Dropping from the back
1462        // keeps the positions of the ones still to drop correct.
1463        dropped.sort_unstable();
1464        for at in dropped.into_iter().rev() {
1465            scope.remove(at);
1466        }
1467
1468        if on != NONE {
1469            if !merged.is_empty() {
1470                return Err(Error::binder("a join cannot have both ON and USING"));
1471            }
1472            self.clause = "JOIN condition";
1473            let predicate = self.bind_expr(ast, on, &scope)?;
1474            conditions.push(self.as_boolean(predicate, "JOIN")?);
1475        }
1476
1477        if kind == ast::JoinKind::Cross {
1478            if !conditions.is_empty() {
1479                return Err(Error::binder("a CROSS JOIN cannot have a condition"));
1480            }
1481            let node =
1482                self.plan.add_node(Node::CrossProduct { left: left_node, right: right_node });
1483            return Ok((node, scope));
1484        }
1485        if conditions.is_empty() && kind == ast::JoinKind::Inner {
1486            let node =
1487                self.plan.add_node(Node::CrossProduct { left: left_node, right: right_node });
1488            return Ok((node, scope));
1489        }
1490        let kind = match kind {
1491            ast::JoinKind::Inner | ast::JoinKind::Cross => JoinKind::Inner,
1492            ast::JoinKind::Left => JoinKind::Left,
1493            ast::JoinKind::Right => JoinKind::Right,
1494            ast::JoinKind::Full => JoinKind::Full,
1495            ast::JoinKind::Semi => JoinKind::Semi,
1496            ast::JoinKind::Anti => JoinKind::Anti,
1497            ast::JoinKind::Positional => JoinKind::Positional,
1498        };
1499        let conditions = self.plan.add_expr_list(&conditions);
1500        let node =
1501            self.plan.add_node(Node::Join { left: left_node, right: right_node, kind, conditions });
1502        Ok((node, scope))
1503    }
1504
1505    // -------------------------------------------------------------- aggregates
1506
1507    /// Binds an aggregate call, records it, and hands back a reference to where its result lands.
1508    pub(crate) fn bind_aggregate(
1509        &mut self,
1510        ast: &Ast,
1511        name: &str,
1512        args: &[ast::ExprRef],
1513        distinct: bool,
1514        scope: &Scope,
1515    ) -> Result<ExprRef> {
1516        if self.in_aggregate {
1517            return Err(Error::binder(format!(
1518                "aggregate function calls cannot be nested, and {name}() is inside one"
1519            )));
1520        }
1521        if self.aggregation.is_none() {
1522            return Err(Error::binder(format!(
1523                "aggregate function calls cannot be used in the {}",
1524                self.clause
1525            )));
1526        }
1527        self.in_aggregate = true;
1528        let mut bound = Vec::with_capacity(args.len());
1529        let mut failure = None;
1530        for &arg in args {
1531            match self.bind_expr(ast, arg, scope) {
1532                Ok(expr) => bound.push(expr),
1533                Err(error) => {
1534                    failure = Some(error);
1535                    break;
1536                }
1537            }
1538        }
1539        self.in_aggregate = false;
1540        if let Some(error) = failure {
1541            return Err(error);
1542        }
1543
1544        let types: Vec<LogicalType> =
1545            bound.iter().map(|&arg| self.plan.expr_type(arg).clone()).collect();
1546        let resolved = resolve(name, &types)?;
1547        let mut cast = Vec::with_capacity(bound.len());
1548        for (arg, wanted) in bound.iter().zip(&resolved.arguments) {
1549            cast.push(self.cast_to(*arg, wanted));
1550        }
1551        let args = self.plan.add_expr_list(&cast);
1552        let name = self.plan.intern(resolved.name);
1553        let ty = resolved.returns;
1554        let call =
1555            self.plan.add_expr(Expr::Aggregate { name, args, distinct, filter: None }, ty.clone());
1556
1557        // Two identical aggregates are one column of the aggregate's output. `SELECT sum(x),
1558        // sum(x) / count(*)` computes one sum, not two.
1559        let existing = self.aggregation.as_ref().map(|held| held.aggregates.clone());
1560        let existing = existing.unwrap_or_default();
1561        let at = match existing.iter().position(|&held| self.same_expr(held, call)) {
1562            Some(at) => at,
1563            None => {
1564                let aggregation = self.aggregation.as_mut().expect("checked above");
1565                aggregation.aggregates.push(call);
1566                aggregation.aggregates.len() - 1
1567            }
1568        };
1569        let aggregation = self.aggregation.as_ref().expect("checked above");
1570        let (index, groups) = (aggregation.index, aggregation.groups.len());
1571        Ok(self.column(index, groups + at, ty))
1572    }
1573
1574    /// Rewrites a bound expression into one the aggregate's output can answer.
1575    ///
1576    /// A subexpression that is one of the group expressions becomes a reference to that group. A
1577    /// column that is neither grouped nor inside an aggregate is the error every SQL user has seen,
1578    /// and it is reported here because this is the first point where it is knowable.
1579    pub(crate) fn over_aggregate(&mut self, expr: ExprRef, scope: &Scope) -> Result<ExprRef> {
1580        let Some(aggregation) = self.aggregation.as_ref() else {
1581            return Ok(expr);
1582        };
1583        let index = aggregation.index;
1584        let groups = aggregation.groups.clone();
1585        for (at, group) in groups.iter().enumerate() {
1586            if self.same_expr(expr, *group) {
1587                let ty = self.plan.expr_type(*group).clone();
1588                return Ok(self.column(index, at, ty));
1589            }
1590        }
1591        let ty = self.plan.expr_type(expr).clone();
1592        match self.plan.expr(expr).clone() {
1593            Expr::Column(binding) if binding.table == index => Ok(expr),
1594            Expr::Column(binding) => {
1595                let name =
1596                    scope.columns.iter().find(|column| column.binding == binding).map_or_else(
1597                        || "a column".to_string(),
1598                        |column| format!("\"{}\"", column.name),
1599                    );
1600                Err(Error::binder(format!(
1601                    "column {name} must appear in the GROUP BY clause or must be part of an aggregate function"
1602                )))
1603            }
1604            Expr::Constant(_) | Expr::Aggregate { .. } => Ok(expr),
1605            Expr::Cast { input, try_cast } => {
1606                let input = self.over_aggregate(input, scope)?;
1607                Ok(self.plan.add_expr(Expr::Cast { input, try_cast }, ty))
1608            }
1609            Expr::Compare { op, left, right } => {
1610                let left = self.over_aggregate(left, scope)?;
1611                let right = self.over_aggregate(right, scope)?;
1612                Ok(self.plan.add_expr(Expr::Compare { op, left, right }, ty))
1613            }
1614            Expr::Conjunction { op, children } => {
1615                let written = self.plan.expr_list(children).to_vec();
1616                let mut rewritten = Vec::with_capacity(written.len());
1617                for child in written {
1618                    rewritten.push(self.over_aggregate(child, scope)?);
1619                }
1620                let children = self.plan.add_expr_list(&rewritten);
1621                Ok(self.plan.add_expr(Expr::Conjunction { op, children }, ty))
1622            }
1623            Expr::Function { name, args } => {
1624                let written = self.plan.expr_list(args).to_vec();
1625                let mut rewritten = Vec::with_capacity(written.len());
1626                for arg in written {
1627                    rewritten.push(self.over_aggregate(arg, scope)?);
1628                }
1629                let args = self.plan.add_expr_list(&rewritten);
1630                Ok(self.plan.add_expr(Expr::Function { name, args }, ty))
1631            }
1632            Expr::Case { arms, otherwise } => {
1633                let written = self.plan.arm_list(arms).to_vec();
1634                let mut rewritten = Vec::with_capacity(written.len());
1635                for arm in written {
1636                    let when = self.over_aggregate(arm.when, scope)?;
1637                    let then = self.over_aggregate(arm.then, scope)?;
1638                    rewritten.push(rudb_plan::Arm { when, then });
1639                }
1640                let otherwise = match otherwise {
1641                    Some(expr) => Some(self.over_aggregate(expr, scope)?),
1642                    None => None,
1643                };
1644                let arms = self.plan.add_arms(&rewritten);
1645                Ok(self.plan.add_expr(Expr::Case { arms, otherwise }, ty))
1646            }
1647        }
1648    }
1649
1650    /// Whether two bound expressions are the same expression, by shape rather than by reference.
1651    pub(crate) fn same_expr(&self, left: ExprRef, right: ExprRef) -> bool {
1652        same_expr(&self.plan, left, right)
1653    }
1654}
1655
1656/// The named parameters a table function call was written with.
1657///
1658/// A struct rather than the fields loose, because the seventeen DuckDB has on `read_parquet` and the
1659/// thirty on `read_csv` are all going to want somewhere to go, and because a call with none of them
1660/// written should read as the default of this rather than as a bare false somewhere.
1661///
1662/// The CSV half goes on to the reader and is opened with, here and again in the executor. The
1663/// Parquet half is answered here and nothing downstream sees it, which is what `binary_as_string`
1664/// turning a BLOB column into a VARCHAR one is.
1665#[derive(Debug, Default)]
1666struct Options {
1667    /// `binary_as_string`, which says an unannotated byte array column in a Parquet file holds
1668    /// text. The ClickBench file has twenty eight of those and every query reads them as strings.
1669    binary_as_string: bool,
1670    /// `all_varchar`, which reads every column of a CSV file as text rather than sniffing a type.
1671    all_varchar: bool,
1672    /// `delim`, `sep`, `quote`, `escape` and `header`, which are what the sniffer would decide.
1673    given: Given,
1674}
1675
1676impl Options {
1677    /// What these named parameters add up to.
1678    ///
1679    /// Each one was already checked against the function's list, so a name in here is a name that
1680    /// function takes and the value is already the type it wants. What is left is reading them, and
1681    /// the last one written wins, which is DuckDB's answer to `delim='|', delim=','` and was
1682    /// measured rather than assumed.
1683    fn of(written: &[(&'static str, Value, ExprRef)]) -> Result<Self> {
1684        let mut options = Self::default();
1685        for (parameter, value, _) in written {
1686            match (*parameter, value) {
1687                ("binary_as_string", Value::Boolean(on)) => options.binary_as_string = *on,
1688                ("all_varchar", Value::Boolean(on)) => options.all_varchar = *on,
1689                _ => {}
1690            }
1691        }
1692        let named: Vec<(&str, Value)> =
1693            written.iter().map(|(parameter, value, _)| (*parameter, value.clone())).collect();
1694        options.given = csv_given(&named)?;
1695        Ok(options)
1696    }
1697}
1698
1699/// DuckDB's complaint about a named parameter that was given a null, which is a different sentence
1700/// for almost every parameter.
1701///
1702/// Three of them were measured on `v2.0.0-dev84237` and no two agree: `binary_as_string` is the
1703/// first, `all_varchar` is the second and `header` is the third. They read like three people each
1704/// writing the message in front of them, which is what they are, and a harness that compares error
1705/// text compares all of it. Anything not measured gets the first one, which is the most general of
1706/// the three.
1707fn null_parameter(function: TableFunction, parameter: &str) -> String {
1708    match parameter {
1709        "header" => format!("\"{parameter}\" expects a non-null boolean value (e.g. TRUE or 1)"),
1710        "all_varchar" => format!("{} \"{parameter}\" cannot be NULL", function.name()),
1711        _ => format!("Cannot use NULL as argument to \"{parameter}\""),
1712    }
1713}
1714
1715/// The complaint about a `REPLACE` entry that named a column the star did not stand for.
1716///
1717/// It reads like the complaint about any other name that is not there, down to the list of names
1718/// that are, because from the writer's side it is the same mistake.
1719fn missing_replacement(name: &str, input: &Scope) -> Error {
1720    Error::binder(format!(
1721        "Column \"{name}\" in REPLACE list not found in FROM clause{}",
1722        input.candidates()
1723    ))
1724}
1725
1726/// A sort key with SQL's defaults filled in.
1727///
1728/// Unstated is ascending, and unstated nulls go where the direction puts them, which is last for
1729/// ascending and first for descending. That is DuckDB's rule and it is the one that makes
1730/// `ORDER BY x DESC` the exact reverse of `ORDER BY x`.
1731fn sort_key(expr: ExprRef, item: ast::OrderItem) -> SortKey {
1732    let descending = item.order == Order::Descending;
1733    let nulls_first = match item.nulls {
1734        Nulls::First => true,
1735        Nulls::Last => false,
1736        Nulls::Unstated => descending,
1737    };
1738    SortKey { expr, descending, nulls_first }
1739}
1740
1741/// Structural equality over two expressions of one plan.
1742fn same_expr(plan: &Plan, left: ExprRef, right: ExprRef) -> bool {
1743    if left == right {
1744        return true;
1745    }
1746    if plan.expr_type(left) != plan.expr_type(right) {
1747        return false;
1748    }
1749    let lists = |left, right| {
1750        let left: &[ExprRef] = plan.expr_list(left);
1751        let right: &[ExprRef] = plan.expr_list(right);
1752        left.len() == right.len()
1753            && left.iter().zip(right).all(|(&left, &right)| same_expr(plan, left, right))
1754    };
1755    match (plan.expr(left), plan.expr(right)) {
1756        (Expr::Column(left), Expr::Column(right)) => left == right,
1757        (Expr::Constant(left), Expr::Constant(right)) => plan.value(*left) == plan.value(*right),
1758        (
1759            Expr::Cast { input: left, try_cast: left_try },
1760            Expr::Cast { input: right, try_cast: right_try },
1761        ) => left_try == right_try && same_expr(plan, *left, *right),
1762        (
1763            Expr::Compare { op: left_op, left: left_a, right: left_b },
1764            Expr::Compare { op: right_op, left: right_a, right: right_b },
1765        ) => {
1766            left_op == right_op
1767                && same_expr(plan, *left_a, *right_a)
1768                && same_expr(plan, *left_b, *right_b)
1769        }
1770        (
1771            Expr::Conjunction { op: left_op, children: left_children },
1772            Expr::Conjunction { op: right_op, children: right_children },
1773        ) => left_op == right_op && lists(*left_children, *right_children),
1774        (
1775            Expr::Function { name: left_name, args: left_args },
1776            Expr::Function { name: right_name, args: right_args },
1777        ) => plan.string(*left_name) == plan.string(*right_name) && lists(*left_args, *right_args),
1778        (
1779            Expr::Aggregate {
1780                name: left_name,
1781                args: left_args,
1782                distinct: left_distinct,
1783                filter: left_filter,
1784            },
1785            Expr::Aggregate {
1786                name: right_name,
1787                args: right_args,
1788                distinct: right_distinct,
1789                filter: right_filter,
1790            },
1791        ) => {
1792            plan.string(*left_name) == plan.string(*right_name)
1793                && left_distinct == right_distinct
1794                && match (left_filter, right_filter) {
1795                    (None, None) => true,
1796                    (Some(left), Some(right)) => same_expr(plan, *left, *right),
1797                    _ => false,
1798                }
1799                && lists(*left_args, *right_args)
1800        }
1801        (
1802            Expr::Case { arms: left_arms, otherwise: left_otherwise },
1803            Expr::Case { arms: right_arms, otherwise: right_otherwise },
1804        ) => {
1805            let left_arms = plan.arm_list(*left_arms);
1806            let right_arms = plan.arm_list(*right_arms);
1807            left_arms.len() == right_arms.len()
1808                && left_arms.iter().zip(right_arms).all(|(left, right)| {
1809                    same_expr(plan, left.when, right.when) && same_expr(plan, left.then, right.then)
1810                })
1811                && match (left_otherwise, right_otherwise) {
1812                    (None, None) => true,
1813                    (Some(left), Some(right)) => same_expr(plan, *left, *right),
1814                    _ => false,
1815                }
1816        }
1817        _ => false,
1818    }
1819}