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