I have sqlite database with a list of files. I have a function which can iterate over these files. Because it can take a very long time to process some of the entries I want to add the ability to process a subset by specifying a (subtree) path.
So if the database contains:
foo/bar/baz/big.iso
qux/another_big_iso
.. and the user passes foo/bar
to the processing function, it should query the files using a WHERE LIKE ? || '%'
clause where ?
is set to the foo/bar
parameter. However, if the user does not pass any prefix/subset path, it should process all the files in the database.
I'm using rusqlite and am doing something like this:
// Used to gather values to bind (this will end up being empty
// if user didn't supply a subset prefix).
let mut vals: Vec<&dyn ToSql> = Vec::new();
// Determine the query string.
// sub is an Option<PathBuf> which contains
// the subset prefix if Some()
let q = if let Some(sub) = &sub {
let sub2 = sub.to_str().unwrap(); // ToDo: Don't unwrap
// User supplied a subset prefix path, so add it to the parameters
// This is the problem -- rust doesn't think sub lives long enough, which is
// true, but kind of not true as well, since it's a buffer held by the
// Option<PathBuf>.
vals.push(&sub2);
// process subset query
r#"SELECT f.id, f.ufile_id, f.fname, uf.hash
FROM files AS f
LEFT JOIN ufiles AS uf ON f.ufile_id=uf.id
WHERE f.fname LIKE ? || '%'
ORDER BY f.fname;"#
} else {
// process all query
r#"SELECT f.id, f.ufile_id, f.fname, uf.hash
FROM files AS f
LEFT JOIN ufiles AS uf ON f.ufile_id=uf.id
ORDER BY f.fname;"#
};
let mut stmt = conn.prepare_cached(q)?;
let mut rows = stmt.query(&vals[..])?;
while let Some(row) = rows.next()? {
// .. process file entry ..
}
I understand what the compiler is telling me (error at the bottom), and I tried some creative ideas to work around it, but I kept running into the same problem. (I tried to return stmt
, rows
and vals
as a tuple from the if
scopes to not have to worry about references, but it would still complain about lifetimes not being long enough).
I have some workarounds which solve the issue but violate DRY far more than the solutions I tried which did not work.
Are there any solutions to this which won't require me to copy more code into the two if
scopes?
Also: Should Rust be able to track the lifetime of sub
back to the Option<PathBuf>
? The sub: Option<PathBuf>
parameter is an input parameter to the function, so it should live (at least) as long as vals
.
error[E0597]: `sub2` does not live long enough
--> src/db.rs:682:15
|
682 | vals.push(&sub2);
| ^^^^^ borrowed value does not live long enough
...
691 | } else {
| - `sub2` dropped here while still borrowed
...
705 | let mut rows = stmt.query(&vals[..])?;
| ---- borrow later used here