Skip to main content

rudb_bind/
statement.rs

1//! From an `Ast` to a `Bound`, which is a statement rather than a query.
2//!
3//! A `SELECT` binds to a [`Plan`] and nothing else, and that is why [`bind`](crate::bind) can hand
4//! one back. `CREATE TABLE`, `DROP TABLE` and `INSERT` are not plans and are deliberately not being
5//! made into plans. A `Node::CreateTable` would be a node with no columns, no rows, no cost and no
6//! reason to be pushed past anything, which is to say a node the optimizer has to be told to leave
7//! alone and the executor has to special case at the root. `spec/09-optimizer.md` section 9.1 says
8//! every node in a plan produces rows, and a DDL statement does not, so it goes beside the plan and
9//! not inside it.
10//!
11//! What each variant carries is the statement with every name and type already resolved, so the
12//! thing that runs it does catalog calls and nothing else. An `INSERT` in particular arrives with
13//! a plan whose output is exactly the target's columns in the target's order and the target's
14//! types, with the casts and the nulls for unmentioned columns already in it, so appending is a
15//! loop over chunks.
16
17use rudb_catalog::{Catalog, Entry, QualifiedName, duplicate_check, same_name};
18use rudb_common::bounds::End;
19use rudb_common::{
20    Bound as ColumnBound, Clustering, Error, Field, LogicalType, Result, Session, Stat, Value,
21    Width,
22};
23use rudb_parse::ast::{self, Ast};
24use rudb_parse::{NONE, deparse, parse_ast};
25use rudb_plan::{Expr, ExprRef, Node, Plan, SortKey};
26
27use crate::binder::Binder;
28use crate::parameters::Parameters;
29
30/// One statement, bound.
31///
32/// Not `#[non_exhaustive]`. A new variant here is a new kind of statement, and the compiler
33/// pointing at every place that has to decide what to do with it is the whole value of the enum.
34#[derive(Debug)]
35pub enum Bound {
36    /// A query, which is the only one of these that produces rows.
37    Query(Plan),
38    /// `CREATE TABLE`.
39    CreateTable(CreateTable),
40    /// `CREATE VIEW`.
41    CreateView(CreateView),
42    /// `DROP TABLE` or `DROP VIEW`.
43    DropTable(DropTable),
44    /// `INSERT INTO`.
45    Insert(Insert),
46    /// `SET name = value`, or `RESET name`, which is the same thing with no value.
47    Setting(Setting),
48    /// Flushes a persistent database snapshot.
49    Checkpoint,
50    /// `EXPLAIN` over a query, holding the plan of the query rather than the query.
51    ///
52    /// The same `Plan` a [`Bound::Query`] would have carried, bound the same way and by the same
53    /// code. What makes it an explain is that the layer above optimizes it and prints it instead
54    /// of running it, which is the point: a plan that was built differently because somebody asked
55    /// to see it is not the plan that runs.
56    ///
57    /// With `analyze` set the layer above runs it as well and prints what happened on it. Still the
58    /// same plan, for the same reason.
59    ///
60    /// With `statistics` set it prints what the planner knew as well, which is the use and the class
61    /// behind every number in the plan. That one changes nothing about the plan or the run either.
62    Explain { plan: Plan, analyze: bool, statistics: bool },
63}
64
65/// A bound `SET` or `RESET`.
66///
67/// The value is a [`Value`] rather than an expression, because every setting there is takes a
68/// string or a number and nothing that runs one wants a plan. What a setting does with the value it
69/// gets is the setting's own business and is decided a layer up, since the binder has no idea what
70/// settings exist.
71///
72/// The narrow part of that is that the value has to already be a constant. `SET threads = 2 + 2` is
73/// four in DuckDB and is refused here, because folding it needs the expression rewriter and the
74/// rewriter is two layers above the binder. Nothing writes arithmetic in a `SET` and the refusal
75/// says what it is, so this waits for a reason to move.
76#[derive(Debug)]
77pub struct Setting {
78    /// The setting name, as written.
79    pub name: String,
80    /// The scope word, if one was written.
81    pub scope: ast::Scope,
82    /// The value, or `None` for a `RESET`.
83    pub value: Option<Value>,
84    /// Whether the statement was written as a bare `PRAGMA name`, which carries its value in it.
85    pub pragma: bool,
86}
87
88/// A bound `CREATE TABLE`.
89#[derive(Debug)]
90pub struct CreateTable {
91    /// The full name the table gets.
92    pub name: QualifiedName,
93    /// The columns, in order, with the types already resolved. For a `CREATE TABLE AS` these are
94    /// the query's output types under whatever names the statement or the query gave them.
95    pub columns: Vec<Field>,
96    /// The query to fill it from, for a `CREATE TABLE AS`.
97    pub source: Option<Plan>,
98    /// Whether an existing table of that name is left alone rather than being an error.
99    pub if_not_exists: bool,
100    /// Whether an existing table of that name is dropped first.
101    pub or_replace: bool,
102}
103
104/// A bound `CREATE VIEW`.
105///
106/// The body is the text that was written rather than the plan it bound to. It was bound once on the
107/// way through here, which is what refuses a view over a table that is not there, and the plan that
108/// came out of that is then thrown away, because a view follows the tables underneath it and a plan
109/// cannot. See [`rudb_catalog::View`].
110#[derive(Debug)]
111pub struct CreateView {
112    /// The full name the view gets.
113    pub name: QualifiedName,
114    /// The body, as written.
115    pub sql: String,
116    /// The whole statement written back out, which is what `duckdb_views()` reports as `sql`.
117    ///
118    /// Written here because this is the last place the tree is in reach. See
119    /// [`rudb_catalog::View::statement`] for what the column is and why it is not the text.
120    pub statement: String,
121    /// The column names the statement gave, which rename a prefix of what the body produces.
122    pub aliases: Vec<String>,
123    /// Whether an existing entry of that name is left alone rather than being an error.
124    pub if_not_exists: bool,
125    /// Whether an existing entry of that name is dropped first.
126    pub or_replace: bool,
127    /// The columns binding the body produced, after the alias list was applied.
128    ///
129    /// Worked out here because this is where the body is bound, and carried to the catalog because
130    /// that is where `duckdb_columns()` and `duckdb_views()` read it from. See the doc on
131    /// `rudb_catalog::View` for why the catalog keeps a list it will have to refresh later.
132    pub columns: Vec<Field>,
133}
134
135/// A bound `DROP TABLE` or `DROP VIEW`.
136#[derive(Debug)]
137pub struct DropTable {
138    /// The tables or views to drop, already resolved. With `IF EXISTS` a name that does not resolve
139    /// is not in here at all, which is what makes running this a sequence of drops that cannot
140    /// fail for being missing. Dropping one of these as the wrong type still can, because `DROP
141    /// TABLE IF EXISTS v` where `v` is a view is an error in DuckDB and was measured to be one.
142    pub names: Vec<QualifiedName>,
143    /// Which of the two the statement said it was dropping.
144    pub kind: Entry,
145}
146
147/// A bound `INSERT`.
148#[derive(Debug)]
149pub struct Insert {
150    /// The table to append to.
151    pub name: QualifiedName,
152    /// The rows to append. The output is the table's columns, in the table's order, with the
153    /// table's types, so nothing between here and the append has a decision left to make.
154    pub source: Plan,
155}
156
157/// Binds one parsed statement against a catalog.
158///
159/// # Errors
160///
161/// If the script does not hold exactly one statement, if a name does not resolve, if a type does
162/// not work out, or if the statement uses something that is not bound yet.
163pub fn bind_statement(ast: &Ast, catalog: &Catalog) -> Result<Bound> {
164    bind_statement_with(ast, catalog, &Parameters::new(), &Session::new())
165}
166
167/// Binds one parsed statement against a catalog, with values for its parameters and its settings.
168///
169/// This is the prepared statement path. The statement is parsed once and bound once per set of
170/// values, so a parameter is a constant by the time the plan exists and everything after the binder
171/// sees an ordinary query. That is why there is no parameter in `rudb_plan::Expr`.
172///
173/// # Errors
174///
175/// Everything [`bind_statement`] reports, plus an error for a parameter that was given no value.
176pub fn bind_statement_with(
177    ast: &Ast,
178    catalog: &Catalog,
179    parameters: &Parameters,
180    session: &Session,
181) -> Result<Bound> {
182    bind_one(ast, catalog, parameters, session, false)
183}
184
185/// Binds one statement the way [`bind_statement_with`] does, except that a query reads a Parquet
186/// file that could go through a native mirror from its columns and row count alone.
187///
188/// For the first bind of a query that will be bound again once its mirrors are in. A query that
189/// comes back with [`rudb_plan::Plan::wanted_mirrors`] empty was bound in full and can run. One that
190/// comes back with any must be bound again with [`bind_statement_with`] before it runs, because the
191/// reads that asked for a mirror were bound without the bounds and the distinct counts the
192/// optimizer would have used.
193///
194/// # Errors
195///
196/// Everything [`bind_statement_with`] reports.
197pub fn bind_statement_outlined(
198    ast: &Ast,
199    catalog: &Catalog,
200    parameters: &Parameters,
201    session: &Session,
202) -> Result<Bound> {
203    bind_one(ast, catalog, parameters, session, true)
204}
205
206fn bind_one(
207    ast: &Ast,
208    catalog: &Catalog,
209    parameters: &Parameters,
210    session: &Session,
211    outlined: bool,
212) -> Result<Bound> {
213    let statement = match ast.statements.as_slice() {
214        [statement] => *statement,
215        [] => return Err(Error::binder("no statement to bind")),
216        _ => return Err(Error::not_implemented("a script of more than one statement")),
217    };
218    match statement {
219        ast::Statement::Query(query) => {
220            let mut binder = Binder::with(catalog, parameters, session);
221            binder.outlined = outlined;
222            let (root, _) = binder.bind_query(ast, query)?;
223            Ok(Bound::Query(finish(binder, root)?))
224        }
225        ast::Statement::CreateTable(index) => {
226            create_table(ast, catalog, parameters, session, index)
227        }
228        ast::Statement::CreateView(index) => create_view(ast, catalog, parameters, session, index),
229        ast::Statement::DropTable(index) => drop_table(ast, catalog, index),
230        ast::Statement::Insert(index) => insert(ast, catalog, parameters, session, index),
231        ast::Statement::Set(index) | ast::Statement::Reset(index) => {
232            setting(ast, catalog, parameters, session, index)
233        }
234        ast::Statement::Checkpoint => Ok(Bound::Checkpoint),
235        ast::Statement::Explain { query, analyze, statistics } => {
236            let mut binder = Binder::with(catalog, parameters, session);
237            let (root, _) = binder.bind_query(ast, query)?;
238            Ok(Bound::Explain { plan: finish(binder, root)?, analyze, statistics })
239        }
240    }
241}
242
243/// Parses and binds one statement, which is the whole front end in one call.
244///
245/// # Errors
246///
247/// Anything the parser or the binder reports.
248pub fn bind_statement_sql(sql: &str, catalog: &Catalog) -> Result<Bound> {
249    let ast = parse_ast(sql)?;
250    bind_statement(&ast, catalog)
251}
252
253/// Roots a binder's plan and checks it.
254fn finish(binder: Binder<'_>, root: rudb_plan::NodeRef) -> Result<Plan> {
255    let mut plan = binder.into_plan();
256    plan.set_root(root);
257    plan.validate()?;
258    Ok(plan)
259}
260
261fn create_table(
262    ast: &Ast,
263    catalog: &Catalog,
264    parameters: &Parameters,
265    session: &Session,
266    index: ast::CreateTableRef,
267) -> Result<Bound> {
268    let written = ast.create_table(index);
269    let parts: Vec<&str> = ast.name(written.name).collect();
270    let name = if written.temporary {
271        catalog.resolve_for_create_temporary(&parts)?
272    } else {
273        catalog.resolve_for_create(&parts)?
274    };
275    let defs = ast.column_defs(written.columns);
276    let (columns, source) = if written.query == NONE {
277        let mut columns = Vec::with_capacity(defs.len());
278        for def in defs {
279            let text = ast.string(def.ty);
280            if text.is_empty() {
281                return Err(Error::binder(format!(
282                    "Column \"{}\" was declared without a type",
283                    ast.string(def.name)
284                )));
285            }
286            let ty = LogicalType::parse(text)?;
287            let column = ast.string(def.name);
288            columns.push(if def.not_null {
289                Field::required(column, ty)
290            } else {
291                Field::new(column, ty)
292            });
293        }
294        (columns, None)
295    } else {
296        let mut binder = Binder::with(catalog, parameters, session);
297        let (root, scope) = binder.bind_query(ast, written.query)?;
298        if defs.len() > scope.len() {
299            // DuckDB's sentence, typo and all. A column list shorter than the query is fine and
300            // renames a prefix, so only this direction is an error.
301            return Err(Error::binder("Target table has more colum names than query result."));
302        }
303        let mut columns = Vec::with_capacity(scope.len());
304        for (at, column) in scope.columns.iter().enumerate() {
305            let named = match defs.get(at) {
306                Some(def) => ast.string(def.name).to_string(),
307                None => column.name.clone(),
308            };
309            columns.push(Field::new(named, column.ty.clone()));
310        }
311        if defs.is_empty() {
312            deduplicate(&mut columns);
313        }
314        (columns, Some(finish(binder, root)?))
315    };
316    duplicate_check(&columns)?;
317    Ok(Bound::CreateTable(CreateTable {
318        name,
319        columns,
320        source,
321        if_not_exists: written.if_not_exists,
322        or_replace: written.or_replace,
323    }))
324}
325
326/// Renames the columns a query repeated, which is what makes `CREATE TABLE t AS SELECT 1 AS a, 2 AS
327/// a` a table rather than an error.
328///
329/// A query is allowed to produce two columns of one name and `SELECT 1 AS a, 2 AS a` prints two
330/// columns called `a`, so a statement that turns a query into a table has to decide what to do with
331/// that, and DuckDB renames rather than refusing. The suffix is `_1`, then `_2`, counting up until
332/// the name is free, so a query that already has an `a_1` in it pushes the renamed column to `a_2`
333/// rather than colliding with it.
334///
335/// This only runs when the statement wrote no column list. With a list, even a short one, duckdb
336/// v1.4.1 takes the names as they come and a repeat is an error, so `CREATE TABLE t (z) AS SELECT 1
337/// AS a, 2 AS a` is a table of `z` and `a` and adding a third `a` to that query is a refusal.
338fn deduplicate(columns: &mut [Field]) {
339    for at in 0..columns.len() {
340        let taken = |name: &str, upto: usize, columns: &[Field]| {
341            columns[..upto].iter().any(|held| same_name(&held.name, name))
342        };
343        if !taken(&columns[at].name, at, columns) {
344            continue;
345        }
346        let mut suffix = 1;
347        let mut candidate = format!("{}_{suffix}", columns[at].name);
348        while taken(&candidate, at, columns) {
349            suffix += 1;
350            candidate = format!("{}_{suffix}", columns[at].name);
351        }
352        columns[at].name = candidate;
353    }
354}
355
356/// Binds a `CREATE VIEW`, which means binding the body and then throwing the plan away.
357///
358/// Throwing it away is the point. The body is bound here so that a view over a table that is not
359/// there is refused now rather than at the first select, and so that the column list can be checked
360/// against what the body actually produces. What the catalog keeps is the text, because a view
361/// follows the tables underneath it and a plan is a photograph of the day it was built.
362fn create_view(
363    ast: &Ast,
364    catalog: &Catalog,
365    parameters: &Parameters,
366    session: &Session,
367    index: ast::CreateViewRef,
368) -> Result<Bound> {
369    let written = ast.create_view(index);
370    let parts: Vec<&str> = ast.name(written.name).collect();
371    let name = if written.temporary {
372        catalog.resolve_for_create_temporary(&parts)?
373    } else {
374        catalog.resolve_for_create(&parts)?
375    };
376    let aliases: Vec<String> = ast.name(written.columns).map(str::to_string).collect();
377
378    let mut binder = Binder::with(catalog, parameters, session);
379    // The plan is thrown away and the columns are all that is kept, so a file is read for its
380    // columns and nothing else.
381    binder.outlined = true;
382    let (_, mut scope) = binder.bind_query(ast, written.query)?;
383    if aliases.len() > scope.len() {
384        return Err(Error::binder("More VIEW aliases than columns in query result"));
385    }
386    if !aliases.is_empty() {
387        let written: Vec<&str> = aliases.iter().map(String::as_str).collect();
388        scope.rename(&written, "unnamed_subquery")?;
389    }
390
391    Ok(Bound::CreateView(CreateView {
392        name,
393        sql: ast.string(written.sql).to_string(),
394        statement: deparse::create_view(ast, index),
395        aliases,
396        if_not_exists: written.if_not_exists,
397        or_replace: written.or_replace,
398        columns: scope.fields(),
399    }))
400}
401
402fn drop_table(ast: &Ast, catalog: &Catalog, index: ast::DropTableRef) -> Result<Bound> {
403    let written = ast.drop_table(index);
404    let kind = if written.view { Entry::View } else { Entry::Table };
405    let mut names = Vec::new();
406    for &name in ast.name_list(written.names) {
407        let parts: Vec<&str> = ast.name(name).collect();
408        // The statement said which of the two it meant, so a name that is not there is a missing
409        // one of those and not a missing table.
410        match catalog.resolve_as(&parts, kind) {
411            Ok(resolved) => names.push(resolved),
412            Err(error) if written.if_exists => drop(error),
413            Err(error) => return Err(error),
414        }
415    }
416    Ok(Bound::DropTable(DropTable { names, kind }))
417}
418
419/// Binds a `SET` or a `RESET`, which is resolving its value and nothing else.
420///
421/// The name is not checked here. The binder knows what tables exist and has no idea what settings
422/// exist, since a setting is a knob on the engine rather than an entry in a catalog, and a version
423/// of this that held the list would be the binder holding a copy of something it cannot enforce.
424fn setting(
425    ast: &Ast,
426    catalog: &Catalog,
427    parameters: &Parameters,
428    session: &Session,
429    index: ast::SettingRef,
430) -> Result<Bound> {
431    let written = ast.setting(index);
432    let name = ast.string(written.name).to_string();
433    let value = if written.value == NONE {
434        None
435    } else {
436        let mut binder = Binder::with(catalog, parameters, session);
437        let bound = binder.bind_setting_value(ast, written.value)?;
438        let Expr::Constant(value) = *binder.plan().expr(bound) else {
439            return Err(Error::not_implemented(format!(
440                "a value for {name} that is not a constant"
441            )));
442        };
443        Some(binder.plan().value(value).clone())
444    };
445    Ok(Bound::Setting(Setting { name, scope: written.scope, value, pragma: written.pragma }))
446}
447
448/// Sorts an insert's rows into the order the target table declared.
449///
450/// Returns the input unchanged when the statement supplies none of the declared columns, because
451/// every one of them is then a constant null and sorting on a constant is a sort that buys nothing
452/// and costs a pass. A statement that supplies some of them sorts on those: the declaration is
453/// about the order the rows are written in, and the columns that are there still order them.
454///
455/// The leading key carries the width. `date_trunc('month', d)` and `d` sort the same rows into the
456/// same fragments for any predicate a month wide or wider, and the difference is what happens
457/// inside a month: bucketed, the second key orders the whole month, which is the key locality the
458/// joins want and the reason the width is part of the declaration at all.
459fn clustered(
460    binder: &mut Binder<'_>,
461    input: rudb_plan::NodeRef,
462    scope: &crate::scope::Scope,
463    clustering: &Clustering,
464    targets: &[usize],
465    fields: &[Field],
466) -> Result<rudb_plan::NodeRef> {
467    let mut keys: Vec<SortKey> = Vec::with_capacity(clustering.columns().len());
468    for (at, &column) in clustering.columns().iter().enumerate() {
469        let Some(from) = targets.iter().position(|&target| target == column as usize) else {
470            continue;
471        };
472        let source = &scope.columns[from];
473        let expr = binder.plan_mut().add_expr(Expr::Column(source.binding), source.ty.clone());
474        // Cast to the column's own type before bucketing, since the source of a load is a file
475        // whose date column can arrive as a timestamp and `date_trunc` gives back the type it was
476        // handed. Sorting on a different type than the column stores would still be an order, but
477        // it would not be the order the declaration names.
478        let expr = binder.checked_cast_to(expr, &fields[column as usize].ty, false)?;
479        let expr =
480            if at == 0 { bucketed(binder, expr, clustering.width(), fields, column) } else { expr };
481        keys.push(SortKey { expr, descending: false, nulls_first: false });
482    }
483    if keys.is_empty() {
484        return Ok(input);
485    }
486    let keys = binder.plan_mut().add_sort_keys(&keys);
487    Ok(binder.plan_mut().add_node(Node::Sort { input, keys }))
488}
489
490/// The declaration with an automatic width turned into the bucket the incoming rows ask for.
491///
492/// A declaration that named no width says the bucket should come from how many rows a partition
493/// would hold, and this is the only place that number is in reach. The rows are the source's, not
494/// the target's: a load into an empty table has a target with nothing to count, and the whole case
495/// the rule exists for is the first load of a big table. So the count and the range come off the
496/// source's own zones, which is the Parquet footer for a file and the directory for a table, and
497/// both are already on the plan because the estimator wanted them.
498///
499/// Everything about this is best effort and that is by design. The three widths hold the same rows
500/// and answer the same queries, so guessing wrong costs some pruning or some key locality and
501/// cannot cost an answer. A source that is a join, a group by or a values list has no zones to read
502/// and gets [`Width::DEFAULT`], which is what the fixed default was before the rule existed.
503fn fitted(
504    binder: &Binder<'_>,
505    scope: &crate::scope::Scope,
506    clustering: &Clustering,
507    targets: &[usize],
508) -> Clustering {
509    if clustering.width() != Width::Auto {
510        return clustering.clone();
511    }
512    let Some(from) = targets.iter().position(|&target| target == clustering.partition() as usize)
513    else {
514        return clustering.fitted(0, 0);
515    };
516    let source = &scope.columns[from];
517    let Some(zones) = binder.plan().sole_zones() else {
518        return clustering.fitted(0, 0);
519    };
520    // By name, and off whichever store the plan reads rather than off the one this column is bound
521    // to. The binding points at the projection over the scan, since a load is a projection into the
522    // target's types, and following a binding back through a projection is the optimizer's job. A
523    // load reads one table or one file, so the store with bounds on it is the store the name is in.
524    let Some(at) = zones.column(&source.name) else {
525        return clustering.fitted(0, 0);
526    };
527    let rows = zones.surviving(&[]).unwrap_or(0);
528    let days = span(&zones.extreme(at, End::Low), &zones.extreme(at, End::High)).unwrap_or(0);
529    clustering.fitted(rows, days)
530}
531
532/// How many days a column covers, from the smallest and largest values in it.
533///
534/// `None` wherever the two do not make a span, which is a column that is entirely null, a store
535/// that could not fold its parts into one answer, and a pair of bounds that are not the same shape.
536/// All of them mean the same thing here, which is that there is nothing to divide the row count by.
537fn span(low: &Stat<ColumnBound>, high: &Stat<ColumnBound>) -> Option<u64> {
538    let (Stat::Known { value: low, .. }, Stat::Known { value: high, .. }) = (low, high) else {
539        return None;
540    };
541    let days = match (low, high) {
542        // A date is a day count already, which is the common case and the only exact one.
543        (ColumnBound::Int(low), ColumnBound::Int(high)) => high.checked_sub(*low)?,
544        // A timestamp is a count of seconds at whichever unit the column keeps, so the span is that
545        // difference divided by a day's worth of them. A scale wide enough to overflow the divisor
546        // is a column no calendar covers and falls out as no span at all.
547        (
548            ColumnBound::Scaled { unscaled: low, scale: at },
549            ColumnBound::Scaled { unscaled: high, scale: to },
550        ) if at == to => {
551            let day = 86_400_i128.checked_mul(10_i128.checked_pow(u32::from(*at))?)?;
552            high.checked_sub(*low)? / day
553        }
554        _ => return None,
555    };
556    u64::try_from(days).ok()
557}
558
559/// Wraps a sort key in the calendar bucket its declaration asked for.
560fn bucketed(
561    binder: &mut Binder<'_>,
562    expr: ExprRef,
563    width: Width,
564    fields: &[Field],
565    column: u32,
566) -> ExprRef {
567    if width == Width::Exact {
568        return expr;
569    }
570    let unit = binder.plan_mut().add_value(Value::Varchar(width.to_string().to_lowercase()));
571    let unit = binder.plan_mut().add_expr(Expr::Constant(unit), LogicalType::Varchar);
572    let args = binder.plan_mut().add_expr_list(&[unit, expr]);
573    let name = binder.plan_mut().intern("date_trunc");
574    let ty = fields[column as usize].ty.clone();
575    binder.plan_mut().add_expr(Expr::Function { name, args }, ty)
576}
577
578fn insert(
579    ast: &Ast,
580    catalog: &Catalog,
581    parameters: &Parameters,
582    session: &Session,
583    index: ast::InsertRef,
584) -> Result<Bound> {
585    let written = ast.insert(index);
586    let parts: Vec<&str> = ast.name(written.name).collect();
587    let name = catalog.resolve(&parts)?;
588    if catalog.entry(&name)? == Entry::View {
589        // The binary's sentence, article and all. A view has no rows of its own to append to, and
590        // an updatable view is a rule about rewriting the insert that neither database has.
591        return Err(Error::catalog(format!("{} is not an table", name.table)));
592    }
593    let target = catalog.table(&name)?;
594    let fields: Vec<Field> = target.columns().to_vec();
595    let clustering = target.clustering().cloned();
596
597    // Which table column each source column lands in. Without a column list that is the first n
598    // columns in order, and with one it is whatever the list says, which is also the check that
599    // the list names columns the table has and names none of them twice.
600    let targets: Vec<usize> = if written.columns.is_empty() {
601        (0..fields.len()).collect()
602    } else {
603        let mut targets = Vec::new();
604        for column in ast.name(written.columns) {
605            let at = fields.iter().position(|field| same_name(&field.name, column)).ok_or_else(
606                || {
607                    Error::binder(format!(
608                        "Table \"{}\" does not have a column named \"{column}\"",
609                        name.table
610                    ))
611                },
612            )?;
613            if targets.contains(&at) {
614                return Err(Error::binder(format!(
615                    "Column \"{column}\" is named twice in the same INSERT"
616                )));
617            }
618            targets.push(at);
619        }
620        targets
621    };
622
623    let mut binder = Binder::with(catalog, parameters, session);
624    let (root, scope) = binder.bind_query(ast, written.source)?;
625    if scope.len() != targets.len() {
626        return Err(Error::binder(format!(
627            "Table \"{}\" has {} columns but {} values were supplied",
628            name.table,
629            targets.len(),
630            scope.len()
631        )));
632    }
633
634    // A table that declared what order its rows go in gets the sort here, under the projection
635    // rather than over it, because a projection does not reorder rows and the bindings the sort
636    // keys need are the ones the query just produced. This is the whole of the loader honouring
637    // the declaration: the rows arrive at the writer in order and the per fragment ranges, which
638    // are built from whatever order arrives, come out narrow instead of each covering the table.
639    let root = match &clustering {
640        None => root,
641        Some(clustering) => {
642            // The width is settled here and not on the table. A declaration that left the bucket to
643            // the data is a standing instruction, so it stays on the table as one and every load
644            // answers it with the rows that load is carrying. What the sort needs is an answer, and
645            // that is what this is.
646            let fitted = fitted(&binder, &scope, clustering, &targets);
647            clustered(&mut binder, root, &scope, &fitted, &targets, &fields)?
648        }
649    };
650
651    // The projection that makes the source look exactly like the table. Every column the statement
652    // did not name becomes a null of the column's own type, so the append never has to know that a
653    // column list was written at all.
654    let mut exprs: Vec<ExprRef> = Vec::with_capacity(fields.len());
655    let mut names = Vec::with_capacity(fields.len());
656    for (at, field) in fields.iter().enumerate() {
657        let expr = match targets.iter().position(|&target| target == at) {
658            Some(from) => {
659                let column = &scope.columns[from];
660                let expr =
661                    binder.plan_mut().add_expr(Expr::Column(column.binding), column.ty.clone());
662                binder.checked_cast_to(expr, &field.ty, false)?
663            }
664            None => {
665                // A typed null rather than `add_constant`, which would give it the null type and
666                // make the column's type depend on whether a row happened to be inserted into it.
667                let value = binder.plan_mut().add_value(Value::Null);
668                binder.plan_mut().add_expr(Expr::Constant(value), field.ty.clone())
669            }
670        };
671        exprs.push(expr);
672        let interned = binder.plan_mut().intern(&field.name);
673        names.push(interned);
674    }
675    let exprs = binder.plan_mut().add_expr_list(&exprs);
676    let names = binder.plan_mut().add_name_list(&names);
677    let index = binder.fresh_index();
678    let root = binder.plan_mut().add_node(Node::Project { input: root, index, exprs, names });
679    Ok(Bound::Insert(Insert { name, source: finish(binder, root)? }))
680}