pub mod check; pub mod fmt; use strum_macros::{EnumIter, EnumString}; use crate::ast::fmt::ToTokens; /// `?` or `$` Prepared statement arg placeholder(s) #[derive(Default)] pub struct ParameterInfo { /// Number of SQL parameters in a prepared statement, like `sqlite3_bind_parameter_count` pub count: u32, /// Parameter name(s) if any pub names: Vec, } /// Statement or Explain statement // https://sqlite.org/syntax/sql-stmt.html #[derive(Clone, Debug, PartialEq, Eq)] pub enum Cmd { /// `EXPLAIN` statement Explain(Stmt), /// `EXPLAIN QUERY PLAN` statement ExplainQueryPlan(Stmt), /// statement 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(AlterTable), /// `ANALYSE`: object name Analyze { // object name name: Option, }, /// `ATTACH DATABASE` Attach { /// filename // TODO distinction between ATTACH and ATTACH DATABASE expr: Box, /// schema name db_name: Box, /// password key: Option>, }, /// `BEGIN`: tx type, tx name Begin { // transaction type typ: Option, // transaction name name: Option, }, /// `COMMIT`/`END`: tx name Commit { // tx name name: Option, }, // TODO distinction between COMMIT and END /// `CREATE INDEX` CreateIndex { /// `UNIQUE` unique: bool, /// `IF NOT EXISTS` if_not_exists: bool, /// index name idx_name: QualifiedName, /// table name tbl_name: Name, /// indexed columns or expressions columns: Vec, /// partial index where_clause: Option>, }, /// `CREATE TABLE` CreateTable { /// `TEMPORARY` temporary: bool, // TODO distinction between TEMP and TEMPORARY /// `IF NOT EXISTS` if_not_exists: bool, /// table name tbl_name: QualifiedName, /// table body body: CreateTableBody, }, /// `CREATE TRIGGER` CreateTrigger { /// `TEMPORARY` temporary: bool, /// `IF NOT EXISTS` if_not_exists: bool, /// trigger name trigger_name: QualifiedName, /// `BEFORE`/`AFTER`/`INSTEAD OF` time: Option, /// `DELETE`/`INSERT`/`UPDATE` event: TriggerEvent, /// table name tbl_name: QualifiedName, /// `FOR EACH ROW` for_each_row: bool, /// `WHEN` when_clause: Option>, /// statements commands: Vec, }, /// `CREATE VIEW` CreateView { /// `TEMPORARY` temporary: bool, /// `IF NOT EXISTS` if_not_exists: bool, /// view name view_name: QualifiedName, /// columns columns: Vec, /// query select: Select, }, /// `CREATE MATERIALIZED VIEW` CreateMaterializedView { /// `IF NOT EXISTS` if_not_exists: bool, /// view name view_name: QualifiedName, /// columns columns: Vec, /// query select: Select, }, /// `CREATE VIRTUAL TABLE` CreateVirtualTable(CreateVirtualTable), /// `DELETE` Delete { /// CTE with: Option, /// `FROM` table name tbl_name: QualifiedName, /// `INDEXED` indexed: Option, /// `WHERE` clause where_clause: Option>, /// `RETURNING` returning: Vec, /// `ORDER BY` order_by: Vec, /// `LIMIT` limit: Option, }, /// `DETACH DATABASE`: db name Detach { // db name name: Box, }, // TODO distinction between DETACH and DETACH DATABASE /// `DROP INDEX` DropIndex { /// `IF EXISTS` if_exists: bool, /// index name idx_name: QualifiedName, }, /// `DROP TABLE` DropTable { /// `IF EXISTS` if_exists: bool, /// table name tbl_name: QualifiedName, }, /// `DROP TRIGGER` DropTrigger { /// `IF EXISTS` if_exists: bool, /// trigger name trigger_name: QualifiedName, }, /// `DROP VIEW` DropView { /// `IF EXISTS` if_exists: bool, /// view name view_name: QualifiedName, }, /// `INSERT` Insert { /// CTE with: Option, /// `OR` or_conflict: Option, // TODO distinction between REPLACE and INSERT OR REPLACE /// table name tbl_name: QualifiedName, /// `COLUMNS` columns: Vec, /// `VALUES` or `SELECT` body: InsertBody, /// `RETURNING` returning: Vec, }, /// `PRAGMA`: pragma name, body Pragma { // pragma name name: QualifiedName, // pragma body body: Option, }, /// `REINDEX` Reindex { /// collation or index or table name name: Option, }, /// `RELEASE`: savepoint name Release { // savepoint name name: Name, }, // TODO distinction between RELEASE and RELEASE SAVEPOINT /// `ROLLBACK` Rollback { /// transaction name tx_name: Option, /// savepoint name savepoint_name: Option, // TODO distinction between TO and TO SAVEPOINT }, /// `SAVEPOINT`: savepoint name Savepoint { // savepoint name name: Name, }, /// `SELECT` Select(Select), /// `UPDATE` Update(Update), /// `VACUUM`: database name, into expr Vacuum { // database name name: Option, // into expression into: Option>, }, } #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] /// Internal ID of a table reference. /// /// Used by [Expr::Column] and [Expr::RowId] to refer to a table. /// E.g. in 'SELECT * FROM t UNION ALL SELECT * FROM t', there are two table references, /// so there are two TableInternalIds. /// /// 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)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Expr { /// `BETWEEN` Between { /// expression lhs: Box, /// `NOT` not: bool, /// start start: Box, /// end end: Box, }, /// binary expression Binary(Box, Operator, Box), /// Register reference for DBSP expression compilation /// This is not part of SQL syntax but used internally for incremental computation Register(usize), /// `CASE` expression Case { /// operand base: Option>, /// `WHEN` condition `THEN` result when_then_pairs: Vec<(Box, Box)>, /// `ELSE` result else_expr: Option>, }, /// CAST expression Cast { /// expression expr: Box, /// `AS` type name type_name: Option, }, /// `COLLATE`: expression Collate(Box, Name), /// schema-name.table-name.column-name DoublyQualified(Name, Name, Name), /// `EXISTS` subquery Exists(Select), /// call to a built-in function FunctionCall { /// function name name: Name, /// `DISTINCT` distinctness: Option, /// arguments args: Vec>, /// `ORDER BY` order_by: Vec, /// `FILTER` filter_over: FunctionTail, }, /// Function call expression with '*' as arg FunctionCallStar { /// function name name: Name, /// `FILTER` filter_over: FunctionTail, }, /// Identifier Id(Name), /// Column Column { /// the x in `x.y.z`. index of the db in catalog. database: Option, /// the y in `x.y.z`. index of the table in catalog. table: TableInternalId, /// the z in `x.y.z`. index of the column in the table. column: usize, /// is the column a rowid alias is_rowid_alias: bool, }, /// `ROWID` RowId { /// the x in `x.y.z`. index of the db in catalog. database: Option, /// the y in `x.y.z`. index of the table in catalog. table: TableInternalId, }, /// `IN` InList { /// expression lhs: Box, /// `NOT` not: bool, /// values rhs: Vec>, }, /// `IN` subselect InSelect { /// expression lhs: Box, /// `NOT` not: bool, /// subquery rhs: Select, }, /// `IN` table name / function InTable { /// expression lhs: Box, /// `NOT` not: bool, /// table name rhs: QualifiedName, /// table function arguments args: Vec>, }, /// `IS NULL` IsNull(Box), /// `LIKE` Like { /// expression lhs: Box, /// `NOT` not: bool, /// operator op: LikeOperator, /// pattern rhs: Box, /// `ESCAPE` char escape: Option>, }, /// Literal expression Literal(Literal), /// Name Name(Name), /// `NOT NULL` or `NOTNULL` NotNull(Box), /// Parenthesized subexpression Parenthesized(Vec>), /// Qualified name Qualified(Name, Name), /// `RAISE` function call Raise(ResolveType, Option>), /// Subquery expression Subquery(Select), /// Unary expression Unary(UnaryOperator, Box), /// Parameters Variable(String), } impl Expr { pub fn into_boxed(self) -> Box { Box::new(self) } pub fn unary(operator: UnaryOperator, expr: Expr) -> Expr { Expr::Unary(operator, Box::new(expr)) } pub fn binary(lhs: Expr, operator: Operator, rhs: Expr) -> Expr { Expr::Binary(Box::new(lhs), operator, Box::new(rhs)) } pub fn not_null(expr: Expr) -> Expr { Expr::NotNull(Box::new(expr)) } pub fn between(lhs: Expr, not: bool, start: Expr, end: Expr) -> Expr { Expr::Between { lhs: Box::new(lhs), not, start: Box::new(start), end: Box::new(end), } } pub fn in_select(lhs: Expr, not: bool, select: Select) -> Expr { Expr::InSelect { lhs: Box::new(lhs), not, rhs: select, } } pub fn like( lhs: Expr, not: bool, operator: LikeOperator, rhs: Expr, escape: Option, ) -> Expr { Expr::Like { lhs: Box::new(lhs), not, op: operator, rhs: Box::new(rhs), escape: escape.map(Box::new), } } pub fn is_null(expr: Expr) -> Expr { Expr::IsNull(Box::new(expr)) } pub fn collate(expr: Expr, name: Name) -> Expr { Expr::Collate(Box::new(expr), name) } pub fn cast(expr: Expr, type_name: Option) -> Expr { Expr::Cast { expr: Box::new(expr), type_name, } } pub fn raise(resolve_type: ResolveType, expr: Option) -> Expr { Expr::Raise(resolve_type, expr.map(Box::new)) } } /// SQL literal #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Literal { /// Number Numeric(String), /// String // TODO Check that string is already quoted and correctly escaped String(String), /// BLOB // TODO Check that string is valid (only hexa) Blob(String), /// Keyword Keyword(String), /// `NULL` Null, /// `CURRENT_DATE` CurrentDate, /// `CURRENT_TIME` CurrentTime, /// `CURRENT_TIMESTAMP` CurrentTimestamp, } /// Textual comparison operator in an expression #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum LikeOperator { /// `GLOB` Glob, /// `LIKE` Like, /// `MATCH` Match, /// `REGEXP` Regexp, } /// SQL operators #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Operator { /// `+` Add, /// `AND` And, /// `->` ArrowRight, /// `->>` ArrowRightShift, /// `&` BitwiseAnd, /// `|` BitwiseOr, /// `~` BitwiseNot, /// String concatenation (`||`) Concat, /// `=` or `==` Equals, /// `/` Divide, /// `>` Greater, /// `>=` GreaterEquals, /// `IS` Is, /// `IS NOT` IsNot, /// `<<` LeftShift, /// `<` Less, /// `<=` LessEquals, /// `%` Modulus, /// `*` Multiply, /// `!=` or `<>` NotEquals, /// `OR` Or, /// `>>` RightShift, /// `-` 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))] pub enum UnaryOperator { /// bitwise negation (`~`) BitwiseNot, /// negative-sign Negative, /// `NOT` Not, /// positive-sign Positive, } /// `SELECT` statement // https://sqlite.org/lang_select.html // https://sqlite.org/syntax/factored-select-stmt.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Select { /// CTE pub with: Option, /// body pub body: SelectBody, /// `ORDER BY` pub order_by: Vec, // ORDER BY term does not match any column in the result set /// `LIMIT` pub limit: Option, } /// `SELECT` body #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct SelectBody { /// first select pub select: OneSelect, /// compounds pub compounds: Vec, } /// Compound select #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CompoundSelect { /// operator pub operator: CompoundOperator, /// select pub select: OneSelect, } /// Compound operators // https://sqlite.org/syntax/compound-operator.html #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum CompoundOperator { /// `UNION` Union, /// `UNION ALL` UnionAll, /// `EXCEPT` Except, /// `INTERSECT` Intersect, } /// `SELECT` core // https://sqlite.org/syntax/select-core.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum OneSelect { /// `SELECT` Select { /// `DISTINCT` distinctness: Option, /// columns columns: Vec, /// `FROM` clause from: Option, /// `WHERE` clause where_clause: Option>, /// `GROUP BY` group_by: Option, /// `WINDOW` definition window_clause: Vec, }, /// `VALUES` Values(Vec>>), } /// `SELECT` ... `FROM` clause // https://sqlite.org/syntax/join-clause.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct FromClause { /// table pub select: Box, // FIXME mandatory /// `JOIN`ed tabled pub joins: Vec, } /// `SELECT` distinctness #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Distinctness { /// `DISTINCT` Distinct, /// `ALL` All, } /// `SELECT` or `RETURNING` result column // https://sqlite.org/syntax/result-column.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum ResultColumn { /// expression Expr(Box, Option), /// `*` Star, /// table name.`*` TableStar(Name), } /// Alias #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum As { /// `AS` As(Name), /// no `AS` Elided(Name), // FIXME Ids } /// `JOIN` clause // https://sqlite.org/syntax/join-clause.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct JoinedSelectTable { /// operator pub operator: JoinOperator, /// table pub table: Box, /// constraint pub constraint: Option, } /// Table or subquery // https://sqlite.org/syntax/table-or-subquery.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum SelectTable { /// table Table(QualifiedName, Option, Option), /// table function call TableCall(QualifiedName, Vec>, Option), /// `SELECT` subquery Select(Select, Option), /// subquery Sub(FromClause, Option), } /// Join operators // https://sqlite.org/syntax/join-operator.html #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum JoinOperator { /// `,` Comma, /// `JOIN` TypedJoin(Option), } // https://github.com/sqlite/sqlite/blob/80511f32f7e71062026edd471913ef0455563964/src/select.c#L197-L257 bitflags::bitflags! { /// `JOIN` types #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct JoinType: u8 { /// `INNER` const INNER = 0x01; /// `CROSS` => INNER|CROSS const CROSS = 0x02; /// `NATURAL` const NATURAL = 0x04; /// `LEFT` => LEFT|OUTER const LEFT = 0x08; /// `RIGHT` => RIGHT|OUTER const RIGHT = 0x10; /// `OUTER` const OUTER = 0x20; } } /// `JOIN` constraint #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum JoinConstraint { /// `ON` On(Box), /// `USING`: col names Using(Vec), } /// `GROUP BY` #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct GroupBy { /// expressions pub exprs: Vec>, /// `HAVING` pub having: Option>, // HAVING clause on a non-aggregate query } /// identifier or string or `CROSS` or `FULL` or `INNER` or `LEFT` or `NATURAL` or `OUTER` or `RIGHT`. #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Name { /// Identifier Ident(String), /// Quoted values Quoted(String), } 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 #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct QualifiedName { /// schema pub db_name: Option, /// object name pub name: Name, /// alias 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) } } impl QualifiedName { /// Constructor pub fn single(name: Name) -> Self { Self { db_name: None, name, alias: None, } } /// Constructor pub fn fullname(db_name: Name, name: Name) -> Self { Self { db_name: Some(db_name), name, alias: None, } } /// Constructor pub fn xfullname(db_name: Name, name: Name, alias: Name) -> Self { Self { db_name: Some(db_name), name, alias: Some(alias), } } /// Constructor pub fn alias(name: Name, alias: Name) -> Self { Self { db_name: None, name, alias: Some(alias), } } } /// `ALTER TABLE` body // https://sqlite.org/lang_altertable.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum AlterTableBody { /// `RENAME TO`: new table name RenameTo(Name), /// `ADD COLUMN` AddColumn(ColumnDefinition), // TODO distinction between ADD and ADD COLUMN /// `ALTER COLUMN` AlterColumn { old: Name, new: ColumnDefinition }, /// `RENAME COLUMN` RenameColumn { /// old name old: Name, /// new name new: Name, }, /// `DROP COLUMN` DropColumn(Name), // TODO distinction between DROP and DROP COLUMN } /// `CREATE TABLE` body // https://sqlite.org/lang_createtable.html // https://sqlite.org/syntax/create-table-stmt.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum CreateTableBody { /// columns and constraints ColumnsAndConstraints { /// table column definitions columns: Vec, /// table constraints constraints: Vec, /// table options options: TableOptions, }, /// `AS` select AsSelect(Select), } /// Table column definition // https://sqlite.org/syntax/column-def.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ColumnDefinition { /// column name pub col_name: Name, /// column type pub col_type: Option, /// column constraints pub constraints: Vec, } /// Named column constraint // https://sqlite.org/syntax/column-constraint.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct NamedColumnConstraint { /// constraint name pub name: Option, /// constraint pub constraint: ColumnConstraint, } /// Column constraint // https://sqlite.org/syntax/column-constraint.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum ColumnConstraint { /// `PRIMARY KEY` PrimaryKey { /// `ASC` / `DESC` order: Option, /// `ON CONFLICT` clause conflict_clause: Option, /// `AUTOINCREMENT` auto_increment: bool, }, /// `NULL` NotNull { /// `NOT` nullable: bool, /// `ON CONFLICT` clause conflict_clause: Option, }, /// `UNIQUE` Unique(Option), /// `CHECK` Check(Box), /// `DEFAULT` Default(Box), /// `COLLATE` Collate { /// collation name collation_name: Name, // FIXME Ids }, /// `REFERENCES` foreign-key clause ForeignKey { /// clause clause: ForeignKeyClause, /// `DEFERRABLE` deref_clause: Option, }, /// `GENERATED` Generated { /// expression expr: Box, /// `STORED` / `VIRTUAL` typ: Option, }, } /// Named table constraint // https://sqlite.org/syntax/table-constraint.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct NamedTableConstraint { /// constraint name pub name: Option, /// constraint pub constraint: TableConstraint, } /// Table constraint // https://sqlite.org/syntax/table-constraint.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum TableConstraint { /// `PRIMARY KEY` PrimaryKey { /// columns columns: Vec, /// `AUTOINCREMENT` auto_increment: bool, /// `ON CONFLICT` clause conflict_clause: Option, }, /// `UNIQUE` Unique { /// columns columns: Vec, /// `ON CONFLICT` clause conflict_clause: Option, }, /// `CHECK` Check(Box), /// `FOREIGN KEY` ForeignKey { /// columns columns: Vec, /// `REFERENCES` clause: ForeignKeyClause, /// `DEFERRABLE` deref_clause: Option, }, } bitflags::bitflags! { /// `CREATE TABLE` options #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TableOptions: u8 { /// None const NONE = 0; /// `WITHOUT ROWID` const WITHOUT_ROWID = 1; /// `STRICT` const STRICT = 2; } } /// Sort orders #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum SortOrder { /// `ASC` Asc, /// `DESC` Desc, } impl core::fmt::Display for SortOrder { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.to_fmt(f) } } /// `NULLS FIRST` or `NULLS LAST` #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum NullsOrder { /// `NULLS FIRST` First, /// `NULLS LAST` Last, } /// `REFERENCES` clause // https://sqlite.org/syntax/foreign-key-clause.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ForeignKeyClause { /// foreign table name pub tbl_name: Name, /// foreign table columns pub columns: Vec, /// referential action(s) / deferrable option(s) pub args: Vec, } /// foreign-key reference args #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum RefArg { /// `ON DELETE` OnDelete(RefAct), /// `ON INSERT` OnInsert(RefAct), /// `ON UPDATE` OnUpdate(RefAct), /// `MATCH` Match(Name), } /// foreign-key reference actions #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum RefAct { /// `SET NULL` SetNull, /// `SET DEFAULT` SetDefault, /// `CASCADE` Cascade, /// `RESTRICT` Restrict, /// `NO ACTION` NoAction, } /// foreign-key defer clause #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DeferSubclause { /// `DEFERRABLE` pub deferrable: bool, /// `INITIALLY` `DEFERRED` / `IMMEDIATE` pub init_deferred: Option, } /// `INITIALLY` `DEFERRED` / `IMMEDIATE` #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum InitDeferredPred { /// `INITIALLY DEFERRED` InitiallyDeferred, /// `INITIALLY IMMEDIATE` InitiallyImmediate, // default } /// Indexed column // https://sqlite.org/syntax/indexed-column.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct IndexedColumn { /// column name pub col_name: Name, /// `COLLATE` pub collation_name: Option, // FIXME Ids /// `ORDER BY` pub order: Option, } /// `INDEXED BY` / `NOT INDEXED` #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Indexed { /// `INDEXED BY`: idx name IndexedBy(Name), /// `NOT INDEXED` NotIndexed, } /// Sorted column #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct SortedColumn { /// expression pub expr: Box, /// `ASC` / `DESC` pub order: Option, /// `NULLS FIRST` / `NULLS LAST` pub nulls: Option, } /// `LIMIT` #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Limit { /// count pub expr: Box, /// `OFFSET` pub offset: Option>, // TODO distinction between LIMIT offset, count and LIMIT count OFFSET offset } /// `INSERT` body // https://sqlite.org/lang_insert.html // https://sqlite.org/syntax/insert-stmt.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[allow(clippy::large_enum_variant)] pub enum InsertBody { /// `SELECT` or `VALUES` Select(Select, Option>), /// `DEFAULT VALUES` DefaultValues, } /// `UPDATE ... SET` #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Set { /// column name(s) pub col_names: Vec, /// expression pub expr: Box, } /// `PRAGMA` body // https://sqlite.org/syntax/pragma-stmt.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum PragmaBody { /// `=` Equals(PragmaValue), /// function call Call(PragmaValue), } /// `PRAGMA` value // https://sqlite.org/syntax/pragma-value.html pub type PragmaValue = Box; // TODO /// `PRAGMA` value // https://sqlite.org/pragma.html #[derive(Clone, Debug, PartialEq, Eq, EnumIter, EnumString, strum::Display)] #[strum(serialize_all = "snake_case")] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum PragmaName { /// Returns the application ID of the database file. ApplicationId, /// set the autovacuum mode AutoVacuum, /// `cache_size` pragma CacheSize, /// encryption cipher algorithm name for encrypted databases #[strum(serialize = "cipher")] #[cfg_attr(feature = "serde", serde(rename = "cipher"))] EncryptionCipher, /// List databases 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, specified as hexadecimal string. #[strum(serialize = "hexkey")] #[cfg_attr(feature = "serde", serde(rename = "hexkey"))] 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, /// Control database synchronization mode (OFF | FULL | NORMAL | EXTRA) Synchronous, /// returns information about the columns of a table TableInfo, /// enable capture-changes logic for the connection UnstableCaptureDataChangesConn, /// Returns the user version of the database file. UserVersion, /// trigger a checkpoint to run on database(s) if WAL is enabled WalCheckpoint, } /// `CREATE TRIGGER` time #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum TriggerTime { /// `BEFORE` Before, // default /// `AFTER` After, /// `INSTEAD OF` InsteadOf, } /// `CREATE TRIGGER` event #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum TriggerEvent { /// `DELETE` Delete, /// `INSERT` Insert, /// `UPDATE` Update, /// `UPDATE OF`: col names UpdateOf(Vec), } /// `CREATE TRIGGER` command // https://sqlite.org/lang_createtrigger.html // https://sqlite.org/syntax/create-trigger-stmt.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum TriggerCmd { /// `UPDATE` Update { /// `OR` or_conflict: Option, /// table name tbl_name: Name, /// `SET` assignments sets: Vec, /// `FROM` from: Option, /// `WHERE` clause where_clause: Option>, }, /// `INSERT` Insert { /// `OR` or_conflict: Option, /// table name tbl_name: Name, /// `COLUMNS` col_names: Vec, /// `SELECT` or `VALUES` select: Select, /// `ON CONFLICT` clause upsert: Option>, /// `RETURNING` returning: Vec, }, /// `DELETE` Delete { /// table name tbl_name: Name, /// `WHERE` clause where_clause: Option>, }, /// `SELECT` Select(Select), } /// Conflict resolution types #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum ResolveType { /// `ROLLBACK` Rollback, /// `ABORT` Abort, // default /// `FAIL` Fail, /// `IGNORE` Ignore, /// `REPLACE` 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 #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct With { /// `RECURSIVE` pub recursive: bool, /// CTEs pub ctes: Vec, } /// CTE materialization #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Materialized { /// No hint Any, /// `MATERIALIZED` Yes, /// `NOT MATERIALIZED` No, } /// CTE // https://sqlite.org/syntax/common-table-expression.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CommonTableExpr { /// table name pub tbl_name: Name, /// table columns pub columns: Vec, // check no duplicate /// `MATERIALIZED` pub materialized: Materialized, /// query pub select: Select, } /// Column type // https://sqlite.org/syntax/type-name.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Type { /// type name pub name: String, // TODO Validate: Ids+ /// type size pub size: Option, } /// Column type size limit(s) // https://sqlite.org/syntax/type-name.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum TypeSize { /// maximum size MaxSize(Box), /// precision TypeSize(Box, Box), } /// Transaction types #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum TransactionType { /// `DEFERRED` Deferred, // default /// `IMMEDIATE` Immediate, /// `EXCLUSIVE` Exclusive, } /// Upsert clause // https://sqlite.org/lang_upsert.html // https://sqlite.org/syntax/upsert-clause.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Upsert { /// conflict targets pub index: Option, /// `DO` clause pub do_clause: UpsertDo, /// next upsert pub next: Option>, } /// Upsert conflict targets #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct UpsertIndex { /// columns pub targets: Vec, /// `WHERE` clause pub where_clause: Option>, } /// Upsert `DO` action #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum UpsertDo { /// `SET` Set { /// assignments sets: Vec, /// `WHERE` clause where_clause: Option>, }, /// `NOTHING` Nothing, } /// Function call tail #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct FunctionTail { /// `FILTER` clause pub filter_clause: Option>, /// `OVER` clause pub over_clause: Option, } /// Function call `OVER` clause // https://sqlite.org/syntax/over-clause.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Over { /// Window definition Window(Window), /// Window name Name(Name), } /// `OVER` window definition #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct WindowDef { /// window name pub name: Name, /// window definition pub window: Window, } /// Window definition // https://sqlite.org/syntax/window-defn.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Window { /// base window name pub base: Option, /// `PARTITION BY` pub partition_by: Vec>, /// `ORDER BY` pub order_by: Vec, /// frame spec pub frame_clause: Option, } /// Frame specification // https://sqlite.org/syntax/frame-spec.html #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct FrameClause { /// unit pub mode: FrameMode, /// start bound pub start: FrameBound, /// end bound pub end: Option, /// `EXCLUDE` pub exclude: Option, } /// Frame modes #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum FrameMode { /// `GROUPS` Groups, /// `RANGE` Range, /// `ROWS` Rows, } /// Frame bounds #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum FrameBound { /// `CURRENT ROW` CurrentRow, /// `FOLLOWING` Following(Box), /// `PRECEDING` Preceding(Box), /// `UNBOUNDED FOLLOWING` UnboundedFollowing, /// `UNBOUNDED PRECEDING` UnboundedPreceding, } /// Frame exclusions #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum FrameExclude { /// `NO OTHERS` NoOthers, /// `CURRENT ROW` CurrentRow, /// `GROUP` Group, /// `TIES` Ties, }