mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-08-04 18:18:03 +00:00
Merge 'Implement create virtual table using vtab modules, more work on virtual tables' from Preston Thorpe
This PR started out as one to improve the API of extensions but I ended up building on top of this quite a bit and it just kept going. Sorry this one is so large but there wasn't really a good stopping point, as it kept leaving stuff in broken states. **VCreate**: Support for `CREATE VIRTUAL TABLE t USING vtab_module` **VUpdate**: Support for `INSERT` and `DELETE` methods on virtual tables. Sqlite uses `xUpdate` function with the `VUpdate` opcode to handle all insert/update/delete functionality in virtual tables.. have to just document that: ``` if args[0] == NULL: INSERT args[1] the values in args[2..] if args[1] == NULL: DELETE args[0] if args[0] != NULL && len(args) > 2: Update values=args[2..] rowid=args[0] ``` I know I asked @jussisaurio on discord about this already, but it just sucked so bad that I added some internal translation so we could expose a [nice API](https://github.com/tursodatabase/limbo/pull/996/files#diff- 3e8f8a660b11786745b48b528222d11671e9f19fa00a032a4eefb5412e8200d1R54) and handle the logic ourselves while keeping with sqlite's opcodes. I'll change it back if I have to, I just thought it was genuinely awful to have to rely on comments to explain all that to extension authors. The included extension is not meant to be a legitimately useful one, it is there for testing purposes. I did something similar in #960 using a test extension, so I figure when they are both merged, I will go back and combine them into one since you can do many kinds at once, and that way it will reduce the amount of crates and therefore compile time. 1. Remaining opcodes. 2. `UPDATE` (when we support the syntax) 3. `xConnect` - expose API for a DB connection to a vtab so it can perform arbitrary queries. Closes #996
This commit is contained in:
commit
7f2525ac27
30 changed files with 1362 additions and 378 deletions
54
core/util.rs
54
core/util.rs
|
@ -5,7 +5,7 @@ use std::{rc::Rc, sync::Arc};
|
|||
use crate::{
|
||||
schema::{self, Column, Schema, Type},
|
||||
types::OwnedValue,
|
||||
LimboError, OpenFlags, Result, Statement, StepResult, IO,
|
||||
LimboError, OpenFlags, Result, Statement, StepResult, SymbolTable, IO,
|
||||
};
|
||||
|
||||
// https://sqlite.org/lang_keywords.html
|
||||
|
@ -30,6 +30,7 @@ pub fn parse_schema_rows(
|
|||
rows: Option<Statement>,
|
||||
schema: &mut Schema,
|
||||
io: Arc<dyn IO>,
|
||||
syms: &SymbolTable,
|
||||
) -> Result<()> {
|
||||
if let Some(mut rows) = rows {
|
||||
let mut automatic_indexes = Vec::new();
|
||||
|
@ -38,15 +39,21 @@ pub fn parse_schema_rows(
|
|||
StepResult::Row => {
|
||||
let row = rows.row().unwrap();
|
||||
let ty = row.get::<&str>(0)?;
|
||||
if ty != "table" && ty != "index" {
|
||||
if !["table", "index"].contains(&ty) {
|
||||
continue;
|
||||
}
|
||||
match ty {
|
||||
"table" => {
|
||||
let root_page: i64 = row.get::<i64>(3)?;
|
||||
let sql: &str = row.get::<&str>(4)?;
|
||||
let table = schema::BTreeTable::from_sql(sql, root_page as usize)?;
|
||||
schema.add_table(Rc::new(table));
|
||||
if root_page == 0 && sql.to_lowercase().contains("virtual") {
|
||||
let name: &str = row.get::<&str>(1)?;
|
||||
let vtab = syms.vtabs.get(name).unwrap().clone();
|
||||
schema.add_virtual_table(vtab);
|
||||
} else {
|
||||
let table = schema::BTreeTable::from_sql(sql, root_page as usize)?;
|
||||
schema.add_btree_table(Rc::new(table));
|
||||
}
|
||||
}
|
||||
"index" => {
|
||||
let root_page: i64 = row.get::<i64>(3)?;
|
||||
|
@ -85,7 +92,7 @@ pub fn parse_schema_rows(
|
|||
}
|
||||
for (index_name, table_name, root_page) in automatic_indexes {
|
||||
// We need to process these after all tables are loaded into memory due to the schema.get_table() call
|
||||
let table = schema.get_table(&table_name).unwrap();
|
||||
let table = schema.get_btree_table(&table_name).unwrap();
|
||||
let index =
|
||||
schema::Index::automatic_from_primary_key(&table, &index_name, root_page as usize)?;
|
||||
schema.add_index(Rc::new(index));
|
||||
|
@ -309,9 +316,11 @@ pub fn exprs_are_equivalent(expr1: &Expr, expr2: &Expr) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn columns_from_create_table_body(body: ast::CreateTableBody) -> Result<Vec<Column>, ()> {
|
||||
pub fn columns_from_create_table_body(body: &ast::CreateTableBody) -> crate::Result<Vec<Column>> {
|
||||
let CreateTableBody::ColumnsAndConstraints { columns, .. } = body else {
|
||||
return Err(());
|
||||
return Err(crate::LimboError::ParseError(
|
||||
"CREATE TABLE body must contain columns and constraints".to_string(),
|
||||
));
|
||||
};
|
||||
|
||||
Ok(columns
|
||||
|
@ -324,7 +333,7 @@ pub fn columns_from_create_table_body(body: ast::CreateTableBody) -> Result<Vec<
|
|||
}
|
||||
}
|
||||
let column = Column {
|
||||
name: Some(name.0),
|
||||
name: Some(name.0.clone()),
|
||||
ty: match column_def.col_type {
|
||||
Some(ref data_type) => {
|
||||
// https://www.sqlite.org/datatype3.html
|
||||
|
@ -782,6 +791,35 @@ pub fn text_to_real(text: &str) -> (OwnedValue, CastTextToRealResultCode) {
|
|||
return (OwnedValue::Float(0.0), CastTextToRealResultCode::NotValid);
|
||||
}
|
||||
|
||||
// for TVF's we need these at planning time so we cannot emit translate_expr
|
||||
pub fn vtable_args(args: &[ast::Expr]) -> Vec<limbo_ext::Value> {
|
||||
let mut vtable_args = Vec::new();
|
||||
for arg in args {
|
||||
match arg {
|
||||
Expr::Literal(lit) => match lit {
|
||||
Literal::Numeric(i) => {
|
||||
if i.contains('.') {
|
||||
vtable_args.push(limbo_ext::Value::from_float(i.parse().unwrap()));
|
||||
} else {
|
||||
vtable_args.push(limbo_ext::Value::from_integer(i.parse().unwrap()));
|
||||
}
|
||||
}
|
||||
Literal::String(s) => {
|
||||
vtable_args.push(limbo_ext::Value::from_text(s.clone()));
|
||||
}
|
||||
Literal::Blob(b) => {
|
||||
vtable_args.push(limbo_ext::Value::from_blob(b.as_bytes().into()));
|
||||
}
|
||||
_ => {
|
||||
vtable_args.push(limbo_ext::Value::null());
|
||||
}
|
||||
},
|
||||
_ => vtable_args.push(limbo_ext::Value::null()),
|
||||
}
|
||||
}
|
||||
vtable_args
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue