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