Last active
October 10, 2023 06:50
-
-
Save leiless/363b289db2940388cf1ec87376f5d53e to your computer and use it in GitHub Desktop.
LMDB `DUP_SORT` `cursor.put(CURRENT)` different value size bug
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use lmdb::{Cursor, Transaction}; | |
fn test_lmdb_dup_sort_update_in_place() -> anyhow::Result<()> { | |
let db_dir = "lmdb-dir"; | |
if let Err(err) = std::fs::remove_dir_all(db_dir) { | |
if err.kind() != std::io::ErrorKind::NotFound { | |
return Err(err.try_into()?); | |
} | |
} | |
std::fs::create_dir_all(db_dir)?; | |
let env = { | |
let builder = lmdb::Environment::new(); | |
builder.open(std::path::Path::new(db_dir)).unwrap() | |
}; | |
let db = env.create_db( | |
None, | |
lmdb::DatabaseFlags::INTEGER_KEY | lmdb::DatabaseFlags::DUP_SORT, | |
)?; | |
let key = 0x3003_0000_0000_003d_u64; | |
eprintln!("Input key: {:#x}", key); | |
let key_bytes = key.to_ne_bytes(); | |
// cursor.put(..., CURRENT) BUG: when new val len is less than old val len | |
let val1 = "新建"; // "新建".len() = 3 * 2 = 6 | |
let val2 = "123456"; | |
let mut txn = env.begin_rw_txn()?; | |
txn.put(db, &key_bytes, &val1, lmdb::WriteFlags::empty())?; | |
let val = txn.get(db, &key_bytes)?; | |
assert_eq!(val, val1.as_bytes()); | |
let mut cursor = txn.open_rw_cursor(db)?; | |
for it in cursor.iter_from(key_bytes) { | |
let (it_key, it_val) = it?; | |
assert_eq!(it_key, key_bytes); | |
assert_eq!(it_val, val1.as_bytes()); | |
cursor.put(&it_key, &val2, lmdb::WriteFlags::CURRENT)?; | |
// HOWTO fix: | |
//cursor.del(lmdb::WriteFlags::empty())?; | |
//cursor.put(&it_key, &val2, lmdb::WriteFlags::empty())?; | |
break; | |
} | |
drop(cursor); | |
txn.commit()?; | |
let txn = env.begin_ro_txn()?; | |
let mut cursor = txn.open_ro_cursor(db)?; | |
let mut count = 0; | |
for it in cursor.iter_from(key_bytes) { | |
let (it_key, it_val) = it?; | |
let a = u64::from_ne_bytes(it_key.try_into()?); | |
let b = std::str::from_utf8(it_val)?; | |
eprintln!(" Read key: {:#x}", a); | |
eprintln!(" Read val: {}", b); | |
count += 1; | |
assert_eq!(count, 1); | |
assert_eq!(a, key, "{:#x} vs {:#x}", a, key); | |
assert_eq!(b, val2); | |
} | |
drop(cursor); | |
txn.abort(); | |
assert_eq!(count, 1); | |
let txn = env.begin_ro_txn()?; | |
let val = txn.get(db, &key_bytes)?; | |
assert_eq!(val, val2.as_bytes()); | |
txn.abort(); | |
Ok(()) | |
} | |
fn main() -> Result<(), Box<dyn std::error::Error>> { | |
test_lmdb_dup_sort_update_in_place()?; | |
eprintln!("OK!"); | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Reproduce
Change
let val1 = "新建";
tolet val1 = "新建_";
let val1 = "新建__";
let val1 = "新建___";
See
https://github.com/leiless/lmdb
leiless/lmdb#1
meilisearch/lmdb#2