mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-18 09:04:19 +01:00
core: Move result row to ProgramState
Move result row to `ProgramState` to mimic what SQLite does where `Vdbe` struct has a `pResultRow` member. This makes it easier to deal with result lifetime, but more importantly, eventually lazily parse values at the edges of the API.
This commit is contained in:
@@ -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<Statement>,
|
||||
conn: &'conn mut LimboConn,
|
||||
cursor: Option<Row<'a>>,
|
||||
err: Option<LimboError>,
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -129,10 +129,12 @@ impl Cursor {
|
||||
pub fn fetchone(&mut self, py: Python) -> Result<Option<PyObject>> {
|
||||
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::<OperationalError, _>(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::<OperationalError, _>(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<PyObject> = 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),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
23
cli/app.rs
23
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<_>> = 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(' ');
|
||||
}
|
||||
|
||||
@@ -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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
17
core/lib.rs
17
core/lib.rs
@@ -468,7 +468,7 @@ impl Statement {
|
||||
self.state.interrupt();
|
||||
}
|
||||
|
||||
pub fn step(&mut self) -> Result<StepResult<'_>> {
|
||||
pub fn step(&mut self) -> Result<StepResult> {
|
||||
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<T: types::FromValue<'a> + 'a>(&self, idx: usize) -> Result<T> {
|
||||
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<String, Rc<function::ExternalFunc>>,
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
|
||||
115
core/types.rs
115
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<Value<'_>> 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<Self>
|
||||
fn from_value(value: &'a OwnedValue) -> Result<Self>
|
||||
where
|
||||
Self: Sized + 'a;
|
||||
}
|
||||
|
||||
impl<'a> FromValue<'a> for i64 {
|
||||
fn from_value(value: &Value<'a>) -> Result<Self> {
|
||||
fn from_value(value: &'a OwnedValue) -> Result<Self> {
|
||||
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<Self> {
|
||||
fn from_value(value: &'a OwnedValue) -> Result<Self> {
|
||||
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<Self> {
|
||||
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<Value<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Record<'a> {
|
||||
pub fn new(values: Vec<Value<'a>>) -> Self {
|
||||
Self { values }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct OwnedRecord {
|
||||
pub values: Vec<OwnedValue>,
|
||||
}
|
||||
|
||||
impl OwnedRecord {
|
||||
pub fn get<'a, T: FromValue<'a> + 'a>(&'a self, idx: usize) -> Result<T> {
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Vec<Option<Cursor>>>,
|
||||
registers: Vec<OwnedValue>,
|
||||
pub(crate) result_row: Option<OwnedRecord>,
|
||||
last_compare: Option<std::cmp::Ordering>,
|
||||
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<Pager>,
|
||||
) -> Result<StepResult<'a>> {
|
||||
pub fn step(&self, state: &mut ProgramState, pager: Rc<Pager>) -> Result<StepResult> {
|
||||
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<R: Rng>(cursor: &mut BTreeCursor, mut rng: R) -> Result<CursorR
|
||||
Ok(CursorResult::Ok(rowid.try_into().unwrap()))
|
||||
}
|
||||
|
||||
fn make_record<'a>(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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()),
|
||||
};
|
||||
|
||||
@@ -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<Option<limbo_core::Row<'a>>>,
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()),
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user