danburkert / lmdb-rs

Safe Rust bindings for LMDB
Apache License 2.0
172 stars 87 forks source link

Cursor::iter_start() panics on empty database #27

Open marshallpierce opened 6 years ago

marshallpierce commented 6 years ago

It seems like it should instead return an iterator that yields no results.

Unfortunately, it doesn't work to simply ignore the result of get() in iter_start() and return the Iter anyway: the subsequent mdb_cursor_get() call in Iter yields error code 22.

One way to do this would be to add a bool field to Iter to record whether or not it should try to look up more records, and set the field to true if the first get() doesn't find a key. Could also set the field when the first error is returned to spare further lookups, though that's likely not a bottleneck for anybody.

Anyway, if this sounds good I'm happy to submit a PR for it.

marshallpierce commented 6 years ago

I went ahead and tossed together a PR on the off chance the implementation approach above sounds good to you.

jgarzik commented 5 years ago

Hitting this issue also. Test case demonstrating problem:

use lmdb::{Cursor, Transaction};
use std::path::Path;

fn main() {
    let cfg_builder = lmdb::Environment::new();
    let path = Path::new("./test.db");
    let env = cfg_builder.open(path).unwrap();
    let db = env.create_db(None, lmdb::DatabaseFlags::empty()).unwrap();
    let txn = env.begin_ro_txn().unwrap();
    let cursor_res = txn.open_ro_cursor(db);
    if cursor_res.is_err() { panic!("open-ro-cursor"); }
    let mut cursor = cursor_res.unwrap();

    println!("calling cursor.iter_start");
    let mut it = cursor.iter_start();
    loop {
        match it.next() {
            None => break,
            Some(tupl) => {
                println!("key = {}", String::from_utf8_lossy(tupl.0));
            }
        }
    }
}