Skip to content

Commit 2cca1f5

Browse files
committed
Add rename_table() and rename_multimap_table()
1 parent a454d8c commit 2cca1f5

File tree

4 files changed

+164
-0
lines changed

4 files changed

+164
-0
lines changed

src/error.rs

+12
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ pub enum TableError {
9393
},
9494
/// Table name does not match any table in database
9595
TableDoesNotExist(String),
96+
/// Table name already exists in the database
97+
TableExists(String),
9698
// Tables cannot be opened for writing multiple times, since they could retrieve immutable &
9799
// mutable references to the same dirty pages, or multiple mutable references via insert_reserve()
98100
TableAlreadyOpen(String, &'static panic::Location<'static>),
@@ -108,6 +110,7 @@ impl TableError {
108110
| TableError::TableIsNotMultimap(_)
109111
| TableError::TypeDefinitionChanged { .. }
110112
| TableError::TableDoesNotExist(_)
113+
| TableError::TableExists(_)
111114
| TableError::TableAlreadyOpen(_, _) => {
112115
StorageError::Corrupted(format!("{msg}: {self}"))
113116
}
@@ -134,6 +137,7 @@ impl From<TableError> for Error {
134137
TableError::TableIsMultimap(table) => Error::TableIsMultimap(table),
135138
TableError::TableIsNotMultimap(table) => Error::TableIsNotMultimap(table),
136139
TableError::TableDoesNotExist(table) => Error::TableDoesNotExist(table),
140+
TableError::TableExists(table) => Error::TableExists(table),
137141
TableError::TableAlreadyOpen(name, location) => Error::TableAlreadyOpen(name, location),
138142
TableError::Storage(storage) => storage.into(),
139143
}
@@ -179,6 +183,9 @@ impl Display for TableError {
179183
TableError::TableDoesNotExist(table) => {
180184
write!(f, "Table '{table}' does not exist")
181185
}
186+
TableError::TableExists(table) => {
187+
write!(f, "Table '{table}' already exists")
188+
}
182189
TableError::TableAlreadyOpen(name, location) => {
183190
write!(f, "Table '{name}' already opened at: {location}")
184191
}
@@ -476,6 +483,8 @@ pub enum Error {
476483
},
477484
/// Table name does not match any table in database
478485
TableDoesNotExist(String),
486+
/// Table name already exists in the database
487+
TableExists(String),
479488
// Tables cannot be opened for writing multiple times, since they could retrieve immutable &
480489
// mutable references to the same dirty pages, or multiple mutable references via insert_reserve()
481490
TableAlreadyOpen(String, &'static panic::Location<'static>),
@@ -545,6 +554,9 @@ impl Display for Error {
545554
Error::TableDoesNotExist(table) => {
546555
write!(f, "Table '{table}' does not exist")
547556
}
557+
Error::TableExists(table) => {
558+
write!(f, "Table '{table}' already exists")
559+
}
548560
Error::TableAlreadyOpen(name, location) => {
549561
write!(f, "Table '{name}' already opened at: {location}")
550562
}

src/transactions.rs

+70
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,46 @@ impl<'db> TableNamespace<'db> {
421421
))
422422
}
423423

424+
#[track_caller]
425+
fn inner_rename(
426+
&mut self,
427+
name: &str,
428+
new_name: &str,
429+
table_type: TableType,
430+
) -> Result<(), TableError> {
431+
if let Some(location) = self.open_tables.get(name) {
432+
return Err(TableError::TableAlreadyOpen(name.to_string(), location));
433+
}
434+
435+
self.table_tree.rename_table(name, new_name, table_type)
436+
}
437+
438+
#[track_caller]
439+
fn rename_table(
440+
&mut self,
441+
transaction: &WriteTransaction,
442+
name: &str,
443+
new_name: &str,
444+
) -> Result<(), TableError> {
445+
#[cfg(feature = "logging")]
446+
debug!("Renaming table: {} to {}", name, new_name);
447+
transaction.dirty.store(true, Ordering::Release);
448+
self.inner_rename(name, new_name, TableType::Normal)
449+
}
450+
451+
#[track_caller]
452+
fn rename_multimap_table(
453+
&mut self,
454+
transaction: &WriteTransaction,
455+
name: &str,
456+
new_name: &str,
457+
) -> Result<(), TableError> {
458+
#[cfg(feature = "logging")]
459+
debug!("Renaming multimap table: {} to {}", name, new_name);
460+
transaction.dirty.store(true, Ordering::Release);
461+
self.inner_rename(name, new_name, TableType::Multimap)
462+
}
463+
424464
#[track_caller]
425465
fn inner_delete(&mut self, name: &str, table_type: TableType) -> Result<bool, TableError> {
426466
if let Some(location) = self.open_tables.get(name) {
@@ -1140,6 +1180,36 @@ impl WriteTransaction {
11401180
self.tables.lock().unwrap().close_table(name, table, length);
11411181
}
11421182

1183+
/// Rename the given table
1184+
pub fn rename_table(
1185+
&self,
1186+
definition: impl TableHandle,
1187+
new_name: impl TableHandle,
1188+
) -> Result<(), TableError> {
1189+
let name = definition.name().to_string();
1190+
// Drop the definition so that callers can pass in a `Table` to rename, without getting a TableAlreadyOpen error
1191+
drop(definition);
1192+
self.tables
1193+
.lock()
1194+
.unwrap()
1195+
.rename_table(self, &name, new_name.name())
1196+
}
1197+
1198+
/// Rename the given multimap table
1199+
pub fn rename_multimap_table(
1200+
&self,
1201+
definition: impl MultimapTableHandle,
1202+
new_name: impl MultimapTableHandle,
1203+
) -> Result<(), TableError> {
1204+
let name = definition.name().to_string();
1205+
// Drop the definition so that callers can pass in a `MultimapTable` to rename, without getting a TableAlreadyOpen error
1206+
drop(definition);
1207+
self.tables
1208+
.lock()
1209+
.unwrap()
1210+
.rename_multimap_table(self, &name, new_name.name())
1211+
}
1212+
11431213
/// Delete the given table
11441214
///
11451215
/// Returns a bool indicating whether the table existed

src/tree_store/table_tree.rs

+23
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,29 @@ impl<'txn> TableTreeMut<'txn> {
557557
result
558558
}
559559

560+
pub(crate) fn rename_table(
561+
&mut self,
562+
name: &str,
563+
new_name: &str,
564+
table_type: TableType,
565+
) -> Result<(), TableError> {
566+
if let Some(definition) = self.get_table_untyped(name, table_type)? {
567+
if self.get_table_untyped(new_name, table_type)?.is_some() {
568+
return Err(TableError::TableExists(new_name.to_string()));
569+
}
570+
if let Some(update) = self.pending_table_updates.remove(name) {
571+
self.pending_table_updates
572+
.insert(new_name.to_string(), update);
573+
}
574+
assert!(self.tree.remove(&name)?.is_some());
575+
assert!(self.tree.insert(&new_name, &definition)?.is_none());
576+
} else {
577+
return Err(TableError::TableDoesNotExist(name.to_string()));
578+
}
579+
580+
Ok(())
581+
}
582+
560583
pub(crate) fn delete_table(
561584
&mut self,
562585
name: &str,

tests/basic_tests.rs

+59
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,65 @@ fn delete_table() {
944944
write_txn.commit().unwrap();
945945
}
946946

947+
#[test]
948+
fn rename_table() {
949+
let table_def: TableDefinition<&str, &str> = TableDefinition::new("x");
950+
let table_def2: TableDefinition<&str, &str> = TableDefinition::new("x2");
951+
let multitable_def: MultimapTableDefinition<&str, &str> = MultimapTableDefinition::new("x");
952+
let multitable_def2: MultimapTableDefinition<&str, &str> = MultimapTableDefinition::new("x2");
953+
954+
let tmpfile = create_tempfile();
955+
let db = Database::create(tmpfile.path()).unwrap();
956+
let write_txn = db.begin_write().unwrap();
957+
{
958+
let mut table = write_txn.open_table(table_def).unwrap();
959+
table.insert("hi", "hi").unwrap();
960+
write_txn.rename_table(table, table_def2).unwrap();
961+
assert!(matches!(
962+
write_txn.rename_table(table_def, table_def2).unwrap_err(),
963+
TableError::TableDoesNotExist(_)
964+
));
965+
966+
let table = write_txn.open_table(table_def).unwrap();
967+
assert!(matches!(
968+
write_txn.rename_table(table, table_def2).unwrap_err(),
969+
TableError::TableExists(_)
970+
));
971+
972+
assert!(matches!(
973+
write_txn
974+
.rename_multimap_table(multitable_def, multitable_def2)
975+
.unwrap_err(),
976+
TableError::TableIsNotMultimap(_)
977+
));
978+
}
979+
write_txn.commit().unwrap();
980+
981+
let write_txn = db.begin_write().unwrap();
982+
{
983+
let table = write_txn.open_table(table_def).unwrap();
984+
assert!(table.is_empty().unwrap());
985+
let table2 = write_txn.open_table(table_def2).unwrap();
986+
assert_eq!(table2.get("hi").unwrap().unwrap().value(), "hi");
987+
}
988+
}
989+
990+
#[test]
991+
fn rename_open_table() {
992+
let tmpfile = create_tempfile();
993+
let db = Database::create(tmpfile.path()).unwrap();
994+
let write_txn = db.begin_write().unwrap();
995+
{
996+
let table = write_txn.open_table(STR_TABLE).unwrap();
997+
assert!(matches!(
998+
write_txn.rename_table(STR_TABLE, STR_TABLE).unwrap_err(),
999+
TableError::TableAlreadyOpen(_, _)
1000+
));
1001+
drop(table);
1002+
}
1003+
write_txn.commit().unwrap();
1004+
}
1005+
9471006
#[test]
9481007
fn no_dirty_reads() {
9491008
let tmpfile = create_tempfile();

0 commit comments

Comments
 (0)