Hi,
I'm trying to create a custom iterator for a query execution using rusqlite.
The final goal of this iterator is to return arrow::RecordBach instances
containing at most 100 rows.
The naive implementation below does not compile. I understand why the borrow checker is not happy but there should be a way to go around the borrow checker since the SqliteQuery
returned could guaranty the lifetime of the values involved.
My Understanding is that the raw_query()
method by taking a mutable reference of the statement prevent me to later move the statement into the SqliteQuery
.
pub fn raw_query(&mut self) -> Rows<'_>
I tried to play with RefCell
for a runtime checking of the borrowing but apparently I'm still not yet experimented in RUST to play with this...
Any help would be greatly appreciated!
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
struct RecordBatch {/* simulate an arrow_array::RecordBatch */}
struct SqliteQuery<'c> {
stmt: rusqlite::Statement<'c>,
rows: rusqlite::Rows<'c>,
}
// Implementation of the custom iterator that returns RecordBatch instances each containing at most 100 rows
impl<'c> Iterator for SqliteQuery<'c> {
type Item = Result<RecordBatch>;
fn next(&mut self) -> Option<Result<RecordBatch>> {
let batch = RecordBatch {};
for _i in 0..100 {
match self.rows.next() {
Ok(Some(_row)) => {
todo!("Add row to batch")
}
Ok(None) => {
return None;
}
Err(e) => {
return Some(Err(e.into()));
}
}
}
Some(Ok(batch))
}
}
// Function to query the database and return the custom iterator
fn query<'c>(
conn: &'c rusqlite::Connection,
sql: String,
) -> Result<Box<dyn Iterator<Item = Result<RecordBatch>> + 'c>> {
let mut stmt: rusqlite::Statement = conn.prepare(sql.as_str())?;
let rows: rusqlite::Rows = stmt.raw_query();
let query = SqliteQuery { stmt, rows };
Ok(Box::new(query))
}
fn main() {
let conn = rusqlite::Connection::open_in_memory().unwrap();
let rows = query(&conn, "SELECT 1".to_string()).unwrap();
for row in rows {
match row {
Ok(_batch) => {
// Do something with the record batch
}
Err(e) => {
eprintln!("Error: {}", e);
}
}
}
}
Errors:
Compiling playground v0.0.1 (/playground)
error[E0505]: cannot move out of `stmt` because it is borrowed
--> src/lib.rs:40:31
|
34 | fn query<'c>(
| -- lifetime `'c` defined here
...
38 | let mut stmt: rusqlite::Statement = conn.prepare(sql.as_str())?;
| -------- binding `stmt` declared here
39 | let rows: rusqlite::Rows = stmt.raw_query();
| ---- borrow of `stmt` occurs here
40 | let query = SqliteQuery { stmt, rows };
| ^^^^ move out of `stmt` occurs here
41 | Ok(Box::new(query))
| ------------------- returning this value requires that `stmt` is borrowed for `'c`
error[E0515]: cannot return value referencing local variable `stmt`
--> src/lib.rs:41:5
|
39 | let rows: rusqlite::Rows = stmt.raw_query();
| ---- `stmt` is borrowed here
40 | let query = SqliteQuery { stmt, rows };
41 | Ok(Box::new(query))
| ^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function
Some errors have detailed explanations: E0505, E0515.
For more information about an error, try `rustc --explain E0505`.
error: could not compile `playground` (lib) due to 2 previous errors