Skip to main content

inillucent_sql/
directive.rs

1//! Statements the session carries out itself rather than compiling.
2//!
3//! Invariant: a directive is a decision, already resolved, with nothing left
4//! to look up. Binding a `DROP TABLE` resolves the name and refuses a missing
5//! one here; what reaches the session is "free this root page and remove this
6//! `sqlite_schema` row", not a name it has to resolve again.
7//!
8//! Transaction control and DDL are here rather than in the bytecode for a
9//! reason the TDD's own DDL protocol describes: their steps are catalog
10//! publication, cookie invalidation and lock transitions, none of which the
11//! machine's register-and-cursor model expresses. They still run inside the
12//! same transaction machinery as DML - the statement savepoint, the journal
13//! and the commit are identical - which is what the protocol actually
14//! requires. The row-touching part of DDL is ordinary storage work and goes
15//! through the same pager as everything else.
16
17use crate::ast::{self, ObjectKind, TransactionBehaviour};
18use crate::bind::{no_such_table, refused, schema_refused, unsupported, Binder, BoundExpr};
19use crate::catalog_view::CatalogView;
20use crate::catalog_view::TableKind;
21use crate::diagnostic::ParseError;
22use crate::lexer::Span;
23use inillucent_value::Collation;
24
25/// Returns the direct children of an expression node.
26///
27/// The arena has no walker of its own, and the only caller that needs one is
28/// the generated-column check, so it lives beside it rather than becoming a
29/// method every other reader would have to ignore.
30pub(crate) fn expression_children(ast: &crate::ast::Ast, expr: ast::ExprId) -> Vec<ast::ExprId> {
31    let mut out = Vec::new();
32    let Some(node) = ast.expr(expr) else {
33        return out;
34    };
35    match node {
36        ast::Expr::Unary { operand, .. } => out.push(*operand),
37        ast::Expr::Binary { left, right, .. } => {
38            out.push(*left);
39            out.push(*right);
40        }
41        ast::Expr::Collate { operand, .. } | ast::Expr::Cast { operand, .. } => out.push(*operand),
42        ast::Expr::IsNull { operand, .. } => out.push(*operand),
43        ast::Expr::Raise {
44            message: Some(message),
45            ..
46        } => out.push(*message),
47        ast::Expr::Is { left, right, .. } => {
48            out.push(*left);
49            out.push(*right);
50        }
51        ast::Expr::Between {
52            operand, low, high, ..
53        } => {
54            out.push(*operand);
55            out.push(*low);
56            out.push(*high);
57        }
58        ast::Expr::In { operand, rhs, .. } => {
59            out.push(*operand);
60            if let ast::InRhs::List(items) = rhs {
61                out.extend(items.iter().copied());
62            }
63        }
64        ast::Expr::Case {
65            operand,
66            branches,
67            otherwise,
68        } => {
69            if let Some(operand) = operand {
70                out.push(*operand);
71            }
72            for (when, then) in branches {
73                out.push(*when);
74                out.push(*then);
75            }
76            if let Some(otherwise) = otherwise {
77                out.push(*otherwise);
78            }
79        }
80        ast::Expr::Pattern {
81            operand,
82            pattern,
83            escape,
84            ..
85        } => {
86            out.push(*operand);
87            out.push(*pattern);
88            if let Some(escape) = escape {
89                out.push(*escape);
90            }
91        }
92        ast::Expr::Function {
93            arguments: Some(arguments),
94            ..
95        } => out.extend(arguments.iter().copied()),
96        _ => {}
97    }
98    out
99}
100
101/// Returns whether a column is declared `UNIQUE` in its own definition.
102///
103/// SQLite sets its "unique" flag on a column only for `UNIQUE` written in the
104/// column's definition. A column named by a table level `UNIQUE (a, b)` or by
105/// `CREATE UNIQUE INDEX` does not get it, and `DROP COLUMN` treats the two
106/// differently: only the first is refused up front.
107///
108/// @param create_sql - the table's stored `CREATE TABLE` text
109/// @param position - the column's declared position
110fn declared_unique(create_sql: &[u8], position: usize) -> bool {
111    let limits = inillucent_base::limits::Limits::default();
112    let Ok(parsed) = crate::parser::parse_next_statement(create_sql, 0, &limits) else {
113        return false;
114    };
115    let ast::Statement::CreateTable {
116        body: ast::CreateTableBody::Columns { columns, .. },
117        ..
118    } = &parsed.statement
119    else {
120        return false;
121    };
122    columns.get(position).is_some_and(|column| {
123        column
124            .constraints
125            .iter()
126            .any(|(_, constraint)| matches!(constraint, ast::ColumnConstraint::Unique(_)))
127    })
128}
129
130/// Refuses `NULLS FIRST` and `NULLS LAST` on an index key.
131///
132/// SQLite's grammar accepts them there, because it reads an index key as an ORDER BY term, and
133/// then refuses them with a message that points at nothing.
134///
135/// @param columns - the key columns as written
136fn refuse_nulls_order(columns: &[ast::IndexedColumn]) -> Result<(), ParseError> {
137    for column in columns {
138        let word = match column.nulls {
139            Some(ast::NullOrder::First) => "FIRST",
140            Some(ast::NullOrder::Last) => "LAST",
141            None => continue,
142        };
143        return Err(refused(
144            format!("unsupported use of NULLS {word}"),
145            Span::default(),
146        ));
147    }
148    Ok(())
149}
150
151/// Returns the failure `RENAME COLUMN` and `DROP COLUMN` give for a column that
152/// is not there, which SQLite words with the name in double quotes.
153///
154/// @param name - the column as the statement wrote it
155fn no_such_quoted_column(name: &[u8]) -> ParseError {
156    refused(
157        format!("no such column: \"{}\"", String::from_utf8_lossy(name)),
158        Span::default(),
159    )
160}
161
162/// Returns the failure an `ALTER TABLE` gives for a name that is not a table.
163///
164/// SQLite words it differently for each kind of statement when the name is a
165/// view.
166///
167/// @param action - what the statement does
168/// @param target - the object that was named
169fn not_a_table_message(
170    action: &ast::AlterAction,
171    target: &crate::catalog_view::TableInfo,
172) -> String {
173    let name = String::from_utf8_lossy(&target.name).into_owned();
174    if target.kind != TableKind::View {
175        return format!("cannot alter {name}: not a table");
176    }
177    match action {
178        ast::AlterAction::RenameTo(_) => format!("view {name} may not be altered"),
179        ast::AlterAction::RenameColumn { .. } => {
180            format!("cannot rename columns of view \"{name}\"")
181        }
182        ast::AlterAction::AddColumn(_) => "Cannot add a column to a view".to_string(),
183        ast::AlterAction::DropColumn(_) => format!("cannot drop column from view \"{name}\""),
184        ast::AlterAction::SetNotNull { .. }
185        | ast::AlterAction::DropNotNull(_)
186        | ast::AlterAction::AddCheck { .. }
187        | ast::AlterAction::DropConstraint(_) => {
188            format!("cannot edit constraints of view \"{name}\"")
189        }
190    }
191}
192
193/// Returns the failure `REINDEX` gives for a name that is nothing it knows.
194///
195/// SQLite's message does not name the object, and it points at nothing, so the
196/// failure carries no position either. It used to be an `Unexpected` token failure,
197/// which printed `near "unable to identify ...": syntax error`.
198///
199/// @param name - the name that matched no table, index or collation
200/// @param span - where the name was written, which the failure does not report
201fn no_such_collation_sequence(name: &[u8], span: Span) -> ParseError {
202    let _ = (name, span);
203    ParseError::new(
204        crate::diagnostic::ParseErrorKind::Refused(
205            "unable to identify the object to be reindexed".to_string(),
206        ),
207        Span::default(),
208    )
209}
210
211/// How an explicit `BEGIN` acquires its rights.
212#[derive(Clone, Copy, Debug, Eq, PartialEq)]
213pub enum BeginKind {
214    /// Take nothing until the first read or write needs it.
215    Deferred,
216    /// Take the writer's reservation now.
217    Immediate,
218    /// Take the write lock now, excluding readers too.
219    Exclusive,
220}
221
222impl BeginKind {
223    /// Returns the kind a `BEGIN` clause names, defaulting to DEFERRED.
224    pub fn of(behaviour: Option<TransactionBehaviour>) -> BeginKind {
225        match behaviour {
226            None | Some(TransactionBehaviour::Deferred) => BeginKind::Deferred,
227            Some(TransactionBehaviour::Immediate) => BeginKind::Immediate,
228            Some(TransactionBehaviour::Exclusive) => BeginKind::Exclusive,
229        }
230    }
231}
232
233/// What an added column would do to rows that already exist.
234///
235/// SQLite refuses `PRIMARY KEY` and `UNIQUE` while it is still compiling,
236/// because no table can take them however empty it is. The other three it
237/// defers: a `NOT NULL` column with no default, a non-constant default and a
238/// `STORED` generated column are refused *only when there is a row to break*,
239/// and are accepted on an empty table. That is not a quirk worth smoothing
240/// over - it is the difference between a migration that runs on a fresh
241/// database and one that runs on a populated one - so the binder records what
242/// it saw and the executor, which knows the row count, decides.
243#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
244pub struct AddedColumnRisk {
245    /// `REFERENCES` with a `DEFAULT` that is not `NULL`.
246    ///
247    /// Only a refusal while `PRAGMA foreign_keys` is on, which the binder does
248    /// not know, so [`AddedColumnRisk::refusal`] takes it as an argument.
249    pub references_with_default: bool,
250    /// `NOT NULL` with nothing to fill the existing rows with.
251    pub null_without_default: bool,
252    /// A `DEFAULT` the existing rows cannot all be given one answer from.
253    pub non_constant_default: bool,
254    /// `GENERATED ALWAYS AS (...) STORED`, which needs a value in every record.
255    pub generated_stored: bool,
256}
257
258impl AddedColumnRisk {
259    /// Returns the refusal a table with rows in it owes, in SQLite's wording.
260    ///
261    /// The capitalisation is the reference's own and is inconsistent between
262    /// the four; it is reproduced rather than tidied, because a caller
263    /// matching on the message is matching on what SQLite prints. The order is
264    /// the order SQLite tests in, which decides the message when a column
265    /// breaks more than one rule.
266    ///
267    /// @param foreign_keys - whether `PRAGMA foreign_keys` is on
268    pub fn refusal(&self, foreign_keys: bool) -> Option<&'static str> {
269        if foreign_keys && self.references_with_default {
270            return Some("Cannot add a REFERENCES column with non-NULL default value");
271        }
272        if self.null_without_default {
273            return Some("Cannot add a NOT NULL column with default value NULL");
274        }
275        if self.non_constant_default {
276            return Some("Cannot add a column with non-constant default");
277        }
278        if self.generated_stored {
279            return Some("cannot add a STORED column");
280        }
281        None
282    }
283}
284
285/// What an `ALTER TABLE` does, with every name already resolved.
286#[derive(Clone, Debug, PartialEq, Eq)]
287pub enum AlterKind {
288    /// `RENAME TO`.
289    RenameTable {
290        /// The new name, as written.
291        to: Vec<u8>,
292    },
293    /// `RENAME COLUMN a TO b`.
294    RenameColumn {
295        /// The column's current name, as stored.
296        from: Vec<u8>,
297        /// Its new name, as written.
298        to: Vec<u8>,
299        /// Whether the new name was written quoted, which makes every
300        /// occurrence in the schema quoted.
301        to_quoted: bool,
302    },
303    /// `ADD COLUMN`.
304    AddColumn {
305        /// Where the definition starts in the statement's own source.
306        ///
307        /// The offsets rather than the text, for the same reason `CREATE TABLE`
308        /// carries an offset: the executor has the statement's source and
309        /// slicing it there keeps the *written* definition - its spacing, its
310        /// case and its comments - rather than something re-rendered from the
311        /// parse.
312        start: u32,
313        /// Where it ends.
314        end: u32,
315        /// What it would do to rows that already exist.
316        risk: AddedColumnRisk,
317    },
318    /// An `ADD COLUMN` that SQLite refuses only after it has changed the schema.
319    AddColumnFailsAfter {
320        /// The full message, for example `error in table t after add column: ...`.
321        message: String,
322    },
323    /// `DROP COLUMN`.
324    DropColumn {
325        /// The column's name, as stored.
326        name: Vec<u8>,
327        /// Its declared position, which is the record slot to remove.
328        position: u16,
329    },
330    /// `ALTER COLUMN ... SET NOT NULL`.
331    SetNotNull {
332        /// The column's name, as stored.
333        name: Vec<u8>,
334        /// Its declared position.
335        position: u16,
336        /// Where `NOT NULL` starts in the statement's own source.
337        start: u32,
338        /// Where the clause ends.
339        end: u32,
340    },
341    /// `ALTER COLUMN ... DROP NOT NULL`.
342    DropNotNull {
343        /// The column's name, as stored.
344        name: Vec<u8>,
345        /// Its declared position.
346        position: u16,
347    },
348    /// `ADD [CONSTRAINT name] CHECK (...)`.
349    AddCheck {
350        /// The constraint's name, when it has one.
351        name: Option<Vec<u8>>,
352        /// Where the constraint starts in the statement's own source.
353        start: u32,
354        /// Where it ends.
355        end: u32,
356        /// Where the predicate starts in the statement's own source.
357        expr_start: u32,
358        /// Where the predicate ends.
359        expr_end: u32,
360    },
361    /// `DROP CONSTRAINT name`.
362    DropConstraint {
363        /// The constraint's name, as written.
364        name: Vec<u8>,
365    },
366}
367
368/// One key column of an index being created.
369#[derive(Clone, Debug, PartialEq, Eq)]
370pub struct IndexKeyColumn {
371    /// The table column, when the key is a bare column.
372    ///
373    /// `None` for a key that is an expression. It was a bare `u16` while
374    /// `CREATE INDEX ix ON t(lower(a))` was refused in the binder; the field is
375    /// an `Option` now so that a reader which needs a column - a module-backed
376    /// index, say - has to say what it does when there is not one, rather than
377    /// reading a position that was invented to fill the slot.
378    pub column: Option<u16>,
379    /// The key expression, as written, when the key is one.
380    pub expr_sql: Option<Vec<u8>>,
381    /// The folded collation name.
382    pub collation: Vec<u8>,
383    /// Whether the key is stored descending.
384    pub descending: bool,
385}
386
387/// A statement the session carries out.
388#[derive(Clone, Debug, PartialEq)]
389pub enum Directive {
390    /// `BEGIN`.
391    Begin(BeginKind),
392    /// `COMMIT` or `END`.
393    Commit,
394    /// `ROLLBACK`, or `ROLLBACK TO savepoint`.
395    Rollback {
396        /// The savepoint to roll back to, when one was named.
397        savepoint: Option<Vec<u8>>,
398    },
399    /// `SAVEPOINT name`.
400    Savepoint(Vec<u8>),
401    /// `RELEASE name`.
402    Release(Vec<u8>),
403    /// `CREATE TABLE`.
404    CreateTable {
405        /// Whether `IF NOT EXISTS` was written.
406        if_not_exists: bool,
407        /// Which attached database.
408        database: usize,
409        /// The table name as written.
410        name: Vec<u8>,
411        /// The byte the name starts at in the statement's source.
412        name_offset: u32,
413        /// Whether the table already exists.
414        exists: bool,
415        /// A failure the statement reports when it runs rather than when it is prepared.
416        ///
417        /// **A generated column loop is found by running a query.** SQLite finishes
418        /// `CREATE TABLE` by running `SELECT * FROM` the new table, and that query is
419        /// where `generated column loop on "c"` comes from, so the shell prints it as
420        /// `Error near line N`, not `Parse error`. Nothing is created when it is set.
421        refusal: Option<String>,
422    },
423    /// `CREATE TABLE ... AS SELECT`.
424    ///
425    /// A `CREATE` whose column list comes from a plan, which is why it is a
426    /// directive of its own rather than a flag on the one above: everything
427    /// about the table - its column names, and the declared types it inherits
428    /// from the query's origin columns - is decided by binding the query, and
429    /// the `CREATE` text that is stored is *synthesised* rather than being a
430    /// slice of what was typed.
431    CreateTableAsSelect {
432        /// Whether `IF NOT EXISTS` was written.
433        if_not_exists: bool,
434        /// Which attached database.
435        database: usize,
436        /// The table name as written.
437        name: Vec<u8>,
438        /// Whether the table already exists.
439        exists: bool,
440        /// The `CREATE TABLE name(...)` text to store, built from the query.
441        create_sql: Vec<u8>,
442        /// The `SELECT` that fills it, as the source text it was written as.
443        ///
444        /// The text rather than the bound query, because the rows are inserted
445        /// by an ordinary `INSERT INTO name <select>` compiled against the
446        /// schema *after* the table exists - which is one implementation of
447        /// what an insert means rather than a second one written here.
448        select_sql: Vec<u8>,
449    },
450    /// `CREATE VIRTUAL TABLE`.
451    CreateVirtualTable {
452        /// Whether `IF NOT EXISTS` was written.
453        if_not_exists: bool,
454        /// Which attached database.
455        database: usize,
456        /// The table name as written.
457        name: Vec<u8>,
458        /// The module name as written.
459        module: Vec<u8>,
460        /// The arguments inside the parentheses, as written.
461        arguments: Vec<Vec<u8>>,
462        /// The byte the name starts at in the statement's source.
463        name_offset: u32,
464        /// Whether the table already exists.
465        exists: bool,
466    },
467    /// `ALTER TABLE`.
468    Alter {
469        /// Which attached database.
470        database: usize,
471        /// The table being altered, by its stored name.
472        table: Vec<u8>,
473        /// What to do to it.
474        action: AlterKind,
475    },
476    /// `REINDEX`, over one index, one table's indexes, or everything.
477    Reindex {
478        /// Which attached database.
479        database: usize,
480        /// The indexes to rebuild, by name.
481        indexes: Vec<Vec<u8>>,
482    },
483    /// `VACUUM`, which rebuilds the database into a fresh file.
484    Vacuum {
485        /// Which attached database.
486        database: usize,
487        /// The file `VACUUM INTO` writes the rebuilt copy to.
488        ///
489        /// A string literal, as SQLite's grammar has it. `INTO` leaves the
490        /// database it was run on completely alone, which is the difference
491        /// between the two forms and the reason the path is carried rather
492        /// than resolved here.
493        into: Option<Vec<u8>>,
494        /// The text of an `INTO` expression that is not a string literal, such
495        /// as `(SELECT n FROM p)` or `'a' || 'b'`, which the engine evaluates
496        /// when the statement runs. `into` is `None` when this is set.
497        into_sql: Option<String>,
498    },
499    /// `ATTACH`, which adds a database file to this connection.
500    Attach {
501        /// The file to open, as the literal it was written as.
502        file: Vec<u8>,
503        /// The name it will be known by.
504        schema: Vec<u8>,
505        /// The `KEY` clause's text: the file's encryption key, or empty for a
506        /// plaintext file. `None` when there was no `KEY` clause, which means
507        /// the connection's own key.
508        key: Option<Vec<u8>>,
509    },
510    /// `DETACH`, which removes one.
511    Detach {
512        /// The name it was attached under.
513        schema: Vec<u8>,
514    },
515    /// `ANALYZE`, over one object or the whole schema.
516    Analyze {
517        /// Which attached database.
518        database: usize,
519        /// The one table or index to measure, or nothing for all of them.
520        table: Option<Vec<u8>>,
521        /// Whether the statement was a bare `ANALYZE`, which measures every
522        /// database but `temp` rather than `database` alone.
523        every_schema: bool,
524    },
525    /// `CREATE VIEW`.
526    CreateView {
527        /// Whether `IF NOT EXISTS` was written.
528        if_not_exists: bool,
529        /// Which attached database.
530        database: usize,
531        /// The view name as written.
532        name: Vec<u8>,
533        /// The byte the name starts at in the statement's source.
534        name_offset: u32,
535        /// Whether the view already exists.
536        exists: bool,
537    },
538    /// `CREATE TRIGGER`.
539    CreateTrigger {
540        /// Which attached database.
541        database: usize,
542        /// The trigger name as written.
543        name: Vec<u8>,
544        /// The byte the name starts at in the statement's source.
545        name_offset: u32,
546        /// The table or view the trigger is attached to.
547        table: Vec<u8>,
548        /// Whether the trigger already exists.
549        exists: bool,
550    },
551    /// `CREATE INDEX`.
552    CreateIndex {
553        /// Whether `UNIQUE` was written.
554        unique: bool,
555        /// Whether `IF NOT EXISTS` was written.
556        if_not_exists: bool,
557        /// Which attached database.
558        database: usize,
559        /// The index name as written.
560        name: Vec<u8>,
561        /// The byte the name starts at in the statement's source.
562        name_offset: u32,
563        /// The table it indexes.
564        table: Vec<u8>,
565        /// The root page of that table.
566        table_root: u32,
567        /// The module named by `USING`, folded, when one was.
568        using: Option<Vec<u8>>,
569        /// The key columns.
570        columns: Vec<IndexKeyColumn>,
571        /// The storage parameters `WITH ( ... )` named, checked against the
572        /// module that will read them.
573        settings: Vec<(Vec<u8>, Vec<u8>)>,
574        /// Whether the index already exists.
575        exists: bool,
576    },
577    /// `DROP TABLE` or `DROP INDEX`.
578    Drop {
579        /// Which kind of object.
580        kind: ObjectKind,
581        /// Whether `IF EXISTS` was written.
582        if_exists: bool,
583        /// Which attached database.
584        database: usize,
585        /// The object name.
586        name: Vec<u8>,
587        /// The root page to free, or zero when the object has none.
588        root: u32,
589        /// The root pages of the indexes a `DROP TABLE` takes with it.
590        index_roots: Vec<u32>,
591        /// Whether the object exists.
592        exists: bool,
593    },
594    /// `PRAGMA`.
595    Pragma {
596        /// The schema the pragma was qualified with, when one was written.
597        ///
598        /// `PRAGMA aux.table_info(t)` asks about the attached database rather
599        /// than about `main`, and a pragma that dropped the qualifier would
600        /// answer confidently about the wrong file.
601        database: Option<usize>,
602        /// The pragma name, folded.
603        name: Vec<u8>,
604        /// The argument, when one was written.
605        argument: Option<PragmaArgument>,
606    },
607}
608
609/// What a `PRAGMA` was given.
610#[derive(Clone, Debug, PartialEq)]
611pub enum PragmaArgument {
612    /// A bare word, such as `PRAGMA journal_mode = WAL`.
613    Name(Vec<u8>),
614    /// An expression, such as `PRAGMA user_version = 4`.
615    Value(BoundExpr),
616}
617
618/// Whether a `CREATE INDEX` declared `UNIQUE`.
619///
620/// **An enum rather than a `bool` beside another `bool` (task-1962, A9).**
621/// `bind_create_index` took `unique` and `if_not_exists` adjacent and
622/// positional; swapping them compiles and declares a unique index where the
623/// statement asked for `IF NOT EXISTS`.
624#[derive(Clone, Copy, Debug, Eq, PartialEq)]
625pub enum Uniqueness {
626    /// `CREATE UNIQUE INDEX`: two rows may not share a key.
627    Unique,
628    /// `CREATE INDEX`: a key may repeat.
629    Duplicates,
630}
631
632/// Whether a `CREATE` declared `IF NOT EXISTS`.
633#[derive(Clone, Copy, Debug, Eq, PartialEq)]
634pub enum IfNotExists {
635    /// The statement is a no-op when the object is already there.
636    Skip,
637    /// The statement fails when the object is already there.
638    Refuse,
639}
640
641/// Everything a `CREATE INDEX` statement names.
642///
643/// The grammar's own fields, gathered rather than passed as nine positional
644/// arguments of which two were adjacent booleans.
645pub struct CreateIndexSpec<'a> {
646    /// Whether the index refuses a repeated key.
647    pub unique: Uniqueness,
648    /// What to do when the index is already there.
649    pub if_not_exists: IfNotExists,
650    /// The schema the index is created in, when one was written.
651    pub database: Option<ast::NameId>,
652    /// The index's name.
653    pub name: ast::NameId,
654    /// The table it is over.
655    pub table: ast::NameId,
656    /// The module named by `USING`, for the extension index forms.
657    pub using: Option<ast::NameId>,
658    /// The indexed columns, in key order.
659    pub columns: &'a [ast::IndexedColumn],
660    /// The `WITH` settings, as written.
661    pub settings: &'a [Vec<u8>],
662    /// The `WHERE` of a partial index, as an expression of the statement.
663    pub filter: Option<ast::ExprId>,
664}
665
666/// The fields of a `CREATE TRIGGER`, passed as one argument.
667///
668/// Ten parameters is past the point where their order is checkable by reading,
669/// and every one of them is a field of the statement rather than something
670/// computed here.
671pub(crate) struct CreateTriggerParts<'p> {
672    /// Whether `TEMP` was written.
673    pub temporary: bool,
674    /// Whether `IF NOT EXISTS` was written.
675    pub if_not_exists: bool,
676    /// The schema qualifier.
677    pub database: Option<ast::NameId>,
678    /// The trigger name.
679    pub name: ast::NameId,
680    /// When it fires.
681    pub time: Option<ast::TriggerTime>,
682    /// The table it is attached to.
683    pub table: ast::NameId,
684    /// The schema qualifier on the table.
685    pub table_database: Option<ast::NameId>,
686    /// Whether `FOR EACH ROW` was written.
687    pub for_each_row: bool,
688    /// The `WHEN` guard.
689    pub when: Option<ast::ExprId>,
690    /// The body statements.
691    pub body: &'p [ast::Statement],
692}
693
694impl<'a> Binder<'a> {
695    /// Binds a statement the session carries out itself.
696    pub fn bind_directive(&mut self, statement: &ast::Statement) -> Result<Directive, ParseError> {
697        match statement {
698            ast::Statement::Begin { behaviour } => Ok(Directive::Begin(BeginKind::of(*behaviour))),
699            ast::Statement::Commit => Ok(Directive::Commit),
700            ast::Statement::Rollback { savepoint } => Ok(Directive::Rollback {
701                savepoint: savepoint.map(|id| self.ast.text(id).to_vec()),
702            }),
703            ast::Statement::Savepoint(name) => {
704                Ok(Directive::Savepoint(self.ast.text(*name).to_vec()))
705            }
706            ast::Statement::Release(name) => Ok(Directive::Release(self.ast.text(*name).to_vec())),
707            ast::Statement::CreateTable {
708                temporary,
709                if_not_exists,
710                database,
711                name,
712                body,
713            } => self.bind_create_table(*temporary, *if_not_exists, *database, *name, body),
714            ast::Statement::CreateVirtualTable {
715                if_not_exists,
716                database,
717                name,
718                module,
719                arguments,
720            } => {
721                self.bind_create_virtual_table(*if_not_exists, *database, *name, *module, arguments)
722            }
723            ast::Statement::CreateIndex {
724                unique,
725                if_not_exists,
726                database,
727                name,
728                table,
729                using,
730                columns,
731                settings,
732                filter,
733            } => self.bind_create_index(&CreateIndexSpec {
734                unique: if *unique {
735                    Uniqueness::Unique
736                } else {
737                    Uniqueness::Duplicates
738                },
739                if_not_exists: if *if_not_exists {
740                    IfNotExists::Skip
741                } else {
742                    IfNotExists::Refuse
743                },
744                database: *database,
745                name: *name,
746                table: *table,
747                using: *using,
748                columns,
749                settings,
750                filter: *filter,
751            }),
752            ast::Statement::Analyze { database, name } => self.bind_analyze(*database, *name),
753            ast::Statement::AlterTable {
754                database,
755                table,
756                action,
757            } => self.bind_alter(*database, *table, action),
758            ast::Statement::Reindex { database, name } => self.bind_reindex(*database, *name),
759            ast::Statement::Vacuum { database, into } => self.bind_vacuum(*database, *into),
760            ast::Statement::Attach { file, schema, key } => self.bind_attach(*file, *schema, *key),
761            ast::Statement::Detach { schema } => self.bind_detach(*schema),
762            ast::Statement::CreateView {
763                temporary,
764                if_not_exists,
765                database,
766                name,
767                columns,
768                select,
769            } => self.bind_create_view(
770                *temporary,
771                *if_not_exists,
772                *database,
773                *name,
774                columns,
775                *select,
776            ),
777            ast::Statement::CreateTrigger {
778                temporary,
779                if_not_exists,
780                database,
781                name,
782                time,
783                event: _,
784                table,
785                table_database,
786                for_each_row,
787                when,
788                body,
789            } => self.bind_create_trigger(CreateTriggerParts {
790                temporary: *temporary,
791                if_not_exists: *if_not_exists,
792                database: *database,
793                name: *name,
794                time: *time,
795                table: *table,
796                table_database: *table_database,
797                for_each_row: *for_each_row,
798                when: *when,
799                body,
800            }),
801            ast::Statement::Drop {
802                kind,
803                if_exists,
804                database,
805                name,
806            } => self.bind_drop(*kind, *if_exists, *database, *name),
807            ast::Statement::Pragma {
808                database,
809                name,
810                value,
811            } => self.bind_pragma(*database, *name, value),
812            _ => Err(unsupported(
813                "this statement is not implemented yet",
814                Span::default(),
815            )),
816        }
817    }
818
819    /// Binds a `CREATE TABLE`.
820    fn bind_create_virtual_table(
821        &mut self,
822        if_not_exists: bool,
823        database: Option<ast::NameId>,
824        name: ast::NameId,
825        module: ast::NameId,
826        arguments: &[Vec<u8>],
827    ) -> Result<Directive, ParseError> {
828        let index = self.resolve_database(database)?;
829        let written = self.ast.text(name).to_vec();
830        if written.to_ascii_lowercase().starts_with(b"sqlite_") {
831            return Err(refused(
832                format!(
833                    "object name reserved for internal use: {}",
834                    String::from_utf8_lossy(&written)
835                ),
836                Span::default(),
837            ));
838        }
839        let folded = self.ast.folded(name).to_vec();
840        let database_name = self.catalog.database_name(index).to_vec();
841        let exists = self
842            .catalog
843            .find_table(Some(database_name.as_slice()), &folded)
844            .is_some();
845        if exists && !if_not_exists {
846            return Err(self.already_exists(&database_name, &folded, name));
847        }
848        if !exists {
849            self.refuse_index_namesake(&database_name, &folded, &written)?;
850        }
851        Ok(Directive::CreateVirtualTable {
852            if_not_exists,
853            database: index,
854            name: written,
855            module: self.ast.text(module).to_vec(),
856            arguments: arguments.to_vec(),
857            name_offset: self
858                .ast
859                .name(name)
860                .map(|entry| entry.span.start)
861                .unwrap_or_default(),
862            exists,
863        })
864    }
865
866    /// Binds `CREATE TABLE`, refusing what the file format cannot hold.
867    fn bind_create_table(
868        &mut self,
869        temporary: bool,
870        if_not_exists: bool,
871        database: Option<ast::NameId>,
872        name: ast::NameId,
873        body: &ast::CreateTableBody,
874    ) -> Result<Directive, ParseError> {
875        let temp = self.temporary_database(temporary, database, false)?;
876        // **Two bodies, and the second one is built.** This used to be written
877        // as two `let ... else` bindings, the inner one answering
878        // `unsupported("CREATE TABLE ... AS SELECT")` - an arm no statement
879        // could reach, because `CreateTableBody` has exactly these two
880        // variants, so a feature that works was described by a refusal
881        // (task-1979, section 8.3). A match over both says the same thing with
882        // nothing left over.
883        let (columns, constraints, without_rowid, strict) = match body {
884            ast::CreateTableBody::AsSelect(select) => {
885                return self.bind_create_table_as_select(
886                    temp,
887                    if_not_exists,
888                    database,
889                    name,
890                    *select,
891                )
892            }
893            ast::CreateTableBody::Columns {
894                columns,
895                constraints,
896                without_rowid,
897                strict,
898            } => (columns, constraints, without_rowid, strict),
899        };
900        // First, because SQLite meets `PRIMARY KEY(... AUTOINCREMENT)` before it
901        // looks at what the key names.
902        self.check_table_autoincrement(columns, constraints, *without_rowid)?;
903        for (_, constraint) in constraints {
904            match constraint {
905                ast::TableConstraint::PrimaryKey { columns, .. }
906                | ast::TableConstraint::Unique { columns, .. } => refuse_nulls_order(columns)?,
907                _ => {}
908            }
909        }
910        self.check_table_shape(self.ast.text(name), columns, constraints)?;
911        self.check_table_declarations(self.ast.text(name), columns, constraints)?;
912        if *without_rowid && !self.declares_primary_key(columns, constraints) {
913            return Err(schema_refused(
914                format!(
915                    "PRIMARY KEY missing on table {}",
916                    String::from_utf8_lossy(self.ast.text(name))
917                ),
918                Span::default(),
919            ));
920        }
921        self.check_autoincrement(columns, *without_rowid)?;
922        self.check_column_collations(columns)?;
923        if *strict {
924            self.check_strict(columns, name)?;
925        }
926        let refusal = self.check_generated(columns, constraints)?;
927        self.refuse_all_generated(columns)?;
928        if columns.is_empty() {
929            return Err(refused(
930                "a table must have at least one column",
931                Span::default(),
932            ));
933        }
934        let index = match temp {
935            Some(index) => index,
936            None => self.resolve_database(database)?,
937        };
938        let written = self.ast.text(name).to_vec();
939        if written.to_ascii_lowercase().starts_with(b"sqlite_") {
940            return Err(refused(
941                format!(
942                    "object name reserved for internal use: {}",
943                    String::from_utf8_lossy(&written)
944                ),
945                Span::default(),
946            ));
947        }
948        let folded = self.ast.folded(name).to_vec();
949        let database_name = self.catalog.database_name(index).to_vec();
950        let exists = self
951            .catalog
952            .find_table(Some(database_name.as_slice()), &folded)
953            .is_some();
954        if exists && !if_not_exists {
955            return Err(self.already_exists(&database_name, &folded, name));
956        }
957        if !exists {
958            self.refuse_index_namesake(&database_name, &folded, &written)?;
959        }
960        self.record_write_dependency(index);
961        Ok(Directive::CreateTable {
962            if_not_exists,
963            database: index,
964            name: written,
965            name_offset: self.name_offset(name),
966            exists,
967            refusal,
968        })
969    }
970
971    /// Binds `CREATE TABLE ... AS SELECT`.
972    ///
973    /// **The column list comes from a plan**, which is the whole of why this is
974    /// a shape of its own. SQLite takes the table's columns from the query's
975    /// result columns: the name each one reports, and the declared type it
976    /// carries when it is a plain reference to a column that has one. So
977    /// `CREATE TABLE u AS SELECT a*2 AS d, b, c FROM t` on `t(a INTEGER, b TEXT,
978    /// c REAL)` stores `CREATE TABLE u(d,b TEXT,c REAL)` - `d` is an expression
979    /// and inherits nothing, and the other two inherit their origin's type.
980    ///
981    /// The rows are inserted afterwards by an ordinary `INSERT INTO name
982    /// <select>`, compiled against the schema once the table is in it. That is
983    /// one implementation of what an insert means rather than a second one
984    /// written into the DDL path, and it is what makes the affinity Part B4
985    /// applies reach these rows too.
986    ///
987    /// @param temp - the temporary database's index, when `TEMP` was written
988    /// @param if_not_exists - whether `IF NOT EXISTS` was written
989    /// @param database - the schema qualifier, when one was written
990    /// @param name - the table's name
991    /// @param select - the query the table is built from
992    fn bind_create_table_as_select(
993        &mut self,
994        temp: Option<usize>,
995        if_not_exists: bool,
996        database: Option<ast::NameId>,
997        name: ast::NameId,
998        select: ast::SelectId,
999    ) -> Result<Directive, ParseError> {
1000        let index = match temp {
1001            Some(index) => index,
1002            None => self.resolve_database(database)?,
1003        };
1004        let written = self.ast.text(name).to_vec();
1005        if written.to_ascii_lowercase().starts_with(b"sqlite_") {
1006            return Err(refused(
1007                format!(
1008                    "object name reserved for internal use: {}",
1009                    String::from_utf8_lossy(&written)
1010                ),
1011                Span::default(),
1012            ));
1013        }
1014        let folded = self.ast.folded(name).to_vec();
1015        let database_name = self.catalog.database_name(index).to_vec();
1016        let exists = self
1017            .catalog
1018            .find_table(Some(database_name.as_slice()), &folded)
1019            .is_some();
1020        if exists && !if_not_exists {
1021            return Err(self.already_exists(&database_name, &folded, name));
1022        }
1023        if !exists {
1024            self.refuse_index_namesake(&database_name, &folded, &written)?;
1025        }
1026        let span = self
1027            .ast
1028            .select(select)
1029            .map(|held| held.span)
1030            .ok_or_else(|| refused("the query could not be read", Span::default()))?;
1031        let select_sql = self
1032            .source
1033            .get(span.start as usize..span.end as usize)
1034            .ok_or_else(|| refused("the query could not be read", span))?
1035            .to_vec();
1036        // Bound rather than merely parsed, because binding is what resolves the
1037        // result columns' names and origins - and because a query that does not
1038        // bind has to be refused here rather than after the table exists.
1039        let bound = self.bind_select(select)?;
1040        if bound.columns.is_empty() {
1041            return Err(refused(
1042                "a table must have at least one column",
1043                Span::default(),
1044            ));
1045        }
1046        // **The declaration a `CREATE TABLE ... AS SELECT` stores is the
1047        // *affinity*, not the source column's declared type.** SQLite writes
1048        // `a INT` for a source column declared `INTEGER` and `b TEXT` for one
1049        // declared `VARCHAR(3)`, because what survives a query is the affinity
1050        // and nothing else - the width, the precision and the spelling are
1051        // properties of the source table that the copy does not have. Storing
1052        // `VARCHAR(3)` here claimed a constraint the new table does not
1053        // enforce, and made the two schemas differ for every CTAS.
1054        //
1055        // The line break is SQLite's own rule too, so the stored text matches
1056        // byte for byte: the name lengths are added up first, and a wide
1057        // declaration is written one column per line.
1058        // Two columns that share a name are told apart the way a derived table
1059        // tells them apart: `SELECT a, a` makes a table of `a` and `a:1`.
1060        let written_names: Vec<Vec<u8>> = bound
1061            .columns
1062            .iter()
1063            .map(|column| column.name.clone())
1064            .collect();
1065        let names = crate::bind::unique_column_names(&written_names);
1066        let mut width = identifier_width(&written);
1067        for name in &names {
1068            width = width
1069                .saturating_add(identifier_width(name))
1070                .saturating_add(5);
1071        }
1072        let (open, between, close): (&[u8], &[u8], &[u8]) = if width < 50 {
1073            (b"", b",", b")")
1074        } else {
1075            (b"\n  ", b",\n  ", b"\n)")
1076        };
1077        let mut create_sql = Vec::new();
1078        create_sql.extend_from_slice(b"CREATE TABLE ");
1079        // Quoted like a column name: a table name with a quote or a space in it
1080        // must be written as a quoted identifier or the stored text cannot be read.
1081        create_sql.extend_from_slice(&quoted_name(&written));
1082        create_sql.push(b'(');
1083        for (position, (column, name)) in bound.columns.iter().zip(&names).enumerate() {
1084            create_sql.extend_from_slice(if position > 0 { between } else { open });
1085            create_sql.extend_from_slice(&quoted_name(name));
1086            // **A compound's column has the affinity every arm agrees on.**
1087            // SQLite stores no type for `SELECT id FROM a UNION SELECT id FROM
1088            // b` over an INT and a TEXT `id`, and this wrote the first arm's
1089            // type, so a join on the copy converted values it should not.
1090            // The same holds for a column read out of a view or a derived
1091            // table, whose declared type is the first arm's and whose
1092            // affinity is the one the arms agreed on.
1093            let by_affinity =
1094                !bound.compounds.is_empty() || reads_derived_column(&bound, &column.expr);
1095            let (declared, held): (&[u8], _) = if by_affinity {
1096                (b"", bound.column_affinity_if_any(position))
1097            } else {
1098                (&column.declared_type, column.expr.affinity())
1099            };
1100            create_sql.extend_from_slice(affinity_type(declared, held));
1101        }
1102        create_sql.extend_from_slice(close);
1103        self.record_write_dependency(index);
1104        Ok(Directive::CreateTableAsSelect {
1105            if_not_exists,
1106            database: index,
1107            name: written,
1108            exists,
1109            create_sql,
1110            select_sql,
1111        })
1112    }
1113
1114    /// Returns whether a `CREATE TABLE` declares a primary key anywhere.
1115    fn declares_primary_key(
1116        &self,
1117        columns: &[ast::ColumnDef],
1118        constraints: &[(Option<ast::NameId>, ast::TableConstraint)],
1119    ) -> bool {
1120        let on_column = columns.iter().any(|column| {
1121            column.constraints.iter().any(|(_, constraint)| {
1122                matches!(constraint, ast::ColumnConstraint::PrimaryKey { .. })
1123            })
1124        });
1125        on_column
1126            || constraints.iter().any(|(_, constraint)| {
1127                matches!(constraint, ast::TableConstraint::PrimaryKey { .. })
1128            })
1129    }
1130
1131    /// Checks the rules a generated column has to obey.
1132    ///
1133    /// A generated column may not carry a `DEFAULT` - it has no value of its
1134    /// own to fall back to - may not be part of a rowid table's `PRIMARY KEY`,
1135    /// and may not refer to a column that does not exist or to itself. The
1136    /// cycle check is the one that matters: without it a `CREATE TABLE` that
1137    /// describes one is accepted and every later insert recurses.
1138    fn check_generated(
1139        &self,
1140        columns: &[ast::ColumnDef],
1141        constraints: &[(Option<ast::NameId>, ast::TableConstraint)],
1142    ) -> Result<Option<String>, ParseError> {
1143        let names: Vec<Vec<u8>> = columns
1144            .iter()
1145            .map(|column| self.ast.folded(column.name).to_vec())
1146            .collect();
1147        let mut generated: Vec<(usize, Vec<usize>)> = Vec::new();
1148        for (position, column) in columns.iter().enumerate() {
1149            let Some(expr) = self.check_generated_clauses(column)? else {
1150                continue;
1151            };
1152            let mut reads = Vec::new();
1153            self.expression_names(expr, &mut reads);
1154            let mut resolved = Vec::new();
1155            for name in &reads {
1156                let Some(found) = names.iter().position(|candidate| candidate == name) else {
1157                    return Err(crate::bind::no_such_column(name, Span::default()));
1158                };
1159                resolved.push(found);
1160            }
1161            generated.push((position, resolved));
1162        }
1163        self.check_key_has_no_generated(columns, constraints)?;
1164        // A cycle is anything that never becomes computable: repeat the "every
1165        // dependency is settled" pass until it stops making progress, and if
1166        // anything is left it depends on itself, directly or through others.
1167        let mut settled: Vec<usize> = (0..columns.len())
1168            .filter(|position| !generated.iter().any(|(owner, _)| owner == position))
1169            .collect();
1170        let mut pending = generated;
1171        loop {
1172            let before = pending.len();
1173            let mut still = Vec::new();
1174            for (position, reads) in pending {
1175                if reads.iter().all(|read| settled.contains(read)) {
1176                    settled.push(position);
1177                } else {
1178                    still.push((position, reads));
1179                }
1180            }
1181            pending = still;
1182            if pending.is_empty() || pending.len() == before {
1183                break;
1184            }
1185        }
1186        // SQLite computes the generated columns in passes and, when a pass makes no
1187        // progress, names the last column in declaration order that is still waiting.
1188        // That is the column `pending.last()` holds, because `pending` keeps declaration
1189        // order. Measured against the pinned shell for loops of two and three columns.
1190        if let Some((position, _)) = pending.last() {
1191            let written = columns
1192                .get(*position)
1193                .map(|column| String::from_utf8_lossy(self.ast.text(column.name)).into_owned())
1194                .unwrap_or_default();
1195            return Ok(Some(format!("generated column loop on \"{written}\"")));
1196        }
1197        Ok(None)
1198    }
1199
1200    /// Checks the order of the `DEFAULT`, `AS` and `PRIMARY KEY` clauses of one column.
1201    ///
1202    /// SQLite meets the clauses in the order they are written. `AS` after a
1203    /// `DEFAULT` or after another `AS` is `error in generated column "c"`,
1204    /// `DEFAULT` after `AS` is `cannot use DEFAULT on a generated column`, and a
1205    /// `PRIMARY KEY` on a generated column is refused whichever comes first.
1206    /// Returns the generated expression, when the column has one.
1207    ///
1208    /// @param column - the column definition
1209    pub(crate) fn check_generated_clauses(
1210        &self,
1211        column: &ast::ColumnDef,
1212    ) -> Result<Option<ast::ExprId>, ParseError> {
1213        let mut expr = None;
1214        let mut has_default = false;
1215        let mut in_primary_key = false;
1216        for (_, constraint) in &column.constraints {
1217            match constraint {
1218                ast::ColumnConstraint::Generated {
1219                    expr: body,
1220                    bad_storage,
1221                    ..
1222                } => {
1223                    if has_default || expr.is_some() || *bad_storage {
1224                        return Err(refused(
1225                            format!(
1226                                "error in generated column \"{}\"",
1227                                String::from_utf8_lossy(self.ast.text(column.name))
1228                            ),
1229                            Span::default(),
1230                        ));
1231                    }
1232                    expr = Some(*body);
1233                }
1234                ast::ColumnConstraint::Default(_) => {
1235                    if expr.is_some() {
1236                        return Err(refused(
1237                            "cannot use DEFAULT on a generated column",
1238                            Span::default(),
1239                        ));
1240                    }
1241                    has_default = true;
1242                }
1243                ast::ColumnConstraint::PrimaryKey { .. } => in_primary_key = true,
1244                _ => {}
1245            }
1246        }
1247        if expr.is_some() && in_primary_key {
1248            return Err(refused(
1249                "generated columns cannot be part of the PRIMARY KEY",
1250                Span::default(),
1251            ));
1252        }
1253        Ok(expr)
1254    }
1255
1256    /// Refuses a table level `PRIMARY KEY` that names a generated column.
1257    ///
1258    /// A `UNIQUE` constraint may name one.
1259    ///
1260    /// @param columns - the table's columns
1261    /// @param constraints - the table's constraints
1262    fn check_key_has_no_generated(
1263        &self,
1264        columns: &[ast::ColumnDef],
1265        constraints: &[(Option<ast::NameId>, ast::TableConstraint)],
1266    ) -> Result<(), ParseError> {
1267        for (_, constraint) in constraints {
1268            let ast::TableConstraint::PrimaryKey { columns: keys, .. } = constraint else {
1269                continue;
1270            };
1271            for key in keys {
1272                let Some(ast::Expr::Column { column: named, .. }) = self.ast.expr(key.expr) else {
1273                    continue;
1274                };
1275                let folded = self.ast.folded(*named);
1276                let generated = columns.iter().any(|column| {
1277                    self.ast.folded(column.name) == folded
1278                        && column.constraints.iter().any(|(_, constraint)| {
1279                            matches!(constraint, ast::ColumnConstraint::Generated { .. })
1280                        })
1281                });
1282                if generated {
1283                    return Err(refused(
1284                        "generated columns cannot be part of the PRIMARY KEY",
1285                        Span::default(),
1286                    ));
1287                }
1288            }
1289        }
1290        Ok(())
1291    }
1292
1293    /// Collects the folded column names an expression mentions.
1294    fn expression_names(&self, expr: ast::ExprId, into: &mut Vec<Vec<u8>>) {
1295        let Some(node) = self.ast.expr(expr) else {
1296            return;
1297        };
1298        if let ast::Expr::Column { column, .. } = node {
1299            let name = self.ast.folded(*column).to_vec();
1300            if !into.contains(&name) {
1301                into.push(name);
1302            }
1303        }
1304        for child in expression_children(self.ast, expr) {
1305            self.expression_names(child, into);
1306        }
1307    }
1308
1309    /// Refuses a column whose `COLLATE` names a collation sequence that does not exist.
1310    ///
1311    /// SQLite looks the name up when the table is created, so
1312    /// `CREATE TABLE t(a COLLATE nosuch)` fails with `no such collation sequence:
1313    /// nosuch` and creates nothing. It used to be accepted and fail later, on the
1314    /// first statement that compared the column.
1315    ///
1316    /// @param columns - the column definitions
1317    fn check_column_collations(&self, columns: &[ast::ColumnDef]) -> Result<(), ParseError> {
1318        for column in columns {
1319            for (_, constraint) in &column.constraints {
1320                let ast::ColumnConstraint::Collate(name) = constraint else {
1321                    continue;
1322                };
1323                let written = self.ast.text(*name);
1324                if self.collation_named(written).is_none() {
1325                    return Err(crate::bind::no_such_collation(written, Span::default()));
1326                }
1327            }
1328        }
1329        Ok(())
1330    }
1331
1332    /// Checks the rules a `STRICT` table adds to its column list.
1333    ///
1334    /// Every column must name one of six types, and the check is on the
1335    /// declared text rather than on the affinity it maps to: `VARCHAR(10)` has
1336    /// TEXT affinity and is still refused, because STRICT is about what was
1337    /// written and not about what it means.
1338    ///
1339    /// SQLite names the column with its table, `missing datatype for t.a` and
1340    /// `unknown datatype for t.a: "DATETIME"`, with both as written.
1341    ///
1342    /// @param columns - the column definitions
1343    /// @param table - the table's name as written
1344    fn check_strict(
1345        &self,
1346        columns: &[ast::ColumnDef],
1347        table: ast::NameId,
1348    ) -> Result<(), ParseError> {
1349        let table = String::from_utf8_lossy(self.ast.text(table)).into_owned();
1350        for column in columns {
1351            let Some(declared) = column.declared_type.as_ref() else {
1352                return Err(refused(
1353                    format!(
1354                        "missing datatype for {table}.{}",
1355                        String::from_utf8_lossy(self.ast.text(column.name))
1356                    ),
1357                    Span::default(),
1358                ));
1359            };
1360            let folded = declared.to_ascii_uppercase();
1361            let allowed = matches!(
1362                folded.as_slice(),
1363                b"INT" | b"INTEGER" | b"REAL" | b"TEXT" | b"BLOB" | b"ANY"
1364            );
1365            if !allowed {
1366                return Err(refused(
1367                    format!(
1368                        "unknown datatype for {table}.{}: \"{}\"",
1369                        String::from_utf8_lossy(self.ast.text(column.name)),
1370                        String::from_utf8_lossy(declared)
1371                    ),
1372                    Span::default(),
1373                ));
1374            }
1375        }
1376        Ok(())
1377    }
1378
1379    /// Binds an `ANALYZE`.
1380    ///
1381    /// A bare `ANALYZE` measures everything; one with a name measures that
1382    /// object. SQLite accepts a database name, an index name or a table name in
1383    /// the same position and works out which it is, and so does this: the name
1384    /// is resolved against the tables, then the indexes, and only then refused.
1385    fn bind_analyze(
1386        &mut self,
1387        database: Option<ast::NameId>,
1388        name: Option<ast::NameId>,
1389    ) -> Result<Directive, ParseError> {
1390        let Some(name) = name else {
1391            // A bare `ANALYZE` is every database but `temp`, which is SQLite's
1392            // `sqlite3Analyze`.
1393            let temp = self.catalog.database_index(b"temp");
1394            for index in 0..self.catalog.database_count() {
1395                if Some(index) != temp {
1396                    self.record_write_dependency(index);
1397                }
1398            }
1399            return Ok(Directive::Analyze {
1400                database: 0,
1401                table: None,
1402                every_schema: true,
1403            });
1404        };
1405        let folded = self.ast.folded(name).to_vec();
1406        // **An unqualified name may be a database's.** `ANALYZE aux` measures
1407        // every table in `aux`; resolving the name as a table in `main` first
1408        // made it "no such table: aux".
1409        if database.is_none() {
1410            if let Some(index) = self.catalog.database_index(&folded) {
1411                self.record_write_dependency(index);
1412                return Ok(Directive::Analyze {
1413                    database: index,
1414                    table: None,
1415                    every_schema: false,
1416                });
1417            }
1418        }
1419        // An unqualified table or index is searched for in every database, in
1420        // the usual order; a qualified one only in its own. An index is looked
1421        // for first, as SQLite does, and is passed on by its own name, because
1422        // `ANALYZE ix` measures that index alone.
1423        let schema_name = match database {
1424            Some(_) => {
1425                let index = self.resolve_database(database)?;
1426                Some(self.catalog.database_name(index).to_vec())
1427            }
1428            None => None,
1429        };
1430        let found = self
1431            .catalog
1432            .find_index(schema_name.as_deref(), &folded)
1433            .map(|(table, index)| (table.database, index.name.clone()))
1434            .or_else(|| {
1435                self.catalog
1436                    .find_table(schema_name.as_deref(), &folded)
1437                    .map(|table| (table.database, table.name.clone()))
1438            });
1439        let Some((index, table)) = found else {
1440            return Err(no_such_table(self.ast.text(name), Span::default()));
1441        };
1442        self.record_write_dependency(index);
1443        Ok(Directive::Analyze {
1444            database: index,
1445            table: Some(table),
1446            every_schema: false,
1447        })
1448    }
1449
1450    /// Binds an `ALTER TABLE`.
1451    ///
1452    /// Every refusal SQLite makes is made here, where the catalog is available,
1453    /// rather than half-way through rewriting the schema: a rename that is
1454    /// going to fail must fail before anything has been written.
1455    fn bind_alter(
1456        &mut self,
1457        database: Option<ast::NameId>,
1458        table: ast::NameId,
1459        action: &ast::AlterAction,
1460    ) -> Result<Directive, ParseError> {
1461        // **An unqualified `ALTER TABLE` searches `temp` before `main`
1462        // (task-2061).** This resolved every unqualified name through
1463        // `resolve_database(None)`, which answers `main` and nothing else, and
1464        // then looked the table up in `main` alone - so
1465        // `CREATE TEMP TABLE t (a, b); ALTER TABLE t ADD COLUMN c` was
1466        // `no such table: t` when nothing called `t` was in `main`, and altered
1467        // `main.t` when something was. SQLite searches `temp` first for an
1468        // unqualified name in `ALTER TABLE` exactly as it does in a `SELECT`,
1469        // and `find_table(None, ...)` is already that search - the same one
1470        // every query goes through - so the schema comes back from the table
1471        // that was found rather than being decided before the search.
1472        let written = match database {
1473            // A qualifier still has to name a database that exists, and it
1474            // still restricts the search to that one.
1475            //
1476            // **A database that does not exist reads as a table that does not
1477            // exist.** SQLite answers `no such table: nosuch.t` for
1478            // `ALTER TABLE nosuch.t ...`, and never says "unknown database".
1479            Some(qualifier) => match self.resolve_database(database) {
1480                Ok(found) => Some(self.catalog.database_name(found).to_vec()),
1481                Err(_) => {
1482                    let written = [self.ast.text(qualifier), b".", self.ast.text(table)].concat();
1483                    return Err(no_such_table(&written, Span::default()));
1484                }
1485            },
1486            None => None,
1487        };
1488        let folded = self.ast.folded(table).to_vec();
1489        let Some(target) = self
1490            .catalog
1491            .find_table(written.as_deref(), &folded)
1492            .cloned()
1493        else {
1494            return Err(no_such_table(self.ast.text(table), Span::default()));
1495        };
1496        let index = target.database;
1497        let database_name = self.catalog.database_name(index).to_vec();
1498        if target.kind != crate::catalog_view::TableKind::Table {
1499            return Err(refused(
1500                not_a_table_message(action, &target),
1501                Span::default(),
1502            ));
1503        }
1504        if target.folded.starts_with(b"sqlite_") {
1505            return Err(refused(
1506                format!(
1507                    "table {} may not be altered",
1508                    String::from_utf8_lossy(&target.name)
1509                ),
1510                Span::default(),
1511            ));
1512        }
1513        self.record_write_dependency(index);
1514        let kind = match action {
1515            ast::AlterAction::RenameTo(name) => self.bind_rename_to(*name, &database_name)?,
1516            ast::AlterAction::RenameColumn { from, to } => {
1517                let from_folded = self.ast.folded(*from).to_vec();
1518                let Some(position) = target.column_position(&from_folded) else {
1519                    return Err(no_such_quoted_column(self.ast.text(*from)));
1520                };
1521                // A new name that another column already has is refused after
1522                // the rewrite, as `error in table t after rename: duplicate
1523                // column name: b`, because that is where SQLite finds it.
1524                let stored = target
1525                    .column(position)
1526                    .map(|column| column.name.clone())
1527                    .unwrap_or_default();
1528                let to_quoted = self
1529                    .ast
1530                    .name(*to)
1531                    .is_some_and(|name| name.quote != crate::lexer::QuoteForm::Bare);
1532                AlterKind::RenameColumn {
1533                    from: stored,
1534                    to: self.ast.text(*to).to_vec(),
1535                    to_quoted,
1536                }
1537            }
1538            ast::AlterAction::AddColumn(definition) => {
1539                let risk = self.check_added_column(&target, definition)?;
1540                match definition.deferred_failure {
1541                    Some(reason) => AlterKind::AddColumnFailsAfter {
1542                        message: format!(
1543                            "error in table {} after add column: {reason}",
1544                            String::from_utf8_lossy(&target.name)
1545                        ),
1546                    },
1547                    None => AlterKind::AddColumn {
1548                        start: definition.span.start,
1549                        end: definition.span.end,
1550                        risk,
1551                    },
1552                }
1553            }
1554            ast::AlterAction::DropColumn(name) => {
1555                let folded = self.ast.folded(*name).to_vec();
1556                let Some(position) = target.column_position(&folded) else {
1557                    return Err(no_such_quoted_column(self.ast.text(*name)));
1558                };
1559                self.check_dropped_column(&target, position, self.ast.text(*name))?;
1560                let stored = target
1561                    .column(position)
1562                    .map(|column| column.name.clone())
1563                    .unwrap_or_default();
1564                AlterKind::DropColumn {
1565                    name: stored,
1566                    position,
1567                }
1568            }
1569            other => self.bind_constraint_alter(&target, other)?,
1570        };
1571        Ok(Directive::Alter {
1572            database: index,
1573            table: target.name.clone(),
1574            action: kind,
1575        })
1576    }
1577
1578    /// Binds `RENAME TO`, refusing the names SQLite refuses.
1579    ///
1580    /// A name that starts with `sqlite_` is reserved, and that check comes
1581    /// before the one for a name already in use by a table or an index.
1582    ///
1583    /// @param name - the new name
1584    /// @param database_name - the schema the table is in
1585    fn bind_rename_to(
1586        &self,
1587        name: ast::NameId,
1588        database_name: &[u8],
1589    ) -> Result<AlterKind, ParseError> {
1590        let to = self.ast.text(name).to_vec();
1591        let to_folded = self.ast.folded(name).to_vec();
1592        let written = String::from_utf8_lossy(&to).into_owned();
1593        if to_folded.starts_with(b"sqlite_") {
1594            return Err(refused(
1595                format!("object name reserved for internal use: {written}"),
1596                Span::default(),
1597            ));
1598        }
1599        let taken = self
1600            .catalog
1601            .find_table(Some(database_name), &to_folded)
1602            .is_some()
1603            || self
1604                .catalog
1605                .find_index(Some(database_name), &to_folded)
1606                .is_some();
1607        if taken {
1608            return Err(refused(
1609                format!("there is already another table or index with this name: {written}"),
1610                Span::default(),
1611            ));
1612        }
1613        Ok(AlterKind::RenameTable { to })
1614    }
1615
1616    /// Binds the four constraint forms of `ALTER TABLE`.
1617    ///
1618    /// @param target - the table
1619    /// @param action - `SET NOT NULL`, `DROP NOT NULL`, `ADD CHECK` or `DROP CONSTRAINT`
1620    fn bind_constraint_alter(
1621        &self,
1622        target: &crate::catalog_view::TableInfo,
1623        action: &ast::AlterAction,
1624    ) -> Result<AlterKind, ParseError> {
1625        Ok(match action {
1626            ast::AlterAction::SetNotNull { column, start, end } => {
1627                let (name, position) = self.constrained_column(target, *column)?;
1628                AlterKind::SetNotNull {
1629                    name,
1630                    position,
1631                    start: *start,
1632                    end: *end,
1633                }
1634            }
1635            ast::AlterAction::DropNotNull(column) => {
1636                let (name, position) = self.constrained_column(target, *column)?;
1637                AlterKind::DropNotNull { name, position }
1638            }
1639            ast::AlterAction::AddCheck {
1640                name,
1641                expr,
1642                start,
1643                end,
1644            } => {
1645                self.check_names_resolve(target, *expr)?;
1646                let span = self.ast.expr_span(*expr);
1647                AlterKind::AddCheck {
1648                    name: name.map(|id| self.ast.text(id).to_vec()),
1649                    start: *start,
1650                    end: *end,
1651                    expr_start: span.start,
1652                    expr_end: span.end,
1653                }
1654            }
1655            ast::AlterAction::DropConstraint(name) => AlterKind::DropConstraint {
1656                name: self.ast.text(*name).to_vec(),
1657            },
1658            _ => return Err(unsupported("that ALTER TABLE form", Span::default())),
1659        })
1660    }
1661    /// Checks what `ADD COLUMN` may not add.
1662    ///
1663    /// Every one of these is refused because the existing rows have no value
1664    /// for the new column and cannot be given one: a `PRIMARY KEY` or `UNIQUE`
1665    /// column would need an index built over values that are all the same
1666    /// default, and a `NOT NULL` column with no default would make every
1667    /// existing row violate its own table.
1668    fn check_added_column(
1669        &self,
1670        table: &crate::catalog_view::TableInfo,
1671        definition: &ast::ColumnDef,
1672    ) -> Result<AddedColumnRisk, ParseError> {
1673        let folded = self.ast.folded(definition.name).to_vec();
1674        if table.column_position(&folded).is_some() {
1675            return Err(refused(
1676                format!(
1677                    "duplicate column name: {}",
1678                    String::from_utf8_lossy(self.ast.text(definition.name))
1679                ),
1680                Span::default(),
1681            ));
1682        }
1683        // SQLite meets the collation and the default while it reads the column
1684        // definition, so these come before every rule below.
1685        self.check_column_constraints(definition)?;
1686        self.check_generated_clauses(definition)?;
1687        let mut not_null = false;
1688        let mut has_default = false;
1689        let mut constant = true;
1690        let mut generated = false;
1691        let mut generated_stored = false;
1692        let mut references = false;
1693        for (_, constraint) in &definition.constraints {
1694            match constraint {
1695                ast::ColumnConstraint::PrimaryKey { .. } => {
1696                    return Err(schema_refused(
1697                        "Cannot add a PRIMARY KEY column",
1698                        Span::default(),
1699                    ))
1700                }
1701                ast::ColumnConstraint::Unique(_) => {
1702                    return Err(schema_refused(
1703                        "Cannot add a UNIQUE column",
1704                        Span::default(),
1705                    ))
1706                }
1707                ast::ColumnConstraint::NotNull(_) => not_null = true,
1708                ast::ColumnConstraint::Default(expr) => {
1709                    // A literal `DEFAULT NULL` is no default at all to SQLite:
1710                    // `NOT NULL DEFAULT NULL` is refused like `NOT NULL`, and a
1711                    // `REFERENCES` column with it is accepted.
1712                    has_default = !self.is_null_literal(*expr);
1713                    if !self.constant_default(*expr) {
1714                        constant = false;
1715                    }
1716                }
1717                ast::ColumnConstraint::References(_) => references = true,
1718                ast::ColumnConstraint::Generated { stored, .. } => {
1719                    generated = true;
1720                    generated_stored = *stored;
1721                }
1722                _ => {}
1723            }
1724        }
1725        // None of the three default rules applies to a generated column, which
1726        // has no default.
1727        Ok(AddedColumnRisk {
1728            references_with_default: !generated && references && has_default,
1729            null_without_default: !generated && not_null && !has_default,
1730            non_constant_default: !generated && !constant && has_default,
1731            generated_stored,
1732        })
1733    }
1734
1735    /// Returns whether a `DEFAULT` is a constant an existing row can be given.
1736    ///
1737    /// SQLite can evaluate a literal, with a sign, while it compiles the
1738    /// statement. It cannot evaluate `CURRENT_TIMESTAMP` and its two relatives,
1739    /// a function, or an expression such as `1 + 1`, and refuses those.
1740    fn constant_default(&self, expr: ast::ExprId) -> bool {
1741        match self.ast.expr(expr) {
1742            Some(ast::Expr::Literal(
1743                ast::Literal::CurrentDate
1744                | ast::Literal::CurrentTime
1745                | ast::Literal::CurrentTimestamp,
1746            )) => false,
1747            Some(ast::Expr::Literal(_)) => true,
1748            // A bare word is a string in a default: `DEFAULT hello`, `DEFAULT "q"`.
1749            Some(ast::Expr::Column {
1750                database: None,
1751                table: None,
1752                ..
1753            }) => true,
1754            Some(ast::Expr::Unary {
1755                op: ast::UnaryOp::Negate | ast::UnaryOp::Identity,
1756                operand,
1757            })
1758            | Some(ast::Expr::Cast { operand, .. }) => self.constant_default(*operand),
1759            _ => false,
1760        }
1761    }
1762
1763    /// Resolves the column an `ALTER COLUMN` names.
1764    ///
1765    /// The message has no quotes around the name, unlike `DROP COLUMN`'s.
1766    ///
1767    /// @param table - the table
1768    /// @param column - the column as written
1769    fn constrained_column(
1770        &self,
1771        table: &crate::catalog_view::TableInfo,
1772        column: ast::NameId,
1773    ) -> Result<(Vec<u8>, u16), ParseError> {
1774        let folded = self.ast.folded(column).to_vec();
1775        let Some(position) = table.column_position(&folded) else {
1776            return Err(crate::bind::no_such_column(
1777                self.ast.text(column),
1778                Span::default(),
1779            ));
1780        };
1781        let stored = table
1782            .column(position)
1783            .map(|found| found.name.clone())
1784            .unwrap_or_default();
1785        Ok((stored, position))
1786    }
1787
1788    /// Refuses a `CHECK` added by `ALTER TABLE` that names a column the table
1789    /// does not have.
1790    ///
1791    /// @param table - the table
1792    /// @param expr - the predicate
1793    fn check_names_resolve(
1794        &self,
1795        table: &crate::catalog_view::TableInfo,
1796        expr: ast::ExprId,
1797    ) -> Result<(), ParseError> {
1798        let mut pending = vec![expr];
1799        while let Some(id) = pending.pop() {
1800            pending.extend(expression_children(self.ast, id));
1801            let Some(ast::Expr::Column {
1802                table: qualifier,
1803                column,
1804                ..
1805            }) = self.ast.expr(id)
1806            else {
1807                continue;
1808            };
1809            let folded = self.ast.folded(*column);
1810            let own = qualifier.is_none_or(|name| self.ast.folded(name) == table.folded.as_slice());
1811            let rowid = matches!(folded, b"rowid" | b"oid" | b"_rowid_");
1812            if own && (rowid || table.column_position(folded).is_some()) {
1813                continue;
1814            }
1815            let written = match qualifier {
1816                Some(name) => [self.ast.text(*name), b".", self.ast.text(*column)].concat(),
1817                None => self.ast.text(*column).to_vec(),
1818            };
1819            return Err(crate::bind::no_such_column(
1820                &written,
1821                self.ast.expr_span(id),
1822            ));
1823        }
1824        Ok(())
1825    }
1826
1827    /// Returns whether an expression is the literal `NULL`.
1828    fn is_null_literal(&self, expr: ast::ExprId) -> bool {
1829        matches!(
1830            self.ast.expr(expr),
1831            Some(ast::Expr::Literal(ast::Literal::Null))
1832        )
1833    }
1834
1835    /// Checks what `DROP COLUMN` may not drop.
1836    ///
1837    /// SQLite refuses three things before it changes anything: a column that is
1838    /// part of the primary key, a column declared `UNIQUE` in its own
1839    /// definition, and the only column of a table. Everything else that would
1840    /// break (an index, a `CHECK`, a generated column, a view, a trigger) is
1841    /// found after the change is made, by `ALTER TABLE` re-reading the schema.
1842    ///
1843    /// **Only a column level `UNIQUE` is refused here.** A column named by a
1844    /// table level `UNIQUE (a, b)`, or by `CREATE UNIQUE INDEX`, is dropped as
1845    /// far as this check goes, and the index or the table is what fails.
1846    ///
1847    /// @param table - the table
1848    /// @param position - the declared position of the column
1849    /// @param written - the column's name as the statement wrote it, which is
1850    ///   the spelling SQLite puts in its message
1851    fn check_dropped_column(
1852        &self,
1853        table: &crate::catalog_view::TableInfo,
1854        position: u16,
1855        written: &[u8],
1856    ) -> Result<(), ParseError> {
1857        let named = String::from_utf8_lossy(written).into_owned();
1858        let in_primary_key = table.rowid_alias == Some(position)
1859            || table
1860                .column(position)
1861                .is_some_and(|column| column.primary_key_position.is_some());
1862        if in_primary_key {
1863            return Err(refused(
1864                format!("cannot drop PRIMARY KEY column: \"{named}\""),
1865                Span::default(),
1866            ));
1867        }
1868        if declared_unique(&table.create_sql, usize::from(position)) {
1869            return Err(refused(
1870                format!("cannot drop UNIQUE column: \"{named}\""),
1871                Span::default(),
1872            ));
1873        }
1874        if table.columns.len() <= 1 {
1875            return Err(refused(
1876                format!("cannot drop column \"{named}\": no other columns exist"),
1877                Span::default(),
1878            ));
1879        }
1880        Ok(())
1881    }
1882
1883    /// Binds a `REINDEX`.
1884    ///
1885    /// The name is a collation, a table or an index, and SQLite works out which
1886    /// from what it finds - so the resolution order is the same here. A bare
1887    /// `REINDEX` rebuilds everything, which is the form that matters: it is what
1888    /// a person runs after a collation's definition has changed underneath an
1889    /// index that was built with the old one.
1890    fn bind_reindex(
1891        &mut self,
1892        database: Option<ast::NameId>,
1893        name: Option<ast::NameId>,
1894    ) -> Result<Directive, ParseError> {
1895        let index = self.resolve_database(database)?;
1896        self.record_write_dependency(index);
1897        // **An unqualified name means every database**, which is SQLite's
1898        // `sqlite3Reindex`: a bare `REINDEX` and a collation rebuild the
1899        // indexes of every database, and a table or index name is looked for
1900        // in all of them. Reading only `main` made `REINDEX ix` on a temporary
1901        // table's index "unable to identify the object to be reindexed".
1902        let schema_name = database.map(|_| self.catalog.database_name(index).to_vec());
1903        let qualified = database.is_some();
1904        let every_index = |catalog: &dyn CatalogView| -> Vec<Vec<u8>> {
1905            let tables = if qualified {
1906                catalog.tables_of(index)
1907            } else {
1908                catalog.every_table()
1909            };
1910            tables
1911                .into_iter()
1912                .flat_map(|table| table.indexes.iter())
1913                .map(|entry| entry.name.clone())
1914                .filter(|name| !name.is_empty())
1915                .collect()
1916        };
1917        let Some(name) = name else {
1918            return Ok(Directive::Reindex {
1919                database: index,
1920                indexes: every_index(self.catalog),
1921            });
1922        };
1923        let folded = self.ast.folded(name).to_vec();
1924        if let Some(table) = self.catalog.find_table(schema_name.as_deref(), &folded) {
1925            return Ok(Directive::Reindex {
1926                database: index,
1927                indexes: table
1928                    .indexes
1929                    .iter()
1930                    .map(|entry| entry.name.clone())
1931                    .collect(),
1932            });
1933        }
1934        if let Some((_, entry)) = self.catalog.find_index(schema_name.as_deref(), &folded) {
1935            return Ok(Directive::Reindex {
1936                database: index,
1937                indexes: vec![entry.name.clone()],
1938            });
1939        }
1940        // A collation name rebuilds every index ordered by it. An unknown name
1941        // is an error, and SQLite reports it against the collation because that
1942        // is the last thing it tried.
1943        if Collation::from_name(core::str::from_utf8(&folded).unwrap_or("")).is_some() {
1944            let wanted = folded.clone();
1945            let tables = if qualified {
1946                self.catalog.tables_of(index)
1947            } else {
1948                self.catalog.every_table()
1949            };
1950            let indexes = tables
1951                .into_iter()
1952                .flat_map(|table| table.indexes.iter())
1953                .filter(|entry| {
1954                    entry
1955                        .columns
1956                        .iter()
1957                        .any(|key| key.collation.eq_ignore_ascii_case(&wanted))
1958                })
1959                .map(|entry| entry.name.clone())
1960                .collect();
1961            return Ok(Directive::Reindex {
1962                database: index,
1963                indexes,
1964            });
1965        }
1966        Err(no_such_collation_sequence(
1967            self.ast.text(name),
1968            Span::default(),
1969        ))
1970    }
1971
1972    /// Binds a `VACUUM`.
1973    fn bind_vacuum(
1974        &mut self,
1975        database: Option<ast::NameId>,
1976        into: Option<ast::ExprId>,
1977    ) -> Result<Directive, ParseError> {
1978        let literal = into.and_then(|expr| match self.ast.expr(expr) {
1979            Some(ast::Expr::Literal(ast::Literal::String(text))) => Some(text.clone()),
1980            _ => None,
1981        });
1982        // Anything but a string literal is an expression SQLite evaluates when
1983        // the statement runs, so its text travels with the directive.
1984        let into_sql = match (into, &literal) {
1985            (Some(expr), None) => {
1986                let span = self.ast.expr_span(expr);
1987                let written = self
1988                    .source
1989                    .get(span.start as usize..span.end as usize)
1990                    .ok_or_else(|| refused("the file name could not be read", span))?;
1991                Some(String::from_utf8_lossy(written).into_owned())
1992            }
1993            _ => None,
1994        };
1995        let index = self.resolve_database(database)?;
1996        self.record_write_dependency(index);
1997        Ok(Directive::Vacuum {
1998            database: index,
1999            into: literal,
2000            into_sql,
2001        })
2002    }
2003
2004    /// Binds an `ATTACH`.
2005    ///
2006    /// Every operand is a literal, the `KEY` included. SQLite evaluates them, and every other
2007    /// value they could produce is a file name computed at run time - a
2008    /// statement that decides which database to open from arithmetic is not a
2009    /// shape worth supporting before it is asked for, and it is one an
2010    /// authorizer could not check.
2011    pub(crate) fn bind_attach(
2012        &mut self,
2013        file: ast::ExprId,
2014        schema: ast::ExprId,
2015        key: Option<ast::ExprId>,
2016    ) -> Result<Directive, ParseError> {
2017        // SQLCipher's documentation writes a raw key as `KEY "x'...'"`, which
2018        // the grammar reads as a double quoted name, so a name is read as its
2019        // text the way `literal_or_name` reads a schema name.
2020        let key = match key.map(|expr| self.ast.expr(expr)) {
2021            None => None,
2022            Some(Some(ast::Expr::Literal(ast::Literal::String(text)))) => Some(text.clone()),
2023            Some(Some(ast::Expr::Column {
2024                table: None,
2025                column,
2026                ..
2027            })) => Some(self.ast.text(*column).to_vec()),
2028            Some(_) => {
2029                return Err(unsupported(
2030                    "an ATTACH KEY that is not a string literal",
2031                    Span::default(),
2032                ))
2033            }
2034        };
2035        Ok(Directive::Attach {
2036            file: self.literal_path(file, "ATTACH with a file name that is not a literal")?,
2037            schema: self.literal_or_name(schema)?,
2038            key,
2039        })
2040    }
2041
2042    /// Binds a `DETACH`.
2043    pub(crate) fn bind_detach(&mut self, schema: ast::ExprId) -> Result<Directive, ParseError> {
2044        Ok(Directive::Detach {
2045            schema: self.literal_or_name(schema)?,
2046        })
2047    }
2048
2049    /// Reads a name written either as a word or as a string.
2050    ///
2051    /// `ATTACH 'file.db' AS aux` and `ATTACH 'file.db' AS 'aux'` name the same
2052    /// schema. The grammar parses that position as an expression, so a bare
2053    /// word arrives as a reference to a column that does not exist - and what
2054    /// the statement meant is the word.
2055    fn literal_or_name(&mut self, expr: ast::ExprId) -> Result<Vec<u8>, ParseError> {
2056        match self.ast.expr(expr) {
2057            Some(ast::Expr::Literal(ast::Literal::String(text))) => Ok(text.clone()),
2058            Some(ast::Expr::Column {
2059                table: None,
2060                column,
2061                ..
2062            }) => Ok(self.ast.text(*column).to_vec()),
2063            _ => Err(unsupported(
2064                "a schema name that is not a word or a string",
2065                Span::default(),
2066            )),
2067        }
2068    }
2069
2070    /// Reads the file name a `VACUUM INTO` or an `ATTACH` was given.
2071    ///
2072    /// A literal only. SQLite evaluates the expression, but every other value
2073    /// it could produce is a file name computed at run time, and a statement
2074    /// that decides which file to open or where to write a copy of the
2075    /// database from arithmetic is not a shape worth supporting before it is
2076    /// asked for.
2077    ///
2078    /// @param expr - the file name operand
2079    /// @param refused - the construct the refusal names when it is not one
2080    fn literal_path(
2081        &mut self,
2082        expr: ast::ExprId,
2083        refused: &'static str,
2084    ) -> Result<Vec<u8>, ParseError> {
2085        match self.ast.expr(expr) {
2086            Some(ast::Expr::Literal(ast::Literal::String(text))) => Ok(text.clone()),
2087            _ => Err(unsupported(refused, Span::default())),
2088        }
2089    }
2090
2091    /// Binds a `CREATE VIEW`.
2092    ///
2093    /// The body is not resolved here: SQLite checks only the syntax of a view
2094    /// when it is created, and finds a missing table or column when the view is
2095    /// read.
2096    fn bind_create_view(
2097        &mut self,
2098        temporary: bool,
2099        if_not_exists: bool,
2100        database: Option<ast::NameId>,
2101        name: ast::NameId,
2102        _columns: &[ast::NameId],
2103        select: ast::SelectId,
2104    ) -> Result<Directive, ParseError> {
2105        let temp = self.temporary_database(temporary, database, false)?;
2106        let index = match temp {
2107            Some(index) => index,
2108            None => self.resolve_database(database)?,
2109        };
2110        let written = self.ast.text(name).to_vec();
2111        if written.to_ascii_lowercase().starts_with(b"sqlite_") {
2112            return Err(refused(
2113                format!(
2114                    "object name reserved for internal use: {}",
2115                    String::from_utf8_lossy(&written)
2116                ),
2117                Span::default(),
2118            ));
2119        }
2120        let folded = self.ast.folded(name).to_vec();
2121        let database_name = self.catalog.database_name(index).to_vec();
2122        let exists = self
2123            .catalog
2124            .find_table(Some(database_name.as_slice()), &folded)
2125            .is_some();
2126        if exists && !if_not_exists {
2127            return Err(self.already_exists(&database_name, &folded, name));
2128        }
2129        if !exists {
2130            self.refuse_index_namesake(&database_name, &folded, &written)?;
2131            // **The body is not resolved.** SQLite stores a view whose query
2132            // names a table or a column that does not exist, or that reads the
2133            // view itself, and reports it when the view is read; so does a
2134            // column list of the wrong width. Only a parameter is refused here.
2135            self.refuse_view_parameters()?;
2136            if temp.is_none() && !database_name.eq_ignore_ascii_case(b"temp") {
2137                self.refuse_view_in_another_database(&written, &database_name)?;
2138            }
2139        }
2140        let _ = select;
2141        self.record_write_dependency(index);
2142        Ok(Directive::CreateView {
2143            if_not_exists,
2144            database: index,
2145            name: written,
2146            name_offset: self.name_offset(name),
2147            exists,
2148        })
2149    }
2150
2151    /// Refuses a view that is not temporary and names a table of another database.
2152    ///
2153    /// SQLite checks the names the view's query is written with: one qualified
2154    /// with a database other than the view's own is `view v cannot reference
2155    /// objects in database aux`. A temporary view may name any database.
2156    ///
2157    /// @param view - the view's name as written
2158    /// @param home - the database the view is created in
2159    fn refuse_view_in_another_database(&self, view: &[u8], home: &[u8]) -> Result<(), ParseError> {
2160        for index in 0..self.ast.from_term_count() {
2161            let Some(term) = self.ast.from_term(ast::FromTermId(index as u32)) else {
2162                continue;
2163            };
2164            let ast::FromSource::Table {
2165                database: Some(qualifier),
2166                ..
2167            } = &term.source
2168            else {
2169                continue;
2170            };
2171            if self.ast.text(*qualifier).eq_ignore_ascii_case(home) {
2172                continue;
2173            }
2174            return Err(refused(
2175                format!(
2176                    "view {} cannot reference objects in database {}",
2177                    String::from_utf8_lossy(view),
2178                    String::from_utf8_lossy(self.ast.text(*qualifier))
2179                ),
2180                Span::default(),
2181            ));
2182        }
2183        Ok(())
2184    }
2185
2186    /// Refuses the two places `AUTOINCREMENT` may not be written.
2187    ///
2188    /// It counts the rowid the table has handed out, so it needs a rowid to
2189    /// count: only an `INTEGER PRIMARY KEY` column, and never on a table that
2190    /// has no rowid at all. Both messages are the reference's own, because an
2191    /// application that reads them is reading SQLite's.
2192    fn check_autoincrement(
2193        &mut self,
2194        columns: &[ast::ColumnDef],
2195        without_rowid: bool,
2196    ) -> Result<(), ParseError> {
2197        for column in columns {
2198            let declared = column.declared_type.clone().unwrap_or_default();
2199            for (_, constraint) in &column.constraints {
2200                let ast::ColumnConstraint::PrimaryKey {
2201                    autoincrement: true,
2202                    order,
2203                    ..
2204                } = constraint
2205                else {
2206                    continue;
2207                };
2208                if without_rowid {
2209                    return Err(refused(
2210                        "AUTOINCREMENT not allowed on WITHOUT ROWID tables",
2211                        Span::default(),
2212                    ));
2213                }
2214                // A `DESC` key is not the rowid alias, so it is not allowed
2215                // either.
2216                if !declared.eq_ignore_ascii_case(b"integer")
2217                    || *order == ast::SortOrder::Descending
2218                {
2219                    return Err(refused(
2220                        "AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY",
2221                        Span::default(),
2222                    ));
2223                }
2224            }
2225        }
2226        Ok(())
2227    }
2228
2229    /// Refuses `AUTOINCREMENT` written inside a table level `PRIMARY KEY` unless
2230    /// the key is one ascending INTEGER column of a rowid table.
2231    ///
2232    /// @param columns - the table's columns
2233    /// @param constraints - the table's constraints
2234    /// @param without_rowid - whether `WITHOUT ROWID` was written
2235    fn check_table_autoincrement(
2236        &self,
2237        columns: &[ast::ColumnDef],
2238        constraints: &[(Option<ast::NameId>, ast::TableConstraint)],
2239        without_rowid: bool,
2240    ) -> Result<(), ParseError> {
2241        for (_, constraint) in constraints {
2242            let ast::TableConstraint::PrimaryKey {
2243                columns: keys,
2244                autoincrement: true,
2245                ..
2246            } = constraint
2247            else {
2248                continue;
2249            };
2250            if without_rowid {
2251                return Err(refused(
2252                    "AUTOINCREMENT not allowed on WITHOUT ROWID tables",
2253                    Span::default(),
2254                ));
2255            }
2256            // SQLite looks through a `COLLATE` and ignores the term's `DESC`:
2257            // `PRIMARY KEY(a DESC AUTOINCREMENT)` is still the rowid alias.
2258            let single = match keys.as_slice() {
2259                [key] => {
2260                    let named = match self.ast.expr(key.expr) {
2261                        Some(ast::Expr::Collate { operand, .. }) => self.ast.expr(*operand),
2262                        other => other,
2263                    };
2264                    match named {
2265                        Some(ast::Expr::Column {
2266                            table: None,
2267                            column,
2268                            ..
2269                        }) => Some(self.ast.folded(*column)),
2270                        _ => None,
2271                    }
2272                }
2273                _ => None,
2274            };
2275            let integer = single.is_some_and(|folded| {
2276                columns.iter().any(|column| {
2277                    self.ast.folded(column.name) == folded
2278                        && column
2279                            .declared_type
2280                            .as_deref()
2281                            .is_some_and(|declared| declared.eq_ignore_ascii_case(b"integer"))
2282                })
2283            });
2284            if !integer {
2285                return Err(refused(
2286                    "AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY",
2287                    Span::default(),
2288                ));
2289            }
2290        }
2291        Ok(())
2292    }
2293
2294    /// Binds a `CREATE TRIGGER`.
2295    ///
2296    /// The body is bound here, against the table the trigger is attached to, so
2297    /// a trigger that reads a column that does not exist is refused when it is
2298    /// written rather than the first time somebody writes the table. SQLite
2299    /// makes the same promise, and the alternative is a schema that loads and
2300    /// then fails on an unrelated INSERT.
2301    fn bind_create_trigger(
2302        &mut self,
2303        parts: CreateTriggerParts<'_>,
2304    ) -> Result<Directive, ParseError> {
2305        let temp = self.temporary_database(parts.temporary, parts.database, true)?;
2306        // `for_each_row` records whether the words were written, not whether
2307        // the trigger is one: SQLite has only row triggers, an omitted clause
2308        // means FOR EACH ROW, and FOR EACH STATEMENT is a syntax error in the
2309        // parser. There is nothing to refuse here.
2310        let _ = parts.for_each_row;
2311        let index = match temp {
2312            Some(index) => index,
2313            None => self.resolve_database(parts.database)?,
2314        };
2315        let written = self.ast.text(parts.name).to_vec();
2316        let folded = self.ast.folded(parts.name).to_vec();
2317        let database_name = self.catalog.database_name(index).to_vec();
2318        let table_folded = self.ast.folded(parts.table).to_vec();
2319        // A trigger created in a named database fires for a table in that
2320        // database. A temporary one fires for whatever the name finds, which
2321        // is the whole point of `CREATE TEMP TRIGGER ... ON t`: the trigger is
2322        // the connection's and the table is everybody's. `ON main.t` names the
2323        // database: a temporary trigger may name any, and any other trigger
2324        // only its own, which is SQLite's `sqlite3FixSrcList`.
2325        let named = match parts.table_database {
2326            Some(id) => {
2327                let at = self.resolve_database(Some(id))?;
2328                if temp.is_none() && at != index {
2329                    return Err(refused(
2330                        format!(
2331                            "trigger {} cannot reference objects in database {}",
2332                            String::from_utf8_lossy(&written),
2333                            String::from_utf8_lossy(self.ast.text(id))
2334                        ),
2335                        Span::default(),
2336                    ));
2337                }
2338                Some(self.catalog.database_name(at).to_vec())
2339            }
2340            None => None,
2341        };
2342        let scope = match &named {
2343            Some(name) => Some(name.as_slice()),
2344            None => temp.map_or(Some(database_name.as_slice()), |_| None),
2345        };
2346        let Some(target) = self.catalog.find_table(scope, &table_folded).cloned() else {
2347            // SQLite names the schema the table was looked for in, except for a
2348            // temporary trigger, which looks in all of them.
2349            let missing = match scope {
2350                Some(schema) => [schema, b".", self.ast.text(parts.table)].concat(),
2351                None => self.ast.text(parts.table).to_vec(),
2352            };
2353            return Err(crate::bind::no_such_table(&missing, Span::default()));
2354        };
2355        // After the table is found, as SQLite does: `CREATE TRIGGER sqlite_x ... ON missing` is
2356        // a missing table and not a reserved name.
2357        if written.to_ascii_lowercase().starts_with(b"sqlite_") {
2358            return Err(refused(
2359                format!(
2360                    "object name reserved for internal use: {}",
2361                    String::from_utf8_lossy(&written)
2362                ),
2363                Span::default(),
2364            ));
2365        }
2366        let exists = self
2367            .catalog
2368            .find_trigger(Some(database_name.as_slice()), &folded)
2369            .is_some();
2370        if exists && !parts.if_not_exists {
2371            return Err(refused(
2372                format!(
2373                    "trigger {} already exists",
2374                    String::from_utf8_lossy(&written)
2375                ),
2376                Span::default(),
2377            ));
2378        }
2379        let instead_of = parts.time == Some(ast::TriggerTime::InsteadOf);
2380        match target.kind {
2381            TableKind::View if !instead_of => {
2382                return Err(refused(
2383                    format!(
2384                        "cannot create {} trigger on view: {}",
2385                        if parts.time == Some(ast::TriggerTime::After) {
2386                            "AFTER"
2387                        } else {
2388                            "BEFORE"
2389                        },
2390                        String::from_utf8_lossy(&target.name)
2391                    ),
2392                    Span::default(),
2393                ));
2394            }
2395            TableKind::Table if instead_of => {
2396                return Err(refused(
2397                    format!(
2398                        "cannot create INSTEAD OF trigger on table: {}",
2399                        String::from_utf8_lossy(&target.name)
2400                    ),
2401                    Span::default(),
2402                ));
2403            }
2404            TableKind::Virtual | TableKind::Subquery => {
2405                return Err(unsupported("a trigger on that object", Span::default()));
2406            }
2407            _ => {}
2408        }
2409        // `UPDATE OF a, b` is deliberately *not* checked against the table's
2410        // columns. The pinned build accepts `UPDATE OF nosuchcolumn` and simply
2411        // never fires the trigger, and refusing it here would make inillucent's
2412        // language smaller than the reference's - a schema SQLite wrote that
2413        // inillucent could not load.
2414        // The body is deliberately *not* bound here. SQLite stores a trigger
2415        // whose body names a column that does not exist and reports it on the
2416        // first write that fires it - measured against the pinned build, which
2417        // accepts both `UPDATE OF nosuchcolumn` and a body reading a column the
2418        // table has not got. Refusing either here would leave inillucent unable to
2419        // load a schema SQLite had written.
2420        if temp.is_none() {
2421            self.refuse_qualified_trigger_targets(parts.body)?;
2422        }
2423        let _ = (parts.time, parts.when, parts.body);
2424        self.record_write_dependency(index);
2425        Ok(Directive::CreateTrigger {
2426            database: index,
2427            name: written,
2428            name_offset: self.name_offset(parts.name),
2429            table: target.name.clone(),
2430            exists,
2431        })
2432    }
2433
2434    /// Finds the table a `CREATE INDEX` is on, and the schema the index goes in.
2435    ///
2436    /// **An unqualified index goes where its table is.** SQLite looks the
2437    /// table up in the usual order, `temp` first, and creates the index in
2438    /// the schema it found the table in. Taking an unqualified index to mean
2439    /// `main` made `CREATE TEMP TABLE t(a); CREATE INDEX i ON t(a)` report
2440    /// "no such table: t". A table that is not there is reported with the
2441    /// schema it was looked for in, which is `main` when none was written.
2442    ///
2443    /// @param database - the schema the statement wrote, when it wrote one
2444    /// @param table - the table's name
2445    fn index_target(
2446        &self,
2447        database: Option<ast::NameId>,
2448        table: ast::NameId,
2449    ) -> Result<(usize, Vec<u8>, crate::catalog_view::TableInfo), ParseError> {
2450        let table_folded = self.ast.folded(table).to_vec();
2451        let index = match database {
2452            Some(_) => self.resolve_database(database)?,
2453            None => match self.catalog.find_table(None, &table_folded) {
2454                Some(found) => found.database,
2455                None => return Err(self.index_without_table(0, &table_folded, table)),
2456            },
2457        };
2458        let database_name = self.catalog.database_name(index).to_vec();
2459        let Some(target) = self
2460            .catalog
2461            .find_table(Some(database_name.as_slice()), &table_folded)
2462            .cloned()
2463        else {
2464            return Err(self.index_without_table(index, &table_folded, table));
2465        };
2466        Ok((index, database_name, target))
2467    }
2468    /// Binds a `CREATE INDEX`.
2469    ///
2470    /// @param spec - what the statement named
2471    fn bind_create_index(&mut self, spec: &CreateIndexSpec<'_>) -> Result<Directive, ParseError> {
2472        let CreateIndexSpec {
2473            database,
2474            name,
2475            table,
2476            using,
2477            columns,
2478            settings,
2479            ..
2480        } = *spec;
2481        refuse_nulls_order(columns)?;
2482        let unique = spec.unique == Uniqueness::Unique;
2483        let if_not_exists = spec.if_not_exists == IfNotExists::Skip;
2484        // **A `WHERE` is carried in the statement text, not in this
2485        // directive.** The engine re-parses the canonical SQL it stores -
2486        // `index_from_create_sql` already puts the predicate on
2487        // `IndexInfo::partial_sql` - so a field here would be a second copy to
2488        // keep in step. A predicate that names a column the table has not got
2489        // is refused when the index is built, by the query that fills it.
2490        // Only one module can back an index, and naming another is refused here
2491        // rather than accepted and ignored - an index that silently was not the
2492        // structure it asked for is the shape of wrong answer this ticket keeps
2493        // finding.
2494        let using = match using {
2495            None => None,
2496            Some(named) => {
2497                let folded = self.ast.folded(named).to_vec();
2498                // Two structures, and both are real: `inillucent_hnsw` is the
2499                // graph the retrieval engine builds, and `ivfflat` is the
2500                // inverted file pgvector's other index type is - k-means
2501                // centroids and a list per centroid, probed `probes` deep.
2502                // Anything else is refused rather than accepted and ignored:
2503                // an index that silently was not the structure it asked for is
2504                // the shape of wrong answer this ticket keeps finding.
2505                if folded != b"inillucent_hnsw" && folded != b"ivfflat" {
2506                    return Err(unsupported(
2507                        "an index USING a module other than inillucent_hnsw or ivfflat",
2508                        Span::default(),
2509                    ));
2510                }
2511                Some(folded)
2512            }
2513        };
2514        let parsed_settings = index_settings(&using, settings)?;
2515        let (index, database_name, target) = self.index_target(database, table)?;
2516        self.refuse_unindexable(&target)?;
2517        let written = self.ast.text(name).to_vec();
2518        if written.to_ascii_lowercase().starts_with(b"sqlite_") {
2519            return Err(refused(
2520                format!(
2521                    "object name reserved for internal use: {}",
2522                    String::from_utf8_lossy(&written)
2523                ),
2524                Span::default(),
2525            ));
2526        }
2527        let folded = self.ast.folded(name).to_vec();
2528        let exists = self.check_new_index_name(&database_name, &folded, &written, if_not_exists)?;
2529        self.check_index_declarations(&target, columns, spec.filter)?;
2530        let keys = self.index_key_columns(&target, columns)?;
2531        self.record_write_dependency(index);
2532        Ok(Directive::CreateIndex {
2533            unique,
2534            if_not_exists,
2535            database: index,
2536            name: written,
2537            name_offset: self.name_offset(name),
2538            table: target.name.clone(),
2539            table_root: target.root,
2540            using,
2541            columns: keys,
2542            settings: parsed_settings,
2543            exists,
2544        })
2545    }
2546
2547    /// Describes each key of a `CREATE INDEX` for the engine.
2548    ///
2549    /// @param target - the table the index is over
2550    /// @param columns - the indexed columns, in key order
2551    fn index_key_columns(
2552        &self,
2553        target: &crate::catalog_view::TableInfo,
2554        columns: &[ast::IndexedColumn],
2555    ) -> Result<Vec<IndexKeyColumn>, ParseError> {
2556        let mut keys = Vec::with_capacity(columns.len());
2557        for column in columns {
2558            // `CREATE INDEX x ON t(b COLLATE NOCASE DESC)` parses the collation
2559            // into the *expression*, because that is where the grammar puts a
2560            // `COLLATE` that follows a value. It is still an index on a bare
2561            // column, and treating it as one is the difference between
2562            // supporting the everyday form and refusing it as an expression.
2563            let (expr, written_collation) = match self.ast.expr(column.expr) {
2564                Some(ast::Expr::Collate { operand, collation }) => {
2565                    (self.ast.expr(*operand), Some(*collation))
2566                }
2567                other => (other, column.collation),
2568            };
2569            // A key that is not a bare column is an expression, and is carried
2570            // as the source text the engine re-parses. Its collation is BINARY
2571            // unless the statement named one: there is no column to inherit
2572            // from.
2573            let named = match expr {
2574                Some(ast::Expr::Column {
2575                    table: None,
2576                    column: name,
2577                    ..
2578                }) => Some(*name),
2579                _ => None,
2580            };
2581            let Some(name) = named else {
2582                let collation = match written_collation {
2583                    Some(collation) => self.ast.folded(collation).to_vec(),
2584                    None => b"binary".to_vec(),
2585                };
2586                keys.push(IndexKeyColumn {
2587                    column: None,
2588                    expr_sql: Some(self.ast.expr_span(column.expr).slice(self.source).to_vec()),
2589                    collation,
2590                    descending: column.order == ast::SortOrder::Descending,
2591                });
2592                continue;
2593            };
2594            let folded = self.ast.folded(name).to_vec();
2595            let Some(position) = target.column_position(&folded) else {
2596                return Err(crate::bind::no_such_column(
2597                    self.ast.text(name),
2598                    Span::default(),
2599                ));
2600            };
2601            let collation = match written_collation {
2602                Some(collation) => self.ast.folded(collation).to_vec(),
2603                None => target
2604                    .column(position)
2605                    .map(|column| column.collation.clone())
2606                    .unwrap_or_else(|| b"binary".to_vec()),
2607            };
2608            keys.push(IndexKeyColumn {
2609                column: Some(position),
2610                expr_sql: None,
2611                collation,
2612                descending: column.order == ast::SortOrder::Descending,
2613            });
2614        }
2615        Ok(keys)
2616    }
2617
2618    /// Refuses `DROP TABLE` and `DROP VIEW` on the schema table, in SQLite's words.
2619    ///
2620    /// SQLite answers `table sqlite_master may not be dropped` for either statement,
2621    /// with or without `IF EXISTS`, and spells the temporary database's copy
2622    /// `sqlite_temp_master`. All four spellings of the two names are the schema table.
2623    ///
2624    /// @param folded - the name the statement dropped, folded
2625    /// @param database - the database it resolved to
2626    fn refuse_dropping_the_schema_table(
2627        &self,
2628        folded: &[u8],
2629        database: &[u8],
2630    ) -> Result<(), ParseError> {
2631        let main = matches!(folded, b"sqlite_master" | b"sqlite_schema");
2632        let temp = matches!(folded, b"sqlite_temp_master" | b"sqlite_temp_schema");
2633        if !main && !temp {
2634            return Ok(());
2635        }
2636        let in_temp = temp || database.eq_ignore_ascii_case(b"temp");
2637        let said = match in_temp {
2638            true => "table sqlite_temp_master may not be dropped",
2639            false => "table sqlite_master may not be dropped",
2640        };
2641        Err(refused(said, Span::default()))
2642    }
2643
2644    /// Works out which database a `DROP` is about, and how a missing table is named.
2645    ///
2646    /// **An unqualified name is looked for in every database, `temp` first,** which is
2647    /// SQLite's `sqlite3LocateTable` order. Taking it to mean `main` made `DROP TABLE s`
2648    /// "no such table" for a temporary `s`, and dropped `main.s` where SQLite drops the
2649    /// temporary `s` that shadows it.
2650    ///
2651    /// SQLite names a missing table with the schema the statement wrote, and calls an
2652    /// unknown schema in front of a table a missing table: `DROP TABLE nosuch.t` is `no
2653    /// such table: nosuch.t`.
2654    ///
2655    /// @param kind - what the statement drops
2656    /// @param database - the schema as written, when there is one
2657    /// @param written - the object's name as written
2658    /// @param folded - the object's name folded
2659    fn resolve_drop_database(
2660        &self,
2661        kind: ObjectKind,
2662        database: Option<ast::NameId>,
2663        written: &[u8],
2664        folded: &[u8],
2665    ) -> Result<(Vec<u8>, usize), ParseError> {
2666        let qualified = match database {
2667            Some(schema) => [self.ast.text(schema), b".".as_slice(), written].concat(),
2668            None => written.to_vec(),
2669        };
2670        let index = match database {
2671            Some(_) => match self.resolve_database(database) {
2672                Err(_) if kind == ObjectKind::Table => {
2673                    return Err(no_such_table(&qualified, Span::default()))
2674                }
2675                resolved => resolved?,
2676            },
2677            None => self.unqualified_home(kind, folded).unwrap_or(0),
2678        };
2679        Ok((qualified, index))
2680    }
2681
2682    /// Binds `DROP TABLE`, which frees the table's tree and the trees of its indexes.
2683    ///
2684    /// @param if_exists - whether `IF EXISTS` was written
2685    /// @param index - the database the table is in
2686    /// @param database_name - that database's name
2687    /// @param folded - the table's name folded
2688    /// @param written - the table's name as written
2689    /// @param qualified - the name as a failure should print it
2690    fn bind_drop_table(
2691        &self,
2692        if_exists: bool,
2693        index: usize,
2694        database_name: &[u8],
2695        folded: &[u8],
2696        written: Vec<u8>,
2697        qualified: &[u8],
2698    ) -> Result<Directive, ParseError> {
2699        let kind = ObjectKind::Table;
2700        let found = self
2701            .catalog
2702            .find_table(Some(database_name), folded)
2703            .cloned();
2704        let Some(table) = found else {
2705            if if_exists {
2706                return Ok(Directive::Drop {
2707                    kind,
2708                    if_exists,
2709                    database: index,
2710                    name: written,
2711                    root: 0,
2712                    index_roots: Vec::new(),
2713                    exists: false,
2714                });
2715            }
2716            return Err(no_such_table(qualified, Span::default()));
2717        };
2718        self.refuse_dropping_own_table(&table, &written)?;
2719        // A WITHOUT ROWID table's primary key *is* the table's own b-tree, so its entry
2720        // names the same root. Freeing it twice frees a page that is already on the free
2721        // list, which reads back as a malformed database.
2722        let index_roots = table
2723            .indexes
2724            .iter()
2725            .map(|held| held.root)
2726            .filter(|root| *root != 0 && *root != table.root)
2727            .collect();
2728        Ok(Directive::Drop {
2729            kind,
2730            if_exists,
2731            database: index,
2732            name: written,
2733            root: table.root,
2734            index_roots,
2735            exists: true,
2736        })
2737    }
2738
2739    /// Binds a `DROP TABLE` or `DROP INDEX`.
2740    fn bind_drop(
2741        &mut self,
2742        kind: ObjectKind,
2743        if_exists: bool,
2744        database: Option<ast::NameId>,
2745        name: ast::NameId,
2746    ) -> Result<Directive, ParseError> {
2747        let written = self.ast.text(name).to_vec();
2748        let folded = self.ast.folded(name).to_vec();
2749        let (qualified, index) = self.resolve_drop_database(kind, database, &written, &folded)?;
2750        let database_name = self.catalog.database_name(index).to_vec();
2751        self.record_write_dependency(index);
2752        if kind != ObjectKind::Trigger && kind != ObjectKind::Index {
2753            self.refuse_dropping_the_schema_table(&folded, database_name.as_slice())?;
2754        }
2755        if kind == ObjectKind::Trigger {
2756            // A trigger owns no B-tree either, so dropping one is its schema row
2757            // and nothing else.
2758            let exists = self
2759                .catalog
2760                .find_trigger(Some(database_name.as_slice()), &folded)
2761                .is_some();
2762            if !exists && !if_exists {
2763                return Err(refused(
2764                    format!("no such trigger: {}", String::from_utf8_lossy(&written)),
2765                    Span::default(),
2766                ));
2767            }
2768            return Ok(Directive::Drop {
2769                kind,
2770                if_exists,
2771                database: index,
2772                name: written,
2773                root: 0,
2774                index_roots: Vec::new(),
2775                exists,
2776            });
2777        }
2778        if kind == ObjectKind::View {
2779            // A view owns no B-tree, so dropping one is the schema row and
2780            // nothing else - and it must refuse a table, because `DROP VIEW t`
2781            // on a table is an error rather than a drop.
2782            let found = self
2783                .catalog
2784                .find_table(Some(database_name.as_slice()), &folded)
2785                .cloned();
2786            let exists = found
2787                .as_ref()
2788                .is_some_and(|table| table.kind == crate::catalog_view::TableKind::View);
2789            if found.is_some() && !exists {
2790                return Err(refused(
2791                    format!(
2792                        "use DROP TABLE to delete table {}",
2793                        String::from_utf8_lossy(&written)
2794                    ),
2795                    Span::default(),
2796                ));
2797            }
2798            if !exists && !if_exists {
2799                return Err(refused(
2800                    format!("no such view: {}", String::from_utf8_lossy(&written)),
2801                    Span::default(),
2802                ));
2803            }
2804            return Ok(Directive::Drop {
2805                kind,
2806                if_exists,
2807                database: index,
2808                name: written,
2809                root: 0,
2810                index_roots: Vec::new(),
2811                exists,
2812            });
2813        }
2814        if kind == ObjectKind::Table {
2815            return self.bind_drop_table(
2816                if_exists,
2817                index,
2818                &database_name,
2819                &folded,
2820                written,
2821                &qualified,
2822            );
2823        }
2824        self.refuse_dropping_constraint_index(&database_name, &folded)?;
2825        let found = self.find_index_root(index, &folded);
2826        let Some(root) = found else {
2827            if if_exists {
2828                return Ok(Directive::Drop {
2829                    kind,
2830                    if_exists,
2831                    database: index,
2832                    name: written,
2833                    root: 0,
2834                    index_roots: Vec::new(),
2835                    exists: false,
2836                });
2837            }
2838            return Err(refused(
2839                format!("no such index: {}", String::from_utf8_lossy(&written)),
2840                Span::default(),
2841            ));
2842        };
2843        // The index a `UNIQUE` or `PRIMARY KEY` constraint made is part of the table,
2844        // and SQLite refuses to drop it by name.
2845        if folded.starts_with(b"sqlite_autoindex_") {
2846            return Err(refused(
2847                "index associated with UNIQUE or PRIMARY KEY constraint cannot be dropped",
2848                Span::default(),
2849            ));
2850        }
2851        Ok(Directive::Drop {
2852            kind,
2853            if_exists,
2854            database: index,
2855            name: written,
2856            root,
2857            index_roots: Vec::new(),
2858            exists: true,
2859        })
2860    }
2861
2862    /// Returns the database an unqualified object name resolves to.
2863    ///
2864    /// `None` when no database holds an object of that kind by that name, so
2865    /// the caller reports it against `main` as before.
2866    ///
2867    /// @param kind - what sort of object the statement names
2868    /// @param folded - the object's folded name
2869    fn unqualified_home(&self, kind: ObjectKind, folded: &[u8]) -> Option<usize> {
2870        match kind {
2871            ObjectKind::Trigger => self
2872                .catalog
2873                .find_trigger(None, folded)
2874                .map(|(table, _)| table.database),
2875            ObjectKind::Index => self
2876                .catalog
2877                .find_index(None, folded)
2878                .map(|(table, _)| table.database),
2879            _ => self
2880                .catalog
2881                .find_table(None, folded)
2882                .map(|table| table.database),
2883        }
2884    }
2885
2886    /// Binds a `PRAGMA`.
2887    fn bind_pragma(
2888        &mut self,
2889        database: Option<ast::NameId>,
2890        name: ast::NameId,
2891        value: &ast::PragmaValue,
2892    ) -> Result<Directive, ParseError> {
2893        let argument = match value {
2894            ast::PragmaValue::None => None,
2895            ast::PragmaValue::Name(name) => {
2896                Some(PragmaArgument::Name(self.ast.text(*name).to_vec()))
2897            }
2898            ast::PragmaValue::Value(expr) => Some(PragmaArgument::Value(self.bind_expr(*expr)?)),
2899        };
2900        let database = match database {
2901            Some(id) => Some(self.resolve_database(Some(id))?),
2902            None => None,
2903        };
2904        Ok(Directive::Pragma {
2905            database,
2906            name: self.ast.folded(name).to_vec(),
2907            argument,
2908        })
2909    }
2910
2911    /// Returns the temporary database's number when `TEMP` was written.
2912    ///
2913    /// A temporary table's or view's name may be qualified only by `temp`:
2914    /// `CREATE TEMP TABLE main.t` says two different things about where the
2915    /// table goes, and SQLite refuses it rather than picking one, while
2916    /// `CREATE TEMP TABLE temp.t` says the same thing twice and SQLite accepts
2917    /// it. A temporary trigger takes no qualifier at all, which is SQLite's
2918    /// rule in `sqlite3BeginTrigger`.
2919    ///
2920    /// @param temporary - whether `TEMP` was written
2921    /// @param database - the qualifier, when one was written
2922    /// @param trigger - whether the object is a trigger
2923    fn temporary_database(
2924        &self,
2925        temporary: bool,
2926        database: Option<ast::NameId>,
2927        trigger: bool,
2928    ) -> Result<Option<usize>, ParseError> {
2929        if !temporary {
2930            return Ok(None);
2931        }
2932        if let Some(id) = database {
2933            if trigger {
2934                return Err(refused(
2935                    "temporary trigger may not have qualified name",
2936                    Span::default(),
2937                ));
2938            }
2939            if self.ast.folded(id) != b"temp" {
2940                return Err(refused(
2941                    "temporary table name must be unqualified",
2942                    Span::default(),
2943                ));
2944            }
2945        }
2946        self.catalog
2947            .database_index(b"temp")
2948            .map(Some)
2949            .ok_or_else(|| refused("no temporary database", Span::default()))
2950    }
2951
2952    /// Resolves a schema qualifier to an attached database index.
2953    fn resolve_database(&self, database: Option<ast::NameId>) -> Result<usize, ParseError> {
2954        let Some(id) = database else {
2955            return Ok(0);
2956        };
2957        let folded = self.ast.folded(id);
2958        self.catalog.database_index(folded).ok_or_else(|| {
2959            refused(
2960                format!(
2961                    "unknown database {}",
2962                    String::from_utf8_lossy(self.ast.text(id))
2963                ),
2964                // SQLite points at the schema name.
2965                self.ast.name(id).map_or(Span::default(), |name| name.span),
2966            )
2967        })
2968    }
2969
2970    /// Returns the byte an identifier starts at in the statement's source.
2971    ///
2972    /// The canonical `sqlite_schema` text is the statement from its object
2973    /// name onward, which is how `IF NOT EXISTS` and the schema qualifier come
2974    /// to be missing from what SQLite stores. Slicing the source is the only
2975    /// way to reproduce that exactly; rendering the tree back would normalise
2976    /// whitespace and quoting the user chose.
2977    fn name_offset(&self, name: ast::NameId) -> u32 {
2978        self.ast.name(name).map_or(0, |name| name.span.start)
2979    }
2980
2981    /// Returns an index's root page, searching every table of a database.
2982    fn find_index_root(&self, database: usize, folded: &[u8]) -> Option<u32> {
2983        let name = self.catalog.database_name(database).to_vec();
2984        self.catalog
2985            .find_index(Some(name.as_slice()), folded)
2986            .map(|(_, index)| index.root)
2987    }
2988}
2989
2990/// Returns a column name as it can be written back into a `CREATE` statement.
2991///
2992/// A name a query invented - `SELECT 1` reports the column as `1` - is not an
2993/// identifier, so it is quoted the way SQLite quotes it: `CREATE TABLE w("1")`.
2994///
2995/// @param name - the column's name as the query reports it
2996fn quoted_name(name: &[u8]) -> Vec<u8> {
2997    // SQLite quotes a name that is a keyword as well as one that is not a plain
2998    // word, so the stored text of `CREATE TABLE "select" AS ...` can be read back.
2999    let plain = !name.is_empty()
3000        && !name.first().is_some_and(u8::is_ascii_digit)
3001        && name
3002            .iter()
3003            .all(|byte| byte.is_ascii_alphanumeric() || *byte == b'_')
3004        && crate::keyword::lookup(name).is_none();
3005    if plain {
3006        return name.to_vec();
3007    }
3008    let mut out = Vec::with_capacity(name.len().saturating_add(2));
3009    out.push(b'"');
3010    for byte in name {
3011        if *byte == b'"' {
3012            out.push(b'"');
3013        }
3014        out.push(*byte);
3015    }
3016    out.push(b'"');
3017    out
3018}
3019
3020/// Reports whether a result column is a bare column of a derived table, a view
3021/// or a CTE.
3022///
3023/// Such a column's declared type comes from the first arm of the query that
3024/// built it, while its affinity is the one every arm agreed on, and the
3025/// affinity is what `CREATE TABLE ... AS SELECT` writes.
3026///
3027/// @param bound - the bound query
3028/// @param expr - the result column's expression
3029fn reads_derived_column(bound: &crate::bind::BoundSelect, expr: &crate::bind::BoundExpr) -> bool {
3030    let crate::bind::BoundExpr::Column { source, .. } = expr else {
3031        return false;
3032    };
3033    bound
3034        .sources
3035        .iter()
3036        .any(|held| held.id == *source && matches!(held.rows, crate::bind::SourceRows::Subquery(_)))
3037}
3038
3039/// Returns the type name a `CREATE TABLE ... AS SELECT` writes for a column.
3040///
3041/// The affinity's own name, with the leading space, exactly as SQLite writes
3042/// it: BLOB affinity - which is what a column with no declared type has -
3043/// writes nothing at all, so the copy of an untyped column is untyped.
3044///
3045/// A column that is not a bare column of a table has no declared type, and
3046/// takes the affinity of its expression instead: `CAST(1 AS TEXT)` is a `TEXT`
3047/// column, and `1 + 1` has no affinity and so no type.
3048///
3049/// @param declared - the source column's declared type, as written
3050/// @param expression - the affinity of the expression the column is computed by
3051fn affinity_type(
3052    declared: &[u8],
3053    expression: Option<inillucent_value::affinity::Affinity>,
3054) -> &'static [u8] {
3055    let affinity = match (declared.is_empty(), expression) {
3056        (true, Some(held)) => held,
3057        _ => inillucent_value::affinity::for_column(declared),
3058    };
3059    match affinity {
3060        inillucent_value::affinity::Affinity::Blob => b"",
3061        inillucent_value::affinity::Affinity::Text => b" TEXT",
3062        inillucent_value::affinity::Affinity::Integer => b" INT",
3063        inillucent_value::affinity::Affinity::Real => b" REAL",
3064        inillucent_value::affinity::Affinity::Numeric
3065        | inillucent_value::affinity::Affinity::FlexNum => b" NUM",
3066    }
3067}
3068
3069/// Returns the width SQLite counts an identifier as when it decides whether to
3070/// write a `CREATE TABLE ... AS SELECT`'s columns one per line.
3071///
3072/// Its own `identLength`: the name plus the two quotes it might need, plus one
3073/// for each quote inside it that would have to be doubled. The rule that reads
3074/// it is "under fifty, one line", and reproducing both is what makes the stored
3075/// declaration byte-identical rather than merely equivalent.
3076///
3077/// @param name - the identifier
3078fn identifier_width(name: &[u8]) -> usize {
3079    name.len()
3080        .saturating_add(2)
3081        .saturating_add(name.iter().filter(|byte| **byte == b'"').count())
3082}
3083
3084/// The storage parameters `CREATE INDEX ... WITH ( ... )` accepts.
3085///
3086/// One entry per name the vector index understands, with the store option it
3087/// becomes. **A name that is not here is refused rather than ignored**, which is
3088/// the same rule `USING` follows a few lines above and for the same reason: an
3089/// index that quietly was not built the way it was asked to be is a wrong answer
3090/// nobody can see.
3091const INDEX_SETTINGS: [(&str, &str); 10] = [
3092    // The graph's own three, spelled as pgvector spells them.
3093    ("m", "m"),
3094    ("ef_construction", "ef_construction"),
3095    ("ef_search", "ef_search"),
3096    // Whether a query walks the graph (`approximate`, the default for an
3097    // `inillucent_hnsw` index) or compares every vector (`exact`). The store
3098    // validates the value, so `mode = 'fast'` is refused by name.
3099    ("mode", "mode"),
3100    // The distance the index is built for. pgvector puts this in an operator
3101    // class - `USING hnsw (v vector_l2_ops)` - and names it here as well.
3102    ("metric", "metric"),
3103    ("distance", "metric"),
3104    // How many threads the build uses, and how far behind the table the index
3105    // may fall before it is rebuilt.
3106    ("threads", "threads"),
3107    ("compact", "compact"),
3108    // The two an `ivfflat` has: how many centroids it clusters into, and how
3109    // many of those lists a query reads.
3110    ("lists", "lists"),
3111    ("probes", "probes"),
3112];
3113
3114/// Checks `WITH ( ... )` against the structure that will read it.
3115///
3116/// Returns the settings as folded `(name, value)` pairs, in the order written.
3117/// A plain `CREATE INDEX` may not carry any: a b-tree has no parameters, and
3118/// accepting them would mean accepting a setting nothing reads.
3119///
3120/// @param using - the module the index named, when it named one
3121/// @param settings - the raw `name = value` slices
3122fn index_settings(
3123    using: &Option<Vec<u8>>,
3124    settings: &[Vec<u8>],
3125) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ParseError> {
3126    if settings.is_empty() {
3127        return Ok(Vec::new());
3128    }
3129    if using.is_none() {
3130        return Err(unsupported(
3131            "WITH ( ... ) on an index that is not USING a module",
3132            Span::default(),
3133        ));
3134    }
3135    let mut held = Vec::with_capacity(settings.len());
3136    for setting in settings {
3137        let text = String::from_utf8_lossy(setting).to_string();
3138        let Some((name, value)) = text.split_once('=') else {
3139            return Err(refused(
3140                format!("index setting {} is not name = value", text.trim()),
3141                Span::default(),
3142            ));
3143        };
3144        let folded = name.trim().to_ascii_lowercase();
3145        let Some((_, option)) = INDEX_SETTINGS
3146            .iter()
3147            .find(|(known, _)| *known == folded.as_str())
3148        else {
3149            return Err(refused(
3150                format!("no such index setting: {folded}"),
3151                Span::default(),
3152            ));
3153        };
3154        let value = value
3155            .trim()
3156            .trim_matches(|held| held == '\'' || held == '"');
3157        held.push((option.as_bytes().to_vec(), value.as_bytes().to_vec()));
3158    }
3159    Ok(held)
3160}