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.
30fn 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 stored expression names an identifier.
102///
103/// It lexes rather than searches, so a column called `a` is not found inside
104/// `abc` or inside the text of a string literal.
105fn mentions_name(sql: &[u8], folded: &[u8]) -> bool {
106 let mut lexer = crate::lexer::Lexer::at(sql, 0);
107 loop {
108 let Ok(token) = lexer.next_token() else {
109 return false;
110 };
111 match token.kind {
112 crate::lexer::TokenKind::EndOfInput => return false,
113 crate::lexer::TokenKind::Identifier { keyword: None, .. }
114 if token.span.slice(sql).to_ascii_lowercase() == folded =>
115 {
116 return true;
117 }
118 _ => {}
119 }
120 }
121}
122
123/// Returns the failure `REINDEX` gives for a name that is nothing it knows.
124fn no_such_collation_sequence(name: &[u8], span: Span) -> ParseError {
125 ParseError::new(
126 crate::diagnostic::ParseErrorKind::Unexpected {
127 found: format!(
128 "unable to identify the object to be reindexed: {}",
129 String::from_utf8_lossy(name)
130 ),
131 expected: Vec::new(),
132 },
133 span,
134 )
135}
136
137/// How an explicit `BEGIN` acquires its rights.
138#[derive(Clone, Copy, Debug, Eq, PartialEq)]
139pub enum BeginKind {
140 /// Take nothing until the first read or write needs it.
141 Deferred,
142 /// Take the writer's reservation now.
143 Immediate,
144 /// Take the write lock now, excluding readers too.
145 Exclusive,
146}
147
148impl BeginKind {
149 /// Returns the kind a `BEGIN` clause names, defaulting to DEFERRED.
150 pub fn of(behaviour: Option<TransactionBehaviour>) -> BeginKind {
151 match behaviour {
152 None | Some(TransactionBehaviour::Deferred) => BeginKind::Deferred,
153 Some(TransactionBehaviour::Immediate) => BeginKind::Immediate,
154 Some(TransactionBehaviour::Exclusive) => BeginKind::Exclusive,
155 }
156 }
157}
158
159/// What an added column would do to rows that already exist.
160///
161/// SQLite refuses `PRIMARY KEY` and `UNIQUE` while it is still compiling,
162/// because no table can take them however empty it is. The other three it
163/// defers: a `NOT NULL` column with no default, a non-constant default and a
164/// `STORED` generated column are refused *only when there is a row to break*,
165/// and are accepted on an empty table. That is not a quirk worth smoothing
166/// over - it is the difference between a migration that runs on a fresh
167/// database and one that runs on a populated one - so the binder records what
168/// it saw and the executor, which knows the row count, decides.
169#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
170pub struct AddedColumnRisk {
171 /// `NOT NULL` with nothing to fill the existing rows with.
172 pub null_without_default: bool,
173 /// A `DEFAULT` the existing rows cannot all be given one answer from.
174 pub non_constant_default: bool,
175 /// `GENERATED ALWAYS AS (...) STORED`, which needs a value in every record.
176 pub generated_stored: bool,
177}
178
179impl AddedColumnRisk {
180 /// Returns the refusal a table with rows in it owes, in SQLite's wording.
181 ///
182 /// The capitalisation is the reference's own and is inconsistent between
183 /// the three; it is reproduced rather than tidied, because a caller
184 /// matching on the message is matching on what SQLite prints.
185 pub fn refusal(&self) -> Option<&'static str> {
186 if self.null_without_default {
187 return Some("Cannot add a NOT NULL column with default value NULL");
188 }
189 if self.non_constant_default {
190 return Some("Cannot add a column with non-constant default");
191 }
192 if self.generated_stored {
193 return Some("cannot add a STORED column");
194 }
195 None
196 }
197}
198
199/// What an `ALTER TABLE` does, with every name already resolved.
200#[derive(Clone, Debug, PartialEq, Eq)]
201pub enum AlterKind {
202 /// `RENAME TO`.
203 RenameTable {
204 /// The new name, as written.
205 to: Vec<u8>,
206 },
207 /// `RENAME COLUMN a TO b`.
208 RenameColumn {
209 /// The column's current name, as stored.
210 from: Vec<u8>,
211 /// Its new name, as written.
212 to: Vec<u8>,
213 },
214 /// `ADD COLUMN`.
215 AddColumn {
216 /// Where the definition starts in the statement's own source.
217 ///
218 /// The offsets rather than the text, for the same reason `CREATE TABLE`
219 /// carries an offset: the executor has the statement's source and
220 /// slicing it there keeps the *written* definition - its spacing, its
221 /// case and its comments - rather than something re-rendered from the
222 /// parse.
223 start: u32,
224 /// Where it ends.
225 end: u32,
226 /// What it would do to rows that already exist.
227 risk: AddedColumnRisk,
228 },
229 /// `DROP COLUMN`.
230 DropColumn {
231 /// The column's name, as stored.
232 name: Vec<u8>,
233 /// Its declared position, which is the record slot to remove.
234 position: u16,
235 },
236}
237
238/// One key column of an index being created.
239#[derive(Clone, Debug, PartialEq, Eq)]
240pub struct IndexKeyColumn {
241 /// The table column, when the key is a bare column.
242 ///
243 /// `None` for a key that is an expression. It was a bare `u16` while
244 /// `CREATE INDEX ix ON t(lower(a))` was refused in the binder; the field is
245 /// an `Option` now so that a reader which needs a column - a module-backed
246 /// index, say - has to say what it does when there is not one, rather than
247 /// reading a position that was invented to fill the slot.
248 pub column: Option<u16>,
249 /// The key expression, as written, when the key is one.
250 pub expr_sql: Option<Vec<u8>>,
251 /// The folded collation name.
252 pub collation: Vec<u8>,
253 /// Whether the key is stored descending.
254 pub descending: bool,
255}
256
257/// A statement the session carries out.
258#[derive(Clone, Debug, PartialEq)]
259pub enum Directive {
260 /// `BEGIN`.
261 Begin(BeginKind),
262 /// `COMMIT` or `END`.
263 Commit,
264 /// `ROLLBACK`, or `ROLLBACK TO savepoint`.
265 Rollback {
266 /// The savepoint to roll back to, when one was named.
267 savepoint: Option<Vec<u8>>,
268 },
269 /// `SAVEPOINT name`.
270 Savepoint(Vec<u8>),
271 /// `RELEASE name`.
272 Release(Vec<u8>),
273 /// `CREATE TABLE`.
274 CreateTable {
275 /// Whether `IF NOT EXISTS` was written.
276 if_not_exists: bool,
277 /// Which attached database.
278 database: usize,
279 /// The table name as written.
280 name: Vec<u8>,
281 /// The byte the name starts at in the statement's source.
282 name_offset: u32,
283 /// Whether the table already exists.
284 exists: bool,
285 },
286 /// `CREATE TABLE ... AS SELECT`.
287 ///
288 /// A `CREATE` whose column list comes from a plan, which is why it is a
289 /// directive of its own rather than a flag on the one above: everything
290 /// about the table - its column names, and the declared types it inherits
291 /// from the query's origin columns - is decided by binding the query, and
292 /// the `CREATE` text that is stored is *synthesised* rather than being a
293 /// slice of what was typed.
294 CreateTableAsSelect {
295 /// Whether `IF NOT EXISTS` was written.
296 if_not_exists: bool,
297 /// Which attached database.
298 database: usize,
299 /// The table name as written.
300 name: Vec<u8>,
301 /// Whether the table already exists.
302 exists: bool,
303 /// The `CREATE TABLE name(...)` text to store, built from the query.
304 create_sql: Vec<u8>,
305 /// The `SELECT` that fills it, as the source text it was written as.
306 ///
307 /// The text rather than the bound query, because the rows are inserted
308 /// by an ordinary `INSERT INTO name <select>` compiled against the
309 /// schema *after* the table exists - which is one implementation of
310 /// what an insert means rather than a second one written here.
311 select_sql: Vec<u8>,
312 },
313 /// `CREATE VIRTUAL TABLE`.
314 CreateVirtualTable {
315 /// Whether `IF NOT EXISTS` was written.
316 if_not_exists: bool,
317 /// Which attached database.
318 database: usize,
319 /// The table name as written.
320 name: Vec<u8>,
321 /// The module name as written.
322 module: Vec<u8>,
323 /// The arguments inside the parentheses, as written.
324 arguments: Vec<Vec<u8>>,
325 /// The byte the name starts at in the statement's source.
326 name_offset: u32,
327 /// Whether the table already exists.
328 exists: bool,
329 },
330 /// `ALTER TABLE`.
331 Alter {
332 /// Which attached database.
333 database: usize,
334 /// The table being altered, by its stored name.
335 table: Vec<u8>,
336 /// What to do to it.
337 action: AlterKind,
338 },
339 /// `REINDEX`, over one index, one table's indexes, or everything.
340 Reindex {
341 /// Which attached database.
342 database: usize,
343 /// The indexes to rebuild, by name.
344 indexes: Vec<Vec<u8>>,
345 },
346 /// `VACUUM`, which rebuilds the database into a fresh file.
347 Vacuum {
348 /// Which attached database.
349 database: usize,
350 /// The file `VACUUM INTO` writes the rebuilt copy to.
351 ///
352 /// A string literal, as SQLite's grammar has it. `INTO` leaves the
353 /// database it was run on completely alone, which is the difference
354 /// between the two forms and the reason the path is carried rather
355 /// than resolved here.
356 into: Option<Vec<u8>>,
357 },
358 /// `ATTACH`, which adds a database file to this connection.
359 Attach {
360 /// The file to open, as the literal it was written as.
361 file: Vec<u8>,
362 /// The name it will be known by.
363 schema: Vec<u8>,
364 },
365 /// `DETACH`, which removes one.
366 Detach {
367 /// The name it was attached under.
368 schema: Vec<u8>,
369 },
370 /// `ANALYZE`, over one object or the whole schema.
371 Analyze {
372 /// Which attached database.
373 database: usize,
374 /// The one table or index to measure, or nothing for all of them.
375 table: Option<Vec<u8>>,
376 /// Whether the statement was a bare `ANALYZE`, which measures every
377 /// database but `temp` rather than `database` alone.
378 every_schema: bool,
379 },
380 /// `CREATE VIEW`.
381 CreateView {
382 /// Whether `IF NOT EXISTS` was written.
383 if_not_exists: bool,
384 /// Which attached database.
385 database: usize,
386 /// The view name as written.
387 name: Vec<u8>,
388 /// The byte the name starts at in the statement's source.
389 name_offset: u32,
390 /// Whether the view already exists.
391 exists: bool,
392 },
393 /// `CREATE TRIGGER`.
394 CreateTrigger {
395 /// Which attached database.
396 database: usize,
397 /// The trigger name as written.
398 name: Vec<u8>,
399 /// The byte the name starts at in the statement's source.
400 name_offset: u32,
401 /// The table or view the trigger is attached to.
402 table: Vec<u8>,
403 /// Whether the trigger already exists.
404 exists: bool,
405 },
406 /// `CREATE INDEX`.
407 CreateIndex {
408 /// Whether `UNIQUE` was written.
409 unique: bool,
410 /// Whether `IF NOT EXISTS` was written.
411 if_not_exists: bool,
412 /// Which attached database.
413 database: usize,
414 /// The index name as written.
415 name: Vec<u8>,
416 /// The byte the name starts at in the statement's source.
417 name_offset: u32,
418 /// The table it indexes.
419 table: Vec<u8>,
420 /// The root page of that table.
421 table_root: u32,
422 /// The module named by `USING`, folded, when one was.
423 using: Option<Vec<u8>>,
424 /// The key columns.
425 columns: Vec<IndexKeyColumn>,
426 /// The storage parameters `WITH ( ... )` named, checked against the
427 /// module that will read them.
428 settings: Vec<(Vec<u8>, Vec<u8>)>,
429 /// Whether the index already exists.
430 exists: bool,
431 },
432 /// `DROP TABLE` or `DROP INDEX`.
433 Drop {
434 /// Which kind of object.
435 kind: ObjectKind,
436 /// Whether `IF EXISTS` was written.
437 if_exists: bool,
438 /// Which attached database.
439 database: usize,
440 /// The object name.
441 name: Vec<u8>,
442 /// The root page to free, or zero when the object has none.
443 root: u32,
444 /// The root pages of the indexes a `DROP TABLE` takes with it.
445 index_roots: Vec<u32>,
446 /// Whether the object exists.
447 exists: bool,
448 },
449 /// `PRAGMA`.
450 Pragma {
451 /// The schema the pragma was qualified with, when one was written.
452 ///
453 /// `PRAGMA aux.table_info(t)` asks about the attached database rather
454 /// than about `main`, and a pragma that dropped the qualifier would
455 /// answer confidently about the wrong file.
456 database: Option<usize>,
457 /// The pragma name, folded.
458 name: Vec<u8>,
459 /// The argument, when one was written.
460 argument: Option<PragmaArgument>,
461 },
462}
463
464/// What a `PRAGMA` was given.
465#[derive(Clone, Debug, PartialEq)]
466pub enum PragmaArgument {
467 /// A bare word, such as `PRAGMA journal_mode = WAL`.
468 Name(Vec<u8>),
469 /// An expression, such as `PRAGMA user_version = 4`.
470 Value(BoundExpr),
471}
472
473/// Whether a `CREATE INDEX` declared `UNIQUE`.
474///
475/// **An enum rather than a `bool` beside another `bool` (task-1962, A9).**
476/// `bind_create_index` took `unique` and `if_not_exists` adjacent and
477/// positional; swapping them compiles and declares a unique index where the
478/// statement asked for `IF NOT EXISTS`.
479#[derive(Clone, Copy, Debug, Eq, PartialEq)]
480pub enum Uniqueness {
481 /// `CREATE UNIQUE INDEX`: two rows may not share a key.
482 Unique,
483 /// `CREATE INDEX`: a key may repeat.
484 Duplicates,
485}
486
487/// Whether a `CREATE` declared `IF NOT EXISTS`.
488#[derive(Clone, Copy, Debug, Eq, PartialEq)]
489pub enum IfNotExists {
490 /// The statement is a no-op when the object is already there.
491 Skip,
492 /// The statement fails when the object is already there.
493 Refuse,
494}
495
496/// Everything a `CREATE INDEX` statement names.
497///
498/// The grammar's own fields, gathered rather than passed as nine positional
499/// arguments of which two were adjacent booleans.
500pub struct CreateIndexSpec<'a> {
501 /// Whether the index refuses a repeated key.
502 pub unique: Uniqueness,
503 /// What to do when the index is already there.
504 pub if_not_exists: IfNotExists,
505 /// The schema the index is created in, when one was written.
506 pub database: Option<ast::NameId>,
507 /// The index's name.
508 pub name: ast::NameId,
509 /// The table it is over.
510 pub table: ast::NameId,
511 /// The module named by `USING`, for the extension index forms.
512 pub using: Option<ast::NameId>,
513 /// The indexed columns, in key order.
514 pub columns: &'a [ast::IndexedColumn],
515 /// The `WITH` settings, as written.
516 pub settings: &'a [Vec<u8>],
517 /// The `WHERE` of a partial index, as an expression of the statement.
518 pub filter: Option<ast::ExprId>,
519}
520
521/// The fields of a `CREATE TRIGGER`, passed as one argument.
522///
523/// Ten parameters is past the point where their order is checkable by reading,
524/// and every one of them is a field of the statement rather than something
525/// computed here.
526pub(crate) struct CreateTriggerParts<'p> {
527 /// Whether `TEMP` was written.
528 pub temporary: bool,
529 /// Whether `IF NOT EXISTS` was written.
530 pub if_not_exists: bool,
531 /// The schema qualifier.
532 pub database: Option<ast::NameId>,
533 /// The trigger name.
534 pub name: ast::NameId,
535 /// When it fires.
536 pub time: Option<ast::TriggerTime>,
537 /// The table it is attached to.
538 pub table: ast::NameId,
539 /// The schema qualifier on the table.
540 pub table_database: Option<ast::NameId>,
541 /// Whether `FOR EACH ROW` was written.
542 pub for_each_row: bool,
543 /// The `WHEN` guard.
544 pub when: Option<ast::ExprId>,
545 /// The body statements.
546 pub body: &'p [ast::Statement],
547}
548
549impl<'a> Binder<'a> {
550 /// Binds a statement the session carries out itself.
551 pub fn bind_directive(&mut self, statement: &ast::Statement) -> Result<Directive, ParseError> {
552 match statement {
553 ast::Statement::Begin { behaviour } => Ok(Directive::Begin(BeginKind::of(*behaviour))),
554 ast::Statement::Commit => Ok(Directive::Commit),
555 ast::Statement::Rollback { savepoint } => Ok(Directive::Rollback {
556 savepoint: savepoint.map(|id| self.ast.text(id).to_vec()),
557 }),
558 ast::Statement::Savepoint(name) => {
559 Ok(Directive::Savepoint(self.ast.text(*name).to_vec()))
560 }
561 ast::Statement::Release(name) => Ok(Directive::Release(self.ast.text(*name).to_vec())),
562 ast::Statement::CreateTable {
563 temporary,
564 if_not_exists,
565 database,
566 name,
567 body,
568 } => self.bind_create_table(*temporary, *if_not_exists, *database, *name, body),
569 ast::Statement::CreateVirtualTable {
570 if_not_exists,
571 database,
572 name,
573 module,
574 arguments,
575 } => {
576 self.bind_create_virtual_table(*if_not_exists, *database, *name, *module, arguments)
577 }
578 ast::Statement::CreateIndex {
579 unique,
580 if_not_exists,
581 database,
582 name,
583 table,
584 using,
585 columns,
586 settings,
587 filter,
588 } => self.bind_create_index(&CreateIndexSpec {
589 unique: if *unique {
590 Uniqueness::Unique
591 } else {
592 Uniqueness::Duplicates
593 },
594 if_not_exists: if *if_not_exists {
595 IfNotExists::Skip
596 } else {
597 IfNotExists::Refuse
598 },
599 database: *database,
600 name: *name,
601 table: *table,
602 using: *using,
603 columns,
604 settings,
605 filter: *filter,
606 }),
607 ast::Statement::Analyze { database, name } => self.bind_analyze(*database, *name),
608 ast::Statement::AlterTable {
609 database,
610 table,
611 action,
612 } => self.bind_alter(*database, *table, action),
613 ast::Statement::Reindex { database, name } => self.bind_reindex(*database, *name),
614 ast::Statement::Vacuum { database, into } => self.bind_vacuum(*database, *into),
615 ast::Statement::Attach { file, schema, key } => self.bind_attach(*file, *schema, *key),
616 ast::Statement::Detach { schema } => self.bind_detach(*schema),
617 ast::Statement::CreateView {
618 temporary,
619 if_not_exists,
620 database,
621 name,
622 columns,
623 select,
624 } => self.bind_create_view(
625 *temporary,
626 *if_not_exists,
627 *database,
628 *name,
629 columns,
630 *select,
631 ),
632 ast::Statement::CreateTrigger {
633 temporary,
634 if_not_exists,
635 database,
636 name,
637 time,
638 event: _,
639 table,
640 table_database,
641 for_each_row,
642 when,
643 body,
644 } => self.bind_create_trigger(CreateTriggerParts {
645 temporary: *temporary,
646 if_not_exists: *if_not_exists,
647 database: *database,
648 name: *name,
649 time: *time,
650 table: *table,
651 table_database: *table_database,
652 for_each_row: *for_each_row,
653 when: *when,
654 body,
655 }),
656 ast::Statement::Drop {
657 kind,
658 if_exists,
659 database,
660 name,
661 } => self.bind_drop(*kind, *if_exists, *database, *name),
662 ast::Statement::Pragma {
663 database,
664 name,
665 value,
666 } => self.bind_pragma(*database, *name, value),
667 _ => Err(unsupported(
668 "this statement is not implemented yet",
669 Span::default(),
670 )),
671 }
672 }
673
674 /// Binds a `CREATE TABLE`.
675 fn bind_create_virtual_table(
676 &mut self,
677 if_not_exists: bool,
678 database: Option<ast::NameId>,
679 name: ast::NameId,
680 module: ast::NameId,
681 arguments: &[Vec<u8>],
682 ) -> Result<Directive, ParseError> {
683 let index = self.resolve_database(database)?;
684 let written = self.ast.text(name).to_vec();
685 if written.to_ascii_lowercase().starts_with(b"sqlite_") {
686 return Err(refused(
687 format!(
688 "object name reserved for internal use: {}",
689 String::from_utf8_lossy(&written)
690 ),
691 Span::default(),
692 ));
693 }
694 let folded = self.ast.folded(name).to_vec();
695 let database_name = self.catalog.database_name(index).to_vec();
696 let exists = self
697 .catalog
698 .find_table(Some(database_name.as_slice()), &folded)
699 .is_some();
700 if exists && !if_not_exists {
701 return Err(refused(
702 format!("table {} already exists", String::from_utf8_lossy(&written)),
703 Span::default(),
704 ));
705 }
706 Ok(Directive::CreateVirtualTable {
707 if_not_exists,
708 database: index,
709 name: written,
710 module: self.ast.text(module).to_vec(),
711 arguments: arguments.to_vec(),
712 name_offset: self
713 .ast
714 .name(name)
715 .map(|entry| entry.span.start)
716 .unwrap_or_default(),
717 exists,
718 })
719 }
720
721 /// Binds `CREATE TABLE`, refusing what the file format cannot hold.
722 fn bind_create_table(
723 &mut self,
724 temporary: bool,
725 if_not_exists: bool,
726 database: Option<ast::NameId>,
727 name: ast::NameId,
728 body: &ast::CreateTableBody,
729 ) -> Result<Directive, ParseError> {
730 let temp = self.temporary_database(temporary, database, false)?;
731 // **Two bodies, and the second one is built.** This used to be written
732 // as two `let ... else` bindings, the inner one answering
733 // `unsupported("CREATE TABLE ... AS SELECT")` - an arm no statement
734 // could reach, because `CreateTableBody` has exactly these two
735 // variants, so a feature that works was described by a refusal
736 // (task-1979, section 8.3). A match over both says the same thing with
737 // nothing left over.
738 let (columns, constraints, without_rowid, strict) = match body {
739 ast::CreateTableBody::AsSelect(select) => {
740 return self.bind_create_table_as_select(
741 temp,
742 if_not_exists,
743 database,
744 name,
745 *select,
746 )
747 }
748 ast::CreateTableBody::Columns {
749 columns,
750 constraints,
751 without_rowid,
752 strict,
753 } => (columns, constraints, without_rowid, strict),
754 };
755 if *without_rowid && !self.declares_primary_key(columns, constraints) {
756 return Err(schema_refused(
757 format!(
758 "PRIMARY KEY missing on table {}",
759 String::from_utf8_lossy(self.ast.text(name))
760 ),
761 Span::default(),
762 ));
763 }
764 self.check_autoincrement(columns, *without_rowid)?;
765 if *strict {
766 self.check_strict(columns)?;
767 }
768 self.check_generated(columns)?;
769 if columns.is_empty() {
770 return Err(refused(
771 "a table must have at least one column",
772 Span::default(),
773 ));
774 }
775 let index = match temp {
776 Some(index) => index,
777 None => self.resolve_database(database)?,
778 };
779 let written = self.ast.text(name).to_vec();
780 if written.to_ascii_lowercase().starts_with(b"sqlite_") {
781 return Err(refused(
782 format!(
783 "object name reserved for internal use: {}",
784 String::from_utf8_lossy(&written)
785 ),
786 Span::default(),
787 ));
788 }
789 let folded = self.ast.folded(name).to_vec();
790 let database_name = self.catalog.database_name(index).to_vec();
791 let exists = self
792 .catalog
793 .find_table(Some(database_name.as_slice()), &folded)
794 .is_some();
795 if exists && !if_not_exists {
796 return Err(refused(
797 format!("table {} already exists", String::from_utf8_lossy(&written)),
798 Span::default(),
799 ));
800 }
801 self.record_write_dependency(index);
802 Ok(Directive::CreateTable {
803 if_not_exists,
804 database: index,
805 name: written,
806 name_offset: self.name_offset(name),
807 exists,
808 })
809 }
810
811 /// Binds `CREATE TABLE ... AS SELECT`.
812 ///
813 /// **The column list comes from a plan**, which is the whole of why this is
814 /// a shape of its own. SQLite takes the table's columns from the query's
815 /// result columns: the name each one reports, and the declared type it
816 /// carries when it is a plain reference to a column that has one. So
817 /// `CREATE TABLE u AS SELECT a*2 AS d, b, c FROM t` on `t(a INTEGER, b TEXT,
818 /// c REAL)` stores `CREATE TABLE u(d,b TEXT,c REAL)` - `d` is an expression
819 /// and inherits nothing, and the other two inherit their origin's type.
820 ///
821 /// The rows are inserted afterwards by an ordinary `INSERT INTO name
822 /// <select>`, compiled against the schema once the table is in it. That is
823 /// one implementation of what an insert means rather than a second one
824 /// written into the DDL path, and it is what makes the affinity Part B4
825 /// applies reach these rows too.
826 ///
827 /// @param temp - the temporary database's index, when `TEMP` was written
828 /// @param if_not_exists - whether `IF NOT EXISTS` was written
829 /// @param database - the schema qualifier, when one was written
830 /// @param name - the table's name
831 /// @param select - the query the table is built from
832 fn bind_create_table_as_select(
833 &mut self,
834 temp: Option<usize>,
835 if_not_exists: bool,
836 database: Option<ast::NameId>,
837 name: ast::NameId,
838 select: ast::SelectId,
839 ) -> Result<Directive, ParseError> {
840 let index = match temp {
841 Some(index) => index,
842 None => self.resolve_database(database)?,
843 };
844 let written = self.ast.text(name).to_vec();
845 if written.to_ascii_lowercase().starts_with(b"sqlite_") {
846 return Err(refused(
847 format!(
848 "object name reserved for internal use: {}",
849 String::from_utf8_lossy(&written)
850 ),
851 Span::default(),
852 ));
853 }
854 let folded = self.ast.folded(name).to_vec();
855 let database_name = self.catalog.database_name(index).to_vec();
856 let exists = self
857 .catalog
858 .find_table(Some(database_name.as_slice()), &folded)
859 .is_some();
860 if exists && !if_not_exists {
861 return Err(refused(
862 format!("table {} already exists", String::from_utf8_lossy(&written)),
863 Span::default(),
864 ));
865 }
866 let span = self
867 .ast
868 .select(select)
869 .map(|held| held.span)
870 .ok_or_else(|| refused("the query could not be read", Span::default()))?;
871 let select_sql = self
872 .source
873 .get(span.start as usize..span.end as usize)
874 .ok_or_else(|| refused("the query could not be read", span))?
875 .to_vec();
876 // Bound rather than merely parsed, because binding is what resolves the
877 // result columns' names and origins - and because a query that does not
878 // bind has to be refused here rather than after the table exists.
879 let bound = self.bind_select(select)?;
880 if bound.columns.is_empty() {
881 return Err(refused(
882 "a table must have at least one column",
883 Span::default(),
884 ));
885 }
886 // **The declaration a `CREATE TABLE ... AS SELECT` stores is the
887 // *affinity*, not the source column's declared type.** SQLite writes
888 // `a INT` for a source column declared `INTEGER` and `b TEXT` for one
889 // declared `VARCHAR(3)`, because what survives a query is the affinity
890 // and nothing else - the width, the precision and the spelling are
891 // properties of the source table that the copy does not have. Storing
892 // `VARCHAR(3)` here claimed a constraint the new table does not
893 // enforce, and made the two schemas differ for every CTAS.
894 //
895 // The line break is SQLite's own rule too, so the stored text matches
896 // byte for byte: the name lengths are added up first, and a wide
897 // declaration is written one column per line.
898 let mut width = identifier_width(&written);
899 for column in &bound.columns {
900 width = width
901 .saturating_add(identifier_width(&column.name))
902 .saturating_add(5);
903 }
904 let (open, between, close): (&[u8], &[u8], &[u8]) = if width < 50 {
905 (b"", b",", b")")
906 } else {
907 (b"\n ", b",\n ", b"\n)")
908 };
909 let mut create_sql = Vec::new();
910 create_sql.extend_from_slice(b"CREATE TABLE ");
911 create_sql.extend_from_slice(&written);
912 create_sql.push(b'(');
913 let mut seen: Vec<Vec<u8>> = Vec::with_capacity(bound.columns.len());
914 for (position, column) in bound.columns.iter().enumerate() {
915 create_sql.extend_from_slice(if position > 0 { between } else { open });
916 let folded = column.name.to_ascii_lowercase();
917 if seen.contains(&folded) {
918 return Err(refused(
919 format!(
920 "duplicate column name: {}",
921 String::from_utf8_lossy(&column.name)
922 ),
923 Span::default(),
924 ));
925 }
926 seen.push(folded);
927 create_sql.extend_from_slice("ed_name(&column.name));
928 create_sql.extend_from_slice(affinity_type(&column.declared_type));
929 }
930 create_sql.extend_from_slice(close);
931 self.record_write_dependency(index);
932 Ok(Directive::CreateTableAsSelect {
933 if_not_exists,
934 database: index,
935 name: written,
936 exists,
937 create_sql,
938 select_sql,
939 })
940 }
941
942 /// Returns whether a `CREATE TABLE` declares a primary key anywhere.
943 fn declares_primary_key(
944 &self,
945 columns: &[ast::ColumnDef],
946 constraints: &[(Option<ast::NameId>, ast::TableConstraint)],
947 ) -> bool {
948 let on_column = columns.iter().any(|column| {
949 column.constraints.iter().any(|(_, constraint)| {
950 matches!(constraint, ast::ColumnConstraint::PrimaryKey { .. })
951 })
952 });
953 on_column
954 || constraints.iter().any(|(_, constraint)| {
955 matches!(constraint, ast::TableConstraint::PrimaryKey { .. })
956 })
957 }
958
959 /// Checks the rules a generated column has to obey.
960 ///
961 /// A generated column may not carry a `DEFAULT` - it has no value of its
962 /// own to fall back to - may not be part of a rowid table's `PRIMARY KEY`,
963 /// and may not refer to a column that does not exist or to itself. The
964 /// cycle check is the one that matters: without it a `CREATE TABLE` that
965 /// describes one is accepted and every later insert recurses.
966 fn check_generated(&self, columns: &[ast::ColumnDef]) -> Result<(), ParseError> {
967 let names: Vec<Vec<u8>> = columns
968 .iter()
969 .map(|column| self.ast.folded(column.name).to_vec())
970 .collect();
971 let mut generated: Vec<(usize, Vec<usize>)> = Vec::new();
972 for (position, column) in columns.iter().enumerate() {
973 let mut expr = None;
974 let mut has_default = false;
975 let mut in_primary_key = false;
976 for (_, constraint) in &column.constraints {
977 match constraint {
978 ast::ColumnConstraint::Generated { expr: body, .. } => expr = Some(*body),
979 ast::ColumnConstraint::Default(_) => has_default = true,
980 ast::ColumnConstraint::PrimaryKey { .. } => in_primary_key = true,
981 _ => {}
982 }
983 }
984 let Some(expr) = expr else {
985 continue;
986 };
987 let written = String::from_utf8_lossy(self.ast.text(column.name)).into_owned();
988 if has_default {
989 return Err(refused(
990 format!("cannot use DEFAULT on a generated column: {written}"),
991 Span::default(),
992 ));
993 }
994 if in_primary_key {
995 return Err(refused(
996 format!("generated columns cannot be part of the PRIMARY KEY: {written}"),
997 Span::default(),
998 ));
999 }
1000 let mut reads = Vec::new();
1001 self.expression_names(expr, &mut reads);
1002 let mut resolved = Vec::new();
1003 for name in &reads {
1004 let Some(found) = names.iter().position(|candidate| candidate == name) else {
1005 return Err(crate::bind::no_such_column(name, Span::default()));
1006 };
1007 resolved.push(found);
1008 }
1009 generated.push((position, resolved));
1010 }
1011 // A cycle is anything that never becomes computable: repeat the "every
1012 // dependency is settled" pass until it stops making progress, and if
1013 // anything is left it depends on itself, directly or through others.
1014 let mut settled: Vec<usize> = (0..columns.len())
1015 .filter(|position| !generated.iter().any(|(owner, _)| owner == position))
1016 .collect();
1017 let mut pending = generated;
1018 loop {
1019 let before = pending.len();
1020 let mut still = Vec::new();
1021 for (position, reads) in pending {
1022 if reads.iter().all(|read| settled.contains(read)) {
1023 settled.push(position);
1024 } else {
1025 still.push((position, reads));
1026 }
1027 }
1028 pending = still;
1029 if pending.is_empty() || pending.len() == before {
1030 break;
1031 }
1032 }
1033 if let Some((position, _)) = pending.first() {
1034 let written = columns
1035 .get(*position)
1036 .map(|column| String::from_utf8_lossy(self.ast.text(column.name)).into_owned())
1037 .unwrap_or_default();
1038 return Err(refused(
1039 format!("generated column loop on {written}"),
1040 Span::default(),
1041 ));
1042 }
1043 Ok(())
1044 }
1045
1046 /// Collects the folded column names an expression mentions.
1047 fn expression_names(&self, expr: ast::ExprId, into: &mut Vec<Vec<u8>>) {
1048 let Some(node) = self.ast.expr(expr) else {
1049 return;
1050 };
1051 if let ast::Expr::Column { column, .. } = node {
1052 let name = self.ast.folded(*column).to_vec();
1053 if !into.contains(&name) {
1054 into.push(name);
1055 }
1056 }
1057 for child in expression_children(self.ast, expr) {
1058 self.expression_names(child, into);
1059 }
1060 }
1061
1062 /// Checks the rules a `STRICT` table adds to its column list.
1063 ///
1064 /// Every column must name one of six types, and the check is on the
1065 /// declared text rather than on the affinity it maps to: `VARCHAR(10)` has
1066 /// TEXT affinity and is still refused, because STRICT is about what was
1067 /// written and not about what it means.
1068 fn check_strict(&self, columns: &[ast::ColumnDef]) -> Result<(), ParseError> {
1069 for column in columns {
1070 let Some(declared) = column.declared_type.as_ref() else {
1071 return Err(refused(
1072 format!(
1073 "missing datatype for {}",
1074 String::from_utf8_lossy(self.ast.text(column.name))
1075 ),
1076 Span::default(),
1077 ));
1078 };
1079 let folded = declared.to_ascii_uppercase();
1080 let allowed = matches!(
1081 folded.as_slice(),
1082 b"INT" | b"INTEGER" | b"REAL" | b"TEXT" | b"BLOB" | b"ANY"
1083 );
1084 if !allowed {
1085 return Err(refused(
1086 format!(
1087 "unknown datatype for {}: \"{}\"",
1088 String::from_utf8_lossy(self.ast.text(column.name)),
1089 String::from_utf8_lossy(declared)
1090 ),
1091 Span::default(),
1092 ));
1093 }
1094 }
1095 Ok(())
1096 }
1097
1098 /// Binds an `ANALYZE`.
1099 ///
1100 /// A bare `ANALYZE` measures everything; one with a name measures that
1101 /// object. SQLite accepts a database name, an index name or a table name in
1102 /// the same position and works out which it is, and so does this: the name
1103 /// is resolved against the tables, then the indexes, and only then refused.
1104 fn bind_analyze(
1105 &mut self,
1106 database: Option<ast::NameId>,
1107 name: Option<ast::NameId>,
1108 ) -> Result<Directive, ParseError> {
1109 let Some(name) = name else {
1110 // A bare `ANALYZE` is every database but `temp`, which is SQLite's
1111 // `sqlite3Analyze`.
1112 let temp = self.catalog.database_index(b"temp");
1113 for index in 0..self.catalog.database_count() {
1114 if Some(index) != temp {
1115 self.record_write_dependency(index);
1116 }
1117 }
1118 return Ok(Directive::Analyze {
1119 database: 0,
1120 table: None,
1121 every_schema: true,
1122 });
1123 };
1124 let folded = self.ast.folded(name).to_vec();
1125 // **An unqualified name may be a database's.** `ANALYZE aux` measures
1126 // every table in `aux`; resolving the name as a table in `main` first
1127 // made it "no such table: aux".
1128 if database.is_none() {
1129 if let Some(index) = self.catalog.database_index(&folded) {
1130 self.record_write_dependency(index);
1131 return Ok(Directive::Analyze {
1132 database: index,
1133 table: None,
1134 every_schema: false,
1135 });
1136 }
1137 }
1138 // An unqualified table or index is searched for in every database, in
1139 // the usual order; a qualified one only in its own. An index is looked
1140 // for first, as SQLite does, and is passed on by its own name, because
1141 // `ANALYZE ix` measures that index alone.
1142 let schema_name = match database {
1143 Some(_) => {
1144 let index = self.resolve_database(database)?;
1145 Some(self.catalog.database_name(index).to_vec())
1146 }
1147 None => None,
1148 };
1149 let found = self
1150 .catalog
1151 .find_index(schema_name.as_deref(), &folded)
1152 .map(|(table, index)| (table.database, index.name.clone()))
1153 .or_else(|| {
1154 self.catalog
1155 .find_table(schema_name.as_deref(), &folded)
1156 .map(|table| (table.database, table.name.clone()))
1157 });
1158 let Some((index, table)) = found else {
1159 return Err(no_such_table(self.ast.text(name), Span::default()));
1160 };
1161 self.record_write_dependency(index);
1162 Ok(Directive::Analyze {
1163 database: index,
1164 table: Some(table),
1165 every_schema: false,
1166 })
1167 }
1168
1169 /// Binds an `ALTER TABLE`.
1170 ///
1171 /// Every refusal SQLite makes is made here, where the catalog is available,
1172 /// rather than half-way through rewriting the schema: a rename that is
1173 /// going to fail must fail before anything has been written.
1174 fn bind_alter(
1175 &mut self,
1176 database: Option<ast::NameId>,
1177 table: ast::NameId,
1178 action: &ast::AlterAction,
1179 ) -> Result<Directive, ParseError> {
1180 // **An unqualified `ALTER TABLE` searches `temp` before `main`
1181 // (task-2061).** This resolved every unqualified name through
1182 // `resolve_database(None)`, which answers `main` and nothing else, and
1183 // then looked the table up in `main` alone - so
1184 // `CREATE TEMP TABLE t (a, b); ALTER TABLE t ADD COLUMN c` was
1185 // `no such table: t` when nothing called `t` was in `main`, and altered
1186 // `main.t` when something was. SQLite searches `temp` first for an
1187 // unqualified name in `ALTER TABLE` exactly as it does in a `SELECT`,
1188 // and `find_table(None, ...)` is already that search - the same one
1189 // every query goes through - so the schema comes back from the table
1190 // that was found rather than being decided before the search.
1191 let written = match database {
1192 // A qualifier still has to name a database that exists, and it
1193 // still restricts the search to that one.
1194 Some(_) => Some(
1195 self.catalog
1196 .database_name(self.resolve_database(database)?)
1197 .to_vec(),
1198 ),
1199 None => None,
1200 };
1201 let folded = self.ast.folded(table).to_vec();
1202 let Some(target) = self
1203 .catalog
1204 .find_table(written.as_deref(), &folded)
1205 .cloned()
1206 else {
1207 return Err(no_such_table(self.ast.text(table), Span::default()));
1208 };
1209 let index = target.database;
1210 let database_name = self.catalog.database_name(index).to_vec();
1211 if target.kind != crate::catalog_view::TableKind::Table {
1212 return Err(refused(
1213 format!(
1214 "cannot alter {}: not a table",
1215 String::from_utf8_lossy(&target.name)
1216 ),
1217 Span::default(),
1218 ));
1219 }
1220 if target.folded.starts_with(b"sqlite_") {
1221 return Err(refused(
1222 format!(
1223 "table {} may not be altered",
1224 String::from_utf8_lossy(&target.name)
1225 ),
1226 Span::default(),
1227 ));
1228 }
1229 self.record_write_dependency(index);
1230 let kind = match action {
1231 ast::AlterAction::RenameTo(name) => {
1232 let to = self.ast.text(*name).to_vec();
1233 let to_folded = self.ast.folded(*name).to_vec();
1234 if self
1235 .catalog
1236 .find_table(Some(database_name.as_slice()), &to_folded)
1237 .is_some()
1238 {
1239 return Err(refused(
1240 format!(
1241 "there is already another table or index with this name: {}",
1242 String::from_utf8_lossy(&to)
1243 ),
1244 Span::default(),
1245 ));
1246 }
1247 AlterKind::RenameTable { to }
1248 }
1249 ast::AlterAction::RenameColumn { from, to } => {
1250 let from_folded = self.ast.folded(*from).to_vec();
1251 let Some(position) = target.column_position(&from_folded) else {
1252 return Err(crate::bind::no_such_column(
1253 self.ast.text(*from),
1254 Span::default(),
1255 ));
1256 };
1257 let to_folded = self.ast.folded(*to).to_vec();
1258 if target.column_position(&to_folded).is_some() {
1259 return Err(refused(
1260 format!(
1261 "duplicate column name: {}",
1262 String::from_utf8_lossy(self.ast.text(*to))
1263 ),
1264 Span::default(),
1265 ));
1266 }
1267 let stored = target
1268 .column(position)
1269 .map(|column| column.name.clone())
1270 .unwrap_or_default();
1271 AlterKind::RenameColumn {
1272 from: stored,
1273 to: self.ast.text(*to).to_vec(),
1274 }
1275 }
1276 ast::AlterAction::AddColumn(definition) => {
1277 let risk = self.check_added_column(&target, definition)?;
1278 AlterKind::AddColumn {
1279 start: definition.span.start,
1280 end: definition.span.end,
1281 risk,
1282 }
1283 }
1284 ast::AlterAction::DropColumn(name) => {
1285 let folded = self.ast.folded(*name).to_vec();
1286 let Some(position) = target.column_position(&folded) else {
1287 return Err(crate::bind::no_such_column(
1288 self.ast.text(*name),
1289 Span::default(),
1290 ));
1291 };
1292 self.check_dropped_column(&target, position)?;
1293 let stored = target
1294 .column(position)
1295 .map(|column| column.name.clone())
1296 .unwrap_or_default();
1297 AlterKind::DropColumn {
1298 name: stored,
1299 position,
1300 }
1301 }
1302 };
1303 Ok(Directive::Alter {
1304 database: index,
1305 table: target.name.clone(),
1306 action: kind,
1307 })
1308 }
1309
1310 /// Checks what `ADD COLUMN` may not add.
1311 ///
1312 /// Every one of these is refused because the existing rows have no value
1313 /// for the new column and cannot be given one: a `PRIMARY KEY` or `UNIQUE`
1314 /// column would need an index built over values that are all the same
1315 /// default, and a `NOT NULL` column with no default would make every
1316 /// existing row violate its own table.
1317 fn check_added_column(
1318 &self,
1319 table: &crate::catalog_view::TableInfo,
1320 definition: &ast::ColumnDef,
1321 ) -> Result<AddedColumnRisk, ParseError> {
1322 let folded = self.ast.folded(definition.name).to_vec();
1323 if table.column_position(&folded).is_some() {
1324 return Err(refused(
1325 format!(
1326 "duplicate column name: {}",
1327 String::from_utf8_lossy(self.ast.text(definition.name))
1328 ),
1329 Span::default(),
1330 ));
1331 }
1332 let mut not_null = false;
1333 let mut has_default = false;
1334 let mut constant = true;
1335 let mut generated_stored = false;
1336 for (_, constraint) in &definition.constraints {
1337 match constraint {
1338 ast::ColumnConstraint::PrimaryKey { .. } => {
1339 return Err(schema_refused(
1340 "Cannot add a PRIMARY KEY column",
1341 Span::default(),
1342 ))
1343 }
1344 ast::ColumnConstraint::Unique(_) => {
1345 return Err(schema_refused(
1346 "Cannot add a UNIQUE column",
1347 Span::default(),
1348 ))
1349 }
1350 ast::ColumnConstraint::NotNull(_) => not_null = true,
1351 ast::ColumnConstraint::Default(expr) => {
1352 has_default = true;
1353 if !self.constant_default(*expr) {
1354 constant = false;
1355 }
1356 }
1357 ast::ColumnConstraint::Generated { stored, .. } if *stored => {
1358 generated_stored = true;
1359 }
1360 _ => {}
1361 }
1362 }
1363 Ok(AddedColumnRisk {
1364 null_without_default: not_null && !has_default,
1365 non_constant_default: !constant,
1366 generated_stored,
1367 })
1368 }
1369
1370 /// Returns whether a `DEFAULT` is a constant an existing row can be given.
1371 fn constant_default(&self, expr: ast::ExprId) -> bool {
1372 match self.ast.expr(expr) {
1373 Some(ast::Expr::Literal(_)) => true,
1374 Some(ast::Expr::Unary { operand, .. }) => self.constant_default(*operand),
1375 _ => false,
1376 }
1377 }
1378
1379 /// Checks what `DROP COLUMN` may not drop.
1380 fn check_dropped_column(
1381 &self,
1382 table: &crate::catalog_view::TableInfo,
1383 position: u16,
1384 ) -> Result<(), ParseError> {
1385 let named = table
1386 .column(position)
1387 .map(|column| String::from_utf8_lossy(&column.name).into_owned())
1388 .unwrap_or_default();
1389 if table.columns.len() <= 1 {
1390 return Err(refused(
1391 format!("cannot drop column \"{named}\": no other columns exist"),
1392 Span::default(),
1393 ));
1394 }
1395 if table.rowid_alias == Some(position)
1396 || table
1397 .column(position)
1398 .is_some_and(|column| column.primary_key_position.is_some())
1399 {
1400 return Err(refused(
1401 format!("cannot drop column \"{named}\": PRIMARY KEY"),
1402 Span::default(),
1403 ));
1404 }
1405 let indexed = table
1406 .indexes
1407 .iter()
1408 .any(|index| index.columns.iter().any(|key| key.column == Some(position)));
1409 if indexed {
1410 return Err(refused(
1411 format!("cannot drop column \"{named}\": indexed"),
1412 Span::default(),
1413 ));
1414 }
1415 // A CHECK or a generated column that reads it would be left naming a
1416 // column that is gone, and the table would stop loading.
1417 let folded = table
1418 .column(position)
1419 .map(|column| column.folded.clone())
1420 .unwrap_or_default();
1421 let referenced = table
1422 .checks
1423 .iter()
1424 .any(|check| mentions_name(&check.expr_sql, &folded))
1425 || table.columns.iter().enumerate().any(|(other, column)| {
1426 other != usize::from(position)
1427 && column
1428 .generated_sql
1429 .as_ref()
1430 .is_some_and(|sql| mentions_name(sql, &folded))
1431 });
1432 if referenced {
1433 return Err(refused(
1434 format!(
1435 "error in table {}: cannot drop column \"{named}\"",
1436 String::from_utf8_lossy(&table.name)
1437 ),
1438 Span::default(),
1439 ));
1440 }
1441 Ok(())
1442 }
1443
1444 /// Binds a `REINDEX`.
1445 ///
1446 /// The name is a collation, a table or an index, and SQLite works out which
1447 /// from what it finds - so the resolution order is the same here. A bare
1448 /// `REINDEX` rebuilds everything, which is the form that matters: it is what
1449 /// a person runs after a collation's definition has changed underneath an
1450 /// index that was built with the old one.
1451 fn bind_reindex(
1452 &mut self,
1453 database: Option<ast::NameId>,
1454 name: Option<ast::NameId>,
1455 ) -> Result<Directive, ParseError> {
1456 let index = self.resolve_database(database)?;
1457 self.record_write_dependency(index);
1458 // **An unqualified name means every database**, which is SQLite's
1459 // `sqlite3Reindex`: a bare `REINDEX` and a collation rebuild the
1460 // indexes of every database, and a table or index name is looked for
1461 // in all of them. Reading only `main` made `REINDEX ix` on a temporary
1462 // table's index "unable to identify the object to be reindexed".
1463 let schema_name = database.map(|_| self.catalog.database_name(index).to_vec());
1464 let qualified = database.is_some();
1465 let every_index = |catalog: &dyn CatalogView| -> Vec<Vec<u8>> {
1466 let tables = if qualified {
1467 catalog.tables_of(index)
1468 } else {
1469 catalog.every_table()
1470 };
1471 tables
1472 .into_iter()
1473 .flat_map(|table| table.indexes.iter())
1474 .map(|entry| entry.name.clone())
1475 .filter(|name| !name.is_empty())
1476 .collect()
1477 };
1478 let Some(name) = name else {
1479 return Ok(Directive::Reindex {
1480 database: index,
1481 indexes: every_index(self.catalog),
1482 });
1483 };
1484 let folded = self.ast.folded(name).to_vec();
1485 if let Some(table) = self.catalog.find_table(schema_name.as_deref(), &folded) {
1486 return Ok(Directive::Reindex {
1487 database: index,
1488 indexes: table
1489 .indexes
1490 .iter()
1491 .map(|entry| entry.name.clone())
1492 .collect(),
1493 });
1494 }
1495 if let Some((_, entry)) = self.catalog.find_index(schema_name.as_deref(), &folded) {
1496 return Ok(Directive::Reindex {
1497 database: index,
1498 indexes: vec![entry.name.clone()],
1499 });
1500 }
1501 // A collation name rebuilds every index ordered by it. An unknown name
1502 // is an error, and SQLite reports it against the collation because that
1503 // is the last thing it tried.
1504 if Collation::from_name(core::str::from_utf8(&folded).unwrap_or("")).is_some() {
1505 let wanted = folded.clone();
1506 let tables = if qualified {
1507 self.catalog.tables_of(index)
1508 } else {
1509 self.catalog.every_table()
1510 };
1511 let indexes = tables
1512 .into_iter()
1513 .flat_map(|table| table.indexes.iter())
1514 .filter(|entry| {
1515 entry
1516 .columns
1517 .iter()
1518 .any(|key| key.collation.eq_ignore_ascii_case(&wanted))
1519 })
1520 .map(|entry| entry.name.clone())
1521 .collect();
1522 return Ok(Directive::Reindex {
1523 database: index,
1524 indexes,
1525 });
1526 }
1527 Err(no_such_collation_sequence(
1528 self.ast.text(name),
1529 Span::default(),
1530 ))
1531 }
1532
1533 /// Binds a `VACUUM`.
1534 fn bind_vacuum(
1535 &mut self,
1536 database: Option<ast::NameId>,
1537 into: Option<ast::ExprId>,
1538 ) -> Result<Directive, ParseError> {
1539 let target = match into {
1540 Some(expr) => Some(self.literal_path(expr)?),
1541 None => None,
1542 };
1543 let index = self.resolve_database(database)?;
1544 self.record_write_dependency(index);
1545 Ok(Directive::Vacuum {
1546 database: index,
1547 into: target,
1548 })
1549 }
1550
1551 /// Binds an `ATTACH`.
1552 ///
1553 /// Both operands are literals. SQLite evaluates them, and every other
1554 /// value they could produce is a file name computed at run time - a
1555 /// statement that decides which database to open from arithmetic is not a
1556 /// shape worth supporting before it is asked for, and it is one an
1557 /// authorizer could not check.
1558 pub(crate) fn bind_attach(
1559 &mut self,
1560 file: ast::ExprId,
1561 schema: ast::ExprId,
1562 key: Option<ast::ExprId>,
1563 ) -> Result<Directive, ParseError> {
1564 if key.is_some() {
1565 return Err(unsupported("ATTACH ... KEY", Span::default()));
1566 }
1567 Ok(Directive::Attach {
1568 file: self.literal_path(file)?,
1569 schema: self.literal_or_name(schema)?,
1570 })
1571 }
1572
1573 /// Binds a `DETACH`.
1574 pub(crate) fn bind_detach(&mut self, schema: ast::ExprId) -> Result<Directive, ParseError> {
1575 Ok(Directive::Detach {
1576 schema: self.literal_or_name(schema)?,
1577 })
1578 }
1579
1580 /// Reads a name written either as a word or as a string.
1581 ///
1582 /// `ATTACH 'file.db' AS aux` and `ATTACH 'file.db' AS 'aux'` name the same
1583 /// schema. The grammar parses that position as an expression, so a bare
1584 /// word arrives as a reference to a column that does not exist - and what
1585 /// the statement meant is the word.
1586 fn literal_or_name(&mut self, expr: ast::ExprId) -> Result<Vec<u8>, ParseError> {
1587 match self.ast.expr(expr) {
1588 Some(ast::Expr::Literal(ast::Literal::String(text))) => Ok(text.clone()),
1589 Some(ast::Expr::Column {
1590 table: None,
1591 column,
1592 ..
1593 }) => Ok(self.ast.text(*column).to_vec()),
1594 _ => Err(unsupported(
1595 "a schema name that is not a word or a string",
1596 Span::default(),
1597 )),
1598 }
1599 }
1600
1601 /// Reads the file name a `VACUUM INTO` was given.
1602 ///
1603 /// A literal only. SQLite evaluates the expression, but every other value
1604 /// it could produce is a file name computed at run time, and a statement
1605 /// that decides where to write a copy of the database from arithmetic is
1606 /// not a shape worth supporting before it is asked for.
1607 fn literal_path(&mut self, expr: ast::ExprId) -> Result<Vec<u8>, ParseError> {
1608 match self.ast.expr(expr) {
1609 Some(ast::Expr::Literal(ast::Literal::String(text))) => Ok(text.clone()),
1610 _ => Err(unsupported(
1611 "VACUUM INTO with a name that is not a literal",
1612 Span::default(),
1613 )),
1614 }
1615 }
1616
1617 /// Binds a `CREATE VIEW`.
1618 ///
1619 /// The body is bound here, and thrown away, purely to refuse a view whose
1620 /// query does not resolve. SQLite does the same: the definition is checked
1621 /// when the view is created rather than when it is first read, so a typo
1622 /// fails at `CREATE VIEW` rather than in whatever statement happens to
1623 /// select from it next.
1624 fn bind_create_view(
1625 &mut self,
1626 temporary: bool,
1627 if_not_exists: bool,
1628 database: Option<ast::NameId>,
1629 name: ast::NameId,
1630 columns: &[ast::NameId],
1631 select: ast::SelectId,
1632 ) -> Result<Directive, ParseError> {
1633 let temp = self.temporary_database(temporary, database, false)?;
1634 let index = match temp {
1635 Some(index) => index,
1636 None => self.resolve_database(database)?,
1637 };
1638 let written = self.ast.text(name).to_vec();
1639 if written.to_ascii_lowercase().starts_with(b"sqlite_") {
1640 return Err(refused(
1641 format!(
1642 "object name reserved for internal use: {}",
1643 String::from_utf8_lossy(&written)
1644 ),
1645 Span::default(),
1646 ));
1647 }
1648 let folded = self.ast.folded(name).to_vec();
1649 let database_name = self.catalog.database_name(index).to_vec();
1650 let exists = self
1651 .catalog
1652 .find_table(Some(database_name.as_slice()), &folded)
1653 .is_some();
1654 if exists && !if_not_exists {
1655 return Err(refused(
1656 format!("table {} already exists", String::from_utf8_lossy(&written)),
1657 Span::default(),
1658 ));
1659 }
1660 if !exists {
1661 let saved = core::mem::take(&mut self.scopes);
1662 let bound = self.bind_select(select);
1663 self.scopes = saved;
1664 let bound = bound?;
1665 if !columns.is_empty() && columns.len() != bound.columns.len() {
1666 return Err(refused(
1667 format!(
1668 "expected {} columns for {} but got {}",
1669 columns.len(),
1670 String::from_utf8_lossy(&written),
1671 bound.columns.len()
1672 ),
1673 Span::default(),
1674 ));
1675 }
1676 }
1677 self.record_write_dependency(index);
1678 Ok(Directive::CreateView {
1679 if_not_exists,
1680 database: index,
1681 name: written,
1682 name_offset: self.name_offset(name),
1683 exists,
1684 })
1685 }
1686
1687 /// Refuses the two places `AUTOINCREMENT` may not be written.
1688 ///
1689 /// It counts the rowid the table has handed out, so it needs a rowid to
1690 /// count: only an `INTEGER PRIMARY KEY` column, and never on a table that
1691 /// has no rowid at all. Both messages are the reference's own, because an
1692 /// application that reads them is reading SQLite's.
1693 fn check_autoincrement(
1694 &mut self,
1695 columns: &[ast::ColumnDef],
1696 without_rowid: bool,
1697 ) -> Result<(), ParseError> {
1698 for column in columns {
1699 let declared = column.declared_type.clone().unwrap_or_default();
1700 for (_, constraint) in &column.constraints {
1701 let ast::ColumnConstraint::PrimaryKey {
1702 autoincrement: true,
1703 ..
1704 } = constraint
1705 else {
1706 continue;
1707 };
1708 if without_rowid {
1709 return Err(refused(
1710 "AUTOINCREMENT not allowed on WITHOUT ROWID tables",
1711 Span::default(),
1712 ));
1713 }
1714 if !declared.eq_ignore_ascii_case(b"integer") {
1715 return Err(refused(
1716 "AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY",
1717 Span::default(),
1718 ));
1719 }
1720 }
1721 }
1722 Ok(())
1723 }
1724
1725 /// Binds a `CREATE TRIGGER`.
1726 ///
1727 /// The body is bound here, against the table the trigger is attached to, so
1728 /// a trigger that reads a column that does not exist is refused when it is
1729 /// written rather than the first time somebody writes the table. SQLite
1730 /// makes the same promise, and the alternative is a schema that loads and
1731 /// then fails on an unrelated INSERT.
1732 fn bind_create_trigger(
1733 &mut self,
1734 parts: CreateTriggerParts<'_>,
1735 ) -> Result<Directive, ParseError> {
1736 let temp = self.temporary_database(parts.temporary, parts.database, true)?;
1737 // `for_each_row` records whether the words were written, not whether
1738 // the trigger is one: SQLite has only row triggers, an omitted clause
1739 // means FOR EACH ROW, and FOR EACH STATEMENT is a syntax error in the
1740 // parser. There is nothing to refuse here.
1741 let _ = parts.for_each_row;
1742 let index = match temp {
1743 Some(index) => index,
1744 None => self.resolve_database(parts.database)?,
1745 };
1746 let written = self.ast.text(parts.name).to_vec();
1747 if written.to_ascii_lowercase().starts_with(b"sqlite_") {
1748 return Err(refused(
1749 format!(
1750 "object name reserved for internal use: {}",
1751 String::from_utf8_lossy(&written)
1752 ),
1753 Span::default(),
1754 ));
1755 }
1756 let folded = self.ast.folded(parts.name).to_vec();
1757 let database_name = self.catalog.database_name(index).to_vec();
1758 let table_folded = self.ast.folded(parts.table).to_vec();
1759 // A trigger created in a named database fires for a table in that
1760 // database. A temporary one fires for whatever the name finds, which
1761 // is the whole point of `CREATE TEMP TRIGGER ... ON t`: the trigger is
1762 // the connection's and the table is everybody's. `ON main.t` names the
1763 // database: a temporary trigger may name any, and any other trigger
1764 // only its own, which is SQLite's `sqlite3FixSrcList`.
1765 let named = match parts.table_database {
1766 Some(id) => {
1767 let at = self.resolve_database(Some(id))?;
1768 if temp.is_none() && at != index {
1769 return Err(refused(
1770 format!(
1771 "trigger {} cannot reference objects in database {}",
1772 String::from_utf8_lossy(&written),
1773 String::from_utf8_lossy(self.ast.text(id))
1774 ),
1775 Span::default(),
1776 ));
1777 }
1778 Some(self.catalog.database_name(at).to_vec())
1779 }
1780 None => None,
1781 };
1782 let scope = match &named {
1783 Some(name) => Some(name.as_slice()),
1784 None => temp.map_or(Some(database_name.as_slice()), |_| None),
1785 };
1786 let Some(target) = self.catalog.find_table(scope, &table_folded).cloned() else {
1787 return Err(crate::bind::no_such_table(
1788 self.ast.text(parts.table),
1789 Span::default(),
1790 ));
1791 };
1792 let exists = self
1793 .catalog
1794 .find_trigger(Some(database_name.as_slice()), &folded)
1795 .is_some();
1796 if exists && !parts.if_not_exists {
1797 return Err(refused(
1798 format!(
1799 "trigger {} already exists",
1800 String::from_utf8_lossy(&written)
1801 ),
1802 Span::default(),
1803 ));
1804 }
1805 let instead_of = parts.time == Some(ast::TriggerTime::InsteadOf);
1806 match target.kind {
1807 TableKind::View if !instead_of => {
1808 return Err(refused(
1809 format!(
1810 "cannot create {} trigger on view: {}",
1811 if parts.time == Some(ast::TriggerTime::After) {
1812 "AFTER"
1813 } else {
1814 "BEFORE"
1815 },
1816 String::from_utf8_lossy(&target.name)
1817 ),
1818 Span::default(),
1819 ));
1820 }
1821 TableKind::Table if instead_of => {
1822 return Err(refused(
1823 format!(
1824 "cannot create INSTEAD OF trigger on table: {}",
1825 String::from_utf8_lossy(&target.name)
1826 ),
1827 Span::default(),
1828 ));
1829 }
1830 TableKind::Virtual | TableKind::Subquery => {
1831 return Err(unsupported("a trigger on that object", Span::default()));
1832 }
1833 _ => {}
1834 }
1835 // `UPDATE OF a, b` is deliberately *not* checked against the table's
1836 // columns. The pinned build accepts `UPDATE OF nosuchcolumn` and simply
1837 // never fires the trigger, and refusing it here would make inillucent's
1838 // language smaller than the reference's - a schema SQLite wrote that
1839 // inillucent could not load.
1840 // The body is deliberately *not* bound here. SQLite stores a trigger
1841 // whose body names a column that does not exist and reports it on the
1842 // first write that fires it - measured against the pinned build, which
1843 // accepts both `UPDATE OF nosuchcolumn` and a body reading a column the
1844 // table has not got. Refusing either here would leave inillucent unable to
1845 // load a schema SQLite had written.
1846 let _ = (parts.time, parts.when, parts.body);
1847 self.record_write_dependency(index);
1848 Ok(Directive::CreateTrigger {
1849 database: index,
1850 name: written,
1851 name_offset: self.name_offset(parts.name),
1852 table: target.name.clone(),
1853 exists,
1854 })
1855 }
1856
1857 /// Binds a `CREATE INDEX`.
1858 ///
1859 /// @param spec - what the statement named
1860 fn bind_create_index(&mut self, spec: &CreateIndexSpec<'_>) -> Result<Directive, ParseError> {
1861 let CreateIndexSpec {
1862 database,
1863 name,
1864 table,
1865 using,
1866 columns,
1867 settings,
1868 ..
1869 } = *spec;
1870 let unique = spec.unique == Uniqueness::Unique;
1871 let if_not_exists = spec.if_not_exists == IfNotExists::Skip;
1872 // **A `WHERE` is carried in the statement text, not in this
1873 // directive.** The engine re-parses the canonical SQL it stores -
1874 // `index_from_create_sql` already puts the predicate on
1875 // `IndexInfo::partial_sql` - so a field here would be a second copy to
1876 // keep in step. A predicate that names a column the table has not got
1877 // is refused when the index is built, by the query that fills it.
1878 // Only one module can back an index, and naming another is refused here
1879 // rather than accepted and ignored - an index that silently was not the
1880 // structure it asked for is the shape of wrong answer this ticket keeps
1881 // finding.
1882 let using = match using {
1883 None => None,
1884 Some(named) => {
1885 let folded = self.ast.folded(named).to_vec();
1886 // Two structures, and both are real: `inillucent_hnsw` is the
1887 // graph the retrieval engine builds, and `ivfflat` is the
1888 // inverted file pgvector's other index type is - k-means
1889 // centroids and a list per centroid, probed `probes` deep.
1890 // Anything else is refused rather than accepted and ignored:
1891 // an index that silently was not the structure it asked for is
1892 // the shape of wrong answer this ticket keeps finding.
1893 if folded != b"inillucent_hnsw" && folded != b"ivfflat" {
1894 return Err(unsupported(
1895 "an index USING a module other than inillucent_hnsw or ivfflat",
1896 Span::default(),
1897 ));
1898 }
1899 Some(folded)
1900 }
1901 };
1902 let parsed_settings = index_settings(&using, settings)?;
1903 let table_folded = self.ast.folded(table).to_vec();
1904 // **An unqualified index goes where its table is.** SQLite looks the
1905 // table up in the usual order, `temp` first, and creates the index in
1906 // the schema it found the table in. Taking an unqualified index to mean
1907 // `main` made `CREATE TEMP TABLE t(a); CREATE INDEX i ON t(a)` report
1908 // "no such table: t".
1909 let index = match database {
1910 Some(_) => self.resolve_database(database)?,
1911 None => match self.catalog.find_table(None, &table_folded) {
1912 Some(found) => found.database,
1913 None => return Err(no_such_table(self.ast.text(table), Span::default())),
1914 },
1915 };
1916 let database_name = self.catalog.database_name(index).to_vec();
1917 let Some(target) = self
1918 .catalog
1919 .find_table(Some(database_name.as_slice()), &table_folded)
1920 .cloned()
1921 else {
1922 return Err(no_such_table(self.ast.text(table), Span::default()));
1923 };
1924 let written = self.ast.text(name).to_vec();
1925 let folded = self.ast.folded(name).to_vec();
1926 let exists = self
1927 .catalog
1928 .find_index(Some(database_name.as_slice()), &folded)
1929 .is_some();
1930 if exists && !if_not_exists {
1931 return Err(refused(
1932 format!("index {} already exists", String::from_utf8_lossy(&written)),
1933 Span::default(),
1934 ));
1935 }
1936 let mut keys = Vec::with_capacity(columns.len());
1937 for column in columns {
1938 // `CREATE INDEX x ON t(b COLLATE NOCASE DESC)` parses the collation
1939 // into the *expression*, because that is where the grammar puts a
1940 // `COLLATE` that follows a value. It is still an index on a bare
1941 // column, and treating it as one is the difference between
1942 // supporting the everyday form and refusing it as an expression.
1943 let (expr, written_collation) = match self.ast.expr(column.expr) {
1944 Some(ast::Expr::Collate { operand, collation }) => {
1945 (self.ast.expr(*operand), Some(*collation))
1946 }
1947 other => (other, column.collation),
1948 };
1949 // A key that is not a bare column is an expression, and is carried
1950 // as the source text the engine re-parses. Its collation is BINARY
1951 // unless the statement named one: there is no column to inherit
1952 // from.
1953 let named = match expr {
1954 Some(ast::Expr::Column {
1955 table: None,
1956 column: name,
1957 ..
1958 }) => Some(*name),
1959 _ => None,
1960 };
1961 let Some(name) = named else {
1962 let collation = match written_collation {
1963 Some(collation) => self.ast.folded(collation).to_vec(),
1964 None => b"binary".to_vec(),
1965 };
1966 keys.push(IndexKeyColumn {
1967 column: None,
1968 expr_sql: Some(self.ast.expr_span(column.expr).slice(self.source).to_vec()),
1969 collation,
1970 descending: column.order == ast::SortOrder::Descending,
1971 });
1972 continue;
1973 };
1974 let folded = self.ast.folded(name).to_vec();
1975 let Some(position) = target.column_position(&folded) else {
1976 return Err(crate::bind::no_such_column(
1977 self.ast.text(name),
1978 Span::default(),
1979 ));
1980 };
1981 let collation = match written_collation {
1982 Some(collation) => self.ast.folded(collation).to_vec(),
1983 None => target
1984 .column(position)
1985 .map(|column| column.collation.clone())
1986 .unwrap_or_else(|| b"binary".to_vec()),
1987 };
1988 keys.push(IndexKeyColumn {
1989 column: Some(position),
1990 expr_sql: None,
1991 collation,
1992 descending: column.order == ast::SortOrder::Descending,
1993 });
1994 }
1995 self.record_write_dependency(index);
1996 Ok(Directive::CreateIndex {
1997 unique,
1998 if_not_exists,
1999 database: index,
2000 name: written,
2001 name_offset: self.name_offset(name),
2002 table: target.name.clone(),
2003 table_root: target.root,
2004 using,
2005 columns: keys,
2006 settings: parsed_settings,
2007 exists,
2008 })
2009 }
2010
2011 /// Binds a `DROP TABLE` or `DROP INDEX`.
2012 fn bind_drop(
2013 &mut self,
2014 kind: ObjectKind,
2015 if_exists: bool,
2016 database: Option<ast::NameId>,
2017 name: ast::NameId,
2018 ) -> Result<Directive, ParseError> {
2019 let written = self.ast.text(name).to_vec();
2020 let folded = self.ast.folded(name).to_vec();
2021 // **An unqualified name is looked for in every database, `temp`
2022 // first,** which is SQLite's `sqlite3LocateTable` order. Taking it to
2023 // mean `main` made `DROP TABLE s` "no such table" for a temporary `s`,
2024 // and dropped `main.s` where SQLite drops the temporary `s` that
2025 // shadows it.
2026 let index = match database {
2027 Some(_) => self.resolve_database(database)?,
2028 None => self.unqualified_home(kind, &folded).unwrap_or(0),
2029 };
2030 let database_name = self.catalog.database_name(index).to_vec();
2031 self.record_write_dependency(index);
2032 if kind == ObjectKind::Trigger {
2033 // A trigger owns no B-tree either, so dropping one is its schema row
2034 // and nothing else.
2035 let exists = self
2036 .catalog
2037 .find_trigger(Some(database_name.as_slice()), &folded)
2038 .is_some();
2039 if !exists && !if_exists {
2040 return Err(refused(
2041 format!("no such trigger: {}", String::from_utf8_lossy(&written)),
2042 Span::default(),
2043 ));
2044 }
2045 return Ok(Directive::Drop {
2046 kind,
2047 if_exists,
2048 database: index,
2049 name: written,
2050 root: 0,
2051 index_roots: Vec::new(),
2052 exists,
2053 });
2054 }
2055 if kind == ObjectKind::View {
2056 // A view owns no B-tree, so dropping one is the schema row and
2057 // nothing else - and it must refuse a table, because `DROP VIEW t`
2058 // on a table is an error rather than a drop.
2059 let found = self
2060 .catalog
2061 .find_table(Some(database_name.as_slice()), &folded)
2062 .cloned();
2063 let exists = found
2064 .as_ref()
2065 .is_some_and(|table| table.kind == crate::catalog_view::TableKind::View);
2066 if !exists && !if_exists {
2067 return Err(refused(
2068 format!("no such view: {}", String::from_utf8_lossy(&written)),
2069 Span::default(),
2070 ));
2071 }
2072 return Ok(Directive::Drop {
2073 kind,
2074 if_exists,
2075 database: index,
2076 name: written,
2077 root: 0,
2078 index_roots: Vec::new(),
2079 exists,
2080 });
2081 }
2082 if kind == ObjectKind::Table {
2083 let found = self
2084 .catalog
2085 .find_table(Some(database_name.as_slice()), &folded)
2086 .cloned();
2087 let Some(table) = found else {
2088 if if_exists {
2089 return Ok(Directive::Drop {
2090 kind,
2091 if_exists,
2092 database: index,
2093 name: written,
2094 root: 0,
2095 index_roots: Vec::new(),
2096 exists: false,
2097 });
2098 }
2099 return Err(no_such_table(&written, Span::default()));
2100 };
2101 if table.kind == crate::catalog_view::TableKind::View {
2102 return Err(refused(
2103 format!(
2104 "use DROP VIEW to delete view {}",
2105 String::from_utf8_lossy(&written)
2106 ),
2107 Span::default(),
2108 ));
2109 }
2110 // A WITHOUT ROWID table's primary key *is* the table's own b-tree,
2111 // so its entry names the same root. Freeing it twice frees a page
2112 // that is already on the free list, which reads back as a malformed
2113 // database.
2114 let index_roots = table
2115 .indexes
2116 .iter()
2117 .map(|index| index.root)
2118 .filter(|root| *root != 0 && *root != table.root)
2119 .collect();
2120 return Ok(Directive::Drop {
2121 kind,
2122 if_exists,
2123 database: index,
2124 name: written,
2125 root: table.root,
2126 index_roots,
2127 exists: true,
2128 });
2129 }
2130 let found = self.find_index_root(index, &folded);
2131 let Some(root) = found else {
2132 if if_exists {
2133 return Ok(Directive::Drop {
2134 kind,
2135 if_exists,
2136 database: index,
2137 name: written,
2138 root: 0,
2139 index_roots: Vec::new(),
2140 exists: false,
2141 });
2142 }
2143 return Err(refused(
2144 format!("no such index: {}", String::from_utf8_lossy(&written)),
2145 Span::default(),
2146 ));
2147 };
2148 Ok(Directive::Drop {
2149 kind,
2150 if_exists,
2151 database: index,
2152 name: written,
2153 root,
2154 index_roots: Vec::new(),
2155 exists: true,
2156 })
2157 }
2158
2159 /// Returns the database an unqualified object name resolves to.
2160 ///
2161 /// `None` when no database holds an object of that kind by that name, so
2162 /// the caller reports it against `main` as before.
2163 ///
2164 /// @param kind - what sort of object the statement names
2165 /// @param folded - the object's folded name
2166 fn unqualified_home(&self, kind: ObjectKind, folded: &[u8]) -> Option<usize> {
2167 match kind {
2168 ObjectKind::Trigger => self
2169 .catalog
2170 .find_trigger(None, folded)
2171 .map(|(table, _)| table.database),
2172 ObjectKind::Index => self
2173 .catalog
2174 .find_index(None, folded)
2175 .map(|(table, _)| table.database),
2176 _ => self
2177 .catalog
2178 .find_table(None, folded)
2179 .map(|table| table.database),
2180 }
2181 }
2182
2183 /// Binds a `PRAGMA`.
2184 fn bind_pragma(
2185 &mut self,
2186 database: Option<ast::NameId>,
2187 name: ast::NameId,
2188 value: &ast::PragmaValue,
2189 ) -> Result<Directive, ParseError> {
2190 let argument = match value {
2191 ast::PragmaValue::None => None,
2192 ast::PragmaValue::Name(name) => {
2193 Some(PragmaArgument::Name(self.ast.text(*name).to_vec()))
2194 }
2195 ast::PragmaValue::Value(expr) => Some(PragmaArgument::Value(self.bind_expr(*expr)?)),
2196 };
2197 let database = match database {
2198 Some(id) => Some(self.resolve_database(Some(id))?),
2199 None => None,
2200 };
2201 Ok(Directive::Pragma {
2202 database,
2203 name: self.ast.folded(name).to_vec(),
2204 argument,
2205 })
2206 }
2207
2208 /// Returns the temporary database's number when `TEMP` was written.
2209 ///
2210 /// A temporary table's or view's name may be qualified only by `temp`:
2211 /// `CREATE TEMP TABLE main.t` says two different things about where the
2212 /// table goes, and SQLite refuses it rather than picking one, while
2213 /// `CREATE TEMP TABLE temp.t` says the same thing twice and SQLite accepts
2214 /// it. A temporary trigger takes no qualifier at all, which is SQLite's
2215 /// rule in `sqlite3BeginTrigger`.
2216 ///
2217 /// @param temporary - whether `TEMP` was written
2218 /// @param database - the qualifier, when one was written
2219 /// @param trigger - whether the object is a trigger
2220 fn temporary_database(
2221 &self,
2222 temporary: bool,
2223 database: Option<ast::NameId>,
2224 trigger: bool,
2225 ) -> Result<Option<usize>, ParseError> {
2226 if !temporary {
2227 return Ok(None);
2228 }
2229 if let Some(id) = database {
2230 if trigger {
2231 return Err(refused(
2232 "temporary trigger may not have qualified name",
2233 Span::default(),
2234 ));
2235 }
2236 if self.ast.folded(id) != b"temp" {
2237 return Err(refused(
2238 "temporary table name must be unqualified",
2239 Span::default(),
2240 ));
2241 }
2242 }
2243 self.catalog
2244 .database_index(b"temp")
2245 .map(Some)
2246 .ok_or_else(|| refused("no temporary database", Span::default()))
2247 }
2248
2249 /// Resolves a schema qualifier to an attached database index.
2250 fn resolve_database(&self, database: Option<ast::NameId>) -> Result<usize, ParseError> {
2251 let Some(id) = database else {
2252 return Ok(0);
2253 };
2254 let folded = self.ast.folded(id);
2255 self.catalog.database_index(folded).ok_or_else(|| {
2256 refused(
2257 format!(
2258 "unknown database {}",
2259 String::from_utf8_lossy(self.ast.text(id))
2260 ),
2261 Span::default(),
2262 )
2263 })
2264 }
2265
2266 /// Returns the byte an identifier starts at in the statement's source.
2267 ///
2268 /// The canonical `sqlite_schema` text is the statement from its object
2269 /// name onward, which is how `IF NOT EXISTS` and the schema qualifier come
2270 /// to be missing from what SQLite stores. Slicing the source is the only
2271 /// way to reproduce that exactly; rendering the tree back would normalise
2272 /// whitespace and quoting the user chose.
2273 fn name_offset(&self, name: ast::NameId) -> u32 {
2274 self.ast.name(name).map_or(0, |name| name.span.start)
2275 }
2276
2277 /// Returns an index's root page, searching every table of a database.
2278 fn find_index_root(&self, database: usize, folded: &[u8]) -> Option<u32> {
2279 let name = self.catalog.database_name(database).to_vec();
2280 self.catalog
2281 .find_index(Some(name.as_slice()), folded)
2282 .map(|(_, index)| index.root)
2283 }
2284}
2285
2286/// Returns a column name as it can be written back into a `CREATE` statement.
2287///
2288/// A name a query invented - `SELECT 1` reports the column as `1` - is not an
2289/// identifier, so it is quoted the way SQLite quotes it: `CREATE TABLE w("1")`.
2290///
2291/// @param name - the column's name as the query reports it
2292fn quoted_name(name: &[u8]) -> Vec<u8> {
2293 let plain = !name.is_empty()
2294 && !name.first().is_some_and(u8::is_ascii_digit)
2295 && name
2296 .iter()
2297 .all(|byte| byte.is_ascii_alphanumeric() || *byte == b'_');
2298 if plain {
2299 return name.to_vec();
2300 }
2301 let mut out = Vec::with_capacity(name.len().saturating_add(2));
2302 out.push(b'"');
2303 for byte in name {
2304 if *byte == b'"' {
2305 out.push(b'"');
2306 }
2307 out.push(*byte);
2308 }
2309 out.push(b'"');
2310 out
2311}
2312
2313/// Returns the type name a `CREATE TABLE ... AS SELECT` writes for a column.
2314///
2315/// The affinity's own name, with the leading space, exactly as SQLite writes
2316/// it: BLOB affinity - which is what a column with no declared type has -
2317/// writes nothing at all, so the copy of an untyped column is untyped.
2318///
2319/// @param declared - the source column's declared type, as written
2320fn affinity_type(declared: &[u8]) -> &'static [u8] {
2321 match inillucent_value::affinity::for_column(declared) {
2322 inillucent_value::affinity::Affinity::Blob => b"",
2323 inillucent_value::affinity::Affinity::Text => b" TEXT",
2324 inillucent_value::affinity::Affinity::Integer => b" INT",
2325 inillucent_value::affinity::Affinity::Real => b" REAL",
2326 inillucent_value::affinity::Affinity::Numeric
2327 | inillucent_value::affinity::Affinity::FlexNum => b" NUM",
2328 }
2329}
2330
2331/// Returns the width SQLite counts an identifier as when it decides whether to
2332/// write a `CREATE TABLE ... AS SELECT`'s columns one per line.
2333///
2334/// Its own `identLength`: the name plus the two quotes it might need, plus one
2335/// for each quote inside it that would have to be doubled. The rule that reads
2336/// it is "under fifty, one line", and reproducing both is what makes the stored
2337/// declaration byte-identical rather than merely equivalent.
2338///
2339/// @param name - the identifier
2340fn identifier_width(name: &[u8]) -> usize {
2341 name.len()
2342 .saturating_add(2)
2343 .saturating_add(name.iter().filter(|byte| **byte == b'"').count())
2344}
2345
2346/// The storage parameters `CREATE INDEX ... WITH ( ... )` accepts.
2347///
2348/// One entry per name the vector index understands, with the store option it
2349/// becomes. **A name that is not here is refused rather than ignored**, which is
2350/// the same rule `USING` follows a few lines above and for the same reason: an
2351/// index that quietly was not built the way it was asked to be is a wrong answer
2352/// nobody can see.
2353const INDEX_SETTINGS: [(&str, &str); 10] = [
2354 // The graph's own three, spelled as pgvector spells them.
2355 ("m", "m"),
2356 ("ef_construction", "ef_construction"),
2357 ("ef_search", "ef_search"),
2358 // Whether a query walks the graph (`approximate`, the default for an
2359 // `inillucent_hnsw` index) or compares every vector (`exact`). The store
2360 // validates the value, so `mode = 'fast'` is refused by name.
2361 ("mode", "mode"),
2362 // The distance the index is built for. pgvector puts this in an operator
2363 // class - `USING hnsw (v vector_l2_ops)` - and names it here as well.
2364 ("metric", "metric"),
2365 ("distance", "metric"),
2366 // How many threads the build uses, and how far behind the table the index
2367 // may fall before it is rebuilt.
2368 ("threads", "threads"),
2369 ("compact", "compact"),
2370 // The two an `ivfflat` has: how many centroids it clusters into, and how
2371 // many of those lists a query reads.
2372 ("lists", "lists"),
2373 ("probes", "probes"),
2374];
2375
2376/// Checks `WITH ( ... )` against the structure that will read it.
2377///
2378/// Returns the settings as folded `(name, value)` pairs, in the order written.
2379/// A plain `CREATE INDEX` may not carry any: a b-tree has no parameters, and
2380/// accepting them would mean accepting a setting nothing reads.
2381///
2382/// @param using - the module the index named, when it named one
2383/// @param settings - the raw `name = value` slices
2384fn index_settings(
2385 using: &Option<Vec<u8>>,
2386 settings: &[Vec<u8>],
2387) -> Result<Vec<(Vec<u8>, Vec<u8>)>, ParseError> {
2388 if settings.is_empty() {
2389 return Ok(Vec::new());
2390 }
2391 if using.is_none() {
2392 return Err(unsupported(
2393 "WITH ( ... ) on an index that is not USING a module",
2394 Span::default(),
2395 ));
2396 }
2397 let mut held = Vec::with_capacity(settings.len());
2398 for setting in settings {
2399 let text = String::from_utf8_lossy(setting).to_string();
2400 let Some((name, value)) = text.split_once('=') else {
2401 return Err(refused(
2402 format!("index setting {} is not name = value", text.trim()),
2403 Span::default(),
2404 ));
2405 };
2406 let folded = name.trim().to_ascii_lowercase();
2407 let Some((_, option)) = INDEX_SETTINGS
2408 .iter()
2409 .find(|(known, _)| *known == folded.as_str())
2410 else {
2411 return Err(refused(
2412 format!("no such index setting: {folded}"),
2413 Span::default(),
2414 ));
2415 };
2416 let value = value
2417 .trim()
2418 .trim_matches(|held| held == '\'' || held == '"');
2419 held.push((option.as_bytes().to_vec(), value.as_bytes().to_vec()));
2420 }
2421 Ok(held)
2422}