aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/lib.rs8
-rw-r--r--src/secondary_memtable.rs5
-rw-r--r--tests/integration.rs58
3 files changed, 70 insertions, 1 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 4afeb4c..3b249d8 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -224,6 +224,8 @@ impl<Field: Eq + Clone + Debug> DB<Field> {
Ok(db)
}
+ /// Insert a record into the database. If the primary key value already exists,
+ /// the existing record will be replaced by the supplied one.
pub fn upsert(&mut self, record: &Record) -> Result<(), io::Error> {
debug!("Upserting record: {:?}", record);
// Validate the record length
@@ -293,7 +295,7 @@ impl<Field: Eq + Clone + Debug> DB<Field> {
file.unlock()?;
debug!("Record appended to log file, lock released");
- debug!("Updating memtables");
+ debug!("Updating primary memtable");
let primary_value =
&record.values[self.primary_key_index]
@@ -308,6 +310,10 @@ impl<Field: Eq + Clone + Debug> DB<Field> {
self.secondary_memtables
.iter_mut()
.for_each(|secondary_memtable| {
+ debug!(
+ "Updating memtable for index on {:?}",
+ &secondary_memtable.field
+ );
for (index, (schema_field, _)) in self.config.fields.iter().enumerate() {
if schema_field == &secondary_memtable.field {
let key = record.values[index]
diff --git a/src/secondary_memtable.rs b/src/secondary_memtable.rs
index 602db3a..fcf4a7f 100644
--- a/src/secondary_memtable.rs
+++ b/src/secondary_memtable.rs
@@ -85,9 +85,14 @@ impl<Field: Eq + Clone + Debug> SecondaryMemtable<Field> {
match self.records.get_mut(key) {
Some(existing) => {
+ debug!(
+ "Existing entry found with {} records in the set",
+ &existing.len()
+ );
existing.insert(unique_record);
}
None => {
+ debug!("No existing entry found, creating one.");
let mut set = HashSet::with_capacity(1);
set.insert(unique_record);
self.records.insert(key.clone(), set);
diff --git a/tests/integration.rs b/tests/integration.rs
index 41c8807..3e132bc 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -231,3 +231,61 @@ fn test_log_reader_fixture_db1() {
_ => false,
});
}
+
+#[test]
+#[serial]
+fn test_upsert_and_get_from_secondary_memtable() {
+ let _ = env_logger::builder().is_test(true).try_init();
+ let mut db = DB::configure()
+ .data_dir(TEST_DATA_DIR)
+ .fields(&vec![
+ (Field::Id, RecordFieldType::Int),
+ (Field::Name, RecordFieldType::String),
+ (Field::Data, RecordFieldType::Bytes),
+ ])
+ .primary_key(Field::Id)
+ .secondary_keys(vec![Field::Name])
+ .initialize()
+ .expect("Failed to initialize DB instance");
+
+ // Insert some records
+ let record0 = Record {
+ values: vec![
+ RecordValue::Int(0),
+ RecordValue::String("John".to_string()),
+ RecordValue::Bytes(vec![3, 4, 5]),
+ ],
+ };
+ db.upsert(&record0).unwrap();
+
+ let record1 = Record {
+ values: vec![
+ RecordValue::Int(1),
+ RecordValue::String("John".to_string()),
+ RecordValue::Bytes(vec![1, 2, 3]),
+ ],
+ };
+ db.upsert(&record1).unwrap();
+
+ let record2 = Record {
+ values: vec![
+ RecordValue::Int(2),
+ RecordValue::String("George".to_string()),
+ RecordValue::Bytes(vec![1, 2, 3]),
+ ],
+ };
+ db.upsert(&record2).unwrap();
+
+ // Delete the DB so that any results must come from a memtable
+ fs::remove_file(Path::new(TEST_DATA_DIR).join("db")).expect("Failed to delete the DB log file");
+
+ // There should be 2 Johns
+ let johns = db
+ .find_all(&Field::Name, &RecordValue::String("John".to_string()))
+ .expect("Failed to find all Johns");
+
+ assert_eq!(johns.len(), 2);
+
+ // Clean up
+ std::fs::remove_dir_all(TEST_DATA_DIR.to_string()).unwrap();
+}