diff --git a/bindings/go/rs_src/rows.rs b/bindings/go/rs_src/rows.rs index b6aade3c6..714c73e23 100644 --- a/bindings/go/rs_src/rows.rs +++ b/bindings/go/rs_src/rows.rs @@ -2,21 +2,19 @@ use crate::{ types::{LimboValue, ResultCode}, LimboConn, }; -use limbo_core::{LimboError, Row, Statement, StepResult}; +use limbo_core::{LimboError, Statement, StepResult}; use std::ffi::{c_char, c_void}; -pub struct LimboRows<'conn, 'a> { +pub struct LimboRows<'conn> { stmt: Box, conn: &'conn mut LimboConn, - cursor: Option>, err: Option, } -impl<'conn, 'a> LimboRows<'conn, 'a> { +impl<'conn> LimboRows<'conn> { pub fn new(stmt: Statement, conn: &'conn mut LimboConn) -> Self { LimboRows { stmt: Box::new(stmt), - cursor: None, conn, err: None, } @@ -27,7 +25,7 @@ impl<'conn, 'a> LimboRows<'conn, 'a> { Box::into_raw(Box::new(self)) as *mut c_void } - pub fn from_ptr(ptr: *mut c_void) -> &'conn mut LimboRows<'conn, 'a> { + pub fn from_ptr(ptr: *mut c_void) -> &'conn mut LimboRows<'conn> { if ptr.is_null() { panic!("Null pointer"); } @@ -54,10 +52,7 @@ pub extern "C" fn rows_next(ctx: *mut c_void) -> ResultCode { let ctx = LimboRows::from_ptr(ctx); match ctx.stmt.step() { - Ok(StepResult::Row(row)) => { - ctx.cursor = Some(row); - ResultCode::Row - } + Ok(StepResult::Row) => ResultCode::Row, Ok(StepResult::Done) => ResultCode::Done, Ok(StepResult::IO) => { let _ = ctx.conn.io.run_once(); @@ -79,9 +74,10 @@ pub extern "C" fn rows_get_value(ctx: *mut c_void, col_idx: usize) -> *const c_v } let ctx = LimboRows::from_ptr(ctx); - if let Some(ref cursor) = ctx.cursor { - if let Some(value) = cursor.values.get(col_idx) { - return LimboValue::from_value(value).to_ptr(); + if let Some(row) = ctx.stmt.row() { + if let Some(value) = row.values.get(col_idx) { + let value = value.to_value(); + return LimboValue::from_value(&value).to_ptr(); } } std::ptr::null() @@ -142,7 +138,6 @@ pub extern "C" fn rows_close(ctx: *mut c_void) { if !ctx.is_null() { let rows = LimboRows::from_ptr(ctx); rows.stmt.reset(); - rows.cursor = None; rows.err = None; } unsafe { diff --git a/bindings/go/rs_src/statement.rs b/bindings/go/rs_src/statement.rs index 897c246fa..e8cec0618 100644 --- a/bindings/go/rs_src/statement.rs +++ b/bindings/go/rs_src/statement.rs @@ -50,7 +50,7 @@ pub extern "C" fn stmt_execute( } loop { match statement.step() { - Ok(StepResult::Row(_)) => { + Ok(StepResult::Row) => { // unexpected row during execution, error out. return ResultCode::Error; } diff --git a/bindings/java/rs_src/limbo_statement.rs b/bindings/java/rs_src/limbo_statement.rs index aed8e7d99..97328a964 100644 --- a/bindings/java/rs_src/limbo_statement.rs +++ b/bindings/java/rs_src/limbo_statement.rs @@ -63,14 +63,15 @@ pub extern "system" fn Java_org_github_tursodatabase_core_LimboStatement_step<'l }; match step_result { - StepResult::Row(row) => { + StepResult::Row => { + let row = stmt.stmt.row().unwrap(); return match row_to_obj_array(&mut env, &row) { Ok(row) => to_limbo_step_result(&mut env, STEP_RESULT_ID_ROW, Some(row)), Err(e) => { set_err_msg_and_throw_exception(&mut env, obj, LIMBO_ETC, e.to_string()); to_limbo_step_result(&mut env, STEP_RESULT_ID_ERROR, None) } - } + }; } StepResult::IO => { if let Err(e) = stmt.connection.io.run_once() { @@ -104,13 +105,14 @@ fn row_to_obj_array<'local>( env.new_object_array(row.values.len() as i32, "java/lang/Object", JObject::null())?; for (i, value) in row.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)])? + 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)])? + 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(), diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index 16afc5494..fb8189c2d 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -129,10 +129,12 @@ impl Cursor { pub fn fetchone(&mut self, py: Python) -> Result> { if let Some(smt) = &self.smt { loop { - match smt.borrow_mut().step().map_err(|e| { + let mut stmt = smt.borrow_mut(); + match stmt.step().map_err(|e| { PyErr::new::(format!("Step error: {:?}", e)) })? { - limbo_core::StepResult::Row(row) => { + limbo_core::StepResult::Row => { + let row = stmt.row().unwrap(); let py_row = row_to_py(py, &row); return Ok(Some(py_row)); } @@ -163,10 +165,12 @@ impl Cursor { let mut results = Vec::new(); if let Some(smt) = &self.smt { loop { - match smt.borrow_mut().step().map_err(|e| { + let mut stmt = smt.borrow_mut(); + match stmt.step().map_err(|e| { PyErr::new::(format!("Step error: {:?}", e)) })? { - limbo_core::StepResult::Row(row) => { + limbo_core::StepResult::Row => { + let row = stmt.row().unwrap(); let py_row = row_to_py(py, &row); results.push(py_row); } @@ -298,7 +302,7 @@ fn row_to_py(py: Python, row: &limbo_core::Row) -> PyObject { let py_values: Vec = row .values .iter() - .map(|value| match value { + .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), diff --git a/bindings/wasm/lib.rs b/bindings/wasm/lib.rs index 9b34fcb57..26f877199 100644 --- a/bindings/wasm/lib.rs +++ b/bindings/wasm/lib.rs @@ -71,10 +71,13 @@ impl RowIterator { #[wasm_bindgen] pub fn next(&mut self) -> JsValue { - match self.inner.borrow_mut().step() { - Ok(limbo_core::StepResult::Row(row)) => { + let mut stmt = self.inner.borrow_mut(); + match stmt.step() { + Ok(limbo_core::StepResult::Row) => { + let row = stmt.row().unwrap(); let row_array = Array::new(); - for value in row.values { + for value in &row.values { + let value = value.to_value(); let value = to_js_value(value); row_array.push(&value); } @@ -109,10 +112,13 @@ impl Statement { } pub fn get(&self) -> JsValue { - match self.inner.borrow_mut().step() { - Ok(limbo_core::StepResult::Row(row)) => { + let mut stmt = self.inner.borrow_mut(); + match stmt.step() { + Ok(limbo_core::StepResult::Row) => { + let row = stmt.row().unwrap(); let row_array = js_sys::Array::new(); - for value in row.values { + for value in &row.values { + let value = value.to_value(); let value = to_js_value(value); row_array.push(&value); } @@ -129,10 +135,13 @@ impl Statement { pub fn all(&self) -> js_sys::Array { let array = js_sys::Array::new(); loop { - match self.inner.borrow_mut().step() { - Ok(limbo_core::StepResult::Row(row)) => { + let mut stmt = self.inner.borrow_mut(); + match stmt.step() { + Ok(limbo_core::StepResult::Row) => { + let row = stmt.row().unwrap(); let row_array = js_sys::Array::new(); - for value in row.values { + for value in &row.values { + let value = value.to_value(); let value = to_js_value(value); row_array.push(&value); } diff --git a/cli/app.rs b/cli/app.rs index 278bbca43..7d812d7fe 100644 --- a/cli/app.rs +++ b/cli/app.rs @@ -625,8 +625,10 @@ impl Limbo { } match rows.step() { - Ok(StepResult::Row(row)) => { + Ok(StepResult::Row) => { + let row = rows.row().unwrap(); for (i, value) in row.values.iter().enumerate() { + let value = value.to_value(); if i > 0 { let _ = self.writer.write(b"|"); } @@ -670,11 +672,12 @@ impl Limbo { let mut table_rows: Vec> = vec![]; loop { match rows.step() { - Ok(StepResult::Row(row)) => { + Ok(StepResult::Row) => { + let row = rows.row().unwrap(); table_rows.push( row.values .iter() - .map(|value| match value { + .map(|value| match value.to_value() { Value::Null => self.opts.null_value.clone().cell(), Value::Integer(i) => i.to_string().cell(), Value::Float(f) => f.to_string().cell(), @@ -740,8 +743,11 @@ impl Limbo { let mut found = false; loop { match rows.step()? { - StepResult::Row(row) => { - if let Some(Value::Text(schema)) = row.values.first() { + StepResult::Row => { + let row = rows.row().unwrap(); + if let Some(Value::Text(schema)) = + row.values.first().map(|v| v.to_value()) + { let _ = self.write_fmt(format_args!("{};", schema)); found = true; } @@ -797,8 +803,11 @@ impl Limbo { let mut tables = String::new(); loop { match rows.step()? { - StepResult::Row(row) => { - if let Some(Value::Text(table)) = row.values.first() { + StepResult::Row => { + let row = rows.row().unwrap(); + if let Some(Value::Text(table)) = + row.values.first().map(|v| v.to_value()) + { tables.push_str(table); tables.push(' '); } diff --git a/cli/import.rs b/cli/import.rs index b7557d58b..a643713ea 100644 --- a/cli/import.rs +++ b/cli/import.rs @@ -107,7 +107,7 @@ impl<'a> ImportFile<'a> { self.writer.write_all("database is busy\n".as_bytes()); break; } - limbo_core::StepResult::Row(_) => todo!(), + limbo_core::StepResult::Row => todo!(), } } } diff --git a/core/benches/benchmark.rs b/core/benches/benchmark.rs index f25d48144..21b75424d 100644 --- a/core/benches/benchmark.rs +++ b/core/benches/benchmark.rs @@ -61,9 +61,7 @@ fn bench(criterion: &mut Criterion) { b.iter(|| { loop { match stmt.step().unwrap() { - limbo_core::StepResult::Row(row) => { - black_box(row); - } + limbo_core::StepResult::Row => {} limbo_core::StepResult::IO => { let _ = io.run_once(); } @@ -107,9 +105,7 @@ fn bench(criterion: &mut Criterion) { b.iter(|| { loop { match stmt.step().unwrap() { - limbo_core::StepResult::Row(row) => { - black_box(row); - } + limbo_core::StepResult::Row => {} limbo_core::StepResult::IO => { let _ = io.run_once(); } diff --git a/core/lib.rs b/core/lib.rs index 2790c2ba2..98e37d470 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -468,7 +468,7 @@ impl Statement { self.state.interrupt(); } - pub fn step(&mut self) -> Result> { + pub fn step(&mut self) -> Result { self.program.step(&mut self.state, self.pager.clone()) } @@ -495,19 +495,16 @@ impl Statement { pub fn reset(&mut self) { self.state.reset(); } -} -pub type StepResult<'a> = vdbe::StepResult<'a>; - -pub type Row<'a> = types::Record<'a>; - -impl<'a> Row<'a> { - pub fn get + 'a>(&self, idx: usize) -> Result { - let value = &self.values[idx]; - T::from_value(value) + pub fn row(&self) -> Option<&Row> { + self.state.result_row.as_ref() } } +pub type Row = types::OwnedRecord; + +pub type StepResult = vdbe::StepResult; + pub(crate) struct SymbolTable { pub functions: HashMap>, #[cfg(not(target_family = "wasm"))] diff --git a/core/types.rs b/core/types.rs index 5b4ceed1c..340806e25 100644 --- a/core/types.rs +++ b/core/types.rs @@ -123,6 +123,46 @@ 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.value), + 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)] @@ -442,94 +482,55 @@ impl From> for OwnedValue { } } -pub fn to_value(value: &OwnedValue) -> Value<'_> { - match value { - OwnedValue::Null => Value::Null, - OwnedValue::Integer(i) => Value::Integer(*i), - OwnedValue::Float(f) => Value::Float(*f), - OwnedValue::Text(s) => Value::Text(&s.value), - 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) => to_value(count), - AggContext::Max(max) => match max { - Some(max) => to_value(max), - None => Value::Null, - }, - AggContext::Min(min) => match min { - Some(min) => to_value(min), - None => Value::Null, - }, - AggContext::GroupConcat(s) => to_value(s), - AggContext::External(ext_state) => to_value( - ext_state - .finalized_value - .as_ref() - .unwrap_or(&OwnedValue::Null), - ), - }, - OwnedValue::Record(_) => todo!(), - } -} - pub trait FromValue<'a> { - fn from_value(value: &Value<'a>) -> Result + fn from_value(value: &'a OwnedValue) -> Result where Self: Sized + 'a; } impl<'a> FromValue<'a> for i64 { - fn from_value(value: &Value<'a>) -> Result { + fn from_value(value: &'a OwnedValue) -> Result { match value { - Value::Integer(i) => Ok(*i), + OwnedValue::Integer(i) => Ok(*i), _ => Err(LimboError::ConversionError("Expected integer value".into())), } } } impl<'a> FromValue<'a> for String { - fn from_value(value: &Value<'a>) -> Result { + fn from_value(value: &'a OwnedValue) -> Result { match value { - Value::Text(s) => Ok(s.to_string()), + OwnedValue::Text(s) => Ok(s.value.to_string()), _ => Err(LimboError::ConversionError("Expected text value".into())), } } } impl<'a> FromValue<'a> for &'a str { - fn from_value(value: &Value<'a>) -> Result<&'a str> { + fn from_value(value: &'a OwnedValue) -> Result { match value { - Value::Text(s) => Ok(s), + OwnedValue::Text(s) => Ok(s.value.as_str()), _ => Err(LimboError::ConversionError("Expected text value".into())), } } } -#[derive(Debug)] -pub struct Record<'a> { - pub values: Vec>, -} - -impl<'a> Record<'a> { - pub fn new(values: Vec>) -> Self { - Self { values } - } -} - #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct OwnedRecord { pub values: Vec, } +impl OwnedRecord { + pub fn get<'a, T: FromValue<'a> + 'a>(&'a self, idx: usize) -> Result { + let value = &self.values[idx]; + T::from_value(value) + } + + pub fn count(&self) -> usize { + self.values.len() + } +} + const I8_LOW: i64 = -128; const I8_HIGH: i64 = 127; const I16_LOW: i64 = -32768; diff --git a/core/util.rs b/core/util.rs index c81747887..c92c31412 100644 --- a/core/util.rs +++ b/core/util.rs @@ -34,7 +34,8 @@ pub fn parse_schema_rows( let mut automatic_indexes = Vec::new(); loop { match rows.step()? { - StepResult::Row(row) => { + StepResult::Row => { + let row = rows.row().unwrap(); let ty = row.get::<&str>(0)?; if ty != "table" && ty != "index" { continue; diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index f788fe682..b5f205e59 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -37,8 +37,7 @@ use crate::storage::wal::CheckpointResult; use crate::storage::{btree::BTreeCursor, pager::Pager}; use crate::translate::plan::{ResultSetColumn, TableReference}; use crate::types::{ - AggContext, Cursor, CursorResult, ExternalAggState, OwnedRecord, OwnedValue, Record, SeekKey, - SeekOp, + AggContext, Cursor, CursorResult, ExternalAggState, OwnedRecord, OwnedValue, SeekKey, SeekOp, }; use crate::util::parse_schema_rows; use crate::vdbe::builder::CursorType; @@ -143,10 +142,10 @@ pub type PageIdx = usize; type InsnReference = u32; #[derive(Debug)] -pub enum StepResult<'a> { +pub enum StepResult { Done, IO, - Row(Record<'a>), + Row, Interrupt, Busy, } @@ -299,6 +298,7 @@ pub struct ProgramState { pub pc: InsnReference, cursors: RefCell>>, registers: Vec, + pub(crate) result_row: Option, last_compare: Option, deferred_seek: Option<(CursorID, CursorID)>, ended_coroutine: Bitfield<4>, // flag to indicate that a coroutine has ended (key is the yield register. currently we assume that the yield register is always between 0-255, YOLO) @@ -316,6 +316,7 @@ impl ProgramState { pc: 0, cursors, registers, + result_row: None, last_compare: None, deferred_seek: None, ended_coroutine: Bitfield::new(), @@ -412,11 +413,7 @@ impl Program { } } - pub fn step<'a>( - &self, - state: &'a mut ProgramState, - pager: Rc, - ) -> Result> { + pub fn step(&self, state: &mut ProgramState, pager: Rc) -> Result { loop { if state.is_interrupted() { return Ok(StepResult::Interrupt); @@ -963,9 +960,10 @@ impl Program { state.pc += 1; } Insn::ResultRow { start_reg, count } => { - let record = make_record(&state.registers, start_reg, count); + let record = make_owned_record(&state.registers, start_reg, count); + state.result_row = Some(record); state.pc += 1; - return Ok(StepResult::Row(record)); + return Ok(StepResult::Row); } Insn::NextAsync { cursor_id } => { let mut cursors = state.cursors.borrow_mut(); @@ -2654,14 +2652,6 @@ fn get_new_rowid(cursor: &mut BTreeCursor, mut rng: R) -> Result(registers: &'a [OwnedValue], start_reg: &usize, count: &usize) -> Record<'a> { - let mut values = Vec::with_capacity(*count); - for r in registers.iter().skip(*start_reg).take(*count) { - values.push(crate::types::to_value(r)) - } - Record::new(values) -} - fn make_owned_record(registers: &[OwnedValue], start_reg: &usize, count: &usize) -> OwnedRecord { let mut values = Vec::with_capacity(*count); for r in registers.iter().skip(*start_reg).take(*count) { diff --git a/perf/latency/limbo/src/main.rs b/perf/latency/limbo/src/main.rs index a7302e38a..834c12225 100644 --- a/perf/latency/limbo/src/main.rs +++ b/perf/latency/limbo/src/main.rs @@ -39,6 +39,7 @@ fn main() { let row = rows.step().unwrap(); match row { limbo_core::StepResult::Row(_) => { + let row = statement.row(); count += 1; } limbo_core::StepResult::IO => yield, diff --git a/simulator/generation/plan.rs b/simulator/generation/plan.rs index 2a794fbbc..b0c61488d 100644 --- a/simulator/generation/plan.rs +++ b/simulator/generation/plan.rs @@ -416,13 +416,15 @@ impl Interaction { let mut out = Vec::new(); while let Ok(row) = rows.step() { match row { - StepResult::Row(row) => { + StepResult::Row => { + let row = rows.row().unwrap(); let mut r = Vec::new(); for el in &row.values { - let v = match el { + let v = el.to_value(); + 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::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()), }; diff --git a/sqlite3/src/lib.rs b/sqlite3/src/lib.rs index 66d53b349..440715849 100644 --- a/sqlite3/src/lib.rs +++ b/sqlite3/src/lib.rs @@ -2,7 +2,6 @@ #![allow(non_camel_case_types)] use log::trace; -use std::cell::RefCell; use std::ffi::{self, CStr, CString}; use std::rc::Rc; @@ -63,15 +62,13 @@ impl sqlite3 { } } -pub struct sqlite3_stmt<'a> { +pub struct sqlite3_stmt { pub(crate) stmt: limbo_core::Statement, - pub(crate) row: RefCell>>, } -impl sqlite3_stmt<'_> { +impl sqlite3_stmt { pub fn new(stmt: limbo_core::Statement) -> Self { - let row = RefCell::new(None); - Self { stmt, row } + Self { stmt } } } @@ -242,10 +239,7 @@ pub unsafe extern "C" fn sqlite3_step(stmt: *mut sqlite3_stmt) -> ffi::c_int { limbo_core::StepResult::IO => SQLITE_BUSY, limbo_core::StepResult::Done => SQLITE_DONE, limbo_core::StepResult::Interrupt => SQLITE_INTERRUPT, - limbo_core::StepResult::Row(row) => { - stmt.row.replace(Some(row)); - SQLITE_ROW - } + limbo_core::StepResult::Row => SQLITE_ROW, limbo_core::StepResult::Busy => SQLITE_BUSY, } } else { @@ -288,7 +282,7 @@ pub unsafe extern "C" fn sqlite3_exec( #[no_mangle] pub unsafe extern "C" fn sqlite3_reset(stmt: *mut sqlite3_stmt) -> ffi::c_int { let stmt = &mut *stmt; - stmt.row.replace(None); + stmt.stmt.reset(); SQLITE_OK } @@ -447,11 +441,7 @@ pub unsafe extern "C" fn sqlite3_expanded_sql(_stmt: *mut sqlite3_stmt) -> *mut #[no_mangle] pub unsafe extern "C" fn sqlite3_data_count(stmt: *mut sqlite3_stmt) -> ffi::c_int { let stmt = &*stmt; - let row = stmt.row.borrow(); - let row = match row.as_ref() { - Some(row) => row, - None => return 0, - }; + let row = stmt.stmt.row().unwrap(); row.values.len() as ffi::c_int } @@ -640,12 +630,12 @@ pub unsafe extern "C" fn sqlite3_column_text( idx: ffi::c_int, ) -> *const ffi::c_uchar { let stmt = &mut *stmt; - let row = stmt.row.borrow(); + let row = stmt.stmt.row(); let row = match row.as_ref() { Some(row) => row, None => return std::ptr::null(), }; - match row.values.get(idx as usize) { + match row.values.get(idx as usize).map(|v| v.to_value()) { Some(limbo_core::Value::Text(text)) => text.as_bytes().as_ptr(), _ => std::ptr::null(), } diff --git a/tests/integration/functions/test_function_rowid.rs b/tests/integration/functions/test_function_rowid.rs index 03edf4c44..e3f5124df 100644 --- a/tests/integration/functions/test_function_rowid.rs +++ b/tests/integration/functions/test_function_rowid.rs @@ -28,8 +28,9 @@ fn test_last_insert_rowid_basic() -> anyhow::Result<()> { if let Some(ref mut rows) = select_query { loop { match rows.step()? { - StepResult::Row(row) => { - if let Value::Integer(id) = row.values[0] { + StepResult::Row => { + let row = rows.row().unwrap(); + if let Value::Integer(id) = row.values[0].to_value() { assert_eq!(id, 1, "First insert should have rowid 1"); } } @@ -63,8 +64,9 @@ fn test_last_insert_rowid_basic() -> anyhow::Result<()> { match conn.query("SELECT last_insert_rowid()") { Ok(Some(ref mut rows)) => loop { match rows.step()? { - StepResult::Row(row) => { - if let Value::Integer(id) = row.values[0] { + StepResult::Row => { + let row = rows.row().unwrap(); + if let Value::Integer(id) = row.values[0].to_value() { last_id = id; } } @@ -108,8 +110,9 @@ fn test_integer_primary_key() -> anyhow::Result<()> { let mut select_query = conn.query("SELECT * FROM test_rowid")?.unwrap(); loop { match select_query.step()? { - StepResult::Row(row) => { - if let Value::Integer(id) = row.values[0] { + StepResult::Row => { + let row = select_query.row().unwrap(); + if let Value::Integer(id) = row.values[0].to_value() { rowids.push(id); } } diff --git a/tests/integration/fuzz/mod.rs b/tests/integration/fuzz/mod.rs index 9cab41964..8179dfde4 100644 --- a/tests/integration/fuzz/mod.rs +++ b/tests/integration/fuzz/mod.rs @@ -48,17 +48,20 @@ mod tests { let result = stmt.step().unwrap(); let row = loop { match result { - limbo_core::StepResult::Row(row) => break row, + limbo_core::StepResult::Row => { + let row = stmt.row().unwrap(); + break row; + } limbo_core::StepResult::IO => continue, r => panic!("unexpected result {:?}: expecting single row", r), } }; row.values .iter() - .map(|x| match x { + .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::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()), }) diff --git a/tests/integration/query_processing/test_read_path.rs b/tests/integration/query_processing/test_read_path.rs index 16feedfb2..82c29539e 100644 --- a/tests/integration/query_processing/test_read_path.rs +++ b/tests/integration/query_processing/test_read_path.rs @@ -13,8 +13,9 @@ fn test_statement_reset_bind() -> anyhow::Result<()> { loop { match stmt.step()? { - StepResult::Row(row) => { - assert_eq!(row.values[0], Value::Integer(1)); + StepResult::Row => { + let row = stmt.row().unwrap(); + assert_eq!(row.values[0].to_value(), Value::Integer(1)); } StepResult::IO => tmp_db.io.run_once()?, _ => break, @@ -27,8 +28,9 @@ fn test_statement_reset_bind() -> anyhow::Result<()> { loop { match stmt.step()? { - StepResult::Row(row) => { - assert_eq!(row.values[0], Value::Integer(2)); + StepResult::Row => { + let row = stmt.row().unwrap(); + assert_eq!(row.values[0].to_value(), Value::Integer(2)); } StepResult::IO => tmp_db.io.run_once()?, _ => break, @@ -59,24 +61,25 @@ fn test_statement_bind() -> anyhow::Result<()> { loop { match stmt.step()? { - StepResult::Row(row) => { - if let Value::Text(s) = row.values[0] { + StepResult::Row => { + let row = stmt.row().unwrap(); + if let Value::Text(s) = row.values[0].to_value() { assert_eq!(s, "hello") } - if let Value::Text(s) = row.values[1] { + if let Value::Text(s) = row.values[1].to_value() { assert_eq!(s, "hello") } - if let Value::Integer(i) = row.values[2] { + if let Value::Integer(i) = row.values[2].to_value() { assert_eq!(i, 42) } - if let Value::Blob(v) = row.values[3] { + if let Value::Blob(v) = row.values[3].to_value() { assert_eq!(v, &vec![0x1 as u8, 0x2, 0x3]) } - if let Value::Float(f) = row.values[4] { + if let Value::Float(f) = row.values[4].to_value() { assert_eq!(f, 0.5) } } diff --git a/tests/integration/query_processing/test_write_path.rs b/tests/integration/query_processing/test_write_path.rs index 4508ecbad..1f05d714f 100644 --- a/tests/integration/query_processing/test_write_path.rs +++ b/tests/integration/query_processing/test_write_path.rs @@ -41,16 +41,17 @@ fn test_simple_overflow_page() -> anyhow::Result<()> { match conn.query(list_query) { Ok(Some(ref mut rows)) => loop { match rows.step()? { - StepResult::Row(row) => { - let first_value = &row.values[0]; - let text = &row.values[1]; + StepResult::Row => { + let row = rows.row().unwrap(); + let first_value = row.values[0].to_value(); + let text = row.values[1].to_value(); let id = match first_value { - Value::Integer(i) => *i as i32, - Value::Float(f) => *f as i32, + Value::Integer(i) => i as i32, + Value::Float(f) => f as i32, _ => unreachable!(), }; let text = match text { - Value::Text(t) => *t, + Value::Text(t) => t, _ => unreachable!(), }; assert_eq!(1, id); @@ -115,16 +116,17 @@ fn test_sequential_overflow_page() -> anyhow::Result<()> { match conn.query(list_query) { Ok(Some(ref mut rows)) => loop { match rows.step()? { - StepResult::Row(row) => { - let first_value = &row.values[0]; - let text = &row.values[1]; + StepResult::Row => { + let row = rows.row().unwrap(); + let first_value = row.values[0].to_value(); + let text = row.values[1].to_value(); let id = match first_value { - Value::Integer(i) => *i as i32, - Value::Float(f) => *f as i32, + Value::Integer(i) => i as i32, + Value::Float(f) => f as i32, _ => unreachable!(), }; let text = match text { - Value::Text(t) => *t, + Value::Text(t) => t, _ => unreachable!(), }; let huge_text = &huge_texts[current_index]; @@ -186,11 +188,12 @@ fn test_sequential_write() -> anyhow::Result<()> { match conn.query(list_query) { Ok(Some(ref mut rows)) => loop { match rows.step()? { - StepResult::Row(row) => { + StepResult::Row => { + let row = rows.row().unwrap(); let first_value = row.values.first().expect("missing id"); - let id = match first_value { - Value::Integer(i) => *i as i32, - Value::Float(f) => *f as i32, + let id = match first_value.to_value() { + Value::Integer(i) => i as i32, + Value::Float(f) => f as i32, _ => unreachable!(), }; assert_eq!(current_read_index, id); @@ -251,10 +254,11 @@ fn test_regression_multi_row_insert() -> anyhow::Result<()> { match conn.query(list_query) { Ok(Some(ref mut rows)) => loop { match rows.step()? { - StepResult::Row(row) => { + StepResult::Row => { + let row = rows.row().unwrap(); let first_value = row.values.first().expect("missing id"); - let id = match first_value { - Value::Float(f) => *f as i32, + let id = match first_value.to_value() { + Value::Float(f) => f as i32, _ => panic!("expected float"), }; actual_ids.push(id); @@ -296,8 +300,9 @@ fn test_statement_reset() -> anyhow::Result<()> { loop { match stmt.step()? { - StepResult::Row(row) => { - assert_eq!(row.values[0], Value::Integer(1)); + StepResult::Row => { + let row = stmt.row().unwrap(); + assert_eq!(row.values[0].to_value(), Value::Integer(1)); break; } StepResult::IO => tmp_db.io.run_once()?, @@ -309,8 +314,9 @@ fn test_statement_reset() -> anyhow::Result<()> { loop { match stmt.step()? { - StepResult::Row(row) => { - assert_eq!(row.values[0], Value::Integer(1)); + StepResult::Row => { + let row = stmt.row().unwrap(); + assert_eq!(row.values[0].to_value(), Value::Integer(1)); break; } StepResult::IO => tmp_db.io.run_once()?, @@ -358,11 +364,12 @@ fn test_wal_checkpoint() -> anyhow::Result<()> { match conn.query(list_query) { Ok(Some(ref mut rows)) => loop { match rows.step()? { - StepResult::Row(row) => { - let first_value = &row.values[0]; + StepResult::Row => { + let row = rows.row().unwrap(); + let first_value = row.values[0].to_value(); let id = match first_value { - Value::Integer(i) => *i as i32, - Value::Float(f) => *f as i32, + Value::Integer(i) => i as i32, + Value::Float(f) => f as i32, _ => unreachable!(), }; assert_eq!(current_index, id as usize); @@ -421,10 +428,11 @@ fn test_wal_restart() -> anyhow::Result<()> { if let Some(ref mut rows) = conn.query(list_query)? { loop { match rows.step()? { - StepResult::Row(row) => { - let first_value = &row.values[0]; + StepResult::Row => { + let row = rows.row().unwrap(); + let first_value = row.values[0].to_value(); let count = match first_value { - Value::Integer(i) => *i as i32, + Value::Integer(i) => i, _ => unreachable!(), }; debug!("counted {}", count); diff --git a/tests/integration/wal/test_wal.rs b/tests/integration/wal/test_wal.rs index fa320ae8f..994c5d529 100644 --- a/tests/integration/wal/test_wal.rs +++ b/tests/integration/wal/test_wal.rs @@ -39,9 +39,11 @@ pub(crate) fn execute_and_get_strings( let stmt = Rc::new(RefCell::new(statement)); let mut result = Vec::new(); - while let Ok(step_result) = stmt.borrow_mut().step() { + let mut stmt = stmt.borrow_mut(); + while let Ok(step_result) = stmt.step() { match step_result { - StepResult::Row(row) => { + StepResult::Row => { + let row = stmt.row().unwrap(); for el in &row.values { result.push(format!("{el}")); } @@ -65,12 +67,15 @@ pub(crate) fn execute_and_get_ints( let stmt = Rc::new(RefCell::new(statement)); let mut result = Vec::new(); - while let Ok(step_result) = stmt.borrow_mut().step() { + let mut stmt = stmt.borrow_mut(); + while let Ok(step_result) = stmt.step() { match step_result { - StepResult::Row(row) => { + StepResult::Row => { + let row = stmt.row().unwrap(); for value in &row.values { + let value = value.to_value(); let out = match value { - Value::Integer(i) => *i, + Value::Integer(i) => i, _ => { return Err(LimboError::ConversionError(format!( "cannot convert {value} to int"