Skip to content

Commit dd735fd

Browse files
committed
add resultset and finalise statements
- ResultSet as a GC-safe, enumerable hybrid result set. ...exposed changes() for same reason - fix ruby exception maps
1 parent 21c3991 commit dd735fd

File tree

4 files changed

+103
-22
lines changed

4 files changed

+103
-22
lines changed

bindings/ruby/ext/turso/src/connection.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
use magnus::value::ReprValue;
12
use magnus::{block::Proc, method, Error, Module, Ruby, TryConvert, Value};
23
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
34
use std::sync::Arc;
45
use turso_sdk_kit::rsapi::{TursoConnection, TursoStatusCode};
56

67
use crate::errors::map_turso_error;
7-
use crate::statement::{RbExecutionResult, RbStatement};
8+
use crate::statement::{RbResultSet, RbStatement};
89
use crate::value;
910

1011
#[magnus::wrap(class = "Turso::Connection", free_immediately, size)]
@@ -41,7 +42,7 @@ impl RbConnection {
4142
Ok(RbStatement::new(stmt))
4243
}
4344

44-
pub fn execute(&self, args: &[Value]) -> Result<RbExecutionResult, Error> {
45+
pub fn execute(&self, args: &[Value]) -> Result<RbResultSet, Error> {
4546
self.ensure_open()?;
4647

4748
if args.is_empty() {
@@ -59,8 +60,25 @@ impl RbConnection {
5960
value::bind_value(&mut stmt, i + 1, *arg)?;
6061
}
6162

62-
let result = stmt.execute().map_err(map_turso_error)?;
63-
Ok(RbExecutionResult::from_rsapi(result.rows_changed))
63+
let mut rows: Vec<Value> = Vec::new();
64+
loop {
65+
match stmt.step().map_err(map_turso_error)? {
66+
TursoStatusCode::Row => {
67+
rows.push(value::extract_row(&stmt)?.as_value());
68+
}
69+
TursoStatusCode::Done => break,
70+
TursoStatusCode::Io => {
71+
stmt.run_io().map_err(map_turso_error)?;
72+
}
73+
_ => break,
74+
}
75+
}
76+
77+
let changes = self.inner.changes();
78+
stmt.finalize().map_err(map_turso_error)?;
79+
self.last_changes.store(changes, Ordering::Relaxed);
80+
81+
Ok(RbResultSet::new(rows, changes))
6482
}
6583

6684
pub fn transaction(&self, block: Proc) -> Result<Value, Error> {

bindings/ruby/ext/turso/src/errors.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
use magnus::{exception::ExceptionClass, Error, Module, Ruby};
2-
use std::cell::RefCell;
1+
use magnus::{exception::ExceptionClass, Class, Error, Module, Ruby};
2+
33
use turso_sdk_kit::rsapi::{TursoError, TursoStatusCode};
44

55
pub fn define_exceptions(ruby: &Ruby, module: &impl Module) -> Result<(), Error> {
6-
let error = module.define_class("Error", ruby.exception_standard_error())?;
6+
let error = module.define_class("Error", ruby.exception_standard_error().as_r_class())?;
77
module.define_class("BusyError", error)?;
88
module.define_class("InterruptError", error)?;
99
module.define_class("MisuseError", error)?;

bindings/ruby/ext/turso/src/statement.rs

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,59 @@
1-
use magnus::prelude::*;
2-
use magnus::{method, Error, Module, RArray, Ruby, Value};
1+
use magnus::value::ReprValue;
2+
use magnus::{method, DataTypeFunctions, Error, Module, Ruby, TryConvert, TypedData, Value};
33
use std::cell::RefCell;
44
use std::sync::atomic::{AtomicBool, Ordering};
55
use turso_sdk_kit::rsapi::{TursoStatement, TursoStatusCode};
66

77
use crate::errors::{map_turso_error, statement_closed_error};
88
use crate::value;
99

10-
#[magnus::wrap(class = "Turso::ExecutionResult", free_immediately, size)]
11-
pub struct RbExecutionResult {
10+
/// ResultSet containing query results and execution metadata.
11+
/// Implements `Enumerable`, so you can use `.each`, `.map`, `.select`, etc.
12+
#[derive(TypedData)]
13+
#[magnus(class = "Turso::ResultSet", mark, size)]
14+
pub struct RbResultSet {
15+
rows: Vec<Value>,
1216
rows_changed: u64,
1317
}
1418

15-
impl RbExecutionResult {
16-
pub(crate) fn from_rsapi(rows_changed: u64) -> Self {
17-
Self { rows_changed }
19+
20+
unsafe impl Send for RbResultSet {}
21+
unsafe impl Sync for RbResultSet {}
22+
23+
impl RbResultSet {
24+
pub(crate) fn new(rows: Vec<Value>, rows_changed: u64) -> Self {
25+
Self { rows, rows_changed }
1826
}
1927

2028
pub fn rows_changed(&self) -> u64 {
2129
self.rows_changed
2230
}
31+
32+
pub fn each(
33+
ruby: &Ruby,
34+
rb_self: Value,
35+
) -> Result<magnus::block::Yield<impl Iterator<Item = Value>>, Error> {
36+
let this: &Self = TryConvert::try_convert(rb_self)?;
37+
if ruby.block_given() {
38+
Ok(magnus::block::Yield::Iter(this.rows.iter().copied()))
39+
} else {
40+
Ok(magnus::block::Yield::Enumerator(
41+
rb_self.enumeratorize("each", ()),
42+
))
43+
}
44+
}
45+
}
46+
47+
impl DataTypeFunctions for RbResultSet {
48+
fn mark(&self, marker: &magnus::gc::Marker) {
49+
for row in &self.rows {
50+
marker.mark(*row);
51+
}
52+
}
53+
54+
fn size(&self) -> usize {
55+
std::mem::size_of::<Self>() + self.rows.len() * std::mem::size_of::<Value>()
56+
}
2357
}
2458

2559
#[magnus::wrap(class = "Turso::Statement", free_immediately, size)]
@@ -68,17 +102,31 @@ impl RbStatement {
68102
}
69103
}
70104

71-
pub fn execute(&self, args: &[Value]) -> Result<RbExecutionResult, Error> {
105+
pub fn execute(&self, args: &[Value]) -> Result<RbResultSet, Error> {
72106
self.ensure_open()?;
73107
self.bind(args)?;
74108

75-
let result = self.inner.borrow_mut().execute().map_err(map_turso_error)?;
76-
Ok(RbExecutionResult {
77-
rows_changed: result.rows_changed,
78-
})
109+
let mut stmt = self.inner.borrow_mut();
110+
let mut rows: Vec<Value> = Vec::new();
111+
loop {
112+
match stmt.step().map_err(map_turso_error)? {
113+
TursoStatusCode::Row => {
114+
rows.push(value::extract_row(&stmt)?.as_value());
115+
}
116+
TursoStatusCode::Done => break,
117+
TursoStatusCode::Io => {
118+
stmt.run_io().map_err(map_turso_error)?;
119+
}
120+
_ => break,
121+
}
122+
}
123+
let changes = stmt.n_change();
124+
stmt.reset().map_err(map_turso_error)?;
125+
126+
Ok(RbResultSet::new(rows, changes))
79127
}
80128

81-
pub fn columns(&self) -> Result<RArray, Error> {
129+
pub fn columns(&self) -> Result<magnus::RArray, Error> {
82130
self.ensure_open()?;
83131
let ruby = Ruby::get().expect("Ruby not initialized");
84132
let stmt = self.inner.borrow();
@@ -124,9 +172,17 @@ impl Drop for RbStatement {
124172
}
125173
}
126174

175+
pub fn define_result_set(ruby: &Ruby, module: &impl Module) -> Result<(), Error> {
176+
let class = module.define_class("ResultSet", ruby.class_object())?;
177+
class.include_module(ruby.module_enumerable())?;
178+
class.define_method("rows_changed", method!(RbResultSet::rows_changed, 0))?;
179+
class.define_method("each", method!(RbResultSet::each, 0))?;
180+
Ok(())
181+
}
182+
183+
127184
pub fn define_statement(ruby: &Ruby, module: &impl Module) -> Result<(), Error> {
128-
let result_class = module.define_class("ExecutionResult", ruby.class_object())?;
129-
result_class.define_method("rows_changed", method!(RbExecutionResult::rows_changed, 0))?;
185+
define_result_set(ruby, module)?;
130186

131187
let class = module.define_class("Statement", ruby.class_object())?;
132188
class.define_method("bind", method!(RbStatement::bind, -1))?;

sdk-kit/src/rsapi.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,9 @@ impl TursoConnection {
589589
pub fn last_insert_rowid(&self) -> i64 {
590590
self.connection.last_insert_rowid()
591591
}
592+
pub fn changes(&self) -> u64 {
593+
self.connection.changes() as u64
594+
}
592595
/// prepares single SQL statement
593596
pub fn prepare_single(&self, sql: impl AsRef<str>) -> Result<Box<TursoStatement>, TursoError> {
594597
match self.connection.prepare(sql) {
@@ -679,6 +682,10 @@ impl TursoStatement {
679682
pub fn parameters_count(&self) -> usize {
680683
self.statement.parameters_count()
681684
}
685+
/// returns number of changes made by the statement
686+
pub fn n_change(&self) -> u64 {
687+
self.statement.n_change() as u64
688+
}
682689
/// binds positional parameter at the corresponding index (1-based)
683690
pub fn bind_positional(
684691
&mut self,

0 commit comments

Comments
 (0)