mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-23 17:05:36 +01:00
use new internal virtual table type
This commit is contained in:
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
66
core/vtab.rs
66
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<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>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user