diff --git a/core/ext/mod.rs b/core/ext/mod.rs index 4a4a1780d..dde4bcea7 100644 --- a/core/ext/mod.rs +++ b/core/ext/mod.rs @@ -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(()) } diff --git a/core/json/mod.rs b/core/json/mod.rs index a8753a2de..311c595fa 100644 --- a/core/json/mod.rs +++ b/core/json/mod.rs @@ -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 { - 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 { if let Value::Null = value { return Ok(Value::Null); diff --git a/core/json/vtab.rs b/core/json/vtab.rs index e7ec527be..81a90695b 100644 --- a/core/json/vtab.rs +++ b/core/json/vtab.rs @@ -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>, - ) -> Result { - Ok(JsonEachCursor::default()) + _conn: Arc, + ) -> crate::Result>> { + 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 { 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, + _idx_num: i32, + ) -> Result { 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 { 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 { + fn column(&self, idx: usize) -> Result { 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 { - 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 { + pub(super) fn atom(&self) -> Result { Self::atom_from_value(&self.value) } - pub(super) fn value(&self) -> Result { - 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 { + 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 { + fn atom_from_value(value: &Jsonb) -> Result { let element_type = value.element_type().expect("invalid value"); - let string: Result = 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 = 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 { - let string = try_result!("converting jsonb to integer", value.to_string(), Err); - let int = try_result!("parsing int", string.parse::(), Err); + fn jsonb_to_integer(value: &Jsonb) -> Result { + let string = value.to_string()?; + let int = string.parse::()?; - Ok(Value::from_integer(int)) + Ok(Value::Integer(int)) } - fn jsonb_to_float(value: &Jsonb) -> Result { - let string = try_result!("converting jsonb to integer", value.to_string(), Err); - let float = try_result!("parsing float", string.parse::(), Err); + fn jsonb_to_float(value: &Jsonb) -> Result { + let string = value.to_string()?; + let float = string.parse::()?; - 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)) } } } diff --git a/core/vtab.rs b/core/vtab.rs index fc511ba40..1b60267d7 100644 --- a/core/vtab.rs +++ b/core/vtab.rs @@ -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>), } #[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> { - 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> = 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> { @@ -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), External(ExtVirtualTableCursor), + Internal(Arc>), } 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, + ) -> crate::Result>>; + /// 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; + 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; + fn rowid(&self) -> i64; + fn column(&self, column: usize) -> Result; + fn filter( + &mut self, + args: &[Value], + idx_str: Option, + idx_num: i32, + ) -> Result; +}