diff --git a/Cargo.lock b/Cargo.lock index 9ed5854..bb3b754 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,6 +102,7 @@ dependencies = [ "file", "log 0.0.1", "tempfile", + "tracing", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9595681..8fee391 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,14 +4,15 @@ version = "0.0.1" [workspace] resolver = "2" members = [ - "buffer", "client", - "common", - "engine", - "file", - "log", - "network", - "server", - "transaction", + "buffer", + "client", + "common", + "engine", + "file", + "log", + "network", + "server", + "transaction", ] [workspace.dependencies] diff --git a/buffer/Cargo.toml b/buffer/Cargo.toml index fde9891..afcecec 100644 --- a/buffer/Cargo.toml +++ b/buffer/Cargo.toml @@ -7,6 +7,7 @@ version.workspace = true common = { path = "../common" } file = { path = "../file" } log = { path = "../log" } +tracing = { workspace = true } [dev-dependencies] tempfile = { workspace = true } diff --git a/buffer/src/mgr.rs b/buffer/src/mgr.rs index 5bfd05e..5ac75d2 100644 --- a/buffer/src/mgr.rs +++ b/buffer/src/mgr.rs @@ -126,7 +126,10 @@ impl BufferMgr { Some(buffer) => { return Ok(buffer); } - None if start.elapsed() >= MAX_WAIT => return Err(DbError::BufferAbort), + None if start.elapsed() >= MAX_WAIT => { + tracing::debug!("buffer {:?} lock abort", block); + return Err(DbError::BufferAbort); + } None => { drop(lock); thread::sleep(SLEEP) diff --git a/common/src/error.rs b/common/src/error.rs index e304090..d6b16c0 100644 --- a/common/src/error.rs +++ b/common/src/error.rs @@ -38,6 +38,8 @@ pub enum DbError { ToInt(#[from] std::num::TryFromIntError), #[error("Unexpected token: {0}")] UnexpectedToken(String), + #[error("max size ({0}) exceeded: {1}")] + MaxSize(usize, usize), } impl DbError { diff --git a/engine/benches/db_benchmark.rs b/engine/benches/db_benchmark.rs index a8b69d5..3834c23 100644 --- a/engine/benches/db_benchmark.rs +++ b/engine/benches/db_benchmark.rs @@ -20,6 +20,26 @@ fn bench_insert(c: &mut Criterion) { }); } +fn bench_index_insert(c: &mut Criterion) { + let dir = tempdir().unwrap(); + let db = SimpleDB::new(dir.path()).unwrap(); + let tx = db.get_tx().unwrap(); + db.execute(&tx, "CREATE TABLE test(id INT)").unwrap(); + db.execute(&tx, "CREATE INDEX test_ids ON test(id)") + .unwrap(); + tx.commit().unwrap(); + let mut rng = rand::rng(); + c.bench_function("index_insert", |b| { + b.iter(|| { + let id = rng.random::(); + db.execute(&tx, &format!("INSERT INTO test(id) VALUES({})", id)) + .unwrap(); + db.query(&tx, &format!("SELECT id FROM test WHERE id={}", id)) + .unwrap(); + }); + }); +} + fn bench_join(c: &mut Criterion) { let dir = tempdir().unwrap(); let db = SimpleDB::new(dir.path()).unwrap(); @@ -49,5 +69,5 @@ fn bench_join(c: &mut Criterion) { }); } -criterion_group!(benches, bench_insert, bench_join); +criterion_group!(benches, bench_insert, bench_join, bench_index_insert); criterion_main!(benches); diff --git a/engine/src/constant.rs b/engine/src/constant.rs deleted file mode 100644 index 6fa338f..0000000 --- a/engine/src/constant.rs +++ /dev/null @@ -1,66 +0,0 @@ -use common::{DbResult, error::DbError}; - -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Constant { - Integer(i32), - Varchar(String), -} - -impl Constant { - pub fn varchar(value: &str) -> Self { - Constant::Varchar(value.to_string()) - } - - pub fn as_i32(&self) -> DbResult { - match self { - Self::Integer(value) => Ok(*value), - _ => Err(DbError::InvalidFieldType), - } - } - - pub fn as_str(&self) -> DbResult<&str> { - match self { - Self::Varchar(value) => Ok(value), - _ => Err(DbError::InvalidFieldType), - } - } - - pub fn as_string(&self) -> DbResult { - Ok(self.as_str()?.to_string()) - } -} - -impl std::fmt::Display for Constant { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Integer(value) => write!(f, "{}", value), - Self::Varchar(value) => write!(f, "'{}'", value), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - #[should_panic] - fn invalid_i32() { - let constant = Constant::Varchar("string".to_string()); - constant.as_i32().unwrap(); - } - - #[test] - #[should_panic] - fn invalid_str() { - let constant = Constant::Integer(10); - constant.as_str().unwrap(); - } - - #[test] - fn as_str() { - let value = "test"; - let constant = Constant::varchar(value); - assert_eq!(value, constant.as_str().unwrap()); - } -} diff --git a/engine/src/element.rs b/engine/src/element.rs new file mode 100644 index 0000000..9197fd0 --- /dev/null +++ b/engine/src/element.rs @@ -0,0 +1,69 @@ +use common::DbResult; +use common::error::DbError; + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Element { + Raw(String), + View(String, String), + Spec(String, String), +} + +impl Element { + pub fn raw(value: &str) -> Self { + Self::Raw(value.to_string()) + } + + pub fn view(source: &str, name: &str) -> Self { + Self::View(source.to_string(), name.to_string()) + } + + pub fn spec(source: &str, target: &str) -> Self { + Self::Spec(source.to_string(), target.to_string()) + } + + pub fn as_raw(&self) -> DbResult<&str> { + match self { + Self::Raw(s) => Ok(s), + _ => Err(DbError::InvalidFieldType), + } + } + + pub fn len(&self) -> usize { + match self { + Self::Raw(s) => s.len(), + Self::View(source, name) => source.len() + 4 + name.len(), + Self::Spec(source, target) => source.len() + 4 + target.len(), + } + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl std::fmt::Display for Element { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Raw(value) => write!(f, "{}", value), + Self::View(source, name) => write!(f, "{} {}", source, name), + Self::Spec(source, target) => write!(f, "{}.{}", source, target), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn view() { + let view = Element::view("table", "t"); + assert_eq!("table t", view.to_string()); + } + + #[test] + fn spec() { + let spec = Element::spec("t1", "f1"); + assert_eq!("t1.f1", spec.to_string()); + } +} diff --git a/engine/src/index.rs b/engine/src/index.rs index d7ebc25..0bae38e 100644 --- a/engine/src/index.rs +++ b/engine/src/index.rs @@ -1,23 +1,19 @@ use common::DbResult; -use crate::{constant::Constant, rid::RID}; +use crate::{rid::RID, value::Value}; pub mod b_tree; -pub mod btree_dir; -pub mod btree_leaf; -pub mod btree_page; -pub mod dir_entry; pub trait Index { - fn before_first(&self, key: Constant) -> DbResult<()>; + fn before_first(&self, key: Value) -> DbResult<()>; fn next(&self) -> DbResult; fn get_data_rid(&self) -> DbResult; - fn insert(&self, value: Constant, rid: RID) -> DbResult<()>; + fn insert(&self, value: Value, rid: RID) -> DbResult<()>; - fn delete(&self, value: Constant, rid: RID) -> DbResult<()>; + fn delete(&self, value: Value, rid: RID) -> DbResult<()>; fn close(&self) -> DbResult<()>; } @@ -26,6 +22,8 @@ pub trait Index { mod tests { use tempfile::tempdir; + use crate::element::Element; + use crate::schema::SchemaBuilder; use crate::{ SimpleDB, plan::{Plan, table::TablePlan}, @@ -33,22 +31,24 @@ mod tests { #[test] fn index_retrieval() { - use crate::schema::Schema; - use std::sync::Arc; - let dir = tempdir().unwrap(); let db = SimpleDB::configured(dir.path(), 512, 8).unwrap(); let md = db.metadata_mgr(); let table = "student"; + let sid = Element::raw("sid"); + let sname = Element::raw("sname"); + let majorid = Element::raw("majorid"); + let setup_tx = db.get_tx().unwrap(); - let schema = Arc::new(Schema::default()); - schema.add_int_field("sid".to_string()).unwrap(); - schema.add_string_field("sname".to_string(), 16).unwrap(); - schema.add_int_field("majorid".to_string()).unwrap(); + let schema = SchemaBuilder::default() + .add_int_field(sid.clone()) + .add_string_field(sname.clone(), 16) + .add_int_field(majorid.clone()) + .build(); - md.create_table(table, &schema, &setup_tx).unwrap(); + md.create_table(table, schema, &setup_tx).unwrap(); md.create_index("idx_majorid", table, "majorid", &setup_tx) .unwrap(); setup_tx.commit().unwrap(); @@ -58,7 +58,7 @@ mod tests { let _scan = plan.open().unwrap(); let indexes = md.get_index_info(table, &tx).unwrap(); - let _index = indexes.get("majorid").unwrap(); + let _index = indexes.get(&majorid).unwrap(); tx.commit().unwrap(); } } diff --git a/engine/src/index/b_tree.rs b/engine/src/index/b_tree.rs index 8bc9c82..b4bbd4a 100644 --- a/engine/src/index/b_tree.rs +++ b/engine/src/index/b_tree.rs @@ -1,262 +1,430 @@ -use std::sync::{Arc, RwLock}; +use std::cell::RefCell; +use std::sync::Arc; use common::{DbResult, error::DbError}; use file::block::BlockId; use transaction::transaction::Transaction; +use crate::index::Index; use crate::{ - constant::Constant, - field_info::FieldInfo, - index::{ - Index, - btree_dir::BTreeDir, - btree_leaf::BTreeLeaf, - btree_page::{BLOCK, BTreePage, VALUE}, + index::b_tree::{ + page::{ + BTreePage, POINTER_SIZE, TYPE_SIZE, insert_entry, insert_pointer, leaf_size, node_size, + pointer_index, split_entries, split_pointers, + }, + pointer::BTreePointer, }, - layout::Layout, - schema::Schema, + rid::RID, + value::Value, }; -struct BTreeIndexLock { +mod entry; +mod page; +mod pointer; + +struct BTreeIndexInner { + index_name: String, tx: Arc, - dir_layout: Arc, - leaf_layout: Arc, - leaf_table: String, - leaf: Option, - root_block: BlockId, + position: i32, + rid: Vec, } -impl BTreeIndexLock { - fn new(tx: &Arc, name: &str, leaf_layout: &Arc) -> DbResult { - let leaf_table = format!("{}_leaf", name); - if tx.size(&leaf_table)? == 0 { - let block = tx.append(&leaf_table)?; - let node = BTreePage::new(tx, block.clone(), leaf_layout)?; - node.format(&block, -1)?; - } - let dir_schema = Arc::new(Schema::default()); - dir_schema.add(BLOCK.to_string(), &leaf_layout.schema())?; - dir_schema.add(VALUE.to_string(), &leaf_layout.schema())?; - let dir_table = format!("{}_dir", name); - let dir_layout = Arc::new(Layout::new(&dir_schema)?); - let root_block = BlockId::new(&dir_table, 0); - if tx.size(&dir_table)? == 0 { - tx.append(&dir_table)?; - let node = BTreePage::new(tx, root_block.clone(), &dir_layout)?; - node.format(&root_block, 0)?; - if let Some(field) = dir_schema.info(VALUE)? { - let min = match field { - FieldInfo::Integer => Constant::Integer(i32::MIN), - FieldInfo::Varchar(_) => Constant::Varchar("".to_string()), - }; - node.insert_dir(0, min, 0)?; - } - node.close()?; +impl BTreeIndexInner { + fn new(index_name: &str, tx: &Arc) -> DbResult { + if tx.size(index_name)? == 0 { + create_index(tx, index_name)?; } Ok(Self { + index_name: index_name.to_string(), tx: Arc::clone(tx), - dir_layout, - leaf_layout: Arc::clone(leaf_layout), - leaf_table, - leaf: None, - root_block, + position: 0, + rid: vec![], }) } - fn before_first(&mut self, key: Constant) -> DbResult<()> { - self.close()?; - let root = BTreeDir::new(&self.tx, self.root_block.clone(), &self.dir_layout)?; - let block_num = root.search(&key)?; - root.close()?; - let leaf_block = BlockId::new(&self.leaf_table, block_num); - self.leaf = Some(BTreeLeaf::new( - &self.tx, - leaf_block, - &self.leaf_layout, - key, - )?); - Ok(()) + fn before_first(&mut self, key: Value) -> DbResult<()> { + let mut block = BlockId::new(&self.index_name, 0); + let mut page = BTreePage::read(&self.tx, &block)?; + loop { + match page { + BTreePage::Metadata { root } => { + block = BlockId::new(&self.index_name, root); + page = BTreePage::read(&self.tx, &block)?; + } + BTreePage::Node { children, .. } => { + let idx = pointer_index(&children, &key); + block = BlockId::new(&self.index_name, children[idx].block_num); + page = BTreePage::read(&self.tx, &block)?; + } + BTreePage::Leaf { values, .. } => { + match values.binary_search_by(|v| v.value.cmp(&key)) { + Ok(idx) => { + self.position = -1; + self.rid = values[idx].rid.clone(); + } + Err(_) => tracing::debug!("value not found"), + }; + return Ok(()); + } + } + } } - fn next(&self) -> DbResult { - if let Some(leaf) = &self.leaf { - leaf.next() - } else { - Ok(false) - } + fn next(&mut self) -> DbResult { + self.position += 1; + Ok((self.position as usize) < self.rid.len()) } - fn get_data_rid(&self) -> DbResult { - if let Some(leaf) = &self.leaf { - leaf.get_data_rid() - } else { - Err(DbError::other("cannot find RID")) - } + fn get_data_rid(&self) -> DbResult { + Ok(self.rid[self.position as usize].clone()) } - fn insert(&mut self, value: Constant, rid: crate::rid::RID) -> DbResult<()> { - self.before_first(value)?; - let Some(leaf) = &self.leaf else { - return Ok(()); - }; - let Some(e) = leaf.insert(rid)? else { - return Ok(()); - }; - let root = BTreeDir::new(&self.tx, self.root_block.clone(), &self.dir_layout)?; - if let Some(e2) = root.insert(e)? { - root.make_new_root(e2)?; + fn insert(&self, key: Value, rid: RID) -> DbResult<()> { + let mut block = BlockId::new(&self.index_name, 0); + let mut page = BTreePage::read(&self.tx, &block)?; + let mut new_offset = None::<(Value, i32)>; + let block_size = self.tx.block_size() as usize; + let mut new_root = None::; + + loop { + match page { + BTreePage::Metadata { root } => { + if let Some(root) = new_root.take() { + let page = BTreePage::Metadata { root }; + page.write(&block, &self.tx)?; + break; + } + block = BlockId::new(&self.index_name, root); + page = BTreePage::read(&self.tx, &block)?; + } + BTreePage::Node { + parent, + mut children, + } => { + if let Some((key, offset)) = new_offset.take() { + insert_pointer( + &mut children, + BTreePointer { + value: key, + block_num: offset, + }, + ); + if node_size(&children) <= block_size { + let page = BTreePage::Node { parent, children }; + page.write(&block, &self.tx)?; + break; + } + let (children, right_children) = split_pointers(children, block_size); + let left_key = children[0].value.clone(); + let right_key = right_children[0].value.clone(); + if parent == 0 { + let parent_block = self.tx.append(&self.index_name)?; + new_root = Some(parent_block.num); + let right_block = self.tx.append(&self.index_name)?; + let left = BTreePage::Node { + parent: parent_block.num, + children, + }; + self.rewrite_parent(&right_children, parent_block.num)?; + let right = BTreePage::Node { + parent: parent_block.num, + children: right_children, + }; + let parent = BTreePage::Node { + parent: 0, + children: vec![ + BTreePointer { + value: left_key, + block_num: block.num, + }, + BTreePointer { + value: right_key, + block_num: right_block.num, + }, + ], + }; + parent.write(&parent_block, &self.tx)?; + left.write(&block, &self.tx)?; + right.write(&right_block, &self.tx)?; + block = BlockId::new(&self.index_name, 0); + page = BTreePage::Metadata { + root: parent_block.num, + }; + } else { + block = BlockId::new(&self.index_name, parent); + page = BTreePage::read(&self.tx, &block)?; + let right = BTreePage::Node { + parent, + children: right_children.clone(), + }; + let right_block = self.tx.append(&self.index_name)?; + right.write(&right_block, &self.tx)?; + new_offset = Some((right_key, right_block.num)); + } + } else { + let idx = pointer_index(&children, &key); + let Some(child_offset) = children.get(idx) else { + return Err(DbError::other("empty node's leafs")); + }; + block = BlockId::new(&self.index_name, child_offset.block_num); + page = BTreePage::read(&self.tx, &block)?; + } + } + BTreePage::Leaf { + parent, + values: mut children, + } => { + let block_size = self.tx.block_size() as usize; + let size = TYPE_SIZE + key.size(); + let max_size = block_size - (TYPE_SIZE + 3 * POINTER_SIZE); + if size > max_size { + return Err(DbError::MaxSize(max_size, size)); + } + insert_entry(&mut children, key.clone(), rid.clone()); + if leaf_size(&children) <= block_size { + let page = BTreePage::Leaf { + parent, + values: children, + }; + page.write(&block, &self.tx)?; + break; + } + let (children, right_children) = split_entries(children, block_size); + let left_key = children[0].value.clone(); + let right_key = right_children[0].value.clone(); + if parent == 0 { + let parent_block = self.tx.append(&self.index_name)?; + let right_block = self.tx.append(&self.index_name)?; + let left = BTreePage::Leaf { + parent: parent_block.num, + values: children, + }; + let right = BTreePage::Leaf { + parent: parent_block.num, + values: right_children, + }; + let parent = BTreePage::Node { + parent: 0, + children: vec![ + BTreePointer { + value: left_key, + block_num: block.num, + }, + BTreePointer { + value: right_key, + block_num: right_block.num, + }, + ], + }; + parent.write(&parent_block, &self.tx)?; + left.write(&block, &self.tx)?; + right.write(&right_block, &self.tx)?; + new_root = Some(parent_block.num); + block = BlockId::new(&self.index_name, 0); + page = BTreePage::Metadata { + root: parent_block.num, + }; + } else { + let left = BTreePage::Leaf { + parent, + values: children, + }; + let right_block = self.tx.append(&self.index_name)?; + let right = BTreePage::Leaf { + parent, + values: right_children, + }; + left.write(&block, &self.tx)?; + right.write(&right_block, &self.tx)?; + new_offset = Some((right_key, right_block.num)); + block = BlockId::new(&self.index_name, parent); + page = BTreePage::read(&self.tx, &block)?; + } + } + } } - root.close()?; Ok(()) } - fn delete(&mut self, value: Constant, rid: crate::rid::RID) -> DbResult<()> { - self.before_first(value.clone())?; - if let Some(leaf) = &self.leaf { - leaf.delete(rid)?; + fn delete(&self, key: Value, rid: RID) -> DbResult<()> { + let mut block = BlockId::new(&self.index_name, 0); + let mut page = BTreePage::read(&self.tx, &block)?; + loop { + match page { + BTreePage::Metadata { root } => { + block = BlockId::new(&self.index_name, root); + page = BTreePage::read(&self.tx, &block)?; + } + BTreePage::Node { children, .. } => { + let idx = pointer_index(&children, &key); + block = BlockId::new(&self.index_name, children[idx].block_num); + page = BTreePage::read(&self.tx, &block)?; + } + BTreePage::Leaf { parent, mut values } => { + if let Ok(idx) = values.binary_search_by(|v| v.value.cmp(&key)) + && let Some(position) = values[idx].rid.iter().position(|x| *x == rid) + { + values[idx].rid.remove(position); + let page = BTreePage::Leaf { parent, values }; + page.write(&block, &self.tx)?; + } + break; + } + } } Ok(()) } fn close(&self) -> DbResult<()> { - if let Some(leaf) = &self.leaf { - leaf.close()?; + // Each B-tree page read/write pins and unpins its block immediately, + // so the index holds no open buffers to release here. + Ok(()) + } + + fn rewrite_parent(&self, values: &[BTreePointer], parent: i32) -> DbResult<()> { + for value in values { + let block = BlockId::new(&self.index_name, value.block_num); + match BTreePage::read(&self.tx, &block)? { + BTreePage::Node { children, .. } => { + let page = BTreePage::Node { parent, children }; + page.write(&block, &self.tx)?; + } + BTreePage::Leaf { values, .. } => { + let page = BTreePage::Leaf { parent, values }; + page.write(&block, &self.tx)?; + } + _ => return Err(DbError::other("unexpected B-Tree index page type")), + } } Ok(()) } } -pub struct BTreeIndex { - lock: RwLock, +fn create_index(tx: &Transaction, index_name: &str) -> DbResult<()> { + let metadata_block = tx.append(index_name)?; + let leaf_block = tx.append(index_name)?; + + let metadata = BTreePage::Metadata { + root: leaf_block.num, + }; + let leaf = BTreePage::Leaf { + parent: 0, + values: vec![], + }; + metadata.write(&metadata_block, tx)?; + leaf.write(&leaf_block, tx)?; + Ok(()) } +pub(crate) struct BTreeIndex(RefCell); + impl BTreeIndex { - pub fn new(tx: &Arc, name: &str, leaf_layout: &Arc) -> DbResult { - Ok(Self { - lock: RwLock::new(BTreeIndexLock::new(tx, name, leaf_layout)?), - }) + pub(crate) fn new(index_name: &str, tx: &Arc) -> DbResult { + Ok(Self(RefCell::new(BTreeIndexInner::new(index_name, tx)?))) } } impl Index for BTreeIndex { - fn before_first(&self, key: Constant) -> DbResult<()> { - let mut write = self.lock.write().map_err(DbError::lock)?; - write.before_first(key) + fn before_first(&self, key: Value) -> DbResult<()> { + let mut inner = self.0.borrow_mut(); + inner.before_first(key) } fn next(&self) -> DbResult { - let read = self.lock.read().map_err(DbError::lock)?; - read.next() + let mut inner = self.0.borrow_mut(); + inner.next() } - fn get_data_rid(&self) -> DbResult { - let read = self.lock.read().map_err(DbError::lock)?; - read.get_data_rid() + fn get_data_rid(&self) -> DbResult { + let inner = self.0.borrow(); + inner.get_data_rid() } - fn insert(&self, value: Constant, rid: crate::rid::RID) -> DbResult<()> { - let mut write = self.lock.write().map_err(DbError::lock)?; - write.insert(value, rid) + fn insert(&self, key: Value, rid: RID) -> DbResult<()> { + let inner = self.0.borrow(); + inner.insert(key, rid) } - fn delete(&self, value: Constant, rid: crate::rid::RID) -> DbResult<()> { - let mut write = self.lock.write().map_err(DbError::lock)?; - write.delete(value, rid) + fn delete(&self, key: Value, rid: RID) -> DbResult<()> { + let inner = self.0.borrow(); + inner.delete(key, rid) } fn close(&self) -> DbResult<()> { - let read = self.lock.read().map_err(DbError::lock)?; - read.close() + let inner = self.0.borrow(); + inner.close() } } #[cfg(test)] mod tests { use super::*; - use crate::SimpleDB; - use crate::rid::RID; - use tempfile::tempdir; + use crate::tests::{init, init_with_size}; #[test] - fn next_int() { - let dir = tempdir().unwrap(); - let db = SimpleDB::new(dir.path()).unwrap(); - let schema = Schema::default(); - schema.add_int_field("value".to_string()).unwrap(); - schema.add_int_field("block".to_string()).unwrap(); - schema.add_int_field("id".to_string()).unwrap(); - let layout = Arc::new(Layout::new(&Arc::new(schema)).unwrap()); - - let tx = db.get_tx().unwrap(); - - let index = BTreeIndex::new(&tx, "test", &layout).unwrap(); - index - .insert(Constant::Integer(10), RID::new(0, 10)) - .unwrap(); - - index.before_first(Constant::Integer(10)).unwrap(); - assert!(index.next().unwrap()); - assert_eq!(index.get_data_rid().unwrap(), RID::new(0, 10)); - assert!(!index.next().unwrap()); - } - - #[test] - fn next_str() { - let dir = tempdir().unwrap(); - let db = SimpleDB::new(dir.path()).unwrap(); - let schema = Schema::default(); - schema.add_string_field("value".to_string(), 16).unwrap(); - schema.add_int_field("block".to_string()).unwrap(); - schema.add_int_field("id".to_string()).unwrap(); - let layout = Arc::new(Layout::new(&Arc::new(schema)).unwrap()); - - let tx = db.get_tx().unwrap(); - - let index = BTreeIndex::new(&tx, "test", &layout).unwrap(); - index - .insert(Constant::varchar("hello"), RID::new(0, 10)) - .unwrap(); - - index.before_first(Constant::varchar("hello")).unwrap(); - assert!(index.next().unwrap()); - assert_eq!(index.get_data_rid().unwrap(), RID::new(0, 10)); - assert!(!index.next().unwrap()); + fn insert_1000() { + let (_dir, tx) = init(); + let mut index = BTreeIndexInner::new("test_index", &tx).unwrap(); + for i in 0..1000 { + index.insert(Value::Integer(i), RID::new(i, i)).unwrap(); + } + for i in 0..1000 { + index.before_first(Value::Integer(i)).unwrap(); + assert!(index.next().unwrap()); + } + tx.commit().unwrap(); } #[test] - fn next_empty() { - let dir = tempdir().unwrap(); - let db = SimpleDB::new(dir.path()).unwrap(); - let schema = Schema::default(); - schema.add_string_field("value".to_string(), 16).unwrap(); - schema.add_int_field("block".to_string()).unwrap(); - schema.add_int_field("id".to_string()).unwrap(); - let layout = Arc::new(Layout::new(&Arc::new(schema)).unwrap()); - - let tx = db.get_tx().unwrap(); - - let index = BTreeIndex::new(&tx, "test", &layout).unwrap(); + fn two_leafs_one_node() { + let (_dir, tx) = init(); + let index_name = "test"; + let index = BTreeIndexInner::new("test", &tx).unwrap(); + for i in 0..30 { + index.insert(Value::Integer(i), RID::new(i, i)).unwrap(); + } + let metadata_block = BlockId::new(index_name, 0); + let left_block = BlockId::new(index_name, 1); + let root_block = BlockId::new(index_name, 2); + let right_block = BlockId::new(index_name, 3); - index.before_first(Constant::varchar("hello")).unwrap(); - assert!(!index.next().unwrap()); + if let BTreePage::Metadata { root } = BTreePage::read(&tx, &metadata_block).unwrap() { + assert_eq!(root, 2); + } else { + panic!("expected metadata page"); + } + if let BTreePage::Leaf { parent, values } = BTreePage::read(&tx, &left_block).unwrap() { + assert_eq!(parent, 2); + assert_eq!(values.len(), 15); + } else { + panic!("expected left leaf page"); + } + if let BTreePage::Node { parent, children } = BTreePage::read(&tx, &root_block).unwrap() { + assert_eq!(parent, 0); + assert_eq!(children.len(), 2); + } else { + panic!("expected root node page"); + } + if let BTreePage::Leaf { parent, values } = BTreePage::read(&tx, &right_block).unwrap() { + assert_eq!(parent, 2); + assert_eq!(values.len(), 15); + } else { + panic!("expected right leaf page"); + } + tx.commit().unwrap(); } #[test] - fn next_multiply() { - let dir = tempdir().unwrap(); - let db = SimpleDB::new(dir.path()).unwrap(); - let schema = Schema::default(); - - schema.add_int_field("value".to_string()).unwrap(); - schema.add_int_field("block".to_string()).unwrap(); - schema.add_int_field("id".to_string()).unwrap(); - let layout = Arc::new(Layout::new(&Arc::new(schema)).unwrap()); - - let tx = db.get_tx().unwrap(); - - let index = BTreeIndex::new(&tx, "test", &layout).unwrap(); - for i in 0..1000 { - index.insert(Constant::Integer(i), RID::new(0, 10)).unwrap(); + fn small_page_test() { + let (_dir, tx) = init_with_size(32); + let mut index = BTreeIndexInner::new("test_index", &tx).unwrap(); + for i in 0..10 { + index.insert(Value::Integer(i), RID::new(i, i)).unwrap(); + } + for i in 0..10 { + index.before_first(Value::Integer(i)).unwrap(); + assert!(index.next().unwrap()); } + tx.commit().unwrap(); } } diff --git a/engine/src/index/b_tree/entry.rs b/engine/src/index/b_tree/entry.rs new file mode 100644 index 0000000..4e1b6ac --- /dev/null +++ b/engine/src/index/b_tree/entry.rs @@ -0,0 +1,131 @@ +use crate::value::{INTEGER_TYPE, VARCHAR_TYPE}; +use crate::{rid::RID, value::Value}; +use common::DbResult; +use common::error::DbError; +use file::block::BlockId; +use file::page::{I32_SIZE, Page, U8_SIZE}; +use std::cmp::Ordering; +use transaction::transaction::Transaction; + +const TYPE_SIZE: usize = U8_SIZE; +const LEN_SIZE: usize = I32_SIZE; +const PTR_SIZE: usize = I32_SIZE; + +#[derive(Clone, Debug)] +pub(crate) struct BTreeEntry { + pub(crate) value: Value, + pub(crate) rid: Vec, +} + +impl BTreeEntry { + pub(crate) fn read( + tx: &Transaction, + block: &BlockId, + offset: usize, + ) -> DbResult<(Self, usize)> { + let mut read = 0; + let value_type = tx.get_u8(block, offset)?; + read += TYPE_SIZE; + let value = match value_type { + VARCHAR_TYPE => Value::Varchar(tx.get_string(block, offset + read)?), + INTEGER_TYPE => Value::Integer(tx.get_i32(block, offset + read)?), + _ => return Err(DbError::other("unexpected value type")), + }; + read += value.size(); + let mut rid = vec![]; + let len = tx.get_i32(block, offset + read)?; + read += LEN_SIZE; + for _ in 0..len { + let block_num = tx.get_i32(block, offset + read)?; + read += I32_SIZE; + let slot = tx.get_i32(block, offset + read)?; + read += I32_SIZE; + rid.push(RID::new(block_num, slot)); + } + Ok((Self { value, rid }, read)) + } + + pub(crate) fn write( + &self, + tx: &Transaction, + block: &BlockId, + offset: usize, + ) -> DbResult { + let mut write = 0; + match &self.value { + Value::Integer(value) => { + tx.set_u8(block, offset, INTEGER_TYPE, true)?; + write += TYPE_SIZE; + tx.set_i32(block, offset + write, *value, true)?; + write += I32_SIZE; + } + Value::Varchar(value) => { + tx.set_u8(block, offset, VARCHAR_TYPE, true)?; + write += TYPE_SIZE; + tx.set_string(block, offset + write, value, true)?; + write += Page::str_space(value); + } + } + let len = self.rid.len() as i32; + tx.set_i32(block, offset + write, len, true)?; + write += LEN_SIZE; + for r in &self.rid { + tx.set_i32(block, offset + write, r.block_num(), true)?; + write += I32_SIZE; + tx.set_i32(block, offset + write, r.slot(), true)?; + write += I32_SIZE; + } + Ok(write) + } + + pub(crate) fn size(&self) -> usize { + let mut size = TYPE_SIZE + self.value.size() + LEN_SIZE; + for _ in &self.rid { + size += 2 * PTR_SIZE; + } + size + } +} + +impl PartialEq for BTreeEntry { + fn eq(&self, other: &Self) -> bool { + self.value == other.value + } +} + +impl Eq for BTreeEntry {} + +impl PartialOrd for BTreeEntry { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for BTreeEntry { + fn cmp(&self, other: &Self) -> Ordering { + self.value.cmp(&other.value) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::init; + + #[test] + fn write_read_entry() { + let (_dir, tx) = init(); + let block = BlockId::new("test", 0); + tx.pin(&block).unwrap(); + + let entry = BTreeEntry { + value: Value::Integer(10), + rid: vec![RID::new(1, 0), RID::new(2, 10), RID::new(3, 100)], + }; + entry.write(&tx, &block, 0).unwrap(); + let restored = BTreeEntry::read(&tx, &block, 0).unwrap(); + + assert_eq!(restored.0.value, entry.value); + assert_eq!(restored.0.rid, entry.rid); + } +} diff --git a/engine/src/index/b_tree/page.rs b/engine/src/index/b_tree/page.rs new file mode 100644 index 0000000..2865894 --- /dev/null +++ b/engine/src/index/b_tree/page.rs @@ -0,0 +1,365 @@ +use common::{DbResult, error::DbError}; +use file::{ + block::BlockId, + page::{I32_SIZE, Page, U8_SIZE}, +}; +use transaction::transaction::Transaction; + +use crate::{ + index::b_tree::{entry::BTreeEntry, pointer::BTreePointer}, + rid::RID, + value::{INTEGER_TYPE, VARCHAR_TYPE, Value}, +}; + +pub(crate) const TYPE_SIZE: usize = U8_SIZE; +pub(crate) const POINTER_SIZE: usize = I32_SIZE; +pub(crate) const LEN_SIZE: usize = I32_SIZE; + +const METADATA: u8 = 1; +const NODE: u8 = 2; +const LEAF: u8 = 3; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum BTreePage { + Metadata { + root: i32, + }, + Node { + parent: i32, + children: Vec, + }, + Leaf { + parent: i32, + values: Vec, + }, +} + +impl BTreePage { + pub(crate) fn read(tx: &Transaction, block: &BlockId) -> DbResult { + tx.pin(block)?; + let mut offset = 0; + let page_type = tx.get_u8(block, offset)?; + offset += 1; + let page = match page_type { + METADATA => { + let root = tx.get_i32(block, offset)?; + Ok(Self::Metadata { root }) + } + NODE => { + let parent = tx.get_i32(block, offset)?; + offset += I32_SIZE; + let len = tx.get_i32(block, offset)? as usize; + offset += I32_SIZE; + let children = read_pointers(tx, block, offset, len)?; + Ok(Self::Node { parent, children }) + } + LEAF => { + let parent = tx.get_i32(block, offset)?; + offset += I32_SIZE; + let len = tx.get_i32(block, offset)? as usize; + offset += I32_SIZE; + let children = read_entries(tx, block, offset, len)?; + Ok(Self::Leaf { + parent, + values: children, + }) + } + _ => Err(DbError::other("invalid page type")), + }; + tx.unpin(block)?; + page + } + + pub(crate) fn write(&self, block: &BlockId, tx: &Transaction) -> DbResult<()> { + tx.pin(block)?; + match self { + Self::Metadata { root } => { + let mut offset = 0; + tx.set_u8(block, offset, METADATA, true)?; + offset += 1; + tx.set_i32(block, offset, *root, true)?; + } + Self::Node { parent, children } => { + let mut offset = 0; + tx.set_u8(block, offset, NODE, true)?; + offset += U8_SIZE; + tx.set_i32(block, offset, *parent, true)?; + offset += I32_SIZE; + write_pointers(tx, block, children, offset)?; + } + Self::Leaf { + parent, + values: children, + } => { + let mut offset = 0; + tx.set_u8(block, offset, LEAF, true)?; + offset += U8_SIZE; + tx.set_i32(block, offset, *parent, true)?; + offset += I32_SIZE; + write_entries(tx, block, children, offset)?; + } + } + tx.unpin(block) + } +} + +fn read_pointers( + tx: &Transaction, + block: &BlockId, + mut offset: usize, + len: usize, +) -> DbResult> { + let mut entries = vec![]; + for _ in 0..len { + let Some((value, read)) = read_value(tx, block, offset)? else { + break; + }; + offset += read; + let block_num = tx.get_i32(block, offset)?; + offset += I32_SIZE; + entries.push(BTreePointer { value, block_num }); + } + Ok(entries) +} + +fn write_pointers( + tx: &Transaction, + block: &BlockId, + children: &[BTreePointer], + mut offset: usize, +) -> DbResult<()> { + let len = children.len() as i32; + tx.set_i32(block, offset, len, true)?; + offset += POINTER_SIZE; + for child in children { + offset += write_value(tx, block, offset, &child.value)?; + tx.set_i32(block, offset, child.block_num, true)?; + offset += I32_SIZE; + } + Ok(()) +} + +fn read_entries( + tx: &Transaction, + block: &BlockId, + mut offset: usize, + len: usize, +) -> DbResult> { + let mut entries = vec![]; + for _ in 0..len { + let (entry, read) = BTreeEntry::read(tx, block, offset)?; + offset += read; + entries.push(entry); + } + Ok(entries) +} + +fn write_entries( + tx: &Transaction, + block: &BlockId, + children: &[BTreeEntry], + mut offset: usize, +) -> DbResult<()> { + let len = children.len() as i32; + tx.set_i32(block, offset, len, true)?; + offset += LEN_SIZE; + for child in children { + let write = child.write(tx, block, offset)?; + offset += write; + } + Ok(()) +} + +fn read_value( + tx: &Transaction, + block: &BlockId, + mut offset: usize, +) -> DbResult> { + let value_type = tx.get_u8(block, offset)?; + offset += 1; + let mut read = 1; + let value = match value_type { + 0 => return Ok(None), + VARCHAR_TYPE => { + let value = Value::Varchar(tx.get_string(block, offset)?); + let size = Page::str_space(value.as_str()?); + read += size; + value + } + INTEGER_TYPE => { + let value = Value::Integer(tx.get_i32(block, offset)?); + read += I32_SIZE; + value + } + _ => return Err(DbError::other("invalid index value type")), + }; + Ok(Some((value, read))) +} + +fn write_value( + tx: &Transaction, + block: &BlockId, + mut offset: usize, + value: &Value, +) -> DbResult { + let mut write = 0; + match value { + Value::Integer(value) => { + tx.set_u8(block, offset, INTEGER_TYPE, true)?; + write += U8_SIZE; + offset += write; + tx.set_i32(block, offset, *value, true)?; + write += I32_SIZE; + } + Value::Varchar(value) => { + let size = file::page::Page::str_space(value); + tx.set_u8(block, offset, VARCHAR_TYPE, true)?; + write += U8_SIZE; + offset += write; + tx.set_string(block, offset, value, true)?; + write += size; + } + } + Ok(write) +} + +pub(crate) fn pointer_index(values: &[BTreePointer], value: &Value) -> usize { + values + .binary_search_by(|k| k.value.cmp(value)) + .unwrap_or_else(|x| if x == 0 { 0 } else { x - 1 }) +} + +pub(crate) fn insert_pointer(values: &mut Vec, value: BTreePointer) { + let idx = values + .binary_search_by(|kv| kv.value.cmp(&value.value)) + .unwrap_or_else(|x| x); + if idx < values.len() && values[idx].value == value.value { + values[idx] = value; + } else if idx >= values.len() { + values.push(value); + } else { + values.insert(idx, value); + } +} + +pub(crate) fn insert_entry(values: &mut Vec, key: Value, value: RID) { + let idx = values + .binary_search_by(|kv| kv.value.cmp(&key)) + .unwrap_or_else(|x| x); + if idx < values.len() && values[idx].value == key { + values[idx].rid.push(value); + } else if idx >= values.len() { + values.push(BTreeEntry { + value: key, + rid: vec![value], + }); + } else { + values.insert( + idx, + BTreeEntry { + value: key, + rid: vec![value], + }, + ); + } +} + +pub(crate) fn split_pointers( + mut values: Vec, + block_size: usize, +) -> (Vec, Vec) { + let mid = values.len() / 2; + let mut right = values.split_off(mid); + let mut size = node_size(&values); + while size > block_size { + let value = right.remove(0); + size -= TYPE_SIZE + value.value.size() + POINTER_SIZE; + values.push(value); + } + (values, right) +} + +pub(crate) fn split_entries( + mut values: Vec, + block_size: usize, +) -> (Vec, Vec) { + let mid = values.len() / 2; + let mut right = values.split_off(mid); + let mut size = leaf_size(&values); + while size > block_size { + let value = right.remove(0); + size -= TYPE_SIZE + value.value.size() + POINTER_SIZE + POINTER_SIZE; + values.push(value); + } + (values, right) +} + +pub(crate) fn node_size(values: &[BTreePointer]) -> usize { + let mut size = TYPE_SIZE + POINTER_SIZE + LEN_SIZE; + for value in values { + size += TYPE_SIZE; + size += value.value.size(); + size += POINTER_SIZE; + } + size +} + +pub(crate) fn leaf_size(values: &[BTreeEntry]) -> usize { + let mut size = TYPE_SIZE + POINTER_SIZE + LEN_SIZE; + for value in values { + size += value.size(); + } + size +} + +#[cfg(test)] +mod tests { + use crate::{rid::RID, tests::init}; + + use super::*; + + #[test] + fn write_read_metadata() { + let (_dir, tx) = init(); + + let metadata = BTreePage::Metadata { root: 11 }; + let block = BlockId::new("test_index", 0); + metadata.write(&block, &tx).unwrap(); + let restored = BTreePage::read(&tx, &block).unwrap(); + assert_eq!(restored, metadata); + } + + #[test] + fn write_read_node() { + let (_dir, tx) = init(); + + let node = BTreePage::Node { + parent: 1337, + children: vec![BTreePointer { + value: Value::Integer(1000), + block_num: -1337, + }], + }; + let block = BlockId::new("test_index", 1); + node.write(&block, &tx).unwrap(); + let restored = BTreePage::read(&tx, &block).unwrap(); + assert_eq!(restored, node); + } + + #[test] + fn write_read_leaf() { + let (_dir, tx) = init(); + + let leaf = BTreePage::Leaf { + parent: 1337, + values: vec![BTreeEntry { + value: Value::Integer(1000), + rid: vec![RID::new(100, 123)], + }], + }; + let block = BlockId::new("test_index", 1); + leaf.write(&block, &tx).unwrap(); + let restored = BTreePage::read(&tx, &block).unwrap(); + assert_eq!(restored, leaf); + } +} diff --git a/engine/src/index/b_tree/pointer.rs b/engine/src/index/b_tree/pointer.rs new file mode 100644 index 0000000..c5dea34 --- /dev/null +++ b/engine/src/index/b_tree/pointer.rs @@ -0,0 +1,7 @@ +use crate::value::Value; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct BTreePointer { + pub(crate) value: Value, + pub(crate) block_num: i32, +} diff --git a/engine/src/index/btree_dir.rs b/engine/src/index/btree_dir.rs deleted file mode 100644 index e581074..0000000 --- a/engine/src/index/btree_dir.rs +++ /dev/null @@ -1,124 +0,0 @@ -use std::sync::{Arc, RwLock}; - -use common::{DbResult, error::DbError}; -use file::block::BlockId; -use transaction::transaction::Transaction; - -use crate::{ - constant::Constant, - index::{btree_page::BTreePage, dir_entry::DirEntry}, - layout::Layout, -}; - -pub struct BTreeDirLock { - tx: Arc, - layout: Arc, - contents: BTreePage, - filename: String, -} - -impl BTreeDirLock { - pub fn new(tx: &Arc, block: BlockId, layout: &Arc) -> DbResult { - let filename = block.filename.clone(); - Ok(Self { - tx: Arc::clone(tx), - layout: Arc::clone(layout), - contents: BTreePage::new(tx, block, layout)?, - filename, - }) - } - - pub fn close(&self) -> DbResult<()> { - self.contents.close() - } - - pub fn search(&mut self, key: &Constant) -> DbResult { - let mut child_block = self.find_child_block(key)?; - while self.contents.get_flag()? > 0 { - self.contents.close()?; - self.contents = BTreePage::new(&self.tx, child_block.clone(), &self.layout)?; - child_block = self.find_child_block(key)?; - } - Ok(child_block.num) - } - - pub fn make_new_root(&self, e: DirEntry) -> DbResult<()> { - let first_val = self.contents.get_data_val(0)?; - let level = self.contents.get_flag()?; - let new_block = self.contents.split(0, level)?; - let old_root = DirEntry::new(first_val, new_block.num); - self.insert_entry(old_root)?; - self.insert_entry(e)?; - self.contents.set_flag(level + 1)?; - Ok(()) - } - - pub fn insert(&self, e: DirEntry) -> DbResult> { - if self.contents.get_flag()? == 0 { - return self.insert_entry(e); - } - let child_block = self.find_child_block(&e.value)?; - let child = BTreeDirLock::new(&self.tx, child_block, &self.layout)?; - let entry = child.insert(e)?; - child.close()?; - if let Some(entry) = entry { - self.insert_entry(entry) - } else { - Ok(None) - } - } - - fn insert_entry(&self, e: DirEntry) -> DbResult> { - let new_slot = 1 + self.contents.find_slot_before(&e.value)?; - self.contents.insert_dir(new_slot, e.value, e.block_num)?; - if !self.contents.is_full()? { - return Ok(None); - } - let level = self.contents.get_flag()?; - let split_pos = self.contents.get_num_recs()? / 2; - let split_val = self.contents.get_data_val(split_pos)?; - let new_block = self.contents.split(split_pos, level)?; - Ok(Some(DirEntry::new(split_val, new_block.num))) - } - - fn find_child_block(&self, key: &Constant) -> DbResult { - let mut slot = self.contents.find_slot_before(key)?; - if self.contents.get_data_val(slot + 1)? == *key { - slot += 1; - } - let block_num = self.contents.get_child_num(slot)?; - Ok(BlockId::new(&self.filename, block_num)) - } -} - -pub struct BTreeDir { - lock: RwLock, -} - -impl BTreeDir { - pub fn new(tx: &Arc, block: BlockId, layout: &Arc) -> DbResult { - Ok(Self { - lock: RwLock::new(BTreeDirLock::new(tx, block, layout)?), - }) - } - - pub fn close(&self) -> DbResult<()> { - let read = self.lock.read().map_err(DbError::lock)?; - read.close() - } - - pub fn search(&self, key: &Constant) -> DbResult { - let mut write = self.lock.write().map_err(DbError::lock)?; - write.search(key) - } - - pub fn make_new_root(&self, e: DirEntry) -> DbResult<()> { - let read = self.lock.read().map_err(DbError::lock)?; - read.make_new_root(e) - } - - pub fn insert(&self, e: DirEntry) -> DbResult> { - let read = self.lock.read().map_err(DbError::lock)?; - read.insert(e) - } -} diff --git a/engine/src/index/btree_leaf.rs b/engine/src/index/btree_leaf.rs deleted file mode 100644 index 47c609b..0000000 --- a/engine/src/index/btree_leaf.rs +++ /dev/null @@ -1,173 +0,0 @@ -use std::{ - cmp::Ordering, - sync::{Arc, RwLock}, -}; - -use common::{DbResult, error::DbError}; -use file::block::BlockId; -use transaction::transaction::Transaction; - -use crate::{ - constant::Constant, - index::{btree_page::BTreePage, dir_entry::DirEntry}, - layout::Layout, - rid::RID, -}; - -pub struct BTreeLeafLock { - tx: Arc, - layout: Arc, - key: Constant, - contents: BTreePage, - current_slot: i32, - filename: String, -} - -impl BTreeLeafLock { - pub fn new( - tx: &Arc, - block: BlockId, - layout: &Arc, - key: Constant, - ) -> DbResult { - let filename = block.filename.clone(); - let contents = BTreePage::new(tx, block, layout)?; - let current_slot = contents.find_slot_before(&key)?; - Ok(Self { - tx: Arc::clone(tx), - layout: Arc::clone(layout), - filename, - current_slot, - key, - contents, - }) - } - - pub fn close(&self) -> DbResult<()> { - self.contents.close() - } - - pub fn go_next(&mut self) -> DbResult { - self.current_slot += 1; - if self.current_slot >= self.contents.get_num_recs()? { - self.try_overflow() - } else if self.contents.get_data_val(self.current_slot)? == self.key { - Ok(true) - } else { - self.try_overflow() - } - } - - pub fn get_data_rid(&self) -> DbResult { - self.contents.get_data_rid(self.current_slot) - } - - pub fn insert(&mut self, rid: RID) -> DbResult> { - if self.contents.get_flag()? >= 0 - && self.contents.get_data_val(0)?.cmp(&self.key) == Ordering::Greater - { - let first_val = self.contents.get_data_val(0)?; - let new_block = self.contents.split(0, self.contents.get_flag()?)?; - self.contents.set_flag(-1)?; - self.current_slot = 0; - self.contents - .insert_leaf(self.current_slot, self.key.clone(), rid)?; - return Ok(Some(DirEntry::new(first_val, new_block.num))); - } - self.current_slot += 1; - self.contents - .insert_leaf(self.current_slot, self.key.clone(), rid)?; - if !self.contents.is_full()? { - return Ok(None); - } - let first_key = self.contents.get_data_val(0)?; - let last_key = self - .contents - .get_data_val(self.contents.get_num_recs()? - 1)?; - if last_key == first_key { - let new_block = self.contents.split(1, self.contents.get_flag()?)?; - self.contents.set_flag(new_block.num)?; - Ok(None) - } else { - let mut split_pos = self.contents.get_num_recs()? / 2; - let mut split_key = self.contents.get_data_val(split_pos)?; - if split_key == first_key { - while self.contents.get_data_val(split_pos)? == split_key { - split_pos += 1; - } - split_key = self.contents.get_data_val(split_pos)?; - } else { - while self.contents.get_data_val(split_pos - 1)? == split_key { - split_pos -= 1; - } - } - let new_block = self.contents.split(split_pos, -1)?; - Ok(Some(DirEntry::new(split_key, new_block.num))) - } - } - - pub fn try_overflow(&mut self) -> DbResult { - let first_key = self.contents.get_data_val(0)?; - let flag = self.contents.get_flag()?; - if self.key != first_key || flag < 0 { - return Ok(false); - } - self.contents.close()?; - let next_block = BlockId::new(&self.filename, flag); - self.contents = BTreePage::new(&self.tx, next_block, &self.layout)?; - self.current_slot = 0; - Ok(true) - } - - fn delete(&mut self, rid: RID) -> DbResult<()> { - while self.go_next()? { - if self.get_data_rid()? == rid { - self.contents.delete(self.current_slot)?; - return Ok(()); - } - } - Ok(()) - } -} - -pub struct BTreeLeaf { - lock: RwLock, -} - -impl BTreeLeaf { - pub fn new( - tx: &Arc, - block: BlockId, - layout: &Arc, - key: Constant, - ) -> DbResult { - Ok(Self { - lock: RwLock::new(BTreeLeafLock::new(tx, block, layout, key)?), - }) - } - - pub fn insert(&self, rid: RID) -> DbResult> { - let mut write = self.lock.write().map_err(DbError::lock)?; - write.insert(rid) - } - - pub fn next(&self) -> DbResult { - let mut write = self.lock.write().map_err(DbError::lock)?; - write.go_next() - } - - pub fn get_data_rid(&self) -> DbResult { - let read = self.lock.read().map_err(DbError::lock)?; - read.get_data_rid() - } - - pub fn close(&self) -> DbResult<()> { - let read = self.lock.read().map_err(DbError::lock)?; - read.close() - } - - pub fn delete(&self, rid: RID) -> DbResult<()> { - let mut write = self.lock.write().map_err(DbError::lock)?; - write.delete(rid) - } -} diff --git a/engine/src/index/btree_page.rs b/engine/src/index/btree_page.rs deleted file mode 100644 index dbfa900..0000000 --- a/engine/src/index/btree_page.rs +++ /dev/null @@ -1,342 +0,0 @@ -use std::{ - cmp::Ordering, - sync::{Arc, RwLock}, -}; - -use common::{DbResult, error::DbError}; -use file::{block::BlockId, page::I32_SIZE}; -use transaction::transaction::Transaction; - -use crate::{constant::Constant, field_info::FieldInfo, layout::Layout, rid::RID}; - -pub const VALUE: &str = "value"; -pub const BLOCK: &str = "block"; -pub const ID: &str = "id"; - -struct BTreePageLock { - tx: Arc, - current_block: Option, - layout: Arc, -} - -impl BTreePageLock { - fn new(tx: &Arc, block: BlockId, layout: &Arc) -> DbResult { - tx.pin(&block)?; - Ok(Self { - tx: Arc::clone(tx), - current_block: Some(block), - layout: Arc::clone(layout), - }) - } - - fn find_slot_before(&self, key: &Constant) -> DbResult { - let mut slot = 0; - while slot < self.get_num_recs()? && self.get_data_val(slot)?.cmp(key) == Ordering::Less { - slot += 1 - } - Ok(slot - 1) - } - - fn close(&mut self) -> DbResult<()> { - if let Some(block) = &self.current_block { - self.tx.unpin(block)?; - } - self.current_block = None; - Ok(()) - } - - fn is_full(&self) -> DbResult { - Ok(self.slotpos(self.get_num_recs()? + 1) >= self.tx.block_size()) - } - - fn split(&self, splitpos: i32, flag: i32) -> DbResult { - let new_block = self.append_new(flag)?; - let mut page = BTreePageLock::new(&self.tx, new_block.clone(), &self.layout)?; - self.transfer_records(splitpos, &page)?; - page.set_flag(flag)?; - page.close()?; - Ok(new_block) - } - - fn get_data_val(&self, slot: i32) -> DbResult { - self.get_val(slot, VALUE) - } - - fn get_flag(&self) -> DbResult { - if let Some(block) = &self.current_block { - self.tx.get_i32(block, 0) - } else { - Err(DbError::other("cannot access index flag")) - } - } - - fn set_flag(&self, flag: i32) -> DbResult<()> { - if let Some(block) = &self.current_block { - self.tx.set_i32(block, 0, flag, true) - } else { - Err(DbError::other("cannot access index flag")) - } - } - - fn append_new(&self, flag: i32) -> DbResult { - let Some(current_block) = &self.current_block else { - return Err(DbError::other("cannot access index flag")); - }; - let block = self.tx.append(¤t_block.filename)?; - self.tx.pin(&block)?; - self.format(&block, flag)?; - Ok(block) - } - - fn format(&self, block: &BlockId, flag: i32) -> DbResult<()> { - self.tx.set_i32(block, 0, flag, false)?; - self.tx.set_i32(block, I32_SIZE, 0, false)?; - let recsize = self.layout.slotsize(); - let mut pos = 2 * I32_SIZE as i32; - while pos + recsize <= self.tx.block_size() { - self.make_default_record(block, pos)?; - pos += recsize; - } - Ok(()) - } - - fn make_default_record(&self, block: &BlockId, pos: i32) -> DbResult<()> { - for (field, info) in self.layout.schema().fields()? { - let offset = self.layout.offset(&field); - let offset = pos as usize + offset as usize; - match info { - FieldInfo::Integer => self.tx.set_i32(block, offset, 0, false)?, - FieldInfo::Varchar(_) => self.tx.set_string(block, offset, "", false)?, - } - } - Ok(()) - } - - fn get_child_num(&self, slot: i32) -> DbResult { - self.get_i32(slot, BLOCK) - } - - fn insert_dir(&self, slot: i32, value: Constant, blocknum: i32) -> DbResult<()> { - self.insert(slot)?; - self.set_val(slot, VALUE, value)?; - self.set_i32(slot, BLOCK, blocknum)?; - Ok(()) - } - - fn get_data_rid(&self, slot: i32) -> DbResult { - let block_num = self.get_i32(slot, BLOCK)?; - let slot = self.get_i32(slot, ID)?; - Ok(RID::new(block_num, slot)) - } - - fn insert_leaf(&self, slot: i32, value: Constant, rid: RID) -> DbResult<()> { - self.insert(slot)?; - self.set_val(slot, VALUE, value)?; - self.set_i32(slot, BLOCK, rid.block_num())?; - self.set_i32(slot, ID, rid.slot())?; - Ok(()) - } - - fn delete(&self, slot: i32) -> DbResult<()> { - let start = slot as usize + 1; - let end = self.get_num_recs()? as usize; - for i in start..end { - let i = i as i32; - self.copy_record(i, i - 1)?; - } - self.set_num_recs(self.get_num_recs()? - 1)?; - Ok(()) - } - - fn get_num_recs(&self) -> DbResult { - if let Some(block) = &self.current_block { - self.tx.get_i32(block, 4) - } else { - Err(DbError::other("cannot get block")) - } - } - - fn get_i32(&self, slot: i32, field: &str) -> DbResult { - let pos = self.field_pos(slot, field); - if let Some(block) = &self.current_block { - self.tx.get_i32(block, pos as usize) - } else { - Err(DbError::other("cannot get integer")) - } - } - - fn get_string(&self, slot: i32, field: &str) -> DbResult { - let pos = self.field_pos(slot, field); - if let Some(block) = &self.current_block { - self.tx.get_string(block, pos as usize) - } else { - Err(DbError::other("cannot get string value")) - } - } - - fn get_val(&self, slot: i32, field: &str) -> DbResult { - let Some(info) = self.layout.schema().info(field)? else { - return Err(DbError::FieldNotExists(field.to_string())); - }; - match info { - FieldInfo::Integer => Ok(Constant::Integer(self.get_i32(slot, field)?)), - FieldInfo::Varchar(_) => Ok(Constant::Varchar(self.get_string(slot, field)?)), - } - } - - fn set_i32(&self, slot: i32, field: &str, value: i32) -> DbResult<()> { - let pos = self.field_pos(slot, field); - if let Some(block) = &self.current_block { - self.tx.set_i32(block, pos as usize, value, true) - } else { - Err(DbError::other("cannot set integer value")) - } - } - - fn set_string(&self, slot: i32, field: &str, value: &str) -> DbResult<()> { - let pos = self.field_pos(slot, field); - if let Some(block) = &self.current_block { - self.tx.set_string(block, pos as usize, value, true) - } else { - Err(DbError::other("cannot set integer value")) - } - } - - fn set_val(&self, slot: i32, field: &str, value: Constant) -> DbResult<()> { - match self.layout.schema().info(field)? { - Some(FieldInfo::Integer) => self.set_i32(slot, field, value.as_i32()?), - Some(FieldInfo::Varchar(_)) => self.set_string(slot, field, value.as_str()?), - None => Err(DbError::FieldNotExists(field.to_string())), - } - } - - fn set_num_recs(&self, n: i32) -> DbResult<()> { - let Some(block) = &self.current_block else { - return Err(DbError::other("cannot set num recs")); - }; - self.tx.set_i32(block, I32_SIZE, n, true) - } - - fn insert(&self, slot: i32) -> DbResult<()> { - let num_recs = self.get_num_recs()?; - for i in (slot + 1..=num_recs).rev() { - self.copy_record(i - 1, i)?; - } - self.set_num_recs(num_recs + 1) - } - - fn copy_record(&self, from: i32, to: i32) -> DbResult<()> { - let schema = self.layout.schema(); - for (field, _) in schema.fields()? { - let value = self.get_val(from, &field)?; - self.set_val(to, &field, value)?; - } - Ok(()) - } - - fn transfer_records(&self, slot: i32, dest: &Self) -> DbResult<()> { - let mut dest_slot = 0; - while slot < self.get_num_recs()? { - dest.insert(dest_slot)?; - let schema = self.layout.schema(); - for (field, _) in schema.fields()? { - dest.set_val(dest_slot, &field, self.get_val(slot, &field)?)?; - } - self.delete(slot)?; - dest_slot += 1; - } - Ok(()) - } - - fn field_pos(&self, slot: i32, field: &str) -> i32 { - let offset = self.layout.offset(field); - self.slotpos(slot) + offset - } - - fn slotpos(&self, slot: i32) -> i32 { - let slotsize = self.layout.slotsize(); - 4 + 4 + (slot * slotsize) - } -} - -pub struct BTreePage { - lock: RwLock, -} - -impl BTreePage { - pub fn new(tx: &Arc, block: BlockId, layout: &Arc) -> DbResult { - Ok(Self { - lock: RwLock::new(BTreePageLock::new(tx, block, layout)?), - }) - } - - pub fn find_slot_before(&self, key: &Constant) -> DbResult { - let read = self.lock.read().map_err(DbError::lock)?; - read.find_slot_before(key) - } - - pub fn close(&self) -> DbResult<()> { - let mut read = self.lock.write().map_err(DbError::lock)?; - read.close() - } - - pub fn is_full(&self) -> DbResult { - let read = self.lock.read().map_err(DbError::lock)?; - read.is_full() - } - - pub fn get_num_recs(&self) -> DbResult { - let read = self.lock.read().map_err(DbError::lock)?; - read.get_num_recs() - } - - pub fn get_data_val(&self, slot: i32) -> DbResult { - let read = self.lock.read().map_err(DbError::lock)?; - read.get_data_val(slot) - } - - pub fn get_data_rid(&self, slot: i32) -> DbResult { - let read = self.lock.read().map_err(DbError::lock)?; - read.get_data_rid(slot) - } - - pub fn get_flag(&self) -> DbResult { - let read = self.lock.read().map_err(DbError::lock)?; - read.get_flag() - } - - pub fn split(&self, pos: i32, flag: i32) -> DbResult { - let read = self.lock.read().map_err(DbError::lock)?; - read.split(pos, flag) - } - - pub fn set_flag(&self, flag: i32) -> DbResult<()> { - let read = self.lock.read().map_err(DbError::lock)?; - read.set_flag(flag) - } - - pub fn insert_leaf(&self, slot: i32, value: Constant, rid: RID) -> DbResult<()> { - let read = self.lock.read().map_err(DbError::lock)?; - read.insert_leaf(slot, value, rid) - } - - pub fn insert_dir(&self, slot: i32, value: Constant, blocknum: i32) -> DbResult<()> { - let read = self.lock.read().map_err(DbError::lock)?; - read.insert_dir(slot, value, blocknum) - } - - pub fn get_child_num(&self, slot: i32) -> DbResult { - let read = self.lock.read().map_err(DbError::lock)?; - read.get_child_num(slot) - } - - pub fn format(&self, block: &BlockId, flag: i32) -> DbResult<()> { - let read = self.lock.read().map_err(DbError::lock)?; - read.format(block, flag) - } - - pub fn delete(&self, slot: i32) -> DbResult<()> { - let read = self.lock.read().map_err(DbError::lock)?; - read.delete(slot) - } -} diff --git a/engine/src/index/dir_entry.rs b/engine/src/index/dir_entry.rs deleted file mode 100644 index 91d6fdb..0000000 --- a/engine/src/index/dir_entry.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::constant::Constant; - -pub struct DirEntry { - pub value: Constant, - pub block_num: i32, -} - -impl DirEntry { - pub fn new(value: Constant, block_num: i32) -> Self { - Self { value, block_num } - } -} diff --git a/engine/src/index/indexer.rs b/engine/src/index/indexer.rs new file mode 100644 index 0000000..4ab8f11 --- /dev/null +++ b/engine/src/index/indexer.rs @@ -0,0 +1,5 @@ +use crate::index::b_tree::BTreeIndex; + +pub(crate) enum Indexer { + BTree(BTreeIndex), +} diff --git a/engine/src/index_mgr.rs b/engine/src/index_mgr.rs index 27de484..0752260 100644 --- a/engine/src/index_mgr.rs +++ b/engine/src/index_mgr.rs @@ -1,18 +1,18 @@ -use std::{collections::HashMap, rc::Rc, sync::Arc}; - use common::DbResult; use common::error::DbError; +use std::rc::Rc; +use std::{collections::HashMap, sync::Arc}; use transaction::transaction::Transaction; -use crate::index::btree_page::{BLOCK, ID}; -use crate::{ - index::{Index, b_tree::BTreeIndex, btree_page::VALUE}, - layout::Layout, - scan::{Scan, table::TableScan}, - schema::Schema, - stat_mgr::{StatInfo, StatMgr}, - table_mgr::TableMgr, -}; +use crate::element::Element; +use crate::index::Index; +use crate::index::b_tree::BTreeIndex; +use crate::layout::Layout; +use crate::scan::Scan; +use crate::scan::table::TableScan; +use crate::schema::{Schema, SchemaBuilder}; +use crate::stat_mgr::{StatInfo, StatMgr}; +use crate::table_mgr::TableMgr; const IDX_TABLE: &str = "idx"; const IDX_NAME: &str = "idx_name"; @@ -23,50 +23,38 @@ const MAX_LENGTH: i32 = 16; #[derive(Clone)] pub struct IndexInfo { idx_name: String, - field_name: String, + field_name: Element, tx: Arc, - layout: Arc, + slot_size: i32, stat: StatInfo, } impl IndexInfo { pub fn new( idx_name: String, - field_name: String, - schema: &Arc, + field_name: Element, + schema: &Schema, tx: &Arc, stat: StatInfo, ) -> DbResult { - let layout = Arc::new(Self::create_idx_layout(&field_name, schema)?); + let Some(info) = schema.info(&field_name) else { + return Err(DbError::FieldNotExists(field_name.to_string())); + }; Ok(Self { idx_name, + slot_size: info.length(), field_name, tx: Arc::clone(tx), - layout, stat, }) } - fn create_idx_layout(field_name: &str, table_schema: &Arc) -> DbResult { - let schema = Arc::new(Schema::default()); - schema.add_int_field(BLOCK.to_string())?; - schema.add_int_field(ID.to_string())?; - if let Some(info) = table_schema.info(field_name)? { - schema.add_field(VALUE.to_string(), info)?; - } - Layout::new(&schema) - } - pub fn open(&self) -> DbResult> { - Ok(Rc::new(BTreeIndex::new( - &self.tx, - &self.idx_name, - &self.layout, - )?)) + Ok(Rc::new(BTreeIndex::new(&self.idx_name, &self.tx)?)) } pub fn block_accessed(&self) -> DbResult { - let rpb = self.tx.block_size() / self.layout.slotsize(); + let rpb = self.tx.block_size() / self.slot_size; let num_blocks = self.stat.records_output() / rpb; Ok(num_blocks) } @@ -75,8 +63,8 @@ impl IndexInfo { self.stat.records_output() / self.stat.distinct_values() } - pub fn distinct_values(&self, field_name: &str) -> i32 { - if self.field_name == field_name { + pub fn distinct_values(&self, field_name: &Element) -> i32 { + if self.field_name == *field_name { 1 } else { self.stat.distinct_values() @@ -84,34 +72,36 @@ impl IndexInfo { } } +#[derive(Clone)] pub struct IndexMgr { - layout: Arc, - table_mgr: Arc, - stat_mgr: Arc, + layout: Layout, + table_mgr: TableMgr, + stat_mgr: StatMgr, } impl IndexMgr { pub fn new( is_new: bool, - table_mgr: &Arc, - stat_mgr: &Arc, + table_mgr: TableMgr, + stat_mgr: StatMgr, tx: &Arc, ) -> DbResult { if is_new { - let schema = Schema::default(); - schema.add_string_field(IDX_NAME.to_string(), MAX_LENGTH)?; - schema.add_string_field(TABLE_NAME.to_string(), MAX_LENGTH)?; - schema.add_string_field(FIELD_NAME.to_string(), MAX_LENGTH)?; - table_mgr.create_table(IDX_TABLE, &Arc::new(schema), tx)?; + let schema = SchemaBuilder::default() + .add_string_field(Element::raw(IDX_NAME), MAX_LENGTH) + .add_string_field(Element::raw(TABLE_NAME), MAX_LENGTH) + .add_string_field(Element::raw(FIELD_NAME), MAX_LENGTH) + .build(); + table_mgr.create_table(IDX_TABLE, schema, tx)?; } - let layout = Arc::new(table_mgr.get_layout(IDX_TABLE, tx)?); - if layout.schema().fields()?.is_empty() { + let layout = table_mgr.get_layout(IDX_TABLE, tx)?; + if layout.schema().fields().is_empty() { return Err(DbError::other("cannot initialize inner index table")); } Ok(Self { layout, - table_mgr: Arc::clone(table_mgr), - stat_mgr: Arc::clone(stat_mgr), + table_mgr, + stat_mgr, }) } @@ -122,11 +112,11 @@ impl IndexMgr { field_name: &str, tx: &Arc, ) -> DbResult<()> { - let ts = TableScan::new(tx, IDX_TABLE, &self.layout)?; + let ts = TableScan::new(tx, IDX_TABLE, self.layout.clone())?; ts.insert()?; - ts.set_string(IDX_NAME, idx_name)?; - ts.set_string(TABLE_NAME, table_name)?; - ts.set_string(FIELD_NAME, field_name)?; + ts.set_string(&Element::raw(IDX_NAME), idx_name)?; + ts.set_string(&Element::raw(TABLE_NAME), table_name)?; + ts.set_string(&Element::raw(FIELD_NAME), field_name)?; ts.close() } @@ -134,18 +124,25 @@ impl IndexMgr { &self, table_name: &str, tx: &Arc, - ) -> DbResult> { + ) -> DbResult> { let mut result = HashMap::new(); - let ts = TableScan::new(tx, IDX_TABLE, &self.layout)?; + let ts = TableScan::new(tx, IDX_TABLE, self.layout.clone())?; while ts.next()? { - if ts.get_string(TABLE_NAME)? == table_name { - let idx_name = ts.get_string(IDX_NAME)?; - let field_name = ts.get_string(FIELD_NAME)?; - let layout = Arc::new(self.table_mgr.get_layout(table_name, tx)?); - let stat = self.stat_mgr.get_stat_info(table_name, &layout, tx)?; - let index = - IndexInfo::new(idx_name, field_name.clone(), &layout.schema(), tx, stat)?; - result.insert(field_name, index); + if ts.get_string(&Element::raw(TABLE_NAME))? == table_name { + let idx_name = ts.get_string(&Element::raw(IDX_NAME))?; + let field_name = ts.get_string(&Element::raw(FIELD_NAME))?; + let layout = self.table_mgr.get_layout(table_name, tx)?; + let stat = self + .stat_mgr + .get_stat_info(table_name, layout.clone(), tx)?; + let index = IndexInfo::new( + idx_name, + Element::raw(&field_name), + layout.schema(), + tx, + stat, + )?; + result.insert(Element::Raw(field_name), index); } } ts.close()?; diff --git a/engine/src/layout.rs b/engine/src/layout.rs index 00f7d3d..28cfd7f 100644 --- a/engine/src/layout.rs +++ b/engine/src/layout.rs @@ -1,40 +1,40 @@ -use std::{collections::HashMap, sync::Arc}; +use std::{collections::HashMap, rc::Rc}; -use common::DbResult; use file::page::I32_SIZE; -use crate::schema::Schema; +use crate::{element::Element, schema::Schema}; -pub struct Layout { - schema: Arc, - offsets: HashMap, +#[derive(Debug, Clone)] +struct LayoutInner { + schema: Schema, + offsets: HashMap, slotsize: i32, } -impl Layout { - pub fn new(schema: &Arc) -> DbResult { +impl LayoutInner { + fn new(schema: Schema) -> Self { let mut offsets = HashMap::new(); let mut pos = I32_SIZE as i32; - for (field, info) in schema.fields()? { + for (field, info) in schema.fields() { offsets.insert(field, pos); pos += info.length(); } - Ok(Self { - schema: Arc::clone(schema), + Self { + schema, offsets, slotsize: pos, - }) + } } - pub fn from(schema: &Arc, offsets: HashMap, slotsize: i32) -> Self { + fn from(schema: Schema, offsets: HashMap, slotsize: i32) -> Self { Self { - schema: Arc::clone(schema), + schema, offsets, slotsize, } } - pub fn offset(&self, fieldname: &str) -> i32 { + fn offset(&self, fieldname: &Element) -> i32 { if let Some(offset) = self.offsets.get(fieldname) { *offset } else { @@ -42,11 +42,51 @@ impl Layout { } } - pub fn schema(&self) -> Arc { - Arc::clone(&self.schema) + fn schema(&self) -> &Schema { + &self.schema } - pub fn slotsize(&self) -> i32 { + fn slotsize(&self) -> i32 { self.slotsize } } + +#[derive(Debug, Clone)] +pub struct Layout(Rc); + +impl Layout { + pub fn new(schema: Schema) -> Self { + Self(Rc::new(LayoutInner::new(schema))) + } + + pub fn from(schema: Schema, offsets: HashMap, slotsize: i32) -> Self { + Self(Rc::new(LayoutInner::from(schema, offsets, slotsize))) + } + + pub fn offset(&self, field: &Element) -> i32 { + self.0.offset(field) + } + + pub fn schema(&self) -> &Schema { + self.0.schema() + } + + pub fn slotsize(&self) -> i32 { + self.0.slotsize() + } +} + +#[cfg(test)] +mod tests { + use crate::schema::SchemaBuilder; + + use super::*; + + #[test] + fn from() { + let schema = SchemaBuilder::default() + .add_int_field(Element::raw("id")) + .build(); + Layout::from(schema, HashMap::new(), 16); + } +} diff --git a/engine/src/lib.rs b/engine/src/lib.rs index bf3a99e..f6d402e 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -4,9 +4,7 @@ use buffer::mgr::BufferMgr; use common::DbResult; use file::mgr::FileMgr; use log::mgr::LogMgr; -use transaction::{ - lock_table::LockTable, transaction::Transaction, txnum_generator::TxNumGenerator, -}; +use transaction::{lock_table::LockTable, transaction::Transaction}; use crate::{ metadata_mgr::MetadataMgr, @@ -18,7 +16,7 @@ use crate::{ }; pub mod buffer_needs; -pub mod constant; +pub mod element; pub mod field_info; pub mod index; pub mod index_mgr; @@ -35,6 +33,7 @@ pub mod sort_by; pub mod stat_mgr; pub mod table_mgr; mod temp; +pub mod value; pub mod view_mgr; const LOG_FILE: &str = "wal.log"; @@ -42,12 +41,11 @@ const BLOCK_SIZE: usize = 8 * 1024; const NUM_BUFFERS: usize = 1024; pub struct SimpleDB { - tx_num_generator: TxNumGenerator, fm: Arc, lm: Arc, bm: Arc, lock_table: Arc, - md: Arc, + md: MetadataMgr, } impl SimpleDB { @@ -56,18 +54,11 @@ impl SimpleDB { } pub fn configured(dir: &Path, block_size: usize, num_buffers: usize) -> DbResult { - let tx_num_generator = TxNumGenerator::default(); let fm = Arc::new(FileMgr::new(dir, block_size.try_into().unwrap())?); let lm = Arc::new(LogMgr::new(&fm, LOG_FILE.to_string())?); let bm = Arc::new(BufferMgr::new(&fm, &lm, num_buffers)?); let lock_table = Arc::new(LockTable::default()); - let tx = Arc::new(Transaction::new( - &tx_num_generator, - &fm, - &lm, - &bm, - &lock_table, - )?); + let tx = Arc::new(Transaction::new(&fm, &lm, &bm, &lock_table)?); let is_new = fm.is_new()?; if is_new { tracing::debug!("creating new database"); @@ -75,10 +66,9 @@ impl SimpleDB { tracing::debug!("recovering existing database"); tx.recover()?; } - let md = Arc::new(MetadataMgr::new(is_new, &tx)?); + let md = MetadataMgr::new(is_new, &tx)?; tx.commit()?; Ok(Self { - tx_num_generator, fm, lm, bm, @@ -88,18 +78,12 @@ impl SimpleDB { } pub fn get_tx(&self) -> DbResult> { - let tx = Transaction::new( - &self.tx_num_generator, - &self.fm, - &self.lm, - &self.bm, - &self.lock_table, - )?; + let tx = Transaction::new(&self.fm, &self.lm, &self.bm, &self.lock_table)?; Ok(Arc::new(tx)) } - pub fn metadata_mgr(&self) -> Arc { - Arc::clone(&self.md) + pub fn metadata_mgr(&self) -> &MetadataMgr { + &self.md } pub fn query(&self, tx: &Arc, query: &str) -> DbResult> { @@ -114,18 +98,33 @@ impl SimpleDB { } fn planner(&self) -> Planner { - let query_planner = HeuristicQueryPlanner::new(&self.md); - let update_planner = BasicUpdatePlanner::new(&self.md); + let query_planner = HeuristicQueryPlanner::new(self.md.clone()); + let update_planner = BasicUpdatePlanner::new(self.md.clone()); Planner::new(Rc::new(query_planner), Rc::new(update_planner)) } } #[cfg(test)] mod tests { + use super::*; + use crate::element::Element; use std::collections::HashSet; - use tempfile::tempdir; + use tempfile::{TempDir, tempdir}; - use super::*; + pub(crate) fn init() -> (TempDir, Arc) { + init_with_size(512) + } + + pub(crate) fn init_with_size(block_size: i32) -> (TempDir, Arc) { + let dir = tempdir().unwrap(); + let fm = Arc::new(FileMgr::new(dir.path(), block_size).unwrap()); + let lm = Arc::new(LogMgr::new(&fm, "testlog".to_string()).unwrap()); + let bm = Arc::new(BufferMgr::new(&fm, &lm, 16).unwrap()); + let lock_table = Arc::new(LockTable::default()); + + let tx = Arc::new(Transaction::new(&fm, &lm, &bm, &lock_table).unwrap()); + (dir, tx) + } #[test] fn select_with_index() { @@ -144,8 +143,8 @@ mod tests { .query(&tx, "SELECT id, name FROM users WHERE id = 2") .unwrap(); while result.next().unwrap() { - let id = result.get_i32("id").unwrap(); - let name = result.get_string("name").unwrap(); + let id = result.get_i32(&Element::raw("id")).unwrap(); + let name = result.get_string(&Element::raw("name")).unwrap(); assert_eq!(id, 2); assert_eq!(name, "Name"); } @@ -176,7 +175,7 @@ mod tests { } #[test] - fn sort_by() { + fn order_by_field() { let dir = tempdir().unwrap(); let db = SimpleDB::new(dir.path()).unwrap(); let tx = db.get_tx().unwrap(); @@ -201,8 +200,8 @@ mod tests { .unwrap(); for i in 0..1000 { assert!(result.next().unwrap()); - let id = result.get_i32("id").unwrap(); - let name = result.get_string("name").unwrap(); + let id = result.get_i32(&Element::raw("id")).unwrap(); + let name = result.get_string(&Element::raw("name")).unwrap(); assert!(ids.remove(&(id as usize))); if i < 200 { assert_eq!(name, "a"); @@ -238,7 +237,7 @@ mod tests { let result = db.query(&tx, "SELECT id FROM test").unwrap(); while result.next().unwrap() { - let id = result.get_i32("id").unwrap(); + let id = result.get_i32(&Element::raw("id")).unwrap(); assert!(existed.remove(&id)); } assert!(existed.is_empty()); @@ -269,8 +268,8 @@ mod tests { ) .unwrap(); while result.next().unwrap() { - assert_eq!(i, result.get_i32("i1").unwrap()); - assert_eq!(i, result.get_i32("i2").unwrap()); + assert_eq!(i, result.get_i32(&Element::raw("i1")).unwrap()); + assert_eq!(i, result.get_i32(&Element::raw("i2")).unwrap()); } } tx.commit().unwrap(); diff --git a/engine/src/metadata_mgr.rs b/engine/src/metadata_mgr.rs index e401a2e..fb5eade 100644 --- a/engine/src/metadata_mgr.rs +++ b/engine/src/metadata_mgr.rs @@ -3,6 +3,7 @@ use std::{collections::HashMap, sync::Arc}; use common::DbResult; use transaction::transaction::Transaction; +use crate::element::Element; use crate::{ index_mgr::{IndexInfo, IndexMgr}, layout::Layout, @@ -12,19 +13,20 @@ use crate::{ view_mgr::ViewMgr, }; +#[derive(Clone)] pub struct MetadataMgr { - table_mgr: Arc, - view_mgr: Arc, - stat_mgr: Arc, - index_mgr: Arc, + table_mgr: TableMgr, + view_mgr: ViewMgr, + stat_mgr: StatMgr, + index_mgr: IndexMgr, } impl MetadataMgr { pub fn new(is_new: bool, tx: &Arc) -> DbResult { - let table_mgr = Arc::new(TableMgr::new(is_new, tx)?); - let view_mgr = Arc::new(ViewMgr::new(is_new, &table_mgr, tx)?); - let stat_mgr = Arc::new(StatMgr::new(&table_mgr, tx)?); - let index_mgr = Arc::new(IndexMgr::new(is_new, &table_mgr, &stat_mgr, tx)?); + let table_mgr = TableMgr::new(is_new, tx)?; + let view_mgr = ViewMgr::new(is_new, table_mgr.clone(), tx)?; + let stat_mgr = StatMgr::new(table_mgr.clone(), tx)?; + let index_mgr = IndexMgr::new(is_new, table_mgr.clone(), stat_mgr.clone(), tx)?; Ok(Self { table_mgr, view_mgr, @@ -36,7 +38,7 @@ impl MetadataMgr { pub fn create_table( &self, table_name: &str, - schema: &Arc, + schema: Schema, tx: &Arc, ) -> DbResult<()> { self.table_mgr.create_table(table_name, schema, tx) @@ -73,7 +75,7 @@ impl MetadataMgr { pub fn get_stat_info( &self, table: &str, - layout: &Arc, + layout: Layout, tx: &Arc, ) -> DbResult { self.stat_mgr.get_stat_info(table, layout, tx) @@ -83,7 +85,7 @@ impl MetadataMgr { &self, table_name: &str, tx: &Arc, - ) -> DbResult> { + ) -> DbResult> { self.index_mgr.get_index_info(table_name, tx) } } diff --git a/engine/src/plan.rs b/engine/src/plan.rs index a81fca9..9056c35 100644 --- a/engine/src/plan.rs +++ b/engine/src/plan.rs @@ -1,7 +1,8 @@ -use std::{rc::Rc, sync::Arc}; +use std::rc::Rc; use common::DbResult; +use crate::element::Element; use crate::{scan::Scan, schema::Schema}; pub(crate) mod group; @@ -9,10 +10,10 @@ pub mod index; pub(crate) mod materialize; pub(crate) mod merge; pub(crate) mod multibuffer; +pub(crate) mod order; pub mod product; pub mod project; pub mod select; -pub(crate) mod sort; pub mod table; pub trait Plan { @@ -22,7 +23,7 @@ pub trait Plan { fn records_output(&self) -> DbResult; - fn distinct_values(&self, field_name: &str) -> DbResult; + fn distinct_values(&self, field_name: &Element) -> DbResult; - fn schema(&self) -> DbResult>; + fn schema(&self) -> DbResult; } diff --git a/engine/src/plan/group.rs b/engine/src/plan/group.rs index c5d2a77..ec52f15 100644 --- a/engine/src/plan/group.rs +++ b/engine/src/plan/group.rs @@ -3,34 +3,37 @@ use std::{rc::Rc, sync::Arc}; use common::DbResult; use transaction::transaction::Transaction; +use crate::element::Element; +use crate::schema::SchemaBuilder; use crate::{ - plan::{Plan, sort::SortPlan}, + plan::{Plan, order::SortPlan}, scan::group::{AggregationFn, GroupByScan}, schema::Schema, }; pub struct GroupByPlan { plan: Rc, - group_fields: Vec, + group_fields: Vec, aggregation_fn: Vec, - schema: Arc, + schema: Schema, } impl GroupByPlan { pub fn new( tx: &Arc, plan: &Rc, - group_fields: Vec, + group_fields: Vec, aggregation_fn: Vec, ) -> DbResult { - let schema = Arc::new(Schema::default()); + let mut schema = SchemaBuilder::default(); let s = plan.schema()?; for field in &group_fields { - schema.add(field.to_string(), &s)?; + schema = schema.add(field.clone(), &s); } for f in &aggregation_fn { - schema.add_int_field(f.field_name())?; + schema = schema.add_int_field(f.field_name().clone()); } + let schema = schema.build(); let plan = Rc::new(SortPlan::new(tx, plan, group_fields.clone())?); Ok(Self { plan, @@ -63,15 +66,15 @@ impl Plan for GroupByPlan { Ok(num_groups) } - fn distinct_values(&self, field_name: &str) -> DbResult { - if self.plan.schema()?.has_field(field_name)? { + fn distinct_values(&self, field_name: &Element) -> DbResult { + if self.plan.schema()?.has_field(field_name) { self.plan.distinct_values(field_name) } else { self.records_output() } } - fn schema(&self) -> DbResult> { - Ok(Arc::clone(&self.schema)) + fn schema(&self) -> DbResult { + Ok(self.schema.clone()) } } diff --git a/engine/src/plan/index.rs b/engine/src/plan/index.rs index 38bf056..5ab8929 100644 --- a/engine/src/plan/index.rs +++ b/engine/src/plan/index.rs @@ -3,8 +3,9 @@ use std::{rc::Rc, sync::Arc}; use common::DbResult; use transaction::transaction::Transaction; +use crate::element::Element; +use crate::schema::SchemaBuilder; use crate::{ - constant::Constant, index_mgr::IndexInfo, metadata_mgr::MetadataMgr, plan::{Plan, select::SelectPlan, table::TablePlan}, @@ -14,16 +15,17 @@ use crate::{ }, scan::index::{IndexJoinScan, IndexSelectScan}, schema::Schema, + value::Value, }; pub struct IndexSelectPlan { plan: Rc, index: IndexInfo, - value: Constant, + value: Value, } impl IndexSelectPlan { - pub fn new(plan: &Rc, index: IndexInfo, value: Constant) -> Self { + pub fn new(plan: &Rc, index: IndexInfo, value: Value) -> Self { Self { plan: Rc::clone(plan), index, @@ -51,11 +53,11 @@ impl Plan for IndexSelectPlan { Ok(self.index.records_output()) } - fn distinct_values(&self, field_name: &str) -> DbResult { + fn distinct_values(&self, field_name: &Element) -> DbResult { Ok(self.index.distinct_values(field_name)) } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { self.plan.schema() } } @@ -64,8 +66,8 @@ pub struct IndexJoinPlan { p1: Rc, p2: Rc, index: IndexInfo, - field: String, - schema: Arc, + field: Element, + schema: Schema, } impl IndexJoinPlan { @@ -73,13 +75,11 @@ impl IndexJoinPlan { p1: &Rc, p2: &Rc, index: IndexInfo, - field: String, + field: Element, ) -> DbResult { - let schema = Arc::new(Schema::default()); let s1 = p1.schema()?; let s2 = p2.schema()?; - schema.add_all(&s1)?; - schema.add_all(&s2)?; + let schema = SchemaBuilder::default().add_all(&s1).add_all(&s2).build(); Ok(Self { p1: Rc::clone(p1), p2: Rc::clone(p2), @@ -95,7 +95,12 @@ impl Plan for IndexJoinPlan { let s = self.p1.open()?; let ts = self.p2.open()?; let idx = self.index.open()?; - Ok(Rc::new(IndexJoinScan::new(&s, &idx, &self.field, &ts)?)) + Ok(Rc::new(IndexJoinScan::new( + &s, + &idx, + self.field.clone(), + &ts, + )?)) } fn blocks_accessed(&self) -> DbResult { @@ -108,16 +113,16 @@ impl Plan for IndexJoinPlan { Ok(self.p1.records_output()? * self.index.records_output()) } - fn distinct_values(&self, field_name: &str) -> DbResult { - if self.p1.schema()?.has_field(field_name)? { + fn distinct_values(&self, field_name: &Element) -> DbResult { + if self.p1.schema()?.has_field(field_name) { self.p1.distinct_values(field_name) } else { self.p2.distinct_values(field_name) } } - fn schema(&self) -> DbResult> { - Ok(Arc::clone(&self.schema)) + fn schema(&self) -> DbResult { + Ok(self.schema.clone()) } } @@ -201,13 +206,8 @@ impl UpdatePlanner for IndexUpdatePlanner { Ok(count) } - fn execute_create_table( - &self, - data: TableData, - tx: &Arc, - ) -> DbResult { - self.mg - .create_table(&data.name, &Arc::new(data.schema), tx)?; + fn execute_create_table(&self, data: TableData, tx: &Arc) -> DbResult { + self.mg.create_table(&data.name, data.schema.clone(), tx)?; Ok(0) } diff --git a/engine/src/plan/materialize.rs b/engine/src/plan/materialize.rs index fe4fd6f..1d1a239 100644 --- a/engine/src/plan/materialize.rs +++ b/engine/src/plan/materialize.rs @@ -1,3 +1,4 @@ +use crate::element::Element; use crate::layout::Layout; use crate::plan::Plan; use crate::scan::Scan; @@ -25,12 +26,12 @@ impl MaterializePlan { impl Plan for MaterializePlan { fn open(&self) -> DbResult> { let schema = self.source.schema()?; - let temp = TempTable::new(&self.tx, &schema)?; + let temp = TempTable::new(&self.tx, schema.clone())?; let source = self.source.open()?; let dest = temp.open()?; while source.next()? { dest.insert()?; - for (field, _) in schema.fields()? { + for (field, _) in schema.fields() { dest.set_val(&field, source.get_val(&field)?)?; } } @@ -40,7 +41,7 @@ impl Plan for MaterializePlan { } fn blocks_accessed(&self) -> DbResult { - let layout = Layout::new(&self.source.schema()?)?; + let layout = Layout::new(self.source.schema()?); let rpd = self.tx.block_size() / layout.slotsize(); Ok(self.source.records_output()? / rpd) } @@ -49,11 +50,11 @@ impl Plan for MaterializePlan { self.source.records_output() } - fn distinct_values(&self, field_name: &str) -> DbResult { + fn distinct_values(&self, field_name: &Element) -> DbResult { self.source.distinct_values(field_name) } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { self.source.schema() } } diff --git a/engine/src/plan/merge.rs b/engine/src/plan/merge.rs index 6d0b981..cc8bdc9 100644 --- a/engine/src/plan/merge.rs +++ b/engine/src/plan/merge.rs @@ -3,8 +3,10 @@ use std::{rc::Rc, sync::Arc}; use common::DbResult; use transaction::transaction::Transaction; +use crate::element::Element; +use crate::schema::SchemaBuilder; use crate::{ - plan::{Plan, sort::SortPlan}, + plan::{Plan, order::SortPlan}, scan::merge::MergeJoinScan, schema::Schema, }; @@ -12,9 +14,9 @@ use crate::{ pub struct MergeJoinPlan { p1: Rc, p2: Rc, - field_name1: String, - field_name2: String, - schema: Arc, + field_name1: Element, + field_name2: Element, + schema: Schema, } #[allow(dead_code)] @@ -23,17 +25,17 @@ impl MergeJoinPlan { tx: &Arc, p1: &Rc, p2: &Rc, - field_name1: &str, - field_name2: &str, + field_name1: Element, + field_name2: Element, ) -> DbResult { - let p1 = Rc::new(SortPlan::new(tx, p1, vec![field_name1.to_string()])?); - let p2 = Rc::new(SortPlan::new(tx, p2, vec![field_name2.to_string()])?); - let schema = Arc::new(Schema::default()); + let p1 = Rc::new(SortPlan::new(tx, p1, vec![field_name1.clone()])?); + let p2 = Rc::new(SortPlan::new(tx, p2, vec![field_name2.clone()])?); + let schema = SchemaBuilder::default().build(); Ok(Self { p1, p2, - field_name1: field_name1.to_string(), - field_name2: field_name2.to_string(), + field_name1, + field_name2, schema, }) } @@ -46,8 +48,8 @@ impl Plan for MergeJoinPlan { Ok(Rc::new(MergeJoinScan::new( &s1, &s2, - &self.field_name1, - &self.field_name2, + self.field_name1.clone(), + self.field_name2.clone(), )?)) } @@ -63,15 +65,15 @@ impl Plan for MergeJoinPlan { Ok(self.p1.records_output()? * self.p2.records_output()? / max_val) } - fn distinct_values(&self, field_name: &str) -> DbResult { - if self.p1.schema()?.has_field(field_name)? { + fn distinct_values(&self, field_name: &Element) -> DbResult { + if self.p1.schema()?.has_field(field_name) { self.p1.distinct_values(field_name) } else { self.p2.distinct_values(field_name) } } - fn schema(&self) -> DbResult> { - Ok(Arc::clone(&self.schema)) + fn schema(&self) -> DbResult { + Ok(self.schema.clone()) } } diff --git a/engine/src/plan/multibuffer.rs b/engine/src/plan/multibuffer.rs index 3e8eec4..4d5aa1e 100644 --- a/engine/src/plan/multibuffer.rs +++ b/engine/src/plan/multibuffer.rs @@ -3,6 +3,8 @@ use std::{rc::Rc, sync::Arc}; use common::DbResult; use transaction::transaction::Transaction; +use crate::element::Element; +use crate::schema::SchemaBuilder; use crate::{ plan::{Plan, materialize::MaterializePlan}, scan::{Scan, multibuffer::MultiBufferProductScan}, @@ -14,7 +16,7 @@ pub(crate) struct MultiBufferProductPlan { tx: Arc, left: Rc, right: Rc, - schema: Arc, + schema: Schema, } impl MultiBufferProductPlan { @@ -23,11 +25,9 @@ impl MultiBufferProductPlan { left: &Rc, right: &Rc, ) -> DbResult { - let schema = Arc::new(Schema::default()); let s1 = left.schema()?; let s2 = right.schema()?; - schema.add_all(&s1)?; - schema.add_all(&s2)?; + let schema = SchemaBuilder::default().add_all(&s1).add_all(&s2).build(); let plan = Self { tx: Arc::clone(tx), left: Rc::new(MaterializePlan::new(left, tx)), @@ -40,11 +40,11 @@ impl MultiBufferProductPlan { fn copy_records_from(&self, p: &Rc) -> DbResult { let source = p.open()?; let schema = p.schema()?; - let tt = TempTable::new(&self.tx, &schema)?; + let tt = TempTable::new(&self.tx, schema.clone())?; let dest = tt.open()?; while source.next()? { dest.insert()?; - for (field, _) in schema.fields()? { + for (field, _) in schema.fields() { let value = source.get_val(&field)?; dest.set_val(&field, value)?; } @@ -63,7 +63,7 @@ impl Plan for MultiBufferProductPlan { &self.tx, &left, &t.table_name(), - &t.layout(), + t.layout(), )?)) } @@ -78,15 +78,15 @@ impl Plan for MultiBufferProductPlan { Ok(self.left.records_output()? * self.right.records_output()?) } - fn distinct_values(&self, field_name: &str) -> DbResult { - if self.left.schema()?.has_field(field_name)? { + fn distinct_values(&self, field_name: &Element) -> DbResult { + if self.left.schema()?.has_field(field_name) { self.left.distinct_values(field_name) } else { self.right.distinct_values(field_name) } } - fn schema(&self) -> DbResult> { - Ok(Arc::clone(&self.schema)) + fn schema(&self) -> DbResult { + Ok(self.schema.clone()) } } diff --git a/engine/src/plan/sort.rs b/engine/src/plan/order.rs similarity index 85% rename from engine/src/plan/sort.rs rename to engine/src/plan/order.rs index 0430902..e12cbe8 100644 --- a/engine/src/plan/sort.rs +++ b/engine/src/plan/order.rs @@ -3,11 +3,12 @@ use std::{cmp::Ordering, rc::Rc, sync::Arc}; use common::DbResult; use transaction::transaction::Transaction; +use crate::element::Element; use crate::{ plan::{Plan, materialize::MaterializePlan}, scan::{ Scan, - sort::{RecordComparator, SortScan}, + order::{OrderScan, RecordComparator}, }, schema::Schema, temp::TempTable, @@ -16,7 +17,7 @@ use crate::{ pub struct SortPlan { plan: Rc, tx: Arc, - schema: Arc, + schema: Schema, comp: RecordComparator, } @@ -24,7 +25,7 @@ impl SortPlan { pub fn new( tx: &Arc, plan: &Rc, - sort_fields: Vec, + sort_fields: Vec, ) -> DbResult { let schema = plan.schema()?; Ok(Self { @@ -41,13 +42,13 @@ impl SortPlan { if !source.next()? { return Ok(temps); } - let mut current_temp = TempTable::new(&self.tx, &self.schema)?; + let mut current_temp = TempTable::new(&self.tx, self.schema.clone())?; let mut current_scan = current_temp.open()?; temps.push(current_temp); while self.copy(source, ¤t_scan)? { if self.comp.compare(source, ¤t_scan)? == Ordering::Less { current_scan.close()?; - current_temp = TempTable::new(&self.tx, &self.schema)?; + current_temp = TempTable::new(&self.tx, self.schema.clone())?; temps.push(current_temp.clone()); current_scan = current_temp.open()?; } @@ -72,7 +73,7 @@ impl SortPlan { fn merge_two_runs(&self, p1: TempTable, p2: TempTable) -> DbResult { let src1 = p1.open()?; let src2 = p2.open()?; - let result = TempTable::new(&self.tx, &self.schema)?; + let result = TempTable::new(&self.tx, self.schema.clone())?; let dest = result.open()?; let mut has_more1 = src1.next()?; @@ -102,7 +103,7 @@ impl SortPlan { fn copy(&self, source: &Rc, dest: &Rc) -> DbResult { dest.insert()?; - for (field, _) in self.schema.fields()? { + for (field, _) in self.schema.fields() { dest.set_val(&field, source.get_val(&field)?)?; } source.next() @@ -117,7 +118,7 @@ impl Plan for SortPlan { while runs.len() > 2 { runs = self.do_merge_iteration(runs)?; } - Ok(Rc::new(SortScan::new(runs, self.comp.clone())?)) + Ok(Rc::new(OrderScan::new(runs, self.comp.clone())?)) } fn blocks_accessed(&self) -> DbResult { @@ -129,11 +130,11 @@ impl Plan for SortPlan { self.plan.records_output() } - fn distinct_values(&self, field_name: &str) -> DbResult { + fn distinct_values(&self, field_name: &Element) -> DbResult { self.plan.distinct_values(field_name) } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { Ok(self.schema.clone()) } } diff --git a/engine/src/plan/product.rs b/engine/src/plan/product.rs index 0a212d8..c07081b 100644 --- a/engine/src/plan/product.rs +++ b/engine/src/plan/product.rs @@ -1,7 +1,9 @@ -use std::{rc::Rc, sync::Arc}; +use std::rc::Rc; use common::DbResult; +use crate::element::Element; +use crate::schema::SchemaBuilder; use crate::{ plan::Plan, scan::{Scan, product::ProductScan}, @@ -11,16 +13,14 @@ use crate::{ pub struct ProductPlan { p1: Rc, p2: Rc, - schema: Arc, + schema: Schema, } impl ProductPlan { pub fn new(p1: Rc, p2: Rc) -> DbResult { - let schema = Arc::new(Schema::default()); let s1 = p1.schema()?; let s2 = p2.schema()?; - schema.add_all(&s1)?; - schema.add_all(&s2)?; + let schema = SchemaBuilder::default().add_all(&s1).add_all(&s2).build(); Ok(Self { p1, p2, schema }) } } @@ -40,15 +40,15 @@ impl Plan for ProductPlan { Ok(self.p1.records_output()? * self.p2.records_output()?) } - fn distinct_values(&self, field_name: &str) -> DbResult { - if self.p1.schema()?.has_field(field_name)? { + fn distinct_values(&self, field_name: &Element) -> DbResult { + if self.p1.schema()?.has_field(field_name) { self.p1.distinct_values(field_name) } else { self.p2.distinct_values(field_name) } } - fn schema(&self) -> DbResult> { - Ok(Arc::clone(&self.schema)) + fn schema(&self) -> DbResult { + Ok(self.schema.clone()) } } diff --git a/engine/src/plan/project.rs b/engine/src/plan/project.rs index 1da4e91..f3a93a8 100644 --- a/engine/src/plan/project.rs +++ b/engine/src/plan/project.rs @@ -1,8 +1,10 @@ -use std::{collections::HashSet, rc::Rc, sync::Arc}; +use std::{collections::HashSet, rc::Rc}; use common::DbResult; +use crate::schema::SchemaBuilder; use crate::{ + element::Element, plan::Plan, scan::{Scan, project::ProjectScan}, schema::Schema, @@ -10,24 +12,27 @@ use crate::{ pub struct ProjectPlan { plan: Rc, - schema: Arc, + schema: Schema, } impl ProjectPlan { - pub fn new(plan: Rc, fields: Vec) -> DbResult { - let schema = Arc::new(Schema::default()); + pub fn new(plan: Rc, fields: Vec) -> DbResult { + let mut schema = SchemaBuilder::default(); for field in fields { let other = plan.schema()?; - schema.add(field, &other)?; + schema = schema.add(field, &other); } - Ok(Self { plan, schema }) + Ok(Self { + plan, + schema: schema.build(), + }) } } impl Plan for ProjectPlan { fn open(&self) -> DbResult> { let scan = self.plan.open()?; - let fields: HashSet = self.schema.fields()?.into_iter().map(|(f, _)| f).collect(); + let fields: HashSet = self.schema.fields().into_iter().map(|(f, _)| f).collect(); Ok(Rc::new(ProjectScan::new(scan, fields))) } @@ -39,11 +44,11 @@ impl Plan for ProjectPlan { self.plan.records_output() } - fn distinct_values(&self, field_name: &str) -> DbResult { + fn distinct_values(&self, field_name: &Element) -> DbResult { self.plan.distinct_values(field_name) } - fn schema(&self) -> DbResult> { - Ok(Arc::clone(&self.schema)) + fn schema(&self) -> DbResult { + Ok(self.schema.clone()) } } diff --git a/engine/src/plan/select.rs b/engine/src/plan/select.rs index c572b9c..30b4a5b 100644 --- a/engine/src/plan/select.rs +++ b/engine/src/plan/select.rs @@ -1,7 +1,8 @@ -use std::{rc::Rc, sync::Arc}; +use std::rc::Rc; use common::DbResult; +use crate::element::Element; use crate::{ plan::Plan, predicate::Predicate, @@ -34,7 +35,7 @@ impl Plan for SelectPlan { Ok(self.plan.records_output()? / self.predicate.reduction_factor(&self.plan)?) } - fn distinct_values(&self, field_name: &str) -> common::DbResult { + fn distinct_values(&self, field_name: &Element) -> common::DbResult { if self.predicate.equates_with_constant(field_name)?.is_some() { return Ok(1); } else { @@ -48,7 +49,7 @@ impl Plan for SelectPlan { self.plan.distinct_values(field_name) } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { self.plan.schema() } } diff --git a/engine/src/plan/table.rs b/engine/src/plan/table.rs index 4283085..494695e 100644 --- a/engine/src/plan/table.rs +++ b/engine/src/plan/table.rs @@ -3,6 +3,7 @@ use std::{rc::Rc, sync::Arc}; use common::DbResult; use transaction::transaction::Transaction; +use crate::element::Element; use crate::{ layout::Layout, metadata_mgr::MetadataMgr, @@ -15,14 +16,14 @@ use crate::{ pub struct TablePlan { tx: Arc, table: String, - layout: Arc, + layout: Layout, stat: StatInfo, } impl TablePlan { pub fn new(tx: &Arc, table: String, md: &MetadataMgr) -> DbResult { - let layout = Arc::new(md.get_layout(&table, tx)?); - let stat = md.get_stat_info(&table, &layout, tx)?; + let layout = md.get_layout(&table, tx)?; + let stat = md.get_stat_info(&table, layout.clone(), tx)?; Ok(Self { tx: Arc::clone(tx), table, @@ -37,7 +38,7 @@ impl Plan for TablePlan { Ok(Rc::new(TableScan::new( &self.tx, &self.table, - &self.layout, + self.layout.clone(), )?)) } @@ -49,11 +50,11 @@ impl Plan for TablePlan { Ok(self.stat.records_output()) } - fn distinct_values(&self, _: &str) -> DbResult { + fn distinct_values(&self, _: &Element) -> DbResult { Ok(self.stat.distinct_values()) } - fn schema(&self) -> DbResult> { - Ok(self.layout.schema()) + fn schema(&self) -> DbResult { + Ok(self.layout.schema().clone()) } } diff --git a/engine/src/predicate.rs b/engine/src/predicate.rs index ac18b46..167cd2b 100644 --- a/engine/src/predicate.rs +++ b/engine/src/predicate.rs @@ -6,37 +6,39 @@ use std::{ use common::{DbResult, error::DbError}; -use crate::{constant::Constant, plan::Plan, scan::Scan, schema::Schema}; +use crate::element::Element; +use crate::schema::SchemaBuilder; +use crate::{plan::Plan, scan::Scan, schema::Schema, value::Value}; #[derive(Debug, Clone)] pub enum Expression { - Value(Constant), - Field(String), + Value(Value), + Field(Element), } impl Expression { - pub fn evaluate(&self, scan: &Rc) -> DbResult { + pub fn evaluate(&self, scan: &Rc) -> DbResult { match self { Self::Value(value) => Ok(value.clone()), Self::Field(field) => scan.get_val(field), } } - pub fn applies_to(&self, schema: &Schema) -> DbResult { + pub fn applies_to(&self, schema: &Schema) -> bool { match self { - Self::Value(_) => Ok(true), + Self::Value(_) => true, Self::Field(field) => schema.has_field(field), } } - pub fn as_field_name(&self) -> Option<&str> { + pub fn as_field_name(&self) -> Option<&Element> { match self { Self::Field(field) => Some(field), _ => None, } } - pub fn as_constant(&self) -> Option<&Constant> { + pub fn as_constant(&self) -> Option<&Value> { match self { Self::Value(value) => Some(value), _ => None, @@ -70,8 +72,8 @@ impl Term { Ok(left == right) } - pub fn applies_to(&self, schema: &Schema) -> DbResult { - Ok(self.left.applies_to(schema)? && self.right.applies_to(schema)?) + pub fn applies_to(&self, schema: &Schema) -> bool { + self.left.applies_to(schema) && self.right.applies_to(schema) } pub fn reduction_factor(&self, p: &Rc) -> DbResult { @@ -96,7 +98,7 @@ impl Term { } } - pub fn equates_with_constant(&self, field_name: &str) -> DbResult> { + pub fn equates_with_constant(&self, field_name: &Element) -> DbResult> { if let Some(field) = self.left.as_field_name() && field == field_name && let Some(value) = self.right.as_constant() @@ -112,17 +114,17 @@ impl Term { } } - pub fn equates_with_field(&self, field_name: &str) -> DbResult> { + pub fn equates_with_field(&self, field_name: &Element) -> DbResult> { if let Some(left) = self.left.as_field_name() && left == field_name && let Some(right) = self.right.as_field_name() { - Ok(Some(right.to_string())) + Ok(Some(right.clone())) } else if let Some(right) = self.right.as_field_name() && right == field_name && let Some(left) = self.left.as_field_name() { - Ok(Some(left.to_string())) + Ok(Some(left.clone())) } else { Ok(None) } @@ -181,7 +183,7 @@ impl Predicate { { let mut terms = result.terms.write().map_err(DbError::lock)?; for t in read.iter() { - if t.applies_to(schema)? { + if t.applies_to(schema) { terms.push(t.clone()); } } @@ -191,14 +193,12 @@ impl Predicate { pub fn join_sub_pred(&self, s1: &Schema, s2: &Schema) -> DbResult> { let result = Predicate::default(); - let new_schema = Schema::default(); - new_schema.add_all(s1)?; - new_schema.add_all(s2)?; + let new_schema = SchemaBuilder::default().add_all(s1).add_all(s2).build(); let read = self.terms.read().map_err(DbError::lock)?; { let mut terms = result.terms.write().map_err(DbError::lock)?; for t in read.iter() { - if !t.applies_to(s1)? && !t.applies_to(s2)? && t.applies_to(&new_schema)? { + if !t.applies_to(s1) && !t.applies_to(s2) && t.applies_to(&new_schema) { terms.push(t.clone()); } } @@ -209,7 +209,7 @@ impl Predicate { Ok(Some(result)) } - pub fn equates_with_constant(&self, field_name: &str) -> DbResult> { + pub fn equates_with_constant(&self, field_name: &Element) -> DbResult> { let terms = self.terms.read().map_err(DbError::lock)?; for t in terms.iter() { if let Some(c) = t.equates_with_constant(field_name)? { @@ -219,7 +219,7 @@ impl Predicate { Ok(None) } - pub fn equates_with_field(&self, field_name: &str) -> DbResult> { + pub fn equates_with_field(&self, field_name: &Element) -> DbResult> { let terms = self.terms.read().map_err(DbError::lock)?; for t in terms.iter() { if let Some(field) = t.equates_with_field(field_name)? { diff --git a/engine/src/query/basic_planner.rs b/engine/src/query/basic_planner.rs index 7dd758d..2119360 100644 --- a/engine/src/query/basic_planner.rs +++ b/engine/src/query/basic_planner.rs @@ -10,12 +10,12 @@ use crate::{ }; pub struct BasicUpdatePlanner { - md: Arc, + md: MetadataMgr, } impl BasicUpdatePlanner { - pub fn new(md: &Arc) -> Self { - Self { md: Arc::clone(md) } + pub fn new(md: MetadataMgr) -> Self { + Self { md } } } @@ -80,8 +80,7 @@ impl UpdatePlanner for BasicUpdatePlanner { data: super::command::TableData, tx: &Arc, ) -> DbResult { - self.md - .create_table(&data.name, &Arc::new(data.schema), tx)?; + self.md.create_table(&data.name, data.schema, tx)?; Ok(0) } diff --git a/engine/src/query/command.rs b/engine/src/query/command.rs index a217da7..73f032d 100644 --- a/engine/src/query/command.rs +++ b/engine/src/query/command.rs @@ -1,8 +1,9 @@ use crate::{ - constant::Constant, + element::Element, predicate::{Expression, Predicate}, schema::Schema, sort_by::SortByData, + value::Value, }; pub enum Command { @@ -29,16 +30,9 @@ impl std::fmt::Display for Command { } } -#[allow(dead_code)] -pub(crate) enum Element { - Raw(String), - Id { source: String, name: String }, - Object { obj: String, field: String }, -} - pub struct QueryData { - pub fields: Vec, - pub tables: Vec, + pub fields: Vec, + pub tables: Vec, pub predicate: Predicate, pub group_by: GroupByData, pub sort_by: SortByData, @@ -105,8 +99,8 @@ impl std::fmt::Display for DeleteData { pub struct InsertData { pub table: String, - pub fields: Vec, - pub values: Vec, + pub fields: Vec, + pub values: Vec, } impl std::fmt::Display for InsertData { @@ -134,7 +128,7 @@ impl std::fmt::Display for InsertData { pub struct UpdateData { pub table: String, - pub field: String, + pub field: Element, pub value: Expression, pub predicate: Predicate, } @@ -174,13 +168,7 @@ pub struct TableData { impl std::fmt::Display for TableData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "CREATE TABLE {}(", self.name)?; - for (i, (field, value)) in self - .schema - .fields() - .map_err(|_| std::fmt::Error)? - .iter() - .enumerate() - { + for (i, (field, value)) in self.schema.fields().iter().enumerate() { if i == 0 { write!(f, "{} {}", field, value)?; } else { @@ -193,7 +181,7 @@ impl std::fmt::Display for TableData { #[derive(Default)] pub struct GroupByData { - pub fields: Vec, + pub fields: Vec, } impl GroupByData { diff --git a/engine/src/query/heuristic_planner.rs b/engine/src/query/heuristic_planner.rs index 91954e8..ca31744 100644 --- a/engine/src/query/heuristic_planner.rs +++ b/engine/src/query/heuristic_planner.rs @@ -4,7 +4,7 @@ use common::DbResult; use transaction::transaction::Transaction; use crate::plan::group::GroupByPlan; -use crate::plan::sort::SortPlan; +use crate::plan::order::SortPlan; use crate::{ metadata_mgr::MetadataMgr, plan::{Plan, project::ProjectPlan}, @@ -13,20 +13,20 @@ use crate::{ struct HeuristicQueryPlannerInner { table_planners: Vec, - md: Arc, + md: MetadataMgr, } impl HeuristicQueryPlannerInner { - fn new(md: &Arc) -> Self { + fn new(md: MetadataMgr) -> Self { Self { table_planners: vec![], - md: Arc::clone(md), + md, } } fn create_plan(&mut self, data: QueryData, tx: &Arc) -> DbResult> { for table in &data.tables { - let tp = TablePlanner::new(table, data.predicate.clone(), tx, &self.md)?; + let tp = TablePlanner::new(table.as_raw()?, data.predicate.clone(), tx, &self.md)?; self.table_planners.push(tp); } let mut current = self.get_lowest_select_plan()?; @@ -110,7 +110,7 @@ impl HeuristicQueryPlannerInner { pub(crate) struct HeuristicQueryPlanner(RefCell); impl HeuristicQueryPlanner { - pub(crate) fn new(md: &Arc) -> Self { + pub(crate) fn new(md: MetadataMgr) -> Self { Self(RefCell::new(HeuristicQueryPlannerInner::new(md))) } } diff --git a/engine/src/query/lexer.rs b/engine/src/query/lexer.rs index 4a6278c..b3038ea 100644 --- a/engine/src/query/lexer.rs +++ b/engine/src/query/lexer.rs @@ -1,8 +1,8 @@ use common::{DbResult, error::DbError}; use crate::{ - constant::Constant, query::{token::Token, tokenizer::Tokenizer}, + value::Value, }; pub struct Lexer { @@ -27,14 +27,14 @@ impl Lexer { pub fn match_int_constant(&self) -> bool { matches!( self.tokenizer.current(), - Some(Token::Element(Constant::Integer(_))) + Some(Token::Element(Value::Integer(_))) ) } pub fn match_string_constant(&self) -> bool { matches!( self.tokenizer.current(), - Some(Token::Element(Constant::Varchar(_))) + Some(Token::Element(Value::Varchar(_))) ) } @@ -65,19 +65,19 @@ impl Lexer { Ok(()) } - pub fn eat_int_constant(&self) -> DbResult { - if let Some(Token::Element(Constant::Integer(value))) = self.tokenizer.current() { + pub fn eat_int_constant(&self) -> DbResult { + if let Some(Token::Element(Value::Integer(value))) = self.tokenizer.current() { self.tokenizer.next()?; - Ok(Constant::Integer(value)) + Ok(Value::Integer(value)) } else { Err(DbError::BadSyntax) } } - pub fn eat_string_constant(&self) -> DbResult { - if let Some(Token::Element(Constant::Varchar(value))) = self.tokenizer.current() { + pub fn eat_string_constant(&self) -> DbResult { + if let Some(Token::Element(Value::Varchar(value))) = self.tokenizer.current() { self.tokenizer.next()?; - Ok(Constant::Varchar(value)) + Ok(Value::Varchar(value)) } else { Err(DbError::BadSyntax) } diff --git a/engine/src/query/parser.rs b/engine/src/query/parser.rs index 0ae1858..6583b2e 100644 --- a/engine/src/query/parser.rs +++ b/engine/src/query/parser.rs @@ -1,7 +1,8 @@ use common::{DbResult, error::DbError}; +use crate::schema::SchemaBuilder; use crate::{ - constant::Constant, + element::Element, predicate::{Expression, Predicate, Term}, query::{ command::{ @@ -13,6 +14,7 @@ use crate::{ }, schema::Schema, sort_by::SortByData, + value::Value, }; pub(crate) struct Parser { @@ -26,11 +28,25 @@ impl Parser { }) } + pub(crate) fn element(&self) -> DbResult { + let id = self.lexer.eat_id()?; + if self.lexer.match_delim('.') { + self.lexer.eat_delimiter('.')?; + let spec = self.lexer.eat_id()?; + Ok(Element::Spec(id, spec)) + } else if self.lexer.match_id() { + let name = self.lexer.eat_id()?; + Ok(Element::View(id, name)) + } else { + Ok(Element::Raw(id)) + } + } + pub(crate) fn field(&self) -> DbResult { self.lexer.eat_id() } - pub(crate) fn constant(&self) -> DbResult { + pub(crate) fn constant(&self) -> DbResult { if self.lexer.match_string_constant() { self.lexer.eat_string_constant() } else { @@ -40,7 +56,7 @@ impl Parser { pub(crate) fn expression(&self) -> DbResult { if self.lexer.match_id() { - Ok(Expression::Field(self.field()?)) + Ok(Expression::Field(self.element()?)) } else { Ok(Expression::Value(self.constant()?)) } @@ -82,7 +98,7 @@ impl Parser { if self.lexer.match_keyword(Token::Sort) { self.lexer.eat_keyword(Token::Sort)?; self.lexer.eat_keyword(Token::By)?; - sort_by = self.sort_by()?; + sort_by = self.order_by()?; } self.check_remainder()?; Ok(Command::Query(QueryData { @@ -94,22 +110,20 @@ impl Parser { })) } - fn select_list(&self) -> DbResult> { - let mut fields = vec![]; - fields.push(self.field()?); + fn select_list(&self) -> DbResult> { + let mut fields = vec![self.element()?]; while self.lexer.match_delim(',') { self.lexer.eat_delimiter(',')?; - fields.push(self.field()?); + fields.push(self.element()?); } Ok(fields) } - fn table_list(&self) -> DbResult> { - let mut tables = vec![]; - tables.push(self.lexer.eat_id()?); + fn table_list(&self) -> DbResult> { + let mut tables = vec![self.element()?]; while self.lexer.match_delim(',') { self.lexer.eat_delimiter(',')?; - tables.push(self.lexer.eat_id()?); + tables.push(self.element()?); } Ok(tables) } @@ -141,7 +155,7 @@ impl Parser { self.lexer.eat_keyword(Token::Update)?; let table = self.lexer.eat_id()?; self.lexer.eat_keyword(Token::Set)?; - let field = self.field()?; + let field = self.element()?; self.lexer.eat_delimiter('=')?; let value = self.expression()?; let mut predicate = Predicate::default(); @@ -187,17 +201,17 @@ impl Parser { })) } - fn field_list(&self) -> DbResult> { + fn field_list(&self) -> DbResult> { let mut fields = vec![]; - fields.push(self.field()?); + fields.push(self.element()?); while self.lexer.match_delim(',') { self.lexer.eat_delimiter(',')?; - fields.push(self.field()?); + fields.push(self.element()?); } Ok(fields) } - fn constants_list(&self) -> DbResult> { + fn constants_list(&self) -> DbResult> { let mut constants = vec![]; constants.push(self.constant()?); while self.lexer.match_delim(',') { @@ -217,35 +231,39 @@ impl Parser { } fn field_definitions(&self) -> DbResult { - let schema = Schema::default(); - self.field_definition(&schema)?; + let mut schema = SchemaBuilder::default(); + schema = self.field_definition(schema)?; while self.lexer.match_delim(',') { self.lexer.eat_delimiter(',')?; - self.field_definition(&schema)?; + schema = self.field_definition(schema)?; } - Ok(schema) + Ok(schema.build()) } - fn field_definition(&self, schema: &Schema) -> DbResult<()> { - let field_name = self.field()?; + fn field_definition(&self, schema: SchemaBuilder) -> DbResult { + let field_name = self.element()?; self.field_type(field_name, schema) } - fn field_type(&self, field_name: String, schema: &Schema) -> DbResult<()> { + fn field_type( + &self, + field_name: Element, + mut schema: SchemaBuilder, + ) -> DbResult { if self.lexer.match_keyword(Token::Int) { self.lexer.eat_keyword(Token::Int)?; - schema.add_int_field(field_name)?; + schema = schema.add_int_field(field_name); } else { self.lexer.eat_keyword(Token::Varchar)?; self.lexer.eat_delimiter('(')?; let str_len = self.lexer.eat_int_constant()?; self.lexer.eat_delimiter(')')?; match str_len { - Constant::Integer(value) => schema.add_string_field(field_name, value)?, + Value::Integer(value) => schema = schema.add_string_field(field_name, value), _ => return Err(DbError::BadSyntax), } } - Ok(()) + Ok(schema) } pub fn create_view(&self) -> DbResult { @@ -275,19 +293,19 @@ impl Parser { } fn group_by(&self) -> DbResult { - let mut fields = vec![self.lexer.eat_id()?]; + let mut fields = vec![self.element()?]; while self.lexer.match_delim(',') { self.lexer.eat_delimiter(',')?; - fields.push(self.lexer.eat_id()?); + fields.push(self.element()?); } Ok(GroupByData { fields }) } - fn sort_by(&self) -> DbResult { - let mut fields = vec![self.lexer.eat_id()?]; + fn order_by(&self) -> DbResult { + let mut fields = vec![self.element()?]; while self.lexer.match_delim(',') { self.lexer.eat_delimiter(',')?; - fields.push(self.lexer.eat_id()?); + fields.push(self.element()?); } Ok(SortByData { fields }) } diff --git a/engine/src/query/table_planner.rs b/engine/src/query/table_planner.rs index a3760ed..ebaf2cc 100644 --- a/engine/src/query/table_planner.rs +++ b/engine/src/query/table_planner.rs @@ -3,6 +3,7 @@ use std::{collections::HashMap, rc::Rc, sync::Arc}; use common::DbResult; use transaction::transaction::Transaction; +use crate::element::Element; use crate::{ index_mgr::IndexInfo, metadata_mgr::MetadataMgr, @@ -20,8 +21,8 @@ use crate::{ pub(crate) struct TablePlanner { plan: Rc, predicate: Predicate, - schema: Arc, - indexes: HashMap, + schema: Schema, + indexes: HashMap, tx: Arc, } @@ -94,7 +95,7 @@ impl TablePlanner { ) -> DbResult>> { for (field, info) in &self.indexes { if let Some(outer_field) = self.predicate.equates_with_field(field)? - && self.schema.has_field(field)? + && self.schema.has_field(field) { let plan: Rc = self.plan.clone(); let p: Rc = Rc::new(IndexJoinPlan::new( diff --git a/engine/src/query/token.rs b/engine/src/query/token.rs index be6bb9a..cf176c6 100644 --- a/engine/src/query/token.rs +++ b/engine/src/query/token.rs @@ -1,6 +1,6 @@ use common::error::DbError; -use crate::constant::Constant; +use crate::value::Value; #[derive(Clone, Debug, PartialEq, Eq)] pub enum Token { @@ -28,7 +28,7 @@ pub enum Token { By, Field(String), Delimiter(char), - Element(Constant), + Element(Value), } impl Token { @@ -77,7 +77,7 @@ pub(crate) fn tokenize(query: &str) -> Result, DbError> { if str_char == Some(c) && prev_char != '\\' { let token: String = token_chars.into_iter().collect(); token_chars = Vec::new(); - tokens.push(Token::Element(Constant::Varchar(token))); + tokens.push(Token::Element(Value::Varchar(token))); str_char = None; continue; } else if last_idx == i && str_char.is_some() { @@ -99,7 +99,7 @@ pub(crate) fn tokenize(query: &str) -> Result, DbError> { if let Some(token) = Token::parse(&token.to_lowercase()) { tokens.push(token); } else if let Ok(value) = token.parse::() { - tokens.push(Token::Element(Constant::Integer(value))); + tokens.push(Token::Element(Value::Integer(value))); } else { tokens.push(Token::Field(token)) } @@ -121,7 +121,7 @@ fn is_str_token(c: char) -> bool { } fn is_markable_delimeter(c: char) -> bool { - c == '(' || c == ')' || c == ',' || c == '=' + c == '(' || c == ')' || c == ',' || c == '=' || c == '.' } fn is_delimeter(c: char) -> bool { diff --git a/engine/src/record_page.rs b/engine/src/record_page.rs index 10e1bdc..2b02d70 100644 --- a/engine/src/record_page.rs +++ b/engine/src/record_page.rs @@ -4,7 +4,7 @@ use common::DbResult; use file::block::BlockId; use transaction::transaction::Transaction; -use crate::{field_info::FieldInfo, layout::Layout}; +use crate::{element::Element, field_info::FieldInfo, layout::Layout}; const EMPTY: u8 = 0; const USED: u8 = 1; @@ -13,35 +13,35 @@ const USED: u8 = 1; pub struct RecordPage { tx: Arc, block: BlockId, - layout: Arc, + layout: Layout, } impl RecordPage { - pub fn new(tx: &Arc, block: BlockId, layout: &Arc) -> DbResult { + pub fn new(tx: &Arc, block: BlockId, layout: Layout) -> DbResult { tx.pin(&block)?; Ok(Self { tx: Arc::clone(tx), block, - layout: Arc::clone(layout), + layout, }) } - pub fn get_i32(&self, slot: i32, fieldname: &str) -> DbResult { - let pos = self.offset(slot) + self.layout.offset(fieldname); + pub fn get_i32(&self, slot: i32, field: &Element) -> DbResult { + let pos = self.offset(slot) + self.layout.offset(field); self.tx.get_i32(&self.block, pos as usize) } - pub fn get_string(&self, slot: i32, fieldname: &str) -> DbResult { - let pos = self.offset(slot) + self.layout.offset(fieldname); + pub fn get_string(&self, slot: i32, field: &Element) -> DbResult { + let pos = self.offset(slot) + self.layout.offset(field); self.tx.get_string(&self.block, pos as usize) } - pub fn set_i32(&self, slot: i32, field: &str, value: i32) -> DbResult<()> { + pub fn set_i32(&self, slot: i32, field: &Element, value: i32) -> DbResult<()> { let pos = self.offset(slot) + self.layout.offset(field); self.tx.set_i32(&self.block, pos as usize, value, true) } - pub fn set_string(&self, slot: i32, field: &str, value: &str) -> DbResult<()> { + pub fn set_string(&self, slot: i32, field: &Element, value: &str) -> DbResult<()> { let pos = self.offset(slot) + self.layout.offset(field); self.tx.set_string(&self.block, pos as usize, value, true) } @@ -56,7 +56,7 @@ impl RecordPage { self.tx .set_u8(&self.block, self.offset(slot) as usize, EMPTY, false)?; let schema = self.layout.schema(); - for (field, info) in schema.fields()? { + for (field, info) in schema.fields() { let pos = self.offset(slot) + self.layout.offset(&field); match info { FieldInfo::Integer => self.tx.set_i32(&self.block, pos as usize, 0, false)?, @@ -123,9 +123,9 @@ mod tests { use log::mgr::LogMgr; use rand::RngExt; use tempfile::tempdir; - use transaction::{lock_table::LockTable, txnum_generator::TxNumGenerator}; + use transaction::lock_table::LockTable; - use crate::schema::Schema; + use crate::schema::SchemaBuilder; use super::*; @@ -135,20 +135,24 @@ mod tests { let fm = Arc::new(FileMgr::new(dir.path(), 512).unwrap()); let lm = Arc::new(LogMgr::new(&fm, "testlog".to_string()).unwrap()); let bm = Arc::new(BufferMgr::new(&fm, &lm, 1).unwrap()); - let txnum_generator = TxNumGenerator::default(); let lock_table = Arc::new(LockTable::default()); - let tx = Transaction::new(&txnum_generator, &fm, &lm, &bm, &lock_table).unwrap(); + let tx = Transaction::new(&fm, &lm, &bm, &lock_table).unwrap(); let tx = Arc::new(tx); - let schema = Arc::new(Schema::default()); - schema.add_int_field("A".to_string()).unwrap(); - schema.add_string_field("B".to_string(), 9).unwrap(); - let layout = Arc::new(Layout::new(&schema).unwrap()); + let a = Element::raw("A"); + let b = Element::raw("B"); + + let schema = SchemaBuilder::default() + .add_int_field(a.clone()) + .add_string_field(b.clone(), 9) + .build(); + + let layout = Layout::new(schema); let block = tx.append("testfile").unwrap(); tx.pin(&block).unwrap(); - let record_page = RecordPage::new(&tx, block.clone(), &layout).unwrap(); + let record_page = RecordPage::new(&tx, block.clone(), layout).unwrap(); record_page.format().unwrap(); let mut rng = rand::rng(); @@ -164,17 +168,17 @@ mod tests { } else { values_greater.insert((n, format!("rec{}", n))); } - record_page.set_i32(slot, "A", n).unwrap(); + record_page.set_i32(slot, &a, n).unwrap(); record_page - .set_string(slot, "B", &format!("rec{}", n)) + .set_string(slot, &b, &format!("rec{}", n)) .unwrap(); slot = record_page.insert_after(slot).unwrap(); } let mut slot = record_page.next_after(-1).unwrap(); while slot >= 0 { - let a = record_page.get_i32(slot, "A").unwrap(); - let b = record_page.get_string(slot, "B").unwrap(); + let a = record_page.get_i32(slot, &a).unwrap(); + let b = record_page.get_string(slot, &b).unwrap(); if a < 25 { assert!(values_less.contains(&(a, b))); record_page.delete(slot).unwrap(); @@ -184,8 +188,8 @@ mod tests { let mut slot = record_page.next_after(-1).unwrap(); while slot >= 0 { - let a = record_page.get_i32(slot, "A").unwrap(); - let b = record_page.get_string(slot, "B").unwrap(); + let a = record_page.get_i32(slot, &a).unwrap(); + let b = record_page.get_string(slot, &b).unwrap(); assert!(values_greater.contains(&(a, b))); slot = record_page.next_after(slot).unwrap(); } diff --git a/engine/src/scan.rs b/engine/src/scan.rs index 43035cb..281233b 100644 --- a/engine/src/scan.rs +++ b/engine/src/scan.rs @@ -1,18 +1,18 @@ use common::{DbResult, error::DbError}; -use std::sync::Arc; +use crate::element::Element; use crate::schema::Schema; -use crate::{constant::Constant, rid::RID}; +use crate::{rid::RID, value::Value}; pub mod chunk; pub(crate) mod group; pub mod index; pub(crate) mod merge; pub(crate) mod multibuffer; +pub(crate) mod order; pub mod product; pub mod project; pub mod select; -pub(crate) mod sort; pub mod table; pub trait Scan { @@ -20,27 +20,27 @@ pub trait Scan { fn next(&self) -> DbResult; - fn get_i32(&self, field_name: &str) -> DbResult; + fn get_i32(&self, field_name: &Element) -> DbResult; - fn get_string(&self, field_name: &str) -> DbResult; + fn get_string(&self, field_name: &Element) -> DbResult; - fn get_val(&self, field_name: &str) -> DbResult; + fn get_val(&self, field_name: &Element) -> DbResult; - fn has_field(&self, field_name: &str) -> DbResult; + fn has_field(&self, field_name: &Element) -> DbResult; fn close(&self) -> DbResult<()>; - fn schema(&self) -> DbResult>; + fn schema(&self) -> DbResult; - fn set_i32(&self, _: &str, _: i32) -> DbResult<()> { + fn set_i32(&self, _: &Element, _: i32) -> DbResult<()> { Err(DbError::other("cannot set integer")) } - fn set_string(&self, _: &str, _: &str) -> DbResult<()> { + fn set_string(&self, _: &Element, _: &str) -> DbResult<()> { Err(DbError::other("cannot set string")) } - fn set_val(&self, _: &str, _: Constant) -> DbResult<()> { + fn set_val(&self, _: &Element, _: Value) -> DbResult<()> { Err(DbError::other("cannot set value")) } diff --git a/engine/src/scan/chunk.rs b/engine/src/scan/chunk.rs index 51f3427..6b8ddf2 100644 --- a/engine/src/scan/chunk.rs +++ b/engine/src/scan/chunk.rs @@ -5,16 +5,17 @@ use common::{DbResult, error::DbError}; use file::block::BlockId; use transaction::transaction::Transaction; +use crate::element::Element; use crate::schema::Schema; use crate::{ - constant::Constant, field_info::FieldInfo, layout::Layout, record_page::RecordPage, scan::Scan, + field_info::FieldInfo, layout::Layout, record_page::RecordPage, scan::Scan, value::Value, }; pub struct ChunkScanLock { buffers: Vec, tx: Arc, filename: String, - layout: Arc, + layout: Layout, start_b_num: i32, end_b_num: i32, current_b_num: i32, @@ -26,14 +27,14 @@ impl ChunkScanLock { fn new( tx: &Arc, filename: &str, - layout: &Arc, + layout: Layout, start_b_num: i32, end_b_num: i32, ) -> DbResult { let mut buffers = vec![]; for i in start_b_num..=end_b_num { let block = BlockId::new(filename, i); - buffers.push(RecordPage::new(tx, block, layout)?); + buffers.push(RecordPage::new(tx, block, layout.clone())?); } let chunk = Self { buffers, @@ -42,7 +43,7 @@ impl ChunkScanLock { start_b_num, end_b_num, current_b_num: start_b_num, - layout: Arc::clone(layout), + layout, current_slot: -1, rp: 0, }; @@ -83,7 +84,7 @@ impl ChunkScanLock { } } - fn get_i32(&self, field_name: &str) -> DbResult { + fn get_i32(&self, field_name: &Element) -> DbResult { if let Some(rp) = self.buffers.get(self.rp as usize) { rp.get_i32(self.current_slot, field_name) } else { @@ -91,7 +92,7 @@ impl ChunkScanLock { } } - fn get_string(&self, field_name: &str) -> DbResult { + fn get_string(&self, field_name: &Element) -> DbResult { if let Some(rp) = self.buffers.get(self.rp as usize) { rp.get_string(self.current_slot, field_name) } else { @@ -99,20 +100,20 @@ impl ChunkScanLock { } } - fn get_val(&self, field_name: &str) -> DbResult { - match self.layout.schema().info(field_name)? { - Some(FieldInfo::Integer) => Ok(Constant::Integer(self.get_i32(field_name)?)), - Some(FieldInfo::Varchar(_)) => Ok(Constant::Varchar(self.get_string(field_name)?)), - _ => Err(DbError::field_not_exists(field_name)), + fn get_val(&self, field_name: &Element) -> DbResult { + match self.layout.schema().info(field_name) { + Some(FieldInfo::Integer) => Ok(Value::Integer(self.get_i32(field_name)?)), + Some(FieldInfo::Varchar(_)) => Ok(Value::Varchar(self.get_string(field_name)?)), + _ => Err(DbError::FieldNotExists(field_name.to_string())), } } - fn has_field(&self, field_name: &str) -> DbResult { + fn has_field(&self, field_name: &Element) -> bool { self.layout.schema().has_field(field_name) } - fn schema(&self) -> DbResult> { - Ok(self.layout.schema()) + fn schema(&self) -> Schema { + self.layout.schema().clone() } } @@ -122,7 +123,7 @@ impl ChunkScan { pub fn new( tx: &Arc, filename: &str, - layout: &Arc, + layout: Layout, start_b_num: i32, end_b_num: i32, ) -> DbResult { @@ -148,24 +149,24 @@ impl Scan for ChunkScan { write.next() } - fn get_i32(&self, field_name: &str) -> DbResult { + fn get_i32(&self, field_name: &Element) -> DbResult { let read = self.0.borrow(); read.get_i32(field_name) } - fn get_string(&self, field_name: &str) -> DbResult { + fn get_string(&self, field_name: &Element) -> DbResult { let read = self.0.borrow(); read.get_string(field_name) } - fn get_val(&self, field_name: &str) -> DbResult { + fn get_val(&self, field_name: &Element) -> DbResult { let read = self.0.borrow(); read.get_val(field_name) } - fn has_field(&self, field_name: &str) -> DbResult { + fn has_field(&self, field_name: &Element) -> DbResult { let read = self.0.borrow(); - read.has_field(field_name) + Ok(read.has_field(field_name)) } fn close(&self) -> DbResult<()> { @@ -173,8 +174,8 @@ impl Scan for ChunkScan { read.close() } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { let read = self.0.borrow(); - read.schema() + Ok(read.schema()) } } diff --git a/engine/src/scan/group.rs b/engine/src/scan/group.rs index ceeed2e..a0e7cca 100644 --- a/engine/src/scan/group.rs +++ b/engine/src/scan/group.rs @@ -1,13 +1,14 @@ -use std::{cell::RefCell, cmp::Ordering, collections::HashMap, rc::Rc, sync::Arc}; +use std::{cell::RefCell, cmp::Ordering, collections::HashMap, rc::Rc}; use common::{DbResult, error::DbError}; -use crate::{constant::Constant, scan::Scan, schema::Schema}; +use crate::element::Element; +use crate::{scan::Scan, schema::Schema, value::Value}; #[allow(dead_code)] #[derive(Clone)] pub enum AggregationFn { - MaxFn { field: String, value: Constant }, + MaxFn { field: Element, value: Value }, } impl AggregationFn { @@ -32,13 +33,13 @@ impl AggregationFn { Ok(()) } - pub fn field_name(&self) -> String { + pub fn field_name(&self) -> &Element { match self { - Self::MaxFn { field, .. } => format!("max_of_{}", field), + Self::MaxFn { field, .. } => field, } } - pub fn value(&self) -> Constant { + pub fn value(&self) -> Value { match self { Self::MaxFn { value, .. } => value.clone(), } @@ -47,11 +48,11 @@ impl AggregationFn { #[derive(PartialEq, Eq)] struct GroupValue { - values: HashMap, + values: HashMap, } impl GroupValue { - fn new(scan: &Rc, fields: Vec) -> DbResult { + fn new(scan: &Rc, fields: Vec) -> DbResult { let mut values = HashMap::new(); for field in fields { let value = scan.get_val(&field)?; @@ -60,17 +61,17 @@ impl GroupValue { Ok(Self { values }) } - fn get_val(&self, field: &str) -> DbResult { + fn get_val(&self, field: &Element) -> DbResult { match self.values.get(field) { Some(value) => Ok(value.clone()), - _ => Err(DbError::field_not_exists(field)), + _ => Err(DbError::FieldNotExists(field.to_string())), } } } struct GroupByScanLock { scan: Rc, - group_fields: Vec, + group_fields: Vec, agg_fns: Vec, group_val: GroupValue, more_groups: bool, @@ -79,7 +80,7 @@ struct GroupByScanLock { impl GroupByScanLock { fn new( scan: &Rc, - group_fields: Vec, + group_fields: Vec, agg_fns: Vec, ) -> DbResult { let mut g = Self { @@ -127,8 +128,8 @@ impl GroupByScanLock { self.scan.close() } - fn get_val(&self, field_name: &str) -> DbResult { - if self.group_fields.contains(&field_name.to_string()) { + fn get_val(&self, field_name: &Element) -> DbResult { + if self.group_fields.contains(field_name) { return self.group_val.get_val(field_name); } for f in &self.agg_fns { @@ -136,19 +137,19 @@ impl GroupByScanLock { return Ok(f.value()); } } - Err(DbError::field_not_exists(field_name)) + Err(DbError::FieldNotExists(field_name.to_string())) } - fn get_i32(&self, field_name: &str) -> DbResult { + fn get_i32(&self, field_name: &Element) -> DbResult { self.get_val(field_name)?.as_i32() } - fn get_string(&self, field_name: &str) -> DbResult { + fn get_string(&self, field_name: &Element) -> DbResult { self.get_val(field_name)?.as_string() } - fn has_field(&self, field_name: &str) -> bool { - if self.group_fields.contains(&field_name.to_string()) { + fn has_field(&self, field_name: &Element) -> bool { + if self.group_fields.contains(field_name) { return true; } for f in &self.agg_fns { @@ -159,7 +160,7 @@ impl GroupByScanLock { false } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { self.scan.schema() } } @@ -171,7 +172,7 @@ pub struct GroupByScan { impl GroupByScan { pub fn new( scan: &Rc, - group_fields: Vec, + group_fields: Vec, agg_fns: Vec, ) -> DbResult { Ok(Self { @@ -191,22 +192,22 @@ impl Scan for GroupByScan { write.next() } - fn get_i32(&self, field_name: &str) -> DbResult { + fn get_i32(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); read.get_i32(field_name) } - fn get_string(&self, field_name: &str) -> DbResult { + fn get_string(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); read.get_string(field_name) } - fn get_val(&self, field_name: &str) -> DbResult { + fn get_val(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); read.get_val(field_name) } - fn has_field(&self, field_name: &str) -> DbResult { + fn has_field(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); Ok(read.has_field(field_name)) } @@ -216,7 +217,7 @@ impl Scan for GroupByScan { read.close() } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { let read = self.lock.borrow(); read.schema() } diff --git a/engine/src/scan/index.rs b/engine/src/scan/index.rs index 87ae225..9bfda45 100644 --- a/engine/src/scan/index.rs +++ b/engine/src/scan/index.rs @@ -1,18 +1,18 @@ use common::DbResult; use std::rc::Rc; -use std::sync::Arc; -use crate::schema::Schema; -use crate::{constant::Constant, index::Index, scan::Scan}; +use crate::element::Element; +use crate::schema::{Schema, SchemaBuilder}; +use crate::{index::Index, scan::Scan, value::Value}; pub struct IndexSelectScan { scan: Rc, index: Rc, - value: Constant, + value: Value, } impl IndexSelectScan { - pub fn new(scan: &Rc, index: &Rc, value: Constant) -> DbResult { + pub fn new(scan: &Rc, index: &Rc, value: Value) -> DbResult { let scan = Self { scan: Rc::clone(scan), index: Rc::clone(index), @@ -37,19 +37,19 @@ impl Scan for IndexSelectScan { Ok(ok) } - fn get_i32(&self, field_name: &str) -> DbResult { + fn get_i32(&self, field_name: &Element) -> DbResult { self.scan.get_i32(field_name) } - fn get_string(&self, field_name: &str) -> DbResult { + fn get_string(&self, field_name: &Element) -> DbResult { self.scan.get_string(field_name) } - fn get_val(&self, field_name: &str) -> DbResult { + fn get_val(&self, field_name: &Element) -> DbResult { self.scan.get_val(field_name) } - fn has_field(&self, field_name: &str) -> DbResult { + fn has_field(&self, field_name: &Element) -> DbResult { self.scan.has_field(field_name) } @@ -58,7 +58,7 @@ impl Scan for IndexSelectScan { self.scan.close() } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { self.scan.schema() } } @@ -67,20 +67,20 @@ pub struct IndexJoinScan { left: Rc, right: Rc, index: Rc, - field: String, + field: Element, } impl IndexJoinScan { pub fn new( left: &Rc, index: &Rc, - field: &str, + field: Element, right: &Rc, ) -> DbResult { let scan = Self { left: Rc::clone(left), right: Rc::clone(right), - field: field.to_string(), + field, index: Rc::clone(index), }; scan.before_first()?; @@ -113,7 +113,7 @@ impl Scan for IndexJoinScan { } } - fn get_i32(&self, field_name: &str) -> DbResult { + fn get_i32(&self, field_name: &Element) -> DbResult { if self.right.has_field(field_name)? { self.right.get_i32(field_name) } else { @@ -121,7 +121,7 @@ impl Scan for IndexJoinScan { } } - fn get_string(&self, field_name: &str) -> DbResult { + fn get_string(&self, field_name: &Element) -> DbResult { if self.right.has_field(field_name)? { self.right.get_string(field_name) } else { @@ -129,7 +129,7 @@ impl Scan for IndexJoinScan { } } - fn get_val(&self, field_name: &str) -> DbResult { + fn get_val(&self, field_name: &Element) -> DbResult { if self.right.has_field(field_name)? { self.right.get_val(field_name) } else { @@ -137,7 +137,7 @@ impl Scan for IndexJoinScan { } } - fn has_field(&self, field_name: &str) -> DbResult { + fn has_field(&self, field_name: &Element) -> DbResult { Ok(self.right.has_field(field_name)? || self.left.has_field(field_name)?) } @@ -147,40 +147,46 @@ impl Scan for IndexJoinScan { self.right.close() } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { let s1 = self.left.schema()?; let s2 = self.right.schema()?; - s1.add_all(&s2)?; - Ok(s1) + let s = SchemaBuilder::default().add_all(&s1).add_all(&s2).build(); + Ok(s) } } #[cfg(test)] mod tests { use std::collections::HashMap; - use std::sync::Arc; use tempfile::tempdir; - use crate::schema::Schema; + use crate::element::Element; + use crate::schema::SchemaBuilder; use crate::{ SimpleDB, plan::{Plan, table::TablePlan}, }; #[test] - fn update_index() { + fn index_scan_update() { let dir = tempdir().unwrap(); let db = SimpleDB::new(dir.path()).unwrap(); let tx = db.get_tx().unwrap(); let md = db.metadata_mgr(); - let schema = Schema::default(); - schema.add_int_field("sid".to_string()).unwrap(); - schema.add_string_field("sname".to_string(), 16).unwrap(); - schema.add_int_field("gadyear".to_string()).unwrap(); - schema.add_int_field("majorid".to_string()).unwrap(); + let sid = Element::raw("sid"); + let sname = Element::raw("sname"); + let gadyear = Element::raw("gadyear"); + let majorid = Element::raw("majorid"); - md.create_table("users", &Arc::new(schema), &tx).unwrap(); + let schema = SchemaBuilder::default() + .add_int_field(sid.clone()) + .add_string_field(sname.clone(), 16) + .add_int_field(gadyear.clone()) + .add_int_field(majorid.clone()) + .build(); + + md.create_table("users", schema, &tx).unwrap(); md.create_index("users_ids", "users", "sid", &tx).unwrap(); let plan = TablePlan::new(&tx, "users".to_string(), &md).unwrap(); @@ -194,10 +200,10 @@ mod tests { } s.insert().unwrap(); - s.set_i32("sid", 11).unwrap(); - s.set_string("sname", "Sam").unwrap(); - s.set_i32("gadyear", 2023).unwrap(); - s.set_i32("majorid", 30).unwrap(); + s.set_i32(&sid, 11).unwrap(); + s.set_string(&sname, "Sam").unwrap(); + s.set_i32(&gadyear, 2023).unwrap(); + s.set_i32(&majorid, 30).unwrap(); let rid = s.get_rid().unwrap(); for (field, index) in indexes.iter() { @@ -207,7 +213,7 @@ mod tests { s.before_first().unwrap(); while s.next().unwrap() { - if s.get_string("sname").unwrap() == "joe" { + if s.get_string(&sname).unwrap() == "joe" { let rid = s.get_rid().unwrap(); for (field, index) in indexes.iter() { let value = s.get_val(field).unwrap(); @@ -221,8 +227,8 @@ mod tests { while s.next().unwrap() { println!( "{} {}", - s.get_string("sname").unwrap(), - s.get_i32("sid").unwrap() + s.get_string(&sname).unwrap(), + s.get_i32(&sid).unwrap() ); } s.close().unwrap(); diff --git a/engine/src/scan/merge.rs b/engine/src/scan/merge.rs index 709cdd6..4e3708a 100644 --- a/engine/src/scan/merge.rs +++ b/engine/src/scan/merge.rs @@ -1,17 +1,17 @@ use common::DbResult; -use std::sync::Arc; use std::{cell::RefCell, cmp::Ordering, rc::Rc}; -use crate::schema::Schema; -use crate::{constant::Constant, scan::Scan}; +use crate::element::Element; +use crate::schema::{Schema, SchemaBuilder}; +use crate::{scan::Scan, value::Value}; #[allow(dead_code)] struct MergeJoinScanLock { s1: Rc, s2: Rc, - field_name1: String, - field_name2: String, - join_val: Option, + field_name1: Element, + field_name2: Element, + join_val: Option, } #[allow(dead_code)] @@ -19,14 +19,14 @@ impl MergeJoinScanLock { pub fn new( s1: &Rc, s2: &Rc, - field_name1: &str, - field_name2: &str, + field_name1: Element, + field_name2: Element, ) -> DbResult { let s = Self { s1: Rc::clone(s1), s2: Rc::clone(s2), - field_name1: field_name1.to_string(), - field_name2: field_name2.to_string(), + field_name1, + field_name2, join_val: None, }; s.before_first()?; @@ -67,7 +67,7 @@ impl MergeJoinScanLock { Ok(false) } - fn get_i32(&self, field_name: &str) -> DbResult { + fn get_i32(&self, field_name: &Element) -> DbResult { if self.s1.has_field(field_name)? { self.s1.get_i32(field_name) } else { @@ -75,7 +75,7 @@ impl MergeJoinScanLock { } } - fn get_string(&self, field_name: &str) -> DbResult { + fn get_string(&self, field_name: &Element) -> DbResult { if self.s1.has_field(field_name)? { self.s1.get_string(field_name) } else { @@ -83,7 +83,7 @@ impl MergeJoinScanLock { } } - fn get_val(&self, field_name: &str) -> DbResult { + fn get_val(&self, field_name: &Element) -> DbResult { if self.s1.has_field(field_name)? { self.s1.get_val(field_name) } else { @@ -91,7 +91,7 @@ impl MergeJoinScanLock { } } - fn has_field(&self, field_name: &str) -> DbResult { + fn has_field(&self, field_name: &Element) -> DbResult { Ok(self.s1.has_field(field_name)? || self.s2.has_field(field_name)?) } @@ -100,11 +100,11 @@ impl MergeJoinScanLock { self.s2.close() } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { let s1 = self.s1.schema()?; let s2 = self.s2.schema()?; - s1.add_all(&s2)?; - Ok(s1) + let s = SchemaBuilder::default().add_all(&s1).add_all(&s2).build(); + Ok(s) } } @@ -118,8 +118,8 @@ impl MergeJoinScan { pub fn new( s1: &Rc, s2: &Rc, - field_name1: &str, - field_name2: &str, + field_name1: Element, + field_name2: Element, ) -> DbResult { Ok(Self { lock: RefCell::new(MergeJoinScanLock::new(s1, s2, field_name1, field_name2)?), @@ -138,22 +138,22 @@ impl Scan for MergeJoinScan { write.next() } - fn get_i32(&self, field_name: &str) -> DbResult { + fn get_i32(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); read.get_i32(field_name) } - fn get_string(&self, field_name: &str) -> DbResult { + fn get_string(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); read.get_string(field_name) } - fn get_val(&self, field_name: &str) -> DbResult { + fn get_val(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); read.get_val(field_name) } - fn has_field(&self, field_name: &str) -> DbResult { + fn has_field(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); read.has_field(field_name) } @@ -163,7 +163,7 @@ impl Scan for MergeJoinScan { read.close() } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { let read = self.lock.borrow(); read.schema() } diff --git a/engine/src/scan/multibuffer.rs b/engine/src/scan/multibuffer.rs index 769f6cb..2acedb2 100644 --- a/engine/src/scan/multibuffer.rs +++ b/engine/src/scan/multibuffer.rs @@ -3,6 +3,7 @@ use std::{cell::RefCell, rc::Rc, sync::Arc}; use common::{DbResult, error::DbError}; use transaction::transaction::Transaction; +use crate::element::Element; use crate::{ buffer_needs::BufferNeeds, layout::Layout, @@ -16,7 +17,7 @@ struct MultiBufferProductScanInner { right: Option>, prod: Option>, filename: String, - layout: Arc, + layout: Layout, chunk_size: i32, next_block: i32, file_size: i32, @@ -27,7 +28,7 @@ impl MultiBufferProductScanInner { tx: &Arc, left: &Rc, filename: &str, - layout: &Arc, + layout: Layout, ) -> DbResult { let available = tx.available_buffs()? as i32; let file_size = tx.size(filename)? as i32; @@ -36,7 +37,7 @@ impl MultiBufferProductScanInner { left: Rc::clone(left), file_size: tx.size(filename)? as i32, filename: filename.to_string(), - layout: Arc::clone(layout), + layout, chunk_size: BufferNeeds::best_factor(available, file_size), next_block: 0, prod: None, @@ -61,7 +62,7 @@ impl MultiBufferProductScanInner { let right: Rc = Rc::new(ChunkScan::new( &self.tx, &self.filename, - &self.layout, + self.layout.clone(), self.next_block, end, )?); @@ -93,7 +94,7 @@ impl MultiBufferProductScanInner { } } - fn get_i32(&self, field_name: &str) -> DbResult { + fn get_i32(&self, field_name: &Element) -> DbResult { if let Some(prod) = &self.prod { prod.get_i32(field_name) } else { @@ -101,7 +102,7 @@ impl MultiBufferProductScanInner { } } - fn get_string(&self, field_name: &str) -> DbResult { + fn get_string(&self, field_name: &Element) -> DbResult { if let Some(prod) = &self.prod { prod.get_string(field_name) } else { @@ -109,7 +110,7 @@ impl MultiBufferProductScanInner { } } - fn get_val(&self, field_name: &str) -> DbResult { + fn get_val(&self, field_name: &Element) -> DbResult { if let Some(prod) = &self.prod { prod.get_val(field_name) } else { @@ -117,7 +118,7 @@ impl MultiBufferProductScanInner { } } - fn has_field(&self, field_name: &str) -> DbResult { + fn has_field(&self, field_name: &Element) -> DbResult { if let Some(prod) = &self.prod { prod.has_field(field_name) } else { @@ -133,7 +134,7 @@ impl MultiBufferProductScanInner { Ok(()) } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { if let Some(prod) = &self.prod { prod.schema() } else { @@ -151,7 +152,7 @@ impl MultiBufferProductScan { tx: &Arc, left: &Rc, filename: &str, - layout: &Arc, + layout: Layout, ) -> DbResult { Ok(Self { lock: RefCell::new(MultiBufferProductScanInner::new( @@ -172,22 +173,22 @@ impl Scan for MultiBufferProductScan { write.next() } - fn get_i32(&self, field_name: &str) -> DbResult { + fn get_i32(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); read.get_i32(field_name) } - fn get_string(&self, field_name: &str) -> DbResult { + fn get_string(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); read.get_string(field_name) } - fn get_val(&self, field_name: &str) -> DbResult { + fn get_val(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); read.get_val(field_name) } - fn has_field(&self, field_name: &str) -> DbResult { + fn has_field(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); read.has_field(field_name) } @@ -197,7 +198,7 @@ impl Scan for MultiBufferProductScan { read.close() } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { let read = self.lock.borrow(); read.schema() } diff --git a/engine/src/scan/sort.rs b/engine/src/scan/order.rs similarity index 82% rename from engine/src/scan/sort.rs rename to engine/src/scan/order.rs index 211d3c9..17d0c49 100644 --- a/engine/src/scan/sort.rs +++ b/engine/src/scan/order.rs @@ -1,20 +1,19 @@ -use common::{DbResult, error::DbError}; -use std::sync::Arc; -use std::{cell::RefCell, cmp::Ordering, rc::Rc}; - -use crate::constant::Constant; +use crate::element::Element; use crate::rid::RID; use crate::scan::Scan; -use crate::schema::Schema; +use crate::schema::{Schema, SchemaBuilder}; use crate::temp::TempTable; +use crate::value::Value; +use common::{DbResult, error::DbError}; +use std::{cell::RefCell, cmp::Ordering, rc::Rc}; #[derive(Clone)] pub struct RecordComparator { - fields: Vec, + fields: Vec, } impl RecordComparator { - pub fn new(fields: Vec) -> Self { + pub fn new(fields: Vec) -> Self { Self { fields } } @@ -37,7 +36,7 @@ enum CurrentScan { S2, } -pub struct SortScanLock { +pub struct OrderScanInner { s1: Rc, s2: Option>, current_scan: CurrentScan, @@ -47,7 +46,7 @@ pub struct SortScanLock { saved_position: Vec, } -impl SortScanLock { +impl OrderScanInner { fn new(runs: Vec, comp: RecordComparator) -> DbResult { let s1 = runs[0].open()?; let (s2, has_more2) = if runs.len() > 1 { @@ -69,7 +68,7 @@ impl SortScanLock { } } -impl SortScanLock { +impl OrderScanInner { fn before_first(&mut self) -> DbResult<()> { self.s1.before_first()?; self.has_more1 = self.s1.next()?; @@ -113,7 +112,7 @@ impl SortScanLock { Ok(()) } - fn get_val(&self, field_name: &str) -> DbResult { + fn get_val(&self, field_name: &Element) -> DbResult { match self.current_scan { CurrentScan::S1 => self.s1.get_val(field_name), CurrentScan::S2 if let Some(s2) = &self.s2 => s2.get_val(field_name), @@ -121,7 +120,7 @@ impl SortScanLock { } } - fn get_i32(&self, field_name: &str) -> DbResult { + fn get_i32(&self, field_name: &Element) -> DbResult { match self.current_scan { CurrentScan::S1 => self.s1.get_i32(field_name), CurrentScan::S2 if let Some(s2) = &self.s2 => s2.get_i32(field_name), @@ -129,7 +128,7 @@ impl SortScanLock { } } - fn get_string(&self, field_name: &str) -> DbResult { + fn get_string(&self, field_name: &Element) -> DbResult { match self.current_scan { CurrentScan::S1 => self.s1.get_string(field_name), CurrentScan::S2 if let Some(s2) = &self.s2 => s2.get_string(field_name), @@ -137,7 +136,7 @@ impl SortScanLock { } } - fn has_field(&self, field_name: &str) -> DbResult { + fn has_field(&self, field_name: &Element) -> DbResult { match self.current_scan { CurrentScan::S1 => self.s1.has_field(field_name), CurrentScan::S2 if let Some(s2) = &self.s2 => s2.has_field(field_name), @@ -168,24 +167,27 @@ impl SortScanLock { Ok(()) } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { + let mut s = SchemaBuilder::default(); let s1 = self.s1.schema()?; + s = s.add_all(&s1); if let Some(s2) = &self.s2 { let s2 = s2.schema()?; - s1.add_all(&s2)?; + s = s.add_all(&s2); } - Ok(s1) + let s = s.build(); + Ok(s) } } -pub struct SortScan { - lock: RefCell, +pub struct OrderScan { + lock: RefCell, } -impl SortScan { +impl OrderScan { pub fn new(runs: Vec, comp: RecordComparator) -> DbResult { Ok(Self { - lock: RefCell::new(SortScanLock::new(runs, comp)?), + lock: RefCell::new(OrderScanInner::new(runs, comp)?), }) } @@ -202,7 +204,7 @@ impl SortScan { } } -impl Scan for SortScan { +impl Scan for OrderScan { fn before_first(&self) -> DbResult<()> { let mut write = self.lock.borrow_mut(); write.before_first() @@ -213,22 +215,22 @@ impl Scan for SortScan { write.next() } - fn get_i32(&self, field_name: &str) -> DbResult { + fn get_i32(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); read.get_i32(field_name) } - fn get_string(&self, field_name: &str) -> DbResult { + fn get_string(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); read.get_string(field_name) } - fn get_val(&self, field_name: &str) -> DbResult { + fn get_val(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); read.get_val(field_name) } - fn has_field(&self, field_name: &str) -> DbResult { + fn has_field(&self, field_name: &Element) -> DbResult { let read = self.lock.borrow(); read.has_field(field_name) } @@ -238,7 +240,7 @@ impl Scan for SortScan { read.close() } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { let read = self.lock.borrow(); read.schema() } diff --git a/engine/src/scan/product.rs b/engine/src/scan/product.rs index 0fbd5da..c170f56 100644 --- a/engine/src/scan/product.rs +++ b/engine/src/scan/product.rs @@ -1,9 +1,8 @@ +use crate::element::Element; +use crate::scan::Scan; +use crate::schema::{Schema, SchemaBuilder}; use common::DbResult; use std::rc::Rc; -use std::sync::Arc; - -use crate::scan::Scan; -use crate::schema::Schema; pub struct ProductScan { s1: Rc, @@ -33,7 +32,7 @@ impl Scan for ProductScan { } } - fn get_i32(&self, field_name: &str) -> DbResult { + fn get_i32(&self, field_name: &Element) -> DbResult { if self.s1.has_field(field_name)? { self.s1.get_i32(field_name) } else { @@ -41,7 +40,7 @@ impl Scan for ProductScan { } } - fn get_string(&self, field_name: &str) -> DbResult { + fn get_string(&self, field_name: &Element) -> DbResult { if self.s1.has_field(field_name)? { self.s1.get_string(field_name) } else { @@ -49,7 +48,7 @@ impl Scan for ProductScan { } } - fn get_val(&self, field_name: &str) -> DbResult { + fn get_val(&self, field_name: &Element) -> DbResult { if self.s1.has_field(field_name)? { self.s1.get_val(field_name) } else { @@ -57,7 +56,7 @@ impl Scan for ProductScan { } } - fn has_field(&self, field_name: &str) -> DbResult { + fn has_field(&self, field_name: &Element) -> DbResult { Ok(self.s1.has_field(field_name)? || self.s2.has_field(field_name)?) } @@ -66,10 +65,10 @@ impl Scan for ProductScan { self.s2.close() } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { let s1 = self.s1.schema()?; let s2 = self.s2.schema()?; - s1.add_all(&s2)?; - Ok(s1) + let s = SchemaBuilder::default().add_all(&s1).add_all(&s2).build(); + Ok(s) } } diff --git a/engine/src/scan/project.rs b/engine/src/scan/project.rs index 49b7954..52c9f07 100644 --- a/engine/src/scan/project.rs +++ b/engine/src/scan/project.rs @@ -1,17 +1,17 @@ +use crate::element::Element; use crate::scan::Scan; -use crate::schema::Schema; +use crate::schema::{Schema, SchemaBuilder}; use common::DbResult; use common::error::DbError; -use std::sync::Arc; use std::{collections::HashSet, rc::Rc}; pub struct ProjectScan { scan: Rc, - fields: HashSet, + fields: HashSet, } impl ProjectScan { - pub fn new(scan: Rc, fields: HashSet) -> Self { + pub fn new(scan: Rc, fields: HashSet) -> Self { Self { scan, fields } } } @@ -25,31 +25,31 @@ impl Scan for ProjectScan { self.scan.next() } - fn get_i32(&self, field_name: &str) -> DbResult { + fn get_i32(&self, field_name: &Element) -> DbResult { if self.has_field(field_name)? { self.scan.get_i32(field_name) } else { - Err(DbError::field_not_exists(field_name)) + Err(DbError::FieldNotExists(field_name.to_string())) } } - fn get_string(&self, field_name: &str) -> DbResult { + fn get_string(&self, field_name: &Element) -> DbResult { if self.has_field(field_name)? { self.scan.get_string(field_name) } else { - Err(DbError::field_not_exists(field_name)) + Err(DbError::FieldNotExists(field_name.to_string())) } } - fn get_val(&self, field_name: &str) -> DbResult { + fn get_val(&self, field_name: &Element) -> DbResult { if self.has_field(field_name)? { self.scan.get_val(field_name) } else { - Err(DbError::field_not_exists(field_name)) + Err(DbError::FieldNotExists(field_name.to_string())) } } - fn has_field(&self, field_name: &str) -> DbResult { + fn has_field(&self, field_name: &Element) -> DbResult { Ok(self.fields.contains(field_name)) } @@ -57,14 +57,14 @@ impl Scan for ProjectScan { self.scan.close() } - fn schema(&self) -> DbResult> { - let project = Arc::new(Schema::default()); + fn schema(&self) -> DbResult { + let mut project = SchemaBuilder::default(); let schema = self.scan.schema()?; - for (field, info) in schema.fields()? { + for (field, info) in schema.fields() { if self.fields.contains(&field) { - project.add_field(field, info)?; + project = project.add_field(field, info); } } - Ok(project) + Ok(project.build()) } } diff --git a/engine/src/scan/select.rs b/engine/src/scan/select.rs index 5b7e512..b003c32 100644 --- a/engine/src/scan/select.rs +++ b/engine/src/scan/select.rs @@ -1,7 +1,7 @@ use common::DbResult; use std::rc::Rc; -use std::sync::Arc; +use crate::element::Element; use crate::schema::Schema; use crate::{predicate::Predicate, scan::Scan}; @@ -30,19 +30,19 @@ impl Scan for SelectScan { Ok(false) } - fn get_i32(&self, field_name: &str) -> DbResult { + fn get_i32(&self, field_name: &Element) -> DbResult { self.scan.get_i32(field_name) } - fn get_string(&self, field_name: &str) -> DbResult { + fn get_string(&self, field_name: &Element) -> DbResult { self.scan.get_string(field_name) } - fn get_val(&self, field_name: &str) -> common::DbResult { + fn get_val(&self, field_name: &Element) -> common::DbResult { self.scan.get_val(field_name) } - fn has_field(&self, field_name: &str) -> common::DbResult { + fn has_field(&self, field_name: &Element) -> common::DbResult { self.scan.has_field(field_name) } @@ -50,19 +50,19 @@ impl Scan for SelectScan { self.scan.close() } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { self.scan.schema() } - fn set_i32(&self, field_name: &str, value: i32) -> DbResult<()> { + fn set_i32(&self, field_name: &Element, value: i32) -> DbResult<()> { self.scan.set_i32(field_name, value) } - fn set_string(&self, field_name: &str, value: &str) -> DbResult<()> { + fn set_string(&self, field_name: &Element, value: &str) -> DbResult<()> { self.scan.set_string(field_name, value) } - fn set_val(&self, field_name: &str, value: crate::constant::Constant) -> DbResult<()> { + fn set_val(&self, field_name: &Element, value: crate::value::Value) -> DbResult<()> { self.scan.set_val(field_name, value) } diff --git a/engine/src/scan/table.rs b/engine/src/scan/table.rs index d208a8b..54aa40f 100644 --- a/engine/src/scan/table.rs +++ b/engine/src/scan/table.rs @@ -5,35 +5,36 @@ use common::{DbResult, error::DbError}; use file::block::BlockId; use transaction::transaction::Transaction; +use crate::element::Element; use crate::schema::Schema; use crate::{ - constant::Constant, field_info::FieldInfo, layout::Layout, record_page::RecordPage, rid::RID, - scan::Scan, + field_info::FieldInfo, layout::Layout, record_page::RecordPage, rid::RID, scan::Scan, + value::Value, }; struct TableScanInner { tx: Arc, - layout: Arc, + layout: Layout, rp: RecordPage, filename: String, current_slot: i32, } impl TableScanInner { - fn new(tx: &Arc, table_name: &str, layout: &Arc) -> DbResult { + fn new(tx: &Arc, table_name: &str, layout: Layout) -> DbResult { let filename = format!("{}.tbl", table_name); let rp = if tx.size(&filename)? == 0 { let block = tx.append(&filename)?; - let rp = RecordPage::new(tx, block, layout)?; + let rp = RecordPage::new(tx, block, layout.clone())?; rp.format()?; rp } else { let block = BlockId::new(&filename, 0); - RecordPage::new(tx, block, layout)? + RecordPage::new(tx, block, layout.clone())? }; Ok(Self { tx: Arc::clone(tx), - layout: Arc::clone(layout), + layout, rp, current_slot: -1, filename, @@ -60,44 +61,44 @@ impl TableScanInner { Ok(true) } - pub fn get_i32(&self, filename: &str) -> DbResult { + pub fn get_i32(&self, filename: &Element) -> DbResult { self.rp.get_i32(self.current_slot, filename) } - pub fn get_string(&self, filename: &str) -> DbResult { + pub fn get_string(&self, filename: &Element) -> DbResult { self.rp.get_string(self.current_slot, filename) } - pub fn get_val(&self, fieldname: &str) -> DbResult { - let Some(info) = self.layout.schema().info(fieldname)? else { - return Err(DbError::field_not_exists(fieldname)); + pub fn get_val(&self, fieldname: &Element) -> DbResult { + let Some(info) = self.layout.schema().info(fieldname) else { + return Err(DbError::FieldNotExists(fieldname.to_string())); }; match info { - FieldInfo::Integer => Ok(Constant::Integer(self.get_i32(fieldname)?)), - FieldInfo::Varchar(_) => Ok(Constant::Varchar(self.get_string(fieldname)?)), + FieldInfo::Integer => Ok(Value::Integer(self.get_i32(fieldname)?)), + FieldInfo::Varchar(_) => Ok(Value::Varchar(self.get_string(fieldname)?)), } } - pub fn has_field(&self, fieldname: &str) -> DbResult { + pub fn has_field(&self, fieldname: &Element) -> bool { self.layout.schema().has_field(fieldname) } - pub fn set_i32(&self, field: &str, value: i32) -> DbResult<()> { + pub fn set_i32(&self, field: &Element, value: i32) -> DbResult<()> { let current_slot = self.current_slot; let rp = &self.rp; rp.set_i32(current_slot, field, value) } - pub fn set_string(&self, field: &str, value: &str) -> DbResult<()> { + pub fn set_string(&self, field: &Element, value: &str) -> DbResult<()> { let current_slot = self.current_slot; let rp = &self.rp; rp.set_string(current_slot, field, value) } - pub fn set_val(&self, field: &str, value: Constant) -> DbResult<()> { + pub fn set_val(&self, field: &Element, value: Value) -> DbResult<()> { match value { - Constant::Integer(value) => self.set_i32(field, value), - Constant::Varchar(value) => self.set_string(field, &value), + Value::Integer(value) => self.set_i32(field, value), + Value::Varchar(value) => self.set_string(field, &value), } } @@ -125,7 +126,7 @@ impl TableScanInner { pub fn move_to_rid(&mut self, rid: RID) -> DbResult<()> { self.close()?; let block = BlockId::new(&self.filename, rid.block_num()); - let rp = RecordPage::new(&self.tx, block, &self.layout)?; + let rp = RecordPage::new(&self.tx, block, self.layout.clone())?; self.rp = rp; self.current_slot = rid.slot(); Ok(()) @@ -139,7 +140,7 @@ impl TableScanInner { fn move_to_block(&mut self, num: i32) -> DbResult<()> { self.close()?; let block = BlockId::new(&self.filename, num); - self.rp = RecordPage::new(&self.tx, block, &self.layout)?; + self.rp = RecordPage::new(&self.tx, block, self.layout.clone())?; self.current_slot = -1; Ok(()) } @@ -147,7 +148,7 @@ impl TableScanInner { fn move_to_new_block(&mut self) -> DbResult<()> { self.close()?; let block = self.tx.append(&self.filename)?; - self.rp = RecordPage::new(&self.tx, block, &self.layout)?; + self.rp = RecordPage::new(&self.tx, block, self.layout.clone())?; self.rp.format()?; self.current_slot = -1; Ok(()) @@ -157,8 +158,8 @@ impl TableScanInner { Ok(self.rp.block().num == self.tx.size(&self.filename)? as i32 - 1) } - fn schema(&self) -> DbResult> { - Ok(self.layout.schema()) + fn schema(&self) -> DbResult { + Ok(self.layout.schema().clone()) } } @@ -167,7 +168,7 @@ pub struct TableScan { } impl TableScan { - pub fn new(tx: &Arc, tablename: &str, layout: &Arc) -> DbResult { + pub fn new(tx: &Arc, tablename: &str, layout: Layout) -> DbResult { Ok(Self { lock: RefCell::new(TableScanInner::new(tx, tablename, layout)?), }) @@ -185,24 +186,24 @@ impl Scan for TableScan { write.next() } - fn get_i32(&self, field: &str) -> DbResult { + fn get_i32(&self, field: &Element) -> DbResult { let read = self.lock.borrow(); read.get_i32(field) } - fn get_string(&self, field: &str) -> DbResult { + fn get_string(&self, field: &Element) -> DbResult { let read = self.lock.borrow(); read.get_string(field) } - fn get_val(&self, field: &str) -> DbResult { + fn get_val(&self, field: &Element) -> DbResult { let read = self.lock.borrow(); read.get_val(field) } - fn has_field(&self, field: &str) -> DbResult { + fn has_field(&self, field: &Element) -> DbResult { let read = self.lock.borrow(); - read.has_field(field) + Ok(read.has_field(field)) } fn close(&self) -> DbResult<()> { @@ -210,17 +211,17 @@ impl Scan for TableScan { read.close() } - fn set_i32(&self, field: &str, value: i32) -> DbResult<()> { + fn set_i32(&self, field: &Element, value: i32) -> DbResult<()> { let read = self.lock.borrow(); read.set_i32(field, value) } - fn set_string(&self, field: &str, value: &str) -> DbResult<()> { + fn set_string(&self, field: &Element, value: &str) -> DbResult<()> { let read = self.lock.borrow(); read.set_string(field, value) } - fn set_val(&self, field: &str, value: Constant) -> DbResult<()> { + fn set_val(&self, field: &Element, value: Value) -> DbResult<()> { let read = self.lock.borrow(); read.set_val(field, value) } @@ -245,7 +246,7 @@ impl Scan for TableScan { Ok(read.get_rid()) } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { let read = self.lock.borrow(); read.schema() } @@ -253,14 +254,12 @@ impl Scan for TableScan { #[cfg(test)] mod tests { + use crate::schema::SchemaBuilder; use buffer::mgr::BufferMgr; use file::mgr::FileMgr; use log::mgr::LogMgr; - use rand::RngExt; use tempfile::tempdir; - use transaction::{lock_table::LockTable, txnum_generator::TxNumGenerator}; - - use crate::schema::Schema; + use transaction::lock_table::LockTable; use super::*; @@ -270,54 +269,51 @@ mod tests { let fm = Arc::new(FileMgr::new(dir.path(), 512).unwrap()); let lm = Arc::new(LogMgr::new(&fm, "testlog".to_string()).unwrap()); let bm = Arc::new(BufferMgr::new(&fm, &lm, 1).unwrap()); - let txnum_generator = TxNumGenerator::default(); let lock_table = Arc::new(LockTable::default()); - let tx = Arc::new(Transaction::new(&txnum_generator, &fm, &lm, &bm, &lock_table).unwrap()); - let schema = Arc::new(Schema::default()); - schema.add_int_field("A".to_string()).unwrap(); - schema.add_string_field("B".to_string(), 9).unwrap(); + let tx = Arc::new(Transaction::new(&fm, &lm, &bm, &lock_table).unwrap()); + let schema = SchemaBuilder::default() + .add_int_field(Element::raw("A")) + .add_string_field(Element::raw("B"), 9) + .build(); - let layout = Arc::new(Layout::new(&schema).unwrap()); - for (field, _) in layout.schema().fields().unwrap() { - let offset = layout.offset(&field); - println!("{} has offset {}", field, offset); - } + let layout = Layout::new(schema); + let offset_a = layout.offset(&Element::raw("A")); + let offset_b = layout.offset(&Element::raw("B")); + assert_ne!(offset_a, offset_b, "fields must occupy distinct offsets"); - let ts = TableScan::new(&tx, "T", &layout).unwrap(); - println!("Fillins the table with 50 random records"); + // Fill the table with 50 records carrying known A-values 0..50. + let ts = TableScan::new(&tx, "T", layout).unwrap(); ts.before_first().unwrap(); - let mut rng = rand::rng(); - for _ in 0..50 { + for n in 0..50 { ts.insert().unwrap(); - let n = rng.random::(); - ts.set_i32("A", n).unwrap(); - ts.set_string("B", &format!("record{}", n)).unwrap(); - println!( - "inserting into slot {} {{'{}' 'record{}'}}", - ts.get_rid().unwrap(), - n, - n - ); + ts.set_i32(&Element::raw("A"), n).unwrap(); + ts.set_string(&Element::raw("B"), &format!("record{}", n)) + .unwrap(); } - println!("Deleting records with A-values < 10."); - let mut count = 0; + + // Delete every record whose A-value is below 10 (exactly 0..10). + let mut deleted = 0; ts.before_first().unwrap(); while ts.next().unwrap() { - let a = ts.get_i32("A").unwrap(); - let b = ts.get_string("B").unwrap(); + let a = ts.get_i32(&Element::raw("A")).unwrap(); if a < 10 { - count += 1; - println!("slot {} {{'{}' '{}'}}", ts.get_rid().unwrap(), a, b); + ts.delete().unwrap(); + deleted += 1; } } - println!("{} values under 25 were deleted", count); - println!("Here are the remaining records."); + assert_eq!(deleted, 10, "records with A < 10 should be deleted"); + + // The remaining records are exactly the 40 with A-values 10..50. + let mut remaining = 0; ts.before_first().unwrap(); while ts.next().unwrap() { - let a = ts.get_i32("A").unwrap(); - let b = ts.get_string("B").unwrap(); - println!("{} slot {{'{}' '{}'}}", ts.get_rid().unwrap(), a, b); + let a = ts.get_i32(&Element::raw("A")).unwrap(); + let b = ts.get_string(&Element::raw("B")).unwrap(); + assert!(a >= 10, "deleted records should not survive"); + assert_eq!(b, format!("record{}", a), "A and B must stay paired"); + remaining += 1; } + assert_eq!(remaining, 40, "40 records should remain after deletion"); } } diff --git a/engine/src/schema.rs b/engine/src/schema.rs index 6aa0b99..e62f6b2 100644 --- a/engine/src/schema.rs +++ b/engine/src/schema.rs @@ -1,37 +1,30 @@ -use std::{collections::HashMap, fmt::Debug, sync::RwLock}; +use std::{collections::HashMap, fmt::Debug, rc::Rc}; -use common::{DbResult, error::DbError}; +use crate::{element::Element, field_info::FieldInfo}; -use crate::field_info::FieldInfo; - -#[derive(Debug)] -struct SchemaLock { - fields: Vec, - infos: HashMap, +#[derive(Debug, PartialEq, Eq)] +pub struct SchemaInner { + fields: Vec, + infos: HashMap, } -impl SchemaLock { - fn new() -> Self { - Self { - fields: Vec::new(), - infos: HashMap::new(), +impl SchemaInner { + fn add_field(&mut self, fieldname: Element, field_info: FieldInfo) { + if !self.infos.contains_key(&fieldname) { + self.fields.push(fieldname.clone()); + self.infos.insert(fieldname, field_info); } } - fn add_field(&mut self, fieldname: String, field_info: FieldInfo) { - self.fields.push(fieldname.clone()); - self.infos.insert(fieldname, field_info); - } - - fn add_int_field(&mut self, fieldname: String) { + fn add_int_field(&mut self, fieldname: Element) { self.add_field(fieldname, FieldInfo::Integer); } - fn add_string_field(&mut self, fieldname: String, length: i32) { + fn add_string_field(&mut self, fieldname: Element, length: i32) { self.add_field(fieldname, FieldInfo::Varchar(length)); } - fn add(&mut self, fieldname: String, schema: &Self) { + fn add(&mut self, fieldname: Element, schema: &Self) { if let Some(info) = schema.info(&fieldname) { self.add_field(fieldname, info); } @@ -43,81 +36,107 @@ impl SchemaLock { } } - fn info(&self, fieldname: &str) -> Option { + fn info(&self, fieldname: &Element) -> Option { self.infos.get(fieldname).cloned() } -} -pub struct Schema { - lock: RwLock, -} + fn fields(&self) -> Vec<(Element, FieldInfo)> { + let mut result = vec![]; + for field in &self.fields { + if let Some(info) = self.infos.get(field) { + result.push((field.clone(), info.clone())); + } + } + result + } -impl Debug for Schema { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let read = self.lock.read().unwrap(); - write!(f, "Schema {{ {:?} }}", &*read) + fn has_field(&self, field: &Element) -> bool { + self.infos.contains_key(field) } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Schema(Rc); + impl Schema { - pub fn add_field(&self, fieldname: String, info: FieldInfo) -> DbResult<()> { - let mut write = self.lock.write().map_err(DbError::lock)?; - write.add_field(fieldname, info); - Ok(()) + pub fn info(&self, fieldname: &Element) -> Option { + self.0.info(fieldname) } - pub fn add_int_field(&self, fieldname: String) -> DbResult<()> { - let mut write = self.lock.write().map_err(DbError::lock)?; - write.add_int_field(fieldname); - Ok(()) + pub fn fields(&self) -> Vec<(Element, FieldInfo)> { + self.0.fields() } - pub fn add_string_field(&self, fieldname: String, length: i32) -> DbResult<()> { - let mut write = self.lock.write().map_err(DbError::lock)?; - write.add_string_field(fieldname, length); - Ok(()) + pub fn has_field(&self, field: &Element) -> bool { + self.0.has_field(field) } +} + +pub struct SchemaBuilder { + schema: SchemaInner, +} - pub fn add(&self, fieldname: String, schema: &Self) -> DbResult<()> { - let mut write = self.lock.write().map_err(DbError::lock)?; - let read = schema.lock.read().map_err(DbError::lock)?; - write.add(fieldname, &read); - Ok(()) +impl SchemaBuilder { + pub fn add_field(mut self, field: Element, info: FieldInfo) -> Self { + self.schema.add_field(field, info); + self } - pub fn add_all(&self, schema: &Self) -> DbResult<()> { - let mut write = self.lock.write().map_err(DbError::lock)?; - let read = schema.lock.read().map_err(DbError::lock)?; - write.add_all(&read); - Ok(()) + pub fn add_int_field(mut self, field: Element) -> Self { + self.schema.add_int_field(field); + self } - pub fn has_field(&self, fieldname: &str) -> DbResult { - let read = self.lock.read().map_err(DbError::lock)?; - Ok(read.infos.contains_key(fieldname)) + pub fn add_string_field(mut self, field: Element, len: i32) -> Self { + self.schema.add_string_field(field, len); + self } - pub fn info(&self, fieldname: &str) -> DbResult> { - let read = self.lock.read().map_err(DbError::lock)?; - Ok(read.info(fieldname)) + pub fn add(mut self, field: Element, schema: &Schema) -> Self { + self.schema.add(field, &schema.0); + self } - pub fn fields(&self) -> DbResult> { - let read = self.lock.read().map_err(DbError::lock)?; - let mut result = vec![]; - for field in &read.fields { - if let Some(info) = read.infos.get(field) { - result.push((field.clone(), info.clone())); - } - } - Ok(result) + pub fn add_all(mut self, schema: &Schema) -> Self { + self.schema.add_all(&schema.0); + self + } + + pub fn build(self) -> Schema { + Schema(Rc::new(self.schema)) } } -impl Default for Schema { +impl Default for SchemaBuilder { fn default() -> Self { Self { - lock: RwLock::new(SchemaLock::new()), + schema: SchemaInner { + fields: vec![], + infos: HashMap::new(), + }, } } } + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn add() { + let schema = SchemaBuilder::default() + .add_int_field(Element::raw("test")) + .build(); + let schema = SchemaBuilder::default() + .add(Element::raw("test"), &schema) + .build(); + assert!(schema.has_field(&Element::raw("test"))); + let schema = SchemaBuilder::default().add_all(&schema).build(); + assert!(schema.has_field(&Element::raw("test"))); + let schema = SchemaBuilder::default() + .add_field(Element::raw("id"), FieldInfo::Integer) + .build(); + assert!(schema.has_field(&Element::raw("id"))); + } +} diff --git a/engine/src/sort_by.rs b/engine/src/sort_by.rs index 972a424..0f6cfff 100644 --- a/engine/src/sort_by.rs +++ b/engine/src/sort_by.rs @@ -1,6 +1,8 @@ +use crate::element::Element; + #[derive(Debug, Default)] pub struct SortByData { - pub fields: Vec, + pub fields: Vec, } impl SortByData { diff --git a/engine/src/stat_mgr.rs b/engine/src/stat_mgr.rs index f90cfbe..86ca9d7 100644 --- a/engine/src/stat_mgr.rs +++ b/engine/src/stat_mgr.rs @@ -1,16 +1,15 @@ use std::{ collections::HashMap, + rc::Rc, sync::{Arc, RwLock}, }; use common::{DbResult, error::DbError}; use transaction::transaction::Transaction; -use crate::{ - layout::Layout, - scan::{Scan, table::TableScan}, - table_mgr::{TABLE_NAME, TableMgr}, -}; +use crate::scan::table::TableScan; +use crate::table_mgr::{TABLE_NAME, TableMgr}; +use crate::{element::Element, layout::Layout, scan::Scan}; #[derive(Clone, Debug)] pub struct StatInfo { @@ -40,15 +39,15 @@ impl StatInfo { } struct StatMgrLock { - table_mgr: Arc, + table_mgr: TableMgr, table_stats: HashMap, num_calls: u64, } impl StatMgrLock { - fn new(table_mgr: &Arc) -> Self { + fn new(table_mgr: TableMgr) -> Self { Self { - table_mgr: Arc::clone(table_mgr), + table_mgr, table_stats: HashMap::new(), num_calls: 0, } @@ -57,7 +56,7 @@ impl StatMgrLock { fn get_stat_info( &mut self, table_name: &str, - layout: &Arc, + layout: Layout, tx: &Arc, ) -> DbResult { self.num_calls += 1; @@ -77,12 +76,12 @@ impl StatMgrLock { fn refresh_statisic(&mut self, tx: &Arc) -> DbResult<()> { self.table_stats.clear(); self.num_calls = 0; - let layout = Arc::new(self.table_mgr.get_layout(TABLE_NAME, tx)?); - let ts = TableScan::new(tx, TABLE_NAME, &layout)?; + let layout = self.table_mgr.get_layout(TABLE_NAME, tx)?; + let ts = TableScan::new(tx, TABLE_NAME, layout)?; while ts.next()? { - let table_name = ts.get_string(TABLE_NAME)?; + let table_name = ts.get_string(&Element::raw(TABLE_NAME))?; let layout = self.table_mgr.get_layout(&table_name, tx)?; - let stat = self.calc_table_stats(&table_name, &Arc::new(layout), tx)?; + let stat = self.calc_table_stats(&table_name, layout, tx)?; self.table_stats.insert(table_name, stat); } ts.close() @@ -91,7 +90,7 @@ impl StatMgrLock { fn calc_table_stats( &self, table_name: &str, - layout: &Arc, + layout: Layout, tx: &Arc, ) -> DbResult { let mut num_recs = 0; @@ -106,23 +105,24 @@ impl StatMgrLock { } } +#[derive(Clone)] pub struct StatMgr { - lock: RwLock, + lock: Rc>, } impl StatMgr { - pub fn new(table_mgr: &Arc, tx: &Arc) -> DbResult { + pub fn new(table_mgr: TableMgr, tx: &Arc) -> DbResult { let mut lock = StatMgrLock::new(table_mgr); lock.refresh_statisic(tx)?; Ok(Self { - lock: RwLock::new(lock), + lock: Rc::new(RwLock::new(lock)), }) } pub fn get_stat_info( &self, table_name: &str, - layout: &Arc, + layout: Layout, tx: &Arc, ) -> DbResult { let mut write = self.lock.write().map_err(DbError::lock)?; diff --git a/engine/src/table_mgr.rs b/engine/src/table_mgr.rs index 39e8342..489c4cb 100644 --- a/engine/src/table_mgr.rs +++ b/engine/src/table_mgr.rs @@ -3,11 +3,13 @@ use std::{collections::HashMap, sync::Arc}; use common::DbResult; use transaction::transaction::Transaction; +use crate::scan::table::TableScan; use crate::{ + element::Element, field_info::FieldInfo, layout::Layout, - scan::{Scan, table::TableScan}, - schema::Schema, + scan::Scan, + schema::{Schema, SchemaBuilder}, }; const MAX_NAME: i32 = 16; @@ -21,34 +23,37 @@ const F_FIELD_NAME: &str = "field"; const F_TYPE_LENGTH: &str = "length"; const F_OFFSET: &str = "offset"; +#[derive(Clone, Debug)] pub struct TableMgr { - table_catalog_layout: Arc, - fields_catalog_layout: Arc, + table_layout: Layout, + fields_layout: Layout, } impl TableMgr { pub fn new(is_new: bool, tx: &Arc) -> DbResult { - let table_catalog_schema = Arc::new(Schema::default()); - table_catalog_schema.add_string_field(TABLE_NAME.to_string(), MAX_NAME)?; - table_catalog_schema.add_int_field(TABLE_SLOT_SIZE.to_string())?; - let table_catalog_layout = Layout::new(&table_catalog_schema)?; - - let fields_catalog_schema = Arc::new(Schema::default()); - fields_catalog_schema.add_string_field(TABLE_NAME.to_string(), MAX_NAME)?; - fields_catalog_schema.add_string_field(F_FIELD_NAME.to_string(), MAX_NAME)?; - fields_catalog_schema.add_int_field(F_TYPE.to_string())?; - fields_catalog_schema.add_int_field(F_TYPE_LENGTH.to_string())?; - fields_catalog_schema.add_int_field(F_OFFSET.to_string())?; - let fields_catalog_layout = Layout::new(&fields_catalog_schema)?; + let table_schema = SchemaBuilder::default() + .add_string_field(Element::raw(TABLE_NAME), MAX_NAME) + .add_int_field(Element::raw(TABLE_SLOT_SIZE)) + .build(); + let table_layout = Layout::new(table_schema.clone()); + + let fields_schema = SchemaBuilder::default() + .add_string_field(Element::raw(TABLE_NAME), MAX_NAME) + .add_string_field(Element::raw(F_FIELD_NAME), MAX_NAME) + .add_int_field(Element::raw(F_TYPE)) + .add_int_field(Element::raw(F_TYPE_LENGTH)) + .add_int_field(Element::raw(F_OFFSET)) + .build(); + let fields_layout = Layout::new(fields_schema.clone()); let mgr = Self { - table_catalog_layout: Arc::new(table_catalog_layout), - fields_catalog_layout: Arc::new(fields_catalog_layout), + table_layout, + fields_layout, }; if is_new { - mgr.create_table(TABLE_NAME, &table_catalog_schema, tx)?; - mgr.create_table(FIELDS_NAME, &fields_catalog_schema, tx)?; + mgr.create_table(TABLE_NAME, table_schema, tx)?; + mgr.create_table(FIELDS_NAME, fields_schema, tx)?; } Ok(mgr) @@ -57,55 +62,59 @@ impl TableMgr { pub fn create_table( &self, table_name: &str, - schema: &Arc, + schema: Schema, tx: &Arc, ) -> DbResult<()> { - let layout = Layout::new(schema)?; + let layout = Layout::new(schema.clone()); - let tcat = TableScan::new(tx, TABLE_NAME, &self.table_catalog_layout)?; + let tcat = TableScan::new(tx, TABLE_NAME, self.table_layout.clone())?; tcat.insert()?; - tcat.set_string(TABLE_NAME, table_name)?; - tcat.set_i32(TABLE_SLOT_SIZE, layout.slotsize())?; + tcat.set_string(&Element::raw(TABLE_NAME), table_name)?; + tcat.set_i32(&Element::raw(TABLE_SLOT_SIZE), layout.slotsize())?; tcat.close()?; - let fcat = TableScan::new(tx, FIELDS_NAME, &self.fields_catalog_layout)?; - for (field_name, info) in schema.fields()? { + let fcat = TableScan::new(tx, FIELDS_NAME, self.fields_layout.clone())?; + for (field_name, info) in schema.fields() { fcat.insert()?; - fcat.set_string(TABLE_NAME, table_name)?; - fcat.set_string(F_FIELD_NAME, &field_name)?; - fcat.set_i32(F_TYPE, info.type_id())?; - fcat.set_i32(F_TYPE_LENGTH, info.length())?; - fcat.set_i32(F_OFFSET, layout.offset(&field_name))?; + fcat.set_string(&Element::raw(TABLE_NAME), table_name)?; + fcat.set_string(&Element::raw(F_FIELD_NAME), &field_name.to_string())?; + fcat.set_i32(&Element::raw(F_TYPE), info.type_id())?; + fcat.set_i32(&Element::raw(F_TYPE_LENGTH), info.length())?; + fcat.set_i32(&Element::raw(F_OFFSET), layout.offset(&field_name))?; } fcat.close() } pub fn get_layout(&self, table_name: &str, tx: &Arc) -> DbResult { let mut size = -1; - let tcat = TableScan::new(tx, TABLE_NAME, &self.table_catalog_layout)?; + let tcat = TableScan::new(tx, TABLE_NAME, self.table_layout.clone())?; while tcat.next()? { - if tcat.get_string(TABLE_NAME)? == table_name { - size = tcat.get_i32(TABLE_SLOT_SIZE)?; + if tcat.get_string(&Element::raw(TABLE_NAME))? == table_name { + size = tcat.get_i32(&Element::raw(TABLE_SLOT_SIZE))?; break; } } tcat.close()?; - let schema = Arc::new(Schema::default()); + let mut schema = SchemaBuilder::default(); let mut offsets = HashMap::new(); - let fcat = TableScan::new(tx, FIELDS_NAME, &self.fields_catalog_layout)?; + let fcat = TableScan::new(tx, FIELDS_NAME, self.fields_layout.clone())?; while fcat.next()? { - let table = fcat.get_string(TABLE_NAME)?; + let table = fcat.get_string(&Element::raw(TABLE_NAME))?; if table == table_name { - let field_name = fcat.get_string(F_FIELD_NAME)?; - let field_type = fcat.get_i32(F_TYPE)?; - let field_len = fcat.get_i32(F_TYPE_LENGTH)?; - let offset = fcat.get_i32(F_OFFSET)?; - schema.add_field(field_name.clone(), FieldInfo::new(field_type, field_len)?)?; - offsets.insert(field_name, offset); + let field_name = fcat.get_string(&Element::raw(F_FIELD_NAME))?; + let field_type = fcat.get_i32(&Element::raw(F_TYPE))?; + let field_len = fcat.get_i32(&Element::raw(F_TYPE_LENGTH))?; + let offset = fcat.get_i32(&Element::raw(F_OFFSET))?; + schema = schema.add_field( + Element::Raw(field_name.clone()), + FieldInfo::new(field_type, field_len)?, + ); + offsets.insert(Element::Raw(field_name), offset); } } + let schema = schema.build(); fcat.close()?; - Ok(Layout::from(&schema, offsets, size)) + Ok(Layout::from(schema, offsets, size)) } } @@ -113,29 +122,28 @@ impl TableMgr { mod tests { use std::collections::HashSet; + use crate::tests::init; + use super::*; - use crate::SimpleDB; - use tempfile::tempdir; #[test] fn manager() { - let dir = tempdir().unwrap(); - let db = SimpleDB::new(dir.path()).unwrap(); + let (_dir, tx) = init(); - let tx = db.get_tx().unwrap(); let tm = TableMgr::new(true, &tx).unwrap(); - let schema = Arc::new(Schema::default()); - schema.add_int_field("A".to_string()).unwrap(); - schema.add_string_field("B".to_string(), 9).unwrap(); + let schema = SchemaBuilder::default() + .add_int_field(Element::raw("A")) + .add_string_field(Element::raw("B"), 9) + .build(); - tm.create_table("MyTable", &schema, &tx).unwrap(); + tm.create_table("MyTable", schema, &tx).unwrap(); let layout = tm.get_layout("MyTable", &tx).unwrap(); let mut expected_fields = HashSet::new(); - expected_fields.insert(("A".to_string(), FieldInfo::Integer)); - expected_fields.insert(("B".to_string(), FieldInfo::Varchar(9))); - for (field_name, info) in layout.schema().fields().unwrap() { + expected_fields.insert((Element::raw("A"), FieldInfo::Integer)); + expected_fields.insert((Element::raw("B"), FieldInfo::Varchar(9))); + for (field_name, info) in layout.schema().fields() { assert!(expected_fields.contains(&(field_name, info))); } tx.commit().unwrap(); diff --git a/engine/src/temp.rs b/engine/src/temp.rs index dfda0e4..c9735f5 100644 --- a/engine/src/temp.rs +++ b/engine/src/temp.rs @@ -18,15 +18,15 @@ fn next_table_num() -> i32 { pub struct TempTable { tx: Arc, table_name: String, - layout: Arc, + layout: Layout, } impl TempTable { - pub fn new(tx: &Arc, schema: &Arc) -> DbResult { + pub fn new(tx: &Arc, schema: Schema) -> DbResult { Ok(Self { table_name: format!("temp_{}", next_table_num()), tx: Arc::clone(tx), - layout: Arc::new(Layout::new(schema)?), + layout: Layout::new(schema), }) } @@ -34,7 +34,7 @@ impl TempTable { Ok(Rc::new(TableScan::new( &self.tx, &self.table_name, - &self.layout, + self.layout.clone(), )?)) } @@ -42,7 +42,7 @@ impl TempTable { self.table_name.clone() } - pub(crate) fn layout(&self) -> Arc { + pub(crate) fn layout(&self) -> Layout { self.layout.clone() } } diff --git a/engine/src/value.rs b/engine/src/value.rs new file mode 100644 index 0000000..c98ca43 --- /dev/null +++ b/engine/src/value.rs @@ -0,0 +1,79 @@ +use common::{DbResult, error::DbError}; +use file::page::{I32_SIZE, Page}; + +pub const VARCHAR_TYPE: u8 = 1; +pub const INTEGER_TYPE: u8 = 2; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone)] +pub enum Value { + Integer(i32), + Varchar(String), +} + +impl Value { + pub fn varchar(value: &str) -> Self { + Self::Varchar(value.to_string()) + } + + pub fn as_i32(&self) -> DbResult { + match self { + Self::Integer(value) => Ok(*value), + _ => Err(DbError::InvalidFieldType), + } + } + + pub fn as_str(&self) -> DbResult<&str> { + match self { + Self::Varchar(value) => Ok(value), + _ => Err(DbError::InvalidFieldType), + } + } + + pub fn as_string(&self) -> DbResult { + Ok(self.as_str()?.to_string()) + } + + pub fn size(&self) -> usize { + match self { + Self::Integer(_) => I32_SIZE, + Self::Varchar(value) => Page::str_space(value), + } + } +} + +impl std::fmt::Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Integer(value) => write!(f, "{}", value), + Self::Varchar(value) => write!(f, "'{}'", value), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn string_value() { + let value = Value::varchar("test"); + assert_eq!(value.as_string().unwrap(), "test"); + assert!(matches!(value.as_i32(), Err(DbError::InvalidFieldType))); + } + + #[test] + fn int_value() { + let value = Value::Integer(10); + assert_eq!(value.as_i32().unwrap(), 10); + assert!(matches!(value.as_str(), Err(DbError::InvalidFieldType))); + } + + #[test] + fn to_string() { + let value = Value::Integer(10); + assert_eq!(value.to_string(), "10"); + + let value = Value::varchar("1"); + assert_eq!(value.to_string(), "'1'"); + } +} diff --git a/engine/src/view_mgr.rs b/engine/src/view_mgr.rs index 77de05b..faa4133 100644 --- a/engine/src/view_mgr.rs +++ b/engine/src/view_mgr.rs @@ -3,11 +3,9 @@ use std::sync::Arc; use common::DbResult; use transaction::transaction::Transaction; -use crate::{ - scan::{Scan, table::TableScan}, - schema::Schema, - table_mgr::TableMgr, -}; +use crate::scan::table::TableScan; +use crate::table_mgr::TableMgr; +use crate::{element::Element, scan::Scan, schema::SchemaBuilder}; const MAX_NAME: i32 = 16; const MAX_VIEW_DEF: i32 = 100; @@ -16,39 +14,39 @@ const VIEW_NAME: &str = "view"; const VIEW_DEF: &str = "view_def"; const VIEW_TABLE: &str = "sp_view"; +#[derive(Clone)] pub struct ViewMgr { - table_mgr: Arc, + table_mgr: TableMgr, } impl ViewMgr { - pub fn new(is_new: bool, table_mgr: &Arc, tx: &Arc) -> DbResult { + pub fn new(is_new: bool, table_mgr: TableMgr, tx: &Arc) -> DbResult { if is_new { - let schema = Arc::new(Schema::default()); - schema.add_string_field(VIEW_NAME.to_string(), MAX_NAME)?; - schema.add_string_field(VIEW_DEF.to_string(), MAX_VIEW_DEF)?; - table_mgr.create_table(VIEW_TABLE, &schema, tx)?; + let schema = SchemaBuilder::default() + .add_string_field(Element::raw(VIEW_NAME), MAX_NAME) + .add_string_field(Element::raw(VIEW_DEF), MAX_VIEW_DEF) + .build(); + table_mgr.create_table(VIEW_TABLE, schema, tx)?; } - Ok(Self { - table_mgr: Arc::clone(table_mgr), - }) + Ok(Self { table_mgr }) } pub fn create_view(&self, name: &str, def: &str, tx: &Arc) -> DbResult<()> { - let layout = Arc::new(self.table_mgr.get_layout(VIEW_TABLE, tx)?); - let ts = TableScan::new(tx, VIEW_TABLE, &layout)?; + let layout = self.table_mgr.get_layout(VIEW_TABLE, tx)?; + let ts = TableScan::new(tx, VIEW_TABLE, layout)?; ts.insert()?; - ts.set_string(VIEW_NAME, name)?; - ts.set_string(VIEW_DEF, def)?; + ts.set_string(&Element::raw(VIEW_NAME), name)?; + ts.set_string(&Element::raw(VIEW_DEF), def)?; Ok(()) } pub fn get_view_def(&self, name: &str, tx: &Arc) -> DbResult> { let layout = self.table_mgr.get_layout(VIEW_TABLE, tx)?; - let ts = TableScan::new(tx, VIEW_TABLE, &Arc::new(layout))?; + let ts = TableScan::new(tx, VIEW_TABLE, layout)?; let mut result = None; while ts.next()? { - if ts.get_string(VIEW_NAME)? == name { - result = Some(ts.get_string(VIEW_DEF)?); + if ts.get_string(&Element::raw(VIEW_NAME))? == name { + result = Some(ts.get_string(&Element::raw(VIEW_DEF))?); break; } } diff --git a/index/Cargo.toml b/index/Cargo.toml new file mode 100644 index 0000000..1bfbb4c --- /dev/null +++ b/index/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "index" +edition = "2024" +version.workspace = true + +[dependencies] +scan = diff --git a/server/src/bin/cli.rs b/server/src/bin/cli.rs index 6bc43b7..97d6a27 100644 --- a/server/src/bin/cli.rs +++ b/server/src/bin/cli.rs @@ -1,6 +1,7 @@ use clap::Parser; use common::DbResult; use engine::SimpleDB; +use engine::element::Element; use engine::field_info::FieldInfo; use engine::scan::Scan; use engine::schema::Schema; @@ -34,7 +35,7 @@ fn execute(db: &SimpleDB, query: &str) -> DbResult<()> { } fn print_data(schema: &Schema, result: &Rc) -> DbResult<()> { - let fields = schema.fields()?; + let fields = schema.fields(); let mut rows: Vec> = vec![]; while result.next()? { let mut row = Vec::with_capacity(fields.len()); @@ -46,7 +47,7 @@ fn print_data(schema: &Schema, result: &Rc) -> DbResult<()> { } rows.push(row); } - let headers: Vec = fields.into_iter().map(|(f, _)| f).collect(); + let headers: Vec = fields.into_iter().map(|(f, _)| f).collect(); let mut widths: Vec = headers.iter().map(|f| f.len()).collect(); for row in &rows { for (i, w) in widths.iter_mut().enumerate() { diff --git a/transaction/src/concurrency.rs b/transaction/src/concurrency.rs index 20d7fe2..9c38d2d 100644 --- a/transaction/src/concurrency.rs +++ b/transaction/src/concurrency.rs @@ -18,7 +18,7 @@ mod tests { use log::mgr::LogMgr; use tempfile::tempdir; - use crate::{lock_table::LockTable, transaction::Transaction, txnum_generator::TxNumGenerator}; + use crate::{lock_table::LockTable, transaction::Transaction}; #[derive(Hash, PartialEq, Eq)] enum TxResult { @@ -36,23 +36,22 @@ mod tests { let fm = Arc::new(FileMgr::new(dir.path(), 512).unwrap()); let lm = Arc::new(LogMgr::new(&fm, "testlog".to_string()).unwrap()); let bm = Arc::new(BufferMgr::new(&fm, &lm, 8).unwrap()); - let txnum_generator = TxNumGenerator::default(); let lock_table = Arc::new(LockTable::default()); let (tx_tx, tx_rx) = mpsc::channel(); - let tx_a = Transaction::new(&txnum_generator, &fm, &lm, &bm, &lock_table).unwrap(); + let tx_a = Transaction::new(&fm, &lm, &bm, &lock_table).unwrap(); let tx_tx_a = tx_tx.clone(); thread::spawn(move || a(tx_a, tx_tx_a)); thread::sleep(Duration::from_millis(100)); let tx_tx_b = tx_tx.clone(); - let tx_b = Transaction::new(&txnum_generator, &fm, &lm, &bm, &lock_table).unwrap(); + let tx_b = Transaction::new(&fm, &lm, &bm, &lock_table).unwrap(); thread::spawn(move || b(tx_b, tx_tx_b)); thread::sleep(Duration::from_millis(100)); let tx_tx_c = tx_tx.clone(); - let tx_c = Transaction::new(&txnum_generator, &fm, &lm, &bm, &lock_table).unwrap(); + let tx_c = Transaction::new(&fm, &lm, &bm, &lock_table).unwrap(); thread::spawn(move || c(tx_c, tx_tx_c)); thread::sleep(Duration::from_millis(100)); diff --git a/transaction/src/lib.rs b/transaction/src/lib.rs index 0a1db41..a2d20a6 100644 --- a/transaction/src/lib.rs +++ b/transaction/src/lib.rs @@ -4,4 +4,3 @@ pub mod lock_table; pub mod log; pub mod recovery; pub mod transaction; -pub mod txnum_generator; diff --git a/transaction/src/transaction.rs b/transaction/src/transaction.rs index 8172a77..3dc3f83 100644 --- a/transaction/src/transaction.rs +++ b/transaction/src/transaction.rs @@ -2,15 +2,23 @@ use buffer::mgr::BufferMgr; use common::{DbResult, error::DbError}; use file::{block::BlockId, mgr::FileMgr}; use log::mgr::LogMgr; -use std::sync::Arc; +use std::sync::{ + Arc, + atomic::{AtomicI32, Ordering}, +}; use crate::{ buffer_list::BufferList, concurrency::mgr::ConcurrencyMgr, lock_table::LockTable, - recovery::mgr::RecoveryMgr, txnum_generator::TxNumGenerator, + recovery::mgr::RecoveryMgr, }; +static TX_NUM: AtomicI32 = AtomicI32::new(0); const END_OF_FILE: i32 = -1; +fn next_tx_num() -> i32 { + TX_NUM.fetch_add(1, Ordering::SeqCst) +} + pub struct Transaction { concurrency_mgr: ConcurrencyMgr, fm: Arc, @@ -22,14 +30,13 @@ pub struct Transaction { impl Transaction { pub fn new( - txnum_generator: &TxNumGenerator, fm: &Arc, lm: &Arc, bm: &Arc, lock_table: &Arc, ) -> DbResult { + let txnum = next_tx_num(); let concurrency_mgr = ConcurrencyMgr::new(lock_table); - let txnum = txnum_generator.next(); let rm = RecoveryMgr::new(txnum, lm, bm)?; Ok(Self { txnum, @@ -196,23 +203,14 @@ mod tests { use file::mgr::FileMgr; use tempfile::tempdir; - use crate::{lock_table::LockTable, txnum_generator::TxNumGenerator}; - - fn setup( - dir: &std::path::Path, - ) -> ( - Arc, - Arc, - Arc, - TxNumGenerator, - Arc, - ) { + use crate::lock_table::LockTable; + + fn setup(dir: &std::path::Path) -> (Arc, Arc, Arc, Arc) { let fm = Arc::new(FileMgr::new(dir, 512).unwrap()); let lm = Arc::new(LogMgr::new(&fm, "testlog".to_string()).unwrap()); let bm = Arc::new(BufferMgr::new(&fm, &lm, 8).unwrap()); - let txgen = TxNumGenerator::default(); let lock_table = Arc::new(LockTable::default()); - (fm, lm, bm, txgen, lock_table) + (fm, lm, bm, lock_table) } use super::*; @@ -220,19 +218,19 @@ mod tests { #[test] fn recover_undoes_uncommitted() { let dir = tempdir().unwrap(); - let (fm, lm, bm, txgen, lock_table) = setup(dir.path()); + let (fm, lm, bm, lock_table) = setup(dir.path()); // allocate a real block on disk let block = fm.append("testfile").unwrap(); // tx1: write initial value 100, commit (clean baseline) - let tx1 = Transaction::new(&txgen, &fm, &lm, &bm, &lock_table).unwrap(); + let tx1 = Transaction::new(&fm, &lm, &bm, &lock_table).unwrap(); tx1.pin(&block).unwrap(); tx1.set_i32(&block, 0, 100, false).unwrap(); tx1.commit().unwrap(); // tx2: overwrite with 999 (logged), then flush to disk — simulates a crash - let tx2 = Transaction::new(&txgen, &fm, &lm, &bm, &lock_table).unwrap(); + let tx2 = Transaction::new(&fm, &lm, &bm, &lock_table).unwrap(); tx2.pin(&block).unwrap(); tx2.set_i32(&block, 0, 999, true).unwrap(); bm.flush_all(tx2.txnum()).unwrap(); @@ -243,12 +241,12 @@ mod tests { let lock_table = Arc::new(LockTable::default()); // tx3: recovery should undo tx2's write and restore 100 - let tx3 = Transaction::new(&txgen, &fm, &lm, &bm, &lock_table).unwrap(); + let tx3 = Transaction::new(&fm, &lm, &bm, &lock_table).unwrap(); tx3.recover().unwrap(); tx3.commit().unwrap(); // tx4: verify the value was restored - let tx4 = Transaction::new(&txgen, &fm, &lm, &bm, &lock_table).unwrap(); + let tx4 = Transaction::new(&fm, &lm, &bm, &lock_table).unwrap(); tx4.pin(&block).unwrap(); let val = tx4.get_i32(&block, 0).unwrap(); tx4.commit().unwrap(); @@ -259,22 +257,22 @@ mod tests { #[test] fn recover_preserves_committed() { let dir = tempdir().unwrap(); - let (fm, lm, bm, txgen, lock_table) = setup(dir.path()); + let (fm, lm, bm, lock_table) = setup(dir.path()); let block = fm.append("testfile").unwrap(); // tx1: write 42, commit - let tx1 = Transaction::new(&txgen, &fm, &lm, &bm, &lock_table).unwrap(); + let tx1 = Transaction::new(&fm, &lm, &bm, &lock_table).unwrap(); tx1.pin(&block).unwrap(); tx1.set_i32(&block, 0, 42, true).unwrap(); tx1.commit().unwrap(); // tx2: recovery — tx1 was committed, so nothing should be undone - let tx2 = Transaction::new(&txgen, &fm, &lm, &bm, &lock_table).unwrap(); + let tx2 = Transaction::new(&fm, &lm, &bm, &lock_table).unwrap(); tx2.pin(&block).unwrap(); tx2.recover().unwrap(); - let tx3 = Transaction::new(&txgen, &fm, &lm, &bm, &lock_table).unwrap(); + let tx3 = Transaction::new(&fm, &lm, &bm, &lock_table).unwrap(); tx3.pin(&block).unwrap(); let val = tx3.get_i32(&block, 0).unwrap(); tx3.commit().unwrap(); diff --git a/transaction/src/txnum_generator.rs b/transaction/src/txnum_generator.rs deleted file mode 100644 index a16b731..0000000 --- a/transaction/src/txnum_generator.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::sync::atomic::AtomicI32; - -pub struct TxNumGenerator(AtomicI32); - -impl TxNumGenerator { - pub(crate) fn next(&self) -> i32 { - self.0.fetch_add(1, std::sync::atomic::Ordering::SeqCst) - } -} - -impl Default for TxNumGenerator { - fn default() -> Self { - Self(AtomicI32::new(0)) - } -}