diff options
| author | Jan Tuomi <jan@jantuomi.fi> | 2025-02-21 14:16:24 +0200 |
|---|---|---|
| committer | Jan Tuomi <jan@jantuomi.fi> | 2025-02-21 14:16:24 +0200 |
| commit | bea37e2834a5265035b960277ff27e5f8d74d079 (patch) | |
| tree | 9a48f8d0fc926e9fe5dcc232dee7ba287cfac126 /log_db | |
| parent | cdc78ce436459047b06163f98fc661df9f2026f4 (diff) | |
Start working in new schema logic to core
Diffstat (limited to 'log_db')
| -rw-r--r-- | log_db/benches/utils.rs | 18 | ||||
| -rw-r--r-- | log_db/src/common.rs | 5 | ||||
| -rw-r--r-- | log_db/src/config.rs | 34 | ||||
| -rw-r--r-- | log_db/src/engine.rs | 25 | ||||
| -rw-r--r-- | log_db/src/lib.rs | 59 | ||||
| -rw-r--r-- | log_db/tests/integration.rs | 35 |
6 files changed, 126 insertions, 50 deletions
diff --git a/log_db/benches/utils.rs b/log_db/benches/utils.rs index 3a949ae..d25a5af 100644 --- a/log_db/benches/utils.rs +++ b/log_db/benches/utils.rs @@ -17,6 +17,22 @@ pub struct Inst { pub data: Vec<u8>, } +impl AsRef<str> for Field { + fn as_ref(&self) -> &str { + match self { + Field::Id => "id", + Field::Name => "name", + Field::Data => "data", + } + } +} + +impl Into<String> for Field { + fn into(self) -> String { + self.as_ref().to_string() + } +} + impl Inst { pub fn fields() -> Vec<Field> { vec![Field::Id, Field::Name, Field::Data] @@ -82,7 +98,7 @@ pub fn random_inst(from_id: i64, to_id: i64) -> Inst { } pub fn prefill_db( - db: &mut DB<Inst, Field>, + db: &mut DB<Inst>, insts: &mut Vec<Inst>, n_records: usize, compact: bool, diff --git a/log_db/src/common.rs b/log_db/src/common.rs index 9bafdd4..ca9f451 100644 --- a/log_db/src/common.rs +++ b/log_db/src/common.rs @@ -289,10 +289,7 @@ impl Value { } } -pub fn get_secondary_memtable_index_by_field<Field: Eq>( - sks: &Vec<Field>, - field: &Field, -) -> Option<usize> { +pub fn get_secondary_memtable_index_by_field(sks: &Vec<String>, field: &str) -> Option<usize> { sks.iter().position(|schema_field| schema_field == field) } diff --git a/log_db/src/config.rs b/log_db/src/config.rs index 5b3cf26..ec74164 100644 --- a/log_db/src/config.rs +++ b/log_db/src/config.rs @@ -6,23 +6,23 @@ pub struct Schema<F> { pub secondary_keys: Vec<F>, } -pub struct ConfigBuilder<T, F> { +pub struct ConfigBuilder<T> { data_dir: Option<String>, segment_size: Option<usize>, write_durability: Option<WriteDurability>, read_consistency: Option<ReadConsistency>, - fields: Option<Vec<F>>, - primary_key: Option<F>, - secondary_keys: Option<Vec<F>>, + fields: Option<Vec<String>>, + primary_key: Option<String>, + secondary_keys: Option<Vec<String>>, from_record: Option<fn(Vec<Value>) -> T>, into_record: Option<fn(T) -> Vec<Value>>, _marker: PhantomData<T>, } -impl<T, F: Eq + Clone> ConfigBuilder<T, F> { - pub fn new() -> ConfigBuilder<T, F> { +impl<T> ConfigBuilder<T> { + pub fn new() -> ConfigBuilder<T> { ConfigBuilder { data_dir: None, segment_size: None, @@ -71,18 +71,18 @@ impl<T, F: Eq + Clone> ConfigBuilder<T, F> { self } - pub fn fields(mut self, schema: Vec<F>) -> Self { - self.fields = Some(schema); + pub fn fields(mut self, schema: Vec<impl Into<String>>) -> Self { + self.fields = Some(schema.into_iter().map(|s| s.into()).collect()); self } - pub fn primary_key(mut self, primary_key: F) -> Self { - self.primary_key = Some(primary_key); + pub fn primary_key(mut self, primary_key: impl Into<String>) -> Self { + self.primary_key = Some(primary_key.into()); self } - pub fn secondary_keys(mut self, secondary_keys: Vec<F>) -> Self { - self.secondary_keys = Some(secondary_keys); + pub fn secondary_keys(mut self, secondary_keys: Vec<impl Into<String>>) -> Self { + self.secondary_keys = Some(secondary_keys.into_iter().map(|s| s.into()).collect()); self } @@ -96,7 +96,7 @@ impl<T, F: Eq + Clone> ConfigBuilder<T, F> { self } - pub fn initialize(self) -> DBResult<DB<T, F>> { + pub fn initialize(self) -> DBResult<DB<T>> { let schema = self .fields .ok_or_else(|| DBError::ValidationError("Schema not set".to_string()))?; @@ -134,10 +134,10 @@ impl<T, F: Eq + Clone> ConfigBuilder<T, F> { } #[derive(Clone)] -pub struct Config<T, F> { - pub schema: Vec<F>, - pub primary_key: F, - pub secondary_keys: Vec<F>, +pub struct Config<T> { + pub schema: Vec<String>, + pub primary_key: String, + pub secondary_keys: Vec<String>, pub from_record: fn(Vec<Value>) -> T, pub into_record: fn(T) -> Vec<Value>, pub data_dir: String, diff --git a/log_db/src/engine.rs b/log_db/src/engine.rs index c2f9a52..b06bfd2 100644 --- a/log_db/src/engine.rs +++ b/log_db/src/engine.rs @@ -1,7 +1,7 @@ use super::*; -pub struct Engine<T, F: Eq + Clone> { - pub config: Config<T, F>, +pub struct Engine<T> { + pub config: Config<T>, pub lock_manager: LockManager, data_dir_path: PathBuf, @@ -19,8 +19,8 @@ pub struct Engine<T, F: Eq + Clone> { pub secondary_memtables: Vec<SecondaryMemtable>, } -impl<T, F: Eq + Clone> Engine<T, F> { - pub fn initialize(config: Config<T, F>) -> DBResult<Engine<T, F>> { +impl<T> Engine<T> { + pub fn initialize(config: Config<T>) -> DBResult<Engine<T>> { info!("Initializing DB..."); // If data_dir does not exist or is empty, create it and any necessary files. // After creation, the directory should always be in a complete state without missing files. @@ -104,7 +104,7 @@ impl<T, F: Eq + Clone> Engine<T, F> { Path::new(&config.data_dir).join(active_metadata_header.uuid.to_string()); let active_data_file = APPEND_MODE.open(&active_data_path)?; - let mut engine = Engine::<T, F> { + let mut engine = Engine::<T> { config, lock_manager, data_dir_path, @@ -247,7 +247,7 @@ impl<T, F: Eq + Clone> Engine<T, F> { pub fn batch_find_by_records<'a>( &mut self, - field: &F, + field: &str, values: impl Iterator<Item = &'a Value>, params: &QueryParams, ) -> DBResult<Vec<(usize, Record)>> { @@ -378,7 +378,7 @@ impl<T, F: Eq + Clone> Engine<T, F> { pub fn range_by_records<B: RangeBounds<Value>>( &mut self, - field: &F, + field: &str, range: B, params: &QueryParams, ) -> DBResult<Vec<Record>> { @@ -458,7 +458,7 @@ impl<T, F: Eq + Clone> Engine<T, F> { } } - pub fn delete_by_field(&mut self, field: &F, value: &Value) -> DBResult<Vec<Record>> { + pub fn delete_by_field(&mut self, field: &str, value: &Value) -> DBResult<Vec<Record>> { let recs: Vec<Record> = self .batch_find_by_records(field, std::iter::once(value), &DEFAULT_QUERY_PARAMS)? .into_iter() @@ -737,6 +737,15 @@ mod tests { Name, } + impl Into<String> for Field { + fn into(self) -> String { + match self { + Field::Id => "id".to_owned(), + Field::Name => "name".to_owned(), + } + } + } + #[derive(PartialEq, Eq, Debug, Clone)] struct TestInst2 { id: i64, diff --git a/log_db/src/lib.rs b/log_db/src/lib.rs index 753e41c..fddb344 100644 --- a/log_db/src/lib.rs +++ b/log_db/src/lib.rs @@ -37,17 +37,17 @@ use memtable_primary::PrimaryMemtable; use memtable_secondary::SecondaryMemtable; use record::*; -pub struct DB<T, F: Eq + Clone> { - engine: Engine<T, F>, +pub struct DB<T> { + engine: Engine<T>, } -impl<T, F: Eq + Clone> DB<T, F> { +impl<T> DB<T> { /// Create a new database configuration builder. - pub fn configure() -> ConfigBuilder<T, F> { + pub fn configure() -> ConfigBuilder<T> { ConfigBuilder::new() } - fn initialize(config: Config<T, F>) -> DBResult<DB<T, F>> { + fn initialize(config: Config<T>) -> DBResult<DB<T>> { let engine = Engine::initialize(config)?; Ok(DB { engine }) } @@ -85,9 +85,13 @@ impl<T, F: Eq + Clone> DB<T, F> { } /// Get a collection of records based on an indexed field value. - pub fn find_by(&mut self, field: &F, value: &Value) -> DBResult<Vec<T>> { + pub fn find_by(&mut self, field: impl AsRef<str>, value: &Value) -> DBResult<Vec<T>> { let recs = self.engine.with_shared_lock(|engine| { - engine.batch_find_by_records(field, std::iter::once(value), &DEFAULT_QUERY_PARAMS) + engine.batch_find_by_records( + field.as_ref(), + std::iter::once(value), + &DEFAULT_QUERY_PARAMS, + ) })?; Ok(recs @@ -99,12 +103,12 @@ impl<T, F: Eq + Clone> DB<T, F> { /// Get a collection of records based on an indexed field value, with additional parameters. pub fn find_by_with_params( &mut self, - field: &F, + field: impl AsRef<str>, value: &Value, params: &QueryParams, ) -> DBResult<Vec<T>> { let recs = self.engine.with_shared_lock(|engine| { - engine.batch_find_by_records(field, std::iter::once(value), params) + engine.batch_find_by_records(field.as_ref(), std::iter::once(value), params) })?; Ok(recs @@ -116,9 +120,13 @@ impl<T, F: Eq + Clone> DB<T, F> { /// Get a collection of records based on a sequence of indexed field values. /// Returns a vector of pairs where the first value is an index into the given sequence of values, /// and the second value is the record. - pub fn batch_find_by(&mut self, field: &F, values: &[Value]) -> DBResult<Vec<(usize, T)>> { + pub fn batch_find_by( + &mut self, + field: impl Into<String>, + values: &[Value], + ) -> DBResult<Vec<(usize, T)>> { let recs = self.engine.with_shared_lock(|engine| { - engine.batch_find_by_records(field, values.iter(), &DEFAULT_QUERY_PARAMS) + engine.batch_find_by_records(&field.into(), values.iter(), &DEFAULT_QUERY_PARAMS) })?; Ok(recs @@ -132,12 +140,12 @@ impl<T, F: Eq + Clone> DB<T, F> { /// and the second value is the record. pub fn batch_find_by_with_params( &mut self, - field: &F, + field: impl AsRef<str>, values: &[Value], params: &QueryParams, ) -> DBResult<Vec<(usize, T)>> { let recs = self.engine.with_shared_lock(|engine| { - engine.batch_find_by_records(field, values.iter(), params) + engine.batch_find_by_records(field.as_ref(), values.iter(), params) })?; Ok(recs @@ -149,9 +157,13 @@ impl<T, F: Eq + Clone> DB<T, F> { /// Get a collection of records based on a range of indexed field values. /// This method can be used to run comparison-like queries, e.g. `field >= 10` /// could be expressed as `db.range_by(Field::Id, 10..)`. - pub fn range_by<B: RangeBounds<Value>>(&mut self, field: &F, range: B) -> DBResult<Vec<T>> { + pub fn range_by<B: RangeBounds<Value>>( + &mut self, + field: impl AsRef<str>, + range: B, + ) -> DBResult<Vec<T>> { let recs = self.engine.with_shared_lock(|engine| { - engine.range_by_records(field, range, &DEFAULT_QUERY_PARAMS) + engine.range_by_records(field.as_ref(), range, &DEFAULT_QUERY_PARAMS) })?; Ok(recs @@ -165,13 +177,13 @@ impl<T, F: Eq + Clone> DB<T, F> { /// could be expressed as `db.range_by(Field::Id, 10..)`. pub fn range_by_with_params<B: RangeBounds<Value>>( &mut self, - field: &F, + field: impl AsRef<str>, range: B, params: &QueryParams, ) -> DBResult<Vec<T>> { let recs = self .engine - .with_shared_lock(|engine| engine.range_by_records(field, range, params))?; + .with_shared_lock(|engine| engine.range_by_records(field.as_ref(), range, params))?; Ok(recs .into_iter() @@ -185,10 +197,10 @@ impl<T, F: Eq + Clone> DB<T, F> { /// /// Deletion is done by marking the record as a tombstone. The record will still be present in the log file, /// but will be ignored by reads. Upon compaction, tombstoned records will be removed. - pub fn delete_by(&mut self, field: &F, value: &Value) -> DBResult<Vec<T>> { + pub fn delete_by(&mut self, field: impl AsRef<str>, value: &Value) -> DBResult<Vec<T>> { let recs = self .engine - .with_exclusive_lock(|engine| engine.delete_by_field(field, value))?; + .with_exclusive_lock(|engine| engine.delete_by_field(field.as_ref(), value))?; Ok(recs .into_iter() @@ -295,6 +307,15 @@ mod tests { Name, } + impl Into<String> for Field { + fn into(self) -> String { + match self { + Field::Id => "id".to_string(), + Field::Name => "name".to_string(), + } + } + } + struct TestInst1 { id: i64, } diff --git a/log_db/tests/integration.rs b/log_db/tests/integration.rs index 3d71279..af12fb4 100644 --- a/log_db/tests/integration.rs +++ b/log_db/tests/integration.rs @@ -36,6 +36,22 @@ enum Field { Data, } +impl AsRef<str> for Field { + fn as_ref(&self) -> &str { + match self { + Field::Id => "id", + Field::Name => "name", + Field::Data => "data", + } + } +} + +impl Into<String> for Field { + fn into(self) -> String { + self.as_ref().to_owned() + } +} + #[derive(Debug)] struct Inst { pub id: i64, @@ -610,7 +626,7 @@ fn test_batch_find_by() { } let batch: Vec<Value> = (2..5).map(Value::Int).collect(); - let result = db.batch_find_by(&Field::Id, &batch).unwrap(); + let result = db.batch_find_by(Field::Id, &batch).unwrap(); assert_eq!(result.len(), batch.len()); assert_eq!( @@ -707,6 +723,23 @@ enum FieldWithNewNullableField { MaybeStr, } +impl AsRef<str> for FieldWithNewNullableField { + fn as_ref(&self) -> &str { + match self { + FieldWithNewNullableField::Id => "id", + FieldWithNewNullableField::Name => "name", + FieldWithNewNullableField::Data => "data", + FieldWithNewNullableField::MaybeStr => "maybe_str", + } + } +} + +impl Into<String> for FieldWithNewNullableField { + fn into(self) -> String { + self.as_ref().to_owned() + } +} + struct InstWithNewNullableField { pub id: i64, pub name: Option<String>, |
