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