1 use libc::{ 2 c_uint, 3 size_t, 4 }; 5 use std::ffi::CString; 6 #[cfg(windows)] 7 use std::ffi::OsStr; 8 #[cfg(unix)] 9 use std::os::unix::ffi::OsStrExt; 10 use std::path::Path; 11 use std::sync::Mutex; 12 use std::{ 13 fmt, 14 mem, 15 ptr, 16 result, 17 }; 18 19 use ffi; 20 21 use byteorder::{ 22 ByteOrder, 23 NativeEndian, 24 }; 25 26 use cursor::Cursor; 27 use database::Database; 28 use error::{ 29 lmdb_result, 30 Error, 31 Result, 32 }; 33 use flags::{ 34 DatabaseFlags, 35 EnvironmentFlags, 36 }; 37 use transaction::{ 38 RoTransaction, 39 RwTransaction, 40 Transaction, 41 }; 42 43 #[cfg(windows)] 44 /// Adding a 'missing' trait from windows OsStrExt 45 trait OsStrExtLmdb { as_bytes(&self) -> &[u8]46 fn as_bytes(&self) -> &[u8]; 47 } 48 #[cfg(windows)] 49 impl OsStrExtLmdb for OsStr { as_bytes(&self) -> &[u8]50 fn as_bytes(&self) -> &[u8] { 51 &self.to_str().unwrap().as_bytes() 52 } 53 } 54 55 /// An LMDB environment. 56 /// 57 /// An environment supports multiple databases, all residing in the same shared-memory map. 58 pub struct Environment { 59 env: *mut ffi::MDB_env, 60 dbi_open_mutex: Mutex<()>, 61 } 62 63 impl Environment { 64 /// Creates a new builder for specifying options for opening an LMDB environment. 65 #[allow(clippy::new_ret_no_self)] new() -> EnvironmentBuilder66 pub fn new() -> EnvironmentBuilder { 67 EnvironmentBuilder { 68 flags: EnvironmentFlags::empty(), 69 max_readers: None, 70 max_dbs: None, 71 map_size: None, 72 } 73 } 74 75 /// Returns a raw pointer to the underlying LMDB environment. 76 /// 77 /// The caller **must** ensure that the pointer is not dereferenced after the lifetime of the 78 /// environment. env(&self) -> *mut ffi::MDB_env79 pub fn env(&self) -> *mut ffi::MDB_env { 80 self.env 81 } 82 83 /// Opens a handle to an LMDB database. 84 /// 85 /// If `name` is `None`, then the returned handle will be for the default database. 86 /// 87 /// If `name` is not `None`, then the returned handle will be for a named database. In this 88 /// case the environment must be configured to allow named databases through 89 /// `EnvironmentBuilder::set_max_dbs`. 90 /// 91 /// The returned database handle may be shared among any transaction in the environment. 92 /// 93 /// This function will fail with `Error::BadRslot` if called by a thread which has an ongoing 94 /// transaction. 95 /// 96 /// The database name may not contain the null character. open_db<'env>(&'env self, name: Option<&str>) -> Result<Database>97 pub fn open_db<'env>(&'env self, name: Option<&str>) -> Result<Database> { 98 let mutex = self.dbi_open_mutex.lock(); 99 let txn = self.begin_ro_txn()?; 100 let db = unsafe { txn.open_db(name)? }; 101 txn.commit()?; 102 drop(mutex); 103 Ok(db) 104 } 105 106 /// Opens a handle to an LMDB database, creating the database if necessary. 107 /// 108 /// If the database is already created, the given option flags will be added to it. 109 /// 110 /// If `name` is `None`, then the returned handle will be for the default database. 111 /// 112 /// If `name` is not `None`, then the returned handle will be for a named database. In this 113 /// case the environment must be configured to allow named databases through 114 /// `EnvironmentBuilder::set_max_dbs`. 115 /// 116 /// The returned database handle may be shared among any transaction in the environment. 117 /// 118 /// This function will fail with `Error::BadRslot` if called by a thread with an open 119 /// transaction. create_db<'env>(&'env self, name: Option<&str>, flags: DatabaseFlags) -> Result<Database>120 pub fn create_db<'env>(&'env self, name: Option<&str>, flags: DatabaseFlags) -> Result<Database> { 121 let mutex = self.dbi_open_mutex.lock(); 122 let txn = self.begin_rw_txn()?; 123 let db = unsafe { txn.create_db(name, flags)? }; 124 txn.commit()?; 125 drop(mutex); 126 Ok(db) 127 } 128 129 /// Retrieves the set of flags which the database is opened with. 130 /// 131 /// The database must belong to to this environment. get_db_flags(&self, db: Database) -> Result<DatabaseFlags>132 pub fn get_db_flags(&self, db: Database) -> Result<DatabaseFlags> { 133 let txn = self.begin_ro_txn()?; 134 let mut flags: c_uint = 0; 135 unsafe { 136 lmdb_result(ffi::mdb_dbi_flags(txn.txn(), db.dbi(), &mut flags))?; 137 } 138 Ok(DatabaseFlags::from_bits(flags).unwrap()) 139 } 140 141 /// Create a read-only transaction for use with the environment. begin_ro_txn<'env>(&'env self) -> Result<RoTransaction<'env>>142 pub fn begin_ro_txn<'env>(&'env self) -> Result<RoTransaction<'env>> { 143 RoTransaction::new(self) 144 } 145 146 /// Create a read-write transaction for use with the environment. This method will block while 147 /// there are any other read-write transactions open on the environment. begin_rw_txn<'env>(&'env self) -> Result<RwTransaction<'env>>148 pub fn begin_rw_txn<'env>(&'env self) -> Result<RwTransaction<'env>> { 149 RwTransaction::new(self) 150 } 151 152 /// Flush data buffers to disk. 153 /// 154 /// Data is always written to disk when `Transaction::commit` is called, but the operating 155 /// system may keep it buffered. LMDB always flushes the OS buffers upon commit as well, unless 156 /// the environment was opened with `MDB_NOSYNC` or in part `MDB_NOMETASYNC`. sync(&self, force: bool) -> Result<()>157 pub fn sync(&self, force: bool) -> Result<()> { 158 unsafe { 159 lmdb_result(ffi::mdb_env_sync( 160 self.env(), 161 if force { 162 1 163 } else { 164 0 165 }, 166 )) 167 } 168 } 169 170 /// Closes the database handle. Normally unnecessary. 171 /// 172 /// Closing a database handle is not necessary, but lets `Transaction::open_database` reuse the 173 /// handle value. Usually it's better to set a bigger `EnvironmentBuilder::set_max_dbs`, unless 174 /// that value would be large. 175 /// 176 /// ## Safety 177 /// 178 /// This call is not mutex protected. Databases should only be closed by a single thread, and 179 /// only if no other threads are going to reference the database handle or one of its cursors 180 /// any further. Do not close a handle if an existing transaction has modified its database. 181 /// Doing so can cause misbehavior from database corruption to errors like 182 /// `Error::BadValSize` (since the DB name is gone). close_db(&mut self, db: Database)183 pub unsafe fn close_db(&mut self, db: Database) { 184 ffi::mdb_dbi_close(self.env, db.dbi()); 185 } 186 187 /// Retrieves statistics about this environment. stat(&self) -> Result<Stat>188 pub fn stat(&self) -> Result<Stat> { 189 unsafe { 190 let mut stat = Stat::new(); 191 lmdb_try!(ffi::mdb_env_stat(self.env(), stat.mdb_stat())); 192 Ok(stat) 193 } 194 } 195 196 /// Retrieves info about this environment. info(&self) -> Result<Info>197 pub fn info(&self) -> Result<Info> { 198 unsafe { 199 let mut info = Info(mem::zeroed()); 200 lmdb_try!(ffi::mdb_env_info(self.env(), &mut info.0)); 201 Ok(info) 202 } 203 } 204 205 /// Retrieves the total number of pages on the freelist. 206 /// 207 /// Along with `Environment::info()`, this can be used to calculate the exact number 208 /// of used pages as well as free pages in this environment. 209 /// 210 /// ```ignore 211 /// let env = Environment::new().open("/tmp/test").unwrap(); 212 /// let info = env.info().unwrap(); 213 /// let stat = env.stat().unwrap(); 214 /// let freelist = env.freelist().unwrap(); 215 /// let last_pgno = info.last_pgno() + 1; // pgno is 0 based. 216 /// let total_pgs = info.map_size() / stat.page_size() as usize; 217 /// let pgs_in_use = last_pgno - freelist; 218 /// let pgs_free = total_pgs - pgs_in_use; 219 /// ``` 220 /// 221 /// Note: 222 /// 223 /// * LMDB stores all the freelists in the designated database 0 in each environment, 224 /// and the freelist count is stored at the beginning of the value as `libc::size_t` 225 /// in the native byte order. 226 /// 227 /// * It will create a read transaction to traverse the freelist database. freelist(&self) -> Result<size_t>228 pub fn freelist(&self) -> Result<size_t> { 229 let mut freelist: size_t = 0; 230 let db = Database::freelist_db(); 231 let txn = self.begin_ro_txn()?; 232 let mut cursor = txn.open_ro_cursor(db)?; 233 234 for result in cursor.iter() { 235 let (_key, value) = result?; 236 if value.len() < mem::size_of::<size_t>() { 237 return Err(Error::Corrupted); 238 } 239 240 let s = &value[..mem::size_of::<size_t>()]; 241 if cfg!(target_pointer_width = "64") { 242 freelist += NativeEndian::read_u64(s) as size_t; 243 } else { 244 freelist += NativeEndian::read_u32(s) as size_t; 245 } 246 } 247 248 Ok(freelist) 249 } 250 251 /// Sets the size of the memory map to use for the environment. 252 /// 253 /// This could be used to resize the map when the environment is already open. 254 /// 255 /// Note: 256 /// 257 /// * No active transactions allowed when performing resizing in this process. 258 /// 259 /// * The size should be a multiple of the OS page size. Any attempt to set 260 /// a size smaller than the space already consumed by the environment will 261 /// be silently changed to the current size of the used space. 262 /// 263 /// * In the multi-process case, once a process resizes the map, other 264 /// processes need to either re-open the environment, or call set_map_size 265 /// with size 0 to update the environment. Otherwise, new transaction creation 266 /// will fail with `Error::MapResized`. set_map_size(&self, size: size_t) -> Result<()>267 pub fn set_map_size(&self, size: size_t) -> Result<()> { 268 unsafe { lmdb_result(ffi::mdb_env_set_mapsize(self.env(), size)) } 269 } 270 } 271 272 /// Environment statistics. 273 /// 274 /// Contains information about the size and layout of an LMDB environment or database. 275 pub struct Stat(ffi::MDB_stat); 276 277 impl Stat { 278 /// Create a new Stat with zero'd inner struct `ffi::MDB_stat`. new() -> Stat279 pub(crate) fn new() -> Stat { 280 unsafe { Stat(mem::zeroed()) } 281 } 282 283 /// Returns a mut pointer to `ffi::MDB_stat`. mdb_stat(&mut self) -> *mut ffi::MDB_stat284 pub(crate) fn mdb_stat(&mut self) -> *mut ffi::MDB_stat { 285 &mut self.0 286 } 287 } 288 289 impl Stat { 290 /// Size of a database page. This is the same for all databases in the environment. 291 #[inline] page_size(&self) -> u32292 pub fn page_size(&self) -> u32 { 293 self.0.ms_psize 294 } 295 296 /// Depth (height) of the B-tree. 297 #[inline] depth(&self) -> u32298 pub fn depth(&self) -> u32 { 299 self.0.ms_depth 300 } 301 302 /// Number of internal (non-leaf) pages. 303 #[inline] branch_pages(&self) -> usize304 pub fn branch_pages(&self) -> usize { 305 self.0.ms_branch_pages 306 } 307 308 /// Number of leaf pages. 309 #[inline] leaf_pages(&self) -> usize310 pub fn leaf_pages(&self) -> usize { 311 self.0.ms_leaf_pages 312 } 313 314 /// Number of overflow pages. 315 #[inline] overflow_pages(&self) -> usize316 pub fn overflow_pages(&self) -> usize { 317 self.0.ms_overflow_pages 318 } 319 320 /// Number of data items. 321 #[inline] entries(&self) -> usize322 pub fn entries(&self) -> usize { 323 self.0.ms_entries 324 } 325 } 326 327 /// Environment information. 328 /// 329 /// Contains environment information about the map size, readers, last txn id etc. 330 pub struct Info(ffi::MDB_envinfo); 331 332 impl Info { 333 /// Size of memory map. 334 #[inline] map_size(&self) -> usize335 pub fn map_size(&self) -> usize { 336 self.0.me_mapsize 337 } 338 339 /// Last used page number 340 #[inline] last_pgno(&self) -> usize341 pub fn last_pgno(&self) -> usize { 342 self.0.me_last_pgno 343 } 344 345 /// Last transaction ID 346 #[inline] last_txnid(&self) -> usize347 pub fn last_txnid(&self) -> usize { 348 self.0.me_last_txnid 349 } 350 351 /// Max reader slots in the environment 352 #[inline] max_readers(&self) -> u32353 pub fn max_readers(&self) -> u32 { 354 self.0.me_maxreaders 355 } 356 357 /// Max reader slots used in the environment 358 #[inline] num_readers(&self) -> u32359 pub fn num_readers(&self) -> u32 { 360 self.0.me_numreaders 361 } 362 } 363 364 unsafe impl Send for Environment {} 365 unsafe impl Sync for Environment {} 366 367 impl fmt::Debug for Environment { fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error>368 fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> { 369 f.debug_struct("Environment").finish() 370 } 371 } 372 373 impl Drop for Environment { drop(&mut self)374 fn drop(&mut self) { 375 unsafe { ffi::mdb_env_close(self.env) } 376 } 377 } 378 379 /////////////////////////////////////////////////////////////////////////////////////////////////// 380 //// Environment Builder 381 /////////////////////////////////////////////////////////////////////////////////////////////////// 382 383 /// Options for opening or creating an environment. 384 #[derive(Debug, PartialEq, Eq, Copy, Clone)] 385 pub struct EnvironmentBuilder { 386 flags: EnvironmentFlags, 387 max_readers: Option<c_uint>, 388 max_dbs: Option<c_uint>, 389 map_size: Option<size_t>, 390 } 391 392 impl EnvironmentBuilder { 393 /// Open an environment. 394 /// 395 /// On UNIX, the database files will be opened with 644 permissions. 396 /// 397 /// The path may not contain the null character, Windows UNC (Uniform Naming Convention) 398 /// paths are not supported either. open(&self, path: &Path) -> Result<Environment>399 pub fn open(&self, path: &Path) -> Result<Environment> { 400 self.open_with_permissions(path, 0o644) 401 } 402 403 /// Open an environment with the provided UNIX permissions. 404 /// 405 /// On Windows, the permissions will be ignored. 406 /// 407 /// The path may not contain the null character, Windows UNC (Uniform Naming Convention) 408 /// paths are not supported either. open_with_permissions(&self, path: &Path, mode: ffi::mode_t) -> Result<Environment>409 pub fn open_with_permissions(&self, path: &Path, mode: ffi::mode_t) -> Result<Environment> { 410 let mut env: *mut ffi::MDB_env = ptr::null_mut(); 411 unsafe { 412 lmdb_try!(ffi::mdb_env_create(&mut env)); 413 if let Some(max_readers) = self.max_readers { 414 lmdb_try_with_cleanup!(ffi::mdb_env_set_maxreaders(env, max_readers), ffi::mdb_env_close(env)) 415 } 416 if let Some(max_dbs) = self.max_dbs { 417 lmdb_try_with_cleanup!(ffi::mdb_env_set_maxdbs(env, max_dbs), ffi::mdb_env_close(env)) 418 } 419 if let Some(map_size) = self.map_size { 420 lmdb_try_with_cleanup!(ffi::mdb_env_set_mapsize(env, map_size), ffi::mdb_env_close(env)) 421 } 422 let path = match CString::new(path.as_os_str().as_bytes()) { 423 Ok(path) => path, 424 Err(..) => return Err(::Error::Invalid), 425 }; 426 lmdb_try_with_cleanup!( 427 ffi::mdb_env_open(env, path.as_ptr(), self.flags.bits(), mode), 428 ffi::mdb_env_close(env) 429 ); 430 } 431 Ok(Environment { 432 env, 433 dbi_open_mutex: Mutex::new(()), 434 }) 435 } 436 437 /// Sets the provided options in the environment. set_flags(&mut self, flags: EnvironmentFlags) -> &mut EnvironmentBuilder438 pub fn set_flags(&mut self, flags: EnvironmentFlags) -> &mut EnvironmentBuilder { 439 self.flags = flags; 440 self 441 } 442 443 /// Sets the maximum number of threads or reader slots for the environment. 444 /// 445 /// This defines the number of slots in the lock table that is used to track readers in the 446 /// the environment. The default is 126. Starting a read-only transaction normally ties a lock 447 /// table slot to the current thread until the environment closes or the thread exits. If 448 /// `MDB_NOTLS` is in use, `Environment::open_txn` instead ties the slot to the `Transaction` 449 /// object until it or the `Environment` object is destroyed. set_max_readers(&mut self, max_readers: c_uint) -> &mut EnvironmentBuilder450 pub fn set_max_readers(&mut self, max_readers: c_uint) -> &mut EnvironmentBuilder { 451 self.max_readers = Some(max_readers); 452 self 453 } 454 455 /// Sets the maximum number of named databases for the environment. 456 /// 457 /// This function is only needed if multiple databases will be used in the 458 /// environment. Simpler applications that use the environment as a single 459 /// unnamed database can ignore this option. 460 /// 461 /// Currently a moderate number of slots are cheap but a huge number gets 462 /// expensive: 7-120 words per transaction, and every `Transaction::open_db` 463 /// does a linear search of the opened slots. set_max_dbs(&mut self, max_dbs: c_uint) -> &mut EnvironmentBuilder464 pub fn set_max_dbs(&mut self, max_dbs: c_uint) -> &mut EnvironmentBuilder { 465 self.max_dbs = Some(max_dbs); 466 self 467 } 468 469 /// Sets the size of the memory map to use for the environment. 470 /// 471 /// The size should be a multiple of the OS page size. The default is 472 /// 1048576 bytes. The size of the memory map is also the maximum size 473 /// of the database. The value should be chosen as large as possible, 474 /// to accommodate future growth of the database. It may be increased at 475 /// later times. 476 /// 477 /// Any attempt to set a size smaller than the space already consumed 478 /// by the environment will be silently changed to the current size of the used space. set_map_size(&mut self, map_size: size_t) -> &mut EnvironmentBuilder479 pub fn set_map_size(&mut self, map_size: size_t) -> &mut EnvironmentBuilder { 480 self.map_size = Some(map_size); 481 self 482 } 483 } 484 485 #[cfg(test)] 486 mod test { 487 488 extern crate byteorder; 489 490 use self::byteorder::{ 491 ByteOrder, 492 LittleEndian, 493 }; 494 use tempdir::TempDir; 495 496 use flags::*; 497 498 use super::*; 499 500 #[test] test_open()501 fn test_open() { 502 let dir = TempDir::new("test").unwrap(); 503 504 // opening non-existent env with read-only should fail 505 assert!(Environment::new().set_flags(EnvironmentFlags::READ_ONLY).open(dir.path()).is_err()); 506 507 // opening non-existent env should succeed 508 assert!(Environment::new().open(dir.path()).is_ok()); 509 510 // opening env with read-only should succeed 511 assert!(Environment::new().set_flags(EnvironmentFlags::READ_ONLY).open(dir.path()).is_ok()); 512 } 513 514 #[test] test_begin_txn()515 fn test_begin_txn() { 516 let dir = TempDir::new("test").unwrap(); 517 518 { 519 // writable environment 520 let env = Environment::new().open(dir.path()).unwrap(); 521 522 assert!(env.begin_rw_txn().is_ok()); 523 assert!(env.begin_ro_txn().is_ok()); 524 } 525 526 { 527 // read-only environment 528 let env = Environment::new().set_flags(EnvironmentFlags::READ_ONLY).open(dir.path()).unwrap(); 529 530 assert!(env.begin_rw_txn().is_err()); 531 assert!(env.begin_ro_txn().is_ok()); 532 } 533 } 534 535 #[test] test_open_db()536 fn test_open_db() { 537 let dir = TempDir::new("test").unwrap(); 538 let env = Environment::new().set_max_dbs(1).open(dir.path()).unwrap(); 539 540 assert!(env.open_db(None).is_ok()); 541 assert!(env.open_db(Some("testdb")).is_err()); 542 } 543 544 #[test] test_create_db()545 fn test_create_db() { 546 let dir = TempDir::new("test").unwrap(); 547 let env = Environment::new().set_max_dbs(11).open(dir.path()).unwrap(); 548 assert!(env.open_db(Some("testdb")).is_err()); 549 assert!(env.create_db(Some("testdb"), DatabaseFlags::empty()).is_ok()); 550 assert!(env.open_db(Some("testdb")).is_ok()) 551 } 552 553 #[test] test_close_database()554 fn test_close_database() { 555 let dir = TempDir::new("test").unwrap(); 556 let mut env = Environment::new().set_max_dbs(10).open(dir.path()).unwrap(); 557 558 let db = env.create_db(Some("db"), DatabaseFlags::empty()).unwrap(); 559 unsafe { 560 env.close_db(db); 561 } 562 assert!(env.open_db(Some("db")).is_ok()); 563 } 564 565 #[test] test_sync()566 fn test_sync() { 567 let dir = TempDir::new("test").unwrap(); 568 { 569 let env = Environment::new().open(dir.path()).unwrap(); 570 assert!(env.sync(true).is_ok()); 571 } 572 { 573 let env = Environment::new().set_flags(EnvironmentFlags::READ_ONLY).open(dir.path()).unwrap(); 574 assert!(env.sync(true).is_err()); 575 } 576 } 577 578 #[test] test_stat()579 fn test_stat() { 580 let dir = TempDir::new("test").unwrap(); 581 let env = Environment::new().open(dir.path()).unwrap(); 582 583 // Stats should be empty initially. 584 let stat = env.stat().unwrap(); 585 assert_eq!(stat.page_size(), 4096); 586 assert_eq!(stat.depth(), 0); 587 assert_eq!(stat.branch_pages(), 0); 588 assert_eq!(stat.leaf_pages(), 0); 589 assert_eq!(stat.overflow_pages(), 0); 590 assert_eq!(stat.entries(), 0); 591 592 let db = env.open_db(None).unwrap(); 593 594 // Write a few small values. 595 for i in 0..64 { 596 let mut value = [0u8; 8]; 597 LittleEndian::write_u64(&mut value, i); 598 let mut tx = env.begin_rw_txn().expect("begin_rw_txn"); 599 tx.put(db, &value, &value, WriteFlags::default()).expect("tx.put"); 600 tx.commit().expect("tx.commit") 601 } 602 603 // Stats should now reflect inserted values. 604 let stat = env.stat().unwrap(); 605 assert_eq!(stat.page_size(), 4096); 606 assert_eq!(stat.depth(), 1); 607 assert_eq!(stat.branch_pages(), 0); 608 assert_eq!(stat.leaf_pages(), 1); 609 assert_eq!(stat.overflow_pages(), 0); 610 assert_eq!(stat.entries(), 64); 611 } 612 613 #[test] test_info()614 fn test_info() { 615 let map_size = 1024 * 1024; 616 let dir = TempDir::new("test").unwrap(); 617 let env = Environment::new().set_map_size(map_size).open(dir.path()).unwrap(); 618 619 let info = env.info().unwrap(); 620 assert_eq!(info.map_size(), map_size); 621 assert_eq!(info.last_pgno(), 1); 622 assert_eq!(info.last_txnid(), 0); 623 // The default max readers is 126. 624 assert_eq!(info.max_readers(), 126); 625 assert_eq!(info.num_readers(), 0); 626 } 627 628 #[test] test_freelist()629 fn test_freelist() { 630 let dir = TempDir::new("test").unwrap(); 631 let env = Environment::new().open(dir.path()).unwrap(); 632 633 let db = env.open_db(None).unwrap(); 634 let mut freelist = env.freelist().unwrap(); 635 assert_eq!(freelist, 0); 636 637 // Write a few small values. 638 for i in 0..64 { 639 let mut value = [0u8; 8]; 640 LittleEndian::write_u64(&mut value, i); 641 let mut tx = env.begin_rw_txn().expect("begin_rw_txn"); 642 tx.put(db, &value, &value, WriteFlags::default()).expect("tx.put"); 643 tx.commit().expect("tx.commit") 644 } 645 let mut tx = env.begin_rw_txn().expect("begin_rw_txn"); 646 tx.clear_db(db).expect("clear"); 647 tx.commit().expect("tx.commit"); 648 649 // Freelist should not be empty after clear_db. 650 freelist = env.freelist().unwrap(); 651 assert!(freelist > 0); 652 } 653 654 #[test] test_set_map_size()655 fn test_set_map_size() { 656 let dir = TempDir::new("test").unwrap(); 657 let env = Environment::new().open(dir.path()).unwrap(); 658 659 let mut info = env.info().unwrap(); 660 let default_size = info.map_size(); 661 662 // Resizing to 0 merely reloads the map size 663 env.set_map_size(0).unwrap(); 664 info = env.info().unwrap(); 665 assert_eq!(info.map_size(), default_size); 666 667 env.set_map_size(2 * default_size).unwrap(); 668 info = env.info().unwrap(); 669 assert_eq!(info.map_size(), 2 * default_size); 670 671 env.set_map_size(4 * default_size).unwrap(); 672 info = env.info().unwrap(); 673 assert_eq!(info.map_size(), 4 * default_size); 674 675 // Decreasing is also fine if the space hasn't been consumed. 676 env.set_map_size(2 * default_size).unwrap(); 677 info = env.info().unwrap(); 678 assert_eq!(info.map_size(), 2 * default_size); 679 } 680 } 681