use new internal virtual table type

This commit is contained in:
Mikaël Francoeur
2025-08-21 14:44:06 -04:00
parent e6d3d6ea54
commit 54719f4eac
4 changed files with 201 additions and 244 deletions

View File

@@ -186,12 +186,8 @@ impl Database {
#[cfg(feature = "uuid")]
crate::uuid::register_extension(&mut ext_api);
#[cfg(feature = "series")]
crate::series::register_extension(&mut ext_api);
crate::json::register_extension(&mut ext_api);
#[cfg(feature = "fs")]
{
let vfslist = add_builtin_vfs_extensions(Some(ext_api)).map_err(|e| e.to_string())?;
@@ -199,7 +195,6 @@ impl Database {
add_vfs_module(name, vfs);
}
}
let _ = unsafe { Box::from_raw(ctx) };
Ok(())
}

View File

@@ -19,13 +19,6 @@ use jsonb::{ElementType, Jsonb, JsonbHeader, PathOperationMode, SearchOperation,
use std::borrow::Cow;
use std::str::FromStr;
pub fn register_extension(ext_api: &mut turso_ext::ExtensionApi) {
// FIXME: Add macro magic to register functions automatically.
unsafe {
vtab::JsonEachVirtualTable::register_JsonEachVirtualTable(ext_api);
}
}
#[derive(Debug, Clone, Copy)]
pub enum Conv {
Strict,
@@ -489,50 +482,6 @@ pub fn json_string_to_db_type(
}
}
pub fn json_string_to_ext_value(
json: Jsonb,
//TODO remove when I figure out which one I need
flag: OutputVariant,
) -> crate::Result<turso_ext::Value> {
let element_type = json.element_type()?;
let mut json_string = json.to_string()?;
if matches!(flag, OutputVariant::Binary) {
return Ok(turso_ext::Value::from_blob(json.data()));
}
match element_type {
ElementType::ARRAY | ElementType::OBJECT => Ok(turso_ext::Value::from_json(json_string)),
ElementType::TEXT | ElementType::TEXT5 | ElementType::TEXTJ | ElementType::TEXTRAW => {
if matches!(flag, OutputVariant::ElementType) {
json_string.remove(json_string.len() - 1);
json_string.remove(0);
Ok(turso_ext::Value::from_json(json_string))
} else {
Ok(turso_ext::Value::from_text(json_string))
}
}
ElementType::FLOAT5 | ElementType::FLOAT => Ok(turso_ext::Value::from_float(
json_string.parse().expect("Should be valid f64"),
)),
ElementType::INT | ElementType::INT5 => {
let result = i64::from_str(&json_string);
if let Ok(int) = result {
Ok(turso_ext::Value::from_integer(int))
} else {
let res = f64::from_str(&json_string);
match res {
Ok(num) => Ok(turso_ext::Value::from_float(num)),
Err(_) => Ok(turso_ext::Value::null()),
}
}
}
ElementType::TRUE => Ok(turso_ext::Value::from_integer(1)),
ElementType::FALSE => Ok(turso_ext::Value::from_integer(1)),
ElementType::NULL => Ok(turso_ext::Value::null()),
_ => unreachable!(),
}
}
pub fn json_type(value: &Value, path: Option<&Value>) -> crate::Result<Value> {
if let Value::Null = value {
return Ok(Value::Null);

View File

@@ -1,86 +1,49 @@
use turso_ext::{
ConstraintUsage, ResultCode, VTabCursor, VTabModule, VTabModuleDerive, VTable, Value,
};
use std::{cell::RefCell, result::Result, sync::Arc};
use crate::json::{
convert_dbtype_to_jsonb,
jsonb::{ArrayIteratorState, Jsonb, ObjectIteratorState},
vtab::columns::Columns,
Conv,
use turso_ext::{ConstraintUsage, ResultCode};
use crate::{
json::{
convert_dbtype_to_jsonb,
jsonb::{ArrayIteratorState, Jsonb, ObjectIteratorState},
vtab::columns::Columns,
Conv,
},
types::Text,
vtab::{InternalVirtualTable, InternalVirtualTableCursor},
Connection, LimboError, Value,
};
use super::jsonb;
macro_rules! try_result {
($msg:expr, $expr:expr) => {
try_result!($msg, $expr, |x| x)
};
($msg:expr, $expr:expr, $fn:expr) => {
match $expr {
Ok(val) => val,
Err(err) => {
eprintln!("Error: {}: {}", $msg, err);
return $fn(ResultCode::Error);
}
}
};
}
#[derive(VTabModuleDerive)]
pub struct JsonEachVirtualTable;
const COL_KEY: u32 = 0;
const COL_VALUE: u32 = 1;
const COL_TYPE: u32 = 2;
const COL_ATOM: u32 = 3;
const COL_ID: u32 = 4;
const COL_PARENT: u32 = 5;
const COL_FULLKEY: u32 = 6;
const COL_PATH: u32 = 7;
const COL_JSON: u32 = 8;
const COL_ROOT: u32 = 9;
const COL_KEY: usize = 0;
const COL_VALUE: usize = 1;
const COL_TYPE: usize = 2;
const COL_ATOM: usize = 3;
const COL_ID: usize = 4;
const COL_PARENT: usize = 5;
const COL_FULLKEY: usize = 6;
const COL_PATH: usize = 7;
const COL_JSON: usize = 8;
const COL_ROOT: usize = 9;
impl VTabModule for JsonEachVirtualTable {
type Table = JsonEachVirtualTable;
const VTAB_KIND: turso_ext::VTabKind = turso_ext::VTabKind::TableValuedFunction;
const NAME: &'static str = "json_each";
fn create(_args: &[Value]) -> Result<(String, Self::Table), turso_ext::ResultCode> {
Ok((
"CREATE TABLE json_each(
key ANY, -- key for current element relative to its parent
value ANY, -- value for the current element
type TEXT, -- 'object','array','string','integer', etc.
atom ANY, -- value for primitive types, null for array & object
id INTEGER, -- integer ID for this element
parent INTEGER, -- integer ID for the parent of this element
fullkey TEXT, -- full path describing the current element
path TEXT, -- path to the container of the current row
json JSON HIDDEN, -- 1st input parameter: the raw JSON
root TEXT HIDDEN -- 2nd input parameter: the PATH at which to start
);"
.to_owned(),
JsonEachVirtualTable {},
))
impl InternalVirtualTable for JsonEachVirtualTable {
fn name(&self) -> String {
"json_each".to_owned()
}
}
impl VTable for JsonEachVirtualTable {
type Cursor = JsonEachCursor;
type Error = ResultCode;
fn open(
&self,
_conn: Option<std::sync::Arc<turso_ext::Connection>>,
) -> Result<Self::Cursor, Self::Error> {
Ok(JsonEachCursor::default())
_conn: Arc<Connection>,
) -> crate::Result<std::sync::Arc<RefCell<(dyn InternalVirtualTableCursor + 'static)>>> {
Ok(Arc::new(RefCell::new(JsonEachCursor::default())))
}
fn best_index(
_constraints: &[turso_ext::ConstraintInfo],
&self,
constraints: &[turso_ext::ConstraintInfo],
_order_by: &[turso_ext::OrderByInfo],
) -> Result<turso_ext::IndexInfo, ResultCode> {
use turso_ext::ConstraintOp;
@@ -90,12 +53,12 @@ impl VTable for JsonEachVirtualTable {
argv_index: None,
omit: false
};
_constraints.len()
constraints.len()
];
let mut have_json = false;
for (i, c) in _constraints.iter().enumerate() {
if c.usable && c.op == ConstraintOp::Eq && c.column_index == COL_JSON {
for (i, c) in constraints.iter().enumerate() {
if c.usable && c.op == ConstraintOp::Eq && c.column_index as usize == COL_JSON {
usages[i] = ConstraintUsage {
argv_index: Some(1),
omit: true,
@@ -114,6 +77,28 @@ impl VTable for JsonEachVirtualTable {
constraint_usages: usages,
})
}
fn sql(&self) -> String {
"CREATE TABLE json_each(
key ANY, -- key for current element relative to its parent
value ANY, -- value for the current element
type TEXT, -- 'object','array','string','integer', etc.
atom ANY, -- value for primitive types, null for array & object
id INTEGER, -- integer ID for this element
parent INTEGER, -- integer ID for the parent of this element
fullkey TEXT, -- full path describing the current element
path TEXT, -- path to the container of the current row
json JSON HIDDEN, -- 1st input parameter: the raw JSON
root TEXT HIDDEN -- 2nd input parameter: the PATH at which to start
);"
.to_owned()
}
}
impl std::fmt::Debug for JsonEachVirtualTable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("JsonEachVirtualTable").finish()
}
}
enum IteratorState {
@@ -143,37 +128,41 @@ impl Default for JsonEachCursor {
}
}
impl VTabCursor for JsonEachCursor {
type Error = ResultCode;
fn filter(&mut self, args: &[Value], _idx_info: Option<(&str, i32)>) -> ResultCode {
impl InternalVirtualTableCursor for JsonEachCursor {
fn filter(
&mut self,
args: &[Value],
_idx_str: Option<String>,
_idx_num: i32,
) -> Result<bool, LimboError> {
if args.is_empty() {
return ResultCode::EOF;
return Ok(false);
}
if args.len() == 2 {
return Err(LimboError::InvalidArgument(
"2-arg json_each is not supported yet".to_owned(),
));
}
if args.len() != 1 && args.len() != 2 {
return ResultCode::InvalidArgs;
return Err(LimboError::InvalidArgument(
"json_each accepts 1 or 2 arguments".to_owned(),
));
}
let db_value = try_result!(
"converting ext value to db value",
convert_ext_value_to_db_value(&args[0])
);
let db_value = &args[0];
let jsonb = try_result!(
"converting to jsonb",
convert_dbtype_to_jsonb(&db_value, Conv::Strict)
);
let jsonb = convert_dbtype_to_jsonb(db_value, Conv::Strict)?;
let element_type = try_result!("checking jsonb element type", jsonb.element_type());
let element_type = jsonb.element_type()?;
self.json = jsonb;
match element_type {
jsonb::ElementType::ARRAY => {
let iter = try_result!("getting array iterator", self.json.array_iterator());
let iter = self.json.array_iterator()?;
self.iterator_state = IteratorState::Array(iter);
}
jsonb::ElementType::OBJECT => {
let iter = try_result!("getting object iterator", self.json.object_iterator());
let iter = self.json.object_iterator()?;
self.iterator_state = IteratorState::Object(iter);
}
jsonb::ElementType::NULL
@@ -192,25 +181,24 @@ impl VTabCursor for JsonEachCursor {
jsonb::ElementType::RESERVED1
| jsonb::ElementType::RESERVED2
| jsonb::ElementType::RESERVED3 => {
eprintln!("Error: received unexpected element type {element_type:?}");
return ResultCode::Error;
unreachable!("element type not supported: {element_type:?}");
}
}
};
self.next()
}
fn next(&mut self) -> ResultCode {
fn next(&mut self) -> Result<bool, LimboError> {
self.rowid += 1;
if self.no_more_rows {
return ResultCode::EOF;
return Ok(false);
}
match &self.iterator_state {
IteratorState::Array(state) => {
let Some(((idx, jsonb), new_state)) = self.json.array_iterator_next(state) else {
self.no_more_rows = true;
return ResultCode::EOF;
return Ok(false);
};
self.iterator_state = IteratorState::Array(new_state);
self.columns = Columns::new(columns::Key::Integer(idx as i64), jsonb);
@@ -221,11 +209,11 @@ impl VTabCursor for JsonEachCursor {
ObjectIteratorState,
)> = self.json.object_iterator_next(state) else {
self.no_more_rows = true;
return ResultCode::EOF;
return Ok(false);
};
self.iterator_state = IteratorState::Object(new_state);
let key = try_result!("converting key to db value", key.to_string());
let key = key.to_string()?;
self.columns = Columns::new(columns::Key::String(key), value);
}
IteratorState::Primitive => {
@@ -236,65 +224,38 @@ impl VTabCursor for JsonEachCursor {
IteratorState::None => unreachable!(),
};
ResultCode::OK
Ok(true)
}
fn rowid(&self) -> i64 {
self.rowid
}
fn column(&self, idx: u32) -> Result<Value, Self::Error> {
fn column(&self, idx: usize) -> Result<Value, LimboError> {
Ok(match idx {
COL_KEY => self.columns.key(),
COL_VALUE => {
try_result!(
"converting value column to ext value",
self.columns.value(),
Err
)
}
COL_VALUE => self.columns.value()?,
COL_TYPE => self.columns.ttype(),
COL_ATOM => self.columns.atom()?,
COL_ID => Value::from_integer(self.rowid),
COL_ID => Value::Integer(self.rowid),
COL_PARENT => self.columns.parent(),
COL_FULLKEY => self.columns.fullkey(),
COL_PATH => self.columns.path(),
COL_ROOT => Value::from_text("json, todo".to_owned()),
num => {
println!("was asked for column {num}");
Value::null()
}
COL_ROOT => Value::Text(Text::new("json, todo")),
_ => Value::Null,
})
}
fn eof(&self) -> bool {
self.no_more_rows
}
}
fn convert_ext_value_to_db_value(ext_value: &turso_ext::Value) -> Result<crate::Value, ResultCode> {
Ok(match ext_value.value_type() {
turso_ext::ValueType::Null => crate::Value::Null,
turso_ext::ValueType::Text if ext_value.is_json() => crate::Value::Text(
crate::types::Text::json(ext_value.to_text().unwrap().to_owned()),
),
turso_ext::ValueType::Text => {
crate::Value::Text(crate::types::Text::new(ext_value.to_text().unwrap()))
}
turso_ext::ValueType::Integer => crate::Value::Integer(ext_value.to_integer().unwrap()),
turso_ext::ValueType::Float => crate::Value::Float(ext_value.to_float().unwrap()),
turso_ext::ValueType::Blob => crate::Value::Blob(ext_value.to_blob().unwrap()),
turso_ext::ValueType::Error => return Err(ResultCode::InvalidArgs),
})
}
mod columns {
use turso_ext::{ResultCode, Value};
use crate::json::{
json_string_to_ext_value,
jsonb::{self, ElementType, Jsonb},
OutputVariant,
use crate::{
json::{
json_string_to_db_type,
jsonb::{self, ElementType, Jsonb},
OutputVariant,
},
types::Text,
LimboError, Value,
};
#[derive(Debug)]
@@ -310,7 +271,7 @@ mod columns {
fn fullkey_representation(&self) -> Value {
match self {
Key::Integer(ref i) => Value::from_text(format!("$[{i}]")),
Key::Integer(ref i) => Value::Text(Text::new(&format!("$[{i}]"))),
Key::String(ref text) => {
let mut needs_quoting: bool = false;
@@ -324,17 +285,17 @@ mod columns {
}
let s = format!("$.{text}");
Value::from_text(s)
Value::Text(Text::new(&s))
}
}
}
fn key_representation(&self) -> Value {
match self {
Key::Integer(ref i) => Value::from_integer(*i),
Key::String(ref s) => {
Value::from_text(s[1..s.len() - 1].to_owned().replace("\\\"", "\""))
}
Key::Integer(ref i) => Value::Integer(*i),
Key::String(ref s) => Value::Text(Text::new(
&s[1..s.len() - 1].to_owned().replace("\\\"", "\""),
)),
}
}
}
@@ -372,39 +333,33 @@ mod columns {
}
}
pub(super) fn atom(&self) -> Result<Value, ResultCode> {
pub(super) fn atom(&self) -> Result<Value, LimboError> {
Self::atom_from_value(&self.value)
}
pub(super) fn value(&self) -> Result<Value, ResultCode> {
Ok(
match try_result!("reading element type", self.value.element_type(), Err) {
ElementType::ARRAY | ElementType::OBJECT => {
let value = try_result!(
"converting value to extension value",
json_string_to_ext_value(self.value.clone(), OutputVariant::String),
Err
);
value
}
_ => Self::atom_from_value(&self.value)?,
},
)
pub(super) fn value(&self) -> Result<Value, LimboError> {
let element_type = self.value.element_type()?;
Ok(match element_type {
ElementType::ARRAY | ElementType::OBJECT => {
json_string_to_db_type(self.value.clone(), element_type, OutputVariant::String)?
}
_ => Self::atom_from_value(&self.value)?,
})
}
pub(super) fn key(&self) -> Value {
if self.is_primitive {
return Value::null();
return Value::Null;
}
self.key.key_representation()
}
fn atom_from_value(value: &Jsonb) -> Result<Value, ResultCode> {
fn atom_from_value(value: &Jsonb) -> Result<Value, LimboError> {
let element_type = value.element_type().expect("invalid value");
let string: Result<Value, turso_ext::ResultCode> = match element_type {
jsonb::ElementType::NULL => Ok(Value::null()),
jsonb::ElementType::TRUE => Ok(Value::from_integer(1)),
jsonb::ElementType::FALSE => Ok(Value::from_integer(0)),
let string: Result<Value, LimboError> = match element_type {
jsonb::ElementType::NULL => Ok(Value::Null),
jsonb::ElementType::TRUE => Ok(Value::Integer(1)),
jsonb::ElementType::FALSE => Ok(Value::Integer(0)),
jsonb::ElementType::INT | jsonb::ElementType::INT5 => Self::jsonb_to_integer(value),
jsonb::ElementType::FLOAT | jsonb::ElementType::FLOAT5 => {
Self::jsonb_to_float(value)
@@ -413,47 +368,47 @@ mod columns {
| jsonb::ElementType::TEXTJ
| jsonb::ElementType::TEXT5
| jsonb::ElementType::TEXTRAW => {
let s = try_result!("converting value to string", value.to_string(), Err);
let s = value.to_string()?;
let s = (s[1..s.len() - 1]).to_string();
Ok(Value::from_text(s))
Ok(Value::Text(Text::new(&s)))
}
jsonb::ElementType::ARRAY => Ok(Value::null()),
jsonb::ElementType::OBJECT => Ok(Value::null()),
jsonb::ElementType::RESERVED1 => Ok(Value::null()),
jsonb::ElementType::RESERVED2 => Ok(Value::null()),
jsonb::ElementType::RESERVED3 => Ok(Value::null()),
jsonb::ElementType::ARRAY => Ok(Value::Null),
jsonb::ElementType::OBJECT => Ok(Value::Null),
jsonb::ElementType::RESERVED1 => Ok(Value::Null),
jsonb::ElementType::RESERVED2 => Ok(Value::Null),
jsonb::ElementType::RESERVED3 => Ok(Value::Null),
};
string.map_err(|_| ResultCode::Error)
string
}
fn jsonb_to_integer(value: &Jsonb) -> Result<Value, ResultCode> {
let string = try_result!("converting jsonb to integer", value.to_string(), Err);
let int = try_result!("parsing int", string.parse::<i64>(), Err);
fn jsonb_to_integer(value: &Jsonb) -> Result<Value, LimboError> {
let string = value.to_string()?;
let int = string.parse::<i64>()?;
Ok(Value::from_integer(int))
Ok(Value::Integer(int))
}
fn jsonb_to_float(value: &Jsonb) -> Result<Value, ResultCode> {
let string = try_result!("converting jsonb to integer", value.to_string(), Err);
let float = try_result!("parsing float", string.parse::<f64>(), Err);
fn jsonb_to_float(value: &Jsonb) -> Result<Value, LimboError> {
let string = value.to_string()?;
let float = string.parse::<f64>()?;
Ok(Value::from_float(float))
Ok(Value::Float(float))
}
pub(super) fn fullkey(&self) -> Value {
if self.is_primitive {
return Value::from_text("$".to_owned());
return Value::Text(Text::new("$"));
}
self.key.fullkey_representation()
}
pub(super) fn path(&self) -> Value {
Value::from_text("$".to_owned())
Value::Text(Text::new("$"))
}
pub(super) fn parent(&self) -> Value {
Value::null()
Value::Null
}
pub(super) fn ttype(&self) -> Value {
@@ -475,7 +430,7 @@ mod columns {
| jsonb::ElementType::RESERVED3 => unreachable!(),
};
Value::from_text(ttype.to_owned())
Value::Text(Text::new(ttype))
}
}
}

View File

@@ -1,8 +1,9 @@
use crate::json::vtab::JsonEachVirtualTable;
use crate::pragma::{PragmaVirtualTable, PragmaVirtualTableCursor};
use crate::schema::Column;
use crate::util::columns_from_create_table_body;
use crate::{Connection, LimboError, SymbolTable, Value};
use std::cell::RefCell;
use std::ffi::c_void;
use std::ptr::NonNull;
use std::rc::Rc;
@@ -14,6 +15,7 @@ use turso_parser::{ast, parser::Parser};
pub(crate) enum VirtualTableType {
Pragma(PragmaVirtualTable),
External(ExtVirtualTable),
Internal(Arc<RefCell<dyn InternalVirtualTable>>),
}
#[derive(Clone, Debug)]
@@ -29,23 +31,38 @@ impl VirtualTable {
match &self.vtab_type {
VirtualTableType::Pragma(_) => true,
VirtualTableType::External(table) => table.readonly(),
VirtualTableType::Internal(_) => true,
}
}
pub(crate) fn builtin_functions() -> Vec<Arc<VirtualTable>> {
PragmaVirtualTable::functions()
let json_each = JsonEachVirtualTable {};
let json_each_virtual_table = VirtualTable {
name: json_each.name(),
columns: Self::resolve_columns(json_each.sql())
.expect("internal table-valued function schema resolution should not fail"),
kind: VTabKind::TableValuedFunction,
vtab_type: VirtualTableType::Internal(Arc::new(RefCell::new(json_each))),
};
let mut vtables: Vec<Arc<VirtualTable>> = PragmaVirtualTable::functions()
.into_iter()
.map(|(tab, schema)| {
let vtab = VirtualTable {
name: format!("pragma_{}", tab.pragma_name),
columns: Self::resolve_columns(schema)
.expect("built-in function schema resolution should not fail"),
.expect("pragma table-valued function schema resolution should not fail"),
kind: VTabKind::TableValuedFunction,
vtab_type: VirtualTableType::Pragma(tab),
};
Arc::new(vtab)
})
.collect()
.collect();
vtables.push(Arc::new(json_each_virtual_table));
vtables
}
pub(crate) fn function(name: &str, syms: &SymbolTable) -> crate::Result<Arc<VirtualTable>> {
@@ -107,6 +124,9 @@ impl VirtualTable {
VirtualTableType::External(table) => {
Ok(VirtualTableCursor::External(table.open(conn.clone())?))
}
VirtualTableType::Internal(table) => {
Ok(VirtualTableCursor::Internal(table.borrow().open(conn)?))
}
}
}
@@ -114,6 +134,7 @@ impl VirtualTable {
match &self.vtab_type {
VirtualTableType::Pragma(_) => Err(LimboError::ReadOnly),
VirtualTableType::External(table) => table.update(args),
VirtualTableType::Internal(_) => Err(LimboError::ReadOnly),
}
}
@@ -121,6 +142,7 @@ impl VirtualTable {
match &self.vtab_type {
VirtualTableType::Pragma(_) => Ok(()),
VirtualTableType::External(table) => table.destroy(),
VirtualTableType::Internal(_) => Ok(()),
}
}
@@ -132,6 +154,7 @@ impl VirtualTable {
match &self.vtab_type {
VirtualTableType::Pragma(table) => table.best_index(constraints),
VirtualTableType::External(table) => table.best_index(constraints, order_by),
VirtualTableType::Internal(table) => table.borrow().best_index(constraints, order_by),
}
}
}
@@ -139,6 +162,7 @@ impl VirtualTable {
pub enum VirtualTableCursor {
Pragma(Box<PragmaVirtualTableCursor>),
External(ExtVirtualTableCursor),
Internal(Arc<RefCell<dyn InternalVirtualTableCursor>>),
}
impl VirtualTableCursor {
@@ -146,6 +170,7 @@ impl VirtualTableCursor {
match self {
VirtualTableCursor::Pragma(cursor) => cursor.next(),
VirtualTableCursor::External(cursor) => cursor.next(),
VirtualTableCursor::Internal(cursor) => cursor.borrow_mut().next(),
}
}
@@ -153,6 +178,7 @@ impl VirtualTableCursor {
match self {
VirtualTableCursor::Pragma(cursor) => cursor.rowid(),
VirtualTableCursor::External(cursor) => cursor.rowid(),
VirtualTableCursor::Internal(cursor) => cursor.borrow().rowid(),
}
}
@@ -160,6 +186,7 @@ impl VirtualTableCursor {
match self {
VirtualTableCursor::Pragma(cursor) => cursor.column(column),
VirtualTableCursor::External(cursor) => cursor.column(column),
VirtualTableCursor::Internal(cursor) => cursor.borrow().column(column),
}
}
@@ -175,6 +202,9 @@ impl VirtualTableCursor {
VirtualTableCursor::External(cursor) => {
cursor.filter(idx_num, idx_str, arg_count, args)
}
VirtualTableCursor::Internal(cursor) => {
cursor.borrow_mut().filter(&args, idx_str, idx_num)
}
}
}
}
@@ -376,3 +406,31 @@ impl Drop for ExtVirtualTableCursor {
}
}
}
pub trait InternalVirtualTable: std::fmt::Debug {
fn name(&self) -> String;
fn open(
&self,
conn: Arc<Connection>,
) -> crate::Result<Arc<RefCell<dyn InternalVirtualTableCursor>>>;
/// best_index is used by the optimizer. See the comment on `Table::best_index`.
fn best_index(
&self,
constraints: &[turso_ext::ConstraintInfo],
order_by: &[turso_ext::OrderByInfo],
) -> Result<turso_ext::IndexInfo, ResultCode>;
fn sql(&self) -> String;
}
pub trait InternalVirtualTableCursor {
/// next returns `Ok(true)` if there are more rows, and `Ok(false)` otherwise.
fn next(&mut self) -> Result<bool, LimboError>;
fn rowid(&self) -> i64;
fn column(&self, column: usize) -> Result<Value, LimboError>;
fn filter(
&mut self,
args: &[Value],
idx_str: Option<String>,
idx_num: i32,
) -> Result<bool, LimboError>;
}