use std::sync::Arc;
use rustc_hash::FxHashMap as HashMap;
use turso_parser::ast;
use crate::{
schema::IndexColumn,
storage::btree::BTreeCursor,
types::{IOResult, IndexInfo, KeyInfo},
vdbe::Register,
Connection, LimboError, Result, Value,
};
pub mod backing_btree;
#[cfg(all(feature = "fts", not(target_family = "wasm")))]
pub mod fts;
pub mod toy_vector_sparse_ivf;
pub const BACKING_BTREE_INDEX_METHOD_NAME: &str = "backing_btree";
pub const TOY_VECTOR_SPARSE_IVF_INDEX_METHOD_NAME: &str = "toy_vector_sparse_ivf";
pub trait IndexMethod: std::fmt::Debug + Send + Sync {
fn attach(
&self,
configuration: &IndexMethodConfiguration,
) -> Result<Arc<dyn IndexMethodAttachment>>;
}
#[derive(Debug, Clone)]
pub struct IndexMethodConfiguration {
pub table_name: String,
pub index_name: String,
pub columns: Vec<IndexColumn>,
pub parameters: HashMap<String, Value>,
}
pub trait IndexMethodAttachment: std::fmt::Debug + Send + Sync {
fn definition<'a>(&'a self) -> IndexMethodDefinition<'a>;
fn init(&self) -> Result<Box<dyn IndexMethodCursor>>;
}
#[derive(Debug)]
pub struct IndexMethodDefinition<'a> {
pub method_name: &'a str,
pub index_name: &'a str,
pub patterns: &'a [ast::Select],
pub backing_btree: bool,
pub results_materialized: bool,
}
#[derive(Debug, Clone, Copy)]
pub struct IndexMethodCostEstimate {
pub estimated_cost: f64,
pub estimated_rows: u64,
}
pub trait IndexMethodCursor {
fn create(&mut self, connection: &Arc<Connection>, database_id: usize) -> Result<IOResult<()>>;
fn destroy(&mut self, connection: &Arc<Connection>, database_id: usize)
-> Result<IOResult<()>>;
fn open_read(
&mut self,
connection: &Arc<Connection>,
database_id: usize,
) -> Result<IOResult<()>>;
fn open_write(
&mut self,
connection: &Arc<Connection>,
database_id: usize,
) -> Result<IOResult<()>>;
fn insert(&mut self, values: &[Register]) -> Result<IOResult<()>>;
fn delete(&mut self, values: &[Register]) -> Result<IOResult<()>>;
fn query_start(&mut self, values: &[Register]) -> Result<IOResult<bool>>;
fn query_next(&mut self) -> Result<IOResult<bool>>;
fn query_column(&mut self, idx: usize) -> Result<IOResult<Value>>;
fn query_rowid(&mut self) -> Result<IOResult<Option<i64>>>;
fn pre_commit(&mut self) -> Result<IOResult<()>> {
Ok(IOResult::Done(()))
}
fn optimize(
&mut self,
_connection: &Arc<Connection>,
_database_id: usize,
) -> Result<IOResult<()>> {
Ok(IOResult::Done(()))
}
fn estimate_cost(
&self,
pattern_idx: usize,
base_table_rows: f64,
) -> Option<IndexMethodCostEstimate> {
let _ = (pattern_idx, base_table_rows);
None
}
}
pub(crate) fn open_table_cursor(
connection: &Connection,
database_id: usize,
table: &str,
) -> Result<BTreeCursor> {
let pager = connection.get_pager_from_database_index(&database_id)?;
let Some(table) = connection.with_schema(database_id, |schema| schema.get_table(table)) else {
return Err(LimboError::InternalError(format!(
"table {table} not found",
)));
};
let cursor = BTreeCursor::new_table(pager, table.get_root_page()?, table.columns().len());
Ok(cursor)
}
pub(crate) fn open_index_cursor(
connection: &Connection,
database_id: usize,
table: &str,
index: &str,
keys: Vec<KeyInfo>,
) -> Result<BTreeCursor> {
let pager = connection.get_pager_from_database_index(&database_id)?;
let Some(scratch) = connection.with_schema(database_id, |schema| {
schema.get_index(table, index).cloned()
}) else {
return Err(LimboError::InternalError(format!(
"index {index} for table {table} not found",
)));
};
let mut cursor = BTreeCursor::new(pager, scratch.root_page, keys.len());
cursor.index_info = Some(Arc::new(IndexInfo {
has_rowid: false,
num_cols: keys.len(),
key_info: keys,
is_unique: scratch.unique,
}));
Ok(cursor)
}
pub(crate) fn parse_patterns(patterns: &[&str]) -> Result<Vec<ast::Select>> {
let mut parsed = Vec::new();
for pattern in patterns {
let mut parser = turso_parser::parser::Parser::new(pattern.as_bytes());
let Some(ast) = parser.next() else {
return Err(LimboError::ParseError(format!(
"unable to parse pattern statement: {pattern}",
)));
};
let ast = ast?;
let ast::Cmd::Stmt(ast::Stmt::Select(select)) = ast else {
return Err(LimboError::ParseError(format!(
"only select patterns are allowed: {pattern}",
)));
};
parsed.push(select);
}
Ok(parsed)
}