Repository: cberner/redb
Pull request #160 (2022-02-07 00:45:34)

Several new features and cleanups


Patch:
diff --git a/benches/common.rs b/benches/common.rs
index fe2771f..623e091 100644
--- a/benches/common.rs
+++ b/benches/common.rs
@@ -96,7 +96,7 @@ impl<'a, 'b> BenchReadTransaction<'b> for RedbBenchReadTransaction<'a> {
     }
 
     fn exists_after(&'b self, key: &[u8]) -> bool {
-        self.table.get_range(key..).unwrap().next().is_some()
+        self.table.range(key..).unwrap().next().is_some()
     }
 }
 
diff --git a/src/db.rs b/src/db.rs
index 2ed3123..45fe0d6 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -130,7 +130,7 @@ impl DatabaseBuilder {
     ///
     /// The file referenced by `path` must only be concurrently modified by compatible versions
     /// of redb
-    pub unsafe fn open(&self, path: impl AsRef<Path>, db_size: usize) -> Result<Database, Error> {
+    pub unsafe fn create(&self, path: impl AsRef<Path>, db_size: usize) -> Result<Database, Error> {
         let file = OpenOptions::new()
             .read(true)
             .write(true)
@@ -150,9 +150,8 @@ pub struct DatabaseTransaction<'a> {
     storage: &'a Storage,
     transaction_id: u128,
     root_page: Cell<Option<PageNumber>>,
-    pending_table_root_changes:
-        RefCell<HashMap<(Vec<u8>, TableType), Rc<Cell<Option<PageNumber>>>>>,
-    open_tables: RefCell<HashMap<(Vec<u8>, TableType), &'static panic::Location<'static>>>,
+    pending_table_root_changes: RefCell<HashMap<(String, TableType), Rc<Cell<Option<PageNumber>>>>>,
+    open_tables: RefCell<HashMap<(String, TableType), &'static panic::Location<'static>>>,
     completed: AtomicBool,
 }
 
@@ -175,16 +174,13 @@ impl<'a> DatabaseTransaction<'a> {
     /// The table will be created if it does not exist
     pub fn open_table<K: RedbKey + ?Sized, V: RedbValue + ?Sized>(
         &self,
-        name: impl AsRef<[u8]>,
+        name: impl AsRef<str>,
     ) -> Result<Table<'a, K, V>, Error> {
         assert!(!name.as_ref().is_empty());
         assert_ne!(name.as_ref(), FREED_TABLE);
-        let key = (name.as_ref().to_vec(), TableType::Normal);
+        let key = (name.as_ref().to_string(), TableType::Normal);
         if let Some(location) = self.open_tables.borrow().get(&key) {
-            return Err(Error::TableAlreadyOpen(
-                String::from_utf8_lossy(name.as_ref()).to_string(),
-                location,
-            ));
+            return Err(Error::TableAlreadyOpen(name.as_ref().to_string(), location));
         }
         self.open_tables
             .borrow_mut()
@@ -213,16 +209,13 @@ impl<'a> DatabaseTransaction<'a> {
     /// The table will be created if it does not exist
     pub fn open_multimap_table<K: RedbKey + ?Sized, V: RedbKey + ?Sized>(
         &self,
-        name: impl AsRef<[u8]>,
+        name: impl AsRef<str>,
     ) -> Result<MultimapTable<'a, K, V>, Error> {
         assert!(!name.as_ref().is_empty());
         assert_ne!(name.as_ref(), FREED_TABLE);
-        let key = (name.as_ref().to_vec(), TableType::Multimap);
+        let key = (name.as_ref().to_string(), TableType::Multimap);
         if let Some(location) = self.open_tables.borrow().get(&key) {
-            return Err(Error::TableAlreadyOpen(
-                String::from_utf8_lossy(name.as_ref()).to_string(),
-                location,
-            ));
+            return Err(Error::TableAlreadyOpen(name.as_ref().to_string(), location));
         }
         self.open_tables
             .borrow_mut()
@@ -249,7 +242,7 @@ impl<'a> DatabaseTransaction<'a> {
     /// Delete the given table
     ///
     /// Returns a bool indicating whether the table existed
-    pub fn delete_table(&self, name: impl AsRef<[u8]>) -> Result<bool, Error> {
+    pub fn delete_table(&self, name: impl AsRef<str>) -> Result<bool, Error> {
         assert!(!name.as_ref().is_empty());
         let original_root = self.root_page.get();
         let (root, found) = self.storage.delete_table(
@@ -267,7 +260,7 @@ impl<'a> DatabaseTransaction<'a> {
     /// Delete the given table
     ///
     /// Returns a bool indicating whether the table existed
-    pub fn delete_multimap_table(&self, name: impl AsRef<[u8]>) -> Result<bool, Error> {
+    pub fn delete_multimap_table(&self, name: impl AsRef<str>) -> Result<bool, Error> {
         assert!(!name.as_ref().is_empty());
         let original_root = self.root_page.get();
         let (root, found) = self.storage.delete_table(
@@ -282,6 +275,16 @@ impl<'a> DatabaseTransaction<'a> {
         Ok(found)
     }
 
+    pub fn list_tables(&self) -> Result<impl Iterator<Item = String> + '_, Error> {
+        self.storage
+            .list_tables(TableType::Normal, self.root_page.get())
+    }
+
+    pub fn list_multimap_tables(&self) -> Result<impl Iterator<Item = String> + '_, Error> {
+        self.storage
+            .list_tables(TableType::Multimap, self.root_page.get())
+    }
+
     pub fn commit(self) -> Result<(), Error> {
         self.commit_helper(false)
     }
@@ -292,7 +295,7 @@ impl<'a> DatabaseTransaction<'a> {
         self.commit_helper(true)
     }
 
-    pub fn commit_helper(self, non_durable: bool) -> Result<(), Error> {
+    fn commit_helper(self, non_durable: bool) -> Result<(), Error> {
         match self.commit_helper_inner(non_durable) {
             Ok(_) => Ok(()),
             Err(err) => match err {
@@ -307,7 +310,7 @@ impl<'a> DatabaseTransaction<'a> {
         }
     }
 
-    pub fn commit_helper_inner(&self, non_durable: bool) -> Result<(), Error> {
+    fn commit_helper_inner(&self, non_durable: bool) -> Result<(), Error> {
         // Update all the table roots in the master table, before committing
         for ((name, table_type), update) in self.pending_table_root_changes.borrow_mut().drain() {
             let new_root = self.storage.update_table_root(
@@ -367,7 +370,7 @@ impl<'a> ReadOnlyDatabaseTransaction<'a> {
     /// Open the given table
     pub fn open_table<K: RedbKey + ?Sized, V: RedbValue + ?Sized>(
         &self,
-        name: impl AsRef<[u8]>,
+        name: impl AsRef<str>,
     ) -> Result<ReadOnlyTable<'a, K, V>, Error> {
         assert!(!name.as_ref().is_empty());
         assert_ne!(name.as_ref(), FREED_TABLE);
@@ -375,10 +378,7 @@ impl<'a> ReadOnlyDatabaseTransaction<'a> {
             .storage
             .get_table(name.as_ref(), TableType::Normal, self.root_page)?
             .ok_or_else(|| {
-                Error::DoesNotExist(format!(
-                    "Table '{}' does not exist",
-                    String::from_utf8_lossy(name.as_ref())
-                ))
+                Error::DoesNotExist(format!("Table '{}' does not exist", name.as_ref()))
             })?;
 
         Ok(ReadOnlyTable::new(definition.get_root(), self.storage))
@@ -387,7 +387,7 @@ impl<'a> ReadOnlyDatabaseTransaction<'a> {
     /// Open the given table
     pub fn open_multimap_table<K: RedbKey + ?Sized, V: RedbKey + ?Sized>(
         &self,
-        name: impl AsRef<[u8]>,
+        name: impl AsRef<str>,
     ) -> Result<ReadOnlyMultimapTable<'a, K, V>, Error> {
         assert!(!name.as_ref().is_empty());
         assert_ne!(name.as_ref(), FREED_TABLE);
@@ -395,10 +395,7 @@ impl<'a> ReadOnlyDatabaseTransaction<'a> {
             .storage
             .get_table(name.as_ref(), TableType::Multimap, self.root_page)?
             .ok_or_else(|| {
-                Error::DoesNotExist(format!(
-                    "Table '{}' does not exist",
-                    String::from_utf8_lossy(name.as_ref())
-                ))
+                Error::DoesNotExist(format!("Table '{}' does not exist", name.as_ref()))
             })?;
 
         Ok(ReadOnlyMultimapTable::new(
@@ -406,6 +403,15 @@ impl<'a> ReadOnlyDatabaseTransaction<'a> {
             self.storage,
         ))
     }
+
+    pub fn list_tables(&self) -> Result<impl Iterator<Item = String> + '_, Error> {
+        self.storage.list_tables(TableType::Normal, self.root_page)
+    }
+
+    pub fn list_multimap_tables(&self) -> Result<impl Iterator<Item = String> + '_, Error> {
+        self.storage
+            .list_tables(TableType::Multimap, self.root_page)
+    }
 }
 
 impl<'a> Drop for ReadOnlyDatabaseTransaction<'a> {
@@ -425,7 +431,7 @@ mod test {
         let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
         let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
         let write_txn = db.begin_write().unwrap();
-        let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+        let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
         table.insert(b"hello", b"world").unwrap();
         let first_txn_id = write_txn.transaction_id;
         write_txn.commit().unwrap();
diff --git a/src/lib.rs b/src/lib.rs
index 9e3a716..6fa911e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,3 @@
-extern crate core;
-
 pub use db::{Database, DatabaseBuilder, DatabaseTransaction, ReadOnlyDatabaseTransaction};
 pub use error::Error;
 pub use multimap_table::{
diff --git a/src/multimap_table.rs b/src/multimap_table.rs
index d1b2dc5..f387fd6 100644
--- a/src/multimap_table.rs
+++ b/src/multimap_table.rs
@@ -359,6 +359,7 @@ impl<'s, K: RedbKey + ?Sized, V: RedbKey + ?Sized> MultimapTable<'s, K, V> {
         Ok(found)
     }
 
+    // TODO: maybe this should return all the removed values as an iter?
     pub fn remove_all(&mut self, key: &K) -> Result<bool, Error> {
         // Match only on the key, so that we can remove all the associated values
         let key_only = make_serialized_key_with_op(key, MultimapKeyCompareOp::KeyOnly);
@@ -397,7 +398,7 @@ impl<'s, K: RedbKey + ?Sized, V: RedbKey + ?Sized> ReadableMultimapTable<K, V>
             .map(MultimapValueIter::new)
     }
 
-    fn get_range<'a, T: RangeBounds<&'a K> + 'a>(
+    fn range<'a, T: RangeBounds<&'a K> + 'a>(
         &'a self,
         range: T,
     ) -> Result<MultimapGetRangeIterType<'a, K, V>, Error> {
@@ -412,7 +413,7 @@ impl<'s, K: RedbKey + ?Sized, V: RedbKey + ?Sized> ReadableMultimapTable<K, V>
             .map(MultimapRangeIter::new)
     }
 
-    fn get_range_reversed<'a, T: RangeBounds<&'a K> + 'a>(
+    fn range_reversed<'a, T: RangeBounds<&'a K> + 'a>(
         &'a self,
         range: T,
     ) -> Result<MultimapGetRangeIterType<'a, K, V>, Error> {
@@ -444,12 +445,12 @@ type MultimapGetRangeIterType<'a, K, V> =
 pub trait ReadableMultimapTable<K: RedbKey + ?Sized, V: RedbKey + ?Sized> {
     fn get<'a>(&'a self, key: &'a K) -> Result<MultimapGetIterType<'a, K, V>, Error>;
 
-    fn get_range<'a, T: RangeBounds<&'a K> + 'a>(
+    fn range<'a, T: RangeBounds<&'a K> + 'a>(
         &'a self,
         range: T,
     ) -> Result<MultimapGetRangeIterType<'a, K, V>, Error>;
 
-    fn get_range_reversed<'a, T: RangeBounds<&'a K> + 'a>(
+    fn range_reversed<'a, T: RangeBounds<&'a K> + 'a>(
         &'a self,
         range: T,
     ) -> Result<MultimapGetRangeIterType<'a, K, V>, Error>;
@@ -493,7 +494,7 @@ impl<'s, K: RedbKey + ?Sized, V: RedbKey + ?Sized> ReadableMultimapTable<K, V>
             .map(MultimapValueIter::new)
     }
 
-    fn get_range<'a, T: RangeBounds<&'a K> + 'a>(
+    fn range<'a, T: RangeBounds<&'a K> + 'a>(
         &'a self,
         range: T,
     ) -> Result<MultimapGetRangeIterType<'a, K, V>, Error> {
@@ -508,7 +509,7 @@ impl<'s, K: RedbKey + ?Sized, V: RedbKey + ?Sized> ReadableMultimapTable<K, V>
             .map(MultimapRangeIter::new)
     }
 
-    fn get_range_reversed<'a, T: RangeBounds<&'a K> + 'a>(
+    fn range_reversed<'a, T: RangeBounds<&'a K> + 'a>(
         &'a self,
         range: T,
     ) -> Result<MultimapGetRangeIterType<'a, K, V>, Error> {
diff --git a/src/table.rs b/src/table.rs
index 73383b8..c2a73df 100644
--- a/src/table.rs
+++ b/src/table.rs
@@ -76,6 +76,7 @@ impl<'s, K: RedbKey + ?Sized, V: RedbValue + ?Sized> Table<'s, K, V> {
         Ok(guard)
     }
 
+    // TODO: maybe this should return the removed value?
     pub fn remove(&mut self, key: &K) -> Result<bool, Error> {
         // Safety: No other references to this table can exist.
         // Tables can only be opened mutably in one location (see Error::TableAlreadyOpen),
@@ -98,7 +99,7 @@ impl<'s, K: RedbKey + ?Sized, V: RedbValue + ?Sized> ReadableTable<K, V> for Tab
             .get::<K, V>(key.as_bytes().as_ref(), self.table_root.get())
     }
 
-    fn get_range<'a, T: RangeBounds<KR> + 'a, KR: Borrow<K>>(
+    fn range<'a, T: RangeBounds<KR> + 'a, KR: Borrow<K>>(
         &'a self,
         range: T,
     ) -> Result<RangeIter<T, KR, K, V>, Error> {
@@ -107,7 +108,7 @@ impl<'s, K: RedbKey + ?Sized, V: RedbValue + ?Sized> ReadableTable<K, V> for Tab
             .map(RangeIter::new)
     }
 
-    fn get_range_reversed<'a, T: RangeBounds<KR> + 'a, KR: Borrow<K>>(
+    fn range_reversed<'a, T: RangeBounds<KR> + 'a, KR: Borrow<K>>(
         &'a self,
         range: T,
     ) -> Result<RangeIter<T, KR, K, V>, Error> {
@@ -128,12 +129,12 @@ impl<'s, K: RedbKey + ?Sized, V: RedbValue + ?Sized> ReadableTable<K, V> for Tab
 pub trait ReadableTable<K: RedbKey + ?Sized, V: RedbValue + ?Sized> {
     fn get(&self, key: &K) -> Result<Option<AccessGuard<V>>, Error>;
 
-    fn get_range<'a, T: RangeBounds<KR> + 'a, KR: Borrow<K>>(
+    fn range<'a, T: RangeBounds<KR> + 'a, KR: Borrow<K>>(
         &'a self,
         range: T,
     ) -> Result<RangeIter<T, KR, K, V>, Error>;
 
-    fn get_range_reversed<'a, T: RangeBounds<KR> + 'a, KR: Borrow<K>>(
+    fn range_reversed<'a, T: RangeBounds<KR> + 'a, KR: Borrow<K>>(
         &'a self,
         range: T,
     ) -> Result<RangeIter<T, KR, K, V>, Error>;
@@ -172,7 +173,7 @@ impl<'s, K: RedbKey + ?Sized, V: RedbValue + ?Sized> ReadableTable<K, V>
             .get::<K, V>(key.as_bytes().as_ref(), self.table_root)
     }
 
-    fn get_range<'a, T: RangeBounds<KR> + 'a, KR: Borrow<K>>(
+    fn range<'a, T: RangeBounds<KR> + 'a, KR: Borrow<K>>(
         &'a self,
         range: T,
     ) -> Result<RangeIter<T, KR, K, V>, Error> {
@@ -181,7 +182,7 @@ impl<'s, K: RedbKey + ?Sized, V: RedbValue + ?Sized> ReadableTable<K, V>
             .map(RangeIter::new)
     }
 
-    fn get_range_reversed<'a, T: RangeBounds<KR> + 'a, KR: Borrow<K>>(
+    fn range_reversed<'a, T: RangeBounds<KR> + 'a, KR: Borrow<K>>(
         &'a self,
         range: T,
     ) -> Result<RangeIter<T, KR, K, V>, Error> {
@@ -276,7 +277,7 @@ mod test {
         let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
         let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
         let write_txn = db.begin_write().unwrap();
-        let mut table: Table<ReverseKey, [u8]> = write_txn.open_table(b"x").unwrap();
+        let mut table: Table<ReverseKey, [u8]> = write_txn.open_table("x").unwrap();
         for i in 0..10u8 {
             let key = vec![i];
             table.insert(&ReverseKey(key), b"value").unwrap();
@@ -284,10 +285,10 @@ mod test {
         write_txn.commit().unwrap();
 
         let read_txn = db.begin_read().unwrap();
-        let table: ReadOnlyTable<ReverseKey, [u8]> = read_txn.open_table(b"x").unwrap();
+        let table: ReadOnlyTable<ReverseKey, [u8]> = read_txn.open_table("x").unwrap();
         let start = ReverseKey(vec![7u8]); // ReverseKey is used, so 7 < 3
         let end = ReverseKey(vec![3u8]);
-        let mut iter = table.get_range(start..=end).unwrap();
+        let mut iter = table.range(start..=end).unwrap();
         for i in (3..=7u8).rev() {
             let (key, value) = iter.next().unwrap();
             assert_eq!(&[i], key);
diff --git a/src/tree_store/btree_utils.rs b/src/tree_store/btree_utils.rs
index 3050964..f074d5f 100644
--- a/src/tree_store/btree_utils.rs
+++ b/src/tree_store/btree_utils.rs
@@ -1027,7 +1027,7 @@ mod test {
 
         let db = unsafe { Database::create(tmpfile.path(), 16 * 1024 * 1024).unwrap() };
         let txn = db.begin_write().unwrap();
-        let mut table: Table<[u8], [u8]> = txn.open_table(b"x").unwrap();
+        let mut table: Table<[u8], [u8]> = txn.open_table("x").unwrap();
 
         let elements = (BTREE_ORDER / 2).pow(2) as usize - num_internal_entries;
 
@@ -1048,7 +1048,7 @@ mod test {
         let reduce_to = BTREE_ORDER / 2 - num_internal_entries;
 
         let txn = db.begin_write().unwrap();
-        let mut table: Table<[u8], [u8]> = txn.open_table(b"x").unwrap();
+        let mut table: Table<[u8], [u8]> = txn.open_table("x").unwrap();
         for i in 0..(elements - reduce_to) {
             table.remove(&i.to_be_bytes()).unwrap();
         }
diff --git a/src/tree_store/page_store/page_manager.rs b/src/tree_store/page_store/page_manager.rs
index bdcc247..0d5bb9e 100644
--- a/src/tree_store/page_store/page_manager.rs
+++ b/src/tree_store/page_store/page_manager.rs
@@ -1032,7 +1032,7 @@ mod test {
         let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
         let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
         let write_txn = db.begin_write().unwrap();
-        let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+        let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
         table.insert(b"hello", b"world").unwrap();
         write_txn.commit().unwrap();
         drop(db);
@@ -1061,7 +1061,7 @@ mod test {
 
         let db2 = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
         let read_txn = db2.begin_read().unwrap();
-        let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+        let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
         assert_eq!(b"world", table.get(b"hello").unwrap().unwrap().to_value());
     }
 
@@ -1070,7 +1070,7 @@ mod test {
         let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
         let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
         let write_txn = db.begin_write().unwrap();
-        let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+        let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
         table.insert(b"hello", b"world").unwrap();
         write_txn.commit().unwrap();
         let free_pages = db.stats().unwrap().free_pages();
@@ -1101,7 +1101,7 @@ mod test {
         let db2 = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
         assert_eq!(free_pages, db2.stats().unwrap().free_pages());
         let write_txn = db2.begin_write().unwrap();
-        let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+        let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
         table.insert(b"hello2", b"world2").unwrap();
         write_txn.commit().unwrap();
     }
diff --git a/src/tree_store/storage.rs b/src/tree_store/storage.rs
index 4ce9666..7071d97 100644
--- a/src/tree_store/storage.rs
+++ b/src/tree_store/storage.rs
@@ -22,7 +22,7 @@ use std::panic;
 
 // The table of freed pages by transaction. (transaction id, pagination counter) -> binary.
 // The binary blob is a length-prefixed array of big endian PageNumber
-pub(crate) const FREED_TABLE: &[u8] = b"$$internal$$freed";
+pub(crate) const FREED_TABLE: &str = "$$internal$$freed";
 
 #[derive(Debug)]
 pub struct DbStats {
@@ -117,6 +117,27 @@ impl TableDefinition {
     }
 }
 
+pub struct TableNameIter<'a> {
+    inner: BtreeRangeIter<'a, RangeFull, &'static str, str, [u8]>,
+    table_type: TableType,
+}
+
+impl<'a> Iterator for TableNameIter<'a> {
+    type Item = String;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        while let Some(entry) = self.inner.next() {
+            if str::from_bytes(entry.key()) == FREED_TABLE {
+                continue;
+            }
+            if TableDefinition::from_bytes(entry.value()).table_type == self.table_type {
+                return Some(str::from_bytes(entry.key()).to_string());
+            }
+        }
+        None
+    }
+}
+
 pub(in crate) struct Storage {
     mem: TransactionalMemory,
     next_transaction_id: Cell<u128>,
@@ -164,7 +185,8 @@ impl Storage {
                 table_root: None,
                 table_type: TableType::Normal,
             };
-            let (new_root, _) = make_mut_single_leaf(FREED_TABLE, &freed_table.to_bytes(), &mem)?;
+            let (new_root, _) =
+                make_mut_single_leaf(FREED_TABLE.as_bytes(), &freed_table.to_bytes(), &mem)?;
             mem.set_secondary_root_page(new_root)?;
             mem.commit(next_transaction_id)?;
             next_transaction_id += 1;
@@ -212,7 +234,7 @@ impl Storage {
 
     pub(in crate) fn update_table_root(
         &self,
-        name: &[u8],
+        name: &str,
         table_type: TableType,
         table_root: Option<PageNumber>,
         transaction_id: u128,
@@ -223,17 +245,37 @@ impl Storage {
             table_type,
         };
         // Safety: References into the master table are never returned to the user
-        unsafe { self.insert::<[u8]>(name, &definition.to_bytes(), transaction_id, master_root) }
+        unsafe {
+            self.insert::<str>(
+                name.as_bytes(),
+                &definition.to_bytes(),
+                transaction_id,
+                master_root,
+            )
+        }
+    }
+
+    // root_page: the root of the master table
+    pub(in crate) fn list_tables(
+        &self,
+        table_type: TableType,
+        master_root_page: Option<PageNumber>,
+    ) -> Result<TableNameIter, Error> {
+        let iter = self.get_range(.., master_root_page)?;
+        Ok(TableNameIter {
+            inner: iter,
+            table_type,
+        })
     }
 
     // root_page: the root of the master table
     pub(in crate) fn get_table(
         &self,
-        name: &[u8],
+        name: &str,
         table_type: TableType,
         root_page: Option<PageNumber>,
     ) -> Result<Option<TableDefinition>, Error> {
-        if let Some(found) = self.get::<[u8], [u8]>(name, root_page)? {
+        if let Some(found) = self.get::<str, [u8]>(name.as_bytes(), root_page)? {
             let definition = TableDefinition::from_bytes(found.to_value());
             if definition.get_type() != table_type {
                 return Err(Error::TableTypeMismatch(format!(
@@ -251,7 +293,7 @@ impl Storage {
     // root_page: the root of the master table
     pub(in crate) fn delete_table(
         &self,
-        name: &[u8],
+        name: &str,
         table_type: TableType,
         transaction_id: u128,
         root_page: Option<PageNumber>,
@@ -268,7 +310,7 @@ impl Storage {
 
             // Safety: References into the master table are never returned to the user
             let (new_root, found) =
-                unsafe { self.remove::<[u8]>(name, transaction_id, root_page)? };
+                unsafe { self.remove::<str>(name.as_bytes(), transaction_id, root_page)? };
             return Ok((new_root, found));
         }
 
@@ -279,7 +321,7 @@ impl Storage {
     // root_page: the root of the master table
     pub(in crate) fn get_or_create_table(
         &self,
-        name: &[u8],
+        name: &str,
         table_type: TableType,
         transaction_id: u128,
         root_page: Option<PageNumber>,
@@ -294,7 +336,12 @@ impl Storage {
         };
         // Safety: References into the master table are never returned to the user
         let new_root = unsafe {
-            self.insert::<[u8]>(name, &definition.to_bytes(), transaction_id, root_page)?
+            self.insert::<str>(
+                name.as_bytes(),
+                &definition.to_bytes(),
+                transaction_id,
+                root_page,
+            )?
         };
         Ok((definition, new_root))
     }
@@ -467,8 +514,8 @@ impl Storage {
         }
         // Safety: References into the master table are never returned to the user
         unsafe {
-            master_root = Some(self.insert::<[u8]>(
-                FREED_TABLE,
+            master_root = Some(self.insert::<str>(
+                FREED_TABLE.as_bytes(),
                 &freed_table.to_bytes(),
                 transaction_id,
                 master_root,
@@ -514,8 +561,8 @@ impl Storage {
                 // iteration, but that's ok since it would have very few pages to process)
                 // Safety: References into the master table are never returned to the user
                 unsafe {
-                    master_root = Some(self.insert::<[u8]>(
-                        FREED_TABLE,
+                    master_root = Some(self.insert::<str>(
+                        FREED_TABLE.as_bytes(),
                         &freed_table.to_bytes(),
                         transaction_id,
                         master_root,
@@ -715,6 +762,7 @@ impl<'a, V: RedbValue + ?Sized> AccessGuard<'a, V> {
         }
     }
 
+    // TODO: implement Deref instead of this to_value() method, when GAT is stable
     pub fn to_value(&self) -> <<V as RedbValue>::View as WithLifetime>::Out {
         V::from_bytes(&self.page.memory()[self.offset..(self.offset + self.len)])
     }
diff --git a/src/types.rs b/src/types.rs
index a022d13..7cba12f 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -74,6 +74,27 @@ impl RedbKey for [u8] {
     }
 }
 
+impl RedbValue for str {
+    type View = RefLifetime<str>;
+    type ToBytes = RefAsBytesLifetime<str>;
+
+    fn from_bytes(data: &[u8]) -> <Self::View as WithLifetime>::Out {
+        std::str::from_utf8(data).unwrap()
+    }
+
+    fn as_bytes(&self) -> <Self::ToBytes as AsBytesWithLifetime>::Out {
+        self
+    }
+}
+
+impl RedbKey for str {
+    fn compare(data1: &[u8], data2: &[u8]) -> Ordering {
+        let str1 = str::from_bytes(data1);
+        let str2 = str::from_bytes(data2);
+        str1.cmp(str2)
+    }
+}
+
 macro_rules! be_value {
     ($t:ty) => {
         impl RedbValue for $t {


Test patch:
diff --git a/tests/basic_tests.rs b/tests/basic_tests.rs
index 610ec02..c8cdd99 100644
--- a/tests/basic_tests.rs
+++ b/tests/basic_tests.rs
@@ -1,4 +1,4 @@
-use redb::{Database, Error, RangeIter, ReadOnlyTable, ReadableTable, Table};
+use redb::{Database, Error, MultimapTable, RangeIter, ReadOnlyTable, ReadableTable, Table};
 use std::ops::{Range, RangeFull};
 use tempfile::NamedTempFile;
 
@@ -7,14 +7,14 @@ fn len() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     table.insert(b"hello", b"world").unwrap();
     table.insert(b"hello2", b"world2").unwrap();
     table.insert(b"hi", b"world").unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
     assert_eq!(table.len().unwrap(), 3);
 }
 
@@ -23,7 +23,7 @@ fn create_open() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<u64, u64> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<u64, u64> = write_txn.open_table("x").unwrap();
     table.insert(&0, &1).unwrap();
     write_txn.commit().unwrap();
     drop(db);
@@ -31,7 +31,7 @@ fn create_open() {
     let db2 = unsafe { Database::open(tmpfile.path()).unwrap() };
 
     let read_txn = db2.begin_read().unwrap();
-    let table: ReadOnlyTable<u64, u64> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<u64, u64> = read_txn.open_table("x").unwrap();
     assert_eq!(1, table.get(&0).unwrap().unwrap().to_value());
 }
 
@@ -40,34 +40,58 @@ fn multiple_tables() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"1").unwrap();
-    let mut table2: Table<[u8], [u8]> = write_txn.open_table(b"2").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("1").unwrap();
+    let mut table2: Table<[u8], [u8]> = write_txn.open_table("2").unwrap();
 
     table.insert(b"hello", b"world").unwrap();
     table2.insert(b"hello", b"world2").unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"1").unwrap();
-    let table2: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"2").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("1").unwrap();
+    let table2: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("2").unwrap();
     assert_eq!(table.len().unwrap(), 1);
     assert_eq!(b"world", table.get(b"hello").unwrap().unwrap().to_value());
     assert_eq!(table2.len().unwrap(), 1);
     assert_eq!(b"world2", table2.get(b"hello").unwrap().unwrap().to_value());
 }
 
+#[test]
+fn list_tables() {
+    let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
+    let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
+
+    let write_txn = db.begin_write().unwrap();
+    let _: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
+    let _: Table<[u8], [u8]> = write_txn.open_table("y").unwrap();
+    let _: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table("mx").unwrap();
+    let _: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table("my").unwrap();
+
+    let tables: Vec<String> = write_txn.list_tables().unwrap().collect();
+    let multimap_tables: Vec<String> = write_txn.list_multimap_tables().unwrap().collect();
+    assert_eq!(tables, &["x", "y"]);
+    assert_eq!(multimap_tables, &["mx", "my"]);
+    write_txn.commit().unwrap();
+
+    let read_txn = db.begin_read().unwrap();
+    let tables: Vec<String> = read_txn.list_tables().unwrap().collect();
+    let multimap_tables: Vec<String> = read_txn.list_multimap_tables().unwrap().collect();
+    assert_eq!(tables, &["x", "y"]);
+    assert_eq!(multimap_tables, &["mx", "my"]);
+}
+
 #[test]
 fn is_empty() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
 
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     table.insert(b"hello", b"world").unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
     assert!(!table.is_empty().unwrap());
 }
 
@@ -77,22 +101,22 @@ fn abort() {
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
 
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     table.insert(b"hello", b"aborted").unwrap();
     assert_eq!(b"aborted", table.get(b"hello").unwrap().unwrap().to_value());
     write_txn.abort().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: Result<ReadOnlyTable<[u8], [u8]>, Error> = read_txn.open_table(b"x");
+    let table: Result<ReadOnlyTable<[u8], [u8]>, Error> = read_txn.open_table("x");
     assert!(table.is_err());
 
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     table.insert(b"hello", b"world").unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
     assert_eq!(b"world", table.get(b"hello").unwrap().unwrap().to_value());
     assert_eq!(table.len().unwrap(), 1);
 }
@@ -102,21 +126,21 @@ fn insert_overwrite() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     table.insert(b"hello", b"world").unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
     assert_eq!(b"world", table.get(b"hello").unwrap().unwrap().to_value());
 
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     table.insert(b"hello", b"replaced").unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
     assert_eq!(
         b"replaced",
         table.get(b"hello").unwrap().unwrap().to_value()
@@ -128,7 +152,7 @@ fn insert_reserve() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     let value = b"world";
     let mut reserved = table.insert_reserve(b"hello", value.len()).unwrap();
     reserved.as_mut().copy_from_slice(value);
@@ -136,7 +160,7 @@ fn insert_reserve() {
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
     assert_eq!(value, table.get(b"hello").unwrap().unwrap().to_value());
 }
 
@@ -145,23 +169,23 @@ fn delete() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     table.insert(b"hello", b"world").unwrap();
     table.insert(b"hello2", b"world").unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
     assert_eq!(b"world", table.get(b"hello").unwrap().unwrap().to_value());
     assert_eq!(table.len().unwrap(), 2);
 
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     table.remove(b"hello").unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
     assert!(table.get(b"hello").unwrap().is_none());
     assert_eq!(table.len().unwrap(), 1);
 }
@@ -171,16 +195,16 @@ fn no_dirty_reads() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     table.insert(b"hello", b"world").unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: Result<ReadOnlyTable<[u8], [u8]>, Error> = read_txn.open_table(b"x");
+    let table: Result<ReadOnlyTable<[u8], [u8]>, Error> = read_txn.open_table("x");
     assert!(table.is_err());
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
     assert_eq!(b"world", table.get(b"hello").unwrap().unwrap().to_value());
 }
 
@@ -189,23 +213,23 @@ fn read_isolation() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     table.insert(b"hello", b"world").unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
     assert_eq!(b"world", table.get(b"hello").unwrap().unwrap().to_value());
 
     let write_txn = db.begin_write().unwrap();
-    let mut write_table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut write_table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     write_table.remove(b"hello").unwrap();
     write_table.insert(b"hello2", b"world2").unwrap();
     write_table.insert(b"hello3", b"world3").unwrap();
     write_txn.commit().unwrap();
 
     let read_txn2 = db.begin_read().unwrap();
-    let table2: ReadOnlyTable<[u8], [u8]> = read_txn2.open_table(b"x").unwrap();
+    let table2: ReadOnlyTable<[u8], [u8]> = read_txn2.open_table("x").unwrap();
     assert!(table2.get(b"hello").unwrap().is_none());
     assert_eq!(
         b"world2",
@@ -228,12 +252,12 @@ fn u64_type() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<u64, u64> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<u64, u64> = write_txn.open_table("x").unwrap();
     table.insert(&0, &1).unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<u64, u64> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<u64, u64> = read_txn.open_table("x").unwrap();
     assert_eq!(1, table.get(&0).unwrap().unwrap().to_value());
 }
 
@@ -242,16 +266,16 @@ fn i128_type() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<i128, i128> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<i128, i128> = write_txn.open_table("x").unwrap();
     for i in -10..=10 {
         table.insert(&i, &(i - 1)).unwrap();
     }
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<i128, i128> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<i128, i128> = read_txn.open_table("x").unwrap();
     assert_eq!(-2, table.get(&-1).unwrap().unwrap().to_value());
-    let mut iter: RangeIter<RangeFull, i128, i128, i128> = table.get_range(..).unwrap();
+    let mut iter: RangeIter<RangeFull, i128, i128, i128> = table.range(..).unwrap();
     for i in -11..10 {
         assert_eq!(iter.next().unwrap().1, i);
     }
@@ -263,42 +287,70 @@ fn f32_type() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<u8, f32> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<u8, f32> = write_txn.open_table("x").unwrap();
     table.insert(&0, &0.3).unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<u8, f32> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<u8, f32> = read_txn.open_table("x").unwrap();
     assert_eq!(0.3, table.get(&0).unwrap().unwrap().to_value());
 }
 
+#[test]
+fn str_type() {
+    let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
+    let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
+    let write_txn = db.begin_write().unwrap();
+    let mut table: Table<str, str> = write_txn.open_table("x").unwrap();
+    table.insert("hello", "world").unwrap();
+    write_txn.commit().unwrap();
+
+    let read_txn = db.begin_read().unwrap();
+    let table: ReadOnlyTable<str, str> = read_txn.open_table("x").unwrap();
+    let hello = "hello".to_string();
+    assert_eq!("world", table.get(&hello).unwrap().unwrap().to_value());
+
+    let mut iter: RangeIter<RangeFull, &str, str, str> = table.range(..).unwrap();
+    assert_eq!(iter.next().unwrap().1, "world");
+    assert!(iter.next().is_none());
+
+    let mut iter: RangeIter<Range<String>, String, str, str> =
+        table.range("a".to_string().."z".to_string()).unwrap();
+    assert_eq!(iter.next().unwrap().1, "world");
+    assert!(iter.next().is_none());
+
+    let mut iter: RangeIter<Range<&str>, &str, str, str> = table.range("a".."z").unwrap();
+    assert_eq!(iter.next().unwrap().1, "world");
+    assert!(iter.next().is_none());
+}
+
 #[test]
 fn owned_get_signatures() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<u32, u32> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<u32, u32> = write_txn.open_table("x").unwrap();
     for i in 0..10 {
         table.insert(&i, &(i + 1)).unwrap();
     }
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<u32, u32> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<u32, u32> = read_txn.open_table("x").unwrap();
 
     assert_eq!(2, table.get(&1).unwrap().unwrap().to_value());
 
-    let mut iter: RangeIter<RangeFull, u32, u32, u32> = table.get_range(..).unwrap();
+    let mut iter: RangeIter<RangeFull, u32, u32, u32> = table.range(..).unwrap();
     for i in 0..10 {
         assert_eq!(iter.next().unwrap().1, i + 1);
     }
     assert!(iter.next().is_none());
-    let mut iter: RangeIter<Range<u32>, u32, u32, u32> = table.get_range(0..10).unwrap();
+    let mut iter: RangeIter<Range<u32>, u32, u32, u32> = table.range(0..10).unwrap();
     for i in 0..10 {
         assert_eq!(iter.next().unwrap().1, i + 1);
     }
     assert!(iter.next().is_none());
-    let mut iter: RangeIter<Range<&u32>, u32, u32, u32> = table.get_range(&0..&10).unwrap();
+    let mut iter: RangeIter<Range<&u32>, u32, u32, u32> = table.range(&0..&10).unwrap();
     for i in 0..10 {
         assert_eq!(iter.next().unwrap().1, i + 1);
     }
@@ -310,14 +362,14 @@ fn ref_get_signatures() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     for i in 0..10 {
         table.insert(&[i], &[i + 1]).unwrap();
     }
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
 
     let zero = vec![0u8];
     assert_eq!(&[1], table.get(&[0]).unwrap().unwrap().to_value());
@@ -326,28 +378,26 @@ fn ref_get_signatures() {
 
     let start = vec![0u8];
     let end = vec![10u8];
-    let mut iter: RangeIter<RangeFull, &[u8], [u8], [u8]> = table.get_range(..).unwrap();
+    let mut iter: RangeIter<RangeFull, &[u8], [u8], [u8]> = table.range(..).unwrap();
     for i in 0..10 {
         assert_eq!(iter.next().unwrap().1, &[i + 1]);
     }
     assert!(iter.next().is_none());
 
     let mut iter: RangeIter<Range<&[u8]>, &[u8], [u8], [u8]> =
-        table.get_range(start.as_slice()..&end).unwrap();
+        table.range(start.as_slice()..&end).unwrap();
     for i in 0..10 {
         assert_eq!(iter.next().unwrap().1, &[i + 1]);
     }
     assert!(iter.next().is_none());
 
-    let mut iter = table.get_range(start..end).unwrap();
+    let mut iter = table.range(start..end).unwrap();
     for i in 0..10 {
         assert_eq!(iter.next().unwrap().1, &[i + 1]);
     }
     assert!(iter.next().is_none());
 
-    let mut iter = table
-        .get_range([0u8].as_slice()..[10u8].as_slice())
-        .unwrap();
+    let mut iter = table.range([0u8].as_slice()..[10u8].as_slice()).unwrap();
     for i in 0..10 {
         assert_eq!(iter.next().unwrap().1, &[i + 1]);
     }
diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs
index 4c3e5b3..6cfb850 100644
--- a/tests/integration_tests.rs
+++ b/tests/integration_tests.rs
@@ -29,7 +29,7 @@ fn mixed_durable_commit() {
     let db_size = 129 * 4096;
     let db = unsafe { Database::create(tmpfile.path(), db_size).unwrap() };
     let txn = db.begin_write().unwrap();
-    let mut table: Table<u64, u64> = txn.open_table(b"x").unwrap();
+    let mut table: Table<u64, u64> = txn.open_table("x").unwrap();
 
     table.insert(&0, &0).unwrap();
     txn.non_durable_commit().unwrap();
@@ -45,7 +45,7 @@ fn non_durable_commit_persistence() {
     let db_size = 16 * 1024 * 1024;
     let db = unsafe { Database::create(tmpfile.path(), db_size).unwrap() };
     let txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = txn.open_table("x").unwrap();
 
     let pairs = gen_data(100, 16, 20);
 
@@ -61,7 +61,7 @@ fn non_durable_commit_persistence() {
     drop(db);
     let db = unsafe { Database::create(tmpfile.path(), db_size).unwrap() };
     let txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = txn.open_table("x").unwrap();
 
     let mut key_order: Vec<usize> = (0..ELEMENTS).collect();
     key_order.shuffle(&mut rand::thread_rng());
@@ -82,7 +82,7 @@ fn persistence() {
     let db_size = 16 * 1024 * 1024;
     let db = unsafe { Database::create(tmpfile.path(), db_size).unwrap() };
     let txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = txn.open_table("x").unwrap();
 
     let pairs = gen_data(100, 16, 20);
 
@@ -97,7 +97,7 @@ fn persistence() {
     drop(db);
     let db = unsafe { Database::create(tmpfile.path(), db_size).unwrap() };
     let txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = txn.open_table("x").unwrap();
 
     let mut key_order: Vec<usize> = (0..ELEMENTS).collect();
     key_order.shuffle(&mut rand::thread_rng());
@@ -132,7 +132,7 @@ fn resize_db() {
     let mut i = 0u64;
     loop {
         let txn = db.begin_write().unwrap();
-        let mut table: Table<u64, u64> = txn.open_table(b"x").unwrap();
+        let mut table: Table<u64, u64> = txn.open_table("x").unwrap();
         // Fill the database
         match table.insert(&i, &i) {
             Ok(_) => {}
@@ -155,7 +155,7 @@ fn resize_db() {
     }
 
     let txn = db.begin_write().unwrap();
-    let mut table: Table<u64, u64> = txn.open_table(b"x").unwrap();
+    let mut table: Table<u64, u64> = txn.open_table("x").unwrap();
     let mut found = false;
     for _ in 0..999 {
         if matches!(table.insert(&i, &i), Err(Error::OutOfSpace)) {
@@ -174,7 +174,7 @@ fn resize_db() {
 
     let db = unsafe { Database::create(tmpfile.path(), db_size * 2).unwrap() };
     let txn = db.begin_write().unwrap();
-    let mut table: Table<u64, u64> = txn.open_table(b"x").unwrap();
+    let mut table: Table<u64, u64> = txn.open_table("x").unwrap();
     assert!(table.insert(&i, &i).is_ok());
     txn.commit().unwrap();
 }
@@ -186,14 +186,14 @@ fn free() {
     let db_size = 8 * 1024 * 1024;
     let db = unsafe { Database::create(tmpfile.path(), db_size).unwrap() };
     let txn = db.begin_write().unwrap();
-    let _table: Table<[u8], [u8]> = txn.open_table(b"x").unwrap();
-    let _table: Table<[u8], [u8]> = txn.open_table(b"y").unwrap();
+    let _table: Table<[u8], [u8]> = txn.open_table("x").unwrap();
+    let _table: Table<[u8], [u8]> = txn.open_table("y").unwrap();
 
     txn.commit().unwrap();
     let free_pages = db.stats().unwrap().free_pages();
 
     let txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = txn.open_table("x").unwrap();
 
     let key = vec![0; 100];
     let value = vec![0; 1024];
@@ -216,7 +216,7 @@ fn free() {
         // Delete in chunks to be sure that we don't run out of pages due to temp allocations
         for chunk in key_range.chunks(10) {
             let txn = db.begin_write().unwrap();
-            let mut table: Table<[u8], [u8]> = txn.open_table(b"x").unwrap();
+            let mut table: Table<[u8], [u8]> = txn.open_table("x").unwrap();
             for i in chunk {
                 let mut mut_key = key.clone();
                 mut_key.extend_from_slice(&(*i as u64).to_be_bytes());
@@ -236,7 +236,7 @@ fn large_keys() {
     let db_size = 1024_1024;
     let db = unsafe { Database::create(tmpfile.path(), db_size).unwrap() };
     let txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = txn.open_table("x").unwrap();
 
     let mut key = vec![0; 1024];
     let value = vec![0; 1];
@@ -249,7 +249,7 @@ fn large_keys() {
     txn.commit().unwrap();
 
     let txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = txn.open_table("x").unwrap();
     {
         for i in 0..100 {
             key[0] = i;
@@ -269,11 +269,11 @@ fn multi_page_kv() {
     let db = unsafe {
         DatabaseBuilder::new()
             .set_page_size(page_size)
-            .open(tmpfile.path(), db_size)
+            .create(tmpfile.path(), db_size)
             .unwrap()
     };
     let txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = txn.open_table("x").unwrap();
 
     let mut key = vec![0; page_size + 1];
     let mut value = vec![0; page_size + 1];
@@ -287,7 +287,7 @@ fn multi_page_kv() {
     txn.commit().unwrap();
 
     let txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = txn.open_table("x").unwrap();
     for i in 0..elements {
         key[0] = i;
         value[0] = i;
@@ -295,7 +295,7 @@ fn multi_page_kv() {
     }
 
     let txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = txn.open_table("x").unwrap();
     {
         for i in 0..elements {
             key[0] = i;
@@ -313,43 +313,43 @@ fn regression() {
     let db_size = 1024 * 1024;
     let db = unsafe { Database::create(tmpfile.path(), db_size).unwrap() };
     let txn = db.begin_write().unwrap();
-    let mut table: Table<u64, u64> = txn.open_table(b"x").unwrap();
+    let mut table: Table<u64, u64> = txn.open_table("x").unwrap();
 
     table.insert(&1, &1).unwrap();
     txn.commit().unwrap();
 
     let txn = db.begin_write().unwrap();
-    let mut table: Table<u64, u64> = txn.open_table(b"x").unwrap();
+    let mut table: Table<u64, u64> = txn.open_table("x").unwrap();
     table.insert(&6, &9).unwrap();
     txn.commit().unwrap();
 
     let txn = db.begin_write().unwrap();
-    let mut table: Table<u64, u64> = txn.open_table(b"x").unwrap();
+    let mut table: Table<u64, u64> = txn.open_table("x").unwrap();
     table.insert(&12, &10).unwrap();
     txn.commit().unwrap();
 
     let txn = db.begin_write().unwrap();
-    let mut table: Table<u64, u64> = txn.open_table(b"x").unwrap();
+    let mut table: Table<u64, u64> = txn.open_table("x").unwrap();
     table.insert(&18, &27).unwrap();
     txn.commit().unwrap();
 
     let txn = db.begin_write().unwrap();
-    let mut table: Table<u64, u64> = txn.open_table(b"x").unwrap();
+    let mut table: Table<u64, u64> = txn.open_table("x").unwrap();
     table.insert(&24, &33).unwrap();
     txn.commit().unwrap();
 
     let txn = db.begin_write().unwrap();
-    let mut table: Table<u64, u64> = txn.open_table(b"x").unwrap();
+    let mut table: Table<u64, u64> = txn.open_table("x").unwrap();
     table.insert(&30, &14).unwrap();
     txn.commit().unwrap();
 
     let txn = db.begin_write().unwrap();
-    let mut table: Table<u64, u64> = txn.open_table(b"x").unwrap();
+    let mut table: Table<u64, u64> = txn.open_table("x").unwrap();
     table.remove(&30).unwrap();
     txn.commit().unwrap();
 
     let txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<u64, u64> = txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<u64, u64> = txn.open_table("x").unwrap();
     let v = table.get(&6).unwrap().unwrap().to_value();
     assert_eq!(v, 9);
 }
@@ -397,27 +397,27 @@ fn non_durable_read_isolation() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
 
     table.insert(b"hello", b"world").unwrap();
     write_txn.non_durable_commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let read_table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+    let read_table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
     assert_eq!(
         b"world",
         read_table.get(b"hello").unwrap().unwrap().to_value()
     );
 
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     table.remove(b"hello").unwrap();
     table.insert(b"hello2", b"world2").unwrap();
     table.insert(b"hello3", b"world3").unwrap();
     write_txn.non_durable_commit().unwrap();
 
     let read_txn2 = db.begin_read().unwrap();
-    let read_table2: ReadOnlyTable<[u8], [u8]> = read_txn2.open_table(b"x").unwrap();
+    let read_table2: ReadOnlyTable<[u8], [u8]> = read_txn2.open_table("x").unwrap();
     assert!(read_table2.get(b"hello").unwrap().is_none());
     assert_eq!(
         b"world2",
@@ -443,7 +443,7 @@ fn range_query() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     for i in 0..10u8 {
         let key = vec![i];
         table.insert(&key, b"value").unwrap();
@@ -451,10 +451,10 @@ fn range_query() {
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
     let start = vec![3u8];
     let end = vec![7u8];
-    let mut iter = table.get_range(start..end).unwrap();
+    let mut iter = table.range(start..end).unwrap();
     for i in 3..7u8 {
         let (key, value) = iter.next().unwrap();
         assert_eq!(&[i], key);
@@ -468,7 +468,7 @@ fn range_query_reversed() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     for i in 0..10u8 {
         let key = vec![i];
         table.insert(&key, b"value").unwrap();
@@ -476,11 +476,11 @@ fn range_query_reversed() {
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
     let start = vec![3u8];
     let end = vec![7u8];
     let mut iter = table
-        .get_range_reversed(start.as_slice()..end.as_slice())
+        .range_reversed(start.as_slice()..end.as_slice())
         .unwrap();
     for i in (3..7u8).rev() {
         let (key, value) = iter.next().unwrap();
@@ -496,8 +496,8 @@ fn alias_table() {
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
 
     let write_txn = db.begin_write().unwrap();
-    let table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
-    let result: Result<Table<[u8], [u8]>, Error> = write_txn.open_table(b"x");
+    let table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
+    let result: Result<Table<[u8], [u8]>, Error> = write_txn.open_table("x");
     assert!(matches!(
         result.err().unwrap(),
         Error::TableAlreadyOpen(_, _)
@@ -511,24 +511,24 @@ fn delete_table() {
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
 
     let write_txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = write_txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = write_txn.open_table("x").unwrap();
     table.insert(b"hello", b"world").unwrap();
-    let mut multitable: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table(b"y").unwrap();
+    let mut multitable: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table("y").unwrap();
     multitable.insert(b"hello2", b"world2").unwrap();
     write_txn.commit().unwrap();
 
     let write_txn = db.begin_write().unwrap();
-    assert!(write_txn.delete_table(b"x").unwrap());
-    assert!(!write_txn.delete_table(b"x").unwrap());
-    assert!(write_txn.delete_multimap_table(b"y").unwrap());
-    assert!(!write_txn.delete_multimap_table(b"y").unwrap());
+    assert!(write_txn.delete_table("x").unwrap());
+    assert!(!write_txn.delete_table("x").unwrap());
+    assert!(write_txn.delete_multimap_table("y").unwrap());
+    assert!(!write_txn.delete_multimap_table("y").unwrap());
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let result: Result<ReadOnlyTable<[u8], [u8]>, Error> = read_txn.open_table(b"x");
+    let result: Result<ReadOnlyTable<[u8], [u8]>, Error> = read_txn.open_table("x");
     assert!(result.is_err());
     let result: Result<ReadOnlyMultimapTable<[u8], [u8]>, Error> =
-        read_txn.open_multimap_table(b"y");
+        read_txn.open_multimap_table("y");
     assert!(result.is_err());
 }
 
@@ -554,7 +554,7 @@ fn non_page_size_multiple() {
     let db_size = 1024 * 1024 + 1;
     let db = unsafe { Database::create(tmpfile.path(), db_size).unwrap() };
     let txn = db.begin_write().unwrap();
-    let mut table: Table<[u8], [u8]> = txn.open_table(b"x").unwrap();
+    let mut table: Table<[u8], [u8]> = txn.open_table("x").unwrap();
 
     let key = vec![0; 1024];
     let value = vec![0; 1];
@@ -562,6 +562,6 @@ fn non_page_size_multiple() {
     txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table(b"x").unwrap();
+    let table: ReadOnlyTable<[u8], [u8]> = read_txn.open_table("x").unwrap();
     assert_eq!(table.len().unwrap(), 1);
 }
diff --git a/tests/multimap_tests.rs b/tests/multimap_tests.rs
index 4a63c54..e9553f8 100644
--- a/tests/multimap_tests.rs
+++ b/tests/multimap_tests.rs
@@ -19,7 +19,7 @@ fn len() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table(b"x").unwrap();
+    let mut table: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table("x").unwrap();
 
     table.insert(b"hello", b"world").unwrap();
     table.insert(b"hello", b"world2").unwrap();
@@ -27,7 +27,7 @@ fn len() {
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyMultimapTable<[u8], [u8]> = read_txn.open_multimap_table(b"x").unwrap();
+    let table: ReadOnlyMultimapTable<[u8], [u8]> = read_txn.open_multimap_table("x").unwrap();
     assert_eq!(table.len().unwrap(), 3);
 }
 
@@ -37,12 +37,12 @@ fn is_empty() {
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
 
     let write_txn = db.begin_write().unwrap();
-    let mut table: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table(b"x").unwrap();
+    let mut table: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table("x").unwrap();
     table.insert(b"hello", b"world").unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyMultimapTable<[u8], [u8]> = read_txn.open_multimap_table(b"x").unwrap();
+    let table: ReadOnlyMultimapTable<[u8], [u8]> = read_txn.open_multimap_table("x").unwrap();
     assert!(!table.is_empty().unwrap());
 }
 
@@ -51,13 +51,13 @@ fn insert() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table(b"x").unwrap();
+    let mut table: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table("x").unwrap();
     table.insert(b"hello", b"world").unwrap();
     table.insert(b"hello", b"world2").unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyMultimapTable<[u8], [u8]> = read_txn.open_multimap_table(b"x").unwrap();
+    let table: ReadOnlyMultimapTable<[u8], [u8]> = read_txn.open_multimap_table("x").unwrap();
     assert_eq!(
         vec![b"world".to_vec(), b"world2".to_vec()],
         get_vec(&table, b"hello")
@@ -70,7 +70,7 @@ fn range_query() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table(b"x").unwrap();
+    let mut table: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table("x").unwrap();
     for i in 0..5u8 {
         let value = vec![i];
         table.insert(b"0", &value).unwrap();
@@ -86,10 +86,10 @@ fn range_query() {
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyMultimapTable<[u8], [u8]> = read_txn.open_multimap_table(b"x").unwrap();
+    let table: ReadOnlyMultimapTable<[u8], [u8]> = read_txn.open_multimap_table("x").unwrap();
     let start = b"0".as_ref();
     let end = b"1".as_ref();
-    let mut iter = table.get_range(start..=end).unwrap();
+    let mut iter = table.range(start..=end).unwrap();
     for i in 0..10u8 {
         let (key, value) = iter.next().unwrap();
         if i < 5 {
@@ -107,13 +107,13 @@ fn delete() {
     let tmpfile: NamedTempFile = NamedTempFile::new().unwrap();
     let db = unsafe { Database::create(tmpfile.path(), 1024 * 1024).unwrap() };
     let write_txn = db.begin_write().unwrap();
-    let mut table: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table(b"x").unwrap();
+    let mut table: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table("x").unwrap();
     table.insert(b"hello", b"world").unwrap();
     table.insert(b"hello", b"world2").unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyMultimapTable<[u8], [u8]> = read_txn.open_multimap_table(b"x").unwrap();
+    let table: ReadOnlyMultimapTable<[u8], [u8]> = read_txn.open_multimap_table("x").unwrap();
     assert_eq!(
         vec![b"world".to_vec(), b"world2".to_vec()],
         get_vec(&table, b"hello")
@@ -121,22 +121,22 @@ fn delete() {
     assert_eq!(table.len().unwrap(), 2);
 
     let write_txn = db.begin_write().unwrap();
-    let mut table: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table(b"x").unwrap();
+    let mut table: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table("x").unwrap();
     table.remove(b"hello", b"world2").unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyMultimapTable<[u8], [u8]> = read_txn.open_multimap_table(b"x").unwrap();
+    let table: ReadOnlyMultimapTable<[u8], [u8]> = read_txn.open_multimap_table("x").unwrap();
     assert_eq!(vec![b"world".to_vec()], get_vec(&table, b"hello"));
     assert_eq!(table.len().unwrap(), 1);
 
     let write_txn = db.begin_write().unwrap();
-    let mut table: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table(b"x").unwrap();
+    let mut table: MultimapTable<[u8], [u8]> = write_txn.open_multimap_table("x").unwrap();
     table.remove_all(b"hello").unwrap();
     write_txn.commit().unwrap();
 
     let read_txn = db.begin_read().unwrap();
-    let table: ReadOnlyMultimapTable<[u8], [u8]> = read_txn.open_multimap_table(b"x").unwrap();
+    let table: ReadOnlyMultimapTable<[u8], [u8]> = read_txn.open_multimap_table("x").unwrap();
     assert!(table.is_empty().unwrap());
     let empty: Vec<Vec<u8>> = vec![];
     assert_eq!(empty, get_vec(&table, b"hello"));