diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 9181d7bc9..25e217762 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -24,18 +24,59 @@ pub enum Cmd { Stmt(Stmt), } +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct CreateVirtualTable { + /// `IF NOT EXISTS` + pub if_not_exists: bool, + /// table name + pub tbl_name: QualifiedName, + /// module name + pub module_name: Name, + /// args + pub args: Vec, // TODO smol str +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Update { + /// CTE + pub with: Option, + /// `OR` + pub or_conflict: Option, + /// table name + pub tbl_name: QualifiedName, + /// `INDEXED` + pub indexed: Option, + /// `SET` assignments + pub sets: Vec, + /// `FROM` + pub from: Option, + /// `WHERE` clause + pub where_clause: Option>, + /// `RETURNING` + pub returning: Vec, + /// `ORDER BY` + pub order_by: Vec, + /// `LIMIT` + pub limit: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct AlterTable { + // table name + pub name: QualifiedName, + // `ALTER TABLE` body + pub body: AlterTableBody, +} /// SQL statement // https://sqlite.org/syntax/sql-stmt.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Stmt { /// `ALTER TABLE`: table name, body - AlterTable { - // table name - name: QualifiedName, - // `ALTER TABLE` body - body: AlterTableBody, - }, + AlterTable(AlterTable), /// `ANALYSE`: object name Analyze { // object name @@ -123,17 +164,20 @@ pub enum Stmt { /// query select: Select, }, - /// `CREATE VIRTUAL TABLE` - CreateVirtualTable { + /// `CREATE MATERIALIZED VIEW` + CreateMaterializedView { /// `IF NOT EXISTS` if_not_exists: bool, - /// table name - tbl_name: QualifiedName, - /// module name - module_name: Name, - /// args - args: Vec, // TODO smol str + /// view name + view_name: QualifiedName, + /// columns + columns: Vec, + /// query + select: Select, }, + + /// `CREATE VIRTUAL TABLE` + CreateVirtualTable(CreateVirtualTable), /// `DELETE` Delete { /// CTE @@ -231,28 +275,7 @@ pub enum Stmt { /// `SELECT` Select(Select), /// `UPDATE` - Update { - /// CTE - with: Option, - /// `OR` - or_conflict: Option, - /// table name - tbl_name: QualifiedName, - /// `INDEXED` - indexed: Option, - /// `SET` assignments - sets: Vec, - /// `FROM` - from: Option, - /// `WHERE` clause - where_clause: Option>, - /// `RETURNING` - returning: Vec, - /// `ORDER BY` - order_by: Vec, - /// `LIMIT` - limit: Option, - }, + Update(Update), /// `VACUUM`: database name, into expr Vacuum { // database name @@ -274,6 +297,36 @@ pub enum Stmt { /// FIXME: rename this to TableReferenceId. pub struct TableInternalId(usize); +impl Default for TableInternalId { + fn default() -> Self { + Self(1) + } +} + +impl From for TableInternalId { + fn from(value: usize) -> Self { + Self(value) + } +} + +impl std::ops::AddAssign for TableInternalId { + fn add_assign(&mut self, rhs: usize) { + self.0 += rhs; + } +} + +impl From for usize { + fn from(value: TableInternalId) -> Self { + value.0 + } +} + +impl std::fmt::Display for TableInternalId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "t{}", self.0) + } +} + /// SQL expression // https://sqlite.org/syntax/expr.html #[derive(Clone, Debug, PartialEq, Eq)] @@ -508,6 +561,36 @@ pub enum Operator { Subtract, } +impl Operator { + /// returns whether order of operations can be ignored + pub fn is_commutative(&self) -> bool { + matches!( + self, + Operator::Add + | Operator::Multiply + | Operator::BitwiseAnd + | Operator::BitwiseOr + | Operator::Equals + | Operator::NotEquals + ) + } + + /// Returns true if this operator is a comparison operator that may need affinity conversion + pub fn is_comparison(&self) -> bool { + matches!( + self, + Self::Equals + | Self::NotEquals + | Self::Less + | Self::LessEquals + | Self::Greater + | Self::GreaterEquals + | Self::Is + | Self::IsNot + ) + } +} + /// Unary operators #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -732,11 +815,33 @@ pub enum Name { } impl Name { + pub fn new(s: impl AsRef) -> Self { + let s = s.as_ref(); + let bytes = s.as_bytes(); + + if s.is_empty() { + return Name::Ident(s.to_string()); + } + + match bytes[0] { + b'"' | b'\'' | b'`' | b'[' => Name::Quoted(s.to_string()), + _ => Name::Ident(s.to_string()), + } + } + pub fn as_str(&self) -> &str { match self { Name::Ident(s) | Name::Quoted(s) => s.as_str(), } } + + /// Checks if a name represents a double-quoted string that should get fallback behavior + pub fn is_double_quoted(&self) -> bool { + if let Self::Quoted(ident) = self { + return ident.starts_with("\""); + } + false + } } /// Qualified name @@ -751,6 +856,13 @@ pub struct QualifiedName { pub alias: Option, // FIXME restrict alias usage (fullname vs xfullname) } +impl std::fmt::Display for QualifiedName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use fmt::ToTokens as _; + self.to_fmt(f) + } +} + /// `ALTER TABLE` body // https://sqlite.org/lang_altertable.html #[derive(Clone, Debug, PartialEq, Eq)] @@ -1103,16 +1215,30 @@ pub enum PragmaName { DatabaseList, /// Encoding - only support utf8 Encoding, + /// Current free page count. + FreelistCount, /// Run integrity check on the database file IntegrityCheck, /// `journal_mode` pragma JournalMode, + /// encryption key for encrypted databases. This is just called `key` because most + /// extensions use this name instead of `encryption_key`. + #[strum(serialize = "key")] + #[cfg_attr(feature = "serde", serde(rename = "key"))] + EncryptionKey, /// Noop as per SQLite docs LegacyFileFormat, + /// Set or get the maximum number of pages in the database file. + MaxPageCount, + /// `module_list` pragma + /// `module_list` lists modules used by virtual tables. + ModuleList, /// Return the total number of pages in the database file. PageCount, /// Return the page size of the database in bytes. PageSize, + /// make connection query only + QueryOnly, /// Returns schema version of the database file. SchemaVersion, /// returns information about the columns of a table @@ -1212,6 +1338,19 @@ pub enum ResolveType { Replace, } +impl ResolveType { + /// Get the OE_XXX bit value + pub fn bit_value(&self) -> usize { + match self { + ResolveType::Rollback => 1, + ResolveType::Abort => 2, + ResolveType::Fail => 3, + ResolveType::Ignore => 4, + ResolveType::Replace => 5, + } + } +} + /// `WITH` clause // https://sqlite.org/lang_with.html // https://sqlite.org/syntax/with-clause.html diff --git a/parser/src/parser.rs b/parser/src/parser.rs index 56c9a3f23..d78927271 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -1,14 +1,14 @@ use crate::ast::{ - AlterTableBody, As, Cmd, ColumnConstraint, ColumnDefinition, CommonTableExpr, CompoundOperator, - CompoundSelect, CreateTableBody, DeferSubclause, Distinctness, Expr, ForeignKeyClause, - FrameBound, FrameClause, FrameExclude, FrameMode, FromClause, FunctionTail, GroupBy, Indexed, - IndexedColumn, InitDeferredPred, InsertBody, JoinConstraint, JoinOperator, JoinType, - JoinedSelectTable, LikeOperator, Limit, Literal, Materialized, Name, NamedColumnConstraint, - NamedTableConstraint, NullsOrder, OneSelect, Operator, Over, PragmaBody, PragmaValue, - QualifiedName, RefAct, RefArg, ResolveType, ResultColumn, Select, SelectBody, SelectTable, Set, - SortOrder, SortedColumn, Stmt, TableConstraint, TableOptions, TransactionType, TriggerCmd, - TriggerEvent, TriggerTime, Type, TypeSize, UnaryOperator, Upsert, UpsertDo, UpsertIndex, - Window, WindowDef, With, + AlterTable, AlterTableBody, As, Cmd, ColumnConstraint, ColumnDefinition, CommonTableExpr, + CompoundOperator, CompoundSelect, CreateTableBody, CreateVirtualTable, DeferSubclause, + Distinctness, Expr, ForeignKeyClause, FrameBound, FrameClause, FrameExclude, FrameMode, + FromClause, FunctionTail, GroupBy, Indexed, IndexedColumn, InitDeferredPred, InsertBody, + JoinConstraint, JoinOperator, JoinType, JoinedSelectTable, LikeOperator, Limit, Literal, + Materialized, Name, NamedColumnConstraint, NamedTableConstraint, NullsOrder, OneSelect, + Operator, Over, PragmaBody, PragmaValue, QualifiedName, RefAct, RefArg, ResolveType, + ResultColumn, Select, SelectBody, SelectTable, Set, SortOrder, SortedColumn, Stmt, + TableConstraint, TableOptions, TransactionType, TriggerCmd, TriggerEvent, TriggerTime, Type, + TypeSize, UnaryOperator, Update, Upsert, UpsertDo, UpsertIndex, Window, WindowDef, With, }; use crate::error::Error; use crate::lexer::{Lexer, Token}; @@ -172,8 +172,12 @@ impl<'a> Parser<'a> { } } + pub fn offset(&self) -> usize { + self.lexer.offset + } + // entrypoint of parsing - fn next_cmd(&mut self) -> Result, Error> { + pub fn next_cmd(&mut self) -> Result, Error> { // consumes prefix SEMI while let Some(token) = self.peek()? { if token.token_type == Some(TK_SEMI) { @@ -730,6 +734,22 @@ impl<'a> Parser<'a> { }) } + fn parse_create_materialized_view(&mut self) -> Result { + eat_assert!(self, TK_MATERIALIZED); + eat_assert!(self, TK_VIEW); + let if_not_exists = self.parse_if_not_exists()?; + let view_name = self.parse_fullname(false)?; + let columns = self.parse_eid_list()?; + eat_expect!(self, TK_AS); + let select = self.parse_select()?; + Ok(Stmt::CreateMaterializedView { + if_not_exists, + view_name, + columns, + select, + }) + } + fn parse_vtab_arg(&mut self) -> Result { let tok = self.peek_no_eof()?; @@ -808,18 +828,26 @@ impl<'a> Parser<'a> { _ => vec![], }; - Ok(Stmt::CreateVirtualTable { + Ok(Stmt::CreateVirtualTable(CreateVirtualTable { if_not_exists, tbl_name, module_name, args, - }) + })) } fn parse_create_stmt(&mut self) -> Result { eat_assert!(self, TK_CREATE); let mut first_tok = peek_expect!( - self, TK_TEMP, TK_TABLE, TK_VIRTUAL, TK_VIEW, TK_INDEX, TK_UNIQUE, TK_TRIGGER + self, + TK_TEMP, + TK_TABLE, + TK_VIRTUAL, + TK_VIEW, + TK_INDEX, + TK_UNIQUE, + TK_TRIGGER, + TK_MATERIALIZED ); let mut temp = false; if first_tok.token_type == Some(TK_TEMP) { @@ -831,6 +859,7 @@ impl<'a> Parser<'a> { match first_tok.token_type.unwrap() { TK_TABLE => self.parse_create_table(temp), TK_VIEW => self.parse_create_view(temp), + TK_MATERIALIZED => self.parse_create_materialized_view(), TK_TRIGGER => self.parse_create_trigger(temp), TK_VIRTUAL => self.parse_create_virtual(), TK_INDEX | TK_UNIQUE => self.parse_create_index(), @@ -3265,20 +3294,20 @@ impl<'a> Parser<'a> { eat_assert!(self, TK_COLUMNKW); } - Ok(Stmt::AlterTable { + Ok(Stmt::AlterTable(AlterTable { name: tbl_name, body: AlterTableBody::AddColumn(self.parse_column_definition()?), - }) + })) } TK_DROP => { if self.peek_no_eof()?.token_type == Some(TK_COLUMNKW) { eat_assert!(self, TK_COLUMNKW); } - Ok(Stmt::AlterTable { + Ok(Stmt::AlterTable(AlterTable { name: tbl_name, body: AlterTableBody::DropColumn(self.parse_nm()?), - }) + })) } TK_RENAME => { let col_name = match self.peek_no_eof()?.token_type.unwrap().fallback_id_if_ok() { @@ -3294,18 +3323,18 @@ impl<'a> Parser<'a> { let to_name = self.parse_nm()?; if let Some(col_name) = col_name { - Ok(Stmt::AlterTable { + Ok(Stmt::AlterTable(AlterTable { name: tbl_name, body: AlterTableBody::RenameColumn { old: col_name, new: to_name, }, - }) + })) } else { - Ok(Stmt::AlterTable { + Ok(Stmt::AlterTable(AlterTable { name: tbl_name, body: AlterTableBody::RenameTo(to_name), - }) + })) } } _ => unreachable!(), @@ -3744,7 +3773,7 @@ impl<'a> Parser<'a> { let returning = self.parse_returning()?; let order_by = self.parse_order_by()?; let limit = self.parse_limit()?; - Ok(Stmt::Update { + Ok(Stmt::Update(Update { with, or_conflict: resolve_type, tbl_name, @@ -3755,7 +3784,7 @@ impl<'a> Parser<'a> { returning, order_by, limit, - }) + })) } fn parse_update(&mut self) -> Result { @@ -4095,6 +4124,28 @@ mod tests { limit: None, }))], ), + ( + b"SELECT ?".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::Variable("".to_owned())), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), ( b"SELECT ?1".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { @@ -8755,59 +8806,59 @@ mod tests { // parse alter ( b"ALTER TABLE foo RENAME TO bar".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::RenameTo(Name::Ident("bar".to_owned())), - })], + }))], ), ( b"ALTER TABLE foo RENAME baz TO bar".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::RenameColumn { old: Name::Ident("baz".to_owned()), new: Name::Ident("bar".to_owned()) }, - })], + }))], ), ( b"ALTER TABLE foo RENAME COLUMN baz TO bar".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::RenameColumn { old: Name::Ident("baz".to_owned()), new: Name::Ident("bar".to_owned()) }, - })], + }))], ), ( b"ALTER TABLE foo DROP baz".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::DropColumn(Name::Ident("baz".to_owned())), - })], + }))], ), ( b"ALTER TABLE foo DROP COLUMN baz".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::DropColumn(Name::Ident("baz".to_owned())), - })], + }))], ), ( b"ALTER TABLE foo ADD baz".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: None, constraints: vec![], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -8817,11 +8868,11 @@ mod tests { }), constraints: vec![], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER DEFAULT 1".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -8838,11 +8889,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER DEFAULT (1)".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -8861,11 +8912,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER DEFAULT +1".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -8885,11 +8936,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER DEFAULT -1".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -8909,11 +8960,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER DEFAULT hello".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -8930,11 +8981,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER NULL".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -8952,11 +9003,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER NOT NULL ON CONFLICT IGNORE".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -8974,11 +9025,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER NOT NULL ON CONFLICT REPLACE".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -8996,11 +9047,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER NOT NULL ON CONFLICT ROLLBACK".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9018,11 +9069,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER NOT NULL ON CONFLICT ROLLBACK".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9040,95 +9091,11 @@ mod tests { }, ], }), - })], - ), - ( - b"ALTER TABLE foo ADD COLUMN baz INTEGER PRIMARY KEY".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { - name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, - body: AlterTableBody::AddColumn(ColumnDefinition { - col_name: Name::Ident("baz".to_owned()), - col_type: Some(Type { - name: "INTEGER".to_owned(), - size: None, - }), - constraints: vec![ - NamedColumnConstraint { - name: None, - constraint: ColumnConstraint::PrimaryKey { - order: None, - conflict_clause: None, - auto_increment: false, - } - }, - ], - }), - })], - ), - ( - b"ALTER TABLE foo ADD COLUMN baz INTEGER PRIMARY KEY ASC ON CONFLICT ROLLBACK AUTOINCREMENT".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { - name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, - body: AlterTableBody::AddColumn(ColumnDefinition { - col_name: Name::Ident("baz".to_owned()), - col_type: Some(Type { - name: "INTEGER".to_owned(), - size: None, - }), - constraints: vec![ - NamedColumnConstraint { - name: None, - constraint: ColumnConstraint::PrimaryKey { - order: Some(SortOrder::Asc), - conflict_clause: Some(ResolveType::Rollback), - auto_increment: true, - } - }, - ], - }), - })], - ), - ( - b"ALTER TABLE foo ADD COLUMN baz INTEGER UNIQUE".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { - name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, - body: AlterTableBody::AddColumn(ColumnDefinition { - col_name: Name::Ident("baz".to_owned()), - col_type: Some(Type { - name: "INTEGER".to_owned(), - size: None, - }), - constraints: vec![ - NamedColumnConstraint { - name: None, - constraint: ColumnConstraint::Unique(None), - }, - ], - }), - })], - ), - ( - b"ALTER TABLE foo ADD COLUMN baz INTEGER UNIQUE ON CONFLICT ROLLBACK".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { - name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, - body: AlterTableBody::AddColumn(ColumnDefinition { - col_name: Name::Ident("baz".to_owned()), - col_type: Some(Type { - name: "INTEGER".to_owned(), - size: None, - }), - constraints: vec![ - NamedColumnConstraint { - name: None, - constraint: ColumnConstraint::Unique(Some(ResolveType::Rollback)), - }, - ], - }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER CHECK (1)".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable(AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9145,11 +9112,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER CHECK (1)".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9166,11 +9133,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9192,11 +9159,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar(test, test_2) MATCH test_3 ON INSERT SET NULL".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9232,11 +9199,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar(test, test_2) MATCH test_3 ON UPDATE SET NULL".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9272,11 +9239,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar(test, test_2) MATCH test_3 ON DELETE SET NULL".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9312,11 +9279,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar(test, test_2) MATCH test_3 ON DELETE SET DEFAULT".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9352,11 +9319,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar(test, test_2) MATCH test_3 ON DELETE CASCADE".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9392,11 +9359,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar(test, test_2) MATCH test_3 ON DELETE RESTRICT".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9432,11 +9399,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar(test, test_2) MATCH test_3 ON DELETE NO ACTION".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9472,11 +9439,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar DEFERRABLE".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9501,11 +9468,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar NOT DEFERRABLE INITIALLY IMMEDIATE".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9530,11 +9497,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar NOT DEFERRABLE INITIALLY DEFERRED".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9559,11 +9526,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar NOT DEFERRABLE INITIALLY DEFERRED".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9588,11 +9555,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER COLLATE bar".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9609,11 +9576,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER GENERATED ALWAYS AS (1)".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9631,11 +9598,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER AS (1)".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9653,11 +9620,11 @@ mod tests { }, ], }), - })], + }))], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER AS (1) STORED".as_slice(), - vec![Cmd::Stmt(Stmt::AlterTable { + vec![Cmd::Stmt(Stmt::AlterTable (AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -9675,7 +9642,7 @@ mod tests { }, ], }), - })], + }))], ), // parse create index ( @@ -9773,7 +9740,7 @@ mod tests { })], ), ( - b"CREATE TEMP TABLE IF NOT EXISTS foo (bar, baz INTEGER, CONSTRAINT tbl_cons PRIMARY KEY (bar AUTOINCREMENT) ON CONFLICT ROLLBACK) STRICT".as_slice(), + b"CREATE TEMP TABLE IF NOT EXISTS foo (baz INTEGER, CONSTRAINT tbl_cons PRIMARY KEY (bar AUTOINCREMENT) ON CONFLICT ROLLBACK) STRICT".as_slice(), vec![Cmd::Stmt(Stmt::CreateTable { temporary: true, if_not_exists: true, @@ -9784,11 +9751,6 @@ mod tests { }, body: CreateTableBody::ColumnsAndConstraints { columns: vec![ - ColumnDefinition { - col_name: Name::Ident("bar".to_owned()), - col_type: None, - constraints: vec![], - }, ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { @@ -9819,7 +9781,7 @@ mod tests { })], ), ( - b"CREATE TEMP TABLE IF NOT EXISTS foo (bar, baz INTEGER, UNIQUE (bar) ON CONFLICT ROLLBACK) WITHOUT ROWID".as_slice(), + b"CREATE TEMP TABLE IF NOT EXISTS foo (bar INTEGER PRIMARY KEY, baz INTEGER, UNIQUE (bar) ON CONFLICT ROLLBACK) WITHOUT ROWID".as_slice(), vec![Cmd::Stmt(Stmt::CreateTable { temporary: true, if_not_exists: true, @@ -9832,8 +9794,20 @@ mod tests { columns: vec![ ColumnDefinition { col_name: Name::Ident("bar".to_owned()), - col_type: None, - constraints: vec![], + col_type: Some(Type { + name: "INTEGER".to_owned(), + size: None, + }), + constraints: vec![ + NamedColumnConstraint { + name: None, + constraint: ColumnConstraint::PrimaryKey { + order: None, + conflict_clause: None, + auto_increment: false, + } + } + ], }, ColumnDefinition { col_name: Name::Ident("baz".to_owned()), @@ -10636,7 +10610,7 @@ mod tests { ), // parse create view ( - b"CREATE VIEW foo AS SELECT 1".as_slice(), + b"CREATE VIEW foo(bar) AS SELECT 1".as_slice(), vec![Cmd::Stmt(Stmt::CreateView { temporary: false, if_not_exists: false, @@ -10645,7 +10619,13 @@ mod tests { name: Name::Ident("foo".to_owned()), alias: None, }, - columns: vec![], + columns: vec![ + IndexedColumn { + col_name: Name::Ident("bar".to_owned()), + collation_name: None, + order: None, + } + ], select: Select { with: None, body: SelectBody { @@ -10708,7 +10688,7 @@ mod tests { // parse CREATE VIRTUAL TABLE ( b"CREATE VIRTUAL TABLE foo USING bar".as_slice(), - vec![Cmd::Stmt(Stmt::CreateVirtualTable { + vec![Cmd::Stmt(Stmt::CreateVirtualTable(CreateVirtualTable { if_not_exists: false, tbl_name: QualifiedName { db_name: None, @@ -10717,11 +10697,11 @@ mod tests { }, module_name: Name::Ident("bar".to_owned()), args: vec![], - })], + }))], ), ( b"CREATE VIRTUAL TABLE foo USING bar()".as_slice(), - vec![Cmd::Stmt(Stmt::CreateVirtualTable { + vec![Cmd::Stmt(Stmt::CreateVirtualTable(CreateVirtualTable{ if_not_exists: false, tbl_name: QualifiedName { db_name: None, @@ -10730,11 +10710,11 @@ mod tests { }, module_name: Name::Ident("bar".to_owned()), args: vec![], - })], + }))], ), ( b"CREATE VIRTUAL TABLE IF NOT EXISTS foo USING bar(1, 2, ('hello', (3.333), 'world', (1, 2)))".as_slice(), - vec![Cmd::Stmt(Stmt::CreateVirtualTable { + vec![Cmd::Stmt(Stmt::CreateVirtualTable(CreateVirtualTable{ if_not_exists: true, tbl_name: QualifiedName { db_name: None, @@ -10747,11 +10727,11 @@ mod tests { "2".to_owned(), "('hello', (3.333), 'world', (1, 2))".to_owned(), ], - })], + }))], ), ( b"CREATE VIRTUAL TABLE ft USING fts5(x, tokenize = '''porter'' ''ascii''')".as_slice(), - vec![Cmd::Stmt(Stmt::CreateVirtualTable { + vec![Cmd::Stmt(Stmt::CreateVirtualTable(CreateVirtualTable { if_not_exists: false, tbl_name: QualifiedName { db_name: None, @@ -10763,7 +10743,7 @@ mod tests { "x".to_owned(), "tokenize = '''porter'' ''ascii'''".to_owned(), ], - })], + }))], ), // parse delete ( @@ -11094,7 +11074,7 @@ mod tests { // parse update ( b"UPDATE foo SET bar = 1".as_slice(), - vec![Cmd::Stmt(Stmt::Update { + vec![Cmd::Stmt(Stmt::Update(Update { with: None, or_conflict: None, tbl_name: QualifiedName { @@ -11116,11 +11096,11 @@ mod tests { returning: vec![], order_by: vec![], limit: None, - })], + }))], ), ( b"WITH test AS (SELECT 1) UPDATE OR REPLACE foo NOT INDEXED SET bar = 1 FROM foo_2 WHERE 1 RETURNING bar ORDER By bar LIMIT 1".as_slice(), - vec![Cmd::Stmt(Stmt::Update { + vec![Cmd::Stmt(Stmt::Update(Update { with: Some(With { recursive: false, ctes: vec![ @@ -11195,7 +11175,7 @@ mod tests { expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), offset: None, }), - })], + }))], ), // parse reindex (