Merge 'bindings/rust: Fix Rows::next() I/O dispatcher handling' from Pekka Enberg

The `next()` function needs to be a loop to make sure we actually return
rows.

Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #1754
This commit is contained in:
Pekka Enberg 2025-06-16 15:08:34 +03:00
commit 23b2d59871
2 changed files with 49 additions and 12 deletions

View file

@ -289,19 +289,29 @@ unsafe impl Sync for Rows {}
impl Rows {
pub async fn next(&mut self) -> Result<Option<Row>> {
let mut stmt = self
.inner
.lock()
.map_err(|e| Error::MutexError(e.to_string()))?;
match stmt.step() {
Ok(limbo_core::StepResult::Row) => {
let row = stmt.row().unwrap();
Ok(Some(Row {
values: row.get_values().map(|v| v.to_owned()).collect(),
}))
loop {
let mut stmt = self
.inner
.lock()
.map_err(|e| Error::MutexError(e.to_string()))?;
match stmt.step() {
Ok(limbo_core::StepResult::Row) => {
let row = stmt.row().unwrap();
return Ok(Some(Row {
values: row.get_values().map(|v| v.to_owned()).collect(),
}));
}
Ok(limbo_core::StepResult::Done) => return Ok(None),
Ok(limbo_core::StepResult::IO) => {
if let Err(e) = stmt.run_once() {
return Err(e.into());
}
continue;
}
Ok(limbo_core::StepResult::Busy) => return Ok(None),
Ok(limbo_core::StepResult::Interrupt) => return Ok(None),
_ => return Ok(None),
}
_ => Ok(None),
}
}
}

View file

@ -0,0 +1,27 @@
use limbo::Builder;
#[tokio::test]
async fn test_rows_next() {
let builder = Builder::new_local(":memory:");
let db = builder.build().await.unwrap();
let conn = db.connect().unwrap();
conn.execute("CREATE TABLE test (x INTEGER)", ())
.await
.unwrap();
conn.execute("INSERT INTO test (x) VALUES (1)", ())
.await
.unwrap();
conn.execute("INSERT INTO test (x) VALUES (2)", ())
.await
.unwrap();
let mut res = conn.query("SELECT * FROM test", ()).await.unwrap();
assert_eq!(
res.next().await.unwrap().unwrap().get_value(0).unwrap(),
1.into()
);
assert_eq!(
res.next().await.unwrap().unwrap().get_value(0).unwrap(),
2.into()
);
assert!(res.next().await.unwrap().is_none());
}