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            create_sql
1087                .extend_from_slice(affinity_type(&column.declared_type, column.expr.affinity()));
1088        }
1089        create_sql.extend_from_slice(close);
1090        self.record_write_dependency(index);
1091        Ok(Directive::CreateTableAsSelect {
1092            if_not_exists,
1093            database: index,
1094            name: written,
1095            exists,
1096            create_sql,
1097            select_sql,
1098        })
1099    }
1100
1101    /// Returns whether a `CREATE TABLE` declares a primary key anywhere.
1102    fn declares_primary_key(
1103        &self,
1104        columns: &[ast::ColumnDef],
1105        constraints: &[(Option<ast::NameId>, ast::TableConstraint)],
1106    ) -> bool {
1107        let on_column = columns.iter().any(|column| {
1108            column.constraints.iter().any(|(_, constraint)| {
1109                matches!(constraint, ast::ColumnConstraint::PrimaryKey { .. })
1110            })
1111        });
1112        on_column
1113            || constraints.iter().any(|(_, constraint)| {
1114                matches!(constraint, ast::TableConstraint::PrimaryKey { .. })
1115            })
1116    }
1117
1118    /// Checks the rules a generated column has to obey.
1119    ///
1120    /// A generated column may not carry a `DEFAULT` - it has no value of its
1121    /// own to fall back to - may not be part of a rowid table's `PRIMARY KEY`,
1122    /// and may not refer to a column that does not exist or to itself. The
1123    /// cycle check is the one that matters: without it a `CREATE TABLE` that
1124    /// describes one is accepted and every later insert recurses.
1125    fn check_generated(
1126        &self,
1127        columns: &[ast::ColumnDef],
1128        constraints: &[(Option<ast::NameId>, ast::TableConstraint)],
1129    ) -> Result<Option<String>, ParseError> {
1130        let names: Vec<Vec<u8>> = columns
1131            .iter()
1132            .map(|column| self.ast.folded(column.name).to_vec())
1133            .collect();
1134        let mut generated: Vec<(usize, Vec<usize>)> = Vec::new();
1135        for (position, column) in columns.iter().enumerate() {
1136            let Some(expr) = self.check_generated_clauses(column)? else {
1137                continue;
1138            };
1139            let mut reads = Vec::new();
1140            self.expression_names(expr, &mut reads);
1141            let mut resolved = Vec::new();
1142            for name in &reads {
1143                let Some(found) = names.iter().position(|candidate| candidate == name) else {
1144                    return Err(crate::bind::no_such_column(name, Span::default()));
1145                };
1146                resolved.push(found);
1147            }
1148            generated.push((position, resolved));
1149        }
1150        self.check_key_has_no_generated(columns, constraints)?;
1151        // A cycle is anything that never becomes computable: repeat the "every
1152        // dependency is settled" pass until it stops making progress, and if
1153        // anything is left it depends on itself, directly or through others.
1154        let mut settled: Vec<usize> = (0..columns.len())
1155            .filter(|position| !generated.iter().any(|(owner, _)| owner == position))
1156            .collect();
1157        let mut pending = generated;
1158        loop {
1159            let before = pending.len();
1160            let mut still = Vec::new();
1161            for (position, reads) in pending {
1162                if reads.iter().all(|read| settled.contains(read)) {
1163                    settled.push(position);
1164                } else {
1165                    still.push((position, reads));
1166                }
1167            }
1168            pending = still;
1169            if pending.is_empty() || pending.len() == before {
1170                break;
1171            }
1172        }
1173        // SQLite computes the generated columns in passes and, when a pass makes no
1174        // progress, names the last column in declaration order that is still waiting.
1175        // That is the column `pending.last()` holds, because `pending` keeps declaration
1176        // order. Measured against the pinned shell for loops of two and three columns.
1177        if let Some((position, _)) = pending.last() {
1178            let written = columns
1179                .get(*position)
1180                .map(|column| String::from_utf8_lossy(self.ast.text(column.name)).into_owned())
1181                .unwrap_or_default();
1182            return Ok(Some(format!("generated column loop on \"{written}\"")));
1183        }
1184        Ok(None)
1185    }
1186
1187    /// Checks the order of the `DEFAULT`, `AS` and `PRIMARY KEY` clauses of one column.
1188    ///
1189    /// SQLite meets the clauses in the order they are written. `AS` after a
1190    /// `DEFAULT` or after another `AS` is `error in generated column "c"`,
1191    /// `DEFAULT` after `AS` is `cannot use DEFAULT on a generated column`, and a
1192    /// `PRIMARY KEY` on a generated column is refused whichever comes first.
1193    /// Returns the generated expression, when the column has one.
1194    ///
1195    /// @param column - the column definition
1196    pub(crate) fn check_generated_clauses(
1197        &self,
1198        column: &ast::ColumnDef,
1199    ) -> Result<Option<ast::ExprId>, ParseError> {
1200        let mut expr = None;
1201        let mut has_default = false;
1202        let mut in_primary_key = false;
1203        for (_, constraint) in &column.constraints {
1204            match constraint {
1205                ast::ColumnConstraint::Generated {
1206                    expr: body,
1207                    bad_storage,
1208                    ..
1209                } => {
1210                    if has_default || expr.is_some() || *bad_storage {
1211                        return Err(refused(
1212                            format!(
1213                                "error in generated column \"{}\"",
1214                                String::from_utf8_lossy(self.ast.text(column.name))
1215                            ),
1216                            Span::default(),
1217                        ));
1218                    }
1219                    expr = Some(*body);
1220                }
1221                ast::ColumnConstraint::Default(_) => {
1222                    if expr.is_some() {
1223                        return Err(refused(
1224                            "cannot use DEFAULT on a generated column",
1225                            Span::default(),
1226                        ));
1227                    }
1228                    has_default = true;
1229                }
1230                ast::ColumnConstraint::PrimaryKey { .. } => in_primary_key = true,
1231                _ => {}
1232            }
1233        }
1234        if expr.is_some() && in_primary_key {
1235            return Err(refused(
1236                "generated columns cannot be part of the PRIMARY KEY",
1237                Span::default(),
1238            ));
1239        }
1240        Ok(expr)
1241    }
1242
1243    /// Refuses a table level `PRIMARY KEY` that names a generated column.
1244    ///
1245    /// A `UNIQUE` constraint may name one.
1246    ///
1247    /// @param columns - the table's columns
1248    /// @param constraints - the table's constraints
1249    fn check_key_has_no_generated(
1250        &self,
1251        columns: &[ast::ColumnDef],
1252        constraints: &[(Option<ast::NameId>, ast::TableConstraint)],
1253    ) -> Result<(), ParseError> {
1254        for (_, constraint) in constraints {
1255            let ast::TableConstraint::PrimaryKey { columns: keys, .. } = constraint else {
1256                continue;
1257            };
1258            for key in keys {
1259                let Some(ast::Expr::Column { column: named, .. }) = self.ast.expr(key.expr) else {
1260                    continue;
1261                };
1262                let folded = self.ast.folded(*named);
1263                let generated = columns.iter().any(|column| {
1264                    self.ast.folded(column.name) == folded
1265                        && column.constraints.iter().any(|(_, constraint)| {
1266                            matches!(constraint, ast::ColumnConstraint::Generated { .. })
1267                        })
1268                });
1269                if generated {
1270                    return Err(refused(
1271                        "generated columns cannot be part of the PRIMARY KEY",
1272                        Span::default(),
1273                    ));
1274                }
1275            }
1276        }
1277        Ok(())
1278    }
1279
1280    /// Collects the folded column names an expression mentions.
1281    fn expression_names(&self, expr: ast::ExprId, into: &mut Vec<Vec<u8>>) {
1282        let Some(node) = self.ast.expr(expr) else {
1283            return;
1284        };
1285        if let ast::Expr::Column { column, .. } = node {
1286            let name = self.ast.folded(*column).to_vec();
1287            if !into.contains(&name) {
1288                into.push(name);
1289            }
1290        }
1291        for child in expression_children(self.ast, expr) {
1292            self.expression_names(child, into);
1293        }
1294    }
1295
1296    /// Refuses a column whose `COLLATE` names a collation sequence that does not exist.
1297    ///
1298    /// SQLite looks the name up when the table is created, so
1299    /// `CREATE TABLE t(a COLLATE nosuch)` fails with `no such collation sequence:
1300    /// nosuch` and creates nothing. It used to be accepted and fail later, on the
1301    /// first statement that compared the column.
1302    ///
1303    /// @param columns - the column definitions
1304    fn check_column_collations(&self, columns: &[ast::ColumnDef]) -> Result<(), ParseError> {
1305        for column in columns {
1306            for (_, constraint) in &column.constraints {
1307                let ast::ColumnConstraint::Collate(name) = constraint else {
1308                    continue;
1309                };
1310                let written = self.ast.text(*name);
1311                if self.collation_named(written).is_none() {
1312                    return Err(crate::bind::no_such_collation(written, Span::default()));
1313                }
1314            }
1315        }
1316        Ok(())
1317    }
1318
1319    /// Checks the rules a `STRICT` table adds to its column list.
1320    ///
1321    /// Every column must name one of six types, and the check is on the
1322    /// declared text rather than on the affinity it maps to: `VARCHAR(10)` has
1323    /// TEXT affinity and is still refused, because STRICT is about what was
1324    /// written and not about what it means.
1325    ///
1326    /// SQLite names the column with its table, `missing datatype for t.a` and
1327    /// `unknown datatype for t.a: "DATETIME"`, with both as written.
1328    ///
1329    /// @param columns - the column definitions
1330    /// @param table - the table's name as written
1331    fn check_strict(
1332        &self,
1333        columns: &[ast::ColumnDef],
1334        table: ast::NameId,
1335    ) -> Result<(), ParseError> {
1336        let table = String::from_utf8_lossy(self.ast.text(table)).into_owned();
1337        for column in columns {
1338            let Some(declared) = column.declared_type.as_ref() else {
1339                return Err(refused(
1340                    format!(
1341                        "missing datatype for {table}.{}",
1342                        String::from_utf8_lossy(self.ast.text(column.name))
1343                    ),
1344                    Span::default(),
1345                ));
1346            };
1347            let folded = declared.to_ascii_uppercase();
1348            let allowed = matches!(
1349                folded.as_slice(),
1350                b"INT" | b"INTEGER" | b"REAL" | b"TEXT" | b"BLOB" | b"ANY"
1351            );
1352            if !allowed {
1353                return Err(refused(
1354                    format!(
1355                        "unknown datatype for {table}.{}: \"{}\"",
1356                        String::from_utf8_lossy(self.ast.text(column.name)),
1357                        String::from_utf8_lossy(declared)
1358                    ),
1359                    Span::default(),
1360                ));
1361            }
1362        }
1363        Ok(())
1364    }
1365
1366    /// Binds an `ANALYZE`.
1367    ///
1368    /// A bare `ANALYZE` measures everything; one with a name measures that
1369    /// object. SQLite accepts a database name, an index name or a table name in
1370    /// the same position and works out which it is, and so does this: the name
1371    /// is resolved against the tables, then the indexes, and only then refused.
1372    fn bind_analyze(
1373        &mut self,
1374        database: Option<ast::NameId>,
1375        name: Option<ast::NameId>,
1376    ) -> Result<Directive, ParseError> {
1377        let Some(name) = name else {
1378            // A bare `ANALYZE` is every database but `temp`, which is SQLite's
1379            // `sqlite3Analyze`.
1380            let temp = self.catalog.database_index(b"temp");
1381            for index in 0..self.catalog.database_count() {
1382                if Some(index) != temp {
1383                    self.record_write_dependency(index);
1384                }
1385            }
1386            return Ok(Directive::Analyze {
1387                database: 0,
1388                table: None,
1389                every_schema: true,
1390            });
1391        };
1392        let folded = self.ast.folded(name).to_vec();
1393        // **An unqualified name may be a database's.** `ANALYZE aux` measures
1394        // every table in `aux`; resolving the name as a table in `main` first
1395        // made it "no such table: aux".
1396        if database.is_none() {
1397            if let Some(index) = self.catalog.database_index(&folded) {
1398                self.record_write_dependency(index);
1399                return Ok(Directive::Analyze {
1400                    database: index,
1401                    table: None,
1402                    every_schema: false,
1403                });
1404            }
1405        }
1406        // An unqualified table or index is searched for in every database, in
1407        // the usual order; a qualified one only in its own. An index is looked
1408        // for first, as SQLite does, and is passed on by its own name, because
1409        // `ANALYZE ix` measures that index alone.
1410        let schema_name = match database {
1411            Some(_) => {
1412                let index = self.resolve_database(database)?;
1413                Some(self.catalog.database_name(index).to_vec())
1414            }
1415            None => None,
1416        };
1417        let found = self
1418            .catalog
1419            .find_index(schema_name.as_deref(), &folded)
1420            .map(|(table, index)| (table.database, index.name.clone()))
1421            .or_else(|| {
1422                self.catalog
1423                    .find_table(schema_name.as_deref(), &folded)
1424                    .map(|table| (table.database, table.name.clone()))
1425            });
1426        let Some((index, table)) = found else {
1427            return Err(no_such_table(self.ast.text(name), Span::default()));
1428        };
1429        self.record_write_dependency(index);
1430        Ok(Directive::Analyze {
1431            database: index,
1432            table: Some(table),
1433            every_schema: false,
1434        })
1435    }
1436
1437    /// Binds an `ALTER TABLE`.
1438    ///
1439    /// Every refusal SQLite makes is made here, where the catalog is available,
1440    /// rather than half-way through rewriting the schema: a rename that is
1441    /// going to fail must fail before anything has been written.
1442    fn bind_alter(
1443        &mut self,
1444        database: Option<ast::NameId>,
1445        table: ast::NameId,
1446        action: &ast::AlterAction,
1447    ) -> Result<Directive, ParseError> {
1448        // **An unqualified `ALTER TABLE` searches `temp` before `main`
1449        // (task-2061).** This resolved every unqualified name through
1450        // `resolve_database(None)`, which answers `main` and nothing else, and
1451        // then looked the table up in `main` alone - so
1452        // `CREATE TEMP TABLE t (a, b); ALTER TABLE t ADD COLUMN c` was
1453        // `no such table: t` when nothing called `t` was in `main`, and altered
1454        // `main.t` when something was. SQLite searches `temp` first for an
1455        // unqualified name in `ALTER TABLE` exactly as it does in a `SELECT`,
1456        // and `find_table(None, ...)` is already that search - the same one
1457        // every query goes through - so the schema comes back from the table
1458        // that was found rather than being decided before the search.
1459        let written = match database {
1460            // A qualifier still has to name a database that exists, and it
1461            // still restricts the search to that one.
1462            //
1463            // **A database that does not exist reads as a table that does not
1464            // exist.** SQLite answers `no such table: nosuch.t` for
1465            // `ALTER TABLE nosuch.t ...`, and never says "unknown database".
1466            Some(qualifier) => match self.resolve_database(database) {
1467                Ok(found) => Some(self.catalog.database_name(found).to_vec()),
1468                Err(_) => {
1469                    let written = [self.ast.text(qualifier), b".", self.ast.text(table)].concat();
1470                    return Err(no_such_table(&written, Span::default()));
1471                }
1472            },
1473            None => None,
1474        };
1475        let folded = self.ast.folded(table).to_vec();
1476        let Some(target) = self
1477            .catalog
1478            .find_table(written.as_deref(), &folded)
1479            .cloned()
1480        else {
1481            return Err(no_such_table(self.ast.text(table), Span::default()));
1482        };
1483        let index = target.database;
1484        let database_name = self.catalog.database_name(index).to_vec();
1485        if target.kind != crate::catalog_view::TableKind::Table {
1486            return Err(refused(
1487                not_a_table_message(action, &target),
1488                Span::default(),
1489            ));
1490        }
1491        if target.folded.starts_with(b"sqlite_") {
1492            return Err(refused(
1493                format!(
1494                    "table {} may not be altered",
1495                    String::from_utf8_lossy(&target.name)
1496                ),
1497                Span::default(),
1498            ));
1499        }
1500        self.record_write_dependency(index);
1501        let kind = match action {
1502            ast::AlterAction::RenameTo(name) => self.bind_rename_to(*name, &database_name)?,
1503            ast::AlterAction::RenameColumn { from, to } => {
1504                let from_folded = self.ast.folded(*from).to_vec();
1505                let Some(position) = target.column_position(&from_folded) else {
1506                    return Err(no_such_quoted_column(self.ast.text(*from)));
1507                };
1508                // A new name that another column already has is refused after
1509                // the rewrite, as `error in table t after rename: duplicate
1510                // column name: b`, because that is where SQLite finds it.
1511                let stored = target
1512                    .column(position)
1513                    .map(|column| column.name.clone())
1514                    .unwrap_or_default();
1515                let to_quoted = self
1516                    .ast
1517                    .name(*to)
1518                    .is_some_and(|name| name.quote != crate::lexer::QuoteForm::Bare);
1519                AlterKind::RenameColumn {
1520                    from: stored,
1521                    to: self.ast.text(*to).to_vec(),
1522                    to_quoted,
1523                }
1524            }
1525            ast::AlterAction::AddColumn(definition) => {
1526                let risk = self.check_added_column(&target, definition)?;
1527                match definition.deferred_failure {
1528                    Some(reason) => AlterKind::AddColumnFailsAfter {
1529                        message: format!(
1530                            "error in table {} after add column: {reason}",
1531                            String::from_utf8_lossy(&target.name)
1532                        ),
1533                    },
1534                    None => AlterKind::AddColumn {
1535                        start: definition.span.start,
1536                        end: definition.span.end,
1537                        risk,
1538                    },
1539                }
1540            }
1541            ast::AlterAction::DropColumn(name) => {
1542                let folded = self.ast.folded(*name).to_vec();
1543                let Some(position) = target.column_position(&folded) else {
1544                    return Err(no_such_quoted_column(self.ast.text(*name)));
1545                };
1546                self.check_dropped_column(&target, position, self.ast.text(*name))?;
1547                let stored = target
1548                    .column(position)
1549                    .map(|column| column.name.clone())
1550                    .unwrap_or_default();
1551                AlterKind::DropColumn {
1552                    name: stored,
1553                    position,
1554                }
1555            }
1556            other => self.bind_constraint_alter(&target, other)?,
1557        };
1558        Ok(Directive::Alter {
1559            database: index,
1560            table: target.name.clone(),
1561            action: kind,
1562        })
1563    }
1564
1565    /// Binds `RENAME TO`, refusing the names SQLite refuses.
1566    ///
1567    /// A name that starts with `sqlite_` is reserved, and that check comes
1568    /// before the one for a name already in use by a table or an index.
1569    ///
1570    /// @param name - the new name
1571    /// @param database_name - the schema the table is in
1572    fn bind_rename_to(
1573        &self,
1574        name: ast::NameId,
1575        database_name: &[u8],
1576    ) -> Result<AlterKind, ParseError> {
1577        let to = self.ast.text(name).to_vec();
1578        let to_folded = self.ast.folded(name).to_vec();
1579        let written = String::from_utf8_lossy(&to).into_owned();
1580        if to_folded.starts_with(b"sqlite_") {
1581            return Err(refused(
1582                format!("object name reserved for internal use: {written}"),
1583                Span::default(),
1584            ));
1585        }
1586        let taken = self
1587            .catalog
1588            .find_table(Some(database_name), &to_folded)
1589            .is_some()
1590            || self
1591                .catalog
1592                .find_index(Some(database_name), &to_folded)
1593                .is_some();
1594        if taken {
1595            return Err(refused(
1596                format!("there is already another table or index with this name: {written}"),
1597                Span::default(),
1598            ));
1599        }
1600        Ok(AlterKind::RenameTable { to })
1601    }
1602
1603    /// Binds the four constraint forms of `ALTER TABLE`.
1604    ///
1605    /// @param target - the table
1606    /// @param action - `SET NOT NULL`, `DROP NOT NULL`, `ADD CHECK` or `DROP CONSTRAINT`
1607    fn bind_constraint_alter(
1608        &self,
1609        target: &crate::catalog_view::TableInfo,
1610        action: &ast::AlterAction,
1611    ) -> Result<AlterKind, ParseError> {
1612        Ok(match action {
1613            ast::AlterAction::SetNotNull { column, start, end } => {
1614                let (name, position) = self.constrained_column(target, *column)?;
1615                AlterKind::SetNotNull {
1616                    name,
1617                    position,
1618                    start: *start,
1619                    end: *end,
1620                }
1621            }
1622            ast::AlterAction::DropNotNull(column) => {
1623                let (name, position) = self.constrained_column(target, *column)?;
1624                AlterKind::DropNotNull { name, position }
1625            }
1626            ast::AlterAction::AddCheck {
1627                name,
1628                expr,
1629                start,
1630                end,
1631            } => {
1632                self.check_names_resolve(target, *expr)?;
1633                let span = self.ast.expr_span(*expr);
1634                AlterKind::AddCheck {
1635                    name: name.map(|id| self.ast.text(id).to_vec()),
1636                    start: *start,
1637                    end: *end,
1638                    expr_start: span.start,
1639                    expr_end: span.end,
1640                }
1641            }
1642            ast::AlterAction::DropConstraint(name) => AlterKind::DropConstraint {
1643                name: self.ast.text(*name).to_vec(),
1644            },
1645            _ => return Err(unsupported("that ALTER TABLE form", Span::default())),
1646        })
1647    }
1648    /// Checks what `ADD COLUMN` may not add.
1649    ///
1650    /// Every one of these is refused because the existing rows have no value
1651    /// for the new column and cannot be given one: a `PRIMARY KEY` or `UNIQUE`
1652    /// column would need an index built over values that are all the same
1653    /// default, and a `NOT NULL` column with no default would make every
1654    /// existing row violate its own table.
1655    fn check_added_column(
1656        &self,
1657        table: &crate::catalog_view::TableInfo,
1658        definition: &ast::ColumnDef,
1659    ) -> Result<AddedColumnRisk, ParseError> {
1660        let folded = self.ast.folded(definition.name).to_vec();
1661        if table.column_position(&folded).is_some() {
1662            return Err(refused(
1663                format!(
1664                    "duplicate column name: {}",
1665                    String::from_utf8_lossy(self.ast.text(definition.name))
1666                ),
1667                Span::default(),
1668            ));
1669        }
1670        // SQLite meets the collation and the default while it reads the column
1671        // definition, so these come before every rule below.
1672        self.check_column_constraints(definition)?;
1673        self.check_generated_clauses(definition)?;
1674        let mut not_null = false;
1675        let mut has_default = false;
1676        let mut constant = true;
1677        let mut generated = false;
1678        let mut generated_stored = false;
1679        let mut references = false;
1680        for (_, constraint) in &definition.constraints {
1681            match constraint {
1682                ast::ColumnConstraint::PrimaryKey { .. } => {
1683                    return Err(schema_refused(
1684                        "Cannot add a PRIMARY KEY column",
1685                        Span::default(),
1686                    ))
1687                }
1688                ast::ColumnConstraint::Unique(_) => {
1689                    return Err(schema_refused(
1690                        "Cannot add a UNIQUE column",
1691                        Span::default(),
1692                    ))
1693                }
1694                ast::ColumnConstraint::NotNull(_) => not_null = true,
1695                ast::ColumnConstraint::Default(expr) => {
1696                    // A literal `DEFAULT NULL` is no default at all to SQLite:
1697                    // `NOT NULL DEFAULT NULL` is refused like `NOT NULL`, and a
1698                    // `REFERENCES` column with it is accepted.
1699                    has_default = !self.is_null_literal(*expr);
1700                    if !self.constant_default(*expr) {
1701                        constant = false;
1702                    }
1703                }
1704                ast::ColumnConstraint::References(_) => references = true,
1705                ast::ColumnConstraint::Generated { stored, .. } => {
1706                    generated = true;
1707                    generated_stored = *stored;
1708                }
1709                _ => {}
1710            }
1711        }
1712        // None of the three default rules applies to a generated column, which
1713        // has no default.
1714        Ok(AddedColumnRisk {
1715            references_with_default: !generated && references && has_default,
1716            null_without_default: !generated && not_null && !has_default,
1717            non_constant_default: !generated && !constant && has_default,
1718            generated_stored,
1719        })
1720    }
1721
1722    /// Returns whether a `DEFAULT` is a constant an existing row can be given.
1723    ///
1724    /// SQLite can evaluate a literal, with a sign, while it compiles the
1725    /// statement. It cannot evaluate `CURRENT_TIMESTAMP` and its two relatives,
1726    /// a function, or an expression such as `1 + 1`, and refuses those.
1727    fn constant_default(&self, expr: ast::ExprId) -> bool {
1728        match self.ast.expr(expr) {
1729            Some(ast::Expr::Literal(
1730                ast::Literal::CurrentDate
1731                | ast::Literal::CurrentTime
1732                | ast::Literal::CurrentTimestamp,
1733            )) => false,
1734            Some(ast::Expr::Literal(_)) => true,
1735            // A bare word is a string in a default: `DEFAULT hello`, `DEFAULT "q"`.
1736            Some(ast::Expr::Column {
1737                database: None,
1738                table: None,
1739                ..
1740            }) => true,
1741            Some(ast::Expr::Unary {
1742                op: ast::UnaryOp::Negate | ast::UnaryOp::Identity,
1743                operand,
1744            })
1745            | Some(ast::Expr::Cast { operand, .. }) => self.constant_default(*operand),
1746            _ => false,
1747        }
1748    }
1749
1750    /// Resolves the column an `ALTER COLUMN` names.
1751    ///
1752    /// The message has no quotes around the name, unlike `DROP COLUMN`'s.
1753    ///
1754    /// @param table - the table
1755    /// @param column - the column as written
1756    fn constrained_column(
1757        &self,
1758        table: &crate::catalog_view::TableInfo,
1759        column: ast::NameId,
1760    ) -> Result<(Vec<u8>, u16), ParseError> {
1761        let folded = self.ast.folded(column).to_vec();
1762        let Some(position) = table.column_position(&folded) else {
1763            return Err(crate::bind::no_such_column(
1764                self.ast.text(column),
1765                Span::default(),
1766            ));
1767        };
1768        let stored = table
1769            .column(position)
1770            .map(|found| found.name.clone())
1771            .unwrap_or_default();
1772        Ok((stored, position))
1773    }
1774
1775    /// Refuses a `CHECK` added by `ALTER TABLE` that names a column the table
1776    /// does not have.
1777    ///
1778    /// @param table - the table
1779    /// @param expr - the predicate
1780    fn check_names_resolve(
1781        &self,
1782        table: &crate::catalog_view::TableInfo,
1783        expr: ast::ExprId,
1784    ) -> Result<(), ParseError> {
1785        let mut pending = vec![expr];
1786        while let Some(id) = pending.pop() {
1787            pending.extend(expression_children(self.ast, id));
1788            let Some(ast::Expr::Column {
1789                table: qualifier,
1790                column,
1791                ..
1792            }) = self.ast.expr(id)
1793            else {
1794                continue;
1795            };
1796            let folded = self.ast.folded(*column);
1797            let own = qualifier.is_none_or(|name| self.ast.folded(name) == table.folded.as_slice());
1798            let rowid = matches!(folded, b"rowid" | b"oid" | b"_rowid_");
1799            if own && (rowid || table.column_position(folded).is_some()) {
1800                continue;
1801            }
1802            let written = match qualifier {
1803                Some(name) => [self.ast.text(*name), b".", self.ast.text(*column)].concat(),
1804                None => self.ast.text(*column).to_vec(),
1805            };
1806            return Err(crate::bind::no_such_column(
1807                &written,
1808                self.ast.expr_span(id),
1809            ));
1810        }
1811        Ok(())
1812    }
1813
1814    /// Returns whether an expression is the literal `NULL`.
1815    fn is_null_literal(&self, expr: ast::ExprId) -> bool {
1816        matches!(
1817            self.ast.expr(expr),
1818            Some(ast::Expr::Literal(ast::Literal::Null))
1819        )
1820    }
1821
1822    /// Checks what `DROP COLUMN` may not drop.
1823    ///
1824    /// SQLite refuses three things before it changes anything: a column that is
1825    /// part of the primary key, a column declared `UNIQUE` in its own
1826    /// definition, and the only column of a table. Everything else that would
1827    /// break (an index, a `CHECK`, a generated column, a view, a trigger) is
1828    /// found after the change is made, by `ALTER TABLE` re-reading the schema.
1829    ///
1830    /// **Only a column level `UNIQUE` is refused here.** A column named by a
1831    /// table level `UNIQUE (a, b)`, or by `CREATE UNIQUE INDEX`, is dropped as
1832    /// far as this check goes, and the index or the table is what fails.
1833    ///
1834    /// @param table - the table
1835    /// @param position - the declared position of the column
1836    /// @param written - the column's name as the statement wrote it, which is
1837    ///   the spelling SQLite puts in its message
1838    fn check_dropped_column(
1839        &self,
1840        table: &crate::catalog_view::TableInfo,
1841        position: u16,
1842        written: &[u8],
1843    ) -> Result<(), ParseError> {
1844        let named = String::from_utf8_lossy(written).into_owned();
1845        let in_primary_key = table.rowid_alias == Some(position)
1846            || table
1847                .column(position)
1848                .is_some_and(|column| column.primary_key_position.is_some());
1849        if in_primary_key {
1850            return Err(refused(
1851                format!("cannot drop PRIMARY KEY column: \"{named}\""),
1852                Span::default(),
1853            ));
1854        }
1855        if declared_unique(&table.create_sql, usize::from(position)) {
1856            return Err(refused(
1857                format!("cannot drop UNIQUE column: \"{named}\""),
1858                Span::default(),
1859            ));
1860        }
1861        if table.columns.len() <= 1 {
1862            return Err(refused(
1863                format!("cannot drop column \"{named}\": no other columns exist"),
1864                Span::default(),
1865            ));
1866        }
1867        Ok(())
1868    }
1869
1870    /// Binds a `REINDEX`.
1871    ///
1872    /// The name is a collation, a table or an index, and SQLite works out which
1873    /// from what it finds - so the resolution order is the same here. A bare
1874    /// `REINDEX` rebuilds everything, which is the form that matters: it is what
1875    /// a person runs after a collation's definition has changed underneath an
1876    /// index that was built with the old one.
1877    fn bind_reindex(
1878        &mut self,
1879        database: Option<ast::NameId>,
1880        name: Option<ast::NameId>,
1881    ) -> Result<Directive, ParseError> {
1882        let index = self.resolve_database(database)?;
1883        self.record_write_dependency(index);
1884        // **An unqualified name means every database**, which is SQLite's
1885        // `sqlite3Reindex`: a bare `REINDEX` and a collation rebuild the
1886        // indexes of every database, and a table or index name is looked for
1887        // in all of them. Reading only `main` made `REINDEX ix` on a temporary
1888        // table's index "unable to identify the object to be reindexed".
1889        let schema_name = database.map(|_| self.catalog.database_name(index).to_vec());
1890        let qualified = database.is_some();
1891        let every_index = |catalog: &dyn CatalogView| -> Vec<Vec<u8>> {
1892            let tables = if qualified {
1893                catalog.tables_of(index)
1894            } else {
1895                catalog.every_table()
1896            };
1897            tables
1898                .into_iter()
1899                .flat_map(|table| table.indexes.iter())
1900                .map(|entry| entry.name.clone())
1901                .filter(|name| !name.is_empty())
1902                .collect()
1903        };
1904        let Some(name) = name else {
1905            return Ok(Directive::Reindex {
1906                database: index,
1907                indexes: every_index(self.catalog),
1908            });
1909        };
1910        let folded = self.ast.folded(name).to_vec();
1911        if let Some(table) = self.catalog.find_table(schema_name.as_deref(), &folded) {
1912            return Ok(Directive::Reindex {
1913                database: index,
1914                indexes: table
1915                    .indexes
1916                    .iter()
1917                    .map(|entry| entry.name.clone())
1918                    .collect(),
1919            });
1920        }
1921        if let Some((_, entry)) = self.catalog.find_index(schema_name.as_deref(), &folded) {
1922            return Ok(Directive::Reindex {
1923                database: index,
1924                indexes: vec![entry.name.clone()],
1925            });
1926        }
1927        // A collation name rebuilds every index ordered by it. An unknown name
1928        // is an error, and SQLite reports it against the collation because that
1929        // is the last thing it tried.
1930        if Collation::from_name(core::str::from_utf8(&folded).unwrap_or("")).is_some() {
1931            let wanted = folded.clone();
1932            let tables = if qualified {
1933                self.catalog.tables_of(index)
1934            } else {
1935                self.catalog.every_table()
1936            };
1937            let indexes = tables
1938                .into_iter()
1939                .flat_map(|table| table.indexes.iter())
1940                .filter(|entry| {
1941                    entry
1942                        .columns
1943                        .iter()
1944                        .any(|key| key.collation.eq_ignore_ascii_case(&wanted))
1945                })
1946                .map(|entry| entry.name.clone())
1947                .collect();
1948            return Ok(Directive::Reindex {
1949                database: index,
1950                indexes,
1951            });
1952        }
1953        Err(no_such_collation_sequence(
1954            self.ast.text(name),
1955            Span::default(),
1956        ))
1957    }
1958
1959    /// Binds a `VACUUM`.
1960    fn bind_vacuum(
1961        &mut self,
1962        database: Option<ast::NameId>,
1963        into: Option<ast::ExprId>,
1964    ) -> Result<Directive, ParseError> {
1965        let literal = into.and_then(|expr| match self.ast.expr(expr) {
1966            Some(ast::Expr::Literal(ast::Literal::String(text))) => Some(text.clone()),
1967            _ => None,
1968        });
1969        // Anything but a string literal is an expression SQLite evaluates when
1970        // the statement runs, so its text travels with the directive.
1971        let into_sql = match (into, &literal) {
1972            (Some(expr), None) => {
1973                let span = self.ast.expr_span(expr);
1974                let written = self
1975                    .source
1976                    .get(span.start as usize..span.end as usize)
1977                    .ok_or_else(|| refused("the file name could not be read", span))?;
1978                Some(String::from_utf8_lossy(written).into_owned())
1979            }
1980            _ => None,
1981        };
1982        let index = self.resolve_database(database)?;
1983        self.record_write_dependency(index);
1984        Ok(Directive::Vacuum {
1985            database: index,
1986            into: literal,
1987            into_sql,
1988        })
1989    }
1990
1991    /// Binds an `ATTACH`.
1992    ///
1993    /// Every operand is a literal, the `KEY` included. SQLite evaluates them, and every other
1994    /// value they could produce is a file name computed at run time - a
1995    /// statement that decides which database to open from arithmetic is not a
1996    /// shape worth supporting before it is asked for, and it is one an
1997    /// authorizer could not check.
1998    pub(crate) fn bind_attach(
1999        &mut self,
2000        file: ast::ExprId,
2001        schema: ast::ExprId,
2002        key: Option<ast::ExprId>,
2003    ) -> Result<Directive, ParseError> {
2004        // SQLCipher's documentation writes a raw key as `KEY "x'...'"`, which
2005        // the grammar reads as a double quoted name, so a name is read as its
2006        // text the way `literal_or_name` reads a schema name.
2007        let key = match key.map(|expr| self.ast.expr(expr)) {
2008            None => None,
2009            Some(Some(ast::Expr::Literal(ast::Literal::String(text)))) => Some(text.clone()),
2010            Some(Some(ast::Expr::Column {
2011                table: None,
2012                column,
2013                ..
2014            })) => Some(self.ast.text(*column).to_vec()),
2015            Some(_) => {
2016                return Err(unsupported(
2017                    "an ATTACH KEY that is not a string literal",
2018                    Span::default(),
2019                ))
2020            }
2021        };
2022        Ok(Directive::Attach {
2023            file: self.literal_path(file, "ATTACH with a file name that is not a literal")?,
2024            schema: self.literal_or_name(schema)?,
2025            key,
2026        })
2027    }
2028
2029    /// Binds a `DETACH`.
2030    pub(crate) fn bind_detach(&mut self, schema: ast::ExprId) -> Result<Directive, ParseError> {
2031        Ok(Directive::Detach {
2032            schema: self.literal_or_name(schema)?,
2033        })
2034    }
2035
2036    /// Reads a name written either as a word or as a string.
2037    ///
2038    /// `ATTACH 'file.db' AS aux` and `ATTACH 'file.db' AS 'aux'` name the same
2039    /// schema. The grammar parses that position as an expression, so a bare
2040    /// word arrives as a reference to a column that does not exist - and what
2041    /// the statement meant is the word.
2042    fn literal_or_name(&mut self, expr: ast::ExprId) -> Result<Vec<u8>, ParseError> {
2043        match self.ast.expr(expr) {
2044            Some(ast::Expr::Literal(ast::Literal::String(text))) => Ok(text.clone()),
2045            Some(ast::Expr::Column {
2046                table: None,
2047                column,
2048                ..
2049            }) => Ok(self.ast.text(*column).to_vec()),
2050            _ => Err(unsupported(
2051                "a schema name that is not a word or a string",
2052                Span::default(),
2053            )),
2054        }
2055    }
2056
2057    /// Reads the file name a `VACUUM INTO` or an `ATTACH` was given.
2058    ///
2059    /// A literal only. SQLite evaluates the expression, but every other value
2060    /// it could produce is a file name computed at run time, and a statement
2061    /// that decides which file to open or where to write a copy of the
2062    /// database from arithmetic is not a shape worth supporting before it is
2063    /// asked for.
2064    ///
2065    /// @param expr - the file name operand
2066    /// @param refused - the construct the refusal names when it is not one
2067    fn literal_path(
2068        &mut self,
2069        expr: ast::ExprId,
2070        refused: &'static str,
2071    ) -> Result<Vec<u8>, ParseError> {
2072        match self.ast.expr(expr) {
2073            Some(ast::Expr::Literal(ast::Literal::String(text))) => Ok(text.clone()),
2074            _ => Err(unsupported(refused, Span::default())),
2075        }
2076    }
2077
2078    /// Binds a `CREATE VIEW`.
2079    ///
2080    /// The body is not resolved here: SQLite checks only the syntax of a view
2081    /// when it is created, and finds a missing table or column when the view is
2082    /// read.
2083    fn bind_create_view(
2084        &mut self,
2085        temporary: bool,
2086        if_not_exists: bool,
2087        database: Option<ast::NameId>,
2088        name: ast::NameId,
2089        _columns: &[ast::NameId],
2090        select: ast::SelectId,
2091    ) -> Result<Directive, ParseError> {
2092        let temp = self.temporary_database(temporary, database, false)?;
2093        let index = match temp {
2094            Some(index) => index,
2095            None => self.resolve_database(database)?,
2096        };
2097        let written = self.ast.text(name).to_vec();
2098        if written.to_ascii_lowercase().starts_with(b"sqlite_") {
2099            return Err(refused(
2100                format!(
2101                    "object name reserved for internal use: {}",
2102                    String::from_utf8_lossy(&written)
2103                ),
2104                Span::default(),
2105            ));
2106        }
2107        let folded = self.ast.folded(name).to_vec();
2108        let database_name = self.catalog.database_name(index).to_vec();
2109        let exists = self
2110            .catalog
2111            .find_table(Some(database_name.as_slice()), &folded)
2112            .is_some();
2113        if exists && !if_not_exists {
2114            return Err(self.already_exists(&database_name, &folded, name));
2115        }
2116        if !exists {
2117            self.refuse_index_namesake(&database_name, &folded, &written)?;
2118            // **The body is not resolved.** SQLite stores a view whose query
2119            // names a table or a column that does not exist, or that reads the
2120            // view itself, and reports it when the view is read; so does a
2121            // column list of the wrong width. Only a parameter is refused here.
2122            self.refuse_view_parameters()?;
2123            if temp.is_none() && !database_name.eq_ignore_ascii_case(b"temp") {
2124                self.refuse_view_in_another_database(&written, &database_name)?;
2125            }
2126        }
2127        let _ = select;
2128        self.record_write_dependency(index);
2129        Ok(Directive::CreateView {
2130            if_not_exists,
2131            database: index,
2132            name: written,
2133            name_offset: self.name_offset(name),
2134            exists,
2135        })
2136    }
2137
2138    /// Refuses a view that is not temporary and names a table of another database.
2139    ///
2140    /// SQLite checks the names the view's query is written with: one qualified
2141    /// with a database other than the view's own is `view v cannot reference
2142    /// objects in database aux`. A temporary view may name any database.
2143    ///
2144    /// @param view - the view's name as written
2145    /// @param home - the database the view is created in
2146    fn refuse_view_in_another_database(&self, view: &[u8], home: &[u8]) -> Result<(), ParseError> {
2147        for index in 0..self.ast.from_term_count() {
2148            let Some(term) = self.ast.from_term(ast::FromTermId(index as u32)) else {
2149                continue;
2150            };
2151            let ast::FromSource::Table {
2152                database: Some(qualifier),
2153                ..
2154            } = &term.source
2155            else {
2156                continue;
2157            };
2158            if self.ast.text(*qualifier).eq_ignore_ascii_case(home) {
2159                continue;
2160            }
2161            return Err(refused(
2162                format!(
2163                    "view {} cannot reference objects in database {}",
2164                    String::from_utf8_lossy(view),
2165                    String::from_utf8_lossy(self.ast.text(*qualifier))
2166                ),
2167                Span::default(),
2168            ));
2169        }
2170        Ok(())
2171    }
2172
2173    /// Refuses the two places `AUTOINCREMENT` may not be written.
2174    ///
2175    /// It counts the rowid the table has handed out, so it needs a rowid to
2176    /// count: only an `INTEGER PRIMARY KEY` column, and never on a table that
2177    /// has no rowid at all. Both messages are the reference's own, because an
2178    /// application that reads them is reading SQLite's.
2179    fn check_autoincrement(
2180        &mut self,
2181        columns: &[ast::ColumnDef],
2182        without_rowid: bool,
2183    ) -> Result<(), ParseError> {
2184        for column in columns {
2185            let declared = column.declared_type.clone().unwrap_or_default();
2186            for (_, constraint) in &column.constraints {
2187                let ast::ColumnConstraint::PrimaryKey {
2188                    autoincrement: true,
2189                    order,
2190                    ..
2191                } = constraint
2192                else {
2193                    continue;
2194                };
2195                if without_rowid {
2196                    return Err(refused(
2197                        "AUTOINCREMENT not allowed on WITHOUT ROWID tables",
2198                        Span::default(),
2199                    ));
2200                }
2201                // A `DESC` key is not the rowid alias, so it is not allowed
2202                // either.
2203                if !declared.eq_ignore_ascii_case(b"integer")
2204                    || *order == ast::SortOrder::Descending
2205                {
2206                    return Err(refused(
2207                        "AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY",
2208                        Span::default(),
2209                    ));
2210                }
2211            }
2212        }
2213        Ok(())
2214    }
2215
2216    /// Refuses `AUTOINCREMENT` written inside a table level `PRIMARY KEY` unless
2217    /// the key is one ascending INTEGER column of a rowid table.
2218    ///
2219    /// @param columns - the table's columns
2220    /// @param constraints - the table's constraints
2221    /// @param without_rowid - whether `WITHOUT ROWID` was written
2222    fn check_table_autoincrement(
2223        &self,
2224        columns: &[ast::ColumnDef],
2225        constraints: &[(Option<ast::NameId>, ast::TableConstraint)],
2226        without_rowid: bool,
2227    ) -> Result<(), ParseError> {
2228        for (_, constraint) in constraints {
2229            let ast::TableConstraint::PrimaryKey {
2230                columns: keys,
2231                autoincrement: true,
2232                ..
2233            } = constraint
2234            else {
2235                continue;
2236            };
2237            if without_rowid {
2238                return Err(refused(
2239                    "AUTOINCREMENT not allowed on WITHOUT ROWID tables",
2240                    Span::default(),
2241                ));
2242            }
2243            // SQLite looks through a `COLLATE` and ignores the term's `DESC`:
2244            // `PRIMARY KEY(a DESC AUTOINCREMENT)` is still the rowid alias.
2245            let single = match keys.as_slice() {
2246                [key] => {
2247                    let named = match self.ast.expr(key.expr) {
2248                        Some(ast::Expr::Collate { operand, .. }) => self.ast.expr(*operand),
2249                        other => other,
2250                    };
2251                    match named {
2252                        Some(ast::Expr::Column {
2253                            table: None,
2254                            column,
2255                            ..
2256                        }) => Some(self.ast.folded(*column)),
2257                        _ => None,
2258                    }
2259                }
2260                _ => None,
2261            };
2262            let integer = single.is_some_and(|folded| {
2263                columns.iter().any(|column| {
2264                    self.ast.folded(column.name) == folded
2265                        && column
2266                            .declared_type
2267                            .as_deref()
2268                            .is_some_and(|declared| declared.eq_ignore_ascii_case(b"integer"))
2269                })
2270            });
2271            if !integer {
2272                return Err(refused(
2273                    "AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY",
2274                    Span::default(),
2275                ));
2276            }
2277        }
2278        Ok(())
2279    }
2280
2281    /// Binds a `CREATE TRIGGER`.
2282    ///
2283    /// The body is bound here, against the table the trigger is attached to, so
2284    /// a trigger that reads a column that does not exist is refused when it is
2285    /// written rather than the first time somebody writes the table. SQLite
2286    /// makes the same promise, and the alternative is a schema that loads and
2287    /// then fails on an unrelated INSERT.
2288    fn bind_create_trigger(
2289        &mut self,
2290        parts: CreateTriggerParts<'_>,
2291    ) -> Result<Directive, ParseError> {
2292        let temp = self.temporary_database(parts.temporary, parts.database, true)?;
2293        // `for_each_row` records whether the words were written, not whether
2294        // the trigger is one: SQLite has only row triggers, an omitted clause
2295        // means FOR EACH ROW, and FOR EACH STATEMENT is a syntax error in the
2296        // parser. There is nothing to refuse here.
2297        let _ = parts.for_each_row;
2298        let index = match temp {
2299            Some(index) => index,
2300            None => self.resolve_database(parts.database)?,
2301        };
2302        let written = self.ast.text(parts.name).to_vec();
2303        let folded = self.ast.folded(parts.name).to_vec();
2304        let database_name = self.catalog.database_name(index).to_vec();
2305        let table_folded = self.ast.folded(parts.table).to_vec();
2306        // A trigger created in a named database fires for a table in that
2307        // database. A temporary one fires for whatever the name finds, which
2308        // is the whole point of `CREATE TEMP TRIGGER ... ON t`: the trigger is
2309        // the connection's and the table is everybody's. `ON main.t` names the
2310        // database: a temporary trigger may name any, and any other trigger
2311        // only its own, which is SQLite's `sqlite3FixSrcList`.
2312        let named = match parts.table_database {
2313            Some(id) => {
2314                let at = self.resolve_database(Some(id))?;
2315                if temp.is_none() && at != index {
2316                    return Err(refused(
2317                        format!(
2318                            "trigger {} cannot reference objects in database {}",
2319                            String::from_utf8_lossy(&written),
2320                            String::from_utf8_lossy(self.ast.text(id))
2321                        ),
2322                        Span::default(),
2323                    ));
2324                }
2325                Some(self.catalog.database_name(at).to_vec())
2326            }
2327            None => None,
2328        };
2329        let scope = match &named {
2330            Some(name) => Some(name.as_slice()),
2331            None => temp.map_or(Some(database_name.as_slice()), |_| None),
2332        };
2333        let Some(target) = self.catalog.find_table(scope, &table_folded).cloned() else {
2334            // SQLite names the schema the table was looked for in, except for a
2335            // temporary trigger, which looks in all of them.
2336            let missing = match scope {
2337                Some(schema) => [schema, b".", self.ast.text(parts.table)].concat(),
2338                None => self.ast.text(parts.table).to_vec(),
2339            };
2340            return Err(crate::bind::no_such_table(&missing, Span::default()));
2341        };
2342        // After the table is found, as SQLite does: `CREATE TRIGGER sqlite_x ... ON missing` is
2343        // a missing table and not a reserved name.
2344        if written.to_ascii_lowercase().starts_with(b"sqlite_") {
2345            return Err(refused(
2346                format!(
2347                    "object name reserved for internal use: {}",
2348                    String::from_utf8_lossy(&written)
2349                ),
2350                Span::default(),
2351            ));
2352        }
2353        let exists = self
2354            .catalog
2355            .find_trigger(Some(database_name.as_slice()), &folded)
2356            .is_some();
2357        if exists && !parts.if_not_exists {
2358            return Err(refused(
2359                format!(
2360                    "trigger {} already exists",
2361                    String::from_utf8_lossy(&written)
2362                ),
2363                Span::default(),
2364            ));
2365        }
2366        let instead_of = parts.time == Some(ast::TriggerTime::InsteadOf);
2367        match target.kind {
2368            TableKind::View if !instead_of => {
2369                return Err(refused(
2370                    format!(
2371                        "cannot create {} trigger on view: {}",
2372                        if parts.time == Some(ast::TriggerTime::After) {
2373                            "AFTER"
2374                        } else {
2375                            "BEFORE"
2376                        },
2377                        String::from_utf8_lossy(&target.name)
2378                    ),
2379                    Span::default(),
2380                ));
2381            }
2382            TableKind::Table if instead_of => {
2383                return Err(refused(
2384                    format!(
2385                        "cannot create INSTEAD OF trigger on table: {}",
2386                        String::from_utf8_lossy(&target.name)
2387                    ),
2388                    Span::default(),
2389                ));
2390            }
2391            TableKind::Virtual | TableKind::Subquery => {
2392                return Err(unsupported("a trigger on that object", Span::default()));
2393            }
2394            _ => {}
2395        }
2396        // `UPDATE OF a, b` is deliberately *not* checked against the table's
2397        // columns. The pinned build accepts `UPDATE OF nosuchcolumn` and simply
2398        // never fires the trigger, and refusing it here would make inillucent's
2399        // language smaller than the reference's - a schema SQLite wrote that
2400        // inillucent could not load.
2401        // The body is deliberately *not* bound here. SQLite stores a trigger
2402        // whose body names a column that does not exist and reports it on the
2403        // first write that fires it - measured against the pinned build, which
2404        // accepts both `UPDATE OF nosuchcolumn` and a body reading a column the
2405        // table has not got. Refusing either here would leave inillucent unable to
2406        // load a schema SQLite had written.
2407        if temp.is_none() {
2408            self.refuse_qualified_trigger_targets(parts.body)?;
2409        }
2410        let _ = (parts.time, parts.when, parts.body);
2411        self.record_write_dependency(index);
2412        Ok(Directive::CreateTrigger {
2413            database: index,
2414            name: written,
2415            name_offset: self.name_offset(parts.name),
2416            table: target.name.clone(),
2417            exists,
2418        })
2419    }
2420
2421    /// Finds the table a `CREATE INDEX` is on, and the schema the index goes in.
2422    ///
2423    /// **An unqualified index goes where its table is.** SQLite looks the
2424    /// table up in the usual order, `temp` first, and creates the index in
2425    /// the schema it found the table in. Taking an unqualified index to mean
2426    /// `main` made `CREATE TEMP TABLE t(a); CREATE INDEX i ON t(a)` report
2427    /// "no such table: t". A table that is not there is reported with the
2428    /// schema it was looked for in, which is `main` when none was written.
2429    ///
2430    /// @param database - the schema the statement wrote, when it wrote one
2431    /// @param table - the table's name
2432    fn index_target(
2433        &self,
2434        database: Option<ast::NameId>,
2435        table: ast::NameId,
2436    ) -> Result<(usize, Vec<u8>, crate::catalog_view::TableInfo), ParseError> {
2437        let table_folded = self.ast.folded(table).to_vec();
2438        let index = match database {
2439            Some(_) => self.resolve_database(database)?,
2440            None => match self.catalog.find_table(None, &table_folded) {
2441                Some(found) => found.database,
2442                None => return Err(self.index_without_table(0, &table_folded, table)),
2443            },
2444        };
2445        let database_name = self.catalog.database_name(index).to_vec();
2446        let Some(target) = self
2447            .catalog
2448            .find_table(Some(database_name.as_slice()), &table_folded)
2449            .cloned()
2450        else {
2451            return Err(self.index_without_table(index, &table_folded, table));
2452        };
2453        Ok((index, database_name, target))
2454    }
2455    /// Binds a `CREATE INDEX`.
2456    ///
2457    /// @param spec - what the statement named
2458    fn bind_create_index(&mut self, spec: &CreateIndexSpec<'_>) -> Result<Directive, ParseError> {
2459        let CreateIndexSpec {
2460            database,
2461            name,
2462            table,
2463            using,
2464            columns,
2465            settings,
2466            ..
2467        } = *spec;
2468        refuse_nulls_order(columns)?;
2469        let unique = spec.unique == Uniqueness::Unique;
2470        let if_not_exists = spec.if_not_exists == IfNotExists::Skip;
2471        // **A `WHERE` is carried in the statement text, not in this
2472        // directive.** The engine re-parses the canonical SQL it stores -
2473        // `index_from_create_sql` already puts the predicate on
2474        // `IndexInfo::partial_sql` - so a field here would be a second copy to
2475        // keep in step. A predicate that names a column the table has not got
2476        // is refused when the index is built, by the query that fills it.
2477        // Only one module can back an index, and naming another is refused here
2478        // rather than accepted and ignored - an index that silently was not the
2479        // structure it asked for is the shape of wrong answer this ticket keeps
2480        // finding.
2481        let using = match using {
2482            None => None,
2483            Some(named) => {
2484                let folded = self.ast.folded(named).to_vec();
2485                // Two structures, and both are real: `inillucent_hnsw` is the
2486                // graph the retrieval engine builds, and `ivfflat` is the
2487                // inverted file pgvector's other index type is - k-means
2488                // centroids and a list per centroid, probed `probes` deep.
2489                // Anything else is refused rather than accepted and ignored:
2490                // an index that silently was not the structure it asked for is
2491                // the shape of wrong answer this ticket keeps finding.
2492                if folded != b"inillucent_hnsw" && folded != b"ivfflat" {
2493                    return Err(unsupported(
2494                        "an index USING a module other than inillucent_hnsw or ivfflat",
2495                        Span::default(),
2496                    ));
2497                }
2498                Some(folded)
2499            }
2500        };
2501        let parsed_settings = index_settings(&using, settings)?;
2502        let (index, database_name, target) = self.index_target(database, table)?;
2503        self.refuse_unindexable(&target)?;
2504        let written = self.ast.text(name).to_vec();
2505        if written.to_ascii_lowercase().starts_with(b"sqlite_") {
2506            return Err(refused(
2507                format!(
2508                    "object name reserved for internal use: {}",
2509                    String::from_utf8_lossy(&written)
2510                ),
2511                Span::default(),
2512            ));
2513        }
2514        let folded = self.ast.folded(name).to_vec();
2515        let exists = self.check_new_index_name(&database_name, &folded, &written, if_not_exists)?;
2516        self.check_index_declarations(&target, columns, spec.filter)?;
2517        let keys = self.index_key_columns(&target, columns)?;
2518        self.record_write_dependency(index);
2519        Ok(Directive::CreateIndex {
2520            unique,
2521            if_not_exists,
2522            database: index,
2523            name: written,
2524            name_offset: self.name_offset(name),
2525            table: target.name.clone(),
2526            table_root: target.root,
2527            using,
2528            columns: keys,
2529            settings: parsed_settings,
2530            exists,
2531        })
2532    }
2533
2534    /// Describes each key of a `CREATE INDEX` for the engine.
2535    ///
2536    /// @param target - the table the index is over
2537    /// @param columns - the indexed columns, in key order
2538    fn index_key_columns(
2539        &self,
2540        target: &crate::catalog_view::TableInfo,
2541        columns: &[ast::IndexedColumn],
2542    ) -> Result<Vec<IndexKeyColumn>, ParseError> {
2543        let mut keys = Vec::with_capacity(columns.len());
2544        for column in columns {
2545            // `CREATE INDEX x ON t(b COLLATE NOCASE DESC)` parses the collation
2546            // into the *expression*, because that is where the grammar puts a
2547            // `COLLATE` that follows a value. It is still an index on a bare
2548            // column, and treating it as one is the difference between
2549            // supporting the everyday form and refusing it as an expression.
2550            let (expr, written_collation) = match self.ast.expr(column.expr) {
2551                Some(ast::Expr::Collate { operand, collation }) => {
2552                    (self.ast.expr(*operand), Some(*collation))
2553                }
2554                other => (other, column.collation),
2555            };
2556            // A key that is not a bare column is an expression, and is carried
2557            // as the source text the engine re-parses. Its collation is BINARY
2558            // unless the statement named one: there is no column to inherit
2559            // from.
2560            let named = match expr {
2561                Some(ast::Expr::Column {
2562                    table: None,
2563                    column: name,
2564                    ..
2565                }) => Some(*name),
2566                _ => None,
2567            };
2568            let Some(name) = named else {
2569                let collation = match written_collation {
2570                    Some(collation) => self.ast.folded(collation).to_vec(),
2571                    None => b"binary".to_vec(),
2572                };
2573                keys.push(IndexKeyColumn {
2574                    column: None,
2575                    expr_sql: Some(self.ast.expr_span(column.expr).slice(self.source).to_vec()),
2576                    collation,
2577                    descending: column.order == ast::SortOrder::Descending,
2578                });
2579                continue;
2580            };
2581            let folded = self.ast.folded(name).to_vec();
2582            let Some(position) = target.column_position(&folded) else {
2583                return Err(crate::bind::no_such_column(
2584                    self.ast.text(name),
2585                    Span::default(),
2586                ));
2587            };
2588            let collation = match written_collation {
2589                Some(collation) => self.ast.folded(collation).to_vec(),
2590                None => target
2591                    .column(position)
2592                    .map(|column| column.collation.clone())
2593                    .unwrap_or_else(|| b"binary".to_vec()),
2594            };
2595            keys.push(IndexKeyColumn {
2596                column: Some(position),
2597                expr_sql: None,
2598                collation,
2599                descending: column.order == ast::SortOrder::Descending,
2600            });
2601        }
2602        Ok(keys)
2603    }
2604
2605    /// Refuses `DROP TABLE` and `DROP VIEW` on the schema table, in SQLite's words.
2606    ///
2607    /// SQLite answers `table sqlite_master may not be dropped` for either statement,
2608    /// with or without `IF EXISTS`, and spells the temporary database's copy
2609    /// `sqlite_temp_master`. All four spellings of the two names are the schema table.
2610    ///
2611    /// @param folded - the name the statement dropped, folded
2612    /// @param database - the database it resolved to
2613    fn refuse_dropping_the_schema_table(
2614        &self,
2615        folded: &[u8],
2616        database: &[u8],
2617    ) -> Result<(), ParseError> {
2618        let main = matches!(folded, b"sqlite_master" | b"sqlite_schema");
2619        let temp = matches!(folded, b"sqlite_temp_master" | b"sqlite_temp_schema");
2620        if !main && !temp {
2621            return Ok(());
2622        }
2623        let in_temp = temp || database.eq_ignore_ascii_case(b"temp");
2624        let said = match in_temp {
2625            true => "table sqlite_temp_master may not be dropped",
2626            false => "table sqlite_master may not be dropped",
2627        };
2628        Err(refused(said, Span::default()))
2629    }
2630
2631    /// Works out which database a `DROP` is about, and how a missing table is named.
2632    ///
2633    /// **An unqualified name is looked for in every database, `temp` first,** which is
2634    /// SQLite's `sqlite3LocateTable` order. Taking it to mean `main` made `DROP TABLE s`
2635    /// "no such table" for a temporary `s`, and dropped `main.s` where SQLite drops the
2636    /// temporary `s` that shadows it.
2637    ///
2638    /// SQLite names a missing table with the schema the statement wrote, and calls an
2639    /// unknown schema in front of a table a missing table: `DROP TABLE nosuch.t` is `no
2640    /// such table: nosuch.t`.
2641    ///
2642    /// @param kind - what the statement drops
2643    /// @param database - the schema as written, when there is one
2644    /// @param written - the object's name as written
2645    /// @param folded - the object's name folded
2646    fn resolve_drop_database(
2647        &self,
2648        kind: ObjectKind,
2649        database: Option<ast::NameId>,
2650        written: &[u8],
2651        folded: &[u8],
2652    ) -> Result<(Vec<u8>, usize), ParseError> {
2653        let qualified = match database {
2654            Some(schema) => [self.ast.text(schema), b".".as_slice(), written].concat(),
2655            None => written.to_vec(),
2656        };
2657        let index = match database {
2658            Some(_) => match self.resolve_database(database) {
2659                Err(_) if kind == ObjectKind::Table => {
2660                    return Err(no_such_table(&qualified, Span::default()))
2661                }
2662                resolved => resolved?,
2663            },
2664            None => self.unqualified_home(kind, folded).unwrap_or(0),
2665        };
2666        Ok((qualified, index))
2667    }
2668
2669    /// Binds `DROP TABLE`, which frees the table's tree and the trees of its indexes.
2670    ///
2671    /// @param if_exists - whether `IF EXISTS` was written
2672    /// @param index - the database the table is in
2673    /// @param database_name - that database's name
2674    /// @param folded - the table's name folded
2675    /// @param written - the table's name as written
2676    /// @param qualified - the name as a failure should print it
2677    fn bind_drop_table(
2678        &self,
2679        if_exists: bool,
2680        index: usize,
2681        database_name: &[u8],
2682        folded: &[u8],
2683        written: Vec<u8>,
2684        qualified: &[u8],
2685    ) -> Result<Directive, ParseError> {
2686        let kind = ObjectKind::Table;
2687        let found = self
2688            .catalog
2689            .find_table(Some(database_name), folded)
2690            .cloned();
2691        let Some(table) = found else {
2692            if if_exists {
2693                return Ok(Directive::Drop {
2694                    kind,
2695                    if_exists,
2696                    database: index,
2697                    name: written,
2698                    root: 0,
2699                    index_roots: Vec::new(),
2700                    exists: false,
2701                });
2702            }
2703            return Err(no_such_table(qualified, Span::default()));
2704        };
2705        self.refuse_dropping_own_table(&table, &written)?;
2706        // A WITHOUT ROWID table's primary key *is* the table's own b-tree, so its entry
2707        // names the same root. Freeing it twice frees a page that is already on the free
2708        // list, which reads back as a malformed database.
2709        let index_roots = table
2710            .indexes
2711            .iter()
2712            .map(|held| held.root)
2713            .filter(|root| *root != 0 && *root != table.root)
2714            .collect();
2715        Ok(Directive::Drop {
2716            kind,
2717            if_exists,
2718            database: index,
2719            name: written,
2720            root: table.root,
2721            index_roots,
2722            exists: true,
2723        })
2724    }
2725
2726    /// Binds a `DROP TABLE` or `DROP INDEX`.
2727    fn bind_drop(
2728        &mut self,
2729        kind: ObjectKind,
2730        if_exists: bool,
2731        database: Option<ast::NameId>,
2732        name: ast::NameId,
2733    ) -> Result<Directive, ParseError> {
2734        let written = self.ast.text(name).to_vec();
2735        let folded = self.ast.folded(name).to_vec();
2736        let (qualified, index) = self.resolve_drop_database(kind, database, &written, &folded)?;
2737        let database_name = self.catalog.database_name(index).to_vec();
2738        self.record_write_dependency(index);
2739        if kind != ObjectKind::Trigger && kind != ObjectKind::Index {
2740            self.refuse_dropping_the_schema_table(&folded, database_name.as_slice())?;
2741        }
2742        if kind == ObjectKind::Trigger {
2743            // A trigger owns no B-tree either, so dropping one is its schema row
2744            // and nothing else.
2745            let exists = self
2746                .catalog
2747                .find_trigger(Some(database_name.as_slice()), &folded)
2748                .is_some();
2749            if !exists && !if_exists {
2750                return Err(refused(
2751                    format!("no such trigger: {}", String::from_utf8_lossy(&written)),
2752                    Span::default(),
2753                ));
2754            }
2755            return Ok(Directive::Drop {
2756                kind,
2757                if_exists,
2758                database: index,
2759                name: written,
2760                root: 0,
2761                index_roots: Vec::new(),
2762                exists,
2763            });
2764        }
2765        if kind == ObjectKind::View {
2766            // A view owns no B-tree, so dropping one is the schema row and
2767            // nothing else - and it must refuse a table, because `DROP VIEW t`
2768            // on a table is an error rather than a drop.
2769            let found = self
2770                .catalog
2771                .find_table(Some(database_name.as_slice()), &folded)
2772                .cloned();
2773            let exists = found
2774                .as_ref()
2775                .is_some_and(|table| table.kind == crate::catalog_view::TableKind::View);
2776            if found.is_some() && !exists {
2777                return Err(refused(
2778                    format!(
2779                        "use DROP TABLE to delete table {}",
2780                        String::from_utf8_lossy(&written)
2781                    ),
2782                    Span::default(),
2783                ));
2784            }
2785            if !exists && !if_exists {
2786                return Err(refused(
2787                    format!("no such view: {}", String::from_utf8_lossy(&written)),
2788                    Span::default(),
2789                ));
2790            }
2791            return Ok(Directive::Drop {
2792                kind,
2793                if_exists,
2794                database: index,
2795                name: written,
2796                root: 0,
2797                index_roots: Vec::new(),
2798                exists,
2799            });
2800        }
2801        if kind == ObjectKind::Table {
2802            return self.bind_drop_table(
2803                if_exists,
2804                index,
2805                &database_name,
2806                &folded,
2807                written,
2808                &qualified,
2809            );
2810        }
2811        self.refuse_dropping_constraint_index(&database_name, &folded)?;
2812        let found = self.find_index_root(index, &folded);
2813        let Some(root) = found else {
2814            if if_exists {
2815                return Ok(Directive::Drop {
2816                    kind,
2817                    if_exists,
2818                    database: index,
2819                    name: written,
2820                    root: 0,
2821                    index_roots: Vec::new(),
2822                    exists: false,
2823                });
2824            }
2825            return Err(refused(
2826                format!("no such index: {}", String::from_utf8_lossy(&written)),
2827                Span::default(),
2828            ));
2829        };
2830        // The index a `UNIQUE` or `PRIMARY KEY` constraint made is part of the table,
2831        // and SQLite refuses to drop it by name.
2832        if folded.starts_with(b"sqlite_autoindex_") {
2833            return Err(refused(
2834                "index associated with UNIQUE or PRIMARY KEY constraint cannot be dropped",
2835                Span::default(),
2836            ));
2837        }
2838        Ok(Directive::Drop {
2839            kind,
2840            if_exists,
2841            database: index,
2842            name: written,
2843            root,
2844            index_roots: Vec::new(),
2845            exists: true,
2846        })
2847    }
2848
2849    /// Returns the database an unqualified object name resolves to.
2850    ///
2851    /// `None` when no database holds an object of that kind by that name, so
2852    /// the caller reports it against `main` as before.
2853    ///
2854    /// @param kind - what sort of object the statement names
2855    /// @param folded - the object's folded name
2856    fn unqualified_home(&self, kind: ObjectKind, folded: &[u8]) -> Option<usize> {
2857        match kind {
2858            ObjectKind::Trigger => self
2859                .catalog
2860                .find_trigger(None, folded)
2861                .map(|(table, _)| table.database),
2862            ObjectKind::Index => self
2863                .catalog
2864                .find_index(None, folded)
2865                .map(|(table, _)| table.database),
2866            _ => self
2867                .catalog
2868                .find_table(None, folded)
2869                .map(|table| table.database),
2870        }
2871    }
2872
2873    /// Binds a `PRAGMA`.
2874    fn bind_pragma(
2875        &mut self,
2876        database: Option<ast::NameId>,
2877        name: ast::NameId,
2878        value: &ast::PragmaValue,
2879    ) -> Result<Directive, ParseError> {
2880        let argument = match value {
2881            ast::PragmaValue::None => None,
2882            ast::PragmaValue::Name(name) => {
2883                Some(PragmaArgument::Name(self.ast.text(*name).to_vec()))
2884            }
2885            ast::PragmaValue::Value(expr) => Some(PragmaArgument::Value(self.bind_expr(*expr)?)),
2886        };
2887        let database = match database {
2888            Some(id) => Some(self.resolve_database(Some(id))?),
2889            None => None,
2890        };
2891        Ok(Directive::Pragma {
2892            database,
2893            name: self.ast.folded(name).to_vec(),
2894            argument,
2895        })
2896    }
2897
2898    /// Returns the temporary database's number when `TEMP` was written.
2899    ///
2900    /// A temporary table's or view's name may be qualified only by `temp`:
2901    /// `CREATE TEMP TABLE main.t` says two different things about where the
2902    /// table goes, and SQLite refuses it rather than picking one, while
2903    /// `CREATE TEMP TABLE temp.t` says the same thing twice and SQLite accepts
2904    /// it. A temporary trigger takes no qualifier at all, which is SQLite's
2905    /// rule in `sqlite3BeginTrigger`.
2906    ///
2907    /// @param temporary - whether `TEMP` was written
2908    /// @param database - the qualifier, when one was written
2909    /// @param trigger - whether the object is a trigger
2910    fn temporary_database(
2911        &self,
2912        temporary: bool,
2913        database: Option<ast::NameId>,
2914        trigger: bool,
2915    ) -> Result<Option<usize>, ParseError> {
2916        if !temporary {
2917            return Ok(None);
2918        }
2919        if let Some(id) = database {
2920            if trigger {
2921                return Err(refused(
2922                    "temporary trigger may not have qualified name",
2923                    Span::default(),
2924                ));
2925            }
2926            if self.ast.folded(id) != b"temp" {
2927                return Err(refused(
2928                    "temporary table name must be unqualified",
2929                    Span::default(),
2930                ));
2931            }
2932        }
2933        self.catalog
2934            .database_index(b"temp")
2935            .map(Some)
2936            .ok_or_else(|| refused("no temporary database", Span::default()))
2937    }
2938
2939    /// Resolves a schema qualifier to an attached database index.
2940    fn resolve_database(&self, database: Option<ast::NameId>) -> Result<usize, ParseError> {
2941        let Some(id) = database else {
2942            return Ok(0);
2943        };
2944        let folded = self.ast.folded(id);
2945        self.catalog.database_index(folded).ok_or_else(|| {
2946            refused(
2947                format!(
2948                    "unknown database {}",
2949                    String::from_utf8_lossy(self.ast.text(id))
2950                ),
2951                // SQLite points at the schema name.
2952                self.ast.name(id).map_or(Span::default(), |name| name.span),
2953            )
2954        })
2955    }
2956
2957    /// Returns the byte an identifier starts at in the statement's source.
2958    ///
2959    /// The canonical `sqlite_schema` text is the statement from its object
2960    /// name onward, which is how `IF NOT EXISTS` and the schema qualifier come
2961    /// to be missing from what SQLite stores. Slicing the source is the only
2962    /// way to reproduce that exactly; rendering the tree back would normalise
2963    /// whitespace and quoting the user chose.
2964    fn name_offset(&self, name: ast::NameId) -> u32 {
2965        self.ast.name(name).map_or(0, |name| name.span.start)
2966    }
2967
2968    /// Returns an index's root page, searching every table of a database.
2969    fn find_index_root(&self, database: usize, folded: &[u8]) -> Option<u32> {
2970        let name = self.catalog.database_name(database).to_vec();
2971        self.catalog
2972            .find_index(Some(name.as_slice()), folded)
2973            .map(|(_, index)| index.root)
2974    }
2975}
2976
2977/// Returns a column name as it can be written back into a `CREATE` statement.
2978///
2979/// A name a query invented - `SELECT 1` reports the column as `1` - is not an
2980/// identifier, so it is quoted the way SQLite quotes it: `CREATE TABLE w("1")`.
2981///
2982/// @param name - the column's name as the query reports it
2983fn quoted_name(name: &[u8]) -> Vec<u8> {
2984    // SQLite quotes a name that is a keyword as well as one that is not a plain
2985    // word, so the stored text of `CREATE TABLE "select" AS ...` can be read back.
2986    let plain = !name.is_empty()
2987        && !name.first().is_some_and(u8::is_ascii_digit)
2988        && name
2989            .iter()
2990            .all(|byte| byte.is_ascii_alphanumeric() || *byte == b'_')
2991        && crate::keyword::lookup(name).is_none();
2992    if plain {
2993        return name.to_vec();
2994    }
2995    let mut out = Vec::with_capacity(name.len().saturating_add(2));
2996    out.push(b'"');
2997    for byte in name {
2998        if *byte == b'"' {
2999            out.push(b'"');
3000        }
3001        out.push(*byte);
3002    }
3003    out.push(b'"');
3004    out
3005}
3006
3007/// Returns the type name a `CREATE TABLE ... AS SELECT` writes for a column.
3008///
3009/// The affinity's own name, with the leading space, exactly as SQLite writes
3010/// it: BLOB affinity - which is what a column with no declared type has -
3011/// writes nothing at all, so the copy of an untyped column is untyped.
3012///
3013/// A column that is not a bare column of a table has no declared type, and
3014/// takes the affinity of its expression instead: `CAST(1 AS TEXT)` is a `TEXT`
3015/// column, and `1 + 1` has no affinity and so no type.
3016///
3017/// @param declared - the source column's declared type, as written
3018/// @param expression - the affinity of the expression the column is computed by
3019fn affinity_type(
3020    declared: &[u8],
3021    expression: Option<inillucent_value::affinity::Affinity>,
3022) -> &'static [u8] {
3023    let affinity = match (declared.is_empty(), expression) {
3024        (true, Some(held)) => held,
3025        _ => inillucent_value::affinity::for_column(declared),
3026    };
3027    match affinity {
3028        inillucent_value::affinity::Affinity::Blob => b"",
3029        inillucent_value::affinity::Affinity::Text => b" TEXT",
3030        inillucent_value::affinity::Affinity::Integer => b" INT",
3031        inillucent_value::affinity::Affinity::Real => b" REAL",
3032        inillucent_value::affinity::Affinity::Numeric
3033        | inillucent_value::affinity::Affinity::FlexNum => b" NUM",
3034    }
3035}
3036
3037/// Returns the width SQLite counts an identifier as when it decides whether to
3038/// write a `CREATE TABLE ... AS SELECT`'s columns one per line.
3039///
3040/// Its own `identLength`: the name plus the two quotes it might need, plus one
3041/// for each quote inside it that would have to be doubled. The rule that reads
3042/// it is "under fifty, one line", and reproducing both is what makes the stored
3043/// declaration byte-identical rather than merely equivalent.
3044///
3045/// @param name - the identifier
3046fn identifier_width(name: &[u8]) -> usize {
3047    name.len()
3048        .saturating_add(2)
3049        .saturating_add(name.iter().filter(|byte| **byte == b'"').count())
3050}
3051
3052/// The storage parameters `CREATE INDEX ... WITH ( ... )` accepts.
3053///
3054/// One entry per name the vector index understands, with the store option it
3055/// becomes. **A name that is not here is refused rather than ignored**, which is
3056/// the same rule `USING` follows a few lines above and for the same reason: an
3057/// index that quietly was not built the way it was asked to be is a wrong answer
3058/// nobody can see.
3059const INDEX_SETTINGS: [(&str, &str); 10] = [
3060    // The graph's own three, spelled as pgvector spells them.
3061    ("m", "m"),
3062    ("ef_construction", "ef_construction"),
3063    ("ef_search", "ef_search"),
3064    // Whether a query walks the graph (`approximate`, the default for an
3065    // `inillucent_hnsw` index) or compares every vector (`exact`). The store
3066    // validates the value, so `mode = 'fast'` is refused by name.
3067    ("mode", "mode"),
3068    // The distance the index is built for. pgvector puts this in an operator
3069    // class - `USING hnsw (v vector_l2_ops)` - and names it here as well.
3070    ("metric", "metric"),
3071    ("distance", "metric"),
3072    // How many threads the build uses, and how far behind the table the index
3073    // may fall before it is rebuilt.
3074    ("threads", "threads"),
3075    ("compact", "compact"),
3076    // The two an `ivfflat` has: how many centroids it clusters into, and how
3077    // many of those lists a query reads.
3078    ("lists", "lists"),
3079    ("probes", "probes"),
3080];
3081
3082/// Checks `WITH ( ... )` against the structure that will read it.
3083///
3084/// Returns the settings as folded `(name, value)` pairs, in the order written.
3085/// A plain `CREATE INDEX` may not carry any: a b-tree has no parameters, and
3086/// accepting them would mean accepting a setting nothing reads.
3087///
3088/// @param using - the module the index named, when it named one
3089/// @param settings - the raw `name = value` slices
3090fn index_settings(
3091    using: &Option<Vec<u8>>,
3092    settings: &[Vec<u8>],
3093) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ParseError> {
3094    if settings.is_empty() {
3095        return Ok(Vec::new());
3096    }
3097    if using.is_none() {
3098        return Err(unsupported(
3099            "WITH ( ... ) on an index that is not USING a module",
3100            Span::default(),
3101        ));
3102    }
3103    let mut held = Vec::with_capacity(settings.len());
3104    for setting in settings {
3105        let text = String::from_utf8_lossy(setting).to_string();
3106        let Some((name, value)) = text.split_once('=') else {
3107            return Err(refused(
3108                format!("index setting {} is not name = value", text.trim()),
3109                Span::default(),
3110            ));
3111        };
3112        let folded = name.trim().to_ascii_lowercase();
3113        let Some((_, option)) = INDEX_SETTINGS
3114            .iter()
3115            .find(|(known, _)| *known == folded.as_str())
3116        else {
3117            return Err(refused(
3118                format!("no such index setting: {folded}"),
3119                Span::default(),
3120            ));
3121        };
3122        let value = value
3123            .trim()
3124            .trim_matches(|held| held == '\'' || held == '"');
3125        held.push((option.as_bytes().to_vec(), value.as_bytes().to_vec()));
3126    }
3127    Ok(held)
3128}