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