mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-08 02:34:20 +01:00
Merge 'core: Kill value type' from Pekka Enberg
We currently have two value types, `Value` and `OwnedValue`. The original thinking was that `Value` is external type and `OwnedValue` is internal type. However, this just results in unnecessary transformation between the types as data crosses the Limbo library boundary. Let's just follow SQLite here and consolidate on a single value type (where `sqlite3_value` is just an alias for the internal `Mem` type). The way this will eventually work is that we can have bunch of pre- allocated `OwnedValue` objects in `ProgramState` and basically return a reference to them all the way to the application itself, which extracts the actual value. Fixes #906 Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com> Closes #1056
This commit is contained in:
@@ -76,8 +76,7 @@ pub extern "C" fn rows_get_value(ctx: *mut c_void, col_idx: usize) -> *const c_v
|
||||
|
||||
if let Some(row) = ctx.stmt.row() {
|
||||
if let Some(value) = row.get_values().get(col_idx) {
|
||||
let value = value.to_value();
|
||||
return LimboValue::from_value(&value).to_ptr();
|
||||
return LimboValue::from_value(value).to_ptr();
|
||||
}
|
||||
}
|
||||
std::ptr::null()
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use std::ffi::{c_char, c_void};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(C)]
|
||||
pub enum ResultCode {
|
||||
@@ -50,25 +52,18 @@ struct Blob {
|
||||
|
||||
pub struct AllocPool {
|
||||
strings: Vec<String>,
|
||||
blobs: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl AllocPool {
|
||||
pub fn new() -> Self {
|
||||
AllocPool {
|
||||
strings: Vec::new(),
|
||||
blobs: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn add_string(&mut self, s: String) -> &String {
|
||||
self.strings.push(s);
|
||||
self.strings.last().unwrap()
|
||||
}
|
||||
|
||||
pub fn add_blob(&mut self, b: Vec<u8>) -> &Vec<u8> {
|
||||
self.blobs.push(b);
|
||||
self.blobs.last().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -148,61 +143,65 @@ impl LimboValue {
|
||||
Box::into_raw(Box::new(self)) as *const c_void
|
||||
}
|
||||
|
||||
pub fn from_value(value: &limbo_core::Value<'_>) -> Self {
|
||||
pub fn from_value(value: &limbo_core::OwnedValue) -> Self {
|
||||
match value {
|
||||
limbo_core::Value::Integer(i) => {
|
||||
limbo_core::OwnedValue::Integer(i) => {
|
||||
LimboValue::new(ValueType::Integer, ValueUnion::from_int(*i))
|
||||
}
|
||||
limbo_core::Value::Float(r) => {
|
||||
limbo_core::OwnedValue::Float(r) => {
|
||||
LimboValue::new(ValueType::Real, ValueUnion::from_real(*r))
|
||||
}
|
||||
limbo_core::Value::Text(s) => LimboValue::new(ValueType::Text, ValueUnion::from_str(s)),
|
||||
limbo_core::Value::Blob(b) => {
|
||||
limbo_core::OwnedValue::Text(s) => {
|
||||
LimboValue::new(ValueType::Text, ValueUnion::from_str(s.as_str()))
|
||||
}
|
||||
limbo_core::OwnedValue::Blob(b) => {
|
||||
LimboValue::new(ValueType::Blob, ValueUnion::from_bytes(b))
|
||||
}
|
||||
limbo_core::Value::Null => LimboValue::new(ValueType::Null, ValueUnion::from_null()),
|
||||
limbo_core::OwnedValue::Null => {
|
||||
LimboValue::new(ValueType::Null, ValueUnion::from_null())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// The values we get from Go need to be temporarily owned by the statement until they are bound
|
||||
// then they can be cleaned up immediately afterwards
|
||||
pub fn to_value<'pool>(&self, pool: &'pool mut AllocPool) -> limbo_core::Value<'pool> {
|
||||
pub fn to_value(&self, pool: &mut AllocPool) -> limbo_core::OwnedValue {
|
||||
match self.value_type {
|
||||
ValueType::Integer => {
|
||||
if unsafe { self.value.int_val == 0 } {
|
||||
return limbo_core::Value::Null;
|
||||
return limbo_core::OwnedValue::Null;
|
||||
}
|
||||
limbo_core::Value::Integer(unsafe { self.value.int_val })
|
||||
limbo_core::OwnedValue::Integer(unsafe { self.value.int_val })
|
||||
}
|
||||
ValueType::Real => {
|
||||
if unsafe { self.value.real_val == 0.0 } {
|
||||
return limbo_core::Value::Null;
|
||||
return limbo_core::OwnedValue::Null;
|
||||
}
|
||||
limbo_core::Value::Float(unsafe { self.value.real_val })
|
||||
limbo_core::OwnedValue::Float(unsafe { self.value.real_val })
|
||||
}
|
||||
ValueType::Text => {
|
||||
if unsafe { self.value.text_ptr.is_null() } {
|
||||
return limbo_core::Value::Null;
|
||||
return limbo_core::OwnedValue::Null;
|
||||
}
|
||||
let cstr = unsafe { std::ffi::CStr::from_ptr(self.value.text_ptr) };
|
||||
match cstr.to_str() {
|
||||
Ok(utf8_str) => {
|
||||
let owned = utf8_str.to_owned();
|
||||
let borrowed = pool.add_string(owned);
|
||||
limbo_core::Value::Text(borrowed)
|
||||
limbo_core::OwnedValue::build_text(borrowed)
|
||||
}
|
||||
Err(_) => limbo_core::Value::Null,
|
||||
Err(_) => limbo_core::OwnedValue::Null,
|
||||
}
|
||||
}
|
||||
ValueType::Blob => {
|
||||
if unsafe { self.value.blob_ptr.is_null() } {
|
||||
return limbo_core::Value::Null;
|
||||
return limbo_core::OwnedValue::Null;
|
||||
}
|
||||
let bytes = self.value.to_bytes();
|
||||
let borrowed = pool.add_blob(bytes.to_vec());
|
||||
limbo_core::Value::Blob(borrowed)
|
||||
limbo_core::OwnedValue::Blob(Rc::new(bytes.to_vec()))
|
||||
}
|
||||
ValueType::Null => limbo_core::Value::Null,
|
||||
ValueType::Null => limbo_core::OwnedValue::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,9 @@ use crate::utils::set_err_msg_and_throw_exception;
|
||||
use jni::objects::{JByteArray, JObject, JObjectArray, JString, JValue};
|
||||
use jni::sys::{jdouble, jint, jlong};
|
||||
use jni::JNIEnv;
|
||||
use limbo_core::{Statement, StepResult, Value};
|
||||
use limbo_core::{OwnedValue, Statement, StepResult};
|
||||
use std::num::NonZero;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub const STEP_RESULT_ID_ROW: i32 = 10;
|
||||
#[allow(dead_code)]
|
||||
@@ -105,17 +106,17 @@ fn row_to_obj_array<'local>(
|
||||
let obj_array = env.new_object_array(row.len() as i32, "java/lang/Object", JObject::null())?;
|
||||
|
||||
for (i, value) in row.get_values().iter().enumerate() {
|
||||
let value = value.to_value();
|
||||
let obj = match value {
|
||||
limbo_core::Value::Null => JObject::null(),
|
||||
limbo_core::Value::Integer(i) => {
|
||||
env.new_object("java/lang/Long", "(J)V", &[JValue::Long(i)])?
|
||||
limbo_core::OwnedValue::Null => JObject::null(),
|
||||
limbo_core::OwnedValue::Integer(i) => {
|
||||
env.new_object("java/lang/Long", "(J)V", &[JValue::Long(*i)])?
|
||||
}
|
||||
limbo_core::Value::Float(f) => {
|
||||
env.new_object("java/lang/Double", "(D)V", &[JValue::Double(f)])?
|
||||
limbo_core::OwnedValue::Float(f) => {
|
||||
env.new_object("java/lang/Double", "(D)V", &[JValue::Double(*f)])?
|
||||
}
|
||||
limbo_core::Value::Text(s) => env.new_string(s)?.into(),
|
||||
limbo_core::Value::Blob(b) => env.byte_array_from_slice(b)?.into(),
|
||||
limbo_core::OwnedValue::Text(s) => env.new_string(s.as_str())?.into(),
|
||||
limbo_core::OwnedValue::Blob(b) => env.byte_array_from_slice(&b)?.into(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
if let Err(e) = env.set_object_array_element(&obj_array, i as i32, obj) {
|
||||
eprintln!("Error on parsing row: {:?}", e);
|
||||
@@ -163,7 +164,7 @@ pub extern "system" fn Java_tech_turso_core_LimboStatement_bindNull<'local>(
|
||||
};
|
||||
|
||||
stmt.stmt
|
||||
.bind_at(NonZero::new(position as usize).unwrap(), Value::Null);
|
||||
.bind_at(NonZero::new(position as usize).unwrap(), OwnedValue::Null);
|
||||
SQLITE_OK
|
||||
}
|
||||
|
||||
@@ -185,7 +186,7 @@ pub extern "system" fn Java_tech_turso_core_LimboStatement_bindLong<'local>(
|
||||
|
||||
stmt.stmt.bind_at(
|
||||
NonZero::new(position as usize).unwrap(),
|
||||
Value::Integer(value),
|
||||
OwnedValue::Integer(value),
|
||||
);
|
||||
SQLITE_OK
|
||||
}
|
||||
@@ -208,7 +209,7 @@ pub extern "system" fn Java_tech_turso_core_LimboStatement_bindDouble<'local>(
|
||||
|
||||
stmt.stmt.bind_at(
|
||||
NonZero::new(position as usize).unwrap(),
|
||||
Value::Float(value),
|
||||
OwnedValue::Float(value),
|
||||
);
|
||||
SQLITE_OK
|
||||
}
|
||||
@@ -236,7 +237,7 @@ pub extern "system" fn Java_tech_turso_core_LimboStatement_bindText<'local>(
|
||||
|
||||
stmt.stmt.bind_at(
|
||||
NonZero::new(position as usize).unwrap(),
|
||||
Value::Text(text.as_str()),
|
||||
OwnedValue::build_text(text.as_str()),
|
||||
);
|
||||
SQLITE_OK
|
||||
}
|
||||
@@ -264,7 +265,7 @@ pub extern "system" fn Java_tech_turso_core_LimboStatement_bindBlob<'local>(
|
||||
|
||||
stmt.stmt.bind_at(
|
||||
NonZero::new(position as usize).unwrap(),
|
||||
Value::Blob(blob.as_ref()),
|
||||
OwnedValue::Blob(Rc::new(blob)),
|
||||
);
|
||||
SQLITE_OK
|
||||
}
|
||||
|
||||
@@ -302,12 +302,13 @@ fn row_to_py(py: Python, row: &limbo_core::Row) -> PyObject {
|
||||
let py_values: Vec<PyObject> = row
|
||||
.get_values()
|
||||
.iter()
|
||||
.map(|value| match value.to_value() {
|
||||
limbo_core::Value::Null => py.None(),
|
||||
limbo_core::Value::Integer(i) => i.to_object(py),
|
||||
limbo_core::Value::Float(f) => f.to_object(py),
|
||||
limbo_core::Value::Text(s) => s.to_object(py),
|
||||
limbo_core::Value::Blob(b) => b.to_object(py),
|
||||
.map(|value| match value {
|
||||
limbo_core::OwnedValue::Null => py.None(),
|
||||
limbo_core::OwnedValue::Integer(i) => i.to_object(py),
|
||||
limbo_core::OwnedValue::Float(f) => f.to_object(py),
|
||||
limbo_core::OwnedValue::Text(s) => s.as_str().to_object(py),
|
||||
limbo_core::OwnedValue::Blob(b) => b.to_object(py),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
@@ -77,7 +77,6 @@ impl RowIterator {
|
||||
let row = stmt.row().unwrap();
|
||||
let row_array = Array::new();
|
||||
for value in row.get_values() {
|
||||
let value = value.to_value();
|
||||
let value = to_js_value(value);
|
||||
row_array.push(&value);
|
||||
}
|
||||
@@ -118,7 +117,6 @@ impl Statement {
|
||||
let row = stmt.row().unwrap();
|
||||
let row_array = js_sys::Array::new();
|
||||
for value in row.get_values() {
|
||||
let value = value.to_value();
|
||||
let value = to_js_value(value);
|
||||
row_array.push(&value);
|
||||
}
|
||||
@@ -141,7 +139,6 @@ impl Statement {
|
||||
let row = stmt.row().unwrap();
|
||||
let row_array = js_sys::Array::new();
|
||||
for value in row.get_values() {
|
||||
let value = value.to_value();
|
||||
let value = to_js_value(value);
|
||||
row_array.push(&value);
|
||||
}
|
||||
@@ -189,19 +186,21 @@ impl Statement {
|
||||
}
|
||||
}
|
||||
|
||||
fn to_js_value(value: limbo_core::Value) -> JsValue {
|
||||
fn to_js_value(value: &limbo_core::OwnedValue) -> JsValue {
|
||||
match value {
|
||||
limbo_core::Value::Null => JsValue::null(),
|
||||
limbo_core::Value::Integer(i) => {
|
||||
limbo_core::OwnedValue::Null => JsValue::null(),
|
||||
limbo_core::OwnedValue::Integer(i) => {
|
||||
let i = *i;
|
||||
if i >= i32::MIN as i64 && i <= i32::MAX as i64 {
|
||||
JsValue::from(i as i32)
|
||||
} else {
|
||||
JsValue::from(i)
|
||||
}
|
||||
}
|
||||
limbo_core::Value::Float(f) => JsValue::from(f),
|
||||
limbo_core::Value::Text(t) => JsValue::from_str(t),
|
||||
limbo_core::Value::Blob(b) => js_sys::Uint8Array::from(b).into(),
|
||||
limbo_core::OwnedValue::Float(f) => JsValue::from(*f),
|
||||
limbo_core::OwnedValue::Text(t) => JsValue::from_str(t.as_str()),
|
||||
limbo_core::OwnedValue::Blob(b) => js_sys::Uint8Array::from(b.as_slice()).into(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
43
cli/app.rs
43
cli/app.rs
@@ -4,7 +4,7 @@ use crate::{
|
||||
opcodes_dictionary::OPCODE_DESCRIPTIONS,
|
||||
};
|
||||
use comfy_table::{Attribute, Cell, CellAlignment, ContentArrangement, Row, Table};
|
||||
use limbo_core::{Database, LimboError, Statement, StepResult, Value};
|
||||
use limbo_core::{Database, LimboError, OwnedValue, Statement, StepResult};
|
||||
|
||||
use clap::{Parser, ValueEnum};
|
||||
use rustyline::DefaultEditor;
|
||||
@@ -662,19 +662,19 @@ impl<'a> Limbo<'a> {
|
||||
Ok(StepResult::Row) => {
|
||||
let row = rows.row().unwrap();
|
||||
for (i, value) in row.get_values().iter().enumerate() {
|
||||
let value = value.to_value();
|
||||
if i > 0 {
|
||||
let _ = self.writer.write(b"|");
|
||||
}
|
||||
let _ = self.writer.write(
|
||||
match value {
|
||||
Value::Null => self.opts.null_value.clone(),
|
||||
Value::Integer(i) => format!("{}", i),
|
||||
Value::Float(f) => format!("{:?}", f),
|
||||
Value::Text(s) => s.to_string(),
|
||||
Value::Blob(b) => {
|
||||
OwnedValue::Null => self.opts.null_value.clone(),
|
||||
OwnedValue::Integer(i) => format!("{}", i),
|
||||
OwnedValue::Float(f) => format!("{:?}", f),
|
||||
OwnedValue::Text(s) => s.to_string(),
|
||||
OwnedValue::Blob(b) => {
|
||||
format!("{}", String::from_utf8_lossy(b))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
.as_bytes(),
|
||||
)?;
|
||||
@@ -724,17 +724,22 @@ impl<'a> Limbo<'a> {
|
||||
let mut row = Row::new();
|
||||
row.max_height(1);
|
||||
for value in record.get_values() {
|
||||
let (content, alignment) = match value.to_value() {
|
||||
Value::Null => {
|
||||
let (content, alignment) = match value {
|
||||
OwnedValue::Null => {
|
||||
(self.opts.null_value.clone(), CellAlignment::Left)
|
||||
}
|
||||
Value::Integer(i) => (i.to_string(), CellAlignment::Right),
|
||||
Value::Float(f) => (f.to_string(), CellAlignment::Right),
|
||||
Value::Text(s) => (s.to_string(), CellAlignment::Left),
|
||||
Value::Blob(b) => (
|
||||
OwnedValue::Integer(i) => {
|
||||
(i.to_string(), CellAlignment::Right)
|
||||
}
|
||||
OwnedValue::Float(f) => {
|
||||
(f.to_string(), CellAlignment::Right)
|
||||
}
|
||||
OwnedValue::Text(s) => (s.to_string(), CellAlignment::Left),
|
||||
OwnedValue::Blob(b) => (
|
||||
String::from_utf8_lossy(b).to_string(),
|
||||
CellAlignment::Left,
|
||||
),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
row.add_cell(Cell::new(content).set_alignment(alignment));
|
||||
}
|
||||
@@ -796,10 +801,8 @@ impl<'a> Limbo<'a> {
|
||||
match rows.step()? {
|
||||
StepResult::Row => {
|
||||
let row = rows.row().unwrap();
|
||||
if let Some(Value::Text(schema)) =
|
||||
row.get_values().first().map(|v| v.to_value())
|
||||
{
|
||||
let _ = self.write_fmt(format_args!("{};", schema));
|
||||
if let Some(OwnedValue::Text(schema)) = row.get_values().first() {
|
||||
let _ = self.write_fmt(format_args!("{};", schema.as_str()));
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
@@ -856,10 +859,8 @@ impl<'a> Limbo<'a> {
|
||||
match rows.step()? {
|
||||
StepResult::Row => {
|
||||
let row = rows.row().unwrap();
|
||||
if let Some(Value::Text(table)) =
|
||||
row.get_values().first().map(|v| v.to_value())
|
||||
{
|
||||
tables.push_str(table);
|
||||
if let Some(OwnedValue::Text(table)) = row.get_values().first() {
|
||||
tables.push_str(table.as_str());
|
||||
tables.push(' ');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,8 +48,7 @@ use storage::sqlite3_ondisk::{DatabaseHeader, DATABASE_HEADER_SIZE};
|
||||
pub use storage::wal::CheckpointMode;
|
||||
pub use storage::wal::WalFile;
|
||||
pub use storage::wal::WalFileShared;
|
||||
use types::OwnedValue;
|
||||
pub use types::Value;
|
||||
pub use types::OwnedValue;
|
||||
use util::{columns_from_create_table_body, parse_schema_rows};
|
||||
use vdbe::builder::QueryMode;
|
||||
use vdbe::VTabOpaqueCursor;
|
||||
@@ -497,7 +496,7 @@ impl Statement {
|
||||
self.program.parameters.count()
|
||||
}
|
||||
|
||||
pub fn bind_at(&mut self, index: NonZero<usize>, value: Value) {
|
||||
pub fn bind_at(&mut self, index: NonZero<usize>, value: OwnedValue) {
|
||||
self.state.bind_at(index, value.into());
|
||||
}
|
||||
|
||||
|
||||
@@ -11,27 +11,6 @@ use crate::Result;
|
||||
use std::fmt::Display;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Value<'a> {
|
||||
Null,
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
Text(&'a str),
|
||||
Blob(&'a [u8]),
|
||||
}
|
||||
|
||||
impl Display for Value<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Null => write!(f, "NULL"),
|
||||
Self::Integer(i) => write!(f, "{}", i),
|
||||
Self::Float(fl) => write!(f, "{}", fl),
|
||||
Self::Text(s) => write!(f, "{}", s),
|
||||
Self::Blob(b) => write!(f, "{:?}", b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum OwnedValueType {
|
||||
Null,
|
||||
@@ -73,6 +52,10 @@ impl Text {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
self.as_str().to_string()
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
unsafe { std::str::from_utf8_unchecked(self.value.as_ref()) }
|
||||
}
|
||||
@@ -128,46 +111,6 @@ impl OwnedValue {
|
||||
OwnedValue::Record(_) => OwnedValueType::Null, // Map Record to Null for FFI
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_value(&self) -> Value<'_> {
|
||||
match self {
|
||||
OwnedValue::Null => Value::Null,
|
||||
OwnedValue::Integer(i) => Value::Integer(*i),
|
||||
OwnedValue::Float(f) => Value::Float(*f),
|
||||
OwnedValue::Text(s) => Value::Text(s.as_str()),
|
||||
OwnedValue::Blob(b) => Value::Blob(b),
|
||||
OwnedValue::Agg(a) => match a.as_ref() {
|
||||
AggContext::Avg(acc, _count) => match acc {
|
||||
OwnedValue::Integer(i) => Value::Integer(*i),
|
||||
OwnedValue::Float(f) => Value::Float(*f),
|
||||
_ => Value::Float(0.0),
|
||||
},
|
||||
AggContext::Sum(acc) => match acc {
|
||||
OwnedValue::Integer(i) => Value::Integer(*i),
|
||||
OwnedValue::Float(f) => Value::Float(*f),
|
||||
_ => Value::Float(0.0),
|
||||
},
|
||||
AggContext::Count(count) => count.to_value(),
|
||||
AggContext::Max(max) => match max {
|
||||
Some(max) => max.to_value(),
|
||||
None => Value::Null,
|
||||
},
|
||||
AggContext::Min(min) => match min {
|
||||
Some(min) => min.to_value(),
|
||||
None => Value::Null,
|
||||
},
|
||||
AggContext::GroupConcat(s) => s.to_value(),
|
||||
AggContext::External(ext_state) => {
|
||||
let v = ext_state
|
||||
.finalized_value
|
||||
.as_ref()
|
||||
.unwrap_or(&OwnedValue::Null);
|
||||
v.to_value()
|
||||
}
|
||||
},
|
||||
OwnedValue::Record(_) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
@@ -478,18 +421,6 @@ impl std::ops::DivAssign<OwnedValue> for OwnedValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Value<'_>> for OwnedValue {
|
||||
fn from(value: Value<'_>) -> Self {
|
||||
match value {
|
||||
Value::Null => OwnedValue::Null,
|
||||
Value::Integer(i) => OwnedValue::Integer(i),
|
||||
Value::Float(f) => OwnedValue::Float(f),
|
||||
Value::Text(s) => OwnedValue::Text(Text::from_str(s)),
|
||||
Value::Blob(b) => OwnedValue::Blob(Rc::new(b.to_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FromValue<'a> {
|
||||
fn from_value(value: &'a OwnedValue) -> Result<Self>
|
||||
where
|
||||
|
||||
@@ -1875,14 +1875,58 @@ impl Program {
|
||||
unreachable!();
|
||||
};
|
||||
*acc /= count.clone();
|
||||
state.registers[*register] = acc.clone();
|
||||
}
|
||||
AggFunc::Sum | AggFunc::Total => {
|
||||
let AggContext::Sum(acc) = agg.borrow_mut() else {
|
||||
unreachable!();
|
||||
};
|
||||
let value = match acc {
|
||||
OwnedValue::Integer(i) => OwnedValue::Integer(*i),
|
||||
OwnedValue::Float(f) => OwnedValue::Float(*f),
|
||||
_ => OwnedValue::Float(0.0),
|
||||
};
|
||||
state.registers[*register] = value;
|
||||
}
|
||||
AggFunc::Count | AggFunc::Count0 => {
|
||||
let AggContext::Count(count) = agg.borrow_mut() else {
|
||||
unreachable!();
|
||||
};
|
||||
state.registers[*register] = count.clone();
|
||||
}
|
||||
AggFunc::Max => {
|
||||
let AggContext::Max(acc) = agg.borrow_mut() else {
|
||||
unreachable!();
|
||||
};
|
||||
match acc {
|
||||
Some(value) => state.registers[*register] = value.clone(),
|
||||
None => state.registers[*register] = OwnedValue::Null,
|
||||
}
|
||||
}
|
||||
AggFunc::Min => {
|
||||
let AggContext::Min(acc) = agg.borrow_mut() else {
|
||||
unreachable!();
|
||||
};
|
||||
match acc {
|
||||
Some(value) => state.registers[*register] = value.clone(),
|
||||
None => state.registers[*register] = OwnedValue::Null,
|
||||
}
|
||||
}
|
||||
AggFunc::GroupConcat | AggFunc::StringAgg => {
|
||||
let AggContext::GroupConcat(acc) = agg.borrow_mut() else {
|
||||
unreachable!();
|
||||
};
|
||||
state.registers[*register] = acc.clone();
|
||||
}
|
||||
AggFunc::Sum | AggFunc::Total => {}
|
||||
AggFunc::Count | AggFunc::Count0 => {}
|
||||
AggFunc::Max => {}
|
||||
AggFunc::Min => {}
|
||||
AggFunc::GroupConcat | AggFunc::StringAgg => {}
|
||||
AggFunc::External(_) => {
|
||||
agg.compute_external()?;
|
||||
let AggContext::External(agg_state) = agg.borrow_mut() else {
|
||||
unreachable!();
|
||||
};
|
||||
match &agg_state.finalized_value {
|
||||
Some(value) => state.registers[*register] = value.clone(),
|
||||
None => state.registers[*register] = OwnedValue::Null,
|
||||
}
|
||||
}
|
||||
},
|
||||
OwnedValue::Null => {
|
||||
|
||||
@@ -492,14 +492,16 @@ impl Interaction {
|
||||
StepResult::Row => {
|
||||
let row = rows.row().unwrap();
|
||||
let mut r = Vec::new();
|
||||
for el in row.get_values() {
|
||||
let v = el.to_value();
|
||||
for v in row.get_values() {
|
||||
let v = match v {
|
||||
limbo_core::Value::Null => Value::Null,
|
||||
limbo_core::Value::Integer(i) => Value::Integer(i),
|
||||
limbo_core::Value::Float(f) => Value::Float(f),
|
||||
limbo_core::Value::Text(t) => Value::Text(t.to_string()),
|
||||
limbo_core::Value::Blob(b) => Value::Blob(b.to_vec()),
|
||||
limbo_core::OwnedValue::Null => Value::Null,
|
||||
limbo_core::OwnedValue::Integer(i) => Value::Integer(*i),
|
||||
limbo_core::OwnedValue::Float(f) => Value::Float(*f),
|
||||
limbo_core::OwnedValue::Text(t) => {
|
||||
Value::Text(t.as_str().to_string())
|
||||
}
|
||||
limbo_core::OwnedValue::Blob(b) => Value::Blob(b.to_vec()),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
r.push(v);
|
||||
}
|
||||
|
||||
@@ -563,63 +563,64 @@ pub unsafe extern "C" fn sqlite3_column_bytes(
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_value_type(value: *mut ffi::c_void) -> ffi::c_int {
|
||||
let value = value as *mut limbo_core::Value;
|
||||
let value = value as *mut limbo_core::OwnedValue;
|
||||
let value = &*value;
|
||||
match value {
|
||||
limbo_core::Value::Null => 0,
|
||||
limbo_core::Value::Integer(_) => 1,
|
||||
limbo_core::Value::Float(_) => 2,
|
||||
limbo_core::Value::Text(_) => 3,
|
||||
limbo_core::Value::Blob(_) => 4,
|
||||
limbo_core::OwnedValue::Null => 0,
|
||||
limbo_core::OwnedValue::Integer(_) => 1,
|
||||
limbo_core::OwnedValue::Float(_) => 2,
|
||||
limbo_core::OwnedValue::Text(_) => 3,
|
||||
limbo_core::OwnedValue::Blob(_) => 4,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_value_int64(value: *mut ffi::c_void) -> i64 {
|
||||
let value = value as *mut limbo_core::Value;
|
||||
let value = value as *mut limbo_core::OwnedValue;
|
||||
let value = &*value;
|
||||
match value {
|
||||
limbo_core::Value::Integer(i) => *i,
|
||||
limbo_core::OwnedValue::Integer(i) => *i,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_value_double(value: *mut ffi::c_void) -> f64 {
|
||||
let value = value as *mut limbo_core::Value;
|
||||
let value = value as *mut limbo_core::OwnedValue;
|
||||
let value = &*value;
|
||||
match value {
|
||||
limbo_core::Value::Float(f) => *f,
|
||||
limbo_core::OwnedValue::Float(f) => *f,
|
||||
_ => 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_value_text(value: *mut ffi::c_void) -> *const ffi::c_uchar {
|
||||
let value = value as *mut limbo_core::Value;
|
||||
let value = value as *mut limbo_core::OwnedValue;
|
||||
let value = &*value;
|
||||
match value {
|
||||
limbo_core::Value::Text(text) => text.as_bytes().as_ptr(),
|
||||
limbo_core::OwnedValue::Text(text) => text.as_str().as_ptr(),
|
||||
_ => std::ptr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_value_blob(value: *mut ffi::c_void) -> *const ffi::c_void {
|
||||
let value = value as *mut limbo_core::Value;
|
||||
let value = value as *mut limbo_core::OwnedValue;
|
||||
let value = &*value;
|
||||
match value {
|
||||
limbo_core::Value::Blob(blob) => blob.as_ptr() as *const ffi::c_void,
|
||||
limbo_core::OwnedValue::Blob(blob) => blob.as_ptr() as *const ffi::c_void,
|
||||
_ => std::ptr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sqlite3_value_bytes(value: *mut ffi::c_void) -> ffi::c_int {
|
||||
let value = value as *mut limbo_core::Value;
|
||||
let value = value as *mut limbo_core::OwnedValue;
|
||||
let value = &*value;
|
||||
match value {
|
||||
limbo_core::Value::Blob(blob) => blob.len() as ffi::c_int,
|
||||
limbo_core::OwnedValue::Blob(blob) => blob.len() as ffi::c_int,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
@@ -635,8 +636,8 @@ pub unsafe extern "C" fn sqlite3_column_text(
|
||||
Some(row) => row,
|
||||
None => return std::ptr::null(),
|
||||
};
|
||||
match row.get_values().get(idx as usize).map(|v| v.to_value()) {
|
||||
Some(limbo_core::Value::Text(text)) => text.as_bytes().as_ptr(),
|
||||
match row.get_values().get(idx as usize) {
|
||||
Some(limbo_core::OwnedValue::Text(text)) => text.as_str().as_ptr(),
|
||||
_ => std::ptr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::common::{do_flush, TempDatabase};
|
||||
use limbo_core::{StepResult, Value};
|
||||
use limbo_core::StepResult;
|
||||
|
||||
#[test]
|
||||
fn test_last_insert_rowid_basic() -> anyhow::Result<()> {
|
||||
@@ -30,8 +30,8 @@ fn test_last_insert_rowid_basic() -> anyhow::Result<()> {
|
||||
match rows.step()? {
|
||||
StepResult::Row => {
|
||||
let row = rows.row().unwrap();
|
||||
if let Value::Integer(id) = row.get_value(0).to_value() {
|
||||
assert_eq!(id, 1, "First insert should have rowid 1");
|
||||
if let limbo_core::OwnedValue::Integer(id) = row.get_value(0) {
|
||||
assert_eq!(*id, 1, "First insert should have rowid 1");
|
||||
}
|
||||
}
|
||||
StepResult::IO => {
|
||||
@@ -66,8 +66,8 @@ fn test_last_insert_rowid_basic() -> anyhow::Result<()> {
|
||||
match rows.step()? {
|
||||
StepResult::Row => {
|
||||
let row = rows.row().unwrap();
|
||||
if let Value::Integer(id) = row.get_value(0).to_value() {
|
||||
last_id = id;
|
||||
if let limbo_core::OwnedValue::Integer(id) = row.get_value(0) {
|
||||
last_id = *id;
|
||||
}
|
||||
}
|
||||
StepResult::IO => {
|
||||
@@ -112,8 +112,8 @@ fn test_integer_primary_key() -> anyhow::Result<()> {
|
||||
match select_query.step()? {
|
||||
StepResult::Row => {
|
||||
let row = select_query.row().unwrap();
|
||||
if let Value::Integer(id) = row.get_value(0).to_value() {
|
||||
rowids.push(id);
|
||||
if let limbo_core::OwnedValue::Integer(id) = row.get_value(0) {
|
||||
rowids.push(*id);
|
||||
}
|
||||
}
|
||||
StepResult::IO => tmp_db.io.run_once()?,
|
||||
|
||||
@@ -73,12 +73,13 @@ mod tests {
|
||||
let row = row
|
||||
.get_values()
|
||||
.iter()
|
||||
.map(|x| match x.to_value() {
|
||||
limbo_core::Value::Null => rusqlite::types::Value::Null,
|
||||
limbo_core::Value::Integer(x) => rusqlite::types::Value::Integer(x),
|
||||
limbo_core::Value::Float(x) => rusqlite::types::Value::Real(x),
|
||||
limbo_core::Value::Text(x) => rusqlite::types::Value::Text(x.to_string()),
|
||||
limbo_core::Value::Blob(x) => rusqlite::types::Value::Blob(x.to_vec()),
|
||||
.map(|x| match x {
|
||||
limbo_core::OwnedValue::Null => rusqlite::types::Value::Null,
|
||||
limbo_core::OwnedValue::Integer(x) => rusqlite::types::Value::Integer(*x),
|
||||
limbo_core::OwnedValue::Float(x) => rusqlite::types::Value::Real(*x),
|
||||
limbo_core::OwnedValue::Text(x) => rusqlite::types::Value::Text(x.to_string()),
|
||||
limbo_core::OwnedValue::Blob(x) => rusqlite::types::Value::Blob(x.to_vec()),
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect();
|
||||
rows.push(row);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::common::TempDatabase;
|
||||
use limbo_core::{StepResult, Value};
|
||||
use limbo_core::{OwnedValue, StepResult};
|
||||
|
||||
#[test]
|
||||
fn test_statement_reset_bind() -> anyhow::Result<()> {
|
||||
@@ -9,13 +9,13 @@ fn test_statement_reset_bind() -> anyhow::Result<()> {
|
||||
|
||||
let mut stmt = conn.prepare("select ?")?;
|
||||
|
||||
stmt.bind_at(1.try_into()?, Value::Integer(1));
|
||||
stmt.bind_at(1.try_into()?, OwnedValue::Integer(1));
|
||||
|
||||
loop {
|
||||
match stmt.step()? {
|
||||
StepResult::Row => {
|
||||
let row = stmt.row().unwrap();
|
||||
assert_eq!(row.get_value(0).to_value(), Value::Integer(1));
|
||||
assert_eq!(*row.get_value(0), limbo_core::OwnedValue::Integer(1));
|
||||
}
|
||||
StepResult::IO => tmp_db.io.run_once()?,
|
||||
_ => break,
|
||||
@@ -24,13 +24,13 @@ fn test_statement_reset_bind() -> anyhow::Result<()> {
|
||||
|
||||
stmt.reset();
|
||||
|
||||
stmt.bind_at(1.try_into()?, Value::Integer(2));
|
||||
stmt.bind_at(1.try_into()?, OwnedValue::Integer(2));
|
||||
|
||||
loop {
|
||||
match stmt.step()? {
|
||||
StepResult::Row => {
|
||||
let row = stmt.row().unwrap();
|
||||
assert_eq!(row.get_value(0).to_value(), Value::Integer(2));
|
||||
assert_eq!(*row.get_value(0), limbo_core::OwnedValue::Integer(2));
|
||||
}
|
||||
StepResult::IO => tmp_db.io.run_once()?,
|
||||
_ => break,
|
||||
@@ -48,14 +48,14 @@ fn test_statement_bind() -> anyhow::Result<()> {
|
||||
|
||||
let mut stmt = conn.prepare("select ?, ?1, :named, ?3, ?4")?;
|
||||
|
||||
stmt.bind_at(1.try_into()?, Value::Text("hello"));
|
||||
stmt.bind_at(1.try_into()?, OwnedValue::build_text("hello"));
|
||||
|
||||
let i = stmt.parameters().index(":named").unwrap();
|
||||
stmt.bind_at(i, Value::Integer(42));
|
||||
stmt.bind_at(i, OwnedValue::Integer(42));
|
||||
|
||||
stmt.bind_at(3.try_into()?, Value::Blob(&[0x1, 0x2, 0x3]));
|
||||
stmt.bind_at(3.try_into()?, OwnedValue::from_blob(vec![0x1, 0x2, 0x3]));
|
||||
|
||||
stmt.bind_at(4.try_into()?, Value::Float(0.5));
|
||||
stmt.bind_at(4.try_into()?, OwnedValue::Float(0.5));
|
||||
|
||||
assert_eq!(stmt.parameters().count(), 4);
|
||||
|
||||
@@ -63,24 +63,24 @@ fn test_statement_bind() -> anyhow::Result<()> {
|
||||
match stmt.step()? {
|
||||
StepResult::Row => {
|
||||
let row = stmt.row().unwrap();
|
||||
if let Value::Text(s) = row.get_value(0).to_value() {
|
||||
assert_eq!(s, "hello")
|
||||
if let limbo_core::OwnedValue::Text(s) = row.get_value(0) {
|
||||
assert_eq!(s.as_str(), "hello")
|
||||
}
|
||||
|
||||
if let Value::Text(s) = row.get_value(1).to_value() {
|
||||
assert_eq!(s, "hello")
|
||||
if let limbo_core::OwnedValue::Text(s) = row.get_value(1) {
|
||||
assert_eq!(s.as_str(), "hello")
|
||||
}
|
||||
|
||||
if let Value::Integer(i) = row.get_value(2).to_value() {
|
||||
assert_eq!(i, 42)
|
||||
if let limbo_core::OwnedValue::Integer(i) = row.get_value(2) {
|
||||
assert_eq!(*i, 42)
|
||||
}
|
||||
|
||||
if let Value::Blob(v) = row.get_value(3).to_value() {
|
||||
assert_eq!(v, &vec![0x1 as u8, 0x2, 0x3])
|
||||
if let limbo_core::OwnedValue::Blob(v) = row.get_value(3) {
|
||||
assert_eq!(v.as_ref(), &vec![0x1 as u8, 0x2, 0x3])
|
||||
}
|
||||
|
||||
if let Value::Float(f) = row.get_value(4).to_value() {
|
||||
assert_eq!(f, 0.5)
|
||||
if let limbo_core::OwnedValue::Float(f) = row.get_value(4) {
|
||||
assert_eq!(*f, 0.5)
|
||||
}
|
||||
}
|
||||
StepResult::IO => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::common;
|
||||
use crate::common::{compare_string, do_flush, TempDatabase};
|
||||
use limbo_core::{Connection, StepResult, Value};
|
||||
use limbo_core::{Connection, StepResult};
|
||||
use log::debug;
|
||||
use std::rc::Rc;
|
||||
|
||||
@@ -43,15 +43,15 @@ fn test_simple_overflow_page() -> anyhow::Result<()> {
|
||||
match rows.step()? {
|
||||
StepResult::Row => {
|
||||
let row = rows.row().unwrap();
|
||||
let first_value = row.get_value(0).to_value();
|
||||
let text = row.get_value(1).to_value();
|
||||
let first_value = row.get_value(0);
|
||||
let text = row.get_value(1);
|
||||
let id = match first_value {
|
||||
Value::Integer(i) => i as i32,
|
||||
Value::Float(f) => f as i32,
|
||||
limbo_core::OwnedValue::Integer(i) => *i as i32,
|
||||
limbo_core::OwnedValue::Float(f) => *f as i32,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let text = match text {
|
||||
Value::Text(t) => t,
|
||||
limbo_core::OwnedValue::Text(t) => t.as_str(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert_eq!(1, id);
|
||||
@@ -118,15 +118,15 @@ fn test_sequential_overflow_page() -> anyhow::Result<()> {
|
||||
match rows.step()? {
|
||||
StepResult::Row => {
|
||||
let row = rows.row().unwrap();
|
||||
let first_value = row.get_value(0).to_value();
|
||||
let text = row.get_value(1).to_value();
|
||||
let first_value = row.get_value(0);
|
||||
let text = row.get_value(1);
|
||||
let id = match first_value {
|
||||
Value::Integer(i) => i as i32,
|
||||
Value::Float(f) => f as i32,
|
||||
limbo_core::OwnedValue::Integer(i) => *i as i32,
|
||||
limbo_core::OwnedValue::Float(f) => *f as i32,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let text = match text {
|
||||
Value::Text(t) => t,
|
||||
limbo_core::OwnedValue::Text(t) => t.as_str(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let huge_text = &huge_texts[current_index];
|
||||
@@ -191,9 +191,9 @@ fn test_sequential_write() -> anyhow::Result<()> {
|
||||
StepResult::Row => {
|
||||
let row = rows.row().unwrap();
|
||||
let first_value = row.get_values().first().expect("missing id");
|
||||
let id = match first_value.to_value() {
|
||||
Value::Integer(i) => i as i32,
|
||||
Value::Float(f) => f as i32,
|
||||
let id = match first_value {
|
||||
limbo_core::OwnedValue::Integer(i) => *i as i32,
|
||||
limbo_core::OwnedValue::Float(f) => *f as i32,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert_eq!(current_read_index, id);
|
||||
@@ -257,8 +257,8 @@ fn test_regression_multi_row_insert() -> anyhow::Result<()> {
|
||||
StepResult::Row => {
|
||||
let row = rows.row().unwrap();
|
||||
let first_value = row.get_values().first().expect("missing id");
|
||||
let id = match first_value.to_value() {
|
||||
Value::Float(f) => f as i32,
|
||||
let id = match first_value {
|
||||
limbo_core::OwnedValue::Float(f) => *f as i32,
|
||||
_ => panic!("expected float"),
|
||||
};
|
||||
actual_ids.push(id);
|
||||
@@ -302,7 +302,7 @@ fn test_statement_reset() -> anyhow::Result<()> {
|
||||
match stmt.step()? {
|
||||
StepResult::Row => {
|
||||
let row = stmt.row().unwrap();
|
||||
assert_eq!(row.get_value(0).to_value(), Value::Integer(1));
|
||||
assert_eq!(*row.get_value(0), limbo_core::OwnedValue::Integer(1));
|
||||
break;
|
||||
}
|
||||
StepResult::IO => tmp_db.io.run_once()?,
|
||||
@@ -316,7 +316,7 @@ fn test_statement_reset() -> anyhow::Result<()> {
|
||||
match stmt.step()? {
|
||||
StepResult::Row => {
|
||||
let row = stmt.row().unwrap();
|
||||
assert_eq!(row.get_value(0).to_value(), Value::Integer(1));
|
||||
assert_eq!(*row.get_value(0), limbo_core::OwnedValue::Integer(1));
|
||||
break;
|
||||
}
|
||||
StepResult::IO => tmp_db.io.run_once()?,
|
||||
@@ -366,10 +366,10 @@ fn test_wal_checkpoint() -> anyhow::Result<()> {
|
||||
match rows.step()? {
|
||||
StepResult::Row => {
|
||||
let row = rows.row().unwrap();
|
||||
let first_value = row.get_value(0).to_value();
|
||||
let first_value = row.get_value(0);
|
||||
let id = match first_value {
|
||||
Value::Integer(i) => i as i32,
|
||||
Value::Float(f) => f as i32,
|
||||
limbo_core::OwnedValue::Integer(i) => *i as i32,
|
||||
limbo_core::OwnedValue::Float(f) => *f as i32,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert_eq!(current_index, id as usize);
|
||||
@@ -430,13 +430,13 @@ fn test_wal_restart() -> anyhow::Result<()> {
|
||||
match rows.step()? {
|
||||
StepResult::Row => {
|
||||
let row = rows.row().unwrap();
|
||||
let first_value = row.get_value(0).to_value();
|
||||
let first_value = row.get_value(0);
|
||||
let count = match first_value {
|
||||
Value::Integer(i) => i,
|
||||
limbo_core::OwnedValue::Integer(i) => i,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
debug!("counted {}", count);
|
||||
return Ok(count as usize);
|
||||
return Ok(*count as usize);
|
||||
}
|
||||
StepResult::IO => {
|
||||
tmp_db.io.run_once()?;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::common::{do_flush, TempDatabase};
|
||||
use limbo_core::{Connection, LimboError, Result, StepResult, Value};
|
||||
use limbo_core::{Connection, LimboError, Result, StepResult};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
@@ -73,16 +73,15 @@ pub(crate) fn execute_and_get_ints(
|
||||
StepResult::Row => {
|
||||
let row = stmt.row().unwrap();
|
||||
for value in row.get_values() {
|
||||
let value = value.to_value();
|
||||
let out = match value {
|
||||
Value::Integer(i) => i,
|
||||
limbo_core::OwnedValue::Integer(i) => i,
|
||||
_ => {
|
||||
return Err(LimboError::ConversionError(format!(
|
||||
"cannot convert {value} to int"
|
||||
)))
|
||||
}
|
||||
};
|
||||
result.push(out);
|
||||
result.push(*out);
|
||||
}
|
||||
}
|
||||
StepResult::Done => break,
|
||||
|
||||
Reference in New Issue
Block a user