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::{Arm, 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    /// `CREATE SCHEMA` or `DROP SCHEMA`.
45    Schema(SchemaChange),
46    /// `CREATE SEQUENCE` or `DROP SEQUENCE`.
47    Sequence(SequenceChange),
48    /// `CREATE TYPE` or `DROP TYPE`.
49    Type(TypeChange),
50    /// `ALTER TABLE` or `ALTER VIEW`.
51    Alter(Alter),
52    /// `CREATE INDEX` or `DROP INDEX`.
53    Index(IndexChange),
54    /// `INSERT INTO`.
55    Insert(Insert),
56    /// `SET name = value`, or `RESET name`, which is the same thing with no value.
57    Setting(Setting),
58    /// Flushes a persistent database snapshot, of the named database when one is named.
59    Checkpoint(Option<String>),
60    /// `ATTACH`.
61    Attach(Attach),
62    /// `DETACH`, with the name and whether `IF EXISTS` was written.
63    Detach { name: String, if_exists: bool },
64    /// `BEGIN`, `COMMIT` or `ROLLBACK`, which have nothing to bind and are carried as written.
65    Transaction(ast::Transaction),
66    /// `EXPLAIN` over a query, holding the plan of the query rather than the query.
67    ///
68    /// The same `Plan` a [`Bound::Query`] would have carried, bound the same way and by the same
69    /// code. What makes it an explain is that the layer above optimizes it and prints it instead
70    /// of running it, which is the point: a plan that was built differently because somebody asked
71    /// to see it is not the plan that runs.
72    ///
73    /// With `analyze` set the layer above runs it as well and prints what happened on it. Still the
74    /// same plan, for the same reason.
75    ///
76    /// With `statistics` set it prints what the planner knew as well, which is the use and the class
77    /// behind every number in the plan. That one changes nothing about the plan or the run either.
78    ///
79    /// With `codegen` set the layer above hands the plan to the compiled engine and prints what it
80    /// generated instead.
81    Explain { plan: Plan, analyze: bool, statistics: bool, codegen: bool },
82}
83
84/// A bound `SET` or `RESET`.
85///
86/// The value is a [`Value`] rather than an expression, because every setting there is takes a
87/// string or a number and nothing that runs one wants a plan. What a setting does with the value it
88/// gets is the setting's own business and is decided a layer up, since the binder has no idea what
89/// settings exist.
90///
91/// The narrow part of that is that the value has to already be a constant. `SET threads = 2 + 2` is
92/// four in DuckDB and is refused here, because folding it needs the expression rewriter and the
93/// rewriter is two layers above the binder. Nothing writes arithmetic in a `SET` and the refusal
94/// says what it is, so this waits for a reason to move.
95#[derive(Debug)]
96pub struct Setting {
97    /// The setting name, as written.
98    pub name: String,
99    /// The scope word, if one was written.
100    pub scope: ast::Scope,
101    /// The value, or `None` for a `RESET`.
102    pub value: Option<Value>,
103    /// Whether the statement was written as a bare `PRAGMA name`, which carries its value in it.
104    pub pragma: bool,
105}
106
107/// A bound `ATTACH`.
108///
109/// The path and the option values are constants, for the same reason a setting's value is: nothing
110/// that opens a file wants a plan, and the pin folds them to constants before it opens anything.
111#[derive(Debug)]
112pub struct Attach {
113    /// The path, which is `:memory:` or empty for a database with no file behind it.
114    pub path: String,
115    /// The name after `AS`, or `None` when the name comes from the path.
116    pub alias: Option<String>,
117    /// Whether `OR REPLACE` was written.
118    pub or_replace: bool,
119    /// Whether `IF NOT EXISTS` was written.
120    pub if_not_exists: bool,
121    /// The options, with the name as written and the value when one was.
122    pub options: Vec<(String, Option<Value>)>,
123}
124
125/// A bound `CREATE TABLE`.
126#[derive(Debug)]
127pub struct CreateTable {
128    /// The full name the table gets.
129    pub name: QualifiedName,
130    /// The columns, in order, with the types already resolved. For a `CREATE TABLE AS` these are
131    /// the query's output types under whatever names the statement or the query gave them.
132    pub columns: Vec<Field>,
133    /// The query to fill it from, for a `CREATE TABLE AS`.
134    pub source: Option<Plan>,
135    /// Whether an existing table of that name is left alone rather than being an error.
136    pub if_not_exists: bool,
137    /// Whether an existing table of that name is dropped first.
138    pub or_replace: bool,
139    /// The primary key and the unique constraints, over the columns by place.
140    pub keys: Vec<rudb_catalog::Key>,
141    /// Each column's `DEFAULT` as the SQL of its expression, or `None` for a column with none.
142    pub defaults: Vec<Option<String>>,
143    /// The sequences the defaults use, which the table depends on.
144    pub sequences: Vec<QualifiedName>,
145    /// The SQL of each `CHECK`, in the order written.
146    pub checks: Vec<String>,
147    /// The foreign keys, in the order written.
148    pub foreign: Vec<rudb_catalog::ForeignKey>,
149    /// Every constraint, in the order written.
150    pub order: Vec<rudb_catalog::Constraint>,
151}
152
153/// A bound `CREATE VIEW`.
154///
155/// The body is the text that was written rather than the plan it bound to. It was bound once on the
156/// way through here, which is what refuses a view over a table that is not there, and the plan that
157/// came out of that is then thrown away, because a view follows the tables underneath it and a plan
158/// cannot. See [`rudb_catalog::View`].
159#[derive(Debug)]
160pub struct CreateView {
161    /// The full name the view gets.
162    pub name: QualifiedName,
163    /// The body, as written.
164    pub sql: String,
165    /// The whole statement written back out, which is what `duckdb_views()` reports as `sql`.
166    ///
167    /// Written here because this is the last place the tree is in reach. See
168    /// [`rudb_catalog::View::statement`] for what the column is and why it is not the text.
169    pub statement: String,
170    /// The column names the statement gave, which rename a prefix of what the body produces.
171    pub aliases: Vec<String>,
172    /// Whether an existing entry of that name is left alone rather than being an error.
173    pub if_not_exists: bool,
174    /// Whether an existing entry of that name is dropped first.
175    pub or_replace: bool,
176    /// The columns binding the body produced, after the alias list was applied.
177    ///
178    /// Worked out here because this is where the body is bound, and carried to the catalog because
179    /// that is where `duckdb_columns()` and `duckdb_views()` read it from. See the doc on
180    /// `rudb_catalog::View` for why the catalog keeps a list it will have to refresh later.
181    pub columns: Vec<Field>,
182}
183
184/// A bound `CREATE SCHEMA` or `DROP SCHEMA`.
185///
186/// Only the name is resolved here. Whether the schema is there is a question for the catalog the
187/// statement runs against, which is where `IF NOT EXISTS`, `IF EXISTS` and `OR REPLACE` are
188/// answered.
189#[derive(Debug, Clone, PartialEq, Eq)]
190pub struct SchemaChange {
191    /// The database the schema is in.
192    pub catalog: String,
193    /// The schema's own name.
194    pub name: String,
195    /// Whether this is a `DROP` rather than a `CREATE`.
196    pub drop: bool,
197    /// Whether a create over a schema that is there, or a drop of one that is not, does nothing.
198    pub quiet: bool,
199    /// Whether a create drops a schema that is there first, which a schema that holds anything
200    /// refuses.
201    pub or_replace: bool,
202    /// Whether a drop takes everything in the schema with it.
203    pub cascade: bool,
204}
205
206/// A bound `CREATE SEQUENCE` or `DROP SEQUENCE`.
207#[derive(Debug, Clone, PartialEq, Eq)]
208pub struct SequenceChange {
209    /// The full name. `None` for a `DROP SEQUENCE IF EXISTS` of one that is not there.
210    pub name: Option<QualifiedName>,
211    /// Whether this is a `DROP` rather than a `CREATE`.
212    pub drop: bool,
213    /// Whether a create over a sequence that is there does nothing.
214    pub if_not_exists: bool,
215    /// Whether a create replaces a sequence that is there.
216    pub or_replace: bool,
217    /// Whether a drop takes the tables whose defaults use the sequence with it.
218    pub cascade: bool,
219    /// What a create settled.
220    pub options: rudb_common::sequence::Options,
221    /// The table or view an `ALTER SEQUENCE ... OWNED BY` gives the sequence to, which makes this
222    /// an alter rather than a create.
223    pub owner: Option<QualifiedName>,
224}
225
226/// A bound `CREATE TYPE` or `DROP TYPE`.
227#[derive(Debug, Clone, PartialEq, Eq)]
228pub struct TypeChange {
229    /// The full name. `None` for a `DROP TYPE IF EXISTS` of one that is not there.
230    pub name: Option<QualifiedName>,
231    /// What a create makes the name stand for, and `None` on a drop.
232    pub ty: Option<LogicalType>,
233    /// The made types a create read its type through, which it will depend on.
234    pub uses: Vec<QualifiedName>,
235    /// Whether a create over a type that is there does nothing.
236    pub if_not_exists: bool,
237    /// Whether a create replaces a type that is there.
238    pub or_replace: bool,
239    /// Whether a drop takes the types made from this one with it.
240    pub cascade: bool,
241}
242
243/// A bound `ALTER TABLE` or `ALTER VIEW`.
244#[derive(Debug)]
245pub struct Alter {
246    /// The table or view, or `None` when `IF EXISTS` found nothing to change.
247    pub name: Option<QualifiedName>,
248    /// The change, or `None` when an `IF EXISTS` or an `IF NOT EXISTS` on a column made it one.
249    pub alteration: Option<rudb_catalog::Alteration>,
250    /// Every row of the table as it reads after the change, for the changes that move data.
251    pub rewrite: Option<Plan>,
252}
253
254/// A bound `CREATE INDEX` or `DROP INDEX`.
255#[derive(Debug)]
256pub struct IndexChange {
257    /// The table a create is over, and `None` on a drop.
258    pub table: Option<QualifiedName>,
259    /// The index a create makes, stamped by the catalog when it goes in, and `None` on a drop.
260    pub index: Option<rudb_catalog::Index>,
261    /// The name a drop removes, as written, and empty on a create.
262    pub name: Vec<String>,
263    /// Whether `IF NOT EXISTS` or `IF EXISTS` was written.
264    pub quiet: bool,
265}
266
267/// A bound `DROP TABLE` or `DROP VIEW`.
268#[derive(Debug)]
269pub struct DropTable {
270    /// The tables or views to drop, already resolved. With `IF EXISTS` a name that does not resolve
271    /// is not in here at all, which is what makes running this a sequence of drops that cannot
272    /// fail for being missing. Dropping one of these as the wrong type still can, because `DROP
273    /// TABLE IF EXISTS v` where `v` is a view is an error in DuckDB and was measured to be one.
274    pub names: Vec<QualifiedName>,
275    /// Which of the two the statement said it was dropping.
276    pub kind: Entry,
277}
278
279/// A bound `INSERT`.
280#[derive(Debug)]
281pub struct Insert {
282    /// The table to append to.
283    pub name: QualifiedName,
284    /// The rows to append. The output is the table's columns, in the table's order, with the
285    /// table's types, so nothing between here and the append has a decision left to make.
286    pub source: Plan,
287    /// Which of the three writes the source is for.
288    pub write: Write,
289    /// The `RETURNING` list, bound as a query over the table and run over the rows the statement
290    /// wrote in place of the table's own.
291    pub returning: Option<Box<Plan>>,
292    /// What an append does with a row whose key the table already holds.
293    pub conflict: Option<Conflict>,
294    /// The table's `CHECK` constraints, for the rows an append or an update writes.
295    pub checks: Option<Checks>,
296}
297
298/// The `CHECK` constraints of a table, bound as one query over it.
299///
300/// The query answers, for each row the table holds, whether each constraint fails on it. The write
301/// runs it with the table standing in for the rows it wrote, so a failed constraint is found before
302/// anything the statement wrote is kept.
303#[derive(Debug)]
304pub struct Checks {
305    /// One boolean column per constraint, true where the row fails it. A null is a pass.
306    pub plan: Box<Plan>,
307    /// The pin's message for each constraint, in the same order as the columns.
308    pub messages: Vec<String>,
309}
310
311/// A bound `ON CONFLICT`, `INSERT OR REPLACE` or `INSERT OR IGNORE`.
312#[derive(Debug)]
313pub struct Conflict {
314    /// Which of the table's keys a clash is on, or `None` for any of them.
315    pub key: Option<usize>,
316    /// What happens to a row that clashes.
317    pub action: ConflictAction,
318}
319
320/// What happens to a row whose key the table already holds.
321#[derive(Debug)]
322pub enum ConflictAction {
323    /// The row is dropped.
324    Nothing,
325    /// The held row takes the new row's values in these columns.
326    Replace(Vec<usize>),
327    /// The held row takes the values the plan works out in these columns. The plan reads the held
328    /// rows as the table and the new rows as [`QualifiedName::excluded`], one of each per row it
329    /// answers, and after a value for each column answers whether the row is updated at all.
330    Update {
331        /// The columns that are set, by place in the table.
332        columns: Vec<usize>,
333        /// The query that works the values out.
334        plan: Box<Plan>,
335    },
336}
337
338/// What an [`Insert`]'s source means for the table.
339#[derive(Debug, Clone, Copy, PartialEq, Eq)]
340pub enum Write {
341    /// The rows are added to the table.
342    Append,
343    /// The rows are the whole table afterwards, and one more column after the table's says which
344    /// of them the statement changed, so it can count them and return them.
345    Update,
346    /// The rows are the table as it was, and the column after the table's says which of them
347    /// the statement deletes. The table keeps the rest.
348    Delete,
349}
350
351/// The `RETURNING` query of a writing statement, bound over the table it writes.
352fn returning(
353    ast: &Ast,
354    catalog: &Catalog,
355    parameters: &Parameters,
356    session: &Session,
357    query: Option<ast::QueryRef>,
358) -> Result<Option<Box<Plan>>> {
359    let Some(query) = query else { return Ok(None) };
360    let mut binder = Binder::with(catalog, parameters, session);
361    let (root, _) = binder.bind_query(ast, query)?;
362    Ok(Some(Box::new(finish(binder, root)?)))
363}
364
365/// Binds one parsed statement against a catalog.
366///
367/// # Errors
368///
369/// If the script does not hold exactly one statement, if a name does not resolve, if a type does
370/// not work out, or if the statement uses something that is not bound yet.
371pub fn bind_statement(ast: &Ast, catalog: &Catalog) -> Result<Bound> {
372    bind_statement_with(ast, catalog, &Parameters::new(), &Session::new())
373}
374
375/// Binds one parsed statement against a catalog, with values for its parameters and its settings.
376///
377/// This is the prepared statement path. The statement is parsed once and bound once per set of
378/// values, so a parameter is a constant by the time the plan exists and everything after the binder
379/// sees an ordinary query. That is why there is no parameter in `rudb_plan::Expr`.
380///
381/// # Errors
382///
383/// Everything [`bind_statement`] reports, plus an error for a parameter that was given no value.
384pub fn bind_statement_with(
385    ast: &Ast,
386    catalog: &Catalog,
387    parameters: &Parameters,
388    session: &Session,
389) -> Result<Bound> {
390    bind_one(ast, catalog, parameters, session, false)
391}
392
393/// Binds one statement the way [`bind_statement_with`] does, except that a query reads a Parquet
394/// file that could go through a native mirror from its columns and row count alone.
395///
396/// For the first bind of a query that will be bound again once its mirrors are in. A query that
397/// comes back with [`rudb_plan::Plan::wanted_mirrors`] empty was bound in full and can run. One that
398/// comes back with any must be bound again with [`bind_statement_with`] before it runs, because the
399/// reads that asked for a mirror were bound without the bounds and the distinct counts the
400/// optimizer would have used.
401///
402/// # Errors
403///
404/// Everything [`bind_statement_with`] reports.
405pub fn bind_statement_outlined(
406    ast: &Ast,
407    catalog: &Catalog,
408    parameters: &Parameters,
409    session: &Session,
410) -> Result<Bound> {
411    bind_one(ast, catalog, parameters, session, true)
412}
413
414fn bind_one(
415    ast: &Ast,
416    catalog: &Catalog,
417    parameters: &Parameters,
418    session: &Session,
419    outlined: bool,
420) -> Result<Bound> {
421    let statement = match ast.statements.as_slice() {
422        [statement] => *statement,
423        [] => return Err(Error::binder("no statement to bind")),
424        _ => return Err(Error::not_implemented("a script of more than one statement")),
425    };
426    match statement {
427        ast::Statement::Query(query) => {
428            let mut binder = Binder::with(catalog, parameters, session);
429            binder.outlined = outlined;
430            let (root, _) = binder.bind_query(ast, query)?;
431            Ok(Bound::Query(finish(binder, root)?))
432        }
433        ast::Statement::CreateTable(index) => {
434            create_table(ast, catalog, parameters, session, index)
435        }
436        ast::Statement::CreateView(index) => create_view(ast, catalog, parameters, session, index),
437        ast::Statement::DropTable(index) => drop_table(ast, catalog, index),
438        ast::Statement::Schema(index) => {
439            let written = ast.schema(index);
440            if written.temporary {
441                return Err(Error::binder("Temporary schemas are not supported"));
442            }
443            let parts: Vec<&str> = ast.name(written.name).collect();
444            let (catalog, name) = catalog.schema_name(&parts)?;
445            Ok(Bound::Schema(SchemaChange {
446                catalog,
447                name,
448                drop: written.drop,
449                quiet: written.quiet,
450                or_replace: written.or_replace,
451                cascade: written.cascade,
452            }))
453        }
454        ast::Statement::Sequence(index) => {
455            let written = ast.sequence(index);
456            let parts: Vec<&str> = ast.name(written.name).collect();
457            let alter = !written.owner.is_empty();
458            let mut owner = None;
459            let name = if written.drop || alter {
460                match catalog.resolve_sequence(&parts) {
461                    Ok(name) => Some(name),
462                    Err(_) if written.quiet => None,
463                    Err(error) => return Err(error),
464                }
465            } else if written.temporary {
466                Some(catalog.resolve_for_create_temporary(&parts)?)
467            } else {
468                Some(catalog.resolve_for_create(&parts)?)
469            };
470            if alter && name.is_some() {
471                let parts: Vec<&str> = ast.name(written.owner).collect();
472                owner = Some(catalog.resolve_owner(&parts)?);
473            }
474            Ok(Bound::Sequence(SequenceChange {
475                name,
476                drop: written.drop,
477                if_not_exists: written.quiet,
478                or_replace: written.or_replace,
479                cascade: written.cascade,
480                options: written.options,
481                owner,
482            }))
483        }
484        ast::Statement::Type(index) => {
485            let written = ast.type_def(index);
486            let parts: Vec<&str> = ast.name(written.name).collect();
487            let (name, ty, uses) = if written.drop {
488                let name = catalog.resolve_type(&parts).map(|made| made.name().clone());
489                if name.is_none() && !written.quiet {
490                    return Err(Error::catalog(format!(
491                        "Type with name {} does not exist!",
492                        parts.last().copied().unwrap_or_default()
493                    )));
494                }
495                (name, None, Vec::new())
496            } else {
497                let name = if written.temporary {
498                    catalog.resolve_for_create_temporary(&parts)?
499                } else {
500                    catalog.resolve_for_create(&parts)?
501                };
502                let (ty, uses) = written_type(catalog, ast.string(written.ty))?;
503                (Some(name), Some(ty), uses)
504            };
505            Ok(Bound::Type(TypeChange {
506                name,
507                ty,
508                uses,
509                if_not_exists: written.quiet,
510                or_replace: written.or_replace,
511                cascade: written.cascade,
512            }))
513        }
514        ast::Statement::Alter(index) => alter(ast, catalog, parameters, session, index),
515        ast::Statement::Index(index) => create_index(ast, catalog, parameters, session, index),
516        ast::Statement::Insert(index) => insert(ast, catalog, parameters, session, index),
517        ast::Statement::Update(index) => change(ast, catalog, parameters, session, index, false),
518        ast::Statement::Delete(index) => change(ast, catalog, parameters, session, index, true),
519        ast::Statement::Set(index) | ast::Statement::Reset(index) => {
520            setting(ast, catalog, parameters, session, index)
521        }
522        ast::Statement::Checkpoint(name) => {
523            Ok(Bound::Checkpoint((name != NONE).then(|| ast.string(name).to_string())))
524        }
525        ast::Statement::Attach(index) => attach(ast, catalog, parameters, session, index),
526        ast::Statement::Detach { name, if_exists } => {
527            Ok(Bound::Detach { name: ast.string(name).to_string(), if_exists })
528        }
529        ast::Statement::Transaction(kind) => Ok(Bound::Transaction(kind)),
530        ast::Statement::Explain { query, analyze, statistics, codegen } => {
531            let mut binder = Binder::with(catalog, parameters, session);
532            let (root, _) = binder.bind_query(ast, query)?;
533            Ok(Bound::Explain { plan: finish(binder, root)?, analyze, statistics, codegen })
534        }
535    }
536}
537
538/// Parses and binds one statement, which is the whole front end in one call.
539///
540/// # Errors
541///
542/// Anything the parser or the binder reports.
543pub fn bind_statement_sql(sql: &str, catalog: &Catalog) -> Result<Bound> {
544    let ast = parse_ast(sql)?;
545    bind_statement(&ast, catalog)
546}
547
548/// Roots a binder's plan and checks it.
549/// A written type, with any name `CREATE TYPE` made read through `catalog`, and the made types it
550/// was read through, which a type made from this one depends on.
551pub(crate) fn written_type(
552    catalog: &Catalog,
553    text: &str,
554) -> Result<(LogicalType, Vec<QualifiedName>)> {
555    let mut uses = Vec::new();
556    let ty = LogicalType::parse_with(text, &mut |parts| {
557        let parts: Vec<&str> = parts.iter().map(String::as_str).collect();
558        let made = catalog.resolve_type(&parts)?;
559        uses.push(made.name().clone());
560        Some(made.ty().clone())
561    })?;
562    Ok((ty, uses))
563}
564
565/// [`written_type`] for a caller that only wants the type.
566pub(crate) fn read_type(catalog: &Catalog, text: &str) -> Result<LogicalType> {
567    written_type(catalog, text).map(|(ty, _)| ty)
568}
569
570fn finish(binder: Binder<'_>, root: rudb_plan::NodeRef) -> Result<Plan> {
571    let mut plan = binder.into_plan();
572    plan.set_root(root);
573    plan.validate()?;
574    Ok(plan)
575}
576
577fn create_table(
578    ast: &Ast,
579    catalog: &Catalog,
580    parameters: &Parameters,
581    session: &Session,
582    index: ast::CreateTableRef,
583) -> Result<Bound> {
584    let written = ast.create_table(index);
585    let parts: Vec<&str> = ast.name(written.name).collect();
586    let name = if written.temporary {
587        catalog.resolve_for_create_temporary(&parts)?
588    } else {
589        catalog.resolve_for_create(&parts)?
590    };
591    let defs = ast.column_defs(written.columns);
592    let (mut columns, source) = if written.query == NONE {
593        let mut columns = Vec::with_capacity(defs.len());
594        for def in defs {
595            let text = ast.string(def.ty);
596            if text.is_empty() {
597                return Err(Error::binder(format!(
598                    "Column \"{}\" was declared without a type",
599                    ast.string(def.name)
600                )));
601            }
602            let ty = read_type(catalog, text)?;
603            let column = ast.string(def.name);
604            columns.push(if def.not_null {
605                Field::required(column, ty)
606            } else {
607                Field::new(column, ty)
608            });
609        }
610        (columns, None)
611    } else {
612        let mut binder = Binder::with(catalog, parameters, session);
613        let (root, scope) = binder.bind_query(ast, written.query)?;
614        if defs.len() > scope.len() {
615            // DuckDB's sentence, typo and all. A column list shorter than the query is fine and
616            // renames a prefix, so only this direction is an error.
617            return Err(Error::binder("Target table has more colum names than query result."));
618        }
619        let mut columns = Vec::with_capacity(scope.columns.len());
620        for (at, column) in scope.columns.iter().enumerate() {
621            let named = match defs.get(at) {
622                Some(def) => ast.string(def.name).to_string(),
623                None => column.name.clone(),
624            };
625            columns.push(Field::new(named, column.ty.clone()));
626        }
627        if defs.is_empty() {
628            deduplicate(&mut columns);
629        }
630        (columns, Some(finish(binder, root)?))
631    };
632    duplicate_check(&columns)?;
633    let mut defaults = Vec::with_capacity(defs.len());
634    let mut sequences = Vec::new();
635    for def in defs {
636        defaults.push(if def.default == NONE {
637            None
638        } else {
639            let (text, used) = default_text(ast, def.default, catalog, parameters, session)?;
640            for name in used {
641                if !sequences.contains(&name) {
642                    sequences.push(name);
643                }
644            }
645            Some(text)
646        });
647    }
648    let mut checks = Vec::new();
649    for &expr in ast.expr_list(written.checks) {
650        checks.push(check_text(ast, expr, &columns, catalog, parameters, session)?);
651    }
652    let mut keys = Vec::new();
653    for (at, &names) in ast.name_list(written.keys).iter().enumerate() {
654        let mut places = Vec::new();
655        for wanted in ast.name(names) {
656            let Some(place) = columns.iter().position(|field| same_name(&field.name, wanted))
657            else {
658                return Err(Error::catalog(format!(
659                    "table \"{}\" does not have a column named \"{wanted}\"",
660                    name.table
661                )));
662            };
663            places.push(place);
664        }
665        let primary = at as u32 == written.primary;
666        if primary {
667            for &place in &places {
668                columns[place].not_null = true;
669            }
670        }
671        keys.push(rudb_catalog::Key { columns: places, primary });
672    }
673    let mut foreign = Vec::new();
674    let lists = ast.name_list(written.foreign).iter();
675    let tables = ast.name_list(written.foreign_tables).iter();
676    let referenced = ast.name_list(written.foreign_referenced).iter();
677    for ((&names, &table), &wanted) in lists.zip(tables).zip(referenced) {
678        let names: Vec<&str> = ast.name(names).collect();
679        let parts: Vec<&str> = ast.name(table).collect();
680        let wanted: Vec<&str> = ast.name(wanted).collect();
681        let key = (names.as_slice(), parts.as_slice(), wanted.as_slice());
682        foreign.push(foreign_key(catalog, &name, (&columns, &keys), key)?);
683    }
684    Ok(Bound::CreateTable(CreateTable {
685        name,
686        columns,
687        source,
688        if_not_exists: written.if_not_exists,
689        or_replace: written.or_replace,
690        keys,
691        defaults,
692        checks,
693        foreign,
694        sequences,
695        order: ast.constraint_list(written.order).iter().map(|&held| constraint(held)).collect(),
696    }))
697}
698
699/// A constraint the parser kept the place of, as the catalog keeps it.
700fn constraint(held: ast::Constraint) -> rudb_catalog::Constraint {
701    match held {
702        ast::Constraint::Key(at) => rudb_catalog::Constraint::Key(at as usize),
703        ast::Constraint::Check(at) => rudb_catalog::Constraint::Check(at as usize),
704        ast::Constraint::Foreign(at) => rudb_catalog::Constraint::Foreign(at as usize),
705        ast::Constraint::NotNull(at) => rudb_catalog::Constraint::NotNull(at as usize),
706    }
707}
708
709/// One `FOREIGN KEY` of a table being made, refused the way the pin refuses one that names no key
710/// of the referenced table or pairs columns of different types.
711///
712/// The referenced table is the one being made when the name is its own, and then its columns and
713/// keys are the ones this statement declares.
714fn foreign_key(
715    catalog: &Catalog,
716    made: &QualifiedName,
717    (columns, keys): (&[Field], &[rudb_catalog::Key]),
718    (names, parts, wanted): (&[&str], &[&str], &[&str]),
719) -> Result<rudb_catalog::ForeignKey> {
720    let mut places = Vec::with_capacity(names.len());
721    for &wanted in names {
722        let Some(place) = columns.iter().position(|field| same_name(&field.name, wanted)) else {
723            return Err(Error::binder(format!(
724                "Failed to create foreign key: referencing column \"{wanted}\" does not exist"
725            )));
726        };
727        places.push(place);
728    }
729    let own = parts.last().is_some_and(|last| same_name(last, &made.table))
730        && catalog.resolve(parts).map_or(true, |resolved| resolved == *made);
731    let (table, fields, held): (QualifiedName, Vec<Field>, Vec<rudb_catalog::Key>) = if own {
732        (made.clone(), columns.to_vec(), keys.to_vec())
733    } else {
734        let resolved = catalog.resolve(parts)?;
735        if catalog.view(&resolved).is_ok() {
736            return Err(Error::binder("cannot reference a VIEW with a FOREIGN KEY"));
737        }
738        let table = catalog.table(&resolved)?;
739        (resolved, table.columns().to_vec(), table.keys().to_vec())
740    };
741    let referenced = if wanted.is_empty() {
742        let Some(primary) = held.iter().find(|key| key.primary) else {
743            return Err(Error::binder(format!(
744                "Failed to create foreign key: there is no primary key for referenced table \"{}\"",
745                table.table
746            )));
747        };
748        if primary.columns.len() != places.len() {
749            return Err(Error::parser(
750                "The number of referencing and referenced columns for foreign keys must be the same",
751            ));
752        }
753        primary.columns.clone()
754    } else {
755        let mut referenced = Vec::with_capacity(wanted.len());
756        for &column in wanted {
757            let Some(place) = fields.iter().position(|field| same_name(&field.name, column)) else {
758                return Err(Error::binder(format!(
759                    "Failed to create foreign key: referenced table \"{}\" does not have a column \
760                     named \"{column}\"",
761                    table.table
762                )));
763            };
764            referenced.push(place);
765        }
766        let mut sorted = referenced.clone();
767        sorted.sort_unstable();
768        let matched = held.iter().any(|key| {
769            let mut columns = key.columns.clone();
770            columns.sort_unstable();
771            columns == sorted
772        });
773        if !matched && held.is_empty() {
774            return Err(Error::binder(format!(
775                "Failed to create foreign key: there is no primary key or unique constraint for \
776                 referenced table \"{}\"",
777                table.table
778            )));
779        }
780        if !matched {
781            return Err(Error::binder(format!(
782                "Failed to create foreign key: referenced table \"{}\" does not have a primary key \
783                 or unique constraint on the columns {}",
784                table.table,
785                wanted.join(", ")
786            )));
787        }
788        referenced
789    };
790    for (&from, &to) in places.iter().zip(&referenced) {
791        if columns[from].ty != fields[to].ty {
792            return Err(Error::binder(format!(
793                "Failed to create foreign key: incompatible types between column \"{}\" (\"{}\") \
794                 and column \"{}\" (\"{}\")",
795                fields[to].name, fields[to].ty, columns[from].name, columns[from].ty
796            )));
797        }
798    }
799    Ok(rudb_catalog::ForeignKey { columns: places, table, referenced })
800}
801
802/// The SQL a `CHECK` is kept as, refused the way the pin refuses one when the table is made.
803fn check_text(
804    ast: &Ast,
805    expr: ast::ExprRef,
806    columns: &[Field],
807    catalog: &Catalog,
808    parameters: &Parameters,
809    session: &Session,
810) -> Result<String> {
811    if crate::expr::has_aggregate(ast, expr) {
812        return Err(Error::binder("aggregate functions are not allowed in check constraints"));
813    }
814    let mut binder = Binder::with(catalog, parameters, session);
815    let index = binder.fresh_index();
816    let mut scope = crate::scope::Scope::empty();
817    for (at, field) in columns.iter().enumerate() {
818        scope.push(crate::scope::Visible {
819            table: String::new(),
820            name: field.name.clone(),
821            binding: rudb_plan::ColumnBinding::new(index, at as u32),
822            ty: field.ty.clone(),
823            not_null: false,
824            key: None,
825            default: None,
826            qualified: false,
827            also: None,
828        });
829    }
830    match binder.bind_expr(ast, expr, &scope) {
831        Err(error) if error.message().starts_with("Referenced column \"") => {
832            let column = error.message().split('"').nth(1).unwrap_or_default();
833            Err(Error::binder(format!(
834                "Table does not contain column \"{column}\" referenced in check constraint!"
835            )))
836        }
837        Err(error) => Err(error),
838        Ok(_) if !binder.windows.is_empty() => {
839            Err(Error::binder("window functions are not allowed in check constraints"))
840        }
841        Ok(_) => Ok(deparse::expression(ast, expr)),
842    }
843}
844
845/// The `CHECK` constraints of a table as the query a write runs over the rows it wrote, or `None`
846/// for a table with none.
847fn bind_checks(
848    catalog: &Catalog,
849    parameters: &Parameters,
850    session: &Session,
851    name: &QualifiedName,
852) -> Result<Option<Checks>> {
853    let table = catalog.table(name)?;
854    if table.checks().is_empty() {
855        return Ok(None);
856    }
857    let failed: Vec<String> =
858        table.checks().iter().map(|text| format!("NOT CAST(({text}) AS BOOLEAN)")).collect();
859    let ast = parse_ast(&format!("SELECT {}", failed.join(", ")))?;
860    let ast::Statement::Query(query) = ast.statements[0] else {
861        return Err(Error::internal("a check that is not an expression"));
862    };
863    let ast::QueryBody::Select(select) = ast.query(query).body else {
864        return Err(Error::internal("a check that is not an expression"));
865    };
866    let mut binder = Binder::with(catalog, parameters, session);
867    let (root, scope) =
868        binder.bind_catalog_table(&ast, name, name.table.clone(), ast::Slice::default())?;
869    let mut exprs = Vec::with_capacity(failed.len());
870    let mut names = Vec::with_capacity(failed.len());
871    for target in ast.target_list(ast.select(select).targets) {
872        exprs.push(binder.bind_expr(&ast, target.expr, &scope)?);
873        names.push(binder.plan_mut().intern("failed"));
874    }
875    let exprs = binder.plan_mut().add_expr_list(&exprs);
876    let names = binder.plan_mut().add_name_list(&names);
877    let index = binder.fresh_index();
878    let root = binder.plan_mut().add_node(Node::Project { input: root, index, exprs, names });
879    let messages = table
880        .checks()
881        .iter()
882        .map(|text| {
883            format!(
884                "CHECK constraint failed on table \"{}\" with expression CHECK({text})",
885                name.table
886            )
887        })
888        .collect();
889    Ok(Some(Checks { plan: Box::new(finish(binder, root)?), messages }))
890}
891
892/// The SQL a column's `DEFAULT` is kept as, refused the way the pin refuses one when the table is
893/// made. The expression is bound once here to find out, and bound again by every insert that needs
894/// it, because a default like `random()` is worked out per row.
895fn default_text(
896    ast: &Ast,
897    expr: ast::ExprRef,
898    catalog: &Catalog,
899    parameters: &Parameters,
900    session: &Session,
901) -> Result<(String, Vec<QualifiedName>)> {
902    if crate::expr::has_aggregate(ast, expr) {
903        return Err(Error::binder("DEFAULT value cannot contain aggregates!"));
904    }
905    let mut binder = Binder::with(catalog, parameters, session);
906    let before = binder.plan_mut().node_count();
907    match binder.bind_expr(ast, expr, &crate::scope::Scope::empty()) {
908        Err(error) if error.message().starts_with("Referenced ") => {
909            Err(Error::binder("DEFAULT value cannot contain column names"))
910        }
911        Err(error) => Err(error),
912        // Nothing but a subquery adds a node to the plan while an expression over no rows binds.
913        Ok(_) if binder.plan_mut().node_count() > before => {
914            Err(Error::binder("DEFAULT value cannot contain subqueries"))
915        }
916        Ok(_) if !binder.windows.is_empty() => {
917            Err(Error::binder("DEFAULT value cannot contain window functions!"))
918        }
919        Ok(_) => Ok((deparse::expression(ast, expr), binder.sequences)),
920    }
921}
922
923/// `ALTER TABLE` and `ALTER VIEW`, refused the way the pin refuses each change before the catalog
924/// sees it: a missing column is the binder's sentence, and so is changing the type of a column a
925/// constraint is over.
926fn alter(
927    ast: &Ast,
928    catalog: &Catalog,
929    parameters: &Parameters,
930    session: &Session,
931    index: ast::AlterRef,
932) -> Result<Bound> {
933    let written = ast.alter(index);
934    let nothing = |name| Ok(Bound::Alter(Alter { name, alteration: None, rewrite: None }));
935    let parts: Vec<&str> = ast.name(written.name).collect();
936    let wanted = if written.view { Entry::View } else { Entry::Table };
937    let name = match catalog.resolve_as(&parts, wanted) {
938        Ok(name) => name,
939        Err(_) if written.quiet => return nothing(None),
940        Err(error) => return Err(error),
941    };
942    let kind = catalog.entry(&name)?;
943    if written.view && kind == Entry::Table {
944        return Err(Error::catalog("Can only modify table with ALTER TABLE statement"));
945    }
946    if let ast::AlterAction::Rename { to } = written.action {
947        let alteration = rudb_catalog::Alteration::Rename(ast.string(to).to_string());
948        return Ok(Bound::Alter(Alter {
949            name: Some(name),
950            alteration: Some(alteration),
951            rewrite: None,
952        }));
953    }
954    if kind == Entry::View {
955        return Err(Error::catalog("Can only modify view with ALTER VIEW statement"));
956    }
957    let table = catalog.table(&name)?;
958    let fields = table.columns();
959    let place = |column: ast::StrRef| {
960        fields.iter().position(|field| same_name(&field.name, ast.string(column)))
961    };
962    let missing = |column: ast::StrRef| {
963        let names: Vec<String> = fields.iter().map(|field| format!("\"{}\"", field.name)).collect();
964        Error::binder(format!(
965            "Table \"{}\" does not have a column with name \"{}\"\n\nDid you mean: {}",
966            name.table,
967            ast.string(column),
968            names.join(", ")
969        ))
970    };
971    let found = |column: ast::StrRef| place(column).ok_or_else(|| missing(column));
972    let checks = table.checks();
973    let mut rewrite = None;
974    let alteration = match written.action {
975        ast::AlterAction::Rename { .. } => unreachable!("a rename is handled above"),
976        ast::AlterAction::RenameColumn { column, to } => {
977            let at = found(column)?;
978            let (old, to) = (fields[at].name.as_str(), ast.string(to));
979            if in_foreign_key(catalog, &name, table, at) {
980                // The doubled quotes are the pin's, which quotes a name that is already quoted.
981                return Err(Error::catalog(format!(
982                    "Cannot rename column \"\"{old}\"\" because this is involved in the foreign key \
983                     constraint"
984                )));
985            }
986            let checks =
987                checks.iter().map(|text| rename_in(text, old, to)).collect::<Result<Vec<_>>>()?;
988            rudb_catalog::Alteration::RenameColumn { column: at, to: to.to_string(), checks }
989        }
990        ast::AlterAction::AddColumn { column, quiet } => {
991            if quiet && place(column.name).is_some() {
992                return nothing(Some(name));
993            }
994            let ty = read_type(catalog, ast.string(column.ty))?;
995            let field = Field {
996                not_null: column.not_null,
997                ..Field::new(ast.string(column.name), ty.clone())
998            };
999            let (default, sequences) = if column.default == NONE {
1000                (None, Vec::new())
1001            } else {
1002                let (text, used) = default_text(ast, column.default, catalog, parameters, session)?;
1003                (Some(text), used)
1004            };
1005            rewrite = Some(table_rewrite(
1006                ast,
1007                (catalog, parameters, session),
1008                &name,
1009                |binder, _, out| {
1010                    let value = if column.default == NONE {
1011                        let null = binder.add_constant(Value::Null);
1012                        binder.cast_to(null, &ty)
1013                    } else {
1014                        let value =
1015                            binder.bind_expr(ast, column.default, &crate::scope::Scope::empty())?;
1016                        binder.checked_cast_to(value, &ty, false)?
1017                    };
1018                    out.push((value, field.name.clone()));
1019                    Ok(())
1020                },
1021            )?);
1022            rudb_catalog::Alteration::AddColumn { field, default, sequences }
1023        }
1024        ast::AlterAction::DropColumn { column, quiet } => {
1025            let Some(at) = place(column) else {
1026                return if quiet { nothing(Some(name)) } else { Err(missing(column)) };
1027            };
1028            let dropped = fields[at].name.as_str();
1029            let mut kept = Vec::with_capacity(checks.len());
1030            for text in checks {
1031                let used = columns_in(text)?;
1032                if !used.iter().any(|used| same_name(used, dropped)) {
1033                    kept.push(text.clone());
1034                } else if used.iter().any(|used| !same_name(used, dropped)) {
1035                    return Err(Error::catalog(format!(
1036                        "Cannot drop column \"{dropped}\" because there is a CHECK constraint that \
1037                         depends on it"
1038                    )));
1039                }
1040            }
1041            rewrite =
1042                Some(table_rewrite(ast, (catalog, parameters, session), &name, |_, _, out| {
1043                    out.remove(at);
1044                    Ok(())
1045                })?);
1046            rudb_catalog::Alteration::DropColumn { column: at, checks: kept }
1047        }
1048        ast::AlterAction::Default { column, default } => {
1049            let at = found(column)?;
1050            let (default, sequences) = if default == NONE {
1051                (None, Vec::new())
1052            } else {
1053                let (text, used) = default_text(ast, default, catalog, parameters, session)?;
1054                (Some(text), used)
1055            };
1056            rudb_catalog::Alteration::Default { column: at, default, sequences }
1057        }
1058        ast::AlterAction::NotNull { column, set } => {
1059            rudb_catalog::Alteration::NotNull { column: found(column)?, set }
1060        }
1061        ast::AlterAction::Type { column, ty, using } => {
1062            let at = found(column)?;
1063            let changed = fields[at].name.as_str();
1064            if table.keys().iter().any(|key| key.columns.contains(&at)) {
1065                return Err(Error::binder(
1066                    "Cannot change the type of a column that has a UNIQUE or PRIMARY KEY \
1067                     constraint specified",
1068                ));
1069            }
1070            for text in checks {
1071                if columns_in(text)?.iter().any(|used| same_name(used, changed)) {
1072                    return Err(Error::binder(
1073                        "Cannot change the type of a column that has a CHECK constraint specified",
1074                    ));
1075                }
1076            }
1077            if in_foreign_key(catalog, &name, table, at) {
1078                return Err(Error::binder(
1079                    "Cannot change the type of a column that has a FOREIGN KEY constraint specified",
1080                ));
1081            }
1082            let mut target =
1083                if ty == NONE { None } else { Some(read_type(catalog, ast.string(ty))?) };
1084            rewrite = Some(table_rewrite(
1085                ast,
1086                (catalog, parameters, session),
1087                &name,
1088                |binder, scope, out| {
1089                    let value = if using == NONE {
1090                        out[at].0
1091                    } else {
1092                        binder.bind_expr(ast, using, scope)?
1093                    };
1094                    let ty = target
1095                        .get_or_insert_with(|| binder.plan_mut().expr_type(value).clone())
1096                        .clone();
1097                    out[at].0 = binder.checked_cast_to(value, &ty, false)?;
1098                    Ok(())
1099                },
1100            )?);
1101            let ty = target.ok_or_else(|| Error::internal("an ALTER TYPE that settled no type"))?;
1102            rudb_catalog::Alteration::Type { column: at, ty }
1103        }
1104    };
1105    Ok(Bound::Alter(Alter { name: Some(name), alteration: Some(alteration), rewrite }))
1106}
1107
1108/// `CREATE INDEX` and `DROP INDEX`, with every refusal the pin makes before its catalog sees the
1109/// index. A drop is only its name here, since which index it is depends on the catalog it runs
1110/// against.
1111///
1112/// Each element is bound over the table to find its type and the columns it reads. A bare column
1113/// is written back by its name and anything else inside parentheses, so `ON t(b, (a+1))` keeps
1114/// `b` and `((a + 1))`, which is what the pin's `sql` and `expressions` show.
1115fn create_index(
1116    ast: &Ast,
1117    catalog: &Catalog,
1118    parameters: &Parameters,
1119    session: &Session,
1120    at: ast::IndexRef,
1121) -> Result<Bound> {
1122    let written = ast.index(at);
1123    let parts: Vec<String> = ast.name(written.name).map(str::to_string).collect();
1124    if written.drop {
1125        return Ok(Bound::Index(IndexChange {
1126            table: None,
1127            index: None,
1128            name: parts,
1129            quiet: written.quiet,
1130        }));
1131    }
1132    let written_table: Vec<&str> = ast.name(written.table).collect();
1133    let name = catalog.resolve_as(&written_table, Entry::Table)?;
1134    if catalog.entry(&name)? == Entry::View {
1135        return Err(Error::binder("can only create an index on a base table"));
1136    }
1137    if written.using != NONE && !same_name(ast.string(written.using), "art") {
1138        return Err(Error::binder(format!("Unknown index type: {}", ast.string(written.using))));
1139    }
1140    let fields = catalog.table(&name)?.columns();
1141    let mut binder = Binder::with(catalog, parameters, session);
1142    let (_, scope) =
1143        binder.bind_catalog_table(ast, &name, name.table.clone(), ast::Slice::default())?;
1144    let mut columns = Vec::new();
1145    let mut plain = true;
1146    let mut texts = Vec::new();
1147    for &expr in ast.expr_list(written.elements) {
1148        if let ast::Expr::Column { name: column } = ast.exprs[expr as usize] {
1149            let column: Vec<&str> = ast.name(column).collect();
1150            if let [only] = column[..]
1151                && !fields.iter().any(|field| same_name(&field.name, only))
1152            {
1153                let names: Vec<String> =
1154                    fields.iter().map(|field| format!("\"{}\"", field.name)).collect();
1155                // The stray colon is the pin's.
1156                return Err(Error::binder(format!(
1157                    "Table \"{}\" does not have a column named \"{only}\"\n\nCandidate bindings: \
1158                         : {}",
1159                    name.table,
1160                    names.join(", ")
1161                )));
1162            }
1163        }
1164        if crate::expr::has_aggregate(ast, expr) {
1165            return Err(Error::binder("aggregate functions are not allowed in index expressions"));
1166        }
1167        let before = binder.plan_mut().node_count();
1168        let value = binder.bind_expr(ast, expr, &scope)?;
1169        if binder.plan_mut().node_count() > before {
1170            return Err(Error::binder("cannot use subquery in index expressions"));
1171        }
1172        if !binder.windows.is_empty() {
1173            return Err(Error::binder("window functions are not allowed in index expressions"));
1174        }
1175        let ty = binder.plan_mut().expr_type(value).clone();
1176        if ty.is_nested() {
1177            return Err(Error::invalid_type(format!(
1178                "Invalid Type [{ty}]: Invalid type for index key."
1179            )));
1180        }
1181        let bare = match binder.plan_mut().expr(value) {
1182            Expr::Column(binding) => scope.columns.iter().position(|held| held.binding == *binding),
1183            _ => None,
1184        };
1185        if let Some(at) = bare {
1186            columns.push(at);
1187            texts.push(rudb_parse::quoted(&fields[at].name));
1188            continue;
1189        }
1190        plain = false;
1191        let text = deparse::expression(ast, expr);
1192        let used = columns_in(&text)?;
1193        if used.is_empty() {
1194            return Err(Error::binder(
1195                "CREATE INDEX does not refer to any columns in the base table!",
1196            ));
1197        }
1198        for used in used {
1199            if let Some(at) = fields.iter().position(|field| same_name(&field.name, &used)) {
1200                columns.push(at);
1201            }
1202        }
1203        texts.push(format!("({text})"));
1204    }
1205    if written.unique && !plain {
1206        return Err(Error::not_implemented("A UNIQUE index over an expression is not supported"));
1207    }
1208    let expressions = Value::List {
1209        element: LogicalType::Varchar,
1210        values: texts.iter().map(|text| Value::Varchar(text.clone())).collect(),
1211    };
1212    let unique = if written.unique { "UNIQUE " } else { "" };
1213    let table: Vec<String> = written_table.iter().map(|part| rudb_parse::quoted(part)).collect();
1214    let using = if written.using == NONE {
1215        String::new()
1216    } else {
1217        format!(" USING {} ", ast.string(written.using))
1218    };
1219    let index_name = parts.last().cloned().unwrap_or_default();
1220    let sql = format!(
1221        "CREATE {unique}INDEX {} ON {}{using}({});",
1222        rudb_parse::quoted(&index_name),
1223        table.join("."),
1224        texts.join(", ")
1225    );
1226    if !plain {
1227        columns.sort_unstable();
1228        columns.dedup();
1229    }
1230    let index = rudb_catalog::Index {
1231        name: index_name,
1232        unique: written.unique,
1233        columns,
1234        plain,
1235        expressions: expressions.to_string(),
1236        sql,
1237        oid: 0,
1238    };
1239    Ok(Bound::Index(IndexChange {
1240        table: Some(name),
1241        index: Some(index),
1242        name: Vec::new(),
1243        quiet: written.quiet,
1244    }))
1245}
1246
1247/// Whether a column is in one of its table's foreign keys, or is a column another table's foreign
1248/// key points at.
1249fn in_foreign_key(
1250    catalog: &Catalog,
1251    name: &QualifiedName,
1252    table: &rudb_catalog::Table,
1253    at: usize,
1254) -> bool {
1255    table.foreign().iter().any(|key| key.columns.contains(&at))
1256        || catalog.tables().any(|held| {
1257            held.foreign().iter().any(|key| key.table == *name && key.referenced.contains(&at))
1258        })
1259}
1260
1261/// A plan over every row of a table giving each of its columns, as changed by `change`, which gets
1262/// the column expressions and their names in order and can add, drop or replace any of them.
1263fn table_rewrite(
1264    ast: &Ast,
1265    (catalog, parameters, session): (&Catalog, &Parameters, &Session),
1266    name: &QualifiedName,
1267    change: impl FnOnce(
1268        &mut Binder<'_>,
1269        &crate::scope::Scope,
1270        &mut Vec<(ExprRef, String)>,
1271    ) -> Result<()>,
1272) -> Result<Plan> {
1273    let mut binder = Binder::with(catalog, parameters, session);
1274    let (root, scope) =
1275        binder.bind_catalog_table(ast, name, name.table.clone(), ast::Slice::default())?;
1276    let mut out = Vec::with_capacity(scope.columns.len() + 1);
1277    for column in &scope.columns {
1278        let expr = binder.plan_mut().add_expr(Expr::Column(column.binding), column.ty.clone());
1279        out.push((expr, column.name.clone()));
1280    }
1281    change(&mut binder, &scope, &mut out)?;
1282    let exprs: Vec<ExprRef> = out.iter().map(|(expr, _)| *expr).collect();
1283    let names: Vec<_> = out.iter().map(|(_, name)| binder.plan_mut().intern(name)).collect();
1284    let exprs = binder.plan_mut().add_expr_list(&exprs);
1285    let names = binder.plan_mut().add_name_list(&names);
1286    let index = binder.fresh_index();
1287    let root = binder.plan_mut().add_node(Node::Project { input: root, index, exprs, names });
1288    finish(binder, root)
1289}
1290
1291/// A kept `CHECK`, parsed back into an expression.
1292fn check_ast(text: &str) -> Result<(Ast, ast::ExprRef)> {
1293    let ast = parse_ast(&format!("SELECT {text}"))?;
1294    let found = match ast.statements.first() {
1295        Some(&ast::Statement::Query(query)) => match ast.query(query).body {
1296            ast::QueryBody::Select(select) => {
1297                ast.target_list(ast.select(select).targets).first().map(|target| target.expr)
1298            }
1299            _ => None,
1300        },
1301        _ => None,
1302    };
1303    let expr = found.ok_or_else(|| Error::internal("a check that is not an expression"))?;
1304    Ok((ast, expr))
1305}
1306
1307/// The columns a kept `CHECK` reads, by the last part of each name.
1308fn columns_in(text: &str) -> Result<Vec<String>> {
1309    let (ast, _) = check_ast(text)?;
1310    let mut out = Vec::new();
1311    for expr in &ast.exprs {
1312        if let ast::Expr::Column { name } = *expr
1313            && let Some(last) = ast.name(name).last()
1314        {
1315            out.push(last.to_string());
1316        }
1317    }
1318    Ok(out)
1319}
1320
1321/// A kept `CHECK` with every column named `old` renamed to `to`, which is what the pin does to one
1322/// over a column that `RENAME COLUMN` renames.
1323fn rename_in(text: &str, old: &str, to: &str) -> Result<String> {
1324    let (mut ast, expr) = check_ast(text)?;
1325    let mut renamed = false;
1326    for at in 0..ast.exprs.len() {
1327        let ast::Expr::Column { name } = ast.exprs[at] else { continue };
1328        if name.len == 0 {
1329            continue;
1330        }
1331        let last = (name.start + name.len - 1) as usize;
1332        if same_name(ast.string(ast.parts[last]), old) {
1333            let index = ast.strings.len() as u32;
1334            ast.strings.push(to.to_string());
1335            ast.parts[last] = index;
1336            renamed = true;
1337        }
1338    }
1339    Ok(if renamed { deparse::expression(&ast, expr) } else { text.to_string() })
1340}
1341
1342/// Renames the columns a query repeated, which is what makes `CREATE TABLE t AS SELECT 1 AS a, 2 AS
1343/// a` a table rather than an error.
1344///
1345/// A query is allowed to produce two columns of one name and `SELECT 1 AS a, 2 AS a` prints two
1346/// columns called `a`, so a statement that turns a query into a table has to decide what to do with
1347/// that, and DuckDB renames rather than refusing. The suffix is `_1`, then `_2`, counting up until
1348/// the name is free, so a query that already has an `a_1` in it pushes the renamed column to `a_2`
1349/// rather than colliding with it.
1350///
1351/// This only runs when the statement wrote no column list. With a list, even a short one, duckdb
1352/// v1.4.1 takes the names as they come and a repeat is an error, so `CREATE TABLE t (z) AS SELECT 1
1353/// AS a, 2 AS a` is a table of `z` and `a` and adding a third `a` to that query is a refusal.
1354fn deduplicate(columns: &mut [Field]) {
1355    for at in 0..columns.len() {
1356        let taken = |name: &str, upto: usize, columns: &[Field]| {
1357            columns[..upto].iter().any(|held| same_name(&held.name, name))
1358        };
1359        if !taken(&columns[at].name, at, columns) {
1360            continue;
1361        }
1362        let mut suffix = 1;
1363        let mut candidate = format!("{}_{suffix}", columns[at].name);
1364        while taken(&candidate, at, columns) {
1365            suffix += 1;
1366            candidate = format!("{}_{suffix}", columns[at].name);
1367        }
1368        columns[at].name = candidate;
1369    }
1370}
1371
1372/// Binds a `CREATE VIEW`, which means binding the body and then throwing the plan away.
1373///
1374/// Throwing it away is the point. The body is bound here so that a view over a table that is not
1375/// there is refused now rather than at the first select, and so that the column list can be checked
1376/// against what the body actually produces. What the catalog keeps is the text, because a view
1377/// follows the tables underneath it and a plan is a photograph of the day it was built.
1378fn create_view(
1379    ast: &Ast,
1380    catalog: &Catalog,
1381    parameters: &Parameters,
1382    session: &Session,
1383    index: ast::CreateViewRef,
1384) -> Result<Bound> {
1385    let written = ast.create_view(index);
1386    let parts: Vec<&str> = ast.name(written.name).collect();
1387    let name = if written.temporary {
1388        catalog.resolve_for_create_temporary(&parts)?
1389    } else {
1390        catalog.resolve_for_create(&parts)?
1391    };
1392    let aliases: Vec<String> = ast.name(written.columns).map(str::to_string).collect();
1393
1394    let mut binder = Binder::with(catalog, parameters, session);
1395    // The plan is thrown away and the columns are all that is kept, so a file is read for its
1396    // columns and nothing else.
1397    binder.outlined = true;
1398    let (_, mut scope) = binder.bind_query(ast, written.query)?;
1399    if aliases.len() > scope.len() {
1400        return Err(Error::binder("More VIEW aliases than columns in query result"));
1401    }
1402    if !aliases.is_empty() {
1403        let written: Vec<&str> = aliases.iter().map(String::as_str).collect();
1404        scope.rename(&written, "unnamed_subquery")?;
1405    }
1406
1407    Ok(Bound::CreateView(CreateView {
1408        name,
1409        sql: ast.string(written.sql).to_string(),
1410        statement: deparse::create_view(ast, index),
1411        aliases,
1412        if_not_exists: written.if_not_exists,
1413        or_replace: written.or_replace,
1414        columns: scope.fields(),
1415    }))
1416}
1417
1418fn drop_table(ast: &Ast, catalog: &Catalog, index: ast::DropTableRef) -> Result<Bound> {
1419    let written = ast.drop_table(index);
1420    let kind = if written.view { Entry::View } else { Entry::Table };
1421    let mut names = Vec::new();
1422    for &name in ast.name_list(written.names) {
1423        let parts: Vec<&str> = ast.name(name).collect();
1424        // The statement said which of the two it meant, so a name that is not there is a missing
1425        // one of those and not a missing table.
1426        match catalog.resolve_as(&parts, kind) {
1427            Ok(resolved) => names.push(resolved),
1428            Err(error) if written.if_exists => drop(error),
1429            Err(error) => return Err(error),
1430        }
1431    }
1432    Ok(Bound::DropTable(DropTable { names, kind }))
1433}
1434
1435/// Binds a `SET` or a `RESET`, which is resolving its value and nothing else.
1436///
1437/// The name is not checked here. The binder knows what tables exist and has no idea what settings
1438/// exist, since a setting is a knob on the engine rather than an entry in a catalog, and a version
1439/// of this that held the list would be the binder holding a copy of something it cannot enforce.
1440fn setting(
1441    ast: &Ast,
1442    catalog: &Catalog,
1443    parameters: &Parameters,
1444    session: &Session,
1445    index: ast::SettingRef,
1446) -> Result<Bound> {
1447    let written = ast.setting(index);
1448    let name = ast.string(written.name).to_string();
1449    let value = if written.value == NONE {
1450        None
1451    } else {
1452        let mut binder = Binder::with(catalog, parameters, session);
1453        let bound = binder.bind_setting_value(ast, written.value)?;
1454        let Expr::Constant(value) = *binder.plan().expr(bound) else {
1455            return Err(Error::not_implemented(format!(
1456                "a value for {name} that is not a constant"
1457            )));
1458        };
1459        Some(binder.plan().value(value).clone())
1460    };
1461    Ok(Bound::Setting(Setting { name, scope: written.scope, value, pragma: written.pragma }))
1462}
1463
1464/// Binds an `ATTACH`, folding the path and every option value to a constant.
1465fn attach(
1466    ast: &Ast,
1467    catalog: &Catalog,
1468    parameters: &Parameters,
1469    session: &Session,
1470    index: ast::AttachRef,
1471) -> Result<Bound> {
1472    let written = ast.attach(index);
1473    let mut binder = Binder::with(catalog, parameters, session);
1474    let mut constant = |expr: ast::ExprRef, what: &str| -> Result<Value> {
1475        let bound = binder.bind_setting_value(ast, expr)?;
1476        let Expr::Constant(value) = *binder.plan().expr(bound) else {
1477            return Err(Error::not_implemented(format!("{what} of ATTACH that is not a constant")));
1478        };
1479        Ok(binder.plan().value(value).clone())
1480    };
1481    let path = match constant(written.path, "a path")? {
1482        Value::Null => {
1483            return Err(Error::binder("ATTACH path expression must not evaluate to NULL"));
1484        }
1485        value => value.to_string(),
1486    };
1487    let names = ast.name(written.names).map(str::to_string).collect::<Vec<_>>();
1488    let mut options = Vec::with_capacity(names.len());
1489    for (name, &value) in names.into_iter().zip(ast.expr_list(written.values)) {
1490        let value = if value == NONE { None } else { Some(constant(value, "an option")?) };
1491        options.push((name, value));
1492    }
1493    let alias = (written.alias != NONE).then(|| ast.string(written.alias).to_string());
1494    Ok(Bound::Attach(Attach {
1495        path,
1496        alias,
1497        or_replace: written.or_replace,
1498        if_not_exists: written.if_not_exists,
1499        options,
1500    }))
1501}
1502
1503/// Sorts an insert's rows into the order the target table declared.
1504///
1505/// Returns the input unchanged when the statement supplies none of the declared columns, because
1506/// every one of them is then a constant null and sorting on a constant is a sort that buys nothing
1507/// and costs a pass. A statement that supplies some of them sorts on those: the declaration is
1508/// about the order the rows are written in, and the columns that are there still order them.
1509///
1510/// The leading key carries the width. `date_trunc('month', d)` and `d` sort the same rows into the
1511/// same fragments for any predicate a month wide or wider, and the difference is what happens
1512/// inside a month: bucketed, the second key orders the whole month, which is the key locality the
1513/// joins want and the reason the width is part of the declaration at all.
1514fn clustered(
1515    binder: &mut Binder<'_>,
1516    input: rudb_plan::NodeRef,
1517    scope: &crate::scope::Scope,
1518    clustering: &Clustering,
1519    targets: &[usize],
1520    fields: &[Field],
1521) -> Result<rudb_plan::NodeRef> {
1522    let mut keys: Vec<SortKey> = Vec::with_capacity(clustering.columns().len());
1523    for (at, &column) in clustering.columns().iter().enumerate() {
1524        let Some(from) = targets.iter().position(|&target| target == column as usize) else {
1525            continue;
1526        };
1527        let source = &scope.columns[from];
1528        let expr = binder.plan_mut().add_expr(Expr::Column(source.binding), source.ty.clone());
1529        // Cast to the column's own type before bucketing, since the source of a load is a file
1530        // whose date column can arrive as a timestamp and `date_trunc` gives back the type it was
1531        // handed. Sorting on a different type than the column stores would still be an order, but
1532        // it would not be the order the declaration names.
1533        let expr = binder.checked_cast_to(expr, &fields[column as usize].ty, false)?;
1534        let expr =
1535            if at == 0 { bucketed(binder, expr, clustering.width(), fields, column) } else { expr };
1536        keys.push(SortKey { expr, descending: false, nulls_first: false });
1537    }
1538    if keys.is_empty() {
1539        return Ok(input);
1540    }
1541    let keys = binder.plan_mut().add_sort_keys(&keys);
1542    Ok(binder.plan_mut().add_node(Node::Sort { input, keys }))
1543}
1544
1545/// The declaration with an automatic width turned into the bucket the incoming rows ask for.
1546///
1547/// A declaration that named no width says the bucket should come from how many rows a partition
1548/// would hold, and this is the only place that number is in reach. The rows are the source's, not
1549/// the target's: a load into an empty table has a target with nothing to count, and the whole case
1550/// the rule exists for is the first load of a big table. So the count and the range come off the
1551/// source's own zones, which is the Parquet footer for a file and the directory for a table, and
1552/// both are already on the plan because the estimator wanted them.
1553///
1554/// Everything about this is best effort and that is by design. The three widths hold the same rows
1555/// and answer the same queries, so guessing wrong costs some pruning or some key locality and
1556/// cannot cost an answer. A source that is a join, a group by or a values list has no zones to read
1557/// and gets [`Width::DEFAULT`], which is what the fixed default was before the rule existed.
1558fn fitted(
1559    binder: &Binder<'_>,
1560    scope: &crate::scope::Scope,
1561    clustering: &Clustering,
1562    targets: &[usize],
1563) -> Clustering {
1564    if clustering.width() != Width::Auto {
1565        return clustering.clone();
1566    }
1567    let Some(from) = targets.iter().position(|&target| target == clustering.partition() as usize)
1568    else {
1569        return clustering.fitted(0, 0);
1570    };
1571    let source = &scope.columns[from];
1572    let Some(zones) = binder.plan().sole_zones() else {
1573        return clustering.fitted(0, 0);
1574    };
1575    // By name, and off whichever store the plan reads rather than off the one this column is bound
1576    // to. The binding points at the projection over the scan, since a load is a projection into the
1577    // target's types, and following a binding back through a projection is the optimizer's job. A
1578    // load reads one table or one file, so the store with bounds on it is the store the name is in.
1579    let Some(at) = zones.column(&source.name) else {
1580        return clustering.fitted(0, 0);
1581    };
1582    let rows = zones.surviving(&[]).unwrap_or(0);
1583    let days = span(&zones.extreme(at, End::Low), &zones.extreme(at, End::High)).unwrap_or(0);
1584    clustering.fitted(rows, days)
1585}
1586
1587/// How many days a column covers, from the smallest and largest values in it.
1588///
1589/// `None` wherever the two do not make a span, which is a column that is entirely null, a store
1590/// that could not fold its parts into one answer, and a pair of bounds that are not the same shape.
1591/// All of them mean the same thing here, which is that there is nothing to divide the row count by.
1592fn span(low: &Stat<ColumnBound>, high: &Stat<ColumnBound>) -> Option<u64> {
1593    let (Stat::Known { value: low, .. }, Stat::Known { value: high, .. }) = (low, high) else {
1594        return None;
1595    };
1596    let days = match (low, high) {
1597        // A date is a day count already, which is the common case and the only exact one.
1598        (ColumnBound::Int(low), ColumnBound::Int(high)) => high.checked_sub(*low)?,
1599        // A timestamp is a count of seconds at whichever unit the column keeps, so the span is that
1600        // difference divided by a day's worth of them. A scale wide enough to overflow the divisor
1601        // is a column no calendar covers and falls out as no span at all.
1602        (
1603            ColumnBound::Scaled { unscaled: low, scale: at },
1604            ColumnBound::Scaled { unscaled: high, scale: to },
1605        ) if at == to => {
1606            let day = 86_400_i128.checked_mul(10_i128.checked_pow(u32::from(*at))?)?;
1607            high.checked_sub(*low)? / day
1608        }
1609        _ => return None,
1610    };
1611    u64::try_from(days).ok()
1612}
1613
1614/// Wraps a sort key in the calendar bucket its declaration asked for.
1615fn bucketed(
1616    binder: &mut Binder<'_>,
1617    expr: ExprRef,
1618    width: Width,
1619    fields: &[Field],
1620    column: u32,
1621) -> ExprRef {
1622    if width == Width::Exact {
1623        return expr;
1624    }
1625    let unit = binder.plan_mut().add_value(Value::Varchar(width.to_string().to_lowercase()));
1626    let unit = binder.plan_mut().add_expr(Expr::Constant(unit), LogicalType::Varchar);
1627    let args = binder.plan_mut().add_expr_list(&[unit, expr]);
1628    let name = binder.plan_mut().intern("date_trunc");
1629    let ty = fields[column as usize].ty.clone();
1630    binder.plan_mut().add_expr(Expr::Function { name, args }, ty)
1631}
1632
1633fn insert(
1634    ast: &Ast,
1635    catalog: &Catalog,
1636    parameters: &Parameters,
1637    session: &Session,
1638    index: ast::InsertRef,
1639) -> Result<Bound> {
1640    let written = ast.insert(index);
1641    let parts: Vec<&str> = ast.name(written.name).collect();
1642    let name = catalog.resolve(&parts)?;
1643    if catalog.entry(&name)? == Entry::View {
1644        // The binary's sentence, article and all. A view has no rows of its own to append to, and
1645        // an updatable view is a rule about rewriting the insert that neither database has.
1646        return Err(Error::catalog(format!("{} is not an table", name.table)));
1647    }
1648    let target = catalog.table(&name)?;
1649    let fields: Vec<Field> = target.columns().to_vec();
1650    let clustering = target.clustering().cloned();
1651
1652    // Which table column each source column lands in. Without a column list that is the first n
1653    // columns in order, and with one it is whatever the list says, which is also the check that
1654    // the list names columns the table has and names none of them twice.
1655    let targets: Vec<usize> = if written.columns.is_empty() {
1656        (0..fields.len()).collect()
1657    } else {
1658        let mut targets = Vec::new();
1659        for column in ast.name(written.columns) {
1660            let at = fields.iter().position(|field| same_name(&field.name, column)).ok_or_else(
1661                || {
1662                    Error::binder(format!(
1663                        "Table \"{}\" does not have a column named \"{column}\"",
1664                        name.table
1665                    ))
1666                },
1667            )?;
1668            if targets.contains(&at) {
1669                return Err(Error::binder(format!(
1670                    "Column \"{column}\" is named twice in the same INSERT"
1671                )));
1672            }
1673            targets.push(at);
1674        }
1675        targets
1676    };
1677
1678    let defaults: Vec<(LogicalType, Option<String>)> = (0..fields.len())
1679        .map(|at| (fields[at].ty.clone(), target.default(at).map(str::to_owned)))
1680        .collect();
1681    let mut binder = Binder::with(catalog, parameters, session);
1682    let (root, scope) = if written.source == NONE {
1683        // `DEFAULT VALUES` is one row with nothing in it, and the projection below fills every
1684        // column with its default.
1685        (binder.plan_mut().add_node(Node::Dummy), crate::scope::Scope::empty())
1686    } else {
1687        // A `DEFAULT` item of a `VALUES` row is the default of the column it lands in, which only
1688        // this statement knows, so the `VALUES` right under it is told.
1689        if matches!(ast.query(written.source).body, ast::QueryBody::Values(_)) {
1690            binder.insert_defaults = Some(targets.iter().map(|&at| defaults[at].clone()).collect());
1691        }
1692        // A `COPY t FROM 'file'` reads the file as the columns it lands in, the way DuckDB does,
1693        // so a value that does not fit is the reader's conversion error on its line rather than a
1694        // cast failing later with no line to point at. The `read_csv` under it takes the columns.
1695        if written.copy {
1696            binder.copy_into = Some(targets.iter().map(|&at| fields[at].clone()).collect());
1697        }
1698        let bound = binder.bind_query(ast, written.source)?;
1699        binder.copy_into = None;
1700        bound
1701    };
1702    let targets = if written.source == NONE { Vec::new() } else { targets };
1703    if scope.len() != targets.len() {
1704        return Err(Error::binder(format!(
1705            "Table \"{}\" has {} columns but {} values were supplied",
1706            name.table,
1707            targets.len(),
1708            scope.len()
1709        )));
1710    }
1711
1712    // A table that declared what order its rows go in gets the sort here, under the projection
1713    // rather than over it, because a projection does not reorder rows and the bindings the sort
1714    // keys need are the ones the query just produced. This is the whole of the loader honouring
1715    // the declaration: the rows arrive at the writer in order and the per fragment ranges, which
1716    // are built from whatever order arrives, come out narrow instead of each covering the table.
1717    let root = match &clustering {
1718        None => root,
1719        Some(clustering) => {
1720            // The width is settled here and not on the table. A declaration that left the bucket to
1721            // the data is a standing instruction, so it stays on the table as one and every load
1722            // answers it with the rows that load is carrying. What the sort needs is an answer, and
1723            // that is what this is.
1724            let fitted = fitted(&binder, &scope, clustering, &targets);
1725            clustered(&mut binder, root, &scope, &fitted, &targets, &fields)?
1726        }
1727    };
1728
1729    // The projection that makes the source look exactly like the table. Every column the statement
1730    // did not name becomes a null of the column's own type, so the append never has to know that a
1731    // column list was written at all.
1732    let mut exprs: Vec<ExprRef> = Vec::with_capacity(fields.len());
1733    let mut names = Vec::with_capacity(fields.len());
1734    for (at, field) in fields.iter().enumerate() {
1735        let expr = match targets.iter().position(|&target| target == at) {
1736            Some(from) => {
1737                let column = &scope.columns[from];
1738                let expr =
1739                    binder.plan_mut().add_expr(Expr::Column(column.binding), column.ty.clone());
1740                binder.checked_cast_to(expr, &field.ty, false)?
1741            }
1742            // The column's default, or a null of the column's own type when it has none.
1743            None => binder.bind_default(defaults[at].1.as_deref(), &field.ty)?,
1744        };
1745        exprs.push(expr);
1746        let interned = binder.plan_mut().intern(&field.name);
1747        names.push(interned);
1748    }
1749    let exprs = binder.plan_mut().add_expr_list(&exprs);
1750    let names = binder.plan_mut().add_name_list(&names);
1751    let index = binder.fresh_index();
1752    let root = binder.plan_mut().add_node(Node::Project { input: root, index, exprs, names });
1753    let source = finish(binder, root)?;
1754    let returning = returning(ast, catalog, parameters, session, written.returning)?;
1755    let conflict = match written.conflict {
1756        Some(conflict) => {
1757            Some(bind_conflict(ast, catalog, parameters, session, &name, &targets, conflict)?)
1758        }
1759        None => None,
1760    };
1761    let checks = bind_checks(catalog, parameters, session, &name)?;
1762    Ok(Bound::Insert(Insert { name, source, write: Write::Append, returning, conflict, checks }))
1763}
1764
1765/// Which key an `ON CONFLICT` is about and what it does, refused the way the pin refuses one that
1766/// names no key or leaves which key open when that matters.
1767fn bind_conflict(
1768    ast: &Ast,
1769    catalog: &Catalog,
1770    parameters: &Parameters,
1771    session: &Session,
1772    name: &QualifiedName,
1773    targets: &[usize],
1774    conflict: ast::Conflict,
1775) -> Result<Conflict> {
1776    let table = catalog.table(name)?;
1777    let fields = table.columns();
1778    let keys = table.keys();
1779    let key = if conflict.target.is_empty() {
1780        if keys.is_empty() {
1781            return Err(Error::binder(
1782                "There are no UNIQUE/PRIMARY KEY constraints that refer to this table, specify ON \
1783                 CONFLICT columns manually",
1784            ));
1785        }
1786        match conflict.action {
1787            ast::ConflictAction::Nothing => None,
1788            _ if keys.len() > 1 => {
1789                return Err(Error::binder(
1790                    "Conflict target has to be provided for a DO UPDATE operation when the table \
1791                     has multiple UNIQUE/PRIMARY KEY constraints",
1792                ));
1793            }
1794            _ => Some(0),
1795        }
1796    } else {
1797        let mut wanted = Vec::new();
1798        for column in ast.name(conflict.target) {
1799            let Some(at) = fields.iter().position(|field| same_name(&field.name, column)) else {
1800                return Err(Error::binder(format!(
1801                    "Table \"{}\" does not have a column with name \"{column}\"",
1802                    name.table
1803                )));
1804            };
1805            wanted.push(at);
1806        }
1807        wanted.sort_unstable();
1808        wanted.dedup();
1809        let found = keys.iter().position(|key| {
1810            let mut held = key.columns.clone();
1811            held.sort_unstable();
1812            held == wanted
1813        });
1814        let Some(found) = found else {
1815            return Err(Error::binder(
1816                "The specified columns as conflict target are not referenced by a UNIQUE/PRIMARY \
1817                 KEY CONSTRAINT or INDEX",
1818            ));
1819        };
1820        Some(found)
1821    };
1822    let action = match conflict.action {
1823        ast::ConflictAction::Nothing => ConflictAction::Nothing,
1824        ast::ConflictAction::Replace => ConflictAction::Replace(targets.to_vec()),
1825        ast::ConflictAction::Update { columns: written, query } => {
1826            let mut columns = Vec::new();
1827            for column in ast.name(written) {
1828                let Some(at) = fields.iter().position(|field| same_name(&field.name, column))
1829                else {
1830                    return Err(Error::binder(format!(
1831                        "Referenced update column {column} not found in table!"
1832                    )));
1833                };
1834                if columns.contains(&at) {
1835                    return Err(Error::binder(format!(
1836                        "Multiple assignments to same column \"\"{column}\"\""
1837                    )));
1838                }
1839                columns.push(at);
1840            }
1841            let mut binder = Binder::with(catalog, parameters, session);
1842            binder.upsert = true;
1843            let (root, scope) = binder.bind_query(ast, query)?;
1844            // Each value is cast to its column's type here, so the write only has to place it,
1845            // and the condition is cast to a boolean, so the write only has to test it.
1846            let mut exprs = Vec::with_capacity(scope.columns.len());
1847            let mut names = Vec::with_capacity(scope.columns.len());
1848            for (at, column) in scope.columns.iter().enumerate() {
1849                let expr =
1850                    binder.plan_mut().add_expr(Expr::Column(column.binding), column.ty.clone());
1851                let ty = columns.get(at).map_or(LogicalType::Boolean, |&to| fields[to].ty.clone());
1852                exprs.push(binder.checked_cast_to(expr, &ty, false)?);
1853                names.push(binder.plan_mut().intern(&column.name));
1854            }
1855            let exprs = binder.plan_mut().add_expr_list(&exprs);
1856            let names = binder.plan_mut().add_name_list(&names);
1857            let index = binder.fresh_index();
1858            let root =
1859                binder.plan_mut().add_node(Node::Project { input: root, index, exprs, names });
1860            ConflictAction::Update { columns, plan: Box::new(finish(binder, root)?) }
1861        }
1862    };
1863    Ok(Conflict { key, action })
1864}
1865
1866/// An `UPDATE` or a `DELETE`, bound to the query that produces every row the table has afterwards.
1867///
1868/// The source the transform built is `SELECT *, condition, values... FROM table`. A row the
1869/// condition holds for gets the new values in the named columns for an `UPDATE`, and every other
1870/// row comes through as it was. A null condition is a row that did not match, which is what a
1871/// searched `CASE` does with one, so the one expression covers both. After the table's columns
1872/// comes the flag saying which rows matched, which are the rows an `UPDATE` changed and the rows a
1873/// `DELETE` takes out.
1874fn change(
1875    ast: &Ast,
1876    catalog: &Catalog,
1877    parameters: &Parameters,
1878    session: &Session,
1879    index: ast::InsertRef,
1880    delete: bool,
1881) -> Result<Bound> {
1882    let written = ast.insert(index);
1883    let parts: Vec<&str> = ast.name(written.name).collect();
1884    let name = catalog.resolve(&parts)?;
1885    if catalog.entry(&name)? == Entry::View {
1886        return Err(Error::binder(if delete {
1887            "Can only delete from base table"
1888        } else {
1889            "Can only update base table"
1890        }));
1891    }
1892    let fields: Vec<Field> = catalog.table(&name)?.columns().to_vec();
1893    let mut targets: Vec<usize> = Vec::new();
1894    for column in ast.name(written.columns) {
1895        let at =
1896            fields.iter().position(|field| same_name(&field.name, column)).ok_or_else(|| {
1897                Error::binder(format!("Referenced update column {column} not found in table!"))
1898            })?;
1899        if targets.contains(&at) {
1900            return Err(Error::binder(format!(
1901                "Multiple assignments to same column \"\"{column}\"\""
1902            )));
1903        }
1904        targets.push(at);
1905    }
1906
1907    // Which assignments are `SET c = DEFAULT`, which are the last items of the source's list.
1908    let mut defaulted = vec![false; targets.len()];
1909    if let ast::QueryBody::Select(select) = ast.query(written.source).body {
1910        let items = ast.target_list(ast.select(select).targets);
1911        let first = items.len().saturating_sub(targets.len());
1912        for (at, item) in items[first..].iter().enumerate() {
1913            defaulted[at] = matches!(ast.expr(item.expr), ast::Expr::Default);
1914        }
1915    }
1916    let table = catalog.table(&name)?;
1917    let mut binder = Binder::with(catalog, parameters, session);
1918    binder.default_as_null = defaulted.contains(&true);
1919    let (root, scope) = binder.bind_query(ast, written.source)?;
1920    binder.default_as_null = false;
1921    let width = fields.len();
1922    if scope.len() != width + 1 + targets.len() {
1923        return Err(Error::internal(format!(
1924            "an UPDATE source of {} columns over a table of {width}",
1925            scope.len()
1926        )));
1927    }
1928    let column = |binder: &mut Binder<'_>, at: usize| {
1929        let column = &scope.columns[at];
1930        binder.plan_mut().add_expr(Expr::Column(column.binding), column.ty.clone())
1931    };
1932    let hit = column(&mut binder, width);
1933    let hit = binder.checked_cast_to(hit, &LogicalType::Boolean, false)?;
1934    let mut exprs = Vec::with_capacity(width);
1935    let mut names = Vec::with_capacity(width);
1936    for (at, field) in fields.iter().enumerate() {
1937        let old = column(&mut binder, at);
1938        let expr = match targets.iter().position(|&target| target == at) {
1939            Some(from) => {
1940                let then = if defaulted[from] {
1941                    binder.bind_default(table.default(at), &field.ty)?
1942                } else {
1943                    let new = column(&mut binder, width + 1 + from);
1944                    binder.checked_cast_to(new, &field.ty, false)?
1945                };
1946                let arms = binder.plan_mut().add_arms(&[Arm { when: hit, then }]);
1947                binder
1948                    .plan_mut()
1949                    .add_expr(Expr::Case { arms, otherwise: Some(old) }, field.ty.clone())
1950            }
1951            None => old,
1952        };
1953        exprs.push(expr);
1954        let interned = binder.plan_mut().intern(&field.name);
1955        names.push(interned);
1956    }
1957    // The flag is true only where the condition is, so a row whose condition is null is left
1958    // alone the way a `WHERE` leaves it out.
1959    let yes = binder.add_constant(Value::Boolean(true));
1960    let arms = binder.plan_mut().add_arms(&[Arm { when: hit, then: yes }]);
1961    let otherwise = Some(binder.add_constant(Value::Boolean(false)));
1962    exprs.push(binder.plan_mut().add_expr(Expr::Case { arms, otherwise }, LogicalType::Boolean));
1963    let interned = binder.plan_mut().intern("changed");
1964    names.push(interned);
1965    let exprs = binder.plan_mut().add_expr_list(&exprs);
1966    let names = binder.plan_mut().add_name_list(&names);
1967    let index = binder.fresh_index();
1968    let root = binder.plan_mut().add_node(Node::Project { input: root, index, exprs, names });
1969    let source = finish(binder, root)?;
1970    let returning = returning(ast, catalog, parameters, session, written.returning)?;
1971    let write = if delete { Write::Delete } else { Write::Update };
1972    let checks = if delete { None } else { bind_checks(catalog, parameters, session, &name)? };
1973    Ok(Bound::Insert(Insert { name, source, write, returning, conflict: None, checks }))
1974}