Skip to main content

inillucent_sql/
dml.rs

1//! Binding INSERT, UPDATE and DELETE.
2//!
3//! Invariant: a bound DML statement names every value it will write, in table
4//! column order, before anything is compiled. A column the statement did not
5//! mention is not left to be filled in later by whoever runs it - it carries
6//! its `DEFAULT`, or a NULL, as an expression like any other. That is what
7//! makes `INSERT INTO t(b) VALUES(1)` and `INSERT INTO t VALUES(NULL, 1)`
8//! compile to the same shape, and it is why the constraint checks can be
9//! written once against a row image rather than twice against two.
10//!
11//! Constraints are bound here too, out of the `CREATE TABLE` text the file
12//! stores. The catalog keeps them as source, because the catalog sits below
13//! the binder and cannot bind anything; the binder parses that source against
14//! the table it belongs to and gets an ordinary expression back. A CHECK is
15//! therefore evaluated by exactly the machinery that evaluates a WHERE clause,
16//! which is the only way to be sure the two agree about what `x > 0` means
17//! when `x` is text.
18
19use inillucent_base::limits::Limits;
20use inillucent_value::Collation;
21
22use crate::ast::{self, ConflictAction};
23use crate::bind::{
24    no_such_column, refused, unsupported, Binder, BoundExpr, BoundOrderTerm, BoundResultColumn,
25    BoundSelect, BoundSource,
26};
27use crate::catalog_view::{IndexInfo, TableInfo, TableKind, TriggerEventInfo, TriggerInfo};
28use crate::diagnostic::ParseError;
29use crate::lexer::Span;
30use crate::parser::parse_expression;
31
32/// The internal tables an application may write, as SQLite allows.
33///
34/// **Four, and two of them are the schema.** Every table whose
35/// name begins with `sqlite_` used to be refused, which is wrong for all four,
36/// because writing them is the documented way to use them:
37///
38/// - `sqlite_schema`, and `sqlite_master` which is its other name, are what
39///   `PRAGMA writable_schema` is for, and `.dump` emits
40///   `INSERT INTO sqlite_schema(type,name,tbl_name,rootpage,sql)VALUES(...)`
41///   for a virtual table - which is the only way a dump can restore one
42///   without building empty shadow tables over the ones it is about to fill
43///   (task-1979, R2). Whether the pragma is on is the *engine's* question and
44///   not the binder's: `ImportedDatabase::refuse_schema_write` refuses the
45///   statement when it is off, the way `refuse_shadow_write` refuses a write a
46///   defensive connection may not make.
47///
48/// - `sqlite_sequence` holds one row per `AUTOINCREMENT` table, and
49///   `UPDATE sqlite_sequence SET seq = 0 WHERE name = 't'` is how the counter is
50///   reset. `DELETE FROM sqlite_sequence` is how it is reset for every table at
51///   once. Refusing them left no way at all to do either.
52/// - `sqlite_stat1` is what `ANALYZE` writes, and `.dump` emits
53///   `INSERT INTO sqlite_stat1 VALUES(...)` for it - so a dump this engine
54///   produced could not be replayed into it.
55///
56/// They are ordinary tables in every other respect: the rows are what they are,
57/// and a value written into one is used exactly as `ANALYZE` or the rowid
58/// allocator would have used the one it replaced.
59const WRITABLE_INTERNAL: [&[u8]; 4] = [
60    b"sqlite_sequence",
61    b"sqlite_stat1",
62    b"sqlite_schema",
63    b"sqlite_master",
64];
65
66/// Where one column's value comes from in an INSERT.
67#[derive(Clone, Debug, PartialEq)]
68pub enum ColumnSource {
69    /// The value at this position of the source row.
70    Row(usize),
71    /// An expression evaluated once per row, which is what a `DEFAULT` is.
72    Expr(BoundExpr),
73    /// A generated column, computed from the rest of the row rather than from
74    /// anything the statement supplied.
75    ///
76    /// It is its own variant because it is evaluated at a different *time*: a
77    /// `DEFAULT` is a value like any other, while a generated column reads the
78    /// row it is part of and so cannot be computed until the rest of it is.
79    Generated(BoundExpr),
80}
81
82/// What an INSERT inserts.
83#[derive(Clone, Debug, PartialEq)]
84pub enum BoundInsertSource {
85    /// Literal rows, each already bound.
86    Values(Vec<Vec<BoundExpr>>),
87    /// A query, whose result columns feed the target columns in order.
88    Select(Box<BoundSelect>),
89}
90
91/// One `CHECK` constraint, bound against its table.
92#[derive(Clone, Debug, PartialEq)]
93pub struct BoundCheck {
94    /// The constraint's name, when it was written with one.
95    pub name: Option<Vec<u8>>,
96    /// The predicate.
97    pub expr: BoundExpr,
98}
99
100/// A `NOT NULL` column's `DEFAULT`, bound so a `REPLACE` can stand it in.
101///
102/// **REPLACE's rule for a `NOT NULL` violation is to substitute the column's
103/// default, and to fall back to `ABORT` only when there is no default.** So
104/// `UPDATE OR REPLACE t SET c = NULL` on `c TEXT NOT NULL DEFAULT 'd'` stores
105/// `'d'`, and this engine used to refuse the statement instead.
106///
107/// The write path cannot bind one for itself: a default is schema text, and by
108/// the time a row is being checked the parser is long out of scope. The binder
109/// already binds one for every column a statement *omits*; these are the same
110/// expressions bound for the columns it supplies, which is where a NULL that
111/// needs replacing can come from.
112///
113/// Only the columns that can need it are here - `NOT NULL` and with a default -
114/// so an ordinary table carries an empty vector and the write path skips the
115/// whole apparatus.
116#[derive(Clone, Debug, PartialEq)]
117pub struct BoundDefault {
118    /// The column the default belongs to.
119    pub column: u16,
120    /// The default expression, bound.
121    pub expr: BoundExpr,
122}
123
124/// The expressions one index needs evaluated per row to be maintained.
125///
126/// **An index is usually just columns of the row, and then it needs none of
127/// this.** A partial index holds only the rows its predicate accepts, and an
128/// index on an expression holds a value no column carries - so for those two,
129/// maintaining the index means evaluating something per row rather than
130/// copying a slot. They travel on the bound statement for the same reason the
131/// table's `CHECK` predicates do: the binder is what can turn schema text into
132/// a `BoundExpr`, and the write path is what runs it.
133///
134/// The list holds only the indexes that need it, so a table with neither kind
135/// leaves it empty and the write path's loop runs zero times - which is every
136/// table the gate measures.
137#[derive(Clone, Debug, PartialEq)]
138pub struct BoundIndexExprs {
139    /// The index's position in the table's `indexes`.
140    pub position: usize,
141    /// The partial-index predicate, when it has one.
142    pub predicate: Option<BoundExpr>,
143    /// One per key column: the expression it indexes, or `None` for a column.
144    pub keys: Vec<Option<BoundExpr>>,
145}
146
147/// One statement of a trigger body, bound.
148///
149/// The four the grammar allows and no more. A trigger body is not a general
150/// statement list: it cannot create objects, cannot open transactions, and
151/// cannot return rows to the caller, so a variant for anything else would be a
152/// shape the binder is required to refuse.
153#[derive(Clone, Debug, PartialEq)]
154pub enum BoundTriggerStatement {
155    /// `INSERT`.
156    Insert(Box<BoundInsert>),
157    /// `UPDATE`.
158    Update(Box<BoundUpdate>),
159    /// `DELETE`.
160    Delete(Box<BoundDelete>),
161    /// `SELECT`, which a body runs for its side effects - in practice for the
162    /// `RAISE()` inside it.
163    Select(Box<BoundSelect>),
164}
165
166/// A trigger, bound against the write that fires it.
167///
168/// It is bound per statement rather than once per schema because the body's
169/// FROM terms take statement-wide source numbers, and those only exist relative
170/// to the statement they are inlined into.
171#[derive(Clone, Debug, PartialEq)]
172pub struct BoundTrigger {
173    /// The trigger's name, for the diagnostic when its body fails.
174    pub name: Vec<u8>,
175    /// The folded name of the table it is attached to.
176    ///
177    /// Read by the executor to decide whether a body statement is writing the
178    /// trigger's *own* table, which is what `PRAGMA recursive_triggers` is
179    /// about: with it on, such a write fires this trigger again.
180    pub table: Vec<u8>,
181    /// Whether it fires before or after the row is written.
182    pub time: ast::TriggerTime,
183    /// The `WHEN` guard, when one was written.
184    pub when: Option<BoundExpr>,
185    /// The body statements, in written order.
186    pub body: Vec<BoundTriggerStatement>,
187    /// Whether the binder synthesised this from a `REFERENCES` clause rather
188    /// than reading it from a `CREATE TRIGGER`.
189    ///
190    /// **Read by `DROP TABLE` (task-1979, F6).** Dropping a table with foreign
191    /// keys on runs an implicit `DELETE FROM` first, so the keys that reference
192    /// it are enforced - and SQLite's rule is that the implicit delete fires no
193    /// triggers of its own while still performing every foreign key action. A
194    /// delete bound for that purpose keeps the triggers this flag marks and
195    /// drops the rest.
196    pub foreign_key: bool,
197    /// Whether the foreign key this enforces has one table as both its child
198    /// and its parent.
199    ///
200    /// **Also read by `DROP TABLE` (task-1979, F6).** The implicit delete keeps
201    /// the foreign key triggers and drops this one, because emptying a table
202    /// cannot leave a row of that same table pointing at nothing - see
203    /// `ForeignKeyTrigger::self_referencing`, which is where the value comes
204    /// from. Always false on a trigger the schema wrote.
205    pub self_referencing: bool,
206}
207
208/// A bound `INSERT`.
209#[derive(Clone, Debug, PartialEq)]
210pub struct BoundInsert {
211    /// The table being written.
212    pub table: TableInfo,
213    /// The statement-wide number of the FROM term being written.
214    ///
215    /// It used to be implicitly zero, because a DML statement had exactly one
216    /// source. A trigger body is compiled into the statement that fires it, so
217    /// its target takes the next number after the firing statement's - and a
218    /// compiler that assumed zero read the wrong cursor for every fire after
219    /// the first.
220    pub target_source: usize,
221    /// Where each table column's value comes from, in column order.
222    pub columns: Vec<ColumnSource>,
223    /// Where the rowid comes from, when the statement supplies one.
224    pub rowid: Option<ColumnSource>,
225    /// Which value of the supplied row is the rowid, when the statement named
226    /// it outright.
227    ///
228    /// `INSERT INTO t(rowid, a) VALUES (7, 'x')` is legal on any rowid table,
229    /// including one with no `INTEGER PRIMARY KEY` to alias it and including a
230    /// virtual table. It is recorded separately from `rowid` because it is not
231    /// a column: nothing writes it into the record.
232    pub named_rowid: Option<usize>,
233    /// The rows.
234    pub source: BoundInsertSource,
235    /// How many values each source row supplies.
236    pub arity: usize,
237    /// The statement's conflict algorithm, when it wrote one.
238    pub on_conflict: Option<ConflictAction>,
239    /// The table's `CHECK` constraints.
240    pub checks: Vec<BoundCheck>,
241    /// The `DEFAULT`s a `REPLACE` may stand in for a NULL, by column.
242    pub not_null_defaults: Vec<BoundDefault>,
243    /// The expressions the table's partial and expression indexes need.
244    pub index_exprs: Vec<BoundIndexExprs>,
245    /// The `ON CONFLICT ... DO UPDATE` clause, when there is one.
246    pub upsert: Vec<BoundUpsert>,
247    /// `sqlite_sequence`'s root page, when the target is `AUTOINCREMENT`.
248    ///
249    /// Resolved here rather than in the compiler because it is a fact about the
250    /// catalog, and the catalog is what the binder holds. It is zero for every
251    /// other table, which is also what it reads as before the first
252    /// `AUTOINCREMENT` table in a database is created.
253    pub sequence_root: u32,
254    /// The `RETURNING` columns.
255    pub returning: Vec<BoundResultColumn>,
256    /// The triggers this write fires, in schema order.
257    pub triggers: Vec<BoundTrigger>,
258    /// The foreign-key actions a `REPLACE` fires for the row it removes.
259    ///
260    /// A `REPLACE` that deletes a row to make room for another is a delete,
261    /// and the keys pointing at that row have to be told. Written `DELETE`
262    /// triggers are *not* fired - that is SQLite's rule with its default
263    /// `recursive_triggers = off` - so these are only the ones a key implies.
264    pub replace_triggers: Vec<BoundTrigger>,
265}
266
267/// A bound `ON CONFLICT ... DO UPDATE` clause.
268#[derive(Clone, Debug, PartialEq)]
269pub struct BoundUpsert {
270    /// The conflict target columns, when written; empty means any constraint.
271    ///
272    /// Sorted, because a conflict target names a *set* of columns and
273    /// `ON CONFLICT(a,b)` and `ON CONFLICT(b,a)` name the same one. Matching
274    /// them against an index's columns is a set comparison, and sorting here
275    /// is what makes it one comparison rather than a search per column.
276    pub target: Vec<u16>,
277    /// The assignments, or empty for `DO NOTHING`.
278    pub assignments: Vec<BoundAssignment>,
279    /// Whether the action is `DO UPDATE`.
280    pub do_update: bool,
281    /// The `WHERE` on the `DO UPDATE`.
282    pub filter: Option<BoundExpr>,
283}
284
285/// One `SET` assignment.
286#[derive(Clone, Debug, PartialEq)]
287pub struct BoundAssignment {
288    /// The column being assigned, as a declared position.
289    pub column: u16,
290    /// Whether the assignment names the row's own rowid rather than a declared
291    /// column, in which case `column` says nothing.
292    ///
293    /// **`UPDATE t SET rowid = 100` was `no such column: rowid` (task-1979,
294    /// F9).** An assignment target was looked up with `column_position`, which
295    /// only knows the columns the table declares, and a table with no INTEGER
296    /// PRIMARY KEY declares none for its rowid. SQLite accepts all three
297    /// spellings of the rowid on either kind of table and moves the row to the
298    /// new key.
299    pub rowid: bool,
300    /// The new value.
301    pub value: BoundExpr,
302}
303
304/// A bound `UPDATE`.
305#[derive(Clone, Debug, PartialEq)]
306pub struct BoundUpdate {
307    /// The table being written.
308    pub table: TableInfo,
309    /// The statement-wide number of the FROM term being written.
310    ///
311    /// It used to be implicitly zero, because a DML statement had exactly one
312    /// source. A trigger body is compiled into the statement that fires it, so
313    /// its target takes the next number after the firing statement's - and a
314    /// compiler that assumed zero read the wrong cursor for every fire after
315    /// the first.
316    pub source: usize,
317    /// The extra FROM terms of an `UPDATE ... FROM`, in written order.
318    ///
319    /// **The rows being updated come from a join.** `UPDATE t SET v = s.v FROM s
320    /// WHERE s.a = t.a` is the shape a migration writes to copy a column across
321    /// tables, and the values it assigns are not expressions over the target
322    /// row: they read a *different* row, one the join found. So the query that
323    /// finds the keys carries these terms too, and projects the assigned values
324    /// beside the key; see [`BoundUpdate::from`], which is this field.
325    ///
326    /// Empty for every ordinary `UPDATE`, which is what keeps the wider row off
327    /// the path the gate's `txn.large` measures.
328    pub from: Vec<crate::bind::BoundSource>,
329    /// The assignments, in table column order with duplicates already refused.
330    pub assignments: Vec<BoundAssignment>,
331    /// The `STORED` generated columns, recomputed after the assignments.
332    ///
333    /// **A stored generated column is part of the row, so a row that is
334    /// rewritten rewrites it (task-1913).** It is never named in a `SET`, so
335    /// an `UPDATE` used to leave whatever was written when the row was
336    /// inserted: `c GENERATED ALWAYS AS (a + 1) STORED` still read 2 after
337    /// `UPDATE g SET a = 5`, where SQLite reads 6. The wrong value is on the
338    /// disk rather than in an answer, so a later read of the same file is
339    /// wrong too, and an index on the column indexes the stale value.
340    ///
341    /// A `VIRTUAL` column is not here: it has no slot in the record and is
342    /// computed when it is read, which is why only this half needed fixing.
343    ///
344    /// These are evaluated against the row *after* the assignments, which is
345    /// the one difference from [`BoundUpdate::assignments`] - those read the
346    /// before image so `SET a = b, b = a` swaps.
347    pub generated: Vec<BoundAssignment>,
348    /// The `WHERE` clause.
349    pub filter: Option<BoundExpr>,
350    /// The statement's conflict algorithm, when it wrote one.
351    pub on_conflict: Option<ConflictAction>,
352    /// The table's `CHECK` constraints.
353    pub checks: Vec<BoundCheck>,
354    /// The `DEFAULT`s a `REPLACE` may stand in for a NULL, by column.
355    pub not_null_defaults: Vec<BoundDefault>,
356    /// The expressions the table's partial and expression indexes need.
357    pub index_exprs: Vec<BoundIndexExprs>,
358    /// `INDEXED BY` or `NOT INDEXED` on the target, which the query that finds
359    /// the rows to change obeys; `inillucent_exec::dml::hint_target` puts it there.
360    pub index_hint: crate::bind::IndexChoice,
361    /// The `RETURNING` columns.
362    pub returning: Vec<BoundResultColumn>,
363    /// The `ORDER BY` that decides which rows a `LIMIT` keeps.
364    ///
365    /// Empty unless the statement wrote one, and then always with a `LIMIT`,
366    /// because the binder refuses an order with nothing to limit. It goes onto
367    /// the query that finds the rows to change, which is where SQLite puts it
368    /// too: a limited write is `WHERE rowid IN (SELECT rowid ... ORDER BY ...
369    /// LIMIT ...)` there.
370    pub order_by: Vec<BoundOrderTerm>,
371    /// The `LIMIT`.
372    pub limit: Option<BoundExpr>,
373    /// The `OFFSET`.
374    pub offset: Option<BoundExpr>,
375    /// The triggers this write fires, in schema order.
376    pub triggers: Vec<BoundTrigger>,
377    /// The rows to fire an `INSTEAD OF` trigger for, when the target is a view.
378    ///
379    /// A view has no rows of its own, so `OLD` has to come from running the
380    /// view. This is that query, with the statement's `WHERE` on it and one
381    /// result column per view column.
382    pub view_rows: Option<Box<BoundSelect>>,
383}
384
385/// A bound `DELETE`.
386#[derive(Clone, Debug, PartialEq)]
387pub struct BoundDelete {
388    /// The table being written.
389    pub table: TableInfo,
390    /// The expressions the table's partial and expression indexes need.
391    ///
392    /// A delete needs them too: an entry only comes out of a partial index if
393    /// the row was in it, and a key the index computed has to be recomputed to
394    /// be found.
395    pub index_exprs: Vec<BoundIndexExprs>,
396    /// `INDEXED BY` or `NOT INDEXED` on the target, as on [`BoundUpdate`].
397    pub index_hint: crate::bind::IndexChoice,
398    /// The statement-wide number of the FROM term being written.
399    ///
400    /// It used to be implicitly zero, because a DML statement had exactly one
401    /// source. A trigger body is compiled into the statement that fires it, so
402    /// its target takes the next number after the firing statement's - and a
403    /// compiler that assumed zero read the wrong cursor for every fire after
404    /// the first.
405    pub source: usize,
406    /// The `WHERE` clause.
407    pub filter: Option<BoundExpr>,
408    /// The `RETURNING` columns.
409    pub returning: Vec<BoundResultColumn>,
410    /// The `ORDER BY` that decides which rows a `LIMIT` keeps.
411    ///
412    /// Empty unless the statement wrote one, and then always with a `LIMIT`,
413    /// because the binder refuses an order with nothing to limit. It goes onto
414    /// the query that finds the rows to change, which is where SQLite puts it
415    /// too: a limited write is `WHERE rowid IN (SELECT rowid ... ORDER BY ...
416    /// LIMIT ...)` there.
417    pub order_by: Vec<BoundOrderTerm>,
418    /// The `LIMIT`.
419    pub limit: Option<BoundExpr>,
420    /// The `OFFSET`.
421    pub offset: Option<BoundExpr>,
422    /// The triggers this write fires, in schema order.
423    pub triggers: Vec<BoundTrigger>,
424    /// The rows to fire an `INSTEAD OF` trigger for, when the target is a view.
425    pub view_rows: Option<Box<BoundSelect>>,
426}
427
428/// Reports whether an `INSERT` can resolve a conflict by deleting a row.
429///
430/// Either the statement said so, or one of the table's own constraints did.
431/// It is asked before the delete's keys are bound, because binding them costs
432/// a parse and a bind each and the answer is no for almost every insert.
433fn can_replace(table: &TableInfo, statement: Option<ConflictAction>) -> bool {
434    if statement == Some(ConflictAction::Replace) {
435        return true;
436    }
437    table
438        .indexes
439        .iter()
440        .any(|index| index.conflict == Some(ConflictAction::Replace))
441        || table.columns.iter().any(|column| {
442            column.not_null_conflict == Some(ConflictAction::Replace)
443                || column.primary_key_conflict == Some(ConflictAction::Replace)
444        })
445}
446
447/// Reports whether an unusable key's fault is one this write has to report.
448///
449/// A child's write reports a missing parent; a parent's write reports a
450/// mismatch. A statement that touches neither side of the broken key does not
451/// have to care, which is why the fault is carried rather than raised when the
452/// schema was read.
453fn fault_applies(
454    planned: &crate::catalog_view::ForeignKeyTrigger,
455    event: &TriggerEventInfo,
456) -> bool {
457    match event {
458        TriggerEventInfo::Insert => planned.is_check,
459        TriggerEventInfo::Delete => !planned.is_check,
460        TriggerEventInfo::Update(_) => true,
461    }
462}
463
464/// Marks a synthesised body's aborts as the foreign key's rather than a
465/// trigger's.
466///
467/// The generated text says `RAISE(ABORT, ...)` because that is what a person
468/// would have written, and what a person writes reports
469/// `SQLITE_CONSTRAINT_TRIGGER`. A foreign key reports its own code, and the
470/// only difference between the two is which constraint asked - so it is set
471/// here, on the bodies this binder generated, and nowhere else.
472fn report_as_foreign_key(trigger: &mut BoundTrigger) {
473    trigger.foreign_key = true;
474    mark_raises(trigger, true);
475}
476
477/// Sets which code every `RAISE` in a synthesised body reports.
478///
479/// **`ON DELETE RESTRICT` and `ON UPDATE RESTRICT` report the trigger's
480/// code.** SQLite enforces RESTRICT with a trigger program and reports
481/// `SQLITE_CONSTRAINT_TRIGGER` (1811) for it, while a `NO ACTION` key, which
482/// it checks with a counter, reports `SQLITE_CONSTRAINT_FOREIGNKEY` (787).
483/// The message is the same for both.
484///
485/// @param trigger - the bound body
486/// @param foreign_key - whether its aborts report the foreign key's code
487fn mark_raises(trigger: &mut BoundTrigger, foreign_key: bool) {
488    for statement in &mut trigger.body {
489        let BoundTriggerStatement::Select(select) = statement else {
490            continue;
491        };
492        for column in &mut select.columns {
493            if let BoundExpr::Raise {
494                foreign_key: marked,
495                ..
496            } = &mut column.expr
497            {
498                *marked = foreign_key;
499            }
500        }
501    }
502}
503
504/// Returns whether a view has an `INSTEAD OF` trigger for one event.
505fn has_instead_of(table: &TableInfo, event: &TriggerEventInfo) -> bool {
506    table
507        .triggers
508        .iter()
509        .any(|trigger| trigger.time == ast::TriggerTime::InsteadOf && trigger.fires_for(event, &[]))
510}
511
512/// The target position that stands for the rowid rather than a column.
513///
514/// A table cannot have this many columns - SQLite's limit is two thousand - so
515/// there is no position it can collide with, and one sentinel is cheaper than
516/// a parallel `Option` threaded through every target list.
517const ROWID_TARGET: u16 = u16::MAX;
518
519/// Returns whether a name is one of the rowid's three spellings.
520fn is_rowid_name(folded: &[u8]) -> bool {
521    matches!(folded, b"rowid" | b"oid" | b"_rowid_")
522}
523
524/// How deep one write may drive triggers firing other triggers.
525///
526/// SQLite's own limit is `SQLITE_MAX_TRIGGER_DEPTH`, enforced when the frame is
527/// pushed. Trigger bodies are inlined here rather than run as frames, so the
528/// same limit is enforced where the inlining happens - and it has to be, or a
529/// schema in which two triggers write each other's tables would compile until
530/// the compiler ran out of memory.
531///
532/// **This is one number now, and it is the one `.limit` reports.** There used
533/// to be two constants of this name: this one at 32, which was the number
534/// actually enforced, and `inillucent-exec`'s at 1000, checked at run time over
535/// a tree the binder had already capped at 32 - so that check could never fire.
536/// `crates/inillucent-base/manifests/limits.toml` advertised 1000 and
537/// `inillucent diagnose` printed 1000, and a chain of forty distinct triggers
538/// that the oracle ran was refused here (task-1946, H3). The binder reads
539/// `Limit::TriggerDepth` from the connection now, which `.limit trigger_depth`
540/// and the driver both set; this constant is what a binder built without limits
541/// falls back to, and it is the manifest's default.
542pub const MAX_TRIGGER_DEPTH: usize = 1000;
543
544/// How deep one chain of foreign-key actions may go.
545///
546/// A cascade reaches this only when the keys form a cycle, which in practice
547/// means a table whose parent column points at itself. SQLite's own limit is a
548/// run-time recursion depth; this one is a compile-time inlining depth, and it
549/// is smaller for that reason.
550pub const MAX_FOREIGN_KEY_DEPTH: usize = 64;
551
552/// How many foreign-key action bodies one statement may inline in total.
553///
554/// The depth limit alone is not enough: a table with three keys that all cycle
555/// would inline three bodies per level, so the limit that matters is the total.
556/// A chain, which is what a self-referencing tree produces, spends one per
557/// level and reaches the depth limit first.
558pub const MAX_FOREIGN_KEY_STATEMENTS: usize = 256;
559
560impl<'a> Binder<'a> {
561    /// Binds an `INSERT` or `REPLACE`.
562    pub fn bind_insert(&mut self, insert: &ast::Insert) -> Result<BoundInsert, ParseError> {
563        // **A `WITH` on a DML statement is the same `WITH` a `SELECT` has.** The
564        // CTEs are in scope for the whole statement - the source query of an
565        // `INSERT`, the `WHERE` of an `UPDATE` or `DELETE` - and the binder's
566        // CTE stack already handles nesting, so pushing them here is all it
567        // takes. They were refused rather than bound, which is what a migration
568        // script written for SQLite hits first.
569        let pushed = self.push_ctes(&insert.with)?;
570        let bound = self.bind_insert_body(insert);
571        if pushed {
572            self.pop_ctes();
573        }
574        bound
575    }
576
577    /// Binds an `INSERT` with its CTEs already in scope.
578    fn bind_insert_body(&mut self, insert: &ast::Insert) -> Result<BoundInsert, ParseError> {
579        let table = self.writable_target(
580            insert.database,
581            insert.table,
582            Span::default(),
583            &TriggerEventInfo::Insert,
584        )?;
585        let alias = match insert.alias {
586            Some(alias) => self.ast.text(alias).to_vec(),
587            None => table.name.clone(),
588        };
589        let target_source = self.push_write_source(table.clone(), alias);
590        // `DEFAULT VALUES` supplies nothing, so every column takes its default
591        // - which is what an empty target list means here. The grammar does
592        // not allow a column list with it, so there is none to honour.
593        let targets = match insert.source {
594            ast::InsertSource::DefaultValues => Vec::new(),
595            ast::InsertSource::Select(_) => self.insert_targets(&table, &insert.columns)?,
596        };
597        let (source, arity) = self.bind_insert_source(&insert.source, &table, &targets)?;
598        if arity != targets.len() {
599            return Err(refused(
600                format!("{} values for {} columns", arity, targets.len()),
601                Span::default(),
602            ));
603        }
604        let (columns, rowid) = self.column_sources(&table, &targets)?;
605        let named_rowid = targets.iter().position(|target| *target == ROWID_TARGET);
606        let checks = self.bind_checks(&table)?;
607        let not_null_defaults = self.bind_not_null_defaults(&table)?;
608        let index_exprs = self.bind_index_exprs(&table)?;
609        let upsert = self.bind_upsert(&table, insert)?;
610        let returning = self.bind_returning(&insert.returning)?;
611        let mut triggers = self.bind_triggers(&table, TriggerEventInfo::Insert, &[])?;
612        triggers.extend(self.bind_foreign_keys(&table, TriggerEventInfo::Insert, &[])?);
613        let replace_triggers = if can_replace(&table, insert.on_conflict) {
614            self.bind_foreign_keys(&table, TriggerEventInfo::Delete, &[])?
615        } else {
616            Vec::new()
617        };
618        let sequence_root = if table.autoincrement {
619            self.catalog
620                .find_table(None, b"sqlite_sequence")
621                .map_or(0, |sequence| sequence.root)
622        } else {
623            0
624        };
625        Ok(BoundInsert {
626            table,
627            index_exprs,
628            target_source,
629            columns,
630            rowid,
631            named_rowid,
632            source,
633            arity,
634            on_conflict: insert.on_conflict,
635            checks,
636            not_null_defaults,
637            upsert,
638            sequence_root,
639            returning,
640            triggers,
641            replace_triggers,
642        })
643    }
644
645    /// Binds an `UPDATE`.
646    pub fn bind_update(&mut self, update: &ast::Update) -> Result<BoundUpdate, ParseError> {
647        let pushed = self.push_ctes(&update.with)?;
648        let bound = self.bind_update_body(update);
649        if pushed {
650            self.pop_ctes();
651        }
652        bound
653    }
654
655    /// Binds an `UPDATE ... FROM` clause, after the target.
656    ///
657    /// Returns the terms the clause adds and the constraints its table-valued
658    /// functions' arguments became, which belong in the statement's `WHERE`.
659    ///
660    /// **A table-valued function's arguments are constraints on its hidden
661    /// columns**, which `bind_table_arguments` leaves for the statement's
662    /// `WHERE`. A `SELECT` adds them there; the `UPDATE` did not, so `UPDATE
663    /// todo SET position = j.key FROM json_each('[3,1,2]') AS j WHERE todo.id =
664    /// j.value` ran `json_each` with no document, found no rows and reported
665    /// success with nothing changed.
666    ///
667    /// **The terms this block owns, not every source bound since.** A derived
668    /// table binds its own inner terms into the same list, and taking
669    /// everything bound after the target made them top level terms of the
670    /// `UPDATE` as well: `FROM (SELECT id, pos FROM ord) AS p` joined `ord`
671    /// again, beside `p`, so every row was found once per row of `ord` and the
672    /// statement reported 9 changes for 3.
673    ///
674    /// @param from - the clause's terms, in written order
675    fn bind_update_from(
676        &mut self,
677        from: &[ast::FromTermId],
678    ) -> Result<(Vec<crate::bind::BoundSource>, Vec<BoundExpr>), ParseError> {
679        let before = self.sources.len();
680        for term in from {
681            self.bind_from_term(*term)?;
682        }
683        self.desugar_join_constraints(from)?;
684        let arguments = core::mem::take(&mut self.pending_constraints);
685        let joined: Vec<crate::bind::BoundSource> = self
686            .scope()
687            .iter()
688            .filter(|id| **id >= before)
689            .filter_map(|id| self.sources.get(*id).cloned())
690            .collect();
691        Ok((joined, arguments))
692    }
693
694    /// Binds an `UPDATE` with its CTEs already in scope.
695    fn bind_update_body(&mut self, update: &ast::Update) -> Result<BoundUpdate, ParseError> {
696        if let Some(refusal) = order_without_limit(update.limited_at, update.limit, "UPDATE") {
697            return Err(refusal);
698        }
699        let (table, source) =
700            self.write_target_from_term(update.target, &TriggerEventInfo::Update(Vec::new()))?;
701        refuse_module_returning(&table, &update.returning, "UPDATE")?;
702        // **The `FROM` terms are bound after the target**, so the target keeps
703        // the lowest source number and every reference to an unqualified column
704        // resolves to it first - which is SQLite's rule and the reason
705        // `UPDATE t SET v = v + 1 FROM s` means the target's `v`.
706        let (joined, arguments) = self.bind_update_from(&update.from)?;
707        let mut assignments = Vec::new();
708        for (names, value) in &update.assignments {
709            let values = self.assigned_values(names, *value)?;
710            for (name, bound) in names.iter().zip(values) {
711                let folded = self.ast.folded(*name).to_vec();
712                // `rowid`, `oid` and `_rowid_` name the row's key rather than a
713                // declared column, unless the table declares a column by one of
714                // those names - which is what `is_rowid_name` decides.
715                if table.is_rowid_name(&folded) {
716                    if assignments.iter().any(|held: &BoundAssignment| held.rowid) {
717                        return Err(refused(
718                            format!(
719                                "column {} is assigned twice",
720                                String::from_utf8_lossy(self.ast.text(*name))
721                            ),
722                            Span::default(),
723                        ));
724                    }
725                    assignments.push(BoundAssignment {
726                        column: 0,
727                        rowid: true,
728                        value: bound.clone(),
729                    });
730                    continue;
731                }
732                let Some(position) = table.column_position(&folded) else {
733                    return Err(no_such_column(self.ast.text(*name), Span::default()));
734                };
735                // **An assignment to a generated column is refused, not
736                // ignored (task-1913).** SQLite answers `cannot UPDATE
737                // generated column "c"`; this accepted the statement, reported
738                // it as a success, and wrote nothing the caller asked for -
739                // either the record took the value and the column stopped
740                // agreeing with its own expression, or the recompute above put
741                // it back and the assignment was silently dropped. `INSERT`
742                // already refused the same thing.
743                self.refuse_generated(&table, position, "UPDATE", Span::default())?;
744                if assignments
745                    .iter()
746                    .any(|existing: &BoundAssignment| existing.column == position)
747                {
748                    return Err(refused(
749                        format!(
750                            "column {} is assigned twice",
751                            String::from_utf8_lossy(self.ast.text(*name))
752                        ),
753                        Span::default(),
754                    ));
755                }
756                assignments.push(BoundAssignment {
757                    column: position,
758                    rowid: false,
759                    value: bound.clone(),
760                });
761            }
762        }
763        // The rowid assignment sorts with the declared columns rather than
764        // ahead of them, because `column` says nothing for it and the order
765        // only has to be stable.
766        assignments.sort_by_key(|assignment| (assignment.rowid, assignment.column));
767        let mut filter = match update.filter {
768            Some(expr) => Some(self.bind_expr(expr)?),
769            None => None,
770        };
771        for constraint in arguments {
772            filter = Some(match filter.take() {
773                Some(existing) => BoundExpr::And(Box::new(existing), Box::new(constraint)),
774                None => constraint,
775            });
776        }
777        // **The schema's expressions see the target and nothing else.** A
778        // partial index's `WHERE a IS NOT NULL`, a `CHECK` and a generated
779        // column name the target's own columns. With the `FROM` terms still in
780        // scope, a `FROM` table that also had a column `a` made the index's `a`
781        // ambiguous, and `UPDATE t ... FROM r` was refused where SQLite runs it.
782        let saved_scopes = core::mem::replace(&mut self.scopes, vec![vec![source]]);
783        let schema = self.bind_update_schema(&table);
784        self.scopes = saved_scopes;
785        let (generated, checks, not_null_defaults, index_exprs) = schema?;
786        // `RETURNING` reads the row written and nothing else: SQLite does not
787        // let a `FROM` term take part in it, so an unqualified `k` that both
788        // the target and a `FROM` term have is the target's.
789        let saved_scopes = core::mem::replace(&mut self.scopes, vec![vec![source]]);
790        let returning = self.bind_returning(&update.returning);
791        self.scopes = saved_scopes;
792        let returning = returning?;
793        // Bound as expressions, the way an aggregate's own `ORDER BY` is: a
794        // write has no result columns, so a bare integer names no ordinal.
795        let order_by = self.bind_aggregate_order(&update.order_by)?;
796        let limit = match update.limit {
797            Some(expr) => Some(self.bind_expr(expr)?),
798            None => None,
799        };
800        let offset = match update.offset {
801            Some(expr) => Some(self.bind_expr(expr)?),
802            None => None,
803        };
804        // The rowid is not a declared column, so no `UPDATE OF` trigger and no
805        // foreign key can be keyed on it and it contributes no name here.
806        let changed: Vec<Vec<u8>> = assignments
807            .iter()
808            .filter(|assignment| !assignment.rowid)
809            .filter_map(|assignment| table.column(assignment.column))
810            .map(|column| column.folded.clone())
811            .collect();
812        let mut triggers =
813            self.bind_triggers(&table, TriggerEventInfo::Update(Vec::new()), &changed)?;
814        triggers.extend(self.bind_foreign_keys(
815            &table,
816            TriggerEventInfo::Update(Vec::new()),
817            &changed,
818        )?);
819        let view_rows = self
820            .view_rows(&table, filter.clone())
821            .map(|rows| limit_view_rows(rows, &order_by, &limit, &offset));
822        let index_hint = self.write_hint(source, &index_exprs, filter.as_ref(), &joined)?;
823        Ok(BoundUpdate {
824            table,
825            index_exprs,
826            index_hint,
827            source,
828            from: joined,
829            assignments,
830            generated,
831            filter,
832            on_conflict: update.on_conflict,
833            checks,
834            not_null_defaults,
835            returning,
836            order_by,
837            limit,
838            offset,
839            triggers,
840            view_rows,
841        })
842    }
843
844    /// Binds a `DELETE`.
845    pub fn bind_delete(&mut self, delete: &ast::Delete) -> Result<BoundDelete, ParseError> {
846        let pushed = self.push_ctes(&delete.with)?;
847        let bound = self.bind_delete_body(delete);
848        if pushed {
849            self.pop_ctes();
850        }
851        bound
852    }
853
854    /// Binds a `DELETE` with its CTEs already in scope.
855    fn bind_delete_body(&mut self, delete: &ast::Delete) -> Result<BoundDelete, ParseError> {
856        if let Some(refusal) = order_without_limit(delete.limited_at, delete.limit, "DELETE") {
857            return Err(refusal);
858        }
859        let (table, source) =
860            self.write_target_from_term(delete.target, &TriggerEventInfo::Delete)?;
861        refuse_module_returning(&table, &delete.returning, "DELETE")?;
862        let index_exprs = self.bind_index_exprs(&table)?;
863        let filter = match delete.filter {
864            Some(expr) => Some(self.bind_expr(expr)?),
865            None => None,
866        };
867        let returning = self.bind_returning(&delete.returning)?;
868        let order_by = self.bind_aggregate_order(&delete.order_by)?;
869        let limit = match delete.limit {
870            Some(expr) => Some(self.bind_expr(expr)?),
871            None => None,
872        };
873        let offset = match delete.offset {
874            Some(expr) => Some(self.bind_expr(expr)?),
875            None => None,
876        };
877        let mut triggers = self.bind_triggers(&table, TriggerEventInfo::Delete, &[])?;
878        triggers.extend(self.bind_foreign_keys(&table, TriggerEventInfo::Delete, &[])?);
879        let view_rows = self
880            .view_rows(&table, filter.clone())
881            .map(|rows| limit_view_rows(rows, &order_by, &limit, &offset));
882        let index_hint = self.write_hint(source, &index_exprs, filter.as_ref(), &[])?;
883        Ok(BoundDelete {
884            table,
885            index_exprs,
886            index_hint,
887            source,
888            filter,
889            returning,
890            order_by,
891            limit,
892            offset,
893            triggers,
894            view_rows,
895        })
896    }
897
898    /// Binds the triggers one write fires, bodies and all.
899    ///
900    /// The bodies are bound here, into the same binder, so their FROM terms take
901    /// statement-wide source numbers alongside the write's own. That is what
902    /// lets the compiler inline them: a trigger body is not a separate program
903    /// with a separate cursor space, it is more of this statement.
904    ///
905    /// A trigger already being bound is skipped rather than bound again, which
906    /// is SQLite's behaviour with its default `recursive_triggers = off` and is
907    /// also the only reason inlining terminates.
908    ///
909    /// **Walked newest first.** `live.triggers` is in the order
910    /// `inillucent_catalog::paged::tables_from_entries` appended them while
911    /// reading `sqlite_schema` - the order the triggers were created in - and
912    /// SQLite fires two triggers of the same timing and event in the opposite
913    /// order: it keeps each table's trigger list with the most recently
914    /// created one first, so that one fires first.
915    /// `dml_differential.rs`'s `row_triggers_match_sqlite` has two `AFTER
916    /// INSERT` triggers on one table - `t_ai`, created first, and `t_high`,
917    /// created after it - and the pinned reference fires `t_high` before
918    /// `t_ai` on every insert. Reversing the walk here, once, at the one place
919    /// that reads `live.triggers` into a statement's own trigger list, is
920    /// enough: nothing downstream reorders it again.
921    fn bind_triggers(
922        &mut self,
923        table: &TableInfo,
924        event: TriggerEventInfo,
925        changed: &[Vec<u8>],
926    ) -> Result<Vec<BoundTrigger>, ParseError> {
927        // The catalog reference is copied out of `self` first: the trigger's
928        // arena has to outlive the binder for the body to be bound in place,
929        // and a borrow taken through `&self` would end at the first `&mut self`.
930        let catalog = self.catalog;
931        let database = catalog.database_name(table.database).to_vec();
932        let Some(live) = catalog.find_table(Some(database.as_slice()), &table.folded) else {
933            return Ok(Vec::new());
934        };
935        let (old, new) = match event {
936            TriggerEventInfo::Insert => (false, true),
937            TriggerEventInfo::Delete => (true, false),
938            TriggerEventInfo::Update(_) => (true, true),
939        };
940        let mut bound = Vec::new();
941        for trigger in live.triggers.iter().rev() {
942            if !trigger.fires_for(&event, changed) {
943                continue;
944            }
945            if self.firing.contains(&trigger.folded) {
946                continue;
947            }
948            if self.firing.len() >= self.trigger_depth {
949                // The number is in the message because a settable limit that
950                // refuses without saying what it was leaves a reader guessing
951                // between the default and whatever `.limit` last set.
952                return Err(refused(
953                    format!(
954                        "too many levels of trigger recursion: the limit is {}",
955                        self.trigger_depth
956                    ),
957                    Span::default(),
958                ));
959            }
960            self.firing.push(trigger.folded.clone());
961            let saved_ast = self.ast;
962            let saved_scopes = core::mem::take(&mut self.scopes);
963            let saved_aliases = self.row_aliases.take();
964            let saved_target = self.view_target.take();
965            // A trigger body is schema text: the statements in it were written
966            // by whoever wrote the file, and they run because a write happened
967            // rather than because anybody submitted them.
968            let saved_site = self.call_site;
969            self.call_site = crate::function::CallSite::Schema;
970            self.ast = &trigger.ast;
971            self.row_aliases = Some(crate::bind::RowAliases {
972                table: table.clone(),
973                old,
974                new,
975            });
976            let result = self.bind_trigger_body(trigger, table);
977            self.call_site = saved_site;
978            self.ast = saved_ast;
979            self.scopes = saved_scopes;
980            self.row_aliases = saved_aliases;
981            self.view_target = saved_target;
982            self.firing.pop();
983            bound.push(result?);
984        }
985        Ok(bound)
986    }
987
988    /// Binds the triggers this write's foreign keys imply.
989    ///
990    /// The triggers themselves were generated when the schema was read - both
991    /// directions of every key, since nothing in the file records the reverse
992    /// one. What is decided here is which of them apply: whether keys are
993    /// enforced at all, whether a check waits for the commit, and whether this
994    /// particular write touches the columns a check is about.
995    fn bind_foreign_keys(
996        &mut self,
997        table: &TableInfo,
998        event: TriggerEventInfo,
999        changed: &[Vec<u8>],
1000    ) -> Result<Vec<BoundTrigger>, ParseError> {
1001        if !self.foreign_keys || table.kind != TableKind::Table {
1002            return Ok(Vec::new());
1003        }
1004        let catalog = self.catalog;
1005        let database = catalog.database_name(table.database).to_vec();
1006        let Some(live) = catalog.find_table(Some(database.as_slice()), &table.folded) else {
1007            return Ok(Vec::new());
1008        };
1009        let mut bound = Vec::new();
1010        for planned in &live.foreign_key_triggers {
1011            if planned.is_check && (planned.deferred || self.defer_foreign_keys) {
1012                continue;
1013            }
1014            let Some(trigger) = planned.trigger.as_ref() else {
1015                if fault_applies(planned, &event) {
1016                    return Err(crate::bind::schema_refused(
1017                        String::from_utf8_lossy(&planned.fault).into_owned(),
1018                        Span::default(),
1019                    ));
1020                }
1021                continue;
1022            };
1023            if !trigger.fires_for(&event, changed) {
1024                continue;
1025            }
1026            if self.firing_foreign_keys.contains(&trigger.folded) {
1027                continue;
1028            }
1029            let mut one = self.bind_foreign_key_trigger(table, trigger, &event)?;
1030            one.self_referencing = planned.self_referencing;
1031            // A parent action that fires BEFORE the write is a RESTRICT, which
1032            // is the only parent action `foreign_key::parent_action` times so.
1033            if !planned.is_check && trigger.time == ast::TriggerTime::Before {
1034                mark_raises(&mut one, false);
1035            }
1036            // **`PRAGMA defer_foreign_keys` defers the parent's side too.** A
1037            // parent action whose body only checks - `RESTRICT`, and `NO
1038            // ACTION` - is dropped while it is on, and the commit's check of
1039            // every key takes its place: SQLite's `fkActionTrigger` builds no
1040            // `RESTRICT` program under `SQLITE_DeferFKs`, and its `NO ACTION`
1041            // check adds to the deferred counter. A `DELETE` a `RESTRICT` key
1042            // refused inside `BEGIN` therefore runs there, as it does in
1043            // SQLite. The actions that change rows still run.
1044            if self.defer_foreign_keys
1045                && !planned.is_check
1046                && one
1047                    .body
1048                    .iter()
1049                    .all(|statement| matches!(statement, BoundTriggerStatement::Select(_)))
1050            {
1051                continue;
1052            }
1053            bound.push(one);
1054        }
1055        Ok(bound)
1056    }
1057
1058    /// Binds one synthesised trigger, inside the recursion budget.
1059    ///
1060    /// The budget is spent here rather than where the trigger was generated,
1061    /// because what a cascade costs is the *bound* body: one copy per level it
1062    /// can reach, and it can reach itself only when the keys form a cycle.
1063    fn bind_foreign_key_trigger(
1064        &mut self,
1065        table: &TableInfo,
1066        trigger: &'a TriggerInfo,
1067        event: &TriggerEventInfo,
1068    ) -> Result<BoundTrigger, ParseError> {
1069        if self.foreign_key_depth >= MAX_FOREIGN_KEY_DEPTH || self.foreign_key_budget == 0 {
1070            return Err(refused(
1071                "too many levels of foreign key recursion",
1072                Span::default(),
1073            ));
1074        }
1075        self.foreign_key_depth = self.foreign_key_depth.saturating_add(1);
1076        self.foreign_key_budget = self.foreign_key_budget.saturating_sub(1);
1077        self.firing_foreign_keys.push(trigger.folded.clone());
1078        let (old, new) = match event {
1079            TriggerEventInfo::Insert => (false, true),
1080            TriggerEventInfo::Delete => (true, false),
1081            TriggerEventInfo::Update(_) => (true, true),
1082        };
1083        let saved_ast = self.ast;
1084        let saved_scopes = core::mem::take(&mut self.scopes);
1085        let saved_aliases = self.row_aliases.take();
1086        let saved_target = self.view_target.take();
1087        // A synthesised key action is generated from a `REFERENCES` clause the
1088        // schema wrote, so it is schema too - the same site a written trigger
1089        // gets, because the binder turns both into the same text.
1090        let saved_site = self.call_site;
1091        self.call_site = crate::function::CallSite::Schema;
1092        self.ast = &trigger.ast;
1093        self.row_aliases = Some(crate::bind::RowAliases {
1094            table: table.clone(),
1095            old,
1096            new,
1097        });
1098        let result = self.bind_trigger_body(trigger, table);
1099        self.call_site = saved_site;
1100        self.ast = saved_ast;
1101        self.scopes = saved_scopes;
1102        self.row_aliases = saved_aliases;
1103        self.view_target = saved_target;
1104        self.foreign_key_depth = self.foreign_key_depth.saturating_sub(1);
1105        self.firing_foreign_keys.pop();
1106        let mut bound = result?;
1107        report_as_foreign_key(&mut bound);
1108        Ok(bound)
1109    }
1110
1111    /// Binds one trigger's guard and body statements.
1112    fn bind_trigger_body(
1113        &mut self,
1114        trigger: &TriggerInfo,
1115        table: &TableInfo,
1116    ) -> Result<BoundTrigger, ParseError> {
1117        let when = match trigger.when {
1118            Some(expr) => Some(self.bind_expr(expr)?),
1119            None => None,
1120        };
1121        let mut body = Vec::new();
1122        for statement in &trigger.body {
1123            // Each statement gets a fresh scope stack. A body statement's names
1124            // resolve against its own tables and against OLD and NEW, never
1125            // outward into the statement that fired it.
1126            let saved = core::mem::take(&mut self.scopes);
1127            let one = self.bind_trigger_statement(statement);
1128            self.scopes = saved;
1129            body.push(one?);
1130        }
1131        Ok(BoundTrigger {
1132            name: trigger.name.clone(),
1133            table: table.folded.clone(),
1134            time: trigger.time,
1135            when,
1136            body,
1137            foreign_key: false,
1138            self_referencing: false,
1139        })
1140    }
1141
1142    /// Binds one statement of a trigger body.
1143    pub(crate) fn bind_trigger_statement(
1144        &mut self,
1145        statement: &ast::Statement,
1146    ) -> Result<BoundTriggerStatement, ParseError> {
1147        match statement {
1148            ast::Statement::Insert(insert) => {
1149                if !insert.returning.is_empty() {
1150                    return Err(refused(
1151                        "RETURNING is not allowed on a trigger body statement",
1152                        Span::default(),
1153                    ));
1154                }
1155                Ok(BoundTriggerStatement::Insert(Box::new(
1156                    self.bind_insert(insert)?,
1157                )))
1158            }
1159            ast::Statement::Update(update) => {
1160                if !update.returning.is_empty() {
1161                    return Err(refused(
1162                        "RETURNING is not allowed on a trigger body statement",
1163                        Span::default(),
1164                    ));
1165                }
1166                Ok(BoundTriggerStatement::Update(Box::new(
1167                    self.bind_update(update)?,
1168                )))
1169            }
1170            ast::Statement::Delete(delete) => {
1171                if !delete.returning.is_empty() {
1172                    return Err(refused(
1173                        "RETURNING is not allowed on a trigger body statement",
1174                        Span::default(),
1175                    ));
1176                }
1177                Ok(BoundTriggerStatement::Delete(Box::new(
1178                    self.bind_delete(delete)?,
1179                )))
1180            }
1181            ast::Statement::Select(select) => Ok(BoundTriggerStatement::Select(Box::new(
1182                self.bind_select(*select)?,
1183            ))),
1184            _ => Err(unsupported(
1185                "that statement in a trigger body",
1186                Span::default(),
1187            )),
1188        }
1189    }
1190
1191    /// Resolves a write target and refuses the things that cannot be written.
1192    fn writable_target(
1193        &mut self,
1194        database: Option<ast::NameId>,
1195        name: ast::NameId,
1196        span: Span,
1197        event: &TriggerEventInfo,
1198    ) -> Result<TableInfo, ParseError> {
1199        let qualifier = database.map(|id| self.ast.folded(id).to_vec());
1200        let folded = self.ast.folded(name).to_vec();
1201        let Some(table) = self
1202            .catalog
1203            .find_table(qualifier.as_deref(), &folded)
1204            .cloned()
1205        else {
1206            return Err(crate::bind::no_such_table(self.ast.text(name), span));
1207        };
1208        match table.kind {
1209            TableKind::View => {
1210                // A view is writable exactly when it has an `INSTEAD OF`
1211                // trigger for this event: the trigger *is* the write, and the
1212                // view itself is never touched.
1213                if !has_instead_of(&table, event) {
1214                    return Err(unsupported("writing to a view", span));
1215                }
1216                let expanded = self.expanded_view(&table, span)?;
1217                self.record_write_dependency(table.database);
1218                return Ok(expanded);
1219            }
1220            TableKind::Virtual => {
1221                // A module decides whether it can be written; a module that
1222                // cannot refuses the call rather than the statement, because
1223                // "this table is read-only" is the module's fact and not the
1224                // binder's. What the binder still checks is that the table has
1225                // a module at all - a virtual table this build has no module
1226                // for has no columns either, and nothing can be written to it.
1227                if table.columns.is_empty() {
1228                    return Err(unsupported("that virtual table's module", span));
1229                }
1230                self.record_write_dependency(table.database);
1231                return Ok(table);
1232            }
1233            TableKind::Subquery => return Err(unsupported("writing to a subquery", span)),
1234            TableKind::Table => {}
1235        }
1236        if table.folded.starts_with(b"sqlite_")
1237            && !WRITABLE_INTERNAL.contains(&table.folded.as_slice())
1238        {
1239            return Err(unsupported(
1240                "writing to a table whose name begins with sqlite_",
1241                span,
1242            ));
1243        }
1244        self.record_write_dependency(table.database);
1245        Ok(table)
1246    }
1247
1248    /// Resolves the target of an UPDATE or DELETE, which is a FROM term.
1249    fn write_target_from_term(
1250        &mut self,
1251        id: ast::FromTermId,
1252        event: &TriggerEventInfo,
1253    ) -> Result<(TableInfo, usize), ParseError> {
1254        let Some(term) = self.ast.from_term(id) else {
1255            return Err(unsupported("missing target", Span::default()));
1256        };
1257        let ast::FromSource::Table {
1258            database,
1259            name,
1260            indexed_by,
1261            ..
1262        } = term.source
1263        else {
1264            return Err(unsupported("a target that is not a table", term.span));
1265        };
1266        let table = self.writable_target(database, name, term.span, event)?;
1267        // The same rule as a SELECT's: an `INDEXED BY` that names no index of
1268        // the table is refused rather than ignored (task-1979, F7). This path
1269        // has the table in hand rather than a bound source, so it asks the
1270        // table directly.
1271        if let ast::IndexHint::IndexedBy(index) = indexed_by {
1272            let folded = self.ast.folded(index).to_vec();
1273            if !table.indexes.iter().any(|held| held.folded == folded) {
1274                return Err(crate::bind::no_such_index(self.ast.text(index), term.span));
1275            }
1276        }
1277        let alias = match term.alias {
1278            Some(alias) => self.ast.text(alias).to_vec(),
1279            None => table.name.clone(),
1280        };
1281        if table.kind == TableKind::View {
1282            // The view goes in as an ordinary nested query, so the statement's
1283            // WHERE and SET bind against the view's own columns and against the
1284            // term the block producing OLD will iterate. Binding first and
1285            // re-pointing afterwards would be two chances to disagree.
1286            let inner = self.view_query(&table, term.span)?;
1287            let source = BoundSource {
1288                index_hint: crate::bind::IndexChoice::Any,
1289                id: self.sources.len(),
1290                rows: crate::bind::SourceRows::Subquery(Box::new(inner)),
1291                table: std::rc::Rc::new(table.clone()),
1292                alias,
1293                join: ast::JoinKind::Comma,
1294                constraint: None,
1295                suppressed: Vec::new(),
1296                index_exprs: Vec::new(),
1297            };
1298            self.view_target = Some(source.id);
1299            let scope = source.id;
1300            self.sources.push(source);
1301            self.scopes.push(vec![scope]);
1302            return Ok((table, scope));
1303        }
1304        let scope = self.push_write_source(table.clone(), alias);
1305        let choice = self.index_choice(indexed_by);
1306        if let Some(source) = self.sources.get_mut(scope) {
1307            source.index_hint = choice;
1308        }
1309        Ok((table, scope))
1310    }
1311
1312    /// Returns a view's `TableInfo` with the columns its body produces.
1313    ///
1314    /// A view's catalog entry carries no column list - its columns are whatever
1315    /// binding its `SELECT` says they are - so a statement that writes one needs
1316    /// the body bound before `new.column` can resolve to anything at all.
1317    pub(crate) fn expanded_view(
1318        &mut self,
1319        table: &TableInfo,
1320        span: Span,
1321    ) -> Result<TableInfo, ParseError> {
1322        let bound = self.view_query(table, span)?;
1323        let mut expanded = table.clone();
1324        expanded.columns = crate::bind::subquery_columns(&bound, &[]);
1325        Ok(expanded)
1326    }
1327
1328    /// Binds a view's body, out of the arena the catalog snapshot holds.
1329    fn view_query(&mut self, table: &TableInfo, span: Span) -> Result<BoundSelect, ParseError> {
1330        let catalog = self.catalog;
1331        let database = catalog.database_name(table.database).to_vec();
1332        let Some(live) = catalog.find_table(Some(database.as_slice()), &table.folded) else {
1333            return Err(crate::bind::no_such_table(&table.name, span));
1334        };
1335        let Some(body) = live.view.as_ref() else {
1336            return Err(unsupported(
1337                "a view whose definition could not be parsed",
1338                span,
1339            ));
1340        };
1341        let names = body.columns.clone();
1342        let saved_ast = self.ast;
1343        let saved_scopes = core::mem::take(&mut self.scopes);
1344        self.ast = &body.ast;
1345        let bound = self.bind_select(body.select);
1346        self.ast = saved_ast;
1347        self.scopes = saved_scopes;
1348        let mut bound = bound?;
1349        // `CREATE VIEW v (a, b)` renames the body's columns, and those are the
1350        // names `new.a` resolves against.
1351        for (position, name) in names.iter().enumerate() {
1352            if let Some(column) = bound.columns.get_mut(position) {
1353                column.name = name.clone();
1354            }
1355        }
1356        Ok(bound)
1357    }
1358
1359    /// Builds the block whose rows an `INSTEAD OF UPDATE` or `DELETE` fires for.
1360    ///
1361    /// It reads the term `write_target_from_term` already pushed, so the filter
1362    /// handed in here - bound against that same term - needs no adjustment.
1363    fn view_rows(
1364        &mut self,
1365        table: &TableInfo,
1366        filter: Option<BoundExpr>,
1367    ) -> Option<Box<BoundSelect>> {
1368        // The kind is checked before the target is taken. A trigger body's own
1369        // UPDATE binds through here too, and taking first meant the body's
1370        // statement - whose target is an ordinary table - consumed the view
1371        // target belonging to the statement that fired it, which then compiled
1372        // as a write to a view's root page of zero.
1373        if table.kind != TableKind::View {
1374            return None;
1375        }
1376        let id = self.view_target.take()?;
1377        let source = self.sources.get(id)?.clone();
1378        let columns = table
1379            .columns
1380            .iter()
1381            .enumerate()
1382            .map(|(position, column)| BoundResultColumn {
1383                expr: BoundExpr::Column {
1384                    source: id,
1385                    column: position as u16,
1386                    slot: position as u16,
1387                    affinity: column.affinity,
1388                    collation: Collation::from_name(
1389                        core::str::from_utf8(&column.collation).unwrap_or("BINARY"),
1390                    )
1391                    .unwrap_or(Collation::Binary),
1392                },
1393                name: column.name.clone(),
1394                origin: None,
1395                declared_type: column.declared_type.clone(),
1396            })
1397            .collect();
1398        Some(Box::new(crate::bind::block_over(source, filter, columns)))
1399    }
1400
1401    /// Returns the target's index hint, or refuses a write whose `INDEXED BY`
1402    /// index cannot find its rows.
1403    ///
1404    /// The same rule and the same test a `SELECT` gets from
1405    /// `crate::bind::refuse_unanswerable_hints`, asked of the query the write
1406    /// will run to find its rows: the target, any `UPDATE ... FROM` terms, and
1407    /// the statement's `WHERE`. The pinned 3.53.4 shell refuses
1408    /// `DELETE FROM h INDEXED BY h_part WHERE a = 1`, where `h_part` is declared
1409    /// `WHERE c > 3`, with `no query solution`.
1410    /// @param source - the target's statement-wide number
1411    /// @param index_exprs - the target's bound index expressions
1412    /// @param filter - the statement's `WHERE`
1413    /// @param joined - the `UPDATE ... FROM` terms, empty for a `DELETE`
1414    fn write_hint(
1415        &self,
1416        source: usize,
1417        index_exprs: &[BoundIndexExprs],
1418        filter: Option<&BoundExpr>,
1419        joined: &[BoundSource],
1420    ) -> Result<crate::bind::IndexChoice, ParseError> {
1421        let Some(target) = self.sources.get(source) else {
1422            return Ok(crate::bind::IndexChoice::Any);
1423        };
1424        if target.index_hint == crate::bind::IndexChoice::Any {
1425            return Ok(crate::bind::IndexChoice::Any);
1426        }
1427        let mut probe = target.clone();
1428        probe.index_exprs = index_exprs.to_vec();
1429        let mut block = crate::bind::block_over(probe, filter.cloned(), Vec::new());
1430        block.sources.extend(joined.iter().cloned());
1431        if crate::plan::unanswerable_index_hint(&block).is_some() {
1432            return Err(crate::bind::no_query_solution(Span::default()));
1433        }
1434        Ok(target.index_hint.clone())
1435    }
1436
1437    /// Makes the target table the statement's one visible source.
1438    ///
1439    /// It opens a scope holding just the target, so every name in the
1440    /// statement's `SET`, `WHERE` and `RETURNING` resolves against the table
1441    /// being written and nothing else.
1442    fn push_write_source(&mut self, table: TableInfo, alias: Vec<u8>) -> usize {
1443        let id = self.sources.len();
1444        self.sources.push(BoundSource {
1445            index_hint: crate::bind::IndexChoice::Any,
1446            id,
1447            rows: crate::bind::SourceRows::Table,
1448            table: std::rc::Rc::new(table),
1449            alias,
1450            join: ast::JoinKind::Comma,
1451            constraint: None,
1452            suppressed: Vec::new(),
1453            index_exprs: Vec::new(),
1454        });
1455        self.scopes.push(vec![id]);
1456        id
1457    }
1458
1459    /// Refuses an attempt to write a generated column.
1460    ///
1461    /// SQLite's message names the column, because the usual cause is a script
1462    /// that inserts every column of a table one of whose columns has since been
1463    /// made generated. It names the statement too - `INSERT` or `UPDATE` - and
1464    /// so does this.
1465    ///
1466    /// @param table - the table being written
1467    /// @param position - the column the statement named
1468    /// @param verb - `INSERT into` or `UPDATE`, as SQLite writes it
1469    /// @param span - where the name was written
1470    fn refuse_generated(
1471        &self,
1472        table: &TableInfo,
1473        position: u16,
1474        verb: &str,
1475        span: Span,
1476    ) -> Result<(), ParseError> {
1477        let Some(column) = table.column(position) else {
1478            return Ok(());
1479        };
1480        if !column.generated {
1481            return Ok(());
1482        }
1483        Err(refused(
1484            format!(
1485                "cannot {verb} generated column \"{}\"",
1486                String::from_utf8_lossy(&column.name)
1487            ),
1488            span,
1489        ))
1490    }
1491
1492    /// Returns the target column positions an INSERT writes, in source order.
1493    ///
1494    /// With no column list the targets are every column in declaration order,
1495    /// which is why adding a column to a table changes what a positional
1496    /// INSERT means - SQLite's behaviour, and the reason the column list is
1497    /// worth writing.
1498    fn insert_targets(
1499        &self,
1500        table: &TableInfo,
1501        columns: &[ast::NameId],
1502    ) -> Result<Vec<u16>, ParseError> {
1503        if columns.is_empty() {
1504            // A bare `INSERT INTO t VALUES (...)` supplies the columns a person
1505            // can write, which is every column that is not generated - so a
1506            // table with a generated column takes fewer values than it has
1507            // columns, exactly as SQLite counts them.
1508            // A hidden column is not one of them either: a module's arguments
1509            // and its `rank` are named by an application that wants them, and
1510            // an `INSERT INTO fts VALUES ('a', 'b')` supplies the two indexed
1511            // columns and nothing else.
1512            return Ok((0..table.columns.len() as u16)
1513                .filter(|position| {
1514                    table
1515                        .column(*position)
1516                        .is_some_and(|column| !column.generated && !column.hidden)
1517                })
1518                .collect());
1519        }
1520        let mut targets = Vec::with_capacity(columns.len());
1521        for name in columns {
1522            let folded = self.ast.folded(*name).to_vec();
1523            let position = match table.column_position(&folded) {
1524                Some(position) => position,
1525                // A rowid table lets the statement name its rowid, under any
1526                // of its three spellings, and that is not a column: it is the
1527                // key. A declared column of the same name wins, which is why
1528                // this is the fallback rather than the first thing tried.
1529                None if table.has_rowid() && is_rowid_name(&folded) => ROWID_TARGET,
1530                None => return Err(no_such_column(self.ast.text(*name), Span::default())),
1531            };
1532            if targets.contains(&position) {
1533                return Err(refused(
1534                    format!(
1535                        "column {} is named twice",
1536                        String::from_utf8_lossy(self.ast.text(*name))
1537                    ),
1538                    Span::default(),
1539                ));
1540            }
1541            if position != ROWID_TARGET {
1542                self.refuse_generated(table, position, "INSERT into", Span::default())?;
1543            }
1544            targets.push(position);
1545        }
1546        Ok(targets)
1547    }
1548
1549    /// Binds the rows an INSERT supplies.
1550    fn bind_insert_source(
1551        &mut self,
1552        source: &ast::InsertSource,
1553        table: &TableInfo,
1554        targets: &[u16],
1555    ) -> Result<(BoundInsertSource, usize), ParseError> {
1556        match source {
1557            ast::InsertSource::DefaultValues => {
1558                let _ = (table, targets);
1559                Ok((BoundInsertSource::Values(vec![Vec::new()]), 0))
1560            }
1561            ast::InsertSource::Select(id) => {
1562                // The target table is source zero while the rows are bound, so
1563                // that `INSERT INTO t SELECT ... FROM u` resolves `u`'s columns
1564                // and not `t`'s. Binding a SELECT replaces the source list, and
1565                // the target is pushed back afterwards.
1566                // The scope stack is emptied rather than pushed to, because a
1567                // pushed scope would still be searched *outward* into the
1568                // target's, and `INSERT INTO t SELECT a FROM u` would then
1569                // resolve `a` against `t` when `u` has no such column.
1570                let saved = core::mem::take(&mut self.scopes);
1571                let select = self.bind_select(*id);
1572                let bound = match select {
1573                    Ok(bound) => bound,
1574                    Err(error) => {
1575                        self.scopes = saved;
1576                        return Err(error);
1577                    }
1578                };
1579                self.scopes = saved;
1580                if bound.values.is_empty() {
1581                    let arity = bound.columns.len();
1582                    return Ok((BoundInsertSource::Select(Box::new(bound)), arity));
1583                }
1584                let arity = bound.values.first().map_or(0, Vec::len);
1585                for row in &bound.values {
1586                    if row.len() != arity {
1587                        return Err(unsupported(
1588                            "all VALUES rows must have the same number of columns",
1589                            Span::default(),
1590                        ));
1591                    }
1592                }
1593                Ok((BoundInsertSource::Values(bound.values), arity))
1594            }
1595        }
1596    }
1597
1598    /// Works out where every table column's value comes from.
1599    ///
1600    /// A column the statement named takes its value from the source row; a
1601    /// column it did not takes its `DEFAULT`, and a column with no default
1602    /// takes NULL. The rowid is separated out here rather than in the
1603    /// compiler, because an `INTEGER PRIMARY KEY` column *is* the rowid and
1604    /// writing it into the record as well would store a duplicate that SQLite
1605    /// does not.
1606    fn column_sources(
1607        &mut self,
1608        table: &TableInfo,
1609        targets: &[u16],
1610    ) -> Result<(Vec<ColumnSource>, Option<ColumnSource>), ParseError> {
1611        let mut columns = Vec::with_capacity(table.columns.len());
1612        for position in 0..table.columns.len() as u16 {
1613            if let Some(expr) = self.generated_expr(table, position)? {
1614                columns.push(ColumnSource::Generated(expr));
1615                continue;
1616            }
1617            let source = match targets.iter().position(|target| *target == position) {
1618                Some(index) => ColumnSource::Row(index),
1619                None => ColumnSource::Expr(self.default_expr(table, position)?),
1620            };
1621            columns.push(source);
1622        }
1623        let rowid = match table.rowid_alias {
1624            Some(position) => columns.get(position as usize).cloned(),
1625            None => None,
1626        };
1627        Ok((columns, rowid))
1628    }
1629
1630    /// Binds a generated column's expression, when the column is one.
1631    fn generated_expr(
1632        &mut self,
1633        table: &TableInfo,
1634        position: u16,
1635    ) -> Result<Option<BoundExpr>, ParseError> {
1636        let Some(column) = table.column(position) else {
1637            return Ok(None);
1638        };
1639        if !column.generated {
1640            return Ok(None);
1641        }
1642        let Some(sql) = column.generated_sql.clone() else {
1643            return Ok(Some(BoundExpr::Null));
1644        };
1645        Ok(Some(self.bind_schema_expr(&sql)?))
1646    }
1647
1648    /// Binds the schema expressions an `UPDATE` evaluates for each row.
1649    ///
1650    /// The caller narrows the scope to the target first, so a name in the
1651    /// schema text cannot reach a `FROM` term.
1652    ///
1653    /// @param table - the table being written
1654    #[allow(clippy::type_complexity)]
1655    fn bind_update_schema(
1656        &mut self,
1657        table: &TableInfo,
1658    ) -> Result<
1659        (
1660            Vec<BoundAssignment>,
1661            Vec<BoundCheck>,
1662            Vec<BoundDefault>,
1663            Vec<BoundIndexExprs>,
1664        ),
1665        ParseError,
1666    > {
1667        let generated = self.bind_stored_generated(table)?;
1668        let checks = self.bind_checks(table)?;
1669        let not_null_defaults = self.bind_not_null_defaults(table)?;
1670        let index_exprs = self.bind_index_exprs(table)?;
1671        Ok((generated, checks, not_null_defaults, index_exprs))
1672    }
1673
1674    /// Binds every `STORED` generated column's expression.
1675    ///
1676    /// Returns them as assignments, because that is what they are on the write
1677    /// path: a value the statement did not write and the row has to carry. See
1678    /// [`BoundUpdate::generated`] for why an `UPDATE` needs them and a
1679    /// `VIRTUAL` column does not.
1680    ///
1681    /// @param table - the table being written
1682    fn bind_stored_generated(
1683        &mut self,
1684        table: &TableInfo,
1685    ) -> Result<Vec<BoundAssignment>, ParseError> {
1686        let mut generated = Vec::new();
1687        for position in 0..table.columns.len() as u16 {
1688            let Some(column) = table.column(position) else {
1689                continue;
1690            };
1691            if !column.generated || !column.stored {
1692                continue;
1693            }
1694            let Some(expr) = self.generated_expr(table, position)? else {
1695                continue;
1696            };
1697            generated.push(BoundAssignment {
1698                column: position,
1699                rowid: false,
1700                value: expr,
1701            });
1702        }
1703        Ok(generated)
1704    }
1705
1706    /// Binds a column's `DEFAULT`, or NULL when it has none.
1707    fn default_expr(&mut self, table: &TableInfo, position: u16) -> Result<BoundExpr, ParseError> {
1708        let Some(column) = table.column(position) else {
1709            return Ok(BoundExpr::Null);
1710        };
1711        let Some(sql) = column.default_sql.as_ref() else {
1712            return Ok(BoundExpr::Null);
1713        };
1714        if sql.is_empty() {
1715            return Ok(BoundExpr::Null);
1716        }
1717        self.bind_schema_expr(sql)
1718    }
1719
1720    /// Binds the `DEFAULT` of every `NOT NULL` column that declares one.
1721    ///
1722    /// What `REPLACE` substitutes for a NULL in such a column - see
1723    /// [`BoundDefault`]. A column with no default is left out, which is what
1724    /// makes the write path's fallback to `ABORT` the absence of an entry
1725    /// rather than a second test.
1726    ///
1727    /// The rowid alias is left out too: the row image carries the key the
1728    /// statement is about to allocate, and the write path does not check it.
1729    ///
1730    /// @param table - the table being written
1731    fn bind_not_null_defaults(
1732        &mut self,
1733        table: &TableInfo,
1734    ) -> Result<Vec<BoundDefault>, ParseError> {
1735        let mut defaults = Vec::new();
1736        for (position, column) in table.columns.iter().enumerate() {
1737            if !column.not_null || Some(position as u16) == table.rowid_alias {
1738                continue;
1739            }
1740            let Some(sql) = column.default_sql.as_ref() else {
1741                continue;
1742            };
1743            if sql.is_empty() {
1744                continue;
1745            }
1746            let expr = self.bind_schema_expr(&sql.clone())?;
1747            defaults.push(BoundDefault {
1748                column: position as u16,
1749                expr,
1750            });
1751        }
1752        Ok(defaults)
1753    }
1754
1755    /// Binds every `CHECK` the table declares.
1756    fn bind_checks(&mut self, table: &TableInfo) -> Result<Vec<BoundCheck>, ParseError> {
1757        let mut checks = Vec::with_capacity(table.checks.len());
1758        for check in &table.checks {
1759            checks.push(BoundCheck {
1760                name: check.name.clone(),
1761                expr: self.bind_schema_expr(&check.expr_sql)?,
1762            });
1763        }
1764        Ok(checks)
1765    }
1766
1767    /// Binds the expressions the table's indexes need per row.
1768    ///
1769    /// Only the indexes that need any: a partial one, and one with an
1770    /// expression key. Everything else is a slot of the row and needs nothing.
1771    ///
1772    /// @param table - the table being written
1773    fn bind_index_exprs(&mut self, table: &TableInfo) -> Result<Vec<BoundIndexExprs>, ParseError> {
1774        let mut bound = Vec::new();
1775        for (position, index) in table.indexes.iter().enumerate() {
1776            let needs = index.partial_sql.is_some()
1777                || index.columns.iter().any(|key| key.expr_sql.is_some());
1778            if !needs {
1779                continue;
1780            }
1781            let predicate = match index.partial_sql.as_ref() {
1782                Some(sql) => Some(self.bind_schema_expr(sql)?),
1783                None => None,
1784            };
1785            let mut keys = Vec::with_capacity(index.columns.len());
1786            for key in &index.columns {
1787                keys.push(match key.expr_sql.as_ref() {
1788                    Some(sql) => Some(self.bind_schema_expr(sql)?),
1789                    None => None,
1790                });
1791            }
1792            bound.push(BoundIndexExprs {
1793                position,
1794                predicate,
1795                keys,
1796            });
1797        }
1798        Ok(bound)
1799    }
1800
1801    /// Parses and binds an expression that was written in the schema.
1802    ///
1803    /// It is parsed into its own arena and bound against the statement's
1804    /// current sources, so the result is an ordinary `BoundExpr` that refers to
1805    /// the target table by position and carries no reference to the schema
1806    /// text it came from.
1807    pub fn bind_schema_expr(&mut self, sql: &[u8]) -> Result<BoundExpr, ParseError> {
1808        let limits = Limits::default();
1809        let (ast, expr) = parse_expression(sql, &limits)?;
1810        let mut nested = Binder::new(self.catalog, &ast, self.authorizer);
1811        nested.trigger_depth = self.trigger_depth;
1812        // **This is where a `DEFAULT`, a `CHECK`, a generated column, an index
1813        // expression and a partial-index predicate all become a bound tree, so
1814        // it is where all five are told they are a schema (task-1972).** The
1815        // nested binder also inherits the connection's registrations and
1816        // collations, which it did not before: without the registrations
1817        // `bind_external_call` never sees the call at all, because the name
1818        // does not resolve to a registered function and the expression fails as
1819        // "no such function" - an error for the wrong reason, and one that
1820        // disappears the moment an application registers the same name at a
1821        // different arity.
1822        nested.externals = self.externals;
1823        nested.collations = self.collations;
1824        nested.trusted_schema = self.trusted_schema;
1825        nested.call_site = crate::function::CallSite::Schema;
1826        nested.sources = self.sources.clone();
1827        nested.scopes = self.scopes.clone();
1828        let bound = nested.bind_expr(expr)?;
1829        Ok(bound)
1830    }
1831
1832    /// Binds an `ON CONFLICT` clause.
1833    fn bind_upsert(
1834        &mut self,
1835        table: &TableInfo,
1836        insert: &ast::Insert,
1837    ) -> Result<Vec<BoundUpsert>, ParseError> {
1838        if insert.upserts.is_empty() {
1839            return Ok(Vec::new());
1840        }
1841        // A module decides for itself what a clash is, so there is no
1842        // constraint for a conflict target to name. SQLite refuses the clause
1843        // on a virtual table outright, in these words.
1844        if table.kind == TableKind::Virtual {
1845            return Err(crate::bind::schema_refused(
1846                format!(
1847                    "UPSERT not implemented for virtual table \"{}\"",
1848                    String::from_utf8_lossy(&table.name)
1849                ),
1850                Span::default(),
1851            ));
1852        }
1853        // **Every clause is bound, in written order.** A statement may carry
1854        // several - `ON CONFLICT(k) DO UPDATE ... ON CONFLICT(id) DO UPDATE ...`
1855        // - and which one runs is decided at *run time*, by which constraint
1856        // the row actually collided with. Binding only the first was the whole
1857        // of the old refusal.
1858        for upsert in &insert.upserts {
1859            if upsert.target_filter.is_some() {
1860                return Err(unsupported(
1861                    "a partial-index conflict target",
1862                    Span::default(),
1863                ));
1864            }
1865        }
1866        // A clause with no conflict target matches any constraint, so anything
1867        // written after it could never run. SQLite refuses that rather than
1868        // accepting a clause it will never reach.
1869        if let Some(position) = insert
1870            .upserts
1871            .iter()
1872            .position(|upsert| upsert.target.is_empty())
1873        {
1874            if position + 1 < insert.upserts.len() {
1875                return Err(crate::bind::schema_refused(
1876                    "ON CONFLICT clause with no conflict target must be last",
1877                    Span::default(),
1878                ));
1879            }
1880        }
1881        // `excluded` is in scope for the assignments and the WHERE, and only
1882        // there. Setting it around the binding rather than pushing a second
1883        // FROM term keeps unqualified names resolving to the target row, which
1884        // is what SQLite does and what a second source would have made
1885        // ambiguous - every column of the target is also a column of
1886        // `excluded`.
1887        self.excluded = Some(table.clone());
1888        let mut bound = Vec::with_capacity(insert.upserts.len());
1889        for upsert in &insert.upserts {
1890            match self.bind_upsert_body(table, upsert) {
1891                Ok(Some(one)) => bound.push(one),
1892                Ok(None) => {}
1893                Err(error) => {
1894                    self.excluded = None;
1895                    return Err(error);
1896                }
1897            }
1898        }
1899        self.excluded = None;
1900        Ok(bound)
1901    }
1902
1903    /// Binds an upsert's target, assignments and filter.
1904    fn bind_upsert_body(
1905        &mut self,
1906        table: &TableInfo,
1907        upsert: &ast::Upsert,
1908    ) -> Result<Option<BoundUpsert>, ParseError> {
1909        let mut target = Vec::new();
1910        let mut collated: Vec<(u16, Option<Vec<u8>>)> = Vec::new();
1911        for column in &upsert.target {
1912            let Some(name) = bare_indexed_column(self.ast, column) else {
1913                return Err(unsupported(
1914                    "an expression in a conflict target",
1915                    Span::default(),
1916                ));
1917            };
1918            let Some(position) = table.column_position(&name) else {
1919                return Err(no_such_column(&name, Span::default()));
1920            };
1921            target.push(position);
1922            collated.push((position, target_collation(self.ast, column)));
1923        }
1924        target.sort_unstable();
1925        if !target.is_empty() && !conflict_target_matches(table, &collated) {
1926            return Err(crate::bind::schema_refused(
1927                "ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint",
1928                Span::default(),
1929            ));
1930        }
1931        let mut assignments = Vec::new();
1932        for (names, value) in &upsert.assignments {
1933            let values = self.assigned_values(names, *value)?;
1934            for (name, bound) in names.iter().zip(values) {
1935                let folded = self.ast.folded(*name).to_vec();
1936                let Some(position) = table.column_position(&folded) else {
1937                    return Err(no_such_column(self.ast.text(*name), Span::default()));
1938                };
1939                assignments.push(BoundAssignment {
1940                    column: position,
1941                    rowid: false,
1942                    value: bound,
1943                });
1944            }
1945        }
1946        assignments.sort_by_key(|assignment| assignment.column);
1947        let filter = match upsert.filter {
1948            Some(expr) => Some(self.bind_expr(expr)?),
1949            None => None,
1950        };
1951        Ok(Some(BoundUpsert {
1952            target,
1953            assignments,
1954            do_update: upsert.do_update,
1955            filter,
1956        }))
1957    }
1958
1959    /// Binds the value of one `SET` assignment, one bound value per column it
1960    /// names.
1961    ///
1962    /// **`SET (a, b) = (1, 2)` and `SET (a, b) = (SELECT x, y ...)` assign
1963    /// the parts in order**, which is what SQLite does. Both were refused as
1964    /// `unsupported`, and no capability row said so. A list of values is
1965    /// bound part by part; a query is bound once and read column by column,
1966    /// so both columns take the same row. A count that does not match is
1967    /// SQLite's own refusal, "2 columns assigned 3 values".
1968    ///
1969    /// @param names - the columns the assignment names
1970    /// @param value - the expression after `=`
1971    fn assigned_values(
1972        &mut self,
1973        names: &[ast::NameId],
1974        value: ast::ExprId,
1975    ) -> Result<Vec<BoundExpr>, ParseError> {
1976        if names.len() == 1 {
1977            return Ok(vec![self.bind_expr(value)?]);
1978        }
1979        let span = self.ast.expr_span(value);
1980        let values = match self.ast.expr(value) {
1981            Some(ast::Expr::RowValue(parts)) => {
1982                let parts = parts.clone();
1983                let mut bound = Vec::with_capacity(parts.len());
1984                for part in parts {
1985                    bound.push(self.bind_expr(part)?);
1986                }
1987                bound
1988            }
1989            Some(ast::Expr::Subquery(select)) => {
1990                let select = *select;
1991                self.bind_query_columns(select, span)?
1992            }
1993            _ => vec![self.bind_expr(value)?],
1994        };
1995        if values.len() != names.len() {
1996            return Err(refused(
1997                format!("{} columns assigned {} values", names.len(), values.len()),
1998                span,
1999            ));
2000        }
2001        Ok(values)
2002    }
2003
2004    /// Binds a `RETURNING` list, which is a result-column list over the row
2005    /// that was written.
2006    fn bind_returning(
2007        &mut self,
2008        columns: &[ast::ResultColumn],
2009    ) -> Result<Vec<BoundResultColumn>, ParseError> {
2010        if columns.is_empty() {
2011            return Ok(Vec::new());
2012        }
2013        // **`table.*` is refused, as SQLite refuses it.** A `RETURNING` list
2014        // may use a bare `*` and may not qualify it; SQLite answers
2015        // `RETURNING may not use "TABLE.*" wildcards` with code 1, and binding
2016        // it as a select list would have returned the rows.
2017        for column in columns {
2018            if let Some(ast::Expr::Star { table: Some(_) }) = self.ast.expr(column.expr) {
2019                return Err(refused(
2020                    "RETURNING may not use \"TABLE.*\" wildcards",
2021                    column.span,
2022                ));
2023            }
2024        }
2025        self.bind_result_columns_public(columns)
2026    }
2027}
2028
2029/// Returns the collation a conflict target's column names, folded.
2030///
2031/// It may be written as the indexed column's own `COLLATE` or as a `COLLATE`
2032/// around the name; SQLite reads both as the same expression.
2033///
2034/// @param ast - the statement's arena
2035/// @param column - one column of the conflict target
2036fn target_collation(ast: &crate::Ast, column: &ast::IndexedColumn) -> Option<Vec<u8>> {
2037    if let Some(name) = column.collation {
2038        return Some(ast.folded(name).to_vec());
2039    }
2040    match ast.expr(column.expr) {
2041        Some(ast::Expr::Collate { collation, .. }) => Some(ast.folded(*collation).to_vec()),
2042        _ => None,
2043    }
2044}
2045
2046/// Returns an indexed column's bare folded name, when it names a column.
2047///
2048/// A `COLLATE` around the name is looked through, because a conflict target
2049/// may name its collation that way; `target_collation` reads it.
2050fn bare_indexed_column(ast: &crate::Ast, column: &ast::IndexedColumn) -> Option<Vec<u8>> {
2051    let expr = match ast.expr(column.expr) {
2052        Some(ast::Expr::Collate { operand, .. }) => *operand,
2053        _ => column.expr,
2054    };
2055    match ast.expr(expr) {
2056        Some(ast::Expr::Column {
2057            table: None,
2058            column: name,
2059            ..
2060        }) => Some(ast.folded(*name).to_vec()),
2061        _ => None,
2062    }
2063}
2064
2065/// The extended result codes a rejected write reports.
2066///
2067/// The numbers are SQLite's own extended codes. They are written out rather
2068/// than derived because an application matches on them, and a code that was
2069/// computed from an enum's discriminant would change the day the enum did.
2070///
2071/// They live here, beside the binder that decides which constraint a statement
2072/// can violate, because **both** engines report them: the virtual machine
2073/// compiles them into a `HaltError` and the vectorised executor returns them
2074/// from its write path. Two copies would agree until one of them was corrected.
2075pub mod codes {
2076    /// `SQLITE_CONSTRAINT_CHECK`.
2077    pub const CHECK: i32 = 275;
2078    /// `SQLITE_CONSTRAINT_DATATYPE`, which a STRICT table reports.
2079    pub const DATATYPE: i32 = 3091;
2080    /// `SQLITE_CONSTRAINT_NOTNULL`.
2081    pub const NOT_NULL: i32 = 1299;
2082    /// `SQLITE_CONSTRAINT_PRIMARYKEY`.
2083    pub const PRIMARY_KEY: i32 = 1555;
2084    /// `SQLITE_CONSTRAINT_UNIQUE`.
2085    pub const UNIQUE: i32 = 2067;
2086    /// `SQLITE_CONSTRAINT_ROWID`.
2087    pub const ROWID: i32 = 2579;
2088    /// `SQLITE_MISMATCH`, which an `INTEGER PRIMARY KEY` reports for a value
2089    /// that is not an integer.
2090    pub const MISMATCH: i32 = 20;
2091    /// `SQLITE_CONSTRAINT_TRIGGER`, which `RAISE()` reports.
2092    pub const TRIGGER: i32 = 1811;
2093    /// `SQLITE_CONSTRAINT_FOREIGNKEY`.
2094    pub const FOREIGN_KEY: i32 = 787;
2095}
2096
2097/// Returns the message a unique-index violation reports.
2098///
2099/// SQLite names every column of the index, comma separated, which is what an
2100/// application parses to find out which key collided.
2101///
2102/// @param table - the table the index belongs to
2103/// @param index - the index whose key collided
2104pub fn unique_message(table: &TableInfo, index: &IndexInfo) -> String {
2105    let names: Vec<String> = index
2106        .columns
2107        .iter()
2108        .filter_map(|key| key.column)
2109        .filter_map(|column| table.column(column))
2110        .map(|column| {
2111            format!(
2112                "{}.{}",
2113                String::from_utf8_lossy(&table.name),
2114                String::from_utf8_lossy(&column.name)
2115            )
2116        })
2117        .collect();
2118    format!("UNIQUE constraint failed: {}", names.join(", "))
2119}
2120
2121/// Returns the message a duplicate rowid reports, and its extended code.
2122///
2123/// SQLite names the aliasing column when the table has an `INTEGER PRIMARY
2124/// KEY` - and reports `SQLITE_CONSTRAINT_PRIMARYKEY` for it - and names the
2125/// hidden `rowid` under `SQLITE_CONSTRAINT_ROWID` when it does not.
2126///
2127/// **A `WITHOUT ROWID` table has no rowid to name.** Its own key *is* its
2128/// primary key, held in the one index whose root is the table's, so a collision
2129/// reports every column of that key under `SQLITE_CONSTRAINT_PRIMARYKEY` -
2130/// `UNIQUE constraint failed: t.a, t.b`. It used to answer `t.rowid`, naming a
2131/// column the table does not have, on `INSERT` as well as `UPDATE`.
2132///
2133/// @param table - the table whose key collided
2134pub fn rowid_message(table: &TableInfo) -> (i32, String) {
2135    if table.without_rowid {
2136        if let Some(index) = table.indexes.iter().find(|index| index.root == table.root) {
2137            return (codes::PRIMARY_KEY, unique_message(table, index));
2138        }
2139    }
2140    match table.rowid_alias.and_then(|column| table.column(column)) {
2141        Some(column) => (
2142            codes::PRIMARY_KEY,
2143            format!(
2144                "UNIQUE constraint failed: {}.{}",
2145                String::from_utf8_lossy(&table.name),
2146                String::from_utf8_lossy(&column.name)
2147            ),
2148        ),
2149        None => (
2150            codes::ROWID,
2151            format!(
2152                "UNIQUE constraint failed: {}.rowid",
2153                String::from_utf8_lossy(&table.name)
2154            ),
2155        ),
2156    }
2157}
2158
2159/// Puts a limited write's order, limit and offset on the query that finds a
2160/// view's rows.
2161///
2162/// A write through a view's `INSTEAD OF` trigger fires once per row the view
2163/// produces under the statement's `WHERE`, so a `LIMIT` on the write limits
2164/// that query. SQLite's `sqlite3MaterializeView` is handed the same three
2165/// clauses for the same reason.
2166///
2167/// @param rows - the query over the view the binder built
2168/// @param order_by - the statement's bound `ORDER BY`
2169/// @param limit - the statement's bound `LIMIT`
2170/// @param offset - the statement's bound `OFFSET`
2171fn limit_view_rows(
2172    mut rows: Box<BoundSelect>,
2173    order_by: &[BoundOrderTerm],
2174    limit: &Option<BoundExpr>,
2175    offset: &Option<BoundExpr>,
2176) -> Box<BoundSelect> {
2177    rows.order_by = order_by.to_vec();
2178    rows.limit = limit.clone();
2179    rows.offset = offset.clone();
2180    rows
2181}
2182
2183/// Returns the refusal a `DELETE` or `UPDATE` with `ORDER BY` and no `LIMIT`
2184/// earns, or `None` when the clause is allowed.
2185///
2186/// **`ORDER BY` and `LIMIT` on a write are run, not refused (task-2120).** They
2187/// were refused in the pinned reference's words, `near "ORDER": syntax error`,
2188/// because that build is not compiled with `SQLITE_ENABLE_UPDATE_DELETE_LIMIT`
2189/// and has no grammar for the clause. But the builds applications actually link
2190/// often are - Apple's is - and `DELETE FROM t WHERE ... LIMIT 1000` in a loop
2191/// is the ordinary way to trim a large table without one large transaction. A
2192/// consumer probing 0.1.8 against the macOS `sqlite3` reported the refusal as a
2193/// real gap, which it was.
2194///
2195/// What remains is the one rule a build compiled with the option enforces:
2196/// an order with nothing to limit is refused, in SQLite's own words, because
2197/// sorting the rows a statement changes all of changes nothing.
2198///
2199/// @param limited - which of the two words came first and where, from the parser
2200/// @param limit - the statement's `LIMIT`, when it wrote one
2201/// @param statement - `DELETE` or `UPDATE`, for the message
2202fn order_without_limit(
2203    limited: Option<(ast::Limited, Span)>,
2204    limit: Option<ast::ExprId>,
2205    statement: &str,
2206) -> Option<ParseError> {
2207    let (word, span) = limited?;
2208    if word != ast::Limited::OrderBy || limit.is_some() {
2209        return None;
2210    }
2211    Some(refused(
2212        format!("ORDER BY without LIMIT on {statement}"),
2213        span,
2214    ))
2215}
2216
2217/// Refuses `RETURNING` on an `UPDATE` or a `DELETE` of a virtual table.
2218///
2219/// SQLite refuses both when it prepares the statement, before it looks at
2220/// anything else in it, so a statement with a subquery in its `SET` gets this
2221/// message and not one about the subquery. An `INSERT` into a virtual table
2222/// may return rows, and is not refused here.
2223///
2224/// @param table - the table being written
2225/// @param returning - the statement's `RETURNING` list, empty when it has none
2226/// @param statement - `UPDATE` or `DELETE`, for the message
2227fn refuse_module_returning(
2228    table: &TableInfo,
2229    returning: &[ast::ResultColumn],
2230    statement: &str,
2231) -> Result<(), ParseError> {
2232    if table.kind != TableKind::Virtual || returning.is_empty() {
2233        return Ok(());
2234    }
2235    Err(crate::bind::schema_refused(
2236        format!("{statement} RETURNING is not available on virtual tables"),
2237        Span::default(),
2238    ))
2239}
2240
2241/// Reports whether an upsert's conflict target names a key of the table.
2242///
2243/// SQLite accepts a target only when it is exactly the columns of the rowid
2244/// alias, or of a `PRIMARY KEY` or `UNIQUE` index that is not partial, in any
2245/// order. A target that names no key is refused, because no insert could ever
2246/// clash on it and the `DO` clause would never run. A partial index needs the
2247/// target's own `WHERE`, which is refused before this is asked. A column that
2248/// names a collation matches only an index key ordered by that collation, and
2249/// never the rowid alias, which is how `sqlite3UpsertAnalyzeTarget` compares
2250/// them.
2251///
2252/// @param table - the table being inserted into
2253/// @param target - each target column's position and the collation it names
2254fn conflict_target_matches(table: &TableInfo, target: &[(u16, Option<Vec<u8>>)]) -> bool {
2255    if let (false, Some(alias), [(column, None)]) = (table.without_rowid, table.rowid_alias, target)
2256    {
2257        if *column == alias {
2258            return true;
2259        }
2260    }
2261    table.indexes.iter().any(|index| {
2262        if !index.unique || index.partial_sql.is_some() || index.columns.len() != target.len() {
2263            return false;
2264        }
2265        index.columns.iter().all(|key| {
2266            let Some(position) = key.plain_column() else {
2267                return false;
2268            };
2269            target.iter().any(|(column, collation)| {
2270                *column == position
2271                    && collation
2272                        .as_deref()
2273                        .is_none_or(|named| named.eq_ignore_ascii_case(&key.collation))
2274            })
2275        })
2276    })
2277}
2278
2279#[cfg(test)]
2280mod tests {
2281    use super::*;
2282    use crate::catalog_view::{
2283        ColumnInfo, IndexColumnInfo, IndexInfo, IndexOrigin, TableInfo, TableKind,
2284    };
2285    use inillucent_value::Affinity;
2286
2287    /// Returns one plain column.
2288    ///
2289    /// @param name - the column's name
2290    fn a_column(name: &str) -> ColumnInfo {
2291        ColumnInfo {
2292            name: name.as_bytes().to_vec(),
2293            folded: name.to_ascii_lowercase().into_bytes(),
2294            declared_type: b"INTEGER".to_vec(),
2295            affinity: Affinity::Integer,
2296            collation: b"binary".to_vec(),
2297            not_null: false,
2298            not_null_conflict: None,
2299            primary_key_conflict: None,
2300            default_sql: None,
2301            primary_key_position: None,
2302            hidden: false,
2303            generated: false,
2304            stored: false,
2305            generated_sql: None,
2306        }
2307    }
2308
2309    /// Returns a rowid table with the columns named.
2310    ///
2311    /// @param name - the table's name
2312    /// @param columns - the column names, in declaration order
2313    fn a_table(name: &str, columns: &[&str]) -> TableInfo {
2314        TableInfo {
2315            name: name.as_bytes().to_vec(),
2316            folded: name.to_ascii_lowercase().into_bytes(),
2317            database: 0,
2318            root: 2,
2319            columns: columns.iter().map(|held| a_column(held)).collect(),
2320            rowid_alias: None,
2321            without_rowid: false,
2322            strict: false,
2323            autoincrement: false,
2324            kind: TableKind::Table,
2325            create_sql: Vec::new(),
2326            indexes: Vec::new(),
2327            view: None,
2328            triggers: Vec::new(),
2329            analysed_rows: None,
2330            foreign_key_triggers: Vec::new(),
2331            foreign_keys: Vec::new(),
2332            checks: Vec::new(),
2333            module: None,
2334        }
2335    }
2336
2337    /// Returns an index over the table columns named.
2338    ///
2339    /// @param name - the index's name
2340    /// @param root - its own tree, or the table's for a `WITHOUT ROWID` key
2341    /// @param columns - the table columns it keys on
2342    fn an_index(name: &str, root: u32, columns: &[u16]) -> IndexInfo {
2343        IndexInfo {
2344            name: name.as_bytes().to_vec(),
2345            folded: name.to_ascii_lowercase().into_bytes(),
2346            root,
2347            unique: true,
2348            columns: columns
2349                .iter()
2350                .map(|held| IndexColumnInfo {
2351                    column: Some(*held),
2352                    expr_sql: None,
2353                    collation: b"binary".to_vec(),
2354                    descending: false,
2355                    declared_descending: false,
2356                })
2357                .collect(),
2358            partial_sql: None,
2359            origin: IndexOrigin::Unique,
2360            conflict: None,
2361            prefix_rows: Vec::new(),
2362            analysed_rows: None,
2363            metric: None,
2364        }
2365    }
2366
2367    /// The three spellings of the rowid are the three SQLite accepts.
2368    ///
2369    /// **A fourth would be a column name a table could not have (T3,
2370    /// task-1962).** `rowid`, `oid` and `_rowid_` all name the hidden key, and
2371    /// a table that declares a column called any of them shadows it - so the
2372    /// list decides which names a `SELECT rowid` can mean.
2373    #[test]
2374    fn the_rowid_has_three_names() {
2375        assert!(is_rowid_name(b"rowid"));
2376        assert!(is_rowid_name(b"oid"));
2377        assert!(is_rowid_name(b"_rowid_"));
2378        assert!(!is_rowid_name(b"row_id"));
2379        assert!(!is_rowid_name(b"id"));
2380        assert!(
2381            !is_rowid_name(b"ROWID"),
2382            "the argument is already folded, so an unfolded name is not one this asks about"
2383        );
2384    }
2385
2386    /// A unique violation names every column of the index, table-qualified.
2387    ///
2388    /// **The message is what an application matches on.** SQLite's wording is
2389    /// `UNIQUE constraint failed: t.a, t.b`, and a library that switched on it
2390    /// would stop recognising a collision if the columns were listed any other
2391    /// way.
2392    #[test]
2393    fn a_unique_violation_names_every_column_of_the_index() {
2394        let table = a_table("t", &["a", "b", "c"]);
2395        let one = an_index("by_a", 3, &[0]);
2396        assert_eq!(
2397            unique_message(&table, &one),
2398            "UNIQUE constraint failed: t.a"
2399        );
2400        let two = an_index("by_a_b", 4, &[0, 1]);
2401        assert_eq!(
2402            unique_message(&table, &two),
2403            "UNIQUE constraint failed: t.a, t.b",
2404            "both columns, in key order, separated the way the reference separates them"
2405        );
2406    }
2407
2408    /// A rowid collision names the aliasing column when there is one, and the
2409    /// hidden `rowid` when there is not.
2410    ///
2411    /// The extended code differs with it: `SQLITE_CONSTRAINT_PRIMARYKEY` for an
2412    /// `INTEGER PRIMARY KEY` and `SQLITE_CONSTRAINT_ROWID` for the hidden one.
2413    #[test]
2414    fn a_rowid_collision_names_the_column_that_aliases_it() {
2415        let hidden = a_table("t", &["a"]);
2416        assert_eq!(
2417            rowid_message(&hidden),
2418            (
2419                codes::ROWID,
2420                "UNIQUE constraint failed: t.rowid".to_string()
2421            )
2422        );
2423        let mut aliased = a_table("t", &["id", "a"]);
2424        aliased.rowid_alias = Some(0);
2425        assert_eq!(
2426            rowid_message(&aliased),
2427            (
2428                codes::PRIMARY_KEY,
2429                "UNIQUE constraint failed: t.id".to_string()
2430            )
2431        );
2432    }
2433
2434    /// A `WITHOUT ROWID` table has no rowid to name, so it names its key.
2435    ///
2436    /// **It used to answer `t.rowid`, naming a column the table does not
2437    /// have.** Its own key *is* its primary key, held in the one index whose
2438    /// root is the table's.
2439    #[test]
2440    fn a_without_rowid_collision_names_the_primary_key() {
2441        let mut table = a_table("t", &["a", "b"]);
2442        table.without_rowid = true;
2443        table.indexes = vec![an_index("sqlite_autoindex_t_1", table.root, &[0, 1])];
2444        assert_eq!(
2445            rowid_message(&table),
2446            (
2447                codes::PRIMARY_KEY,
2448                "UNIQUE constraint failed: t.a, t.b".to_string()
2449            )
2450        );
2451    }
2452
2453    /// A constraint's own `ON CONFLICT REPLACE` makes a statement able to
2454    /// replace, with no `OR REPLACE` written anywhere.
2455    #[test]
2456    fn a_constraint_can_make_a_plain_insert_replace() {
2457        let plain = a_table("t", &["a"]);
2458        assert!(!can_replace(&plain, None));
2459        assert!(can_replace(&plain, Some(ConflictAction::Replace)));
2460
2461        let mut on_the_index = a_table("t", &["a"]);
2462        let mut index = an_index("by_a", 3, &[0]);
2463        index.conflict = Some(ConflictAction::Replace);
2464        on_the_index.indexes = vec![index];
2465        assert!(
2466            can_replace(&on_the_index, None),
2467            "`a UNIQUE ON CONFLICT REPLACE` replaces without the statement saying so"
2468        );
2469
2470        let mut on_the_column = a_table("t", &["a"]);
2471        if let Some(column) = on_the_column.columns.first_mut() {
2472            column.not_null_conflict = Some(ConflictAction::Replace);
2473        }
2474        assert!(can_replace(&on_the_column, None));
2475    }
2476}