mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-25 12:04:21 +01:00
Currently, when MVCC is enabled, every transaction mode supports concurrent reads and writes, which makes it hard to adopt for existing applications that use `BEGIN DEFERRED` or `BEGIN IMMEDIATE`. Therefore, add support for `BEGIN CONCURRENT` transactions when MVCC is enabled. The transaction mode allows multiple concurrent read/write transactions that don't block each other, with conflicts resolved at commit time. Furthermore, implement the correct semantics for `BEGIN DEFERRED` and `BEGIN IMMEDIATE` by taking advantage of the pager level write lock when transaction upgrades to write. This means that now concurrent MVCC transactions are serialized against the legacy ones when needed. The implementation includes: - Parser support for CONCURRENT keyword in BEGIN statements - New Concurrent variant in TransactionMode to distinguish from regular read/write transactions - MVCC store tracking of exclusive transactions to support IMMEDIATE and EXCLUSIVE modes alongside CONCURRENT - Proper transaction state management for all transaction types in MVCC This enables better concurrency for applications that can handle optimistic concurrency control, while still supporting traditional SQLite transaction semantics via IMMEDIATE and EXCLUSIVE modes.
1678 lines
43 KiB
Rust
1678 lines
43 KiB
Rust
pub mod check;
|
|
pub mod fmt;
|
|
|
|
use strum_macros::{EnumIter, EnumString};
|
|
|
|
/// `?` 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<String>,
|
|
}
|
|
|
|
/// 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<String>, // TODO smol str
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct Update {
|
|
/// CTE
|
|
pub with: Option<With>,
|
|
/// `OR`
|
|
pub or_conflict: Option<ResolveType>,
|
|
/// table name
|
|
pub tbl_name: QualifiedName,
|
|
/// `INDEXED`
|
|
pub indexed: Option<Indexed>,
|
|
/// `SET` assignments
|
|
pub sets: Vec<Set>,
|
|
/// `FROM`
|
|
pub from: Option<FromClause>,
|
|
/// `WHERE` clause
|
|
pub where_clause: Option<Box<Expr>>,
|
|
/// `RETURNING`
|
|
pub returning: Vec<ResultColumn>,
|
|
/// `ORDER BY`
|
|
pub order_by: Vec<SortedColumn>,
|
|
/// `LIMIT`
|
|
pub limit: Option<Limit>,
|
|
}
|
|
|
|
#[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<QualifiedName>,
|
|
},
|
|
/// `ATTACH DATABASE`
|
|
Attach {
|
|
/// filename
|
|
// TODO distinction between ATTACH and ATTACH DATABASE
|
|
expr: Box<Expr>,
|
|
/// schema name
|
|
db_name: Box<Expr>,
|
|
/// password
|
|
key: Option<Box<Expr>>,
|
|
},
|
|
/// `BEGIN`: tx type, tx name
|
|
Begin {
|
|
// transaction type
|
|
typ: Option<TransactionType>,
|
|
// transaction name
|
|
name: Option<Name>,
|
|
},
|
|
/// `COMMIT`/`END`: tx name
|
|
Commit {
|
|
// tx name
|
|
name: Option<Name>,
|
|
}, // 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<SortedColumn>,
|
|
/// partial index
|
|
where_clause: Option<Box<Expr>>,
|
|
},
|
|
/// `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<TriggerTime>,
|
|
/// `DELETE`/`INSERT`/`UPDATE`
|
|
event: TriggerEvent,
|
|
/// table name
|
|
tbl_name: QualifiedName,
|
|
/// `FOR EACH ROW`
|
|
for_each_row: bool,
|
|
/// `WHEN`
|
|
when_clause: Option<Box<Expr>>,
|
|
/// statements
|
|
commands: Vec<TriggerCmd>,
|
|
},
|
|
/// `CREATE VIEW`
|
|
CreateView {
|
|
/// `TEMPORARY`
|
|
temporary: bool,
|
|
/// `IF NOT EXISTS`
|
|
if_not_exists: bool,
|
|
/// view name
|
|
view_name: QualifiedName,
|
|
/// columns
|
|
columns: Vec<IndexedColumn>,
|
|
/// query
|
|
select: Select,
|
|
},
|
|
/// `CREATE MATERIALIZED VIEW`
|
|
CreateMaterializedView {
|
|
/// `IF NOT EXISTS`
|
|
if_not_exists: bool,
|
|
/// view name
|
|
view_name: QualifiedName,
|
|
/// columns
|
|
columns: Vec<IndexedColumn>,
|
|
/// query
|
|
select: Select,
|
|
},
|
|
|
|
/// `CREATE VIRTUAL TABLE`
|
|
CreateVirtualTable(CreateVirtualTable),
|
|
/// `DELETE`
|
|
Delete {
|
|
/// CTE
|
|
with: Option<With>,
|
|
/// `FROM` table name
|
|
tbl_name: QualifiedName,
|
|
/// `INDEXED`
|
|
indexed: Option<Indexed>,
|
|
/// `WHERE` clause
|
|
where_clause: Option<Box<Expr>>,
|
|
/// `RETURNING`
|
|
returning: Vec<ResultColumn>,
|
|
/// `ORDER BY`
|
|
order_by: Vec<SortedColumn>,
|
|
/// `LIMIT`
|
|
limit: Option<Limit>,
|
|
},
|
|
/// `DETACH DATABASE`: db name
|
|
Detach {
|
|
// db name
|
|
name: Box<Expr>,
|
|
}, // 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<With>,
|
|
/// `OR`
|
|
or_conflict: Option<ResolveType>, // TODO distinction between REPLACE and INSERT OR REPLACE
|
|
/// table name
|
|
tbl_name: QualifiedName,
|
|
/// `COLUMNS`
|
|
columns: Vec<Name>,
|
|
/// `VALUES` or `SELECT`
|
|
body: InsertBody,
|
|
/// `RETURNING`
|
|
returning: Vec<ResultColumn>,
|
|
},
|
|
/// `PRAGMA`: pragma name, body
|
|
Pragma {
|
|
// pragma name
|
|
name: QualifiedName,
|
|
// pragma body
|
|
body: Option<PragmaBody>,
|
|
},
|
|
/// `REINDEX`
|
|
Reindex {
|
|
/// collation or index or table name
|
|
name: Option<QualifiedName>,
|
|
},
|
|
/// `RELEASE`: savepoint name
|
|
Release {
|
|
// savepoint name
|
|
name: Name,
|
|
}, // TODO distinction between RELEASE and RELEASE SAVEPOINT
|
|
/// `ROLLBACK`
|
|
Rollback {
|
|
/// transaction name
|
|
tx_name: Option<Name>,
|
|
/// savepoint name
|
|
savepoint_name: Option<Name>, // 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<Name>,
|
|
// into expression
|
|
into: Option<Box<Expr>>,
|
|
},
|
|
}
|
|
|
|
#[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<usize> for TableInternalId {
|
|
fn from(value: usize) -> Self {
|
|
Self(value)
|
|
}
|
|
}
|
|
|
|
impl std::ops::AddAssign<usize> for TableInternalId {
|
|
fn add_assign(&mut self, rhs: usize) {
|
|
self.0 += rhs;
|
|
}
|
|
}
|
|
|
|
impl From<TableInternalId> 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<Expr>,
|
|
/// `NOT`
|
|
not: bool,
|
|
/// start
|
|
start: Box<Expr>,
|
|
/// end
|
|
end: Box<Expr>,
|
|
},
|
|
/// binary expression
|
|
Binary(Box<Expr>, Operator, Box<Expr>),
|
|
/// 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<Box<Expr>>,
|
|
/// `WHEN` condition `THEN` result
|
|
when_then_pairs: Vec<(Box<Expr>, Box<Expr>)>,
|
|
/// `ELSE` result
|
|
else_expr: Option<Box<Expr>>,
|
|
},
|
|
/// CAST expression
|
|
Cast {
|
|
/// expression
|
|
expr: Box<Expr>,
|
|
/// `AS` type name
|
|
type_name: Option<Type>,
|
|
},
|
|
/// `COLLATE`: expression
|
|
Collate(Box<Expr>, 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<Distinctness>,
|
|
/// arguments
|
|
args: Vec<Box<Expr>>,
|
|
/// `ORDER BY`
|
|
order_by: Vec<SortedColumn>,
|
|
/// `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<usize>,
|
|
/// 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<usize>,
|
|
/// the y in `x.y.z`. index of the table in catalog.
|
|
table: TableInternalId,
|
|
},
|
|
/// `IN`
|
|
InList {
|
|
/// expression
|
|
lhs: Box<Expr>,
|
|
/// `NOT`
|
|
not: bool,
|
|
/// values
|
|
rhs: Vec<Box<Expr>>,
|
|
},
|
|
/// `IN` subselect
|
|
InSelect {
|
|
/// expression
|
|
lhs: Box<Expr>,
|
|
/// `NOT`
|
|
not: bool,
|
|
/// subquery
|
|
rhs: Select,
|
|
},
|
|
/// `IN` table name / function
|
|
InTable {
|
|
/// expression
|
|
lhs: Box<Expr>,
|
|
/// `NOT`
|
|
not: bool,
|
|
/// table name
|
|
rhs: QualifiedName,
|
|
/// table function arguments
|
|
args: Vec<Box<Expr>>,
|
|
},
|
|
/// `IS NULL`
|
|
IsNull(Box<Expr>),
|
|
/// `LIKE`
|
|
Like {
|
|
/// expression
|
|
lhs: Box<Expr>,
|
|
/// `NOT`
|
|
not: bool,
|
|
/// operator
|
|
op: LikeOperator,
|
|
/// pattern
|
|
rhs: Box<Expr>,
|
|
/// `ESCAPE` char
|
|
escape: Option<Box<Expr>>,
|
|
},
|
|
/// Literal expression
|
|
Literal(Literal),
|
|
/// Name
|
|
Name(Name),
|
|
/// `NOT NULL` or `NOTNULL`
|
|
NotNull(Box<Expr>),
|
|
/// Parenthesized subexpression
|
|
Parenthesized(Vec<Box<Expr>>),
|
|
/// Qualified name
|
|
Qualified(Name, Name),
|
|
/// `RAISE` function call
|
|
Raise(ResolveType, Option<Box<Expr>>),
|
|
/// Subquery expression
|
|
Subquery(Select),
|
|
/// Unary expression
|
|
Unary(UnaryOperator, Box<Expr>),
|
|
/// Parameters
|
|
Variable(String),
|
|
}
|
|
|
|
impl Expr {
|
|
pub fn into_boxed(self) -> Box<Expr> {
|
|
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 {
|
|
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<Type>) -> Expr {
|
|
Expr::Cast {
|
|
expr: Box::new(expr),
|
|
type_name,
|
|
}
|
|
}
|
|
|
|
pub fn raise(resolve_type: ResolveType, expr: Option<Expr>) -> 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<With>,
|
|
/// body
|
|
pub body: SelectBody,
|
|
/// `ORDER BY`
|
|
pub order_by: Vec<SortedColumn>, // ORDER BY term does not match any column in the result set
|
|
/// `LIMIT`
|
|
pub limit: Option<Limit>,
|
|
}
|
|
|
|
/// `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<CompoundSelect>,
|
|
}
|
|
|
|
/// 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<Distinctness>,
|
|
/// columns
|
|
columns: Vec<ResultColumn>,
|
|
/// `FROM` clause
|
|
from: Option<FromClause>,
|
|
/// `WHERE` clause
|
|
where_clause: Option<Box<Expr>>,
|
|
/// `GROUP BY`
|
|
group_by: Option<GroupBy>,
|
|
/// `WINDOW` definition
|
|
window_clause: Vec<WindowDef>,
|
|
},
|
|
/// `VALUES`
|
|
Values(Vec<Vec<Box<Expr>>>),
|
|
}
|
|
|
|
/// `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<SelectTable>, // FIXME mandatory
|
|
/// `JOIN`ed tabled
|
|
pub joins: Vec<JoinedSelectTable>,
|
|
}
|
|
|
|
/// `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<Expr>, Option<As>),
|
|
/// `*`
|
|
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<SelectTable>,
|
|
/// constraint
|
|
pub constraint: Option<JoinConstraint>,
|
|
}
|
|
|
|
/// 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<As>, Option<Indexed>),
|
|
/// table function call
|
|
TableCall(QualifiedName, Vec<Box<Expr>>, Option<As>),
|
|
/// `SELECT` subquery
|
|
Select(Select, Option<As>),
|
|
/// subquery
|
|
Sub(FromClause, Option<As>),
|
|
}
|
|
|
|
/// 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<JoinType>),
|
|
}
|
|
|
|
// 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<Expr>),
|
|
/// `USING`: col names
|
|
Using(Vec<Name>),
|
|
}
|
|
|
|
/// `GROUP BY`
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct GroupBy {
|
|
/// expressions
|
|
pub exprs: Vec<Box<Expr>>,
|
|
/// `HAVING`
|
|
pub having: Option<Box<Expr>>, // 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<str>) -> 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<Name>,
|
|
/// object name
|
|
pub name: Name,
|
|
/// alias
|
|
pub alias: Option<Name>, // FIXME restrict alias usage (fullname vs xfullname)
|
|
}
|
|
|
|
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<ColumnDefinition>,
|
|
/// table constraints
|
|
constraints: Vec<NamedTableConstraint>,
|
|
/// 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<Type>,
|
|
/// column constraints
|
|
pub constraints: Vec<NamedColumnConstraint>,
|
|
}
|
|
|
|
/// 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<Name>,
|
|
/// 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<SortOrder>,
|
|
/// `ON CONFLICT` clause
|
|
conflict_clause: Option<ResolveType>,
|
|
/// `AUTOINCREMENT`
|
|
auto_increment: bool,
|
|
},
|
|
/// `NULL`
|
|
NotNull {
|
|
/// `NOT`
|
|
nullable: bool,
|
|
/// `ON CONFLICT` clause
|
|
conflict_clause: Option<ResolveType>,
|
|
},
|
|
/// `UNIQUE`
|
|
Unique(Option<ResolveType>),
|
|
/// `CHECK`
|
|
Check(Box<Expr>),
|
|
/// `DEFAULT`
|
|
Default(Box<Expr>),
|
|
/// `COLLATE`
|
|
Collate {
|
|
/// collation name
|
|
collation_name: Name, // FIXME Ids
|
|
},
|
|
/// `REFERENCES` foreign-key clause
|
|
ForeignKey {
|
|
/// clause
|
|
clause: ForeignKeyClause,
|
|
/// `DEFERRABLE`
|
|
deref_clause: Option<DeferSubclause>,
|
|
},
|
|
/// `GENERATED`
|
|
Generated {
|
|
/// expression
|
|
expr: Box<Expr>,
|
|
/// `STORED` / `VIRTUAL`
|
|
typ: Option<Name>,
|
|
},
|
|
}
|
|
|
|
/// 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<Name>,
|
|
/// 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<SortedColumn>,
|
|
/// `AUTOINCREMENT`
|
|
auto_increment: bool,
|
|
/// `ON CONFLICT` clause
|
|
conflict_clause: Option<ResolveType>,
|
|
},
|
|
/// `UNIQUE`
|
|
Unique {
|
|
/// columns
|
|
columns: Vec<SortedColumn>,
|
|
/// `ON CONFLICT` clause
|
|
conflict_clause: Option<ResolveType>,
|
|
},
|
|
/// `CHECK`
|
|
Check(Box<Expr>),
|
|
/// `FOREIGN KEY`
|
|
ForeignKey {
|
|
/// columns
|
|
columns: Vec<IndexedColumn>,
|
|
/// `REFERENCES`
|
|
clause: ForeignKeyClause,
|
|
/// `DEFERRABLE`
|
|
deref_clause: Option<DeferSubclause>,
|
|
},
|
|
}
|
|
|
|
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,
|
|
}
|
|
|
|
/// `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<IndexedColumn>,
|
|
/// referential action(s) / deferrable option(s)
|
|
pub args: Vec<RefArg>,
|
|
}
|
|
|
|
/// 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<InitDeferredPred>,
|
|
}
|
|
|
|
/// `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<Name>, // FIXME Ids
|
|
/// `ORDER BY`
|
|
pub order: Option<SortOrder>,
|
|
}
|
|
|
|
/// `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<Expr>,
|
|
/// `ASC` / `DESC`
|
|
pub order: Option<SortOrder>,
|
|
/// `NULLS FIRST` / `NULLS LAST`
|
|
pub nulls: Option<NullsOrder>,
|
|
}
|
|
|
|
/// `LIMIT`
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct Limit {
|
|
/// count
|
|
pub expr: Box<Expr>,
|
|
/// `OFFSET`
|
|
pub offset: Option<Box<Expr>>, // 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<Box<Upsert>>),
|
|
/// `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<Name>,
|
|
/// expression
|
|
pub expr: Box<Expr>,
|
|
}
|
|
|
|
/// `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<Expr>; // 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<Name>),
|
|
}
|
|
|
|
/// `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<ResolveType>,
|
|
/// table name
|
|
tbl_name: Name,
|
|
/// `SET` assignments
|
|
sets: Vec<Set>,
|
|
/// `FROM`
|
|
from: Option<FromClause>,
|
|
/// `WHERE` clause
|
|
where_clause: Option<Box<Expr>>,
|
|
},
|
|
/// `INSERT`
|
|
Insert {
|
|
/// `OR`
|
|
or_conflict: Option<ResolveType>,
|
|
/// table name
|
|
tbl_name: Name,
|
|
/// `COLUMNS`
|
|
col_names: Vec<Name>,
|
|
/// `SELECT` or `VALUES`
|
|
select: Select,
|
|
/// `ON CONFLICT` clause
|
|
upsert: Option<Box<Upsert>>,
|
|
/// `RETURNING`
|
|
returning: Vec<ResultColumn>,
|
|
},
|
|
/// `DELETE`
|
|
Delete {
|
|
/// table name
|
|
tbl_name: Name,
|
|
/// `WHERE` clause
|
|
where_clause: Option<Box<Expr>>,
|
|
},
|
|
/// `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<CommonTableExpr>,
|
|
}
|
|
|
|
/// 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<IndexedColumn>, // 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<TypeSize>,
|
|
}
|
|
|
|
/// 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<Expr>),
|
|
/// precision
|
|
TypeSize(Box<Expr>, Box<Expr>),
|
|
}
|
|
|
|
/// 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,
|
|
/// `CONCURRENT`,
|
|
Concurrent,
|
|
}
|
|
|
|
/// 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<UpsertIndex>,
|
|
/// `DO` clause
|
|
pub do_clause: UpsertDo,
|
|
/// next upsert
|
|
pub next: Option<Box<Upsert>>,
|
|
}
|
|
|
|
/// Upsert conflict targets
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct UpsertIndex {
|
|
/// columns
|
|
pub targets: Vec<SortedColumn>,
|
|
/// `WHERE` clause
|
|
pub where_clause: Option<Box<Expr>>,
|
|
}
|
|
|
|
/// 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<Set>,
|
|
/// `WHERE` clause
|
|
where_clause: Option<Box<Expr>>,
|
|
},
|
|
/// `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<Box<Expr>>,
|
|
/// `OVER` clause
|
|
pub over_clause: Option<Over>,
|
|
}
|
|
|
|
/// 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<Name>,
|
|
/// `PARTITION BY`
|
|
pub partition_by: Vec<Box<Expr>>,
|
|
/// `ORDER BY`
|
|
pub order_by: Vec<SortedColumn>,
|
|
/// frame spec
|
|
pub frame_clause: Option<FrameClause>,
|
|
}
|
|
|
|
/// 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<FrameBound>,
|
|
/// `EXCLUDE`
|
|
pub exclude: Option<FrameExclude>,
|
|
}
|
|
|
|
/// 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<Expr>),
|
|
/// `PRECEDING`
|
|
Preceding(Box<Expr>),
|
|
/// `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,
|
|
}
|