mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-19 17:34:19 +01:00
This PR integrates virtual tables into the query optimizer. It is a follow-up to https://github.com/tursodatabase/turso/pull/1727. The most immediate improvement is better support for inner joins involving TVFs, particularly when TVF arguments are column references. ### Example The following two queries are semantically equivalent, but require different join orders to be valid: ```sql -- TVF depends on `t.id`, so `t` must be evaluated in outer loop SELECT t.id, series.value FROM target t, generate_series(t.id, 3) series; -- Equivalent query, but with reversed table order in the FROM clause SELECT t.id, series.value FROM generate_series(t.id, 3) series, target t; ``` Without optimizer integration, the second query would fail because the planner would attempt to evaluate `generate_series` before `t`. With this change, the optimizer detects column dependencies and produces the correct join order in both cases. ### TODO Support for outer joins with TVFs is still missing and will be addressed in a follow-up PR. Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com> Closes #2439
118 lines
3.5 KiB
Rust
118 lines
3.5 KiB
Rust
use thiserror::Error;
|
|
|
|
#[derive(Debug, Error, miette::Diagnostic)]
|
|
pub enum LimboError {
|
|
#[error("Corrupt database: {0}")]
|
|
Corrupt(String),
|
|
#[error("File is not a database")]
|
|
NotADB,
|
|
#[error("Internal error: {0}")]
|
|
InternalError(String),
|
|
#[error("Page cache is full")]
|
|
CacheFull,
|
|
#[error("Database is full: {0}")]
|
|
DatabaseFull(String),
|
|
#[error("Parse error: {0}")]
|
|
ParseError(String),
|
|
#[error(transparent)]
|
|
#[diagnostic(transparent)]
|
|
LexerError(#[from] turso_sqlite3_parser::lexer::sql::Error),
|
|
#[error("Conversion error: {0}")]
|
|
ConversionError(String),
|
|
#[error("Env variable error: {0}")]
|
|
EnvVarError(#[from] std::env::VarError),
|
|
#[error("Transaction error: {0}")]
|
|
TxError(String),
|
|
#[error("I/O error: {0}")]
|
|
IOError(#[from] std::io::Error),
|
|
#[cfg(all(target_os = "linux", feature = "io_uring"))]
|
|
#[error("I/O error: {0}")]
|
|
UringIOError(String),
|
|
#[error("Locking error: {0}")]
|
|
LockingError(String),
|
|
#[cfg(target_family = "unix")]
|
|
#[error("I/O error: {0}")]
|
|
RustixIOError(#[from] rustix::io::Errno),
|
|
#[error("Parse error: {0}")]
|
|
ParseIntError(#[from] std::num::ParseIntError),
|
|
#[error("Parse error: {0}")]
|
|
ParseFloatError(#[from] std::num::ParseFloatError),
|
|
#[error("Parse error: {0}")]
|
|
InvalidDate(String),
|
|
#[error("Parse error: {0}")]
|
|
InvalidTime(String),
|
|
#[error("Modifier parsing error: {0}")]
|
|
InvalidModifier(String),
|
|
#[error("Invalid argument supplied: {0}")]
|
|
InvalidArgument(String),
|
|
#[error("Invalid formatter supplied: {0}")]
|
|
InvalidFormatter(String),
|
|
#[error("Runtime error: {0}")]
|
|
Constraint(String),
|
|
#[error("Extension error: {0}")]
|
|
ExtensionError(String),
|
|
#[error("Runtime error: integer overflow")]
|
|
IntegerOverflow,
|
|
#[error("Schema is locked for write")]
|
|
SchemaLocked,
|
|
#[error("Runtime error: database table is locked")]
|
|
TableLocked,
|
|
#[error("Error: Resource is read-only")]
|
|
ReadOnly,
|
|
#[error("Database is busy")]
|
|
Busy,
|
|
#[error("Conflict: {0}")]
|
|
Conflict(String),
|
|
#[error("Database schema changed")]
|
|
SchemaUpdated,
|
|
#[error(
|
|
"Database is empty, header does not exist - page 1 should've been allocated before this"
|
|
)]
|
|
Page1NotAlloc,
|
|
#[error("Transaction terminated")]
|
|
TxTerminated,
|
|
#[error("Write-write conflict")]
|
|
WriteWriteConflict,
|
|
#[error("No such transaction ID: {0}")]
|
|
NoSuchTransactionID(String),
|
|
#[error("Null value")]
|
|
NullValue,
|
|
#[error("invalid column type")]
|
|
InvalidColumnType,
|
|
#[error("Invalid blob size, expected {0}")]
|
|
InvalidBlobSize(usize),
|
|
#[error("Planning error: {0}")]
|
|
PlanningError(String),
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! bail_parse_error {
|
|
($($arg:tt)*) => {
|
|
return Err($crate::error::LimboError::ParseError(format!($($arg)*)))
|
|
};
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! bail_corrupt_error {
|
|
($($arg:tt)*) => {
|
|
return Err($crate::error::LimboError::Corrupt(format!($($arg)*)))
|
|
};
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! bail_constraint_error {
|
|
($($arg:tt)*) => {
|
|
return Err($crate::error::LimboError::Constraint(format!($($arg)*)))
|
|
};
|
|
}
|
|
|
|
impl From<turso_ext::ResultCode> for LimboError {
|
|
fn from(err: turso_ext::ResultCode) -> Self {
|
|
LimboError::ExtensionError(err.to_string())
|
|
}
|
|
}
|
|
|
|
pub const SQLITE_CONSTRAINT: usize = 19;
|
|
pub const SQLITE_CONSTRAINT_PRIMARYKEY: usize = SQLITE_CONSTRAINT | (6 << 8);
|
|
pub const SQLITE_CONSTRAINT_NOTNULL: usize = SQLITE_CONSTRAINT | (5 << 8);
|