From dddd1c6a08ef0eb97f6de307a2177066aec2bc57 Mon Sep 17 00:00:00 2001 From: Daniil Buravlev Date: Fri, 26 Jun 2026 20:22:34 +0300 Subject: [PATCH 01/13] Add Element enum and wire it through parser, planner, project, and commands; add '.' as delimiter and stub QueryResult. --- engine/src/element.rs | 28 ++++++++++++++++++++++++++ engine/src/lib.rs | 1 + engine/src/plan/project.rs | 24 +++++++++++++++------- engine/src/query.rs | 1 + engine/src/query/command.rs | 12 +++-------- engine/src/query/heuristic_planner.rs | 8 ++++++-- engine/src/query/parser.rs | 29 +++++++++++++++++++-------- engine/src/query/result.rs | 9 +++++++++ engine/src/query/token.rs | 2 +- 9 files changed, 87 insertions(+), 27 deletions(-) create mode 100644 engine/src/element.rs create mode 100644 engine/src/query/result.rs diff --git a/engine/src/element.rs b/engine/src/element.rs new file mode 100644 index 0000000..66a104b --- /dev/null +++ b/engine/src/element.rs @@ -0,0 +1,28 @@ +use common::{DbResult, error::DbError}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) enum Element { + Raw(String), + Id { source: String, name: String }, + Specifier { source: String, spec: String }, +} + +impl Element { + pub(crate) fn source(&self) -> DbResult<&str> { + match self { + Self::Raw(value) => Ok(value), + Self::Specifier { source, .. } => Ok(source), + e => Err(DbError::UnexpectedToken(e.to_string())), + } + } +} + +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::Id { source, name } => write!(f, "{} {}", source, name), + Self::Specifier { source, spec } => write!(f, "{}.{}", source, spec), + } + } +} diff --git a/engine/src/lib.rs b/engine/src/lib.rs index bf3a99e..a6aaad7 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -19,6 +19,7 @@ use crate::{ pub mod buffer_needs; pub mod constant; +pub(crate) mod element; pub mod field_info; pub mod index; pub mod index_mgr; diff --git a/engine/src/plan/project.rs b/engine/src/plan/project.rs index 1da4e91..1a63954 100644 --- a/engine/src/plan/project.rs +++ b/engine/src/plan/project.rs @@ -1,8 +1,13 @@ -use std::{collections::HashSet, rc::Rc, sync::Arc}; +use std::{ + collections::{HashMap, HashSet}, + rc::Rc, + sync::Arc, +}; use common::DbResult; use crate::{ + element::Element, plan::Plan, scan::{Scan, project::ProjectScan}, schema::Schema, @@ -11,16 +16,21 @@ use crate::{ pub struct ProjectPlan { plan: Rc, schema: Arc, + mapping: HashMap, } impl ProjectPlan { - pub fn new(plan: Rc, fields: Vec) -> DbResult { + pub fn new(plan: Rc, fields: Vec, tables: Vec) -> DbResult { let schema = Arc::new(Schema::default()); - for field in fields { - let other = plan.schema()?; - schema.add(field, &other)?; - } - Ok(Self { plan, schema }) + //for field in fields { + // let other = plan.schema()?; + // schema.add(field, &other)?; + // } + Ok(Self { + plan, + schema, + mapping: HashMap::new(), + }) } } diff --git a/engine/src/query.rs b/engine/src/query.rs index a0a4a89..12696aa 100644 --- a/engine/src/query.rs +++ b/engine/src/query.rs @@ -4,6 +4,7 @@ pub(crate) mod heuristic_planner; pub mod lexer; pub mod parser; pub mod planner; +pub(crate) mod result; pub(crate) mod table_planner; pub mod token; pub mod tokenizer; diff --git a/engine/src/query/command.rs b/engine/src/query/command.rs index a217da7..755954c 100644 --- a/engine/src/query/command.rs +++ b/engine/src/query/command.rs @@ -1,5 +1,6 @@ use crate::{ constant::Constant, + element::Element, predicate::{Expression, Predicate}, schema::Schema, sort_by::SortByData, @@ -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, diff --git a/engine/src/query/heuristic_planner.rs b/engine/src/query/heuristic_planner.rs index 91954e8..d5d4414 100644 --- a/engine/src/query/heuristic_planner.rs +++ b/engine/src/query/heuristic_planner.rs @@ -26,7 +26,7 @@ impl HeuristicQueryPlannerInner { 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.source()?, data.predicate.clone(), tx, &self.md)?; self.table_planners.push(tp); } let mut current = self.get_lowest_select_plan()?; @@ -48,7 +48,11 @@ impl HeuristicQueryPlannerInner { if !data.sort_by.is_empty() { current = Rc::new(SortPlan::new(tx, ¤t, data.sort_by.fields)?); } - Ok(Rc::new(ProjectPlan::new(current, data.fields)?)) + Ok(Rc::new(ProjectPlan::new( + current, + data.fields, + data.tables, + )?)) } fn get_lowest_select_plan(&mut self) -> DbResult> { diff --git a/engine/src/query/parser.rs b/engine/src/query/parser.rs index 0ae1858..db2ea71 100644 --- a/engine/src/query/parser.rs +++ b/engine/src/query/parser.rs @@ -2,6 +2,7 @@ use common::{DbResult, error::DbError}; use crate::{ constant::Constant, + element::Element, predicate::{Expression, Predicate, Term}, query::{ command::{ @@ -26,6 +27,20 @@ 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::Specifier { source: id, spec }) + } else if self.lexer.match_id() { + let name = self.lexer.eat_id()?; + Ok(Element::Id { source: id, name }) + } else { + Ok(Element::Raw(id)) + } + } + pub(crate) fn field(&self) -> DbResult { self.lexer.eat_id() } @@ -94,22 +109,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) } diff --git a/engine/src/query/result.rs b/engine/src/query/result.rs new file mode 100644 index 0000000..9a416e5 --- /dev/null +++ b/engine/src/query/result.rs @@ -0,0 +1,9 @@ +use std::rc::Rc; + +use crate::scan::Scan; + +pub struct QueryResult { + scan: Rc, +} + +impl QueryResult {} diff --git a/engine/src/query/token.rs b/engine/src/query/token.rs index be6bb9a..b37131a 100644 --- a/engine/src/query/token.rs +++ b/engine/src/query/token.rs @@ -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 { From b79311fab4d22a0c29659ca85a8b0b41a9ff3f31 Mon Sep 17 00:00:00 2001 From: Daniil Buravlev Date: Sat, 27 Jun 2026 21:39:03 +0300 Subject: [PATCH 02/13] Add scan crate implementing table scanning, schema, layout, record pages, RID, values, and unit tests. --- scan/Cargo.toml | 15 +++ scan/src/element.rs | 47 ++++++++++ scan/src/field_info.rs | 74 +++++++++++++++ scan/src/layout.rs | 92 +++++++++++++++++++ scan/src/lib.rs | 170 ++++++++++++++++++++++++++++++++++ scan/src/record_page.rs | 199 ++++++++++++++++++++++++++++++++++++++++ scan/src/rid.rs | 33 +++++++ scan/src/scanner.rs | 103 +++++++++++++++++++++ scan/src/schema.rs | 142 ++++++++++++++++++++++++++++ scan/src/table.rs | 193 ++++++++++++++++++++++++++++++++++++++ scan/src/value.rs | 68 ++++++++++++++ 11 files changed, 1136 insertions(+) create mode 100644 scan/Cargo.toml create mode 100644 scan/src/element.rs create mode 100644 scan/src/field_info.rs create mode 100644 scan/src/layout.rs create mode 100644 scan/src/lib.rs create mode 100644 scan/src/record_page.rs create mode 100644 scan/src/rid.rs create mode 100644 scan/src/scanner.rs create mode 100644 scan/src/schema.rs create mode 100644 scan/src/table.rs create mode 100644 scan/src/value.rs diff --git a/scan/Cargo.toml b/scan/Cargo.toml new file mode 100644 index 0000000..2dcac40 --- /dev/null +++ b/scan/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "scan" +edition = "2024" +version.workspace = true + +[dependencies] +buffer = { path = "../buffer" } +common = { path = "../common" } +file = { path = "../file" } +log = { path = "../log" } +transaction = { path = "../transaction/" } + +[dev-dependencies] +rand = { workspace = true } +tempfile = { workspace = true } diff --git a/scan/src/element.rs b/scan/src/element.rs new file mode 100644 index 0000000..789e730 --- /dev/null +++ b/scan/src/element.rs @@ -0,0 +1,47 @@ +#[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()) + } +} + +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/scan/src/field_info.rs b/scan/src/field_info.rs new file mode 100644 index 0000000..8a4c282 --- /dev/null +++ b/scan/src/field_info.rs @@ -0,0 +1,74 @@ +use common::{DbResult, error::DbError}; +use file::page::I32_SIZE; + +const INTEGER_TYPE: i32 = 1; +const VARCHAR_TYPE: i32 = 2; + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum FieldInfo { + Integer, + Varchar(i32), +} + +impl FieldInfo { + pub fn new(field_type: i32, len: i32) -> DbResult { + let info = match field_type { + INTEGER_TYPE => Self::Integer, + VARCHAR_TYPE => Self::Varchar((len - I32_SIZE as i32) / 4), + _ => return Err(DbError::UnknownType), + }; + Ok(info) + } + + pub fn type_id(&self) -> i32 { + match self { + Self::Integer => INTEGER_TYPE, + Self::Varchar(_) => VARCHAR_TYPE, + } + } + pub fn length(&self) -> i32 { + match self { + Self::Integer => I32_SIZE as i32, + Self::Varchar(len) => I32_SIZE as i32 + *len * 4, + } + } +} + +impl std::fmt::Display for FieldInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Integer => write!(f, "INTEGER"), + Self::Varchar(len) => write!(f, "VARCHAR({})", len), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn type_id() { + let info = FieldInfo::new(1, 16).unwrap(); + assert_eq!(info.type_id(), INTEGER_TYPE); + + let info = FieldInfo::new(2, 0).unwrap(); + assert_eq!(info.type_id(), VARCHAR_TYPE); + } + + #[test] + #[should_panic] + fn invalid_type() { + FieldInfo::new(123, 32).unwrap(); + } + + #[test] + fn to_string() { + let info = FieldInfo::new(1, 0).unwrap(); + assert_eq!("INTEGER", info.to_string()); + + let info = FieldInfo::new(2, 12).unwrap(); + assert_eq!("VARCHAR(2)", info.to_string()); + } +} + diff --git a/scan/src/layout.rs b/scan/src/layout.rs new file mode 100644 index 0000000..28cfd7f --- /dev/null +++ b/scan/src/layout.rs @@ -0,0 +1,92 @@ +use std::{collections::HashMap, rc::Rc}; + +use file::page::I32_SIZE; + +use crate::{element::Element, schema::Schema}; + +#[derive(Debug, Clone)] +struct LayoutInner { + schema: Schema, + offsets: HashMap, + slotsize: i32, +} + +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() { + offsets.insert(field, pos); + pos += info.length(); + } + Self { + schema, + offsets, + slotsize: pos, + } + } + + fn from(schema: Schema, offsets: HashMap, slotsize: i32) -> Self { + Self { + schema, + offsets, + slotsize, + } + } + + fn offset(&self, fieldname: &Element) -> i32 { + if let Some(offset) = self.offsets.get(fieldname) { + *offset + } else { + 0 + } + } + + fn schema(&self) -> &Schema { + &self.schema + } + + 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/scan/src/lib.rs b/scan/src/lib.rs new file mode 100644 index 0000000..e48645b --- /dev/null +++ b/scan/src/lib.rs @@ -0,0 +1,170 @@ +use std::sync::Arc; + +use common::DbResult; +use transaction::transaction::Transaction; + +use crate::{ + element::Element, layout::Layout, rid::RID, scanner::Scanner, schema::Schema, table::TableScan, + value::Value, +}; + +pub mod element; +pub mod field_info; +pub mod layout; +pub mod record_page; +pub mod rid; +pub(crate) mod scanner; +pub mod schema; +pub(crate) mod table; +pub mod value; + +pub struct Scan { + scan: Scanner, +} + +impl Scan { + pub fn table(tx: &Arc, table_name: &str, layout: Layout) -> DbResult { + let table = TableScan::new(tx, table_name, layout)?; + Ok(Self { + scan: Scanner::Table(table), + }) + } + + pub fn before_first(&mut self) -> DbResult<()> { + self.scan.before_first() + } + + pub fn next_row(&mut self) -> DbResult { + self.scan.next() + } + + pub fn get_i32(&self, field: &Element) -> DbResult { + self.scan.get_i32(field) + } + + pub fn get_string(&self, field: &Element) -> DbResult { + self.scan.get_string(field) + } + + pub fn get_val(&self, field: &Element) -> DbResult { + self.scan.get_val(field) + } + + pub fn has_field(&self, field: &Element) -> DbResult { + self.scan.has_field(field) + } + + pub fn close(&self) -> DbResult<()> { + self.scan.close() + } + + pub fn schema(&self) -> DbResult { + self.scan.schema() + } + + pub fn set_i32(&self, field: &Element, value: i32) -> DbResult<()> { + self.scan.set_i32(field, value) + } + + pub fn set_string(&self, field: &Element, value: &str) -> DbResult<()> { + self.scan.set_string(field, value) + } + + pub fn set_val(&self, field: &Element, value: Value) -> DbResult<()> { + self.scan.set_val(field, value) + } + + pub fn insert(&mut self) -> DbResult<()> { + self.scan.insert() + } + + pub fn delete(&self) -> DbResult<()> { + self.scan.delete() + } + + pub fn get_rid(&self) -> DbResult { + self.scan.get_rid() + } + + pub fn move_to_rid(&mut self, rid: RID) -> DbResult<()> { + self.scan.move_to_rid(rid) + } + + pub fn save_position(&self) -> DbResult<()> { + self.scan.save_position() + } +} + +#[cfg(test)] +mod tests { + use buffer::mgr::BufferMgr; + use file::mgr::FileMgr; + use log::mgr::LogMgr; + use tempfile::{TempDir, tempdir}; + use transaction::lock_table::LockTable; + + use crate::schema::SchemaBuilder; + + use super::*; + + #[test] + fn scan_table() { + let (_dir, tx) = init(); + let schema = SchemaBuilder::default() + .add_int_field(Element::raw("id")) + .add_string_field(Element::raw("name"), 16) + .build(); + let layout = Layout::new(schema); + let mut scan = Scan::table(&tx, "test", layout).unwrap(); + + scan.before_first().unwrap(); + assert!(!scan.next_row().unwrap()); + + scan.insert().unwrap(); + scan.set_i32(&Element::raw("id"), 1).unwrap(); + scan.set_string(&Element::raw("name"), "hello").unwrap(); + + scan.before_first().unwrap(); + assert!(scan.next_row().unwrap()); + + assert_eq!(1, scan.get_i32(&Element::raw("id")).unwrap()); + assert_eq!("hello", scan.get_string(&Element::raw("name")).unwrap()); + + scan.set_val(&Element::raw("id"), Value::Integer(10)) + .unwrap(); + assert_eq!( + Value::Integer(10), + scan.get_val(&Element::raw("id")).unwrap() + ); + + assert!(!scan.has_field(&Element::raw("f")).unwrap()); + scan.schema().unwrap(); + + let rid = scan.get_rid().unwrap(); + assert_eq!(rid.slot(), 0); + assert_eq!(rid.block_num(), 0); + + scan.move_to_rid(RID::new(0, 0)).unwrap(); + scan.before_first().unwrap(); + scan.next_row().unwrap(); + scan.delete().unwrap(); + if let Err(e) = scan.save_position() { + println!("{}", e); + } else { + panic!("save position for table scan not implemented"); + } + + scan.close().unwrap(); + } + + pub(crate) fn init() -> (TempDir, Arc) { + let dir = tempdir().unwrap(); + 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 lock_table = Arc::new(LockTable::default()); + + let tx = Arc::new(Transaction::new(&fm, &lm, &bm, &lock_table).unwrap()); + (dir, tx) + } +} diff --git a/scan/src/record_page.rs b/scan/src/record_page.rs new file mode 100644 index 0000000..2b02d70 --- /dev/null +++ b/scan/src/record_page.rs @@ -0,0 +1,199 @@ +use std::sync::Arc; + +use common::DbResult; +use file::block::BlockId; +use transaction::transaction::Transaction; + +use crate::{element::Element, field_info::FieldInfo, layout::Layout}; + +const EMPTY: u8 = 0; +const USED: u8 = 1; + +#[derive(Clone)] +pub struct RecordPage { + tx: Arc, + block: BlockId, + layout: Layout, +} + +impl RecordPage { + pub fn new(tx: &Arc, block: BlockId, layout: Layout) -> DbResult { + tx.pin(&block)?; + Ok(Self { + tx: Arc::clone(tx), + block, + layout, + }) + } + + 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, 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: &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: &Element, value: &str) -> DbResult<()> { + let pos = self.offset(slot) + self.layout.offset(field); + self.tx.set_string(&self.block, pos as usize, value, true) + } + + pub fn delete(&self, slot: i32) -> DbResult<()> { + self.set_flag(slot, EMPTY) + } + + pub fn format(&self) -> DbResult<()> { + let mut slot = 0; + while self.is_valid_slot(slot) { + self.tx + .set_u8(&self.block, self.offset(slot) as usize, EMPTY, false)?; + let schema = self.layout.schema(); + 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)?, + FieldInfo::Varchar(_) => { + self.tx.set_string(&self.block, pos as usize, "", false)? + } + } + } + slot += 1; + } + Ok(()) + } + + pub fn next_after(&self, slot: i32) -> DbResult { + self.search_after(slot, USED) + } + + pub fn insert_after(&self, slot: i32) -> DbResult { + let newslot = self.search_after(slot, EMPTY)?; + if newslot >= 0 { + self.set_flag(newslot, USED)?; + } + Ok(newslot) + } + + pub fn block(&self) -> BlockId { + self.block.clone() + } + + fn set_flag(&self, slot: i32, flag: u8) -> DbResult<()> { + let offset = self.offset(slot) as usize; + self.tx.set_u8(&self.block, offset, flag, true)?; + Ok(()) + } + + fn search_after(&self, mut slot: i32, flag: u8) -> DbResult { + slot += 1; + while self.is_valid_slot(slot) { + if self.tx.get_u8(&self.block, self.offset(slot) as usize)? == flag { + return Ok(slot); + } + slot += 1; + } + Ok(-1) + } + + fn is_valid_slot(&self, slot: i32) -> bool { + let offset = self.offset(slot + 1); + let block_size = self.tx.block_size(); + offset <= block_size + } + + fn offset(&self, slot: i32) -> i32 { + slot * self.layout.slotsize() + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use buffer::mgr::BufferMgr; + use file::mgr::FileMgr; + use log::mgr::LogMgr; + use rand::RngExt; + use tempfile::tempdir; + use transaction::lock_table::LockTable; + + use crate::schema::SchemaBuilder; + + use super::*; + + #[test] + fn record() { + let dir = tempdir().unwrap(); + 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 lock_table = Arc::new(LockTable::default()); + + let tx = Transaction::new(&fm, &lm, &bm, &lock_table).unwrap(); + let tx = Arc::new(tx); + + 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(); + record_page.format().unwrap(); + + let mut rng = rand::rng(); + + let mut values_less = HashSet::new(); + let mut values_greater = HashSet::new(); + + let mut slot = record_page.insert_after(-1).unwrap(); + while slot >= 0 { + let n = rng.random::(); + if n < 25 { + values_less.insert((n, format!("rec{}", n))); + } else { + values_greater.insert((n, format!("rec{}", n))); + } + record_page.set_i32(slot, &a, n).unwrap(); + record_page + .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(); + if a < 25 { + assert!(values_less.contains(&(a, b))); + record_page.delete(slot).unwrap(); + } + slot = record_page.next_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(); + assert!(values_greater.contains(&(a, b))); + slot = record_page.next_after(slot).unwrap(); + } + tx.unpin(&block).unwrap(); + tx.commit().unwrap(); + } +} diff --git a/scan/src/rid.rs b/scan/src/rid.rs new file mode 100644 index 0000000..a71db62 --- /dev/null +++ b/scan/src/rid.rs @@ -0,0 +1,33 @@ +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct RID(i32, i32); + +impl RID { + pub fn new(block_num: i32, slot: i32) -> Self { + Self(block_num, slot) + } + + pub fn block_num(&self) -> i32 { + self.0 + } + + pub fn slot(&self) -> i32 { + self.1 + } +} + +impl std::fmt::Display for RID { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[{}, {}]", self.0, self.1) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn to_string() { + let rid = RID::new(0, 0); + assert_eq!("[0, 0]", rid.to_string()); + } +} diff --git a/scan/src/scanner.rs b/scan/src/scanner.rs new file mode 100644 index 0000000..2be8ff0 --- /dev/null +++ b/scan/src/scanner.rs @@ -0,0 +1,103 @@ +use common::{DbResult, error::DbError}; + +use crate::{element::Element, rid::RID, schema::Schema, table::TableScan, value::Value}; + +pub(crate) enum Scanner { + Table(TableScan), +} + +impl Scanner { + pub(crate) fn before_first(&mut self) -> DbResult<()> { + match self { + Self::Table(table) => table.before_first(), + } + } + + pub fn next(&mut self) -> DbResult { + match self { + Self::Table(table) => table.next(), + } + } + + pub fn get_i32(&self, field: &Element) -> DbResult { + match self { + Self::Table(table) => table.get_i32(field), + } + } + + pub fn get_string(&self, field: &Element) -> DbResult { + match self { + Self::Table(table) => table.get_string(field), + } + } + + pub fn get_val(&self, field: &Element) -> DbResult { + match self { + Self::Table(table) => table.get_val(field), + } + } + + pub fn has_field(&self, field: &Element) -> DbResult { + match self { + Self::Table(table) => Ok(table.has_field(field)), + } + } + + pub fn close(&self) -> DbResult<()> { + match self { + Self::Table(table) => table.close(), + } + } + + pub fn schema(&self) -> DbResult { + match self { + Self::Table(table) => Ok(table.schema().clone()), + } + } + + pub fn set_i32(&self, field: &Element, value: i32) -> DbResult<()> { + match self { + Self::Table(table) => table.set_i32(field, value), + } + } + + pub fn set_string(&self, field: &Element, value: &str) -> DbResult<()> { + match self { + Self::Table(table) => table.set_string(field, value), + } + } + + pub fn set_val(&self, field: &Element, value: Value) -> DbResult<()> { + match self { + Self::Table(table) => table.set_val(field, value), + } + } + + pub fn insert(&mut self) -> DbResult<()> { + match self { + Self::Table(table) => table.insert(), + } + } + + pub fn delete(&self) -> DbResult<()> { + match self { + Self::Table(table) => table.delete(), + } + } + + pub fn get_rid(&self) -> DbResult { + match self { + Self::Table(table) => Ok(table.get_rid()), + } + } + + pub fn move_to_rid(&mut self, rid: RID) -> DbResult<()> { + match self { + Self::Table(table) => table.move_to_rid(rid), + } + } + + pub fn save_position(&self) -> DbResult<()> { + Err(DbError::other("cannot save position")) + } +} diff --git a/scan/src/schema.rs b/scan/src/schema.rs new file mode 100644 index 0000000..42e1a46 --- /dev/null +++ b/scan/src/schema.rs @@ -0,0 +1,142 @@ +use std::{collections::HashMap, fmt::Debug, rc::Rc}; + +use crate::{element::Element, field_info::FieldInfo}; + +#[derive(Debug)] +pub struct SchemaInner { + fields: Vec, + infos: HashMap, +} + +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_int_field(&mut self, fieldname: Element) { + self.add_field(fieldname, FieldInfo::Integer); + } + + fn add_string_field(&mut self, fieldname: Element, length: i32) { + self.add_field(fieldname, FieldInfo::Varchar(length)); + } + + fn add(&mut self, fieldname: Element, schema: &Self) { + if let Some(info) = schema.info(&fieldname) { + self.add_field(fieldname, info); + } + } + + fn add_all(&mut self, schema: &Self) { + for (field, info) in &schema.infos { + self.add_field(field.clone(), info.clone()); + } + } + + fn info(&self, fieldname: &Element) -> Option { + self.infos.get(fieldname).cloned() + } + + 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 + } + + fn has_field(&self, field: &Element) -> bool { + self.infos.contains_key(field) + } +} + +#[derive(Debug, Clone)] +pub struct Schema(Rc); + +impl Schema { + pub fn info(&self, fieldname: &Element) -> Option { + self.0.info(fieldname) + } + + pub fn fields(&self) -> Vec<(Element, FieldInfo)> { + self.0.fields() + } + + pub fn has_field(&self, field: &Element) -> bool { + self.0.has_field(field) + } +} + +pub struct SchemaBuilder { + schema: SchemaInner, +} + +impl SchemaBuilder { + pub fn add_field(mut self, field: Element, info: FieldInfo) -> Self { + self.schema.add_field(field, info); + self + } + + pub fn add_int_field(mut self, field: Element) -> Self { + self.schema.add_int_field(field); + self + } + + pub fn add_string_field(mut self, field: Element, len: i32) -> Self { + self.schema.add_string_field(field, len); + self + } + + pub fn add(mut self, field: Element, schema: &Schema) -> Self { + self.schema.add(field, &schema.0); + self + } + + 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 SchemaBuilder { + fn default() -> Self { + Self { + 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/scan/src/table.rs b/scan/src/table.rs new file mode 100644 index 0000000..3d1e49c --- /dev/null +++ b/scan/src/table.rs @@ -0,0 +1,193 @@ +use std::sync::Arc; + +use common::{DbResult, error::DbError}; +use file::block::BlockId; +use transaction::transaction::Transaction; + +use crate::element::Element; +use crate::schema::Schema; +use crate::value::Value; +use crate::{field_info::FieldInfo, layout::Layout, record_page::RecordPage, rid::RID}; + +pub(crate) struct TableScan { + tx: Arc, + layout: Layout, + rp: RecordPage, + filename: String, + current_slot: i32, +} + +impl TableScan { + pub(crate) 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.clone())?; + rp.format()?; + rp + } else { + let block = BlockId::new(&filename, 0); + RecordPage::new(tx, block, layout.clone())? + }; + Ok(Self { + tx: Arc::clone(tx), + layout, + rp, + current_slot: -1, + filename, + }) + } + + pub(crate) fn close(&self) -> DbResult<()> { + self.tx.unpin(&self.rp.block()) + } + + pub(crate) fn before_first(&mut self) -> DbResult<()> { + self.move_to_block(0) + } + + pub(crate) fn next(&mut self) -> DbResult { + self.current_slot = self.rp.next_after(self.current_slot)?; + while self.current_slot < 0 { + if self.at_last_block()? { + return Ok(false); + } + self.move_to_block(self.rp.block().num + 1)?; + self.current_slot = self.rp.next_after(self.current_slot)?; + } + Ok(true) + } + + pub(crate) fn get_i32(&self, filename: &Element) -> DbResult { + self.rp.get_i32(self.current_slot, filename) + } + + pub(crate) fn get_string(&self, filename: &Element) -> DbResult { + self.rp.get_string(self.current_slot, filename) + } + + pub(crate) 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(Value::Integer(self.get_i32(fieldname)?)), + FieldInfo::Varchar(_) => Ok(Value::Varchar(self.get_string(fieldname)?)), + } + } + + pub(crate) fn has_field(&self, fieldname: &Element) -> bool { + self.layout.schema().has_field(fieldname) + } + + pub(crate) 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(crate) 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(crate) fn set_val(&self, field: &Element, value: Value) -> DbResult<()> { + match value { + Value::Integer(value) => self.set_i32(field, value), + Value::Varchar(value) => self.set_string(field, &value), + } + } + + pub(crate) fn insert(&mut self) -> DbResult<()> { + let last_block = self.tx.size(&self.filename)? as i32 - 1; + if self.rp.block().num != last_block { + self.move_to_block(last_block)?; + } + self.current_slot = self.rp.insert_after(self.current_slot)?; + while self.current_slot < 0 { + if self.at_last_block()? { + self.move_to_new_block()?; + } else { + self.move_to_block(self.rp.block().num + 1)?; + } + self.current_slot = self.rp.insert_after(self.current_slot)?; + } + Ok(()) + } + + pub(crate) fn delete(&self) -> DbResult<()> { + self.rp.delete(self.current_slot) + } + + pub(crate) 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.clone())?; + self.rp = rp; + self.current_slot = rid.slot(); + Ok(()) + } + + pub(crate) fn get_rid(&self) -> RID { + let rp = &self.rp; + RID::new(rp.block().num, self.current_slot) + } + + 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.clone())?; + self.current_slot = -1; + Ok(()) + } + + 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.clone())?; + self.rp.format()?; + self.current_slot = -1; + Ok(()) + } + + fn at_last_block(&self) -> DbResult { + Ok(self.rp.block().num == self.tx.size(&self.filename)? as i32 - 1) + } + + pub(crate) fn schema(&self) -> &Schema { + self.layout.schema() + } +} + +#[cfg(test)] +mod tests { + use crate::{schema::SchemaBuilder, tests::init}; + + use super::*; + + #[test] + fn reopen() { + let (_dir, tx) = init(); + let layout = Layout::new(SchemaBuilder::default().build()); + let mut scan = TableScan::new(&tx, "test", layout.clone()).unwrap(); + scan.insert().unwrap(); + drop(scan); + + TableScan::new(&tx, "test", layout).unwrap(); + } + + #[test] + fn move_to_new_block() { + let (_dir, tx) = init(); + let schema = SchemaBuilder::default() + .add_string_field(Element::raw("name"), 16) + .build(); + let layout = Layout::new(schema); + let mut scan = TableScan::new(&tx, "test", layout).unwrap(); + scan.before_first().unwrap(); + for _ in 0..10 { + scan.insert().unwrap(); + } + } +} diff --git a/scan/src/value.rs b/scan/src/value.rs new file mode 100644 index 0000000..8132bbf --- /dev/null +++ b/scan/src/value.rs @@ -0,0 +1,68 @@ +use common::{DbResult, error::DbError}; + +#[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()) + } +} + +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'"); + } +} From a1eb232d223cff97c7b2d6b0fe12946d02d3dae8 Mon Sep 17 00:00:00 2001 From: Daniil Buravlev Date: Mon, 29 Jun 2026 14:40:21 +0300 Subject: [PATCH 03/13] Add B-Tree index module (pages, entries, pointers, indexer, Index API), value sizing helpers, and tracing workspace dep. --- scan/Cargo.toml | 1 + scan/src/index.rs | 46 ++++ scan/src/index/b_tree.rs | 299 +++++++++++++++++++++++++ scan/src/index/b_tree/entry.rs | 131 +++++++++++ scan/src/index/b_tree/page.rs | 372 +++++++++++++++++++++++++++++++ scan/src/index/b_tree/pointer.rs | 7 + scan/src/index/indexer.rs | 5 + scan/src/lib.rs | 1 + scan/src/value.rs | 11 + 9 files changed, 873 insertions(+) create mode 100644 scan/src/index.rs create mode 100644 scan/src/index/b_tree.rs create mode 100644 scan/src/index/b_tree/entry.rs create mode 100644 scan/src/index/b_tree/page.rs create mode 100644 scan/src/index/b_tree/pointer.rs create mode 100644 scan/src/index/indexer.rs diff --git a/scan/Cargo.toml b/scan/Cargo.toml index 2dcac40..a0d2df4 100644 --- a/scan/Cargo.toml +++ b/scan/Cargo.toml @@ -9,6 +9,7 @@ common = { path = "../common" } file = { path = "../file" } log = { path = "../log" } transaction = { path = "../transaction/" } +tracing = { workspace = true } [dev-dependencies] rand = { workspace = true } diff --git a/scan/src/index.rs b/scan/src/index.rs new file mode 100644 index 0000000..4e7b0a0 --- /dev/null +++ b/scan/src/index.rs @@ -0,0 +1,46 @@ +use common::DbResult; + +use crate::{index::indexer::Indexer, rid::RID, value::Value}; + +pub(crate) mod b_tree; +mod indexer; + +pub struct Index(Indexer); + +impl Index { + pub fn before_first(&mut self, key: Value) -> DbResult<()> { + match &mut self.0 { + Indexer::BTree(index) => index.before_first(key), + } + } + + pub fn next(&mut self) -> DbResult { + match &mut self.0 { + Indexer::BTree(index) => index.next(), + } + } + + pub fn get_data_rid(&self) -> DbResult { + match &self.0 { + Indexer::BTree(index) => index.get_data_rid(), + } + } + + pub fn insert(&self, value: Value, rid: RID) -> DbResult<()> { + match &self.0 { + Indexer::BTree(index) => index.insert(value, rid), + } + } + + pub fn delete(&self, value: Value, rid: RID) -> DbResult<()> { + match &self.0 { + Indexer::BTree(index) => index.delete(value, rid), + } + } + + pub fn close(&self) -> DbResult<()> { + match &self.0 { + Indexer::BTree(index) => index.close(), + } + } +} diff --git a/scan/src/index/b_tree.rs b/scan/src/index/b_tree.rs new file mode 100644 index 0000000..c55f691 --- /dev/null +++ b/scan/src/index/b_tree.rs @@ -0,0 +1,299 @@ +use std::sync::Arc; + +use common::{DbResult, error::DbError}; +use file::block::BlockId; +use transaction::transaction::Transaction; + +use crate::{ + 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, + }, + rid::RID, + value::Value, +}; + +pub(crate) mod entry; +pub(crate) mod page; +pub(crate) mod pointer; + +pub(crate) struct BTreeIndex { + index_name: String, + tx: Arc, + position: i32, + rid: Vec, +} + +impl BTreeIndex { + pub(crate) 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), + position: 0, + rid: vec![], + }) + } + + pub(crate) 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(()); + } + } + } + } + + pub(crate) fn next(&mut self) -> DbResult { + self.position += 1; + Ok((self.position as usize) < self.rid.len()) + } + + pub(crate) fn get_data_rid(&self) -> DbResult { + Ok(self.rid[self.position as usize].clone()) + } + + pub(crate) 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)?; + page = BTreePage::Metadata { + root: parent_block.num, + }; + } else { + let parent_block = BlockId::new(&self.index_name, parent); + page = BTreePage::read(&self.tx, &parent_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")); + }; + let 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); + 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)); + let parent_block = BlockId::new(&self.index_name, parent); + page = BTreePage::read(&self.tx, &parent_block)?; + } + } + } + } + Ok(()) + } + + pub(crate) fn delete(&self, value: Value, rid: RID) -> DbResult<()> { + todo!(); + } + + pub(crate) fn close(&self) -> DbResult<()> { + todo!() + } + + fn rewrite_parent(&self, values: &[BTreePointer], parent: i32) -> DbResult<()> { + for value in values { + let block = BlockId::new(&self.index_name, value.block_num); + let BTreePage::Node { children, .. } = BTreePage::read(&self.tx, &block)? else { + return Err(DbError::other("unexptected B-Tree index page type")); + }; + let page = BTreePage::Node { parent, children }; + page.write(&block, &self.tx)?; + } + Ok(()) + } +} + +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)?; + tx.commit() +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::tests::init; + + #[test] + fn insert_1000() { + let (_dir, tx) = init(); + let mut index = BTreeIndex::new("test_index", &tx).unwrap(); + for i in 0..1000 { + index.insert(Value::Integer(i), RID::new(i, i)).unwrap(); + index.before_first(Value::Integer(i)).unwrap(); + assert!(index.next().unwrap()); + } + tx.commit().unwrap(); + } +} diff --git a/scan/src/index/b_tree/entry.rs b/scan/src/index/b_tree/entry.rs new file mode 100644 index 0000000..4e1b6ac --- /dev/null +++ b/scan/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/scan/src/index/b_tree/page.rs b/scan/src/index/b_tree/page.rs new file mode 100644 index 0000000..968982c --- /dev/null +++ b/scan/src/index/b_tree/page.rs @@ -0,0 +1,372 @@ +use common::{DbResult, error::DbError}; +use file::page::U32_SIZE; +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 = 0; +const NODE: u8 = 1; +const LEAF: u8 = 2; + +#[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.commit()?; + 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.commit() + } +} + +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 rid_index(values: &[BTreeEntry], value: &Value) -> usize { + values + .binary_search_by(|e| e.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/scan/src/index/b_tree/pointer.rs b/scan/src/index/b_tree/pointer.rs new file mode 100644 index 0000000..c5dea34 --- /dev/null +++ b/scan/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/scan/src/index/indexer.rs b/scan/src/index/indexer.rs new file mode 100644 index 0000000..4ab8f11 --- /dev/null +++ b/scan/src/index/indexer.rs @@ -0,0 +1,5 @@ +use crate::index::b_tree::BTreeIndex; + +pub(crate) enum Indexer { + BTree(BTreeIndex), +} diff --git a/scan/src/lib.rs b/scan/src/lib.rs index e48645b..f30dcf7 100644 --- a/scan/src/lib.rs +++ b/scan/src/lib.rs @@ -10,6 +10,7 @@ use crate::{ pub mod element; pub mod field_info; +pub(crate) mod index; pub mod layout; pub mod record_page; pub mod rid; diff --git a/scan/src/value.rs b/scan/src/value.rs index 8132bbf..c98ca43 100644 --- a/scan/src/value.rs +++ b/scan/src/value.rs @@ -1,4 +1,8 @@ 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 { @@ -28,6 +32,13 @@ impl Value { 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 { From 3067b825a15c5a30184767dc12e16ae9144204d4 Mon Sep 17 00:00:00 2001 From: Daniil Buravlev Date: Mon, 29 Jun 2026 21:31:05 +0300 Subject: [PATCH 04/13] Add Predicate and SelectScan, wire into Scan/Scanner, implement BTree delete and fix B-tree/index logic and tests. --- scan/src/field_info.rs | 2 +- scan/src/index.rs | 16 ++- scan/src/index/b_tree.rs | 117 +++++++++++++--- scan/src/index/b_tree/page.rs | 13 +- scan/src/lib.rs | 21 ++- scan/src/predicate.rs | 245 ++++++++++++++++++++++++++++++++++ scan/src/scanner.rs | 20 ++- scan/src/select.rs | 79 +++++++++++ 8 files changed, 480 insertions(+), 33 deletions(-) create mode 100644 scan/src/predicate.rs create mode 100644 scan/src/select.rs diff --git a/scan/src/field_info.rs b/scan/src/field_info.rs index 8a4c282..d23d7ae 100644 --- a/scan/src/field_info.rs +++ b/scan/src/field_info.rs @@ -26,6 +26,7 @@ impl FieldInfo { Self::Varchar(_) => VARCHAR_TYPE, } } + pub fn length(&self) -> i32 { match self { Self::Integer => I32_SIZE as i32, @@ -71,4 +72,3 @@ mod tests { assert_eq!("VARCHAR(2)", info.to_string()); } } - diff --git a/scan/src/index.rs b/scan/src/index.rs index 4e7b0a0..b17f8c2 100644 --- a/scan/src/index.rs +++ b/scan/src/index.rs @@ -1,6 +1,13 @@ +use std::sync::Arc; + use common::DbResult; +use transaction::transaction::Transaction; -use crate::{index::indexer::Indexer, rid::RID, value::Value}; +use crate::{ + index::{b_tree::BTreeIndex, indexer::Indexer}, + rid::RID, + value::Value, +}; pub(crate) mod b_tree; mod indexer; @@ -8,13 +15,18 @@ mod indexer; pub struct Index(Indexer); impl Index { + pub fn new(index_name: &str, tx: &Arc) -> DbResult { + let index = BTreeIndex::new(index_name, tx)?; + Ok(Self(Indexer::BTree(index))) + } + pub fn before_first(&mut self, key: Value) -> DbResult<()> { match &mut self.0 { Indexer::BTree(index) => index.before_first(key), } } - pub fn next(&mut self) -> DbResult { + pub fn next_row(&mut self) -> DbResult { match &mut self.0 { Indexer::BTree(index) => index.next(), } diff --git a/scan/src/index/b_tree.rs b/scan/src/index/b_tree.rs index c55f691..7a06df8 100644 --- a/scan/src/index/b_tree.rs +++ b/scan/src/index/b_tree.rs @@ -144,12 +144,13 @@ impl BTreeIndex { 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 { - let parent_block = BlockId::new(&self.index_name, parent); - page = BTreePage::read(&self.tx, &parent_block)?; + block = BlockId::new(&self.index_name, parent); + page = BTreePage::read(&self.tx, &block)?; let right = BTreePage::Node { parent, children: right_children.clone(), @@ -163,7 +164,7 @@ impl BTreeIndex { let Some(child_offset) = children.get(idx) else { return Err(DbError::other("empty node's leafs")); }; - let block = BlockId::new(&self.index_name, child_offset.block_num); + block = BlockId::new(&self.index_name, child_offset.block_num); page = BTreePage::read(&self.tx, &block)?; } } @@ -173,7 +174,7 @@ impl BTreeIndex { } => { 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; + let max_size = block_size - (TYPE_SIZE + 3 * POINTER_SIZE); if size > max_size { return Err(DbError::MaxSize(max_size, size)); } @@ -217,6 +218,7 @@ impl BTreeIndex { 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, }; @@ -233,8 +235,8 @@ impl BTreeIndex { left.write(&block, &self.tx)?; right.write(&right_block, &self.tx)?; new_offset = Some((right_key, right_block.num)); - let parent_block = BlockId::new(&self.index_name, parent); - page = BTreePage::read(&self.tx, &parent_block)?; + block = BlockId::new(&self.index_name, parent); + page = BTreePage::read(&self.tx, &block)?; } } } @@ -242,8 +244,33 @@ impl BTreeIndex { Ok(()) } - pub(crate) fn delete(&self, value: Value, rid: RID) -> DbResult<()> { - todo!(); + pub(crate) 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(()) } pub(crate) fn close(&self) -> DbResult<()> { @@ -253,11 +280,17 @@ impl BTreeIndex { fn rewrite_parent(&self, values: &[BTreePointer], parent: i32) -> DbResult<()> { for value in values { let block = BlockId::new(&self.index_name, value.block_num); - let BTreePage::Node { children, .. } = BTreePage::read(&self.tx, &block)? else { - return Err(DbError::other("unexptected B-Tree index page type")); - }; - let page = BTreePage::Node { parent, children }; - page.write(&block, &self.tx)?; + 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(()) } @@ -281,9 +314,8 @@ fn create_index(tx: &Transaction, index_name: &str) -> DbResult<()> { #[cfg(test)] mod tests { - use super::*; - use crate::tests::init; + use crate::tests::{init, init_with_size}; #[test] fn insert_1000() { @@ -291,6 +323,61 @@ mod tests { let mut index = BTreeIndex::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 two_leafs_one_node() { + let (_dir, tx) = init(); + let index_name = "test"; + let index = BTreeIndex::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); + + 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 small_page_test() { + let (_dir, tx) = init_with_size(32); + let mut index = BTreeIndex::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()); } diff --git a/scan/src/index/b_tree/page.rs b/scan/src/index/b_tree/page.rs index 968982c..e78a5bc 100644 --- a/scan/src/index/b_tree/page.rs +++ b/scan/src/index/b_tree/page.rs @@ -1,5 +1,4 @@ use common::{DbResult, error::DbError}; -use file::page::U32_SIZE; use file::{ block::BlockId, page::{I32_SIZE, Page, U8_SIZE}, @@ -16,9 +15,9 @@ 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 = 0; -const NODE: u8 = 1; -const LEAF: u8 = 2; +const METADATA: u8 = 1; +const NODE: u8 = 2; +const LEAF: u8 = 3; #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) enum BTreePage { @@ -230,12 +229,6 @@ pub(crate) fn pointer_index(values: &[BTreePointer], value: &Value) -> usize { .unwrap_or_else(|x| if x == 0 { 0 } else { x - 1 }) } -pub(crate) fn rid_index(values: &[BTreeEntry], value: &Value) -> usize { - values - .binary_search_by(|e| e.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)) diff --git a/scan/src/lib.rs b/scan/src/lib.rs index f30dcf7..fe3d85e 100644 --- a/scan/src/lib.rs +++ b/scan/src/lib.rs @@ -4,18 +4,20 @@ use common::DbResult; use transaction::transaction::Transaction; use crate::{ - element::Element, layout::Layout, rid::RID, scanner::Scanner, schema::Schema, table::TableScan, - value::Value, + element::Element, layout::Layout, predicate::Predicate, rid::RID, scanner::Scanner, + schema::Schema, select::SelectScan, table::TableScan, value::Value, }; pub mod element; pub mod field_info; -pub(crate) mod index; +pub mod index; pub mod layout; +pub mod predicate; pub mod record_page; pub mod rid; pub(crate) mod scanner; pub mod schema; +pub(crate) mod select; pub(crate) mod table; pub mod value; @@ -31,6 +33,13 @@ impl Scan { }) } + pub fn select(scan: Box, predicate: Predicate) -> Self { + let select = SelectScan::new(scan, predicate); + Self { + scan: Scanner::Select(select), + } + } + pub fn before_first(&mut self) -> DbResult<()> { self.scan.before_first() } @@ -159,8 +168,12 @@ mod tests { } 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(), 512).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, 1).unwrap()); let lock_table = Arc::new(LockTable::default()); diff --git a/scan/src/predicate.rs b/scan/src/predicate.rs new file mode 100644 index 0000000..bfde991 --- /dev/null +++ b/scan/src/predicate.rs @@ -0,0 +1,245 @@ +use std::{ + fmt::Debug, + sync::{Arc, RwLock}, +}; + +use common::{DbResult, error::DbError}; + +use crate::{ + Scan, + element::Element, + schema::{Schema, SchemaBuilder}, + value::Value, +}; + +#[derive(Debug, Clone)] +pub enum Expression { + Value(Value), + Field(Element), +} + +impl Expression { + pub fn evaluate(&self, scan: &Scan) -> DbResult { + match self { + Self::Value(value) => Ok(value.clone()), + Self::Field(field) => scan.get_val(field), + } + } + + pub fn applies_to(&self, schema: &Schema) -> bool { + match self { + Self::Value(_) => true, + Self::Field(field) => schema.has_field(field), + } + } + + pub fn as_field(&self) -> Option<&Element> { + match self { + Self::Field(field) => Some(field), + _ => None, + } + } + + pub fn as_constant(&self) -> Option<&Value> { + match self { + Self::Value(value) => Some(value), + _ => None, + } + } +} + +impl std::fmt::Display for Expression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Value(value) => write!(f, "{}", value), + Self::Field(field) => write!(f, "{}", field), + } + } +} + +#[derive(Clone, Debug)] +pub struct Term { + left: Expression, + right: Expression, +} + +impl Term { + pub fn new(left: Expression, right: Expression) -> Self { + Self { left, right } + } + + pub fn is_satisfied(&self, s: &Scan) -> DbResult { + let left = self.left.evaluate(s)?; + let right = self.right.evaluate(s)?; + Ok(left == right) + } + + pub fn applies_to(&self, schema: &Schema) -> bool { + self.left.applies_to(schema) && self.right.applies_to(schema) + } + + pub fn reduction_factor(&self, left_distinct: i32, right_distinct: i32) -> i32 { + if self.left.as_field().is_some() && self.right.as_field().is_some() { + return left_distinct.max(right_distinct); + } + if self.left.as_field().is_some() { + return left_distinct; + } + if self.right.as_field().is_some() { + return right_distinct; + } + if let Some(left) = self.left.as_constant() + && let Some(right) = self.right.as_constant() + && left == right + { + 1 + } else { + i32::MAX + } + } + + pub fn equates_with_constant(&self, field_name: &Element) -> DbResult> { + if let Some(field) = self.left.as_field() + && field == field_name + && let Some(value) = self.right.as_constant() + { + Ok(Some(value.clone())) + } else if let Some(right) = self.right.as_field() + && right == field_name + && let Some(value) = self.left.as_constant() + { + Ok(Some(value.clone())) + } else { + Ok(None) + } + } + + pub fn equates_with_field(&self, field_name: &Element) -> DbResult> { + if let Some(left) = self.left.as_field() + && left == field_name + && let Some(right) = self.right.as_field() + { + Ok(Some(right.to_string())) + } else if let Some(right) = self.right.as_field() + && right == field_name + && let Some(left) = self.left.as_field() + { + Ok(Some(left.to_string())) + } else { + Ok(None) + } + } +} + +impl std::fmt::Display for Term { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}={}", self.left, self.right) + } +} + +#[derive(Default, Clone)] +pub struct Predicate { + terms: Arc>>, +} + +impl Predicate { + pub fn new(term: Term) -> Self { + Self { + terms: Arc::new(RwLock::new(vec![term])), + } + } + + pub fn conjoin_with(&self, p: &Self) -> DbResult<()> { + let mut write = self.terms.write().map_err(DbError::lock)?; + let read = p.terms.read().map_err(DbError::lock)?; + for term in read.iter() { + write.push(term.clone()); + } + Ok(()) + } + + pub fn is_satisfied(&self, s: &Scan) -> DbResult { + let read = self.terms.read().map_err(DbError::lock)?; + for t in read.iter() { + if !t.is_satisfied(s)? { + return Ok(false); + } + } + Ok(true) + } + + pub fn reduction_factor(&self, left_distinct: i32, right_distinct: i32) -> DbResult { + let mut factor = 1; + let read = self.terms.read().map_err(DbError::lock)?; + for t in read.iter() { + factor *= t.reduction_factor(left_distinct, right_distinct); + } + Ok(factor) + } + + pub fn select_sub_pred(&self, schema: &Schema) -> DbResult { + let result = Predicate::default(); + 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(schema) { + terms.push(t.clone()); + } + } + } + Ok(result) + } + + pub fn join_sub_pred(&self, s1: &Schema, s2: &Schema) -> DbResult> { + let result = Predicate::default(); + 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) { + terms.push(t.clone()); + } + } + if terms.is_empty() { + return Ok(None); + } + } + Ok(Some(result)) + } + + 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)? { + return Ok(Some(c)); + } + } + Ok(None) + } + + 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)? { + return Ok(Some(field)); + } + } + Ok(None) + } +} + +impl std::fmt::Display for Predicate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let terms = self.terms.read().map_err(|_| std::fmt::Error)?; + for (i, t) in terms.iter().enumerate() { + if i == 0 { + write!(f, "{}", t)?; + } else { + write!(f, " AND {}", t)?; + } + } + Ok(()) + } +} diff --git a/scan/src/scanner.rs b/scan/src/scanner.rs index 2be8ff0..7a5d423 100644 --- a/scan/src/scanner.rs +++ b/scan/src/scanner.rs @@ -1,99 +1,117 @@ use common::{DbResult, error::DbError}; -use crate::{element::Element, rid::RID, schema::Schema, table::TableScan, value::Value}; +use crate::{ + element::Element, rid::RID, schema::Schema, select::SelectScan, table::TableScan, value::Value, +}; pub(crate) enum Scanner { Table(TableScan), + Select(SelectScan), } impl Scanner { pub(crate) fn before_first(&mut self) -> DbResult<()> { match self { Self::Table(table) => table.before_first(), + Self::Select(select) => select.before_first(), } } pub fn next(&mut self) -> DbResult { match self { Self::Table(table) => table.next(), + Self::Select(select) => select.next(), } } pub fn get_i32(&self, field: &Element) -> DbResult { match self { Self::Table(table) => table.get_i32(field), + Self::Select(select) => select.get_i32(field), } } pub fn get_string(&self, field: &Element) -> DbResult { match self { Self::Table(table) => table.get_string(field), + Self::Select(select) => select.get_string(field), } } pub fn get_val(&self, field: &Element) -> DbResult { match self { Self::Table(table) => table.get_val(field), + Self::Select(select) => select.get_val(field), } } pub fn has_field(&self, field: &Element) -> DbResult { match self { Self::Table(table) => Ok(table.has_field(field)), + Self::Select(select) => select.has_field(field), } } pub fn close(&self) -> DbResult<()> { match self { Self::Table(table) => table.close(), + Self::Select(select) => select.close(), } } pub fn schema(&self) -> DbResult { match self { Self::Table(table) => Ok(table.schema().clone()), + Self::Select(select) => select.schema(), } } pub fn set_i32(&self, field: &Element, value: i32) -> DbResult<()> { match self { Self::Table(table) => table.set_i32(field, value), + Self::Select(select) => select.set_i32(field, value), } } pub fn set_string(&self, field: &Element, value: &str) -> DbResult<()> { match self { Self::Table(table) => table.set_string(field, value), + Self::Select(select) => select.set_string(field, value), } } pub fn set_val(&self, field: &Element, value: Value) -> DbResult<()> { match self { Self::Table(table) => table.set_val(field, value), + Self::Select(select) => select.set_val(field, value), } } pub fn insert(&mut self) -> DbResult<()> { match self { Self::Table(table) => table.insert(), + Self::Select(select) => select.insert(), } } pub fn delete(&self) -> DbResult<()> { match self { Self::Table(table) => table.delete(), + Self::Select(select) => select.delete(), } } pub fn get_rid(&self) -> DbResult { match self { Self::Table(table) => Ok(table.get_rid()), + Self::Select(select) => select.get_rid(), } } pub fn move_to_rid(&mut self, rid: RID) -> DbResult<()> { match self { Self::Table(table) => table.move_to_rid(rid), + Self::Select(table) => table.move_to_rid(rid), } } diff --git a/scan/src/select.rs b/scan/src/select.rs new file mode 100644 index 0000000..b57a409 --- /dev/null +++ b/scan/src/select.rs @@ -0,0 +1,79 @@ +use common::DbResult; + +use crate::{Scan, element::Element, predicate::Predicate, rid::RID, schema::Schema, value::Value}; + +pub(crate) struct SelectScan { + scan: Box, + predicate: Predicate, +} + +impl SelectScan { + pub(crate) fn new(scan: Box, predicate: Predicate) -> Self { + Self { scan, predicate } + } + + pub(crate) fn before_first(&mut self) -> DbResult<()> { + self.scan.before_first() + } + + pub(crate) fn next(&mut self) -> DbResult { + while self.scan.next_row()? { + if self.predicate.is_satisfied(&self.scan)? { + return Ok(true); + } + } + Ok(false) + } + + pub(crate) fn get_i32(&self, field_name: &Element) -> DbResult { + self.scan.get_i32(field_name) + } + + pub(crate) fn get_string(&self, field_name: &Element) -> DbResult { + self.scan.get_string(field_name) + } + + pub(crate) fn get_val(&self, field_name: &Element) -> DbResult { + self.scan.get_val(field_name) + } + + pub(crate) fn has_field(&self, field_name: &Element) -> DbResult { + self.scan.has_field(field_name) + } + + pub(crate) fn close(&self) -> DbResult<()> { + self.scan.close() + } + + pub(crate) fn schema(&self) -> DbResult { + self.scan.schema() + } + + pub(crate) fn set_i32(&self, field_name: &Element, value: i32) -> DbResult<()> { + self.scan.set_i32(field_name, value) + } + + pub(crate) fn set_string(&self, field_name: &Element, value: &str) -> DbResult<()> { + self.scan.set_string(field_name, value) + } + + pub(crate) fn set_val(&self, field_name: &Element, value: Value) -> DbResult<()> { + self.scan.set_val(field_name, value) + } + + pub(crate) fn insert(&mut self) -> DbResult<()> { + self.scan.insert() + } + + pub(crate) fn delete(&self) -> DbResult<()> { + self.scan.delete() + } + + pub(crate) fn get_rid(&self) -> DbResult { + self.scan.get_rid() + } + + pub(crate) fn move_to_rid(&mut self, rid: RID) -> DbResult<()> { + self.scan.move_to_rid(rid) + } +} From 23083f4b96f55a9025bef2620b87e6c0436eb9db Mon Sep 17 00:00:00 2001 From: Daniil Buravlev Date: Tue, 30 Jun 2026 12:26:40 +0300 Subject: [PATCH 05/13] Add planner crate (metadata, table, index, stat, view, plan), move scan into planner, replace TxNumGenerator by atomic TX_NUM. --- Cargo.lock | 14 ++ Cargo.toml | 4 +- common/src/error.rs | 2 + engine/src/lib.rs | 23 +-- engine/src/record_page.rs | 5 +- engine/src/scan/table.rs | 46 +++--- index/Cargo.toml | 7 + {scan => planner}/Cargo.toml | 2 +- {scan => planner}/src/element.rs | 0 {scan => planner}/src/field_info.rs | 0 {scan => planner}/src/index.rs | 0 {scan => planner}/src/index/b_tree.rs | 0 {scan => planner}/src/index/b_tree/entry.rs | 0 {scan => planner}/src/index/b_tree/page.rs | 0 {scan => planner}/src/index/b_tree/pointer.rs | 0 {scan => planner}/src/index/indexer.rs | 0 {scan => planner}/src/layout.rs | 0 planner/src/lib.rs | 38 +++++ planner/src/mgr.rs | 6 + planner/src/mgr/index.rs | 143 +++++++++++++++++ planner/src/mgr/metadata.rs | 97 +++++++++++ planner/src/mgr/stat.rs | 134 ++++++++++++++++ planner/src/mgr/table.rs | 150 ++++++++++++++++++ planner/src/mgr/view.rs | 53 +++++++ planner/src/plan.rs | 68 ++++++++ planner/src/plan/select.rs | 45 ++++++ planner/src/plan/table.rs | 52 ++++++ {scan => planner}/src/predicate.rs | 49 +++--- {scan => planner}/src/record_page.rs | 0 {scan => planner}/src/rid.rs | 0 scan/src/lib.rs => planner/src/scan.rs | 41 +---- {scan/src => planner/src/scan}/scanner.rs | 6 +- {scan/src => planner/src/scan}/select.rs | 4 +- {scan/src => planner/src/scan}/table.rs | 0 {scan => planner}/src/schema.rs | 0 {scan => planner}/src/value.rs | 0 transaction/src/concurrency.rs | 9 +- transaction/src/lib.rs | 1 - transaction/src/transaction.rs | 50 +++--- transaction/src/txnum_generator.rs | 15 -- 40 files changed, 908 insertions(+), 156 deletions(-) create mode 100644 index/Cargo.toml rename {scan => planner}/Cargo.toml (95%) rename {scan => planner}/src/element.rs (100%) rename {scan => planner}/src/field_info.rs (100%) rename {scan => planner}/src/index.rs (100%) rename {scan => planner}/src/index/b_tree.rs (100%) rename {scan => planner}/src/index/b_tree/entry.rs (100%) rename {scan => planner}/src/index/b_tree/page.rs (100%) rename {scan => planner}/src/index/b_tree/pointer.rs (100%) rename {scan => planner}/src/index/indexer.rs (100%) rename {scan => planner}/src/layout.rs (100%) create mode 100644 planner/src/lib.rs create mode 100644 planner/src/mgr.rs create mode 100644 planner/src/mgr/index.rs create mode 100644 planner/src/mgr/metadata.rs create mode 100644 planner/src/mgr/stat.rs create mode 100644 planner/src/mgr/table.rs create mode 100644 planner/src/mgr/view.rs create mode 100644 planner/src/plan.rs create mode 100644 planner/src/plan/select.rs create mode 100644 planner/src/plan/table.rs rename {scan => planner}/src/predicate.rs (83%) rename {scan => planner}/src/record_page.rs (100%) rename {scan => planner}/src/rid.rs (100%) rename scan/src/lib.rs => planner/src/scan.rs (77%) rename {scan/src => planner/src/scan}/scanner.rs (96%) rename {scan/src => planner/src/scan}/select.rs (94%) rename {scan/src => planner/src/scan}/table.rs (100%) rename {scan => planner}/src/schema.rs (100%) rename {scan => planner}/src/value.rs (100%) delete mode 100644 transaction/src/txnum_generator.rs diff --git a/Cargo.lock b/Cargo.lock index 9ed5854..bbbe572 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -579,6 +579,20 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "planner" +version = "0.0.1" +dependencies = [ + "buffer", + "common", + "file", + "log 0.0.1", + "rand", + "tempfile", + "tracing", + "transaction", +] + [[package]] name = "plotters" version = "0.3.7" diff --git a/Cargo.toml b/Cargo.toml index 9595681..6d52a1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,12 +4,14 @@ version = "0.0.1" [workspace] resolver = "2" members = [ - "buffer", "client", + "buffer", + "client", "common", "engine", "file", "log", "network", + "planner", "server", "transaction", ] 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/src/lib.rs b/engine/src/lib.rs index a6aaad7..8db3cbd 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, @@ -43,7 +41,6 @@ const BLOCK_SIZE: usize = 8 * 1024; const NUM_BUFFERS: usize = 1024; pub struct SimpleDB { - tx_num_generator: TxNumGenerator, fm: Arc, lm: Arc, bm: Arc, @@ -57,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"); @@ -79,7 +69,6 @@ impl SimpleDB { let md = Arc::new(MetadataMgr::new(is_new, &tx)?); tx.commit()?; Ok(Self { - tx_num_generator, fm, lm, bm, @@ -89,13 +78,7 @@ 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)) } diff --git a/engine/src/record_page.rs b/engine/src/record_page.rs index 10e1bdc..a5cb769 100644 --- a/engine/src/record_page.rs +++ b/engine/src/record_page.rs @@ -123,7 +123,7 @@ 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; @@ -135,10 +135,9 @@ 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(); diff --git a/engine/src/scan/table.rs b/engine/src/scan/table.rs index d208a8b..cb933b7 100644 --- a/engine/src/scan/table.rs +++ b/engine/src/scan/table.rs @@ -256,9 +256,8 @@ mod tests { 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 transaction::lock_table::LockTable; use crate::schema::Schema; @@ -270,54 +269,49 @@ 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 tx = Arc::new(Transaction::new(&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 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 offset_a = layout.offset("A"); + let offset_b = layout.offset("B"); + assert_ne!(offset_a, offset_b, "fields must occupy distinct offsets"); + // Fill the table with 50 records carrying known A-values 0..50. let ts = TableScan::new(&tx, "T", &layout).unwrap(); - println!("Fillins the table with 50 random records"); 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 - ); } - 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(); 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); + 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/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/scan/Cargo.toml b/planner/Cargo.toml similarity index 95% rename from scan/Cargo.toml rename to planner/Cargo.toml index a0d2df4..5882dd8 100644 --- a/scan/Cargo.toml +++ b/planner/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "scan" +name = "planner" edition = "2024" version.workspace = true diff --git a/scan/src/element.rs b/planner/src/element.rs similarity index 100% rename from scan/src/element.rs rename to planner/src/element.rs diff --git a/scan/src/field_info.rs b/planner/src/field_info.rs similarity index 100% rename from scan/src/field_info.rs rename to planner/src/field_info.rs diff --git a/scan/src/index.rs b/planner/src/index.rs similarity index 100% rename from scan/src/index.rs rename to planner/src/index.rs diff --git a/scan/src/index/b_tree.rs b/planner/src/index/b_tree.rs similarity index 100% rename from scan/src/index/b_tree.rs rename to planner/src/index/b_tree.rs diff --git a/scan/src/index/b_tree/entry.rs b/planner/src/index/b_tree/entry.rs similarity index 100% rename from scan/src/index/b_tree/entry.rs rename to planner/src/index/b_tree/entry.rs diff --git a/scan/src/index/b_tree/page.rs b/planner/src/index/b_tree/page.rs similarity index 100% rename from scan/src/index/b_tree/page.rs rename to planner/src/index/b_tree/page.rs diff --git a/scan/src/index/b_tree/pointer.rs b/planner/src/index/b_tree/pointer.rs similarity index 100% rename from scan/src/index/b_tree/pointer.rs rename to planner/src/index/b_tree/pointer.rs diff --git a/scan/src/index/indexer.rs b/planner/src/index/indexer.rs similarity index 100% rename from scan/src/index/indexer.rs rename to planner/src/index/indexer.rs diff --git a/scan/src/layout.rs b/planner/src/layout.rs similarity index 100% rename from scan/src/layout.rs rename to planner/src/layout.rs diff --git a/planner/src/lib.rs b/planner/src/lib.rs new file mode 100644 index 0000000..da59007 --- /dev/null +++ b/planner/src/lib.rs @@ -0,0 +1,38 @@ +pub mod element; +pub mod field_info; +pub mod index; +pub mod layout; +pub mod mgr; +pub mod plan; +pub mod predicate; +pub mod record_page; +pub mod rid; +pub mod scan; +pub mod schema; +pub mod value; + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use buffer::mgr::BufferMgr; + use file::mgr::FileMgr; + use log::mgr::LogMgr; + use tempfile::{TempDir, tempdir}; + use transaction::{lock_table::LockTable, transaction::Transaction}; + + 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, 1).unwrap()); + let lock_table = Arc::new(LockTable::default()); + + let tx = Arc::new(Transaction::new(&fm, &lm, &bm, &lock_table).unwrap()); + (dir, tx) + } +} diff --git a/planner/src/mgr.rs b/planner/src/mgr.rs new file mode 100644 index 0000000..b8c12e1 --- /dev/null +++ b/planner/src/mgr.rs @@ -0,0 +1,6 @@ +pub mod index; +pub mod metadata; +pub mod stat; +pub mod table; +pub mod view; + diff --git a/planner/src/mgr/index.rs b/planner/src/mgr/index.rs new file mode 100644 index 0000000..ef5327c --- /dev/null +++ b/planner/src/mgr/index.rs @@ -0,0 +1,143 @@ +use std::{collections::HashMap, sync::Arc}; + +use common::DbResult; +use common::error::DbError; +use transaction::transaction::Transaction; + +use crate::element::Element; +use crate::index::Index; +use crate::layout::Layout; +use crate::mgr::stat::{StatInfo, StatMgr}; +use crate::mgr::table::TableMgr; +use crate::scan::Scan; +use crate::schema::{Schema, SchemaBuilder}; + +const IDX_TABLE: &str = "idx"; +const IDX_NAME: &str = "idx_name"; +const TABLE_NAME: &str = "table_name"; +const FIELD_NAME: &str = "field_name"; +const MAX_LENGTH: i32 = 16; + +#[derive(Clone)] +pub struct IndexInfo { + idx_name: String, + field_name: String, + tx: Arc, + slot_size: i32, + stat: StatInfo, +} + +impl IndexInfo { + pub fn new( + idx_name: String, + field_name: String, + schema: &Schema, + tx: &Arc, + stat: StatInfo, + ) -> DbResult { + let Some(info) = schema.info(&Element::raw(&field_name)) else { + return Err(DbError::FieldNotExists(field_name)); + }; + Ok(Self { + idx_name, + slot_size: info.length(), + field_name, + tx: Arc::clone(tx), + stat, + }) + } + + pub fn open(&self) -> DbResult { + Index::new(&self.idx_name, &self.tx) + } + + pub fn block_accessed(&self) -> DbResult { + let rpb = self.tx.block_size() / self.slot_size; + let num_blocks = self.stat.records_output() / rpb; + Ok(num_blocks) + } + + pub fn records_output(&self) -> i32 { + self.stat.records_output() / self.stat.distinct_values() + } + + pub fn distinct_values(&self, field_name: &str) -> i32 { + if self.field_name == field_name { + 1 + } else { + self.stat.distinct_values() + } + } +} + +pub struct IndexMgr { + layout: Layout, + table_mgr: TableMgr, + stat_mgr: StatMgr, +} + +impl IndexMgr { + pub fn new( + is_new: bool, + table_mgr: TableMgr, + stat_mgr: StatMgr, + tx: &Arc, + ) -> DbResult { + if is_new { + 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 = 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, + stat_mgr, + }) + } + + pub fn create_index( + &self, + idx_name: &str, + table_name: &str, + field_name: &str, + tx: &Arc, + ) -> DbResult<()> { + let mut ts = Scan::table(tx, IDX_TABLE, self.layout.clone())?; + ts.insert()?; + 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() + } + + pub fn get_index_info( + &self, + table_name: &str, + tx: &Arc, + ) -> DbResult> { + let mut result = HashMap::new(); + let mut ts = Scan::table(tx, IDX_TABLE, self.layout.clone())?; + while ts.next_row()? { + 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, field_name.clone(), layout.schema(), tx, stat)?; + result.insert(field_name, index); + } + } + ts.close()?; + Ok(result) + } +} diff --git a/planner/src/mgr/metadata.rs b/planner/src/mgr/metadata.rs new file mode 100644 index 0000000..04602b3 --- /dev/null +++ b/planner/src/mgr/metadata.rs @@ -0,0 +1,97 @@ +use std::{collections::HashMap, sync::Arc}; + +use common::DbResult; +use transaction::transaction::Transaction; + +use crate::{ + layout::Layout, + mgr::{ + index::{IndexInfo, IndexMgr}, + stat::{StatInfo, StatMgr}, + table::TableMgr, + view::ViewMgr, + }, + schema::Schema, +}; + +pub struct MetadataMgr { + 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 = 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, + stat_mgr, + index_mgr, + }) + } + + pub fn create_table( + &self, + table_name: &str, + schema: Schema, + tx: &Arc, + ) -> DbResult<()> { + self.table_mgr.create_table(table_name, schema, tx) + } + + pub fn get_layout(&self, table_name: &str, tx: &Arc) -> DbResult { + self.table_mgr.get_layout(table_name, tx) + } + + pub fn create_view( + &self, + view_name: &str, + view_def: &str, + tx: &Arc, + ) -> DbResult<()> { + self.view_mgr.create_view(view_name, view_def, tx) + } + + pub fn get_view_def(&self, view_name: &str, tx: &Arc) -> DbResult> { + self.view_mgr.get_view_def(view_name, tx) + } + + pub fn create_index( + &self, + idx_name: &str, + table_name: &str, + field_name: &str, + tx: &Arc, + ) -> DbResult<()> { + self.index_mgr + .create_index(idx_name, table_name, field_name, tx) + } + + pub fn get_stat_info( + &self, + table: &str, + layout: Layout, + tx: &Arc, + ) -> DbResult { + self.stat_mgr.get_stat_info(table, layout, tx) + } + + pub fn get_index_info( + &self, + table_name: &str, + tx: &Arc, + ) -> DbResult> { + self.index_mgr.get_index_info(table_name, tx) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn metadata() {} +} diff --git a/planner/src/mgr/stat.rs b/planner/src/mgr/stat.rs new file mode 100644 index 0000000..df7989e --- /dev/null +++ b/planner/src/mgr/stat.rs @@ -0,0 +1,134 @@ +use std::{ + collections::HashMap, + rc::Rc, + sync::{Arc, RwLock}, +}; + +use common::{DbResult, error::DbError}; +use transaction::transaction::Transaction; + +use crate::{ + element::Element, + layout::Layout, + mgr::table::{TABLE_NAME, TableMgr}, + scan::Scan, +}; + +#[derive(Clone, Debug)] +pub struct StatInfo { + num_blocks: i32, + num_recs: u16, +} + +impl StatInfo { + pub fn new(num_blocks: i32, num_recs: u16) -> Self { + Self { + num_blocks, + num_recs, + } + } + + pub fn block_accessed(&self) -> i32 { + self.num_blocks + } + + pub fn records_output(&self) -> i32 { + self.num_recs as i32 + } + + pub fn distinct_values(&self) -> i32 { + 1 + (self.num_recs / 3) as i32 + } +} + +struct StatMgrLock { + table_mgr: TableMgr, + table_stats: HashMap, + num_calls: u64, +} + +impl StatMgrLock { + fn new(table_mgr: TableMgr) -> Self { + Self { + table_mgr, + table_stats: HashMap::new(), + num_calls: 0, + } + } + + fn get_stat_info( + &mut self, + table_name: &str, + layout: Layout, + tx: &Arc, + ) -> DbResult { + self.num_calls += 1; + if self.num_calls > 100 { + self.refresh_statisic(tx)?; + } + if let Some(stat) = self.table_stats.get(table_name).cloned() { + Ok(stat) + } else { + let stat = self.calc_table_stats(table_name, layout, tx)?; + self.table_stats + .insert(table_name.to_string(), stat.clone()); + Ok(stat) + } + } + + fn refresh_statisic(&mut self, tx: &Arc) -> DbResult<()> { + self.table_stats.clear(); + self.num_calls = 0; + let layout = self.table_mgr.get_layout(TABLE_NAME, tx)?; + let mut ts = Scan::table(tx, TABLE_NAME, layout.clone())?; + while ts.next_row()? { + 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, layout, tx)?; + self.table_stats.insert(table_name, stat); + } + ts.close() + } + + fn calc_table_stats( + &self, + table_name: &str, + layout: Layout, + tx: &Arc, + ) -> DbResult { + let mut num_recs = 0; + let mut num_blocks = 0; + let mut ts = Scan::table(tx, table_name, layout)?; + while ts.next_row()? { + num_recs += 1; + num_blocks = ts.get_rid()?.block_num() + 1; + } + ts.close()?; + Ok(StatInfo::new(num_blocks, num_recs)) + } +} + +#[derive(Clone)] +pub struct StatMgr { + lock: Rc>, +} + +impl StatMgr { + pub fn new(table_mgr: TableMgr, tx: &Arc) -> DbResult { + let mut lock = StatMgrLock::new(table_mgr); + lock.refresh_statisic(tx)?; + Ok(Self { + lock: Rc::new(RwLock::new(lock)), + }) + } + + pub fn get_stat_info( + &self, + table_name: &str, + layout: Layout, + tx: &Arc, + ) -> DbResult { + let mut write = self.lock.write().map_err(DbError::lock)?; + write.get_stat_info(table_name, layout, tx) + } +} diff --git a/planner/src/mgr/table.rs b/planner/src/mgr/table.rs new file mode 100644 index 0000000..d3c0197 --- /dev/null +++ b/planner/src/mgr/table.rs @@ -0,0 +1,150 @@ +use std::{collections::HashMap, sync::Arc}; + +use common::DbResult; +use transaction::transaction::Transaction; + +use crate::{ + element::Element, + field_info::FieldInfo, + layout::Layout, + scan::Scan, + schema::{Schema, SchemaBuilder}, +}; + +const MAX_NAME: i32 = 16; + +pub(crate) const TABLE_NAME: &str = "sp_table"; +const TABLE_SLOT_SIZE: &str = "slot_size"; +const FIELDS_NAME: &str = "sp_fields"; + +const F_TYPE: &str = "type"; +const F_FIELD_NAME: &str = "field"; +const F_TYPE_LENGTH: &str = "length"; +const F_OFFSET: &str = "offset"; + +#[derive(Clone, Debug)] +pub struct TableMgr { + table_layout: Layout, + fields_layout: Layout, +} + +impl TableMgr { + pub fn new(is_new: bool, tx: &Arc) -> DbResult { + 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_layout, + fields_layout, + }; + + if is_new { + mgr.create_table(TABLE_NAME, table_schema, tx)?; + mgr.create_table(FIELDS_NAME, fields_schema, tx)?; + } + + Ok(mgr) + } + + pub fn create_table( + &self, + table_name: &str, + schema: Schema, + tx: &Arc, + ) -> DbResult<()> { + let layout = Layout::new(schema.clone()); + + let mut tcat = Scan::table(tx, TABLE_NAME, self.table_layout.clone())?; + tcat.insert()?; + tcat.set_string(&Element::raw(TABLE_NAME), table_name)?; + tcat.set_i32(&Element::raw(TABLE_SLOT_SIZE), layout.slotsize())?; + tcat.close()?; + + let mut fcat = Scan::table(tx, FIELDS_NAME, self.fields_layout.clone())?; + for (field_name, info) in schema.fields() { + fcat.insert()?; + 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 mut tcat = Scan::table(tx, TABLE_NAME, self.table_layout.clone())?; + while tcat.next_row()? { + if tcat.get_string(&Element::raw(TABLE_NAME))? == table_name { + size = tcat.get_i32(&Element::raw(TABLE_SLOT_SIZE))?; + break; + } + } + tcat.close()?; + let mut schema = SchemaBuilder::default(); + let mut offsets = HashMap::new(); + let mut fcat = Scan::table(tx, FIELDS_NAME, self.fields_layout.clone())?; + while fcat.next_row()? { + let table = fcat.get_string(&Element::raw(TABLE_NAME))?; + if table == table_name { + 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)) + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use crate::tests::init; + + use super::*; + + #[test] + fn manager() { + let (_dir, tx) = init(); + + let tm = TableMgr::new(true, &tx).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(); + + let layout = tm.get_layout("MyTable", &tx).unwrap(); + let mut expected_fields = HashSet::new(); + 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/planner/src/mgr/view.rs b/planner/src/mgr/view.rs new file mode 100644 index 0000000..6b69601 --- /dev/null +++ b/planner/src/mgr/view.rs @@ -0,0 +1,53 @@ +use std::sync::Arc; + +use common::DbResult; +use transaction::transaction::Transaction; + +use crate::{element::Element, mgr::table::TableMgr, scan::Scan, schema::SchemaBuilder}; + +const MAX_NAME: i32 = 16; +const MAX_VIEW_DEF: i32 = 100; + +const VIEW_NAME: &str = "view"; +const VIEW_DEF: &str = "view_def"; +const VIEW_TABLE: &str = "sp_view"; + +pub struct ViewMgr { + table_mgr: TableMgr, +} + +impl ViewMgr { + pub fn new(is_new: bool, table_mgr: TableMgr, tx: &Arc) -> DbResult { + if is_new { + 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 }) + } + + pub fn create_view(&self, name: &str, def: &str, tx: &Arc) -> DbResult<()> { + let layout = self.table_mgr.get_layout(VIEW_TABLE, tx)?; + let mut ts = Scan::table(tx, VIEW_TABLE, layout)?; + ts.insert()?; + 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 mut ts = Scan::table(tx, VIEW_TABLE, layout)?; + let mut result = None; + while ts.next_row()? { + if ts.get_string(&Element::raw(VIEW_NAME))? == name { + result = Some(ts.get_string(&Element::raw(VIEW_DEF))?); + break; + } + } + ts.close()?; + Ok(result) + } +} diff --git a/planner/src/plan.rs b/planner/src/plan.rs new file mode 100644 index 0000000..c28f51f --- /dev/null +++ b/planner/src/plan.rs @@ -0,0 +1,68 @@ +use std::sync::Arc; + +use common::DbResult; +use transaction::transaction::Transaction; + +use crate::{ + element::Element, + mgr::metadata::MetadataMgr, + plan::{select::SelectPlan, table::TablePlan}, + predicate::Predicate, + scan::Scan, + schema::Schema, +}; + +pub(crate) mod select; +pub(crate) mod table; + +pub(crate) enum Planner { + Table(TablePlan), + Select(SelectPlan), +} + +pub struct Plan(Planner); + +impl Plan { + pub fn table(tx: &Arc, table: String, md: &MetadataMgr) -> DbResult { + Ok(Self(Planner::Table(TablePlan::new(tx, table, md)?))) + } + + pub fn select(plan: Box, predicate: Predicate) -> Self { + Self(Planner::Select(SelectPlan::new(plan, predicate))) + } + + pub fn open(&self) -> DbResult { + match &self.0 { + Planner::Table(table) => table.open(), + Planner::Select(table) => table.open(), + } + } + + pub fn blocks_accessed(&self) -> DbResult { + match &self.0 { + Planner::Table(table) => table.blocks_accessed(), + Planner::Select(table) => table.blocks_accessed(), + } + } + + pub fn records_output(&self) -> DbResult { + match &self.0 { + Planner::Table(table) => table.records_output(), + Planner::Select(table) => table.records_output(), + } + } + + pub fn distinct_values(&self, field_name: &Element) -> DbResult { + match &self.0 { + Planner::Table(table) => table.distinct_values(field_name), + Planner::Select(table) => table.distinct_values(field_name), + } + } + + pub fn schema(&self) -> DbResult { + match &self.0 { + Planner::Table(table) => Ok(table.schema()), + Planner::Select(table) => table.schema(), + } + } +} diff --git a/planner/src/plan/select.rs b/planner/src/plan/select.rs new file mode 100644 index 0000000..d0819b1 --- /dev/null +++ b/planner/src/plan/select.rs @@ -0,0 +1,45 @@ +use common::DbResult; + +use crate::{element::Element, plan::Plan, predicate::Predicate, scan::Scan, schema::Schema}; + +pub(crate) struct SelectPlan { + plan: Box, + predicate: Predicate, +} + +impl SelectPlan { + pub(crate) fn new(plan: Box, predicate: Predicate) -> Self { + Self { plan, predicate } + } + + pub(crate) fn open(&self) -> DbResult { + let s = self.plan.open()?; + Ok(Scan::select(Box::new(s), self.predicate.clone())) + } + + pub(crate) fn blocks_accessed(&self) -> DbResult { + self.plan.blocks_accessed() + } + + pub(crate) fn records_output(&self) -> DbResult { + Ok(self.plan.records_output()? / self.predicate.reduction_factor(&self.plan)?) + } + + pub(crate) fn distinct_values(&self, field_name: &Element) -> common::DbResult { + if self.predicate.equates_with_constant(field_name)?.is_some() { + return Ok(1); + } else { + if let Some(second_field) = self.predicate.equates_with_field(field_name)? { + return Ok(self + .plan + .distinct_values(field_name)? + .min(self.plan.distinct_values(&second_field)?)); + } + } + self.plan.distinct_values(field_name) + } + + pub(crate) fn schema(&self) -> DbResult { + self.plan.schema() + } +} diff --git a/planner/src/plan/table.rs b/planner/src/plan/table.rs new file mode 100644 index 0000000..33cebe6 --- /dev/null +++ b/planner/src/plan/table.rs @@ -0,0 +1,52 @@ +use std::sync::Arc; + +use common::DbResult; +use transaction::transaction::Transaction; + +use crate::{ + element::Element, + layout::Layout, + mgr::{metadata::MetadataMgr, stat::StatInfo}, + scan::Scan, + schema::Schema, +}; + +pub(crate) struct TablePlan { + tx: Arc, + table: String, + layout: Layout, + stat: StatInfo, +} + +impl TablePlan { + pub(crate) fn new(tx: &Arc, table: String, md: &MetadataMgr) -> DbResult { + let layout = md.get_layout(&table, tx)?; + let stat = md.get_stat_info(&table, layout.clone(), tx)?; + Ok(Self { + tx: Arc::clone(tx), + table, + layout, + stat, + }) + } + + pub(crate) fn open(&self) -> DbResult { + Scan::table(&self.tx, &self.table, self.layout.clone()) + } + + pub(crate) fn blocks_accessed(&self) -> DbResult { + Ok(self.stat.block_accessed()) + } + + pub(crate) fn records_output(&self) -> DbResult { + Ok(self.stat.records_output()) + } + + pub(crate) fn distinct_values(&self, _: &Element) -> DbResult { + Ok(self.stat.distinct_values()) + } + + pub(crate) fn schema(&self) -> Schema { + self.layout.schema().clone() + } +} diff --git a/scan/src/predicate.rs b/planner/src/predicate.rs similarity index 83% rename from scan/src/predicate.rs rename to planner/src/predicate.rs index bfde991..5aa6d7e 100644 --- a/scan/src/predicate.rs +++ b/planner/src/predicate.rs @@ -6,8 +6,9 @@ use std::{ use common::{DbResult, error::DbError}; use crate::{ - Scan, element::Element, + plan::Plan, + scan::Scan, schema::{Schema, SchemaBuilder}, value::Value, }; @@ -33,7 +34,7 @@ impl Expression { } } - pub fn as_field(&self) -> Option<&Element> { + pub fn as_field_name(&self) -> Option<&Element> { match self { Self::Field(field) => Some(field), _ => None, @@ -78,33 +79,35 @@ impl Term { self.left.applies_to(schema) && self.right.applies_to(schema) } - pub fn reduction_factor(&self, left_distinct: i32, right_distinct: i32) -> i32 { - if self.left.as_field().is_some() && self.right.as_field().is_some() { - return left_distinct.max(right_distinct); + pub fn reduction_factor(&self, p: &Plan) -> DbResult { + if let Some(left) = self.left.as_field_name() + && let Some(right) = self.right.as_field_name() + { + return Ok(p.distinct_values(left)?.max(p.distinct_values(right)?)); } - if self.left.as_field().is_some() { - return left_distinct; + if let Some(left) = self.left.as_field_name() { + return p.distinct_values(left); } - if self.right.as_field().is_some() { - return right_distinct; + if let Some(right) = self.right.as_field_name() { + return p.distinct_values(right); } if let Some(left) = self.left.as_constant() && let Some(right) = self.right.as_constant() && left == right { - 1 + Ok(1) } else { - i32::MAX + Ok(i32::MAX) } } pub fn equates_with_constant(&self, field_name: &Element) -> DbResult> { - if let Some(field) = self.left.as_field() + if let Some(field) = self.left.as_field_name() && field == field_name && let Some(value) = self.right.as_constant() { Ok(Some(value.clone())) - } else if let Some(right) = self.right.as_field() + } else if let Some(right) = self.right.as_field_name() && right == field_name && let Some(value) = self.left.as_constant() { @@ -114,17 +117,17 @@ impl Term { } } - pub fn equates_with_field(&self, field_name: &Element) -> DbResult> { - if let Some(left) = self.left.as_field() + 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() + && let Some(right) = self.right.as_field_name() { - Ok(Some(right.to_string())) - } else if let Some(right) = self.right.as_field() + Ok(Some(right.clone())) + } else if let Some(right) = self.right.as_field_name() && right == field_name - && let Some(left) = self.left.as_field() + && let Some(left) = self.left.as_field_name() { - Ok(Some(left.to_string())) + Ok(Some(left.clone())) } else { Ok(None) } @@ -168,11 +171,11 @@ impl Predicate { Ok(true) } - pub fn reduction_factor(&self, left_distinct: i32, right_distinct: i32) -> DbResult { + pub fn reduction_factor(&self, p: &Plan) -> DbResult { let mut factor = 1; let read = self.terms.read().map_err(DbError::lock)?; for t in read.iter() { - factor *= t.reduction_factor(left_distinct, right_distinct); + factor *= t.reduction_factor(p)?; } Ok(factor) } @@ -219,7 +222,7 @@ impl Predicate { Ok(None) } - pub fn equates_with_field(&self, field_name: &Element) -> 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/scan/src/record_page.rs b/planner/src/record_page.rs similarity index 100% rename from scan/src/record_page.rs rename to planner/src/record_page.rs diff --git a/scan/src/rid.rs b/planner/src/rid.rs similarity index 100% rename from scan/src/rid.rs rename to planner/src/rid.rs diff --git a/scan/src/lib.rs b/planner/src/scan.rs similarity index 77% rename from scan/src/lib.rs rename to planner/src/scan.rs index fe3d85e..fc1df4e 100644 --- a/scan/src/lib.rs +++ b/planner/src/scan.rs @@ -4,22 +4,18 @@ use common::DbResult; use transaction::transaction::Transaction; use crate::{ - element::Element, layout::Layout, predicate::Predicate, rid::RID, scanner::Scanner, - schema::Schema, select::SelectScan, table::TableScan, value::Value, + element::Element, + layout::Layout, + predicate::Predicate, + rid::RID, + scan::{scanner::Scanner, select::SelectScan, table::TableScan}, + schema::Schema, + value::Value, }; -pub mod element; -pub mod field_info; -pub mod index; -pub mod layout; -pub mod predicate; -pub mod record_page; -pub mod rid; pub(crate) mod scanner; -pub mod schema; pub(crate) mod select; pub(crate) mod table; -pub mod value; pub struct Scan { scan: Scanner, @@ -107,13 +103,7 @@ impl Scan { #[cfg(test)] mod tests { - use buffer::mgr::BufferMgr; - use file::mgr::FileMgr; - use log::mgr::LogMgr; - use tempfile::{TempDir, tempdir}; - use transaction::lock_table::LockTable; - - use crate::schema::SchemaBuilder; + use crate::{schema::SchemaBuilder, tests::init}; use super::*; @@ -166,19 +156,4 @@ mod tests { scan.close().unwrap(); } - - 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, 1).unwrap()); - let lock_table = Arc::new(LockTable::default()); - - let tx = Arc::new(Transaction::new(&fm, &lm, &bm, &lock_table).unwrap()); - (dir, tx) - } } diff --git a/scan/src/scanner.rs b/planner/src/scan/scanner.rs similarity index 96% rename from scan/src/scanner.rs rename to planner/src/scan/scanner.rs index 7a5d423..9b84043 100644 --- a/scan/src/scanner.rs +++ b/planner/src/scan/scanner.rs @@ -1,7 +1,11 @@ use common::{DbResult, error::DbError}; use crate::{ - element::Element, rid::RID, schema::Schema, select::SelectScan, table::TableScan, value::Value, + element::Element, + rid::RID, + scan::{select::SelectScan, table::TableScan}, + schema::Schema, + value::Value, }; pub(crate) enum Scanner { diff --git a/scan/src/select.rs b/planner/src/scan/select.rs similarity index 94% rename from scan/src/select.rs rename to planner/src/scan/select.rs index b57a409..5337f05 100644 --- a/scan/src/select.rs +++ b/planner/src/scan/select.rs @@ -1,6 +1,8 @@ use common::DbResult; -use crate::{Scan, element::Element, predicate::Predicate, rid::RID, schema::Schema, value::Value}; +use crate::{ + element::Element, predicate::Predicate, rid::RID, scan::Scan, schema::Schema, value::Value, +}; pub(crate) struct SelectScan { scan: Box, diff --git a/scan/src/table.rs b/planner/src/scan/table.rs similarity index 100% rename from scan/src/table.rs rename to planner/src/scan/table.rs diff --git a/scan/src/schema.rs b/planner/src/schema.rs similarity index 100% rename from scan/src/schema.rs rename to planner/src/schema.rs diff --git a/scan/src/value.rs b/planner/src/value.rs similarity index 100% rename from scan/src/value.rs rename to planner/src/value.rs 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)) - } -} From 4884e7e5c7a74c4a13060042a40e750652a9b5e6 Mon Sep 17 00:00:00 2001 From: Daniil Buravlev Date: Tue, 30 Jun 2026 15:48:12 +0300 Subject: [PATCH 06/13] Add chunk/group/index/merge scans, change index info keys to Element, derive Schema Eq, add tests and raise test buffer size. --- Cargo.lock | 1 + buffer/Cargo.toml | 1 + buffer/src/mgr.rs | 5 +- planner/src/lib.rs | 2 +- planner/src/mgr/index.rs | 4 +- planner/src/mgr/metadata.rs | 3 +- planner/src/mgr/stat.rs | 2 +- planner/src/plan/select.rs | 60 +++++++++ planner/src/scan.rs | 4 + planner/src/scan/chunk.rs | 116 +++++++++++++++++ planner/src/scan/group.rs | 167 ++++++++++++++++++++++++ planner/src/scan/index.rs | 228 +++++++++++++++++++++++++++++++++ planner/src/scan/merge.rs | 109 ++++++++++++++++ planner/src/schema.rs | 4 +- transaction/src/transaction.rs | 3 + 15 files changed, 701 insertions(+), 8 deletions(-) create mode 100644 planner/src/scan/chunk.rs create mode 100644 planner/src/scan/group.rs create mode 100644 planner/src/scan/index.rs create mode 100644 planner/src/scan/merge.rs diff --git a/Cargo.lock b/Cargo.lock index bbbe572..6c60576 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,6 +102,7 @@ dependencies = [ "file", "log 0.0.1", "tempfile", + "tracing", ] [[package]] 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..6d3c772 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/planner/src/lib.rs b/planner/src/lib.rs index da59007..ce0f065 100644 --- a/planner/src/lib.rs +++ b/planner/src/lib.rs @@ -29,7 +29,7 @@ mod tests { 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, 1).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()); diff --git a/planner/src/mgr/index.rs b/planner/src/mgr/index.rs index ef5327c..0351516 100644 --- a/planner/src/mgr/index.rs +++ b/planner/src/mgr/index.rs @@ -121,7 +121,7 @@ impl IndexMgr { &self, table_name: &str, tx: &Arc, - ) -> DbResult> { + ) -> DbResult> { let mut result = HashMap::new(); let mut ts = Scan::table(tx, IDX_TABLE, self.layout.clone())?; while ts.next_row()? { @@ -134,7 +134,7 @@ impl IndexMgr { .get_stat_info(table_name, layout.clone(), tx)?; let index = IndexInfo::new(idx_name, field_name.clone(), layout.schema(), tx, stat)?; - result.insert(field_name, index); + result.insert(Element::Raw(field_name), index); } } ts.close()?; diff --git a/planner/src/mgr/metadata.rs b/planner/src/mgr/metadata.rs index 04602b3..c7bacc0 100644 --- a/planner/src/mgr/metadata.rs +++ b/planner/src/mgr/metadata.rs @@ -13,6 +13,7 @@ use crate::{ }, schema::Schema, }; +use crate::element::Element; pub struct MetadataMgr { table_mgr: TableMgr, @@ -85,7 +86,7 @@ impl MetadataMgr { &self, table_name: &str, tx: &Arc, - ) -> DbResult> { + ) -> DbResult> { self.index_mgr.get_index_info(table_name, tx) } } diff --git a/planner/src/mgr/stat.rs b/planner/src/mgr/stat.rs index df7989e..3285ea5 100644 --- a/planner/src/mgr/stat.rs +++ b/planner/src/mgr/stat.rs @@ -80,7 +80,7 @@ impl StatMgrLock { self.table_stats.clear(); self.num_calls = 0; let layout = self.table_mgr.get_layout(TABLE_NAME, tx)?; - let mut ts = Scan::table(tx, TABLE_NAME, layout.clone())?; + let mut ts = Scan::table(tx, TABLE_NAME, layout)?; while ts.next_row()? { let table_name = ts.get_string(&Element::raw(TABLE_NAME))?; let layout = self.table_mgr.get_layout(&table_name, tx)?; diff --git a/planner/src/plan/select.rs b/planner/src/plan/select.rs index d0819b1..6d6bfd1 100644 --- a/planner/src/plan/select.rs +++ b/planner/src/plan/select.rs @@ -43,3 +43,63 @@ impl SelectPlan { self.plan.schema() } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::schema::SchemaBuilder; + use crate::{ + mgr::metadata::MetadataMgr, + predicate::{Expression, Term}, + tests::init, + value::Value, + }; + + #[test] + fn select() { + let (_dir, tx) = init(); + let md = MetadataMgr::new(true, &tx).unwrap(); + let schema = SchemaBuilder::default() + .add_int_field(Element::raw("id")) + .build(); + md.create_table("test", schema.clone(), &tx).unwrap(); + tx.commit().unwrap(); + + let table = Plan::table(&tx, "test".to_string(), &md).unwrap(); + + let field = Element::raw("id"); + let value = Value::Integer(1); + + let predicate = predicate(field.clone(), value.clone()); + + let select = Plan::select(Box::new(table), predicate); + + let mut select_scan = select.open().unwrap(); + + for _ in 0..10 { + for i in 0..10 { + select_scan.insert().unwrap(); + select_scan.set_i32(&field, i).unwrap(); + } + } + + assert_eq!(0, select.blocks_accessed().unwrap()); + assert_eq!(0, select.records_output().unwrap()); + assert_eq!(1, select.distinct_values(&field).unwrap()); + assert_eq!(schema, select.schema().unwrap()); + + select_scan.before_first().unwrap(); + while select_scan.next_row().unwrap() { + let found = select_scan.get_val(&field).unwrap(); + assert_eq!(value, found); + } + } + + fn predicate(field: Element, value: Value) -> Predicate { + let expression1 = Expression::Field(field.clone()); + let expression2 = Expression::Value(value.clone()); + + let term = Term::new(expression1, expression2); + Predicate::new(term) + } +} diff --git a/planner/src/scan.rs b/planner/src/scan.rs index fc1df4e..28cfc68 100644 --- a/planner/src/scan.rs +++ b/planner/src/scan.rs @@ -13,6 +13,10 @@ use crate::{ value::Value, }; +pub(crate) mod chunk; +pub(crate) mod group; +pub(crate) mod index; +pub(crate) mod merge; pub(crate) mod scanner; pub(crate) mod select; pub(crate) mod table; diff --git a/planner/src/scan/chunk.rs b/planner/src/scan/chunk.rs new file mode 100644 index 0000000..f837b9d --- /dev/null +++ b/planner/src/scan/chunk.rs @@ -0,0 +1,116 @@ +use std::sync::Arc; + +use common::{DbResult, error::DbError}; +use file::block::BlockId; +use transaction::transaction::Transaction; + +use crate::element::Element; +use crate::schema::Schema; +use crate::value::Value; +use crate::{field_info::FieldInfo, layout::Layout, record_page::RecordPage}; + +pub(crate) struct ChunkScan { + buffers: Vec, + tx: Arc, + filename: String, + layout: Layout, + start_b_num: i32, + end_b_num: i32, + current_b_num: i32, + rp: i32, + current_slot: i32, +} + +impl ChunkScan { + pub(crate) fn new( + tx: &Arc, + filename: &str, + 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.clone())?); + } + let chunk = Self { + buffers, + tx: Arc::clone(tx), + filename: filename.to_string(), + start_b_num, + end_b_num, + current_b_num: start_b_num, + layout, + current_slot: -1, + rp: 0, + }; + Ok(chunk) + } + + pub(crate) fn move_to_block(&mut self, block_num: i32) { + self.current_b_num = block_num; + self.rp = self.current_b_num - self.start_b_num; + self.current_slot = -1; + } + + pub(crate) fn close(&self) -> DbResult<()> { + for i in 0..self.buffers.len() { + let block = BlockId::new(&self.filename, self.start_b_num + i as i32); + self.tx.unpin(&block)?; + } + Ok(()) + } + + pub(crate) fn before_first(&mut self) { + self.move_to_block(self.start_b_num); + } + + pub(crate) fn next(&mut self) -> DbResult { + loop { + let Some(rp) = self.buffers.get(self.rp as usize).cloned() else { + return Err(DbError::other("cannot get record page for chunk")); + }; + self.current_slot = rp.next_after(self.current_slot)?; + if self.current_slot >= 0 { + return Ok(true); + } + if self.current_b_num == self.end_b_num { + return Ok(false); + } + self.move_to_block(rp.block().num + 1); + } + } + + pub(crate) 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 { + Err(DbError::other("cannot get buffer chunk")) + } + } + + pub(crate) 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 { + Err(DbError::other("cannot get buffer chunk")) + } + } + + pub(crate) 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())), + } + } + + pub(crate) fn has_field(&self, field_name: &Element) -> bool { + self.layout.schema().has_field(field_name) + } + + pub(crate) fn schema(&self) -> Schema { + self.layout.schema().clone() + } +} diff --git a/planner/src/scan/group.rs b/planner/src/scan/group.rs new file mode 100644 index 0000000..f4354c3 --- /dev/null +++ b/planner/src/scan/group.rs @@ -0,0 +1,167 @@ +use std::{cell::RefCell, cmp::Ordering, collections::HashMap, rc::Rc, sync::Arc}; + +use common::{DbResult, error::DbError}; + +use crate::element::Element; +use crate::value::Value; +use crate::{scan::Scan, schema::Schema}; + +#[allow(dead_code)] +#[derive(Clone)] +pub enum AggregationFn { + MaxFn { field: Element, value: Value }, +} + +impl AggregationFn { + pub fn process_first(&mut self, scan: &Scan) -> DbResult<()> { + match self { + Self::MaxFn { field, value } => { + *value = scan.get_val(field)?; + } + } + Ok(()) + } + + pub fn process_next(&mut self, scan: &Scan) -> DbResult<()> { + match self { + Self::MaxFn { field, value } => { + let new_value = scan.get_val(field)?; + if new_value.cmp(value) == Ordering::Greater { + *value = new_value; + } + } + } + Ok(()) + } + + pub fn field_name(&self) -> &Element { + match self { + Self::MaxFn { field, .. } => field, + } + } + + pub fn value(&self) -> Value { + match self { + Self::MaxFn { value, .. } => value.clone(), + } + } +} + +#[derive(PartialEq, Eq)] +struct GroupValue { + values: HashMap, +} + +impl GroupValue { + fn new(scan: &Scan, fields: Vec) -> DbResult { + let mut values = HashMap::new(); + for field in fields { + let value = scan.get_val(&field)?; + values.insert(field, value); + } + Ok(Self { values }) + } + + fn get_val(&self, field: &Element) -> DbResult { + match self.values.get(field) { + Some(value) => Ok(value.clone()), + _ => Err(DbError::FieldNotExists(field.to_string())), + } + } +} + +struct GroupByScan { + scan: Box, + group_fields: Vec, + agg_fns: Vec, + group_val: GroupValue, + more_groups: bool, +} + +impl GroupByScan { + fn new( + scan: Box, + group_fields: Vec, + agg_fns: Vec, + ) -> DbResult { + let mut g = Self { + group_val: GroupValue::new(&scan, vec![])?, + scan, + group_fields, + agg_fns, + more_groups: false, + }; + g.before_first()?; + Ok(g) + } + + fn before_first(&mut self) -> DbResult<()> { + self.scan.before_first()?; + self.more_groups = self.scan.next_row()?; + Ok(()) + } + + fn next(&mut self) -> DbResult { + if !self.more_groups { + return Ok(false); + } + for f in &mut self.agg_fns { + f.process_first(&self.scan)?; + } + self.group_val = GroupValue::new(&self.scan, self.group_fields.clone())?; + loop { + self.more_groups = self.scan.next_row()?; + if !self.more_groups { + break; + } + let gv = GroupValue::new(&self.scan, self.group_fields.clone())?; + if gv != self.group_val { + break; + } + for f in &mut self.agg_fns { + f.process_next(&self.scan)?; + } + } + Ok(true) + } + + fn close(&self) -> DbResult<()> { + self.scan.close() + } + + 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 { + if f.field_name() == field_name { + return Ok(f.value()); + } + } + Err(DbError::FieldNotExists(field_name.to_string())) + } + + fn get_i32(&self, field_name: &Element) -> DbResult { + self.get_val(field_name)?.as_i32() + } + + fn get_string(&self, field_name: &Element) -> DbResult { + self.get_val(field_name)?.as_string() + } + + fn has_field(&self, field_name: &Element) -> bool { + if self.group_fields.contains(field_name) { + return true; + } + for f in &self.agg_fns { + if f.field_name() == field_name { + return true; + } + } + false + } + + fn schema(&self) -> DbResult { + self.scan.schema() + } +} diff --git a/planner/src/scan/index.rs b/planner/src/scan/index.rs new file mode 100644 index 0000000..519b03b --- /dev/null +++ b/planner/src/scan/index.rs @@ -0,0 +1,228 @@ +use common::DbResult; + +use crate::element::Element; +use crate::schema::{Schema, SchemaBuilder}; +use crate::value::Value; +use crate::{index::Index, scan::Scan}; + +pub struct IndexSelectScan { + scan: Box, + index: Index, + value: Value, +} + +impl IndexSelectScan { + pub fn new(scan: Box, index: Index, value: Value) -> DbResult { + let mut scan = Self { scan, index, value }; + scan.before_first()?; + Ok(scan) + } + + pub(crate) fn before_first(&mut self) -> DbResult<()> { + self.index.before_first(self.value.clone()) + } + + pub(crate) fn next_rot(&mut self) -> DbResult { + let ok = self.index.next_row()?; + if ok { + let rid = self.index.get_data_rid()?; + self.scan.move_to_rid(rid)?; + } + Ok(ok) + } + + pub(crate) fn get_i32(&self, field_name: &Element) -> DbResult { + self.scan.get_i32(field_name) + } + + pub(crate) fn get_string(&self, field_name: &Element) -> DbResult { + self.scan.get_string(field_name) + } + + pub(crate) fn get_val(&self, field_name: &Element) -> DbResult { + self.scan.get_val(field_name) + } + + pub(crate) fn has_field(&self, field_name: &Element) -> DbResult { + self.scan.has_field(field_name) + } + + pub(crate) fn close(&self) -> DbResult<()> { + self.index.close()?; + self.scan.close() + } + + pub(crate) fn schema(&self) -> DbResult { + self.scan.schema() + } +} + +pub(crate) struct IndexJoinScan { + left: Box, + right: Box, + index: Index, + field: Element, +} + +impl IndexJoinScan { + pub(crate) fn new( + left: Box, + index: Index, + field: Element, + right: Box, + ) -> DbResult { + let mut scan = Self { + left, + right, + field, + index, + }; + scan.before_first()?; + Ok(scan) + } + + pub(crate) fn reset_index(&mut self) -> DbResult<()> { + let key = self.left.get_val(&self.field)?; + self.index.before_first(key) + } + + pub(crate) fn before_first(&mut self) -> DbResult<()> { + self.left.before_first()?; + self.left.next_row()?; + self.reset_index() + } + + pub(crate) fn next_row(&mut self) -> DbResult { + loop { + if self.index.next_row()? { + self.right.move_to_rid(self.index.get_data_rid()?)?; + return Ok(true); + } + if !self.left.next_row()? { + return Ok(false); + } + self.reset_index()?; + } + } + + pub(crate) fn get_i32(&self, field_name: &Element) -> DbResult { + if self.right.has_field(field_name)? { + self.right.get_i32(field_name) + } else { + self.left.get_i32(field_name) + } + } + + pub(crate) fn get_string(&self, field_name: &Element) -> DbResult { + if self.right.has_field(field_name)? { + self.right.get_string(field_name) + } else { + self.left.get_string(field_name) + } + } + + pub(crate) fn get_val(&self, field_name: &Element) -> DbResult { + if self.right.has_field(field_name)? { + self.right.get_val(field_name) + } else { + self.left.get_val(field_name) + } + } + + pub(crate) fn has_field(&self, field_name: &Element) -> DbResult { + Ok(self.right.has_field(field_name)? || self.left.has_field(field_name)?) + } + + pub(crate) fn close(&self) -> DbResult<()> { + self.left.close()?; + self.index.close()?; + self.right.close() + } + + pub(crate) fn schema(&self) -> DbResult { + let s1 = self.left.schema()?; + let s2 = self.right.schema()?; + let s = SchemaBuilder::default().add_all(&s1).add_all(&s2).build(); + Ok(s) + } +} + +#[cfg(test)] +mod tests { + use crate::element::Element; + use crate::mgr::metadata::MetadataMgr; + use crate::plan::table::TablePlan; + use crate::schema::SchemaBuilder; + use crate::tests::init; + use std::collections::HashMap; + + #[test] + fn update_index() { + let (_dir, tx) = init(); + let md = MetadataMgr::new(true, &tx).unwrap(); + tx.commit().unwrap(); + + let sid = Element::raw("sid"); + let sname = Element::raw("sname"); + let gadyear = Element::raw("gadyear"); + let majorid = Element::raw("majorid"); + + 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(); + let mut s = plan.open().unwrap(); + + let mut indexes = HashMap::new(); + let infos = md.get_index_info("users", &tx).unwrap(); + for (field, info) in infos { + let index = info.open().unwrap(); + indexes.insert(field, index); + } + + 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(); + + let rid = s.get_rid().unwrap(); + for (field, index) in indexes.iter() { + let value = s.get_val(field).unwrap(); + index.insert(value, rid.clone()).unwrap(); + } + + s.before_first().unwrap(); + while s.next_row().unwrap() { + 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(); + index.delete(value, rid.clone()).unwrap(); + } + s.delete().unwrap(); + break; + } + } + s.before_first().unwrap(); + while s.next_row().unwrap() { + println!( + "{} {}", + s.get_string(&sname).unwrap(), + s.get_i32(&sid).unwrap() + ); + } + s.close().unwrap(); + for idx in indexes.values() { + idx.close().unwrap(); + } + tx.commit().unwrap(); + } +} diff --git a/planner/src/scan/merge.rs b/planner/src/scan/merge.rs new file mode 100644 index 0000000..8a9cb6f --- /dev/null +++ b/planner/src/scan/merge.rs @@ -0,0 +1,109 @@ +use crate::element::Element; +use crate::scan::Scan; +use crate::schema::{Schema, SchemaBuilder}; +use crate::value::Value; +use common::DbResult; +use std::cmp::Ordering; + +#[allow(dead_code)] +struct MergeJoinScan { + s1: Box, + s2: Box, + field_name1: Element, + field_name2: Element, + join_val: Option, +} + +#[allow(dead_code)] +impl MergeJoinScan { + pub fn new( + s1: Box, + s2: Box, + field_name1: Element, + field_name2: Element, + ) -> DbResult { + let mut s = Self { + s1, + s2, + field_name1, + field_name2, + join_val: None, + }; + s.before_first()?; + Ok(s) + } + + pub(crate) fn before_first(&mut self) -> DbResult<()> { + self.s1.before_first()?; + self.s2.before_first() + } + + pub(crate) fn next(&mut self) -> DbResult { + let Some(join_val) = &self.join_val else { + return Ok(false); + }; + let mut has_more2 = self.s2.next_row()?; + if has_more2 && self.s2.get_val(&self.field_name2)? == *join_val { + return Ok(true); + } + let mut has_more1 = self.s1.next_row()?; + if has_more1 && self.s1.get_val(&self.field_name1)? == *join_val { + return Ok(true); + } + + while has_more1 && has_more2 { + let v1 = self.s1.get_val(&self.field_name1)?; + let v2 = self.s2.get_val(&self.field_name2)?; + match v1.cmp(&v2) { + Ordering::Less => has_more1 = self.s1.next_row()?, + Ordering::Greater => has_more2 = self.s1.next_row()?, + _ => { + self.s2.save_position()?; + self.join_val = Some(self.s2.get_val(&self.field_name2)?); + return Ok(true); + } + } + } + Ok(false) + } + + fn get_i32(&self, field_name: &Element) -> DbResult { + if self.s1.has_field(field_name)? { + self.s1.get_i32(field_name) + } else { + self.s2.get_i32(field_name) + } + } + + fn get_string(&self, field_name: &Element) -> DbResult { + if self.s1.has_field(field_name)? { + self.s1.get_string(field_name) + } else { + self.s2.get_string(field_name) + } + } + + fn get_val(&self, field_name: &Element) -> DbResult { + if self.s1.has_field(field_name)? { + self.s1.get_val(field_name) + } else { + self.s2.get_val(field_name) + } + } + + fn has_field(&self, field_name: &Element) -> DbResult { + Ok(self.s1.has_field(field_name)? || self.s2.has_field(field_name)?) + } + + fn close(&self) -> DbResult<()> { + self.s1.close()?; + self.s2.close() + } + + fn schema(&self) -> DbResult { + let s1 = self.s1.schema()?; + let s2 = self.s2.schema()?; + let s = SchemaBuilder::default().add_all(&s1).add_all(&s2).build(); + Ok(s) + } +} diff --git a/planner/src/schema.rs b/planner/src/schema.rs index 42e1a46..e62f6b2 100644 --- a/planner/src/schema.rs +++ b/planner/src/schema.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, fmt::Debug, rc::Rc}; use crate::{element::Element, field_info::FieldInfo}; -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub struct SchemaInner { fields: Vec, infos: HashMap, @@ -55,7 +55,7 @@ impl SchemaInner { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Schema(Rc); impl Schema { diff --git a/transaction/src/transaction.rs b/transaction/src/transaction.rs index 3dc3f83..aa5a816 100644 --- a/transaction/src/transaction.rs +++ b/transaction/src/transaction.rs @@ -126,6 +126,9 @@ impl Transaction { pub fn get_u8(&self, block: &BlockId, offset: usize) -> DbResult { self.concurrency_mgr.s_lock(block)?; + if block.filename == "users.tbl" { + println!("s lock users"); + } let Some(buffer) = self.buffers.get_buffer(block)? else { return Err(DbError::BufferAbort); }; From 6f8dabaf1cbcf5f5a07c65fa9106217e058f0f9b Mon Sep 17 00:00:00 2001 From: Daniil Buravlev Date: Tue, 30 Jun 2026 16:20:46 +0300 Subject: [PATCH 07/13] Remove not valid commits in B-Tree index --- engine/src/scan/index.rs | 2 +- planner/src/index/b_tree.rs | 6 ++++-- planner/src/index/b_tree/page.rs | 4 ++-- transaction/src/transaction.rs | 3 --- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/engine/src/scan/index.rs b/engine/src/scan/index.rs index 87ae225..e6aa044 100644 --- a/engine/src/scan/index.rs +++ b/engine/src/scan/index.rs @@ -168,7 +168,7 @@ mod tests { }; #[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(); diff --git a/planner/src/index/b_tree.rs b/planner/src/index/b_tree.rs index 7a06df8..c1c0063 100644 --- a/planner/src/index/b_tree.rs +++ b/planner/src/index/b_tree.rs @@ -274,7 +274,9 @@ impl BTreeIndex { } pub(crate) fn close(&self) -> DbResult<()> { - todo!() + // 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<()> { @@ -309,7 +311,7 @@ fn create_index(tx: &Transaction, index_name: &str) -> DbResult<()> { }; metadata.write(&metadata_block, tx)?; leaf.write(&leaf_block, tx)?; - tx.commit() + Ok(()) } #[cfg(test)] diff --git a/planner/src/index/b_tree/page.rs b/planner/src/index/b_tree/page.rs index e78a5bc..2865894 100644 --- a/planner/src/index/b_tree/page.rs +++ b/planner/src/index/b_tree/page.rs @@ -66,7 +66,7 @@ impl BTreePage { } _ => Err(DbError::other("invalid page type")), }; - tx.commit()?; + tx.unpin(block)?; page } @@ -99,7 +99,7 @@ impl BTreePage { write_entries(tx, block, children, offset)?; } } - tx.commit() + tx.unpin(block) } } diff --git a/transaction/src/transaction.rs b/transaction/src/transaction.rs index aa5a816..3dc3f83 100644 --- a/transaction/src/transaction.rs +++ b/transaction/src/transaction.rs @@ -126,9 +126,6 @@ impl Transaction { pub fn get_u8(&self, block: &BlockId, offset: usize) -> DbResult { self.concurrency_mgr.s_lock(block)?; - if block.filename == "users.tbl" { - println!("s lock users"); - } let Some(buffer) = self.buffers.get_buffer(block)? else { return Err(DbError::BufferAbort); }; From 5800b7c9ee43a252b314c2191c35d68a5e5a3114 Mon Sep 17 00:00:00 2001 From: Daniil Buravlev Date: Tue, 30 Jun 2026 19:43:50 +0300 Subject: [PATCH 08/13] Refactor Rc&Locks -> Box --- planner/src/buffer_needs.rs | 31 ++++++ planner/src/lib.rs | 2 + planner/src/scan.rs | 28 ++++- planner/src/scan/group.rs | 2 +- planner/src/scan/multibuffer.rs | 154 ++++++++++++++++++++++++++ planner/src/scan/order.rs | 189 ++++++++++++++++++++++++++++++++ planner/src/scan/product.rs | 74 +++++++++++++ planner/src/scan/project.rs | 71 ++++++++++++ planner/src/scan/scanner.rs | 36 +++++- planner/src/temp.rs | 42 +++++++ 10 files changed, 626 insertions(+), 3 deletions(-) create mode 100644 planner/src/buffer_needs.rs create mode 100644 planner/src/scan/multibuffer.rs create mode 100644 planner/src/scan/order.rs create mode 100644 planner/src/scan/product.rs create mode 100644 planner/src/scan/project.rs create mode 100644 planner/src/temp.rs diff --git a/planner/src/buffer_needs.rs b/planner/src/buffer_needs.rs new file mode 100644 index 0000000..54ca664 --- /dev/null +++ b/planner/src/buffer_needs.rs @@ -0,0 +1,31 @@ +pub struct BufferNeeds {} + +impl BufferNeeds { + pub fn best_root(available: i32, size: i32) -> i32 { + let available = available - 2; + if available <= 1 { + return 1; + } + let mut k = i32::MAX; + let mut i = 1f32; + while k > available { + i += 1f32; + k = (size as f32).powf(1.0 / i).ceil() as i32; + } + k + } + + pub fn best_factor(availabe: i32, size: i32) -> i32 { + let available = availabe - 2; + if available <= 1 { + return 1; + } + let mut k = size; + let mut i: f32 = 1.0; + while k > available { + i += 1f32; + k = (size as f32 / i).ceil() as i32; + } + k + } +} diff --git a/planner/src/lib.rs b/planner/src/lib.rs index ce0f065..006df24 100644 --- a/planner/src/lib.rs +++ b/planner/src/lib.rs @@ -1,3 +1,4 @@ +pub mod buffer_needs; pub mod element; pub mod field_info; pub mod index; @@ -9,6 +10,7 @@ pub mod record_page; pub mod rid; pub mod scan; pub mod schema; +pub mod temp; pub mod value; #[cfg(test)] diff --git a/planner/src/scan.rs b/planner/src/scan.rs index 28cfc68..7beb4a4 100644 --- a/planner/src/scan.rs +++ b/planner/src/scan.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use common::DbResult; use transaction::transaction::Transaction; +use crate::scan::chunk::ChunkScan; use crate::{ element::Element, layout::Layout, @@ -12,11 +13,16 @@ use crate::{ schema::Schema, value::Value, }; +use crate::scan::product::ProductScan; pub(crate) mod chunk; pub(crate) mod group; pub(crate) mod index; pub(crate) mod merge; +pub(crate) mod multibuffer; +pub(crate) mod order; +pub(crate) mod product; +pub(crate) mod project; pub(crate) mod scanner; pub(crate) mod select; pub(crate) mod table; @@ -33,13 +39,33 @@ impl Scan { }) } - pub fn select(scan: Box, predicate: Predicate) -> Self { + pub fn select(scan: Box, predicate: Predicate) -> Self { let select = SelectScan::new(scan, predicate); Self { scan: Scanner::Select(select), } } + pub fn product(s1: Box, s2: Box) -> DbResult { + let product = ProductScan::new(s1, s2)?; + Ok(Self { + scan: Scanner::Product(product), + }) + } + + pub fn chunk( + tx: &Arc, + filename: &str, + layout: Layout, + start_b_num: i32, + end_b_num: i32, + ) -> DbResult { + let select = ChunkScan::new(tx, filename, layout, start_b_num, end_b_num)?; + Ok(Self { + scan: Scanner::Chunk(select), + }) + } + pub fn before_first(&mut self) -> DbResult<()> { self.scan.before_first() } diff --git a/planner/src/scan/group.rs b/planner/src/scan/group.rs index f4354c3..f611129 100644 --- a/planner/src/scan/group.rs +++ b/planner/src/scan/group.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, cmp::Ordering, collections::HashMap, rc::Rc, sync::Arc}; +use std::{cmp::Ordering, collections::HashMap}; use common::{DbResult, error::DbError}; diff --git a/planner/src/scan/multibuffer.rs b/planner/src/scan/multibuffer.rs new file mode 100644 index 0000000..4a203c2 --- /dev/null +++ b/planner/src/scan/multibuffer.rs @@ -0,0 +1,154 @@ +use std::sync::Arc; + +use common::{DbResult, error::DbError}; +use transaction::transaction::Transaction; + +use crate::buffer_needs::BufferNeeds; +use crate::element::Element; +use crate::value::Value; +use crate::{ + layout::Layout, + scan::Scan, + schema::{Schema, SchemaBuilder}, +}; + +pub(crate) struct MultiBufferProductScan { + tx: Arc, + left: Box, + right: Option>, + filename: String, + layout: Layout, + chunk_size: i32, + next_block: i32, + file_size: i32, +} + +impl MultiBufferProductScan { + pub(crate) fn new( + tx: &Arc, + left: Box, + filename: &str, + layout: Layout, + ) -> DbResult { + let available = tx.available_buffs()? as i32; + let file_size = tx.size(filename)? as i32; + let mut scan = Self { + tx: Arc::clone(tx), + left, + file_size: tx.size(filename)? as i32, + filename: filename.to_string(), + layout, + chunk_size: BufferNeeds::best_factor(available, file_size), + next_block: 0, + right: None, + }; + scan.before_first()?; + Ok(scan) + } + + + pub(crate) fn use_next_chunk(&mut self) -> DbResult { + if let Some(right) = self.right.take() { + right.close()?; + } + if self.next_block >= self.file_size { + return Ok(false); + } + let mut end = self.next_block + self.chunk_size - 1; + if end >= self.file_size { + end = self.file_size - 1; + } + let mut right = Box::new(Scan::chunk( + &self.tx, + &self.filename, + self.layout.clone(), + self.next_block, + end, + )?); + self.left.before_first()?; + self.left.next_row()?; + right.before_first()?; + self.right = Some(right); + self.next_block = end + 1; + Ok(true) + } + + pub(crate) fn before_first(&mut self) -> DbResult<()> { + self.next_block = 0; + self.use_next_chunk()?; + Ok(()) + } + + pub(crate) fn next(&mut self) -> DbResult { + loop { + if let Some(right) = self.right.as_mut() { + if right.next_row()? { + return Ok(true); + } + right.before_first()?; + if right.next_row()? && self.left.next_row()? { + return Ok(true); + } + } + if !self.use_next_chunk()? { + return Ok(false); + } + } + } + + pub(crate) fn get_i32(&self, field_name: &Element) -> DbResult { + if self.left.has_field(field_name)? { + self.left.get_i32(field_name) + } else { + self.right_scan()?.get_i32(field_name) + } + } + + pub(crate) fn get_string(&self, field_name: &Element) -> DbResult { + if self.left.has_field(field_name)? { + self.left.get_string(field_name) + } else { + self.right_scan()?.get_string(field_name) + } + } + + pub(crate) fn get_val(&self, field_name: &Element) -> DbResult { + if self.left.has_field(field_name)? { + self.left.get_val(field_name) + } else { + self.right_scan()?.get_val(field_name) + } + } + + pub(crate) fn has_field(&self, field_name: &Element) -> DbResult { + if self.left.has_field(field_name)? { + return Ok(true); + } + match &self.right { + Some(right) => right.has_field(field_name), + None => Ok(false), + } + } + + pub(crate) fn close(&self) -> DbResult<()> { + self.left.close()?; + if let Some(right) = &self.right { + right.close()?; + } + Ok(()) + } + + pub(crate) fn schema(&self) -> DbResult { + let mut builder = SchemaBuilder::default().add_all(&self.left.schema()?); + if let Some(right) = &self.right { + builder = builder.add_all(&right.schema()?); + } + Ok(builder.build()) + } + + fn right_scan(&self) -> DbResult<&Scan> { + self.right + .as_deref() + .ok_or_else(|| DbError::other("multibuffer right chunk not open")) + } +} diff --git a/planner/src/scan/order.rs b/planner/src/scan/order.rs new file mode 100644 index 0000000..4f9811b --- /dev/null +++ b/planner/src/scan/order.rs @@ -0,0 +1,189 @@ +use common::{DbResult, error::DbError}; +use std::cmp::Ordering; + +use crate::element::Element; +use crate::rid::RID; +use crate::scan::Scan; +use crate::schema::{Schema, SchemaBuilder}; +use crate::temp::TempTable; +use crate::value::Value; + +#[derive(Clone)] +pub struct RecordComparator { + fields: Vec, +} + +impl RecordComparator { + pub fn new(fields: Vec) -> Self { + Self { fields } + } + + pub fn compare(&self, s1: &Scan, s2: &Scan) -> DbResult { + for field in &self.fields { + let val1 = s1.get_val(field)?; + let val2 = s2.get_val(field)?; + let result = val1.cmp(&val2); + if result != Ordering::Equal { + return Ok(result); + } + } + Ok(Ordering::Equal) + } +} + +enum CurrentScan { + None, + S1, + S2, +} + +pub struct SortScan { + s1: Box, + s2: Option>, + current_scan: CurrentScan, + comp: RecordComparator, + has_more1: bool, + has_more2: bool, + saved_position: Vec, +} + +impl SortScan { + fn new(runs: Vec, comp: RecordComparator) -> DbResult { + let mut s1 = Box::new(runs[0].open()?); + let has_more1 = s1.next_row()?; + let (s2, has_more2) = if runs.len() > 1 { + let mut s2 = Box::new(runs[1].open()?); + let has_more2 = s2.next_row()?; + (Some(s2), has_more2) + } else { + (None, false) + }; + Ok(Self { + comp, + has_more1, + has_more2, + s1, + s2, + current_scan: CurrentScan::None, + saved_position: vec![], + }) + } + + fn before_first(&mut self) -> DbResult<()> { + self.s1.before_first()?; + self.has_more1 = self.s1.next_row()?; + if let Some(s2) = self.s2.as_mut() { + s2.before_first()?; + self.has_more2 = s2.next_row()?; + } + Ok(()) + } + + fn next(&mut self) -> DbResult { + match self.current_scan { + CurrentScan::S1 => self.has_more1 = self.s1.next_row()?, + CurrentScan::S2 => { + if let Some(s2) = self.s2.as_mut() { + self.has_more2 = s2.next_row()?; + } + } + CurrentScan::None => {} + } + if !self.has_more1 && !self.has_more2 { + return Ok(false); + } + if self.has_more1 && self.has_more2 { + if let Some(s2) = self.s2.as_ref() { + if self.comp.compare(&self.s1, s2)? == Ordering::Less { + self.current_scan = CurrentScan::S1; + } else { + self.current_scan = CurrentScan::S2; + } + } else { + self.current_scan = CurrentScan::S1; + } + } else if self.has_more1 { + self.current_scan = CurrentScan::S1; + } else { + self.current_scan = CurrentScan::S2; + } + Ok(true) + } + + fn close(&self) -> DbResult<()> { + self.s1.close()?; + if let Some(s2) = &self.s2 { + s2.close()?; + } + Ok(()) + } + + fn get_val(&self, field_name: &Element) -> DbResult { + match self.current_scan { + CurrentScan::S1 => self.s1.get_val(field_name), + CurrentScan::S2 => self.right()?.get_val(field_name), + CurrentScan::None => Err(DbError::other("invalid scan")), + } + } + + fn get_i32(&self, field_name: &Element) -> DbResult { + match self.current_scan { + CurrentScan::S1 => self.s1.get_i32(field_name), + CurrentScan::S2 => self.right()?.get_i32(field_name), + CurrentScan::None => Err(DbError::other("invalid scan")), + } + } + + fn get_string(&self, field_name: &Element) -> DbResult { + match self.current_scan { + CurrentScan::S1 => self.s1.get_string(field_name), + CurrentScan::S2 => self.right()?.get_string(field_name), + CurrentScan::None => Err(DbError::other("invalid scan")), + } + } + + fn has_field(&self, field_name: &Element) -> DbResult { + match self.current_scan { + CurrentScan::S1 => self.s1.has_field(field_name), + CurrentScan::S2 => self.right()?.has_field(field_name), + CurrentScan::None => Err(DbError::other("invalid scan")), + } + } + + fn save_position(&mut self) -> DbResult<()> { + let rid1 = self.s1.get_rid()?; + if let Some(s2) = &self.s2 { + let rid2 = s2.get_rid()?; + self.saved_position = vec![rid1, rid2]; + } else { + self.saved_position = vec![rid1]; + } + Ok(()) + } + + fn restore_position(&mut self) -> DbResult<()> { + if let Some(rid1) = self.saved_position.first().cloned() { + self.s1.move_to_rid(rid1)?; + } + if let Some(rid2) = self.saved_position.get(1).cloned() { + if let Some(s2) = self.s2.as_mut() { + s2.move_to_rid(rid2)?; + } + } + Ok(()) + } + + fn schema(&self) -> DbResult { + let mut builder = SchemaBuilder::default().add_all(&self.s1.schema()?); + if let Some(s2) = self.s2.as_ref() { + builder = builder.add_all(&s2.schema()?); + } + Ok(builder.build()) + } + + fn right(&self) -> DbResult<&Scan> { + self.s2 + .as_deref() + .ok_or_else(|| DbError::other("invalid scan")) + } +} \ No newline at end of file diff --git a/planner/src/scan/product.rs b/planner/src/scan/product.rs new file mode 100644 index 0000000..966e1fb --- /dev/null +++ b/planner/src/scan/product.rs @@ -0,0 +1,74 @@ +use crate::element::Element; +use crate::scan::Scan; +use crate::schema::{Schema, SchemaBuilder}; +use crate::value::Value; +use common::DbResult; + +pub struct ProductScan { + s1: Box, + s2: Box, +} + +impl ProductScan { + pub fn new(mut s1: Box, s2: Box) -> DbResult { + s1.next_row()?; + Ok(Self { s1, s2 }) + } +} + +impl ProductScan { + pub(crate) fn before_first(&mut self) -> DbResult<()> { + self.s1.before_first()?; + self.s1.next_row()?; + self.s2.before_first() + } + + pub(crate) fn next_row(&mut self) -> DbResult { + if self.s2.next_row()? { + Ok(true) + } else { + self.s2.before_first()?; + Ok(self.s2.next_row()? && self.s1.next_row()?) + } + } + + pub(crate) fn get_i32(&self, field_name: &Element) -> DbResult { + if self.s1.has_field(field_name)? { + self.s1.get_i32(field_name) + } else { + self.s2.get_i32(field_name) + } + } + + pub(crate) fn get_string(&self, field_name: &Element) -> DbResult { + if self.s1.has_field(field_name)? { + self.s1.get_string(field_name) + } else { + self.s2.get_string(field_name) + } + } + + pub(crate) fn get_val(&self, field_name: &Element) -> DbResult { + if self.s1.has_field(field_name)? { + self.s1.get_val(field_name) + } else { + self.s2.get_val(field_name) + } + } + + pub(crate) fn has_field(&self, field_name: &Element) -> DbResult { + Ok(self.s1.has_field(field_name)? || self.s2.has_field(field_name)?) + } + + pub(crate) fn close(&self) -> DbResult<()> { + self.s1.close()?; + self.s2.close() + } + + pub(crate) fn schema(&self) -> DbResult { + let s1 = self.s1.schema()?; + let s2 = self.s2.schema()?; + let s = SchemaBuilder::default().add_all(&s1).add_all(&s2).build(); + Ok(s) + } +} diff --git a/planner/src/scan/project.rs b/planner/src/scan/project.rs new file mode 100644 index 0000000..aac89ec --- /dev/null +++ b/planner/src/scan/project.rs @@ -0,0 +1,71 @@ +use crate::element::Element; +use crate::scan::Scan; +use crate::schema::{Schema, SchemaBuilder}; +use crate::value::Value; +use common::DbResult; +use common::error::DbError; +use std::collections::HashSet; + +pub struct ProjectScan { + scan: Box, + fields: HashSet, +} + +impl ProjectScan { + pub fn new(scan: Box, fields: HashSet) -> Self { + Self { scan, fields } + } +} + +impl ProjectScan { + pub(crate) fn before_first(&mut self) -> DbResult<()> { + self.scan.before_first() + } + + pub(crate) fn next(&mut self) -> DbResult { + self.scan.next_row() + } + + pub(crate) fn get_i32(&self, field_name: &Element) -> DbResult { + if self.has_field(field_name)? { + self.scan.get_i32(field_name) + } else { + Err(DbError::FieldNotExists(field_name.to_string())) + } + } + + pub(crate) fn get_string(&self, field_name: &Element) -> DbResult { + if self.has_field(field_name)? { + self.scan.get_string(field_name) + } else { + Err(DbError::FieldNotExists(field_name.to_string())) + } + } + + pub(crate) fn get_val(&self, field_name: &Element) -> DbResult { + if self.has_field(field_name)? { + self.scan.get_val(field_name) + } else { + Err(DbError::FieldNotExists(field_name.to_string())) + } + } + + pub(crate) fn has_field(&self, field_name: &Element) -> DbResult { + Ok(self.fields.contains(field_name)) + } + + pub(crate) fn close(&self) -> DbResult<()> { + self.scan.close() + } + + pub(crate) fn schema(&self) -> DbResult { + let mut project = SchemaBuilder::default(); + let schema = self.scan.schema()?; + for (field, info) in schema.fields() { + if self.fields.contains(&field) { + project = project.add_field(field, info); + } + } + Ok(project.build()) + } +} diff --git a/planner/src/scan/scanner.rs b/planner/src/scan/scanner.rs index 9b84043..37f1c8c 100644 --- a/planner/src/scan/scanner.rs +++ b/planner/src/scan/scanner.rs @@ -7,10 +7,14 @@ use crate::{ schema::Schema, value::Value, }; +use crate::scan::chunk::ChunkScan; +use crate::scan::product::ProductScan; pub(crate) enum Scanner { Table(TableScan), Select(SelectScan), + Chunk(ChunkScan), + Product(ProductScan), } impl Scanner { @@ -18,6 +22,11 @@ impl Scanner { match self { Self::Table(table) => table.before_first(), Self::Select(select) => select.before_first(), + Self::Chunk(chunk) => { + chunk.before_first(); + Ok(()) + } + Self::Product(product) => product.before_first(), } } @@ -25,6 +34,8 @@ impl Scanner { match self { Self::Table(table) => table.next(), Self::Select(select) => select.next(), + Self::Chunk(chunk) => chunk.next(), + Self::Product(product) => product.next_row(), } } @@ -32,6 +43,8 @@ impl Scanner { match self { Self::Table(table) => table.get_i32(field), Self::Select(select) => select.get_i32(field), + Self::Chunk(chunk) => chunk.get_i32(field), + Self::Product(product) => product.get_i32(field), } } @@ -39,6 +52,8 @@ impl Scanner { match self { Self::Table(table) => table.get_string(field), Self::Select(select) => select.get_string(field), + Self::Chunk(chunk) => chunk.get_string(field), + Self::Product(product) => product.get_string(field), } } @@ -46,6 +61,8 @@ impl Scanner { match self { Self::Table(table) => table.get_val(field), Self::Select(select) => select.get_val(field), + Self::Chunk(chunk) => chunk.get_val(field), + Self::Product(product) => product.get_val(field), } } @@ -53,6 +70,8 @@ impl Scanner { match self { Self::Table(table) => Ok(table.has_field(field)), Self::Select(select) => select.has_field(field), + Self::Chunk(chunk) => Ok(chunk.has_field(field)), + Self::Product(product) => product.has_field(field), } } @@ -60,6 +79,8 @@ impl Scanner { match self { Self::Table(table) => table.close(), Self::Select(select) => select.close(), + Self::Chunk(chunk) => chunk.close(), + Self::Product(product) => product.close(), } } @@ -67,6 +88,8 @@ impl Scanner { match self { Self::Table(table) => Ok(table.schema().clone()), Self::Select(select) => select.schema(), + Self::Chunk(chunk) => Ok(chunk.schema()), + Self::Product(product) => product.schema(), } } @@ -74,6 +97,7 @@ impl Scanner { match self { Self::Table(table) => table.set_i32(field, value), Self::Select(select) => select.set_i32(field, value), + Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), } } @@ -81,6 +105,7 @@ impl Scanner { match self { Self::Table(table) => table.set_string(field, value), Self::Select(select) => select.set_string(field, value), + Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), } } @@ -88,6 +113,7 @@ impl Scanner { match self { Self::Table(table) => table.set_val(field, value), Self::Select(select) => select.set_val(field, value), + Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), } } @@ -95,6 +121,7 @@ impl Scanner { match self { Self::Table(table) => table.insert(), Self::Select(select) => select.insert(), + Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), } } @@ -102,6 +129,7 @@ impl Scanner { match self { Self::Table(table) => table.delete(), Self::Select(select) => select.delete(), + Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), } } @@ -109,17 +137,23 @@ impl Scanner { match self { Self::Table(table) => Ok(table.get_rid()), Self::Select(select) => select.get_rid(), + Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), } } pub fn move_to_rid(&mut self, rid: RID) -> DbResult<()> { match self { Self::Table(table) => table.move_to_rid(rid), - Self::Select(table) => table.move_to_rid(rid), + Self::Select(select) => select.move_to_rid(rid), + Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), } } pub fn save_position(&self) -> DbResult<()> { Err(DbError::other("cannot save position")) } + + fn read_only() -> DbError { + DbError::other("scan does not support record modification") + } } diff --git a/planner/src/temp.rs b/planner/src/temp.rs new file mode 100644 index 0000000..17683c4 --- /dev/null +++ b/planner/src/temp.rs @@ -0,0 +1,42 @@ +use crate::layout::Layout; +use crate::scan::Scan; +use crate::schema::Schema; +use common::DbResult; +use std::sync::atomic::AtomicI32; +use std::sync::{Arc, atomic}; +use transaction::transaction::Transaction; + +static TABLE_NUM_COUNTER: AtomicI32 = AtomicI32::new(0); + +fn next_table_num() -> i32 { + TABLE_NUM_COUNTER.fetch_add(1, atomic::Ordering::SeqCst) +} + +#[derive(Clone)] +pub struct TempTable { + tx: Arc, + table_name: String, + layout: Layout, +} + +impl TempTable { + pub fn new(tx: &Arc, schema: Schema) -> DbResult { + Ok(Self { + table_name: format!("temp_{}", next_table_num()), + tx: Arc::clone(tx), + layout: Layout::new(schema), + }) + } + + pub fn open(&self) -> DbResult { + Scan::table(&self.tx, &self.table_name, self.layout.clone()) + } + + pub(crate) fn table_name(&self) -> String { + self.table_name.clone() + } + + pub(crate) fn layout(&self) -> Layout { + self.layout.clone() + } +} From c5c84976a200bdb2fd6a2a387808b4ca19ad4327 Mon Sep 17 00:00:00 2001 From: Daniil Buravlev Date: Tue, 30 Jun 2026 21:30:33 +0300 Subject: [PATCH 09/13] Break ALL --- Cargo.lock | 1 + engine/Cargo.toml | 1 + engine/src/buffer_needs.rs | 31 --- engine/src/constant.rs | 66 ----- engine/src/element.rs | 28 --- engine/src/field_info.rs | 44 ---- engine/src/index.rs | 64 ----- engine/src/index/b_tree.rs | 262 -------------------- engine/src/index/btree_dir.rs | 124 ---------- engine/src/index/btree_leaf.rs | 173 ------------- engine/src/index/btree_page.rs | 342 -------------------------- engine/src/index/dir_entry.rs | 12 - engine/src/index_mgr.rs | 154 ------------ engine/src/layout.rs | 52 ---- engine/src/lib.rs | 41 +-- engine/src/metadata_mgr.rs | 95 ------- engine/src/{sort_by.rs => order.rs} | 6 +- engine/src/plan.rs | 28 --- engine/src/plan/group.rs | 77 ------ engine/src/plan/index.rs | 225 ----------------- engine/src/plan/materialize.rs | 59 ----- engine/src/plan/merge.rs | 77 ------ engine/src/plan/multibuffer.rs | 92 ------- engine/src/plan/product.rs | 54 ---- engine/src/plan/project.rs | 59 ----- engine/src/plan/select.rs | 54 ---- engine/src/plan/sort.rs | 139 ----------- engine/src/plan/table.rs | 59 ----- engine/src/predicate.rs | 245 ------------------ engine/src/query/basic_planner.rs | 37 ++- engine/src/query/command.rs | 30 +-- engine/src/query/heuristic_planner.rs | 53 ++-- engine/src/query/parser.rs | 2 +- engine/src/query/table_planner.rs | 32 +-- engine/src/query/token.rs | 9 +- engine/src/record_page.rs | 194 --------------- engine/src/rid.rs | 22 -- engine/src/scan.rs | 66 ----- engine/src/scan/chunk.rs | 180 -------------- engine/src/scan/group.rs | 223 ----------------- engine/src/scan/index.rs | 234 ------------------ engine/src/scan/merge.rs | 170 ------------- engine/src/scan/multibuffer.rs | 204 --------------- engine/src/scan/product.rs | 75 ------ engine/src/scan/project.rs | 70 ------ engine/src/scan/select.rs | 84 ------- engine/src/scan/sort.rs | 245 ------------------ engine/src/scan/table.rs | 317 ------------------------ engine/src/schema.rs | 123 --------- engine/src/stat_mgr.rs | 131 ---------- engine/src/table_mgr.rs | 143 ----------- engine/src/temp.rs | 48 ---- engine/src/view_mgr.rs | 58 ----- planner/src/element.rs | 10 + planner/src/plan.rs | 5 + planner/src/plan/project.rs | 55 +++++ planner/src/scan.rs | 11 +- planner/src/scan/project.rs | 2 +- planner/src/scan/scanner.rs | 24 +- 59 files changed, 174 insertions(+), 5347 deletions(-) delete mode 100644 engine/src/buffer_needs.rs delete mode 100644 engine/src/constant.rs delete mode 100644 engine/src/element.rs delete mode 100644 engine/src/field_info.rs delete mode 100644 engine/src/index.rs delete mode 100644 engine/src/index/b_tree.rs delete mode 100644 engine/src/index/btree_dir.rs delete mode 100644 engine/src/index/btree_leaf.rs delete mode 100644 engine/src/index/btree_page.rs delete mode 100644 engine/src/index/dir_entry.rs delete mode 100644 engine/src/index_mgr.rs delete mode 100644 engine/src/layout.rs delete mode 100644 engine/src/metadata_mgr.rs rename engine/src/{sort_by.rs => order.rs} (84%) delete mode 100644 engine/src/plan.rs delete mode 100644 engine/src/plan/group.rs delete mode 100644 engine/src/plan/index.rs delete mode 100644 engine/src/plan/materialize.rs delete mode 100644 engine/src/plan/merge.rs delete mode 100644 engine/src/plan/multibuffer.rs delete mode 100644 engine/src/plan/product.rs delete mode 100644 engine/src/plan/project.rs delete mode 100644 engine/src/plan/select.rs delete mode 100644 engine/src/plan/sort.rs delete mode 100644 engine/src/plan/table.rs delete mode 100644 engine/src/predicate.rs delete mode 100644 engine/src/record_page.rs delete mode 100644 engine/src/rid.rs delete mode 100644 engine/src/scan.rs delete mode 100644 engine/src/scan/chunk.rs delete mode 100644 engine/src/scan/group.rs delete mode 100644 engine/src/scan/index.rs delete mode 100644 engine/src/scan/merge.rs delete mode 100644 engine/src/scan/multibuffer.rs delete mode 100644 engine/src/scan/product.rs delete mode 100644 engine/src/scan/project.rs delete mode 100644 engine/src/scan/select.rs delete mode 100644 engine/src/scan/sort.rs delete mode 100644 engine/src/scan/table.rs delete mode 100644 engine/src/schema.rs delete mode 100644 engine/src/stat_mgr.rs delete mode 100644 engine/src/table_mgr.rs delete mode 100644 engine/src/temp.rs delete mode 100644 engine/src/view_mgr.rs create mode 100644 planner/src/plan/project.rs diff --git a/Cargo.lock b/Cargo.lock index 6c60576..73fdc6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -325,6 +325,7 @@ dependencies = [ "criterion", "file", "log 0.0.1", + "planner", "rand", "tempfile", "tracing", diff --git a/engine/Cargo.toml b/engine/Cargo.toml index dc37d1c..93353b0 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -15,6 +15,7 @@ file = { path = "../file" } transaction = { path = "../transaction" } tracing = { workspace = true } rand = { workspace = true } +planner = { path = "../planner" } [dev-dependencies] criterion = { workspace = true } diff --git a/engine/src/buffer_needs.rs b/engine/src/buffer_needs.rs deleted file mode 100644 index 54ca664..0000000 --- a/engine/src/buffer_needs.rs +++ /dev/null @@ -1,31 +0,0 @@ -pub struct BufferNeeds {} - -impl BufferNeeds { - pub fn best_root(available: i32, size: i32) -> i32 { - let available = available - 2; - if available <= 1 { - return 1; - } - let mut k = i32::MAX; - let mut i = 1f32; - while k > available { - i += 1f32; - k = (size as f32).powf(1.0 / i).ceil() as i32; - } - k - } - - pub fn best_factor(availabe: i32, size: i32) -> i32 { - let available = availabe - 2; - if available <= 1 { - return 1; - } - let mut k = size; - let mut i: f32 = 1.0; - while k > available { - i += 1f32; - k = (size as f32 / i).ceil() as i32; - } - k - } -} 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 deleted file mode 100644 index 66a104b..0000000 --- a/engine/src/element.rs +++ /dev/null @@ -1,28 +0,0 @@ -use common::{DbResult, error::DbError}; - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(crate) enum Element { - Raw(String), - Id { source: String, name: String }, - Specifier { source: String, spec: String }, -} - -impl Element { - pub(crate) fn source(&self) -> DbResult<&str> { - match self { - Self::Raw(value) => Ok(value), - Self::Specifier { source, .. } => Ok(source), - e => Err(DbError::UnexpectedToken(e.to_string())), - } - } -} - -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::Id { source, name } => write!(f, "{} {}", source, name), - Self::Specifier { source, spec } => write!(f, "{}.{}", source, spec), - } - } -} diff --git a/engine/src/field_info.rs b/engine/src/field_info.rs deleted file mode 100644 index a3e03ec..0000000 --- a/engine/src/field_info.rs +++ /dev/null @@ -1,44 +0,0 @@ -use common::{DbResult, error::DbError}; -use file::page::I32_SIZE; - -const INTEGER_TYPE: i32 = 1; -const VARCHAR_TYPE: i32 = 2; - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum FieldInfo { - Integer, - Varchar(i32), -} - -impl FieldInfo { - pub fn new(field_type: i32, len: i32) -> DbResult { - let info = match field_type { - INTEGER_TYPE => Self::Integer, - VARCHAR_TYPE => Self::Varchar((len - I32_SIZE as i32) / 4), - _ => return Err(DbError::UnknownType), - }; - Ok(info) - } - - pub fn type_id(&self) -> i32 { - match self { - Self::Integer => INTEGER_TYPE, - Self::Varchar(_) => VARCHAR_TYPE, - } - } - pub fn length(&self) -> i32 { - match self { - Self::Integer => I32_SIZE as i32, - Self::Varchar(len) => I32_SIZE as i32 + *len * 4, - } - } -} - -impl std::fmt::Display for FieldInfo { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Integer => write!(f, "INTEGER"), - Self::Varchar(len) => write!(f, "VARCHAR({})", len), - } - } -} diff --git a/engine/src/index.rs b/engine/src/index.rs deleted file mode 100644 index d7ebc25..0000000 --- a/engine/src/index.rs +++ /dev/null @@ -1,64 +0,0 @@ -use common::DbResult; - -use crate::{constant::Constant, rid::RID}; - -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 next(&self) -> DbResult; - - fn get_data_rid(&self) -> DbResult; - - fn insert(&self, value: Constant, rid: RID) -> DbResult<()>; - - fn delete(&self, value: Constant, rid: RID) -> DbResult<()>; - - fn close(&self) -> DbResult<()>; -} - -#[cfg(test)] -mod tests { - use tempfile::tempdir; - - use crate::{ - SimpleDB, - plan::{Plan, table::TablePlan}, - }; - - #[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 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(); - - md.create_table(table, &schema, &setup_tx).unwrap(); - md.create_index("idx_majorid", table, "majorid", &setup_tx) - .unwrap(); - setup_tx.commit().unwrap(); - - let tx = db.get_tx().unwrap(); - let plan = TablePlan::new(&tx, table.to_string(), &md).unwrap(); - let _scan = plan.open().unwrap(); - - let indexes = md.get_index_info(table, &tx).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 deleted file mode 100644 index 8bc9c82..0000000 --- a/engine/src/index/b_tree.rs +++ /dev/null @@ -1,262 +0,0 @@ -use std::sync::{Arc, RwLock}; - -use common::{DbResult, error::DbError}; -use file::block::BlockId; -use transaction::transaction::Transaction; - -use crate::{ - constant::Constant, - field_info::FieldInfo, - index::{ - Index, - btree_dir::BTreeDir, - btree_leaf::BTreeLeaf, - btree_page::{BLOCK, BTreePage, VALUE}, - }, - layout::Layout, - schema::Schema, -}; - -struct BTreeIndexLock { - tx: Arc, - dir_layout: Arc, - leaf_layout: Arc, - leaf_table: String, - leaf: Option, - root_block: BlockId, -} - -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()?; - } - Ok(Self { - tx: Arc::clone(tx), - dir_layout, - leaf_layout: Arc::clone(leaf_layout), - leaf_table, - leaf: None, - root_block, - }) - } - - 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 next(&self) -> DbResult { - if let Some(leaf) = &self.leaf { - leaf.next() - } else { - Ok(false) - } - } - - fn get_data_rid(&self) -> DbResult { - if let Some(leaf) = &self.leaf { - leaf.get_data_rid() - } else { - Err(DbError::other("cannot find RID")) - } - } - - 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)?; - } - 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)?; - } - Ok(()) - } - - fn close(&self) -> DbResult<()> { - if let Some(leaf) = &self.leaf { - leaf.close()?; - } - Ok(()) - } -} - -pub struct BTreeIndex { - lock: RwLock, -} - -impl BTreeIndex { - pub fn new(tx: &Arc, name: &str, leaf_layout: &Arc) -> DbResult { - Ok(Self { - lock: RwLock::new(BTreeIndexLock::new(tx, name, leaf_layout)?), - }) - } -} - -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 next(&self) -> DbResult { - let read = self.lock.read().map_err(DbError::lock)?; - read.next() - } - - fn get_data_rid(&self) -> DbResult { - let read = self.lock.read().map_err(DbError::lock)?; - read.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 delete(&self, value: Constant, rid: crate::rid::RID) -> DbResult<()> { - let mut write = self.lock.write().map_err(DbError::lock)?; - write.delete(value, rid) - } - - fn close(&self) -> DbResult<()> { - let read = self.lock.read().map_err(DbError::lock)?; - read.close() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::SimpleDB; - use crate::rid::RID; - use tempfile::tempdir; - - #[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()); - } - - #[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(); - - index.before_first(Constant::varchar("hello")).unwrap(); - assert!(!index.next().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(); - } - } -} 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_mgr.rs b/engine/src/index_mgr.rs deleted file mode 100644 index 27de484..0000000 --- a/engine/src/index_mgr.rs +++ /dev/null @@ -1,154 +0,0 @@ -use std::{collections::HashMap, rc::Rc, sync::Arc}; - -use common::DbResult; -use common::error::DbError; -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, -}; - -const IDX_TABLE: &str = "idx"; -const IDX_NAME: &str = "idx_name"; -const TABLE_NAME: &str = "table_name"; -const FIELD_NAME: &str = "field_name"; -const MAX_LENGTH: i32 = 16; - -#[derive(Clone)] -pub struct IndexInfo { - idx_name: String, - field_name: String, - tx: Arc, - layout: Arc, - stat: StatInfo, -} - -impl IndexInfo { - pub fn new( - idx_name: String, - field_name: String, - schema: &Arc, - tx: &Arc, - stat: StatInfo, - ) -> DbResult { - let layout = Arc::new(Self::create_idx_layout(&field_name, schema)?); - Ok(Self { - idx_name, - 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, - )?)) - } - - pub fn block_accessed(&self) -> DbResult { - let rpb = self.tx.block_size() / self.layout.slotsize(); - let num_blocks = self.stat.records_output() / rpb; - Ok(num_blocks) - } - - pub fn records_output(&self) -> i32 { - self.stat.records_output() / self.stat.distinct_values() - } - - pub fn distinct_values(&self, field_name: &str) -> i32 { - if self.field_name == field_name { - 1 - } else { - self.stat.distinct_values() - } - } -} - -pub struct IndexMgr { - layout: Arc, - table_mgr: Arc, - stat_mgr: Arc, -} - -impl IndexMgr { - pub fn new( - is_new: bool, - table_mgr: &Arc, - stat_mgr: &Arc, - 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 layout = Arc::new(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), - }) - } - - pub fn create_index( - &self, - idx_name: &str, - table_name: &str, - field_name: &str, - tx: &Arc, - ) -> DbResult<()> { - let ts = TableScan::new(tx, IDX_TABLE, &self.layout)?; - ts.insert()?; - ts.set_string(IDX_NAME, idx_name)?; - ts.set_string(TABLE_NAME, table_name)?; - ts.set_string(FIELD_NAME, field_name)?; - ts.close() - } - - pub fn get_index_info( - &self, - table_name: &str, - tx: &Arc, - ) -> DbResult> { - let mut result = HashMap::new(); - let ts = TableScan::new(tx, IDX_TABLE, &self.layout)?; - 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); - } - } - ts.close()?; - Ok(result) - } -} diff --git a/engine/src/layout.rs b/engine/src/layout.rs deleted file mode 100644 index 00f7d3d..0000000 --- a/engine/src/layout.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::{collections::HashMap, sync::Arc}; - -use common::DbResult; -use file::page::I32_SIZE; - -use crate::schema::Schema; - -pub struct Layout { - schema: Arc, - offsets: HashMap, - slotsize: i32, -} - -impl Layout { - pub fn new(schema: &Arc) -> DbResult { - let mut offsets = HashMap::new(); - let mut pos = I32_SIZE as i32; - for (field, info) in schema.fields()? { - offsets.insert(field, pos); - pos += info.length(); - } - Ok(Self { - schema: Arc::clone(schema), - offsets, - slotsize: pos, - }) - } - - pub fn from(schema: &Arc, offsets: HashMap, slotsize: i32) -> Self { - Self { - schema: Arc::clone(schema), - offsets, - slotsize, - } - } - - pub fn offset(&self, fieldname: &str) -> i32 { - if let Some(offset) = self.offsets.get(fieldname) { - *offset - } else { - 0 - } - } - - pub fn schema(&self) -> Arc { - Arc::clone(&self.schema) - } - - pub fn slotsize(&self) -> i32 { - self.slotsize - } -} diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 8db3cbd..f992a21 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -4,37 +4,16 @@ use buffer::mgr::BufferMgr; use common::DbResult; use file::mgr::FileMgr; use log::mgr::LogMgr; +use planner::mgr::metadata::MetadataMgr; +use planner::scan::Scan; use transaction::{lock_table::LockTable, transaction::Transaction}; -use crate::{ - metadata_mgr::MetadataMgr, - query::{ - basic_planner::BasicUpdatePlanner, heuristic_planner::HeuristicQueryPlanner, - planner::Planner, - }, - scan::Scan, +use crate::query::{ + basic_planner::BasicUpdatePlanner, heuristic_planner::HeuristicQueryPlanner, planner::Planner, }; -pub mod buffer_needs; -pub mod constant; -pub(crate) mod element; -pub mod field_info; -pub mod index; -pub mod index_mgr; -pub mod layout; -pub mod metadata_mgr; -pub mod plan; -pub mod predicate; pub mod query; -pub mod record_page; -pub mod rid; -pub mod scan; -pub mod schema; -pub mod sort_by; -pub mod stat_mgr; -pub mod table_mgr; -mod temp; -pub mod view_mgr; +mod order; const LOG_FILE: &str = "wal.log"; const BLOCK_SIZE: usize = 8 * 1024; @@ -45,7 +24,7 @@ pub struct SimpleDB { lm: Arc, bm: Arc, lock_table: Arc, - md: Arc, + md: MetadataMgr, } impl SimpleDB { @@ -66,7 +45,7 @@ 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 { fm, @@ -82,11 +61,11 @@ impl SimpleDB { 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> { + pub fn query(&self, tx: &Arc, query: &str) -> DbResult { let planner = self.planner(); let plan = planner.create_query_plan(query, tx)?; plan.open() diff --git a/engine/src/metadata_mgr.rs b/engine/src/metadata_mgr.rs deleted file mode 100644 index e401a2e..0000000 --- a/engine/src/metadata_mgr.rs +++ /dev/null @@ -1,95 +0,0 @@ -use std::{collections::HashMap, sync::Arc}; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::{ - index_mgr::{IndexInfo, IndexMgr}, - layout::Layout, - schema::Schema, - stat_mgr::{StatInfo, StatMgr}, - table_mgr::TableMgr, - view_mgr::ViewMgr, -}; - -pub struct MetadataMgr { - table_mgr: Arc, - view_mgr: Arc, - stat_mgr: Arc, - index_mgr: Arc, -} - -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)?); - Ok(Self { - table_mgr, - view_mgr, - stat_mgr, - index_mgr, - }) - } - - pub fn create_table( - &self, - table_name: &str, - schema: &Arc, - tx: &Arc, - ) -> DbResult<()> { - self.table_mgr.create_table(table_name, schema, tx) - } - - pub fn get_layout(&self, table_name: &str, tx: &Arc) -> DbResult { - self.table_mgr.get_layout(table_name, tx) - } - - pub fn create_view( - &self, - view_name: &str, - view_def: &str, - tx: &Arc, - ) -> DbResult<()> { - self.view_mgr.create_view(view_name, view_def, tx) - } - - pub fn get_view_def(&self, view_name: &str, tx: &Arc) -> DbResult> { - self.view_mgr.get_view_def(view_name, tx) - } - - pub fn create_index( - &self, - idx_name: &str, - table_name: &str, - field_name: &str, - tx: &Arc, - ) -> DbResult<()> { - self.index_mgr - .create_index(idx_name, table_name, field_name, tx) - } - - pub fn get_stat_info( - &self, - table: &str, - layout: &Arc, - tx: &Arc, - ) -> DbResult { - self.stat_mgr.get_stat_info(table, layout, tx) - } - - pub fn get_index_info( - &self, - table_name: &str, - tx: &Arc, - ) -> DbResult> { - self.index_mgr.get_index_info(table_name, tx) - } -} - -#[cfg(test)] -mod tests { - #[test] - fn metadata() {} -} diff --git a/engine/src/sort_by.rs b/engine/src/order.rs similarity index 84% rename from engine/src/sort_by.rs rename to engine/src/order.rs index 972a424..64f9760 100644 --- a/engine/src/sort_by.rs +++ b/engine/src/order.rs @@ -1,15 +1,15 @@ #[derive(Debug, Default)] -pub struct SortByData { +pub struct OrderByData { pub fields: Vec, } -impl SortByData { +impl OrderByData { pub fn is_empty(&self) -> bool { self.fields.is_empty() } } -impl std::fmt::Display for SortByData { +impl std::fmt::Display for OrderByData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "SORT BY")?; for (i, field) in self.fields.iter().enumerate() { diff --git a/engine/src/plan.rs b/engine/src/plan.rs deleted file mode 100644 index a81fca9..0000000 --- a/engine/src/plan.rs +++ /dev/null @@ -1,28 +0,0 @@ -use std::{rc::Rc, sync::Arc}; - -use common::DbResult; - -use crate::{scan::Scan, schema::Schema}; - -pub(crate) mod group; -pub mod index; -pub(crate) mod materialize; -pub(crate) mod merge; -pub(crate) mod multibuffer; -pub mod product; -pub mod project; -pub mod select; -pub(crate) mod sort; -pub mod table; - -pub trait Plan { - fn open(&self) -> DbResult>; - - fn blocks_accessed(&self) -> DbResult; - - fn records_output(&self) -> DbResult; - - fn distinct_values(&self, field_name: &str) -> DbResult; - - fn schema(&self) -> DbResult>; -} diff --git a/engine/src/plan/group.rs b/engine/src/plan/group.rs deleted file mode 100644 index c5d2a77..0000000 --- a/engine/src/plan/group.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::{rc::Rc, sync::Arc}; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::{ - plan::{Plan, sort::SortPlan}, - scan::group::{AggregationFn, GroupByScan}, - schema::Schema, -}; - -pub struct GroupByPlan { - plan: Rc, - group_fields: Vec, - aggregation_fn: Vec, - schema: Arc, -} - -impl GroupByPlan { - pub fn new( - tx: &Arc, - plan: &Rc, - group_fields: Vec, - aggregation_fn: Vec, - ) -> DbResult { - let schema = Arc::new(Schema::default()); - let s = plan.schema()?; - for field in &group_fields { - schema.add(field.to_string(), &s)?; - } - for f in &aggregation_fn { - schema.add_int_field(f.field_name())?; - } - let plan = Rc::new(SortPlan::new(tx, plan, group_fields.clone())?); - Ok(Self { - plan, - group_fields, - aggregation_fn, - schema, - }) - } -} - -impl Plan for GroupByPlan { - fn open(&self) -> DbResult> { - let s = self.plan.open()?; - Ok(Rc::new(GroupByScan::new( - &s, - self.group_fields.clone(), - self.aggregation_fn.clone(), - )?)) - } - - fn blocks_accessed(&self) -> DbResult { - self.plan.blocks_accessed() - } - - fn records_output(&self) -> DbResult { - let mut num_groups = 1; - for field in &self.group_fields { - num_groups *= self.plan.distinct_values(field)?; - } - Ok(num_groups) - } - - fn distinct_values(&self, field_name: &str) -> 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)) - } -} diff --git a/engine/src/plan/index.rs b/engine/src/plan/index.rs deleted file mode 100644 index 38bf056..0000000 --- a/engine/src/plan/index.rs +++ /dev/null @@ -1,225 +0,0 @@ -use std::{rc::Rc, sync::Arc}; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::{ - constant::Constant, - index_mgr::IndexInfo, - metadata_mgr::MetadataMgr, - plan::{Plan, select::SelectPlan, table::TablePlan}, - query::{ - command::{DeleteData, IndexData, InsertData, TableData, UpdateData, ViewData}, - planner::UpdatePlanner, - }, - scan::index::{IndexJoinScan, IndexSelectScan}, - schema::Schema, -}; - -pub struct IndexSelectPlan { - plan: Rc, - index: IndexInfo, - value: Constant, -} - -impl IndexSelectPlan { - pub fn new(plan: &Rc, index: IndexInfo, value: Constant) -> Self { - Self { - plan: Rc::clone(plan), - index, - value, - } - } -} - -impl Plan for IndexSelectPlan { - fn open(&self) -> DbResult> { - let ts = self.plan.open()?; - let index = self.index.open()?; - Ok(Rc::new(IndexSelectScan::new( - &ts, - &index, - self.value.clone(), - )?)) - } - - fn blocks_accessed(&self) -> DbResult { - Ok(self.index.block_accessed()? + self.index.records_output()) - } - - fn records_output(&self) -> DbResult { - Ok(self.index.records_output()) - } - - fn distinct_values(&self, field_name: &str) -> DbResult { - Ok(self.index.distinct_values(field_name)) - } - - fn schema(&self) -> DbResult> { - self.plan.schema() - } -} - -pub struct IndexJoinPlan { - p1: Rc, - p2: Rc, - index: IndexInfo, - field: String, - schema: Arc, -} - -impl IndexJoinPlan { - pub fn new( - p1: &Rc, - p2: &Rc, - index: IndexInfo, - field: String, - ) -> DbResult { - let schema = Arc::new(Schema::default()); - let s1 = p1.schema()?; - let s2 = p2.schema()?; - schema.add_all(&s1)?; - schema.add_all(&s2)?; - Ok(Self { - p1: Rc::clone(p1), - p2: Rc::clone(p2), - index, - field, - schema, - }) - } -} - -impl Plan for IndexJoinPlan { - fn open(&self) -> DbResult> { - 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)?)) - } - - fn blocks_accessed(&self) -> DbResult { - Ok(self.p1.blocks_accessed()? - + (self.p1.records_output()? * self.index.block_accessed()?) - + self.records_output()?) - } - - fn records_output(&self) -> DbResult { - 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)? { - self.p1.distinct_values(field_name) - } else { - self.p2.distinct_values(field_name) - } - } - - fn schema(&self) -> DbResult> { - Ok(Arc::clone(&self.schema)) - } -} - -pub struct IndexUpdatePlanner { - mg: Arc, -} - -impl UpdatePlanner for IndexUpdatePlanner { - fn execute_insert(&self, data: InsertData, tx: &Arc) -> DbResult { - let table = data.table; - let indexes = self.mg.get_index_info(&table, tx)?; - let plan = TablePlan::new(tx, table, &self.mg)?; - - let s = plan.open()?; - s.insert()?; - let rid = s.get_rid()?; - - for (field, value) in data.fields.iter().zip(data.values) { - tracing::debug!("Modify field: {} {}", field, value); - s.set_val(field, value.clone())?; - - if let Some(info) = indexes.get(field) { - let index = info.open()?; - index.insert(value, rid.clone())?; - index.close()?; - } - } - s.close()?; - Ok(1) - } - - fn execute_update(&self, data: UpdateData, tx: &Arc) -> DbResult { - let table = data.table; - let field = data.field; - let index = if let Some(info) = self.mg.get_index_info(&table, tx)?.get(&field) { - Some(info.open()?) - } else { - None - }; - let plan = TablePlan::new(tx, table, &self.mg)?; - let plan = SelectPlan::new(Rc::new(plan), data.predicate); - let s = plan.open()?; - let mut count = 0; - while s.next()? { - let new_val = data.value.evaluate(&s)?; - let oldval = s.get_val(&field)?; - s.set_val(&field, new_val.clone())?; - if let Some(index) = &index { - let rid = s.get_rid()?; - index.delete(oldval, rid.clone())?; - index.delete(new_val, rid.clone())?; - } - count += 1; - } - if let Some(index) = index { - index.close()?; - } - s.close()?; - Ok(count) - } - - fn execute_delete(&self, data: DeleteData, tx: &Arc) -> DbResult { - let table = data.name; - let indexes = self.mg.get_index_info(&table, tx)?; - let plan = TablePlan::new(tx, table, &self.mg)?; - let plan = SelectPlan::new(Rc::new(plan), data.predicate); - let s = plan.open()?; - let mut count = 0; - while s.next()? { - let rid = s.get_rid()?; - for (field, value) in indexes.iter() { - let val = s.get_val(field)?; - let index = value.open()?; - index.delete(val, rid.clone())?; - index.close()?; - } - s.delete()?; - count += 1; - } - s.close()?; - Ok(count) - } - - fn execute_create_table( - &self, - data: TableData, - tx: &Arc, - ) -> DbResult { - self.mg - .create_table(&data.name, &Arc::new(data.schema), tx)?; - Ok(0) - } - - fn execute_create_view(&self, data: ViewData, tx: &Arc) -> DbResult { - self.mg - .create_view(&data.name, &data.query.to_string(), tx)?; - Ok(0) - } - - fn execute_create_index(&self, data: IndexData, tx: &Arc) -> DbResult { - self.mg - .create_index(&data.index, &data.table, &data.field, tx)?; - Ok(0) - } -} diff --git a/engine/src/plan/materialize.rs b/engine/src/plan/materialize.rs deleted file mode 100644 index fe4fd6f..0000000 --- a/engine/src/plan/materialize.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::layout::Layout; -use crate::plan::Plan; -use crate::scan::Scan; -use crate::schema::Schema; -use crate::temp::TempTable; -use common::DbResult; -use std::rc::Rc; -use std::sync::Arc; -use transaction::transaction::Transaction; - -pub struct MaterializePlan { - source: Rc, - tx: Arc, -} - -impl MaterializePlan { - pub fn new(source: &Rc, tx: &Arc) -> Self { - Self { - source: Rc::clone(source), - tx: Arc::clone(tx), - } - } -} - -impl Plan for MaterializePlan { - fn open(&self) -> DbResult> { - let schema = self.source.schema()?; - let temp = TempTable::new(&self.tx, &schema)?; - let source = self.source.open()?; - let dest = temp.open()?; - while source.next()? { - dest.insert()?; - for (field, _) in schema.fields()? { - dest.set_val(&field, source.get_val(&field)?)?; - } - } - source.close()?; - dest.before_first()?; - Ok(dest) - } - - fn blocks_accessed(&self) -> DbResult { - let layout = Layout::new(&self.source.schema()?)?; - let rpd = self.tx.block_size() / layout.slotsize(); - Ok(self.source.records_output()? / rpd) - } - - fn records_output(&self) -> DbResult { - self.source.records_output() - } - - fn distinct_values(&self, field_name: &str) -> DbResult { - self.source.distinct_values(field_name) - } - - fn schema(&self) -> DbResult> { - self.source.schema() - } -} diff --git a/engine/src/plan/merge.rs b/engine/src/plan/merge.rs deleted file mode 100644 index 6d0b981..0000000 --- a/engine/src/plan/merge.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::{rc::Rc, sync::Arc}; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::{ - plan::{Plan, sort::SortPlan}, - scan::merge::MergeJoinScan, - schema::Schema, -}; - -pub struct MergeJoinPlan { - p1: Rc, - p2: Rc, - field_name1: String, - field_name2: String, - schema: Arc, -} - -#[allow(dead_code)] -impl MergeJoinPlan { - pub fn new( - tx: &Arc, - p1: &Rc, - p2: &Rc, - field_name1: &str, - field_name2: &str, - ) -> 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()); - Ok(Self { - p1, - p2, - field_name1: field_name1.to_string(), - field_name2: field_name2.to_string(), - schema, - }) - } -} - -impl Plan for MergeJoinPlan { - fn open(&self) -> DbResult> { - let s1 = self.p1.open()?; - let s2 = self.p2.open()?; - Ok(Rc::new(MergeJoinScan::new( - &s1, - &s2, - &self.field_name1, - &self.field_name2, - )?)) - } - - fn blocks_accessed(&self) -> DbResult { - Ok(self.p1.blocks_accessed()? + self.p2.blocks_accessed()?) - } - - fn records_output(&self) -> DbResult { - let max_val = self - .p1 - .distinct_values(&self.field_name1)? - .max(self.p2.distinct_values(&self.field_name2)?); - 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)? { - self.p1.distinct_values(field_name) - } else { - self.p2.distinct_values(field_name) - } - } - - fn schema(&self) -> DbResult> { - Ok(Arc::clone(&self.schema)) - } -} diff --git a/engine/src/plan/multibuffer.rs b/engine/src/plan/multibuffer.rs deleted file mode 100644 index 3e8eec4..0000000 --- a/engine/src/plan/multibuffer.rs +++ /dev/null @@ -1,92 +0,0 @@ -use std::{rc::Rc, sync::Arc}; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::{ - plan::{Plan, materialize::MaterializePlan}, - scan::{Scan, multibuffer::MultiBufferProductScan}, - schema::Schema, - temp::TempTable, -}; - -pub(crate) struct MultiBufferProductPlan { - tx: Arc, - left: Rc, - right: Rc, - schema: Arc, -} - -impl MultiBufferProductPlan { - pub(crate) fn new( - tx: &Arc, - 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 plan = Self { - tx: Arc::clone(tx), - left: Rc::new(MaterializePlan::new(left, tx)), - right: Rc::clone(right), - schema, - }; - Ok(plan) - } - - fn copy_records_from(&self, p: &Rc) -> DbResult { - let source = p.open()?; - let schema = p.schema()?; - let tt = TempTable::new(&self.tx, &schema)?; - let dest = tt.open()?; - while source.next()? { - dest.insert()?; - for (field, _) in schema.fields()? { - let value = source.get_val(&field)?; - dest.set_val(&field, value)?; - } - } - source.close()?; - dest.close()?; - Ok(tt) - } -} - -impl Plan for MultiBufferProductPlan { - fn open(&self) -> DbResult> { - let left = self.left.open()?; - let t = self.copy_records_from(&self.right)?; - Ok(Rc::new(MultiBufferProductScan::new( - &self.tx, - &left, - &t.table_name(), - &t.layout(), - )?)) - } - - fn blocks_accessed(&self) -> DbResult { - let available = self.tx.available_buffs()? as i32; - let size = MaterializePlan::new(&self.right, &self.tx).blocks_accessed()?; - let num_chunks = size / available; - Ok(self.right.blocks_accessed()? + self.left.blocks_accessed()? * num_chunks) - } - - fn records_output(&self) -> DbResult { - 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)? { - self.left.distinct_values(field_name) - } else { - self.right.distinct_values(field_name) - } - } - - fn schema(&self) -> DbResult> { - Ok(Arc::clone(&self.schema)) - } -} diff --git a/engine/src/plan/product.rs b/engine/src/plan/product.rs deleted file mode 100644 index 0a212d8..0000000 --- a/engine/src/plan/product.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::{rc::Rc, sync::Arc}; - -use common::DbResult; - -use crate::{ - plan::Plan, - scan::{Scan, product::ProductScan}, - schema::Schema, -}; - -pub struct ProductPlan { - p1: Rc, - p2: Rc, - schema: Arc, -} - -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)?; - Ok(Self { p1, p2, schema }) - } -} - -impl Plan for ProductPlan { - fn open(&self) -> DbResult> { - let s1 = self.p1.open()?; - let s2 = self.p2.open()?; - Ok(Rc::new(ProductScan::new(s1, s2)?)) - } - - fn blocks_accessed(&self) -> DbResult { - Ok(self.p1.blocks_accessed()? + (self.p1.records_output()? * self.p2.blocks_accessed()?)) - } - - fn records_output(&self) -> DbResult { - 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)? { - self.p1.distinct_values(field_name) - } else { - self.p2.distinct_values(field_name) - } - } - - fn schema(&self) -> DbResult> { - Ok(Arc::clone(&self.schema)) - } -} diff --git a/engine/src/plan/project.rs b/engine/src/plan/project.rs deleted file mode 100644 index 1a63954..0000000 --- a/engine/src/plan/project.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::{ - collections::{HashMap, HashSet}, - rc::Rc, - sync::Arc, -}; - -use common::DbResult; - -use crate::{ - element::Element, - plan::Plan, - scan::{Scan, project::ProjectScan}, - schema::Schema, -}; - -pub struct ProjectPlan { - plan: Rc, - schema: Arc, - mapping: HashMap, -} - -impl ProjectPlan { - pub fn new(plan: Rc, fields: Vec, tables: Vec) -> DbResult { - let schema = Arc::new(Schema::default()); - //for field in fields { - // let other = plan.schema()?; - // schema.add(field, &other)?; - // } - Ok(Self { - plan, - schema, - mapping: HashMap::new(), - }) - } -} - -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(); - Ok(Rc::new(ProjectScan::new(scan, fields))) - } - - fn blocks_accessed(&self) -> DbResult { - self.plan.blocks_accessed() - } - - fn records_output(&self) -> DbResult { - self.plan.records_output() - } - - fn distinct_values(&self, field_name: &str) -> DbResult { - self.plan.distinct_values(field_name) - } - - fn schema(&self) -> DbResult> { - Ok(Arc::clone(&self.schema)) - } -} diff --git a/engine/src/plan/select.rs b/engine/src/plan/select.rs deleted file mode 100644 index c572b9c..0000000 --- a/engine/src/plan/select.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::{rc::Rc, sync::Arc}; - -use common::DbResult; - -use crate::{ - plan::Plan, - predicate::Predicate, - scan::{Scan, select::SelectScan}, - schema::Schema, -}; - -pub struct SelectPlan { - plan: Rc, - predicate: Predicate, -} - -impl SelectPlan { - pub fn new(plan: Rc, predicate: Predicate) -> Self { - Self { plan, predicate } - } -} - -impl Plan for SelectPlan { - fn open(&self) -> DbResult> { - let s = self.plan.open()?; - Ok(Rc::new(SelectScan::new(s, self.predicate.clone()))) - } - - fn blocks_accessed(&self) -> DbResult { - self.plan.blocks_accessed() - } - - fn records_output(&self) -> DbResult { - Ok(self.plan.records_output()? / self.predicate.reduction_factor(&self.plan)?) - } - - fn distinct_values(&self, field_name: &str) -> common::DbResult { - if self.predicate.equates_with_constant(field_name)?.is_some() { - return Ok(1); - } else { - if let Some(second_field) = self.predicate.equates_with_field(field_name)? { - return Ok(self - .plan - .distinct_values(field_name)? - .min(self.plan.distinct_values(&second_field)?)); - } - } - self.plan.distinct_values(field_name) - } - - fn schema(&self) -> DbResult> { - self.plan.schema() - } -} diff --git a/engine/src/plan/sort.rs b/engine/src/plan/sort.rs deleted file mode 100644 index 0430902..0000000 --- a/engine/src/plan/sort.rs +++ /dev/null @@ -1,139 +0,0 @@ -use std::{cmp::Ordering, rc::Rc, sync::Arc}; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::{ - plan::{Plan, materialize::MaterializePlan}, - scan::{ - Scan, - sort::{RecordComparator, SortScan}, - }, - schema::Schema, - temp::TempTable, -}; - -pub struct SortPlan { - plan: Rc, - tx: Arc, - schema: Arc, - comp: RecordComparator, -} - -impl SortPlan { - pub fn new( - tx: &Arc, - plan: &Rc, - sort_fields: Vec, - ) -> DbResult { - let schema = plan.schema()?; - Ok(Self { - plan: Rc::clone(plan), - tx: Arc::clone(tx), - schema, - comp: RecordComparator::new(sort_fields), - }) - } - - fn split_into_runs(&self, source: &Rc) -> DbResult> { - let mut temps = vec![]; - source.before_first()?; - if !source.next()? { - return Ok(temps); - } - let mut current_temp = TempTable::new(&self.tx, &self.schema)?; - 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)?; - temps.push(current_temp.clone()); - current_scan = current_temp.open()?; - } - } - current_scan.close()?; - Ok(temps) - } - - fn do_merge_iteration(&self, mut runs: Vec) -> DbResult> { - let mut result = vec![]; - while runs.len() > 1 { - let p1 = runs.remove(0); - let p2 = runs.remove(0); - result.push(self.merge_two_runs(p1, p2)?); - } - if runs.len() == 1 { - result.push(runs.first().cloned().unwrap()); - } - Ok(result) - } - - 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 dest = result.open()?; - - let mut has_more1 = src1.next()?; - let mut has_more2 = src2.next()?; - while has_more1 && has_more2 { - if self.comp.compare(&src1, &src2)? == Ordering::Less { - has_more1 = self.copy(&src1, &dest)?; - } else { - has_more2 = self.copy(&src2, &dest)?; - } - } - - if has_more1 { - while has_more1 { - has_more1 = self.copy(&src1, &dest)?; - } - } else { - while has_more2 { - has_more2 = self.copy(&src2, &dest)?; - } - } - src1.close()?; - src2.close()?; - dest.close()?; - Ok(result) - } - - fn copy(&self, source: &Rc, dest: &Rc) -> DbResult { - dest.insert()?; - for (field, _) in self.schema.fields()? { - dest.set_val(&field, source.get_val(&field)?)?; - } - source.next() - } -} - -impl Plan for SortPlan { - fn open(&self) -> DbResult> { - let scan = self.plan.open()?; - let mut runs = self.split_into_runs(&scan)?; - scan.close()?; - while runs.len() > 2 { - runs = self.do_merge_iteration(runs)?; - } - Ok(Rc::new(SortScan::new(runs, self.comp.clone())?)) - } - - fn blocks_accessed(&self) -> DbResult { - let mp = MaterializePlan::new(&self.plan, &self.tx); - mp.blocks_accessed() - } - - fn records_output(&self) -> DbResult { - self.plan.records_output() - } - - fn distinct_values(&self, field_name: &str) -> DbResult { - self.plan.distinct_values(field_name) - } - - fn schema(&self) -> DbResult> { - Ok(self.schema.clone()) - } -} diff --git a/engine/src/plan/table.rs b/engine/src/plan/table.rs deleted file mode 100644 index 4283085..0000000 --- a/engine/src/plan/table.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::{rc::Rc, sync::Arc}; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::{ - layout::Layout, - metadata_mgr::MetadataMgr, - plan::Plan, - scan::{Scan, table::TableScan}, - schema::Schema, - stat_mgr::StatInfo, -}; - -pub struct TablePlan { - tx: Arc, - table: String, - layout: Arc, - 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)?; - Ok(Self { - tx: Arc::clone(tx), - table, - layout, - stat, - }) - } -} - -impl Plan for TablePlan { - fn open(&self) -> DbResult> { - Ok(Rc::new(TableScan::new( - &self.tx, - &self.table, - &self.layout, - )?)) - } - - fn blocks_accessed(&self) -> DbResult { - Ok(self.stat.block_accessed()) - } - - fn records_output(&self) -> DbResult { - Ok(self.stat.records_output()) - } - - fn distinct_values(&self, _: &str) -> DbResult { - Ok(self.stat.distinct_values()) - } - - fn schema(&self) -> DbResult> { - Ok(self.layout.schema()) - } -} diff --git a/engine/src/predicate.rs b/engine/src/predicate.rs deleted file mode 100644 index ac18b46..0000000 --- a/engine/src/predicate.rs +++ /dev/null @@ -1,245 +0,0 @@ -use std::{ - fmt::Debug, - rc::Rc, - sync::{Arc, RwLock}, -}; - -use common::{DbResult, error::DbError}; - -use crate::{constant::Constant, plan::Plan, scan::Scan, schema::Schema}; - -#[derive(Debug, Clone)] -pub enum Expression { - Value(Constant), - Field(String), -} - -impl Expression { - 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 { - match self { - Self::Value(_) => Ok(true), - Self::Field(field) => schema.has_field(field), - } - } - - pub fn as_field_name(&self) -> Option<&str> { - match self { - Self::Field(field) => Some(field), - _ => None, - } - } - - pub fn as_constant(&self) -> Option<&Constant> { - match self { - Self::Value(value) => Some(value), - _ => None, - } - } -} - -impl std::fmt::Display for Expression { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Value(value) => write!(f, "{}", value), - Self::Field(field) => write!(f, "{}", field), - } - } -} - -#[derive(Clone, Debug)] -pub struct Term { - left: Expression, - right: Expression, -} - -impl Term { - pub fn new(left: Expression, right: Expression) -> Self { - Self { left, right } - } - - pub fn is_satisfied(&self, s: &Rc) -> DbResult { - let left = self.left.evaluate(s)?; - let right = self.right.evaluate(s)?; - Ok(left == right) - } - - pub fn applies_to(&self, schema: &Schema) -> DbResult { - Ok(self.left.applies_to(schema)? && self.right.applies_to(schema)?) - } - - pub fn reduction_factor(&self, p: &Rc) -> DbResult { - if let Some(left) = self.left.as_field_name() - && let Some(right) = self.right.as_field_name() - { - return Ok(p.distinct_values(left)?.max(p.distinct_values(right)?)); - } - if let Some(left) = self.left.as_field_name() { - return p.distinct_values(left); - } - if let Some(right) = self.right.as_field_name() { - return p.distinct_values(right); - } - if let Some(left) = self.left.as_constant() - && let Some(right) = self.right.as_constant() - && left == right - { - Ok(1) - } else { - Ok(i32::MAX) - } - } - - pub fn equates_with_constant(&self, field_name: &str) -> DbResult> { - if let Some(field) = self.left.as_field_name() - && field == field_name - && let Some(value) = self.right.as_constant() - { - Ok(Some(value.clone())) - } else if let Some(right) = self.right.as_field_name() - && right == field_name - && let Some(value) = self.left.as_constant() - { - Ok(Some(value.clone())) - } else { - Ok(None) - } - } - - pub fn equates_with_field(&self, field_name: &str) -> 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())) - } 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())) - } else { - Ok(None) - } - } -} - -impl std::fmt::Display for Term { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}={}", self.left, self.right) - } -} - -#[derive(Default, Clone)] -pub struct Predicate { - terms: Arc>>, -} - -impl Predicate { - pub fn new(term: Term) -> Self { - Self { - terms: Arc::new(RwLock::new(vec![term])), - } - } - - pub fn conjoin_with(&self, p: &Self) -> DbResult<()> { - let mut write = self.terms.write().map_err(DbError::lock)?; - let read = p.terms.read().map_err(DbError::lock)?; - for term in read.iter() { - write.push(term.clone()); - } - Ok(()) - } - - pub fn is_satisfied(&self, s: &Rc) -> DbResult { - let read = self.terms.read().map_err(DbError::lock)?; - for t in read.iter() { - if !t.is_satisfied(s)? { - return Ok(false); - } - } - Ok(true) - } - - pub fn reduction_factor(&self, p: &Rc) -> DbResult { - let mut factor = 1; - let read = self.terms.read().map_err(DbError::lock)?; - for t in read.iter() { - factor *= t.reduction_factor(p)?; - } - Ok(factor) - } - - pub fn select_sub_pred(&self, schema: &Schema) -> DbResult { - let result = Predicate::default(); - 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(schema)? { - terms.push(t.clone()); - } - } - } - Ok(result) - } - - 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 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)? { - terms.push(t.clone()); - } - } - if terms.is_empty() { - return Ok(None); - } - } - Ok(Some(result)) - } - - pub fn equates_with_constant(&self, field_name: &str) -> 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)? { - return Ok(Some(c)); - } - } - Ok(None) - } - - pub fn equates_with_field(&self, field_name: &str) -> 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)? { - return Ok(Some(field)); - } - } - Ok(None) - } -} - -impl std::fmt::Display for Predicate { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let terms = self.terms.read().map_err(|_| std::fmt::Error)?; - for (i, t) in terms.iter().enumerate() { - if i == 0 { - write!(f, "{}", t)?; - } else { - write!(f, " AND {}", t)?; - } - } - Ok(()) - } -} diff --git a/engine/src/query/basic_planner.rs b/engine/src/query/basic_planner.rs index 7dd758d..9d4e2be 100644 --- a/engine/src/query/basic_planner.rs +++ b/engine/src/query/basic_planner.rs @@ -1,32 +1,27 @@ use std::{rc::Rc, sync::Arc}; use common::{DbResult, error::DbError}; +use planner::mgr::metadata::MetadataMgr; +use planner::plan::Plan; use transaction::transaction::Transaction; -use crate::{ - metadata_mgr::MetadataMgr, - plan::{Plan, select::SelectPlan, table::TablePlan}, - query::{command::IndexData, planner::UpdatePlanner}, -}; +use crate::query::command::{InsertData, TableData}; +use crate::query::{command::IndexData, planner::UpdatePlanner}; 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 } } } impl UpdatePlanner for BasicUpdatePlanner { - fn execute_insert( - &self, - data: super::command::InsertData, - tx: &Arc, - ) -> DbResult { - let p = Rc::new(TablePlan::new(tx, data.table.clone(), &self.md)?); - let s = p.open()?; + fn execute_insert(&self, data: InsertData, tx: &Arc) -> DbResult { + let p = Plan::table(tx, data.table.clone(), &self.md)?; + let mut s = p.open()?; s.insert()?; if data.fields.len() != data.values.len() { return Err(DbError::InvalidValuesAmount); @@ -45,8 +40,8 @@ impl UpdatePlanner for BasicUpdatePlanner { data: super::command::UpdateData, tx: &Arc, ) -> DbResult { - let p = Rc::new(TablePlan::new(tx, data.table, &self.md)?); - let p = Rc::new(SelectPlan::new(p, data.predicate)); + let p = Box::new(Plan::table(tx, data.table, &self.md)?); + let p = Plan::select(p, data.predicate); let s = p.open()?; let mut count = 0; while s.next()? { @@ -63,8 +58,8 @@ impl UpdatePlanner for BasicUpdatePlanner { data: super::command::DeleteData, tx: &Arc, ) -> DbResult { - let p = Rc::new(TablePlan::new(tx, data.name, &self.md)?); - let p = Rc::new(SelectPlan::new(p, data.predicate)); + let p = Box::new(Plan::table(tx, data.name, &self.md)?); + let p = Plan::select(p, data.predicate); let s = p.open()?; let mut count = 0; while s.next()? { @@ -77,11 +72,11 @@ impl UpdatePlanner for BasicUpdatePlanner { fn execute_create_table( &self, - data: super::command::TableData, + data: TableData, tx: &Arc, ) -> DbResult { self.md - .create_table(&data.name, &Arc::new(data.schema), tx)?; + .create_table(&data.name, data.schema, tx)?; Ok(0) } diff --git a/engine/src/query/command.rs b/engine/src/query/command.rs index 755954c..ee9deee 100644 --- a/engine/src/query/command.rs +++ b/engine/src/query/command.rs @@ -1,10 +1,8 @@ -use crate::{ - constant::Constant, - element::Element, - predicate::{Expression, Predicate}, - schema::Schema, - sort_by::SortByData, -}; +use planner::element::Element; +use planner::predicate::{Expression, Predicate}; +use planner::schema::Schema; +use planner::value::Value; +use crate::order::OrderByData; pub enum Command { Insert(InsertData), @@ -35,7 +33,7 @@ pub struct QueryData { pub tables: Vec, pub predicate: Predicate, pub group_by: GroupByData, - pub sort_by: SortByData, + pub order_by: OrderByData, } impl std::fmt::Display for QueryData { @@ -63,8 +61,8 @@ impl std::fmt::Display for QueryData { if !self.group_by.is_empty() { write!(f, " {}", self.group_by)?; } - if !self.sort_by.is_empty() { - write!(f, " {}", self.sort_by)?; + if !self.order_by.is_empty() { + write!(f, " {}", self.order_by)?; } Ok(()) } @@ -99,8 +97,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 { @@ -168,13 +166,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 { diff --git a/engine/src/query/heuristic_planner.rs b/engine/src/query/heuristic_planner.rs index d5d4414..c651371 100644 --- a/engine/src/query/heuristic_planner.rs +++ b/engine/src/query/heuristic_planner.rs @@ -1,32 +1,28 @@ use std::{cell::RefCell, rc::Rc, sync::Arc}; use common::DbResult; +use planner::mgr::metadata::MetadataMgr; +use planner::plan::Plan; use transaction::transaction::Transaction; -use crate::plan::group::GroupByPlan; -use crate::plan::sort::SortPlan; -use crate::{ - metadata_mgr::MetadataMgr, - plan::{Plan, project::ProjectPlan}, - query::{command::QueryData, planner::QueryPlanner, table_planner::TablePlanner}, -}; +use crate::query::{command::QueryData, planner::QueryPlanner, table_planner::TablePlanner}; -struct HeuristicQueryPlannerInner { +struct HeuristicQueryPlanner { table_planners: Vec, - md: Arc, + md: MetadataMgr, } -impl HeuristicQueryPlannerInner { - fn new(md: &Arc) -> Self { +impl HeuristicQueryPlanner { + fn new(md: MetadataMgr) -> Self { Self { table_planners: vec![], - md: Arc::clone(md), + md, } } - fn create_plan(&mut self, data: QueryData, tx: &Arc) -> DbResult> { + fn create_plan(&mut self, data: QueryData, tx: &Arc) -> DbResult { for table in &data.tables { - let tp = TablePlanner::new(table.source()?, 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()?; @@ -45,17 +41,17 @@ impl HeuristicQueryPlannerInner { vec![], )?); } - if !data.sort_by.is_empty() { - current = Rc::new(SortPlan::new(tx, ¤t, data.sort_by.fields)?); + if !data.order_by.is_empty() { + current = Rc::new(SortPlan::new(tx, ¤t, data.order_by.fields)?); } - Ok(Rc::new(ProjectPlan::new( + Ok(Plan::project( current, data.fields, data.tables, - )?)) + )) } - fn get_lowest_select_plan(&mut self) -> DbResult> { + fn get_lowest_select_plan(&mut self) -> DbResult> { let mut index = 0; let mut best_plan = self.table_planners.first().unwrap().make_select_plan()?; for (i, tp) in self.table_planners.iter().skip(1).enumerate() { @@ -69,7 +65,7 @@ impl HeuristicQueryPlannerInner { Ok(best_plan) } - fn get_lowest_join_plan(&mut self, current: &Rc) -> DbResult>> { + fn get_lowest_join_plan(&mut self, current: &Plan) -> DbResult> { let mut index = 0; let mut best_plan = None; for (i, tp) in self.table_planners.iter().enumerate() { @@ -92,7 +88,7 @@ impl HeuristicQueryPlannerInner { Ok(best_plan) } - fn get_lowest_product_plan(&mut self, current: &Rc) -> DbResult> { + fn get_lowest_product_plan(&mut self, current: &Plan) -> DbResult { let mut index = 0; let mut best_plan = self .table_planners @@ -110,18 +106,3 @@ impl HeuristicQueryPlannerInner { Ok(best_plan) } } - -pub(crate) struct HeuristicQueryPlanner(RefCell); - -impl HeuristicQueryPlanner { - pub(crate) fn new(md: &Arc) -> Self { - Self(RefCell::new(HeuristicQueryPlannerInner::new(md))) - } -} - -impl QueryPlanner for HeuristicQueryPlanner { - fn create_plan(&self, data: QueryData, tx: &Arc) -> DbResult> { - let mut write = self.0.borrow_mut(); - write.create_plan(data, tx) - } -} diff --git a/engine/src/query/parser.rs b/engine/src/query/parser.rs index db2ea71..4883066 100644 --- a/engine/src/query/parser.rs +++ b/engine/src/query/parser.rs @@ -105,7 +105,7 @@ impl Parser { tables, predicate, group_by, - sort_by, + order_by: sort_by, })) } diff --git a/engine/src/query/table_planner.rs b/engine/src/query/table_planner.rs index a3760ed..4cd9877 100644 --- a/engine/src/query/table_planner.rs +++ b/engine/src/query/table_planner.rs @@ -1,27 +1,19 @@ use std::{collections::HashMap, rc::Rc, sync::Arc}; use common::DbResult; +use planner::element::Element; +use planner::mgr::index::IndexInfo; +use planner::mgr::metadata::MetadataMgr; +use planner::plan::Plan; +use planner::predicate::Predicate; +use planner::schema::Schema; use transaction::transaction::Transaction; -use crate::{ - index_mgr::IndexInfo, - metadata_mgr::MetadataMgr, - plan::{ - Plan, - index::{IndexJoinPlan, IndexSelectPlan}, - multibuffer::MultiBufferProductPlan, - select::SelectPlan, - table::TablePlan, - }, - predicate::Predicate, - schema::Schema, -}; - pub(crate) struct TablePlanner { - plan: Rc, + plan: Plan, predicate: Predicate, - schema: Arc, - indexes: HashMap, + schema: Schema, + indexes: HashMap, tx: Arc, } @@ -30,14 +22,14 @@ impl TablePlanner { table: &str, predicate: Predicate, tx: &Arc, - md: &MetadataMgr, + md: MetadataMgr, ) -> DbResult { - let plan = TablePlan::new(tx, table.to_string(), md)?; + let plan = Plan::table(tx, table.to_string(), &md)?; Ok(Self { predicate, tx: Arc::clone(tx), schema: plan.schema()?, - plan: Rc::new(plan), + plan: plan, indexes: md.get_index_info(table, tx)?, }) } diff --git a/engine/src/query/token.rs b/engine/src/query/token.rs index b37131a..f055ee8 100644 --- a/engine/src/query/token.rs +++ b/engine/src/query/token.rs @@ -1,6 +1,5 @@ use common::error::DbError; - -use crate::constant::Constant; +use planner::value::Value; #[derive(Clone, Debug, PartialEq, Eq)] pub enum Token { @@ -28,7 +27,7 @@ pub enum Token { By, Field(String), Delimiter(char), - Element(Constant), + Element(Value), } impl Token { @@ -77,7 +76,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 +98,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)) } diff --git a/engine/src/record_page.rs b/engine/src/record_page.rs deleted file mode 100644 index a5cb769..0000000 --- a/engine/src/record_page.rs +++ /dev/null @@ -1,194 +0,0 @@ -use std::sync::Arc; - -use common::DbResult; -use file::block::BlockId; -use transaction::transaction::Transaction; - -use crate::{field_info::FieldInfo, layout::Layout}; - -const EMPTY: u8 = 0; -const USED: u8 = 1; - -#[derive(Clone)] -pub struct RecordPage { - tx: Arc, - block: BlockId, - layout: Arc, -} - -impl RecordPage { - pub fn new(tx: &Arc, block: BlockId, layout: &Arc) -> DbResult { - tx.pin(&block)?; - Ok(Self { - tx: Arc::clone(tx), - block, - layout: Arc::clone(layout), - }) - } - - pub fn get_i32(&self, slot: i32, fieldname: &str) -> DbResult { - let pos = self.offset(slot) + self.layout.offset(fieldname); - 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); - self.tx.get_string(&self.block, pos as usize) - } - - pub fn set_i32(&self, slot: i32, field: &str, 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<()> { - let pos = self.offset(slot) + self.layout.offset(field); - self.tx.set_string(&self.block, pos as usize, value, true) - } - - pub fn delete(&self, slot: i32) -> DbResult<()> { - self.set_flag(slot, EMPTY) - } - - pub fn format(&self) -> DbResult<()> { - let mut slot = 0; - while self.is_valid_slot(slot) { - self.tx - .set_u8(&self.block, self.offset(slot) as usize, EMPTY, false)?; - let schema = self.layout.schema(); - 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)?, - FieldInfo::Varchar(_) => { - self.tx.set_string(&self.block, pos as usize, "", false)? - } - } - } - slot += 1; - } - Ok(()) - } - - pub fn next_after(&self, slot: i32) -> DbResult { - self.search_after(slot, USED) - } - - pub fn insert_after(&self, slot: i32) -> DbResult { - let newslot = self.search_after(slot, EMPTY)?; - if newslot >= 0 { - self.set_flag(newslot, USED)?; - } - Ok(newslot) - } - - pub fn block(&self) -> BlockId { - self.block.clone() - } - - fn set_flag(&self, slot: i32, flag: u8) -> DbResult<()> { - let offset = self.offset(slot) as usize; - self.tx.set_u8(&self.block, offset, flag, true)?; - Ok(()) - } - - fn search_after(&self, mut slot: i32, flag: u8) -> DbResult { - slot += 1; - while self.is_valid_slot(slot) { - if self.tx.get_u8(&self.block, self.offset(slot) as usize)? == flag { - return Ok(slot); - } - slot += 1; - } - Ok(-1) - } - - fn is_valid_slot(&self, slot: i32) -> bool { - let offset = self.offset(slot + 1); - let block_size = self.tx.block_size(); - offset <= block_size - } - - fn offset(&self, slot: i32) -> i32 { - slot * self.layout.slotsize() - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashSet; - - use buffer::mgr::BufferMgr; - use file::mgr::FileMgr; - use log::mgr::LogMgr; - use rand::RngExt; - use tempfile::tempdir; - use transaction::lock_table::LockTable; - - use crate::schema::Schema; - - use super::*; - - #[test] - fn record() { - let dir = tempdir().unwrap(); - 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 lock_table = Arc::new(LockTable::default()); - - 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 block = tx.append("testfile").unwrap(); - tx.pin(&block).unwrap(); - let record_page = RecordPage::new(&tx, block.clone(), &layout).unwrap(); - record_page.format().unwrap(); - - let mut rng = rand::rng(); - - let mut values_less = HashSet::new(); - let mut values_greater = HashSet::new(); - - let mut slot = record_page.insert_after(-1).unwrap(); - while slot >= 0 { - let n = rng.random::(); - if n < 25 { - values_less.insert((n, format!("rec{}", n))); - } else { - values_greater.insert((n, format!("rec{}", n))); - } - record_page.set_i32(slot, "A", n).unwrap(); - record_page - .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(); - if a < 25 { - assert!(values_less.contains(&(a, b))); - record_page.delete(slot).unwrap(); - } - slot = record_page.next_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(); - assert!(values_greater.contains(&(a, b))); - slot = record_page.next_after(slot).unwrap(); - } - tx.unpin(&block).unwrap(); - tx.commit().unwrap(); - } -} diff --git a/engine/src/rid.rs b/engine/src/rid.rs deleted file mode 100644 index 5a87bf5..0000000 --- a/engine/src/rid.rs +++ /dev/null @@ -1,22 +0,0 @@ -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct RID(i32, i32); - -impl RID { - pub fn new(block_num: i32, slot: i32) -> Self { - Self(block_num, slot) - } - - pub fn block_num(&self) -> i32 { - self.0 - } - - pub fn slot(&self) -> i32 { - self.1 - } -} - -impl std::fmt::Display for RID { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "[{}, {}]", self.0, self.1) - } -} diff --git a/engine/src/scan.rs b/engine/src/scan.rs deleted file mode 100644 index 43035cb..0000000 --- a/engine/src/scan.rs +++ /dev/null @@ -1,66 +0,0 @@ -use common::{DbResult, error::DbError}; -use std::sync::Arc; - -use crate::schema::Schema; -use crate::{constant::Constant, rid::RID}; - -pub mod chunk; -pub(crate) mod group; -pub mod index; -pub(crate) mod merge; -pub(crate) mod multibuffer; -pub mod product; -pub mod project; -pub mod select; -pub(crate) mod sort; -pub mod table; - -pub trait Scan { - fn before_first(&self) -> DbResult<()>; - - fn next(&self) -> DbResult; - - fn get_i32(&self, field_name: &str) -> DbResult; - - fn get_string(&self, field_name: &str) -> DbResult; - - fn get_val(&self, field_name: &str) -> DbResult; - - fn has_field(&self, field_name: &str) -> DbResult; - - fn close(&self) -> DbResult<()>; - - fn schema(&self) -> DbResult>; - - fn set_i32(&self, _: &str, _: i32) -> DbResult<()> { - Err(DbError::other("cannot set integer")) - } - - fn set_string(&self, _: &str, _: &str) -> DbResult<()> { - Err(DbError::other("cannot set string")) - } - - fn set_val(&self, _: &str, _: Constant) -> DbResult<()> { - Err(DbError::other("cannot set value")) - } - - fn insert(&self) -> DbResult<()> { - Err(DbError::other("cannot insert")) - } - - fn delete(&self) -> DbResult<()> { - Err(DbError::other("cannot delete")) - } - - fn get_rid(&self) -> DbResult { - Err(DbError::other("cannot get rid")) - } - - fn move_to_rid(&self, _: RID) -> DbResult<()> { - Err(DbError::other("cannot update")) - } - - fn save_position(&self) -> DbResult<()> { - Err(DbError::other("cannot save position")) - } -} diff --git a/engine/src/scan/chunk.rs b/engine/src/scan/chunk.rs deleted file mode 100644 index 51f3427..0000000 --- a/engine/src/scan/chunk.rs +++ /dev/null @@ -1,180 +0,0 @@ -use std::cell::RefCell; -use std::sync::Arc; - -use common::{DbResult, error::DbError}; -use file::block::BlockId; -use transaction::transaction::Transaction; - -use crate::schema::Schema; -use crate::{ - constant::Constant, field_info::FieldInfo, layout::Layout, record_page::RecordPage, scan::Scan, -}; - -pub struct ChunkScanLock { - buffers: Vec, - tx: Arc, - filename: String, - layout: Arc, - start_b_num: i32, - end_b_num: i32, - current_b_num: i32, - rp: i32, - current_slot: i32, -} - -impl ChunkScanLock { - fn new( - tx: &Arc, - filename: &str, - layout: &Arc, - 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)?); - } - let chunk = Self { - buffers, - tx: Arc::clone(tx), - filename: filename.to_string(), - start_b_num, - end_b_num, - current_b_num: start_b_num, - layout: Arc::clone(layout), - current_slot: -1, - rp: 0, - }; - Ok(chunk) - } - - fn move_to_block(&mut self, block_num: i32) { - self.current_b_num = block_num; - self.rp = self.current_b_num - self.start_b_num; - self.current_slot = -1; - } - - fn close(&self) -> DbResult<()> { - for i in 0..self.buffers.len() { - let block = BlockId::new(&self.filename, self.start_b_num + i as i32); - self.tx.unpin(&block)?; - } - Ok(()) - } - - fn before_first(&mut self) { - self.move_to_block(self.start_b_num); - } - - fn next(&mut self) -> DbResult { - loop { - let Some(rp) = self.buffers.get(self.rp as usize).cloned() else { - return Err(DbError::other("cannot get record page for chunk")); - }; - self.current_slot = rp.next_after(self.current_slot)?; - if self.current_slot >= 0 { - return Ok(true); - } - if self.current_b_num == self.end_b_num { - return Ok(false); - } - self.move_to_block(rp.block().num + 1); - } - } - - fn get_i32(&self, field_name: &str) -> DbResult { - if let Some(rp) = self.buffers.get(self.rp as usize) { - rp.get_i32(self.current_slot, field_name) - } else { - Err(DbError::other("cannot get buffer chunk")) - } - } - - fn get_string(&self, field_name: &str) -> DbResult { - if let Some(rp) = self.buffers.get(self.rp as usize) { - rp.get_string(self.current_slot, field_name) - } else { - Err(DbError::other("cannot get buffer chunk")) - } - } - - 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 has_field(&self, field_name: &str) -> DbResult { - self.layout.schema().has_field(field_name) - } - - fn schema(&self) -> DbResult> { - Ok(self.layout.schema()) - } -} - -pub struct ChunkScan(RefCell); - -impl ChunkScan { - pub fn new( - tx: &Arc, - filename: &str, - layout: &Arc, - start_b_num: i32, - end_b_num: i32, - ) -> DbResult { - Ok(Self(RefCell::new(ChunkScanLock::new( - tx, - filename, - layout, - start_b_num, - end_b_num, - )?))) - } -} - -impl Scan for ChunkScan { - fn before_first(&self) -> DbResult<()> { - let mut write = self.0.borrow_mut(); - write.before_first(); - Ok(()) - } - - fn next(&self) -> DbResult { - let mut write = self.0.borrow_mut(); - write.next() - } - - fn get_i32(&self, field_name: &str) -> DbResult { - let read = self.0.borrow(); - read.get_i32(field_name) - } - - fn get_string(&self, field_name: &str) -> DbResult { - let read = self.0.borrow(); - read.get_string(field_name) - } - - fn get_val(&self, field_name: &str) -> DbResult { - let read = self.0.borrow(); - read.get_val(field_name) - } - - fn has_field(&self, field_name: &str) -> DbResult { - let read = self.0.borrow(); - read.has_field(field_name) - } - - fn close(&self) -> DbResult<()> { - let read = self.0.borrow(); - read.close() - } - - fn schema(&self) -> DbResult> { - let read = self.0.borrow(); - read.schema() - } -} diff --git a/engine/src/scan/group.rs b/engine/src/scan/group.rs deleted file mode 100644 index ceeed2e..0000000 --- a/engine/src/scan/group.rs +++ /dev/null @@ -1,223 +0,0 @@ -use std::{cell::RefCell, cmp::Ordering, collections::HashMap, rc::Rc, sync::Arc}; - -use common::{DbResult, error::DbError}; - -use crate::{constant::Constant, scan::Scan, schema::Schema}; - -#[allow(dead_code)] -#[derive(Clone)] -pub enum AggregationFn { - MaxFn { field: String, value: Constant }, -} - -impl AggregationFn { - pub fn process_first(&mut self, scan: &Rc) -> DbResult<()> { - match self { - Self::MaxFn { field, value } => { - *value = scan.get_val(field)?; - } - } - Ok(()) - } - - pub fn process_next(&mut self, scan: &Rc) -> DbResult<()> { - match self { - Self::MaxFn { field, value } => { - let new_value = scan.get_val(field)?; - if new_value.cmp(value) == Ordering::Greater { - *value = new_value; - } - } - } - Ok(()) - } - - pub fn field_name(&self) -> String { - match self { - Self::MaxFn { field, .. } => format!("max_of_{}", field), - } - } - - pub fn value(&self) -> Constant { - match self { - Self::MaxFn { value, .. } => value.clone(), - } - } -} - -#[derive(PartialEq, Eq)] -struct GroupValue { - values: HashMap, -} - -impl GroupValue { - fn new(scan: &Rc, fields: Vec) -> DbResult { - let mut values = HashMap::new(); - for field in fields { - let value = scan.get_val(&field)?; - values.insert(field, value); - } - Ok(Self { values }) - } - - fn get_val(&self, field: &str) -> DbResult { - match self.values.get(field) { - Some(value) => Ok(value.clone()), - _ => Err(DbError::field_not_exists(field)), - } - } -} - -struct GroupByScanLock { - scan: Rc, - group_fields: Vec, - agg_fns: Vec, - group_val: GroupValue, - more_groups: bool, -} - -impl GroupByScanLock { - fn new( - scan: &Rc, - group_fields: Vec, - agg_fns: Vec, - ) -> DbResult { - let mut g = Self { - scan: Rc::clone(scan), - group_fields, - agg_fns, - group_val: GroupValue::new(scan, vec![])?, - more_groups: false, - }; - g.before_first()?; - Ok(g) - } - - fn before_first(&mut self) -> DbResult<()> { - self.scan.before_first()?; - self.more_groups = self.scan.next()?; - Ok(()) - } - - fn next(&mut self) -> DbResult { - if !self.more_groups { - return Ok(false); - } - for f in &mut self.agg_fns { - f.process_first(&self.scan)?; - } - self.group_val = GroupValue::new(&self.scan, self.group_fields.clone())?; - loop { - self.more_groups = self.scan.next()?; - if !self.more_groups { - break; - } - let gv = GroupValue::new(&self.scan, self.group_fields.clone())?; - if gv != self.group_val { - break; - } - for f in &mut self.agg_fns { - f.process_next(&self.scan)?; - } - } - Ok(true) - } - - fn close(&self) -> DbResult<()> { - self.scan.close() - } - - fn get_val(&self, field_name: &str) -> DbResult { - if self.group_fields.contains(&field_name.to_string()) { - return self.group_val.get_val(field_name); - } - for f in &self.agg_fns { - if f.field_name() == field_name { - return Ok(f.value()); - } - } - Err(DbError::field_not_exists(field_name)) - } - - fn get_i32(&self, field_name: &str) -> DbResult { - self.get_val(field_name)?.as_i32() - } - - fn get_string(&self, field_name: &str) -> 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()) { - return true; - } - for f in &self.agg_fns { - if f.field_name() == field_name { - return true; - } - } - false - } - - fn schema(&self) -> DbResult> { - self.scan.schema() - } -} - -pub struct GroupByScan { - lock: RefCell, -} - -impl GroupByScan { - pub fn new( - scan: &Rc, - group_fields: Vec, - agg_fns: Vec, - ) -> DbResult { - Ok(Self { - lock: RefCell::new(GroupByScanLock::new(scan, group_fields, agg_fns)?), - }) - } -} - -impl Scan for GroupByScan { - fn before_first(&self) -> DbResult<()> { - let mut write = self.lock.borrow_mut(); - write.before_first() - } - - fn next(&self) -> DbResult { - let mut write = self.lock.borrow_mut(); - write.next() - } - - fn get_i32(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - read.get_i32(field_name) - } - - fn get_string(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - read.get_string(field_name) - } - - fn get_val(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - read.get_val(field_name) - } - - fn has_field(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - Ok(read.has_field(field_name)) - } - - fn close(&self) -> DbResult<()> { - let read = self.lock.borrow(); - read.close() - } - - 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 deleted file mode 100644 index e6aa044..0000000 --- a/engine/src/scan/index.rs +++ /dev/null @@ -1,234 +0,0 @@ -use common::DbResult; -use std::rc::Rc; -use std::sync::Arc; - -use crate::schema::Schema; -use crate::{constant::Constant, index::Index, scan::Scan}; - -pub struct IndexSelectScan { - scan: Rc, - index: Rc, - value: Constant, -} - -impl IndexSelectScan { - pub fn new(scan: &Rc, index: &Rc, value: Constant) -> DbResult { - let scan = Self { - scan: Rc::clone(scan), - index: Rc::clone(index), - value, - }; - scan.before_first()?; - Ok(scan) - } -} - -impl Scan for IndexSelectScan { - fn before_first(&self) -> DbResult<()> { - self.index.before_first(self.value.clone()) - } - - fn next(&self) -> DbResult { - let ok = self.index.next()?; - if ok { - let rid = self.index.get_data_rid()?; - self.scan.move_to_rid(rid)?; - } - Ok(ok) - } - - fn get_i32(&self, field_name: &str) -> DbResult { - self.scan.get_i32(field_name) - } - - fn get_string(&self, field_name: &str) -> DbResult { - self.scan.get_string(field_name) - } - - fn get_val(&self, field_name: &str) -> DbResult { - self.scan.get_val(field_name) - } - - fn has_field(&self, field_name: &str) -> DbResult { - self.scan.has_field(field_name) - } - - fn close(&self) -> DbResult<()> { - self.index.close()?; - self.scan.close() - } - - fn schema(&self) -> DbResult> { - self.scan.schema() - } -} - -pub struct IndexJoinScan { - left: Rc, - right: Rc, - index: Rc, - field: String, -} - -impl IndexJoinScan { - pub fn new( - left: &Rc, - index: &Rc, - field: &str, - right: &Rc, - ) -> DbResult { - let scan = Self { - left: Rc::clone(left), - right: Rc::clone(right), - field: field.to_string(), - index: Rc::clone(index), - }; - scan.before_first()?; - Ok(scan) - } - - fn reset_index(&self) -> DbResult<()> { - let key = self.left.get_val(&self.field)?; - self.index.before_first(key) - } -} - -impl Scan for IndexJoinScan { - fn before_first(&self) -> DbResult<()> { - self.left.before_first()?; - self.left.next()?; - self.reset_index() - } - - fn next(&self) -> DbResult { - loop { - if self.index.next()? { - self.right.move_to_rid(self.index.get_data_rid()?)?; - return Ok(true); - } - if !self.left.next()? { - return Ok(false); - } - self.reset_index()?; - } - } - - fn get_i32(&self, field_name: &str) -> DbResult { - if self.right.has_field(field_name)? { - self.right.get_i32(field_name) - } else { - self.left.get_i32(field_name) - } - } - - fn get_string(&self, field_name: &str) -> DbResult { - if self.right.has_field(field_name)? { - self.right.get_string(field_name) - } else { - self.left.get_string(field_name) - } - } - - fn get_val(&self, field_name: &str) -> DbResult { - if self.right.has_field(field_name)? { - self.right.get_val(field_name) - } else { - self.left.get_val(field_name) - } - } - - fn has_field(&self, field_name: &str) -> DbResult { - Ok(self.right.has_field(field_name)? || self.left.has_field(field_name)?) - } - - fn close(&self) -> DbResult<()> { - self.left.close()?; - self.index.close()?; - self.right.close() - } - - fn schema(&self) -> DbResult> { - let s1 = self.left.schema()?; - let s2 = self.right.schema()?; - s1.add_all(&s2)?; - Ok(s1) - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - use std::sync::Arc; - use tempfile::tempdir; - - use crate::schema::Schema; - use crate::{ - SimpleDB, - plan::{Plan, table::TablePlan}, - }; - - #[test] - 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(); - - md.create_table("users", &Arc::new(schema), &tx).unwrap(); - md.create_index("users_ids", "users", "sid", &tx).unwrap(); - - let plan = TablePlan::new(&tx, "users".to_string(), &md).unwrap(); - let s = plan.open().unwrap(); - - let mut indexes = HashMap::new(); - let infos = md.get_index_info("users", &tx).unwrap(); - for (field, info) in infos { - let index = info.open().unwrap(); - indexes.insert(field, index); - } - - 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(); - - let rid = s.get_rid().unwrap(); - for (field, index) in indexes.iter() { - let value = s.get_val(field).unwrap(); - index.insert(value, rid.clone()).unwrap(); - } - - s.before_first().unwrap(); - while s.next().unwrap() { - 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(); - index.delete(value, rid.clone()).unwrap(); - } - s.delete().unwrap(); - break; - } - } - s.before_first().unwrap(); - while s.next().unwrap() { - println!( - "{} {}", - s.get_string("sname").unwrap(), - s.get_i32("sid").unwrap() - ); - } - s.close().unwrap(); - for idx in indexes.values() { - idx.close().unwrap(); - } - tx.commit().unwrap(); - } -} diff --git a/engine/src/scan/merge.rs b/engine/src/scan/merge.rs deleted file mode 100644 index 709cdd6..0000000 --- a/engine/src/scan/merge.rs +++ /dev/null @@ -1,170 +0,0 @@ -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}; - -#[allow(dead_code)] -struct MergeJoinScanLock { - s1: Rc, - s2: Rc, - field_name1: String, - field_name2: String, - join_val: Option, -} - -#[allow(dead_code)] -impl MergeJoinScanLock { - pub fn new( - s1: &Rc, - s2: &Rc, - field_name1: &str, - field_name2: &str, - ) -> DbResult { - let s = Self { - s1: Rc::clone(s1), - s2: Rc::clone(s2), - field_name1: field_name1.to_string(), - field_name2: field_name2.to_string(), - join_val: None, - }; - s.before_first()?; - Ok(s) - } - - fn before_first(&self) -> DbResult<()> { - self.s1.before_first()?; - self.s2.before_first() - } - - fn next(&mut self) -> DbResult { - let Some(join_val) = &self.join_val else { - return Ok(false); - }; - let mut has_more2 = self.s2.next()?; - if has_more2 && self.s2.get_val(&self.field_name2)? == *join_val { - return Ok(true); - } - let mut has_more1 = self.s1.next()?; - if has_more1 && self.s1.get_val(&self.field_name1)? == *join_val { - return Ok(true); - } - - while has_more1 && has_more2 { - let v1 = self.s1.get_val(&self.field_name1)?; - let v2 = self.s2.get_val(&self.field_name2)?; - match v1.cmp(&v2) { - Ordering::Less => has_more1 = self.s1.next()?, - Ordering::Greater => has_more2 = self.s1.next()?, - _ => { - self.s2.save_position()?; - self.join_val = Some(self.s2.get_val(&self.field_name2)?); - return Ok(true); - } - } - } - Ok(false) - } - - fn get_i32(&self, field_name: &str) -> DbResult { - if self.s1.has_field(field_name)? { - self.s1.get_i32(field_name) - } else { - self.s2.get_i32(field_name) - } - } - - fn get_string(&self, field_name: &str) -> DbResult { - if self.s1.has_field(field_name)? { - self.s1.get_string(field_name) - } else { - self.s2.get_string(field_name) - } - } - - fn get_val(&self, field_name: &str) -> DbResult { - if self.s1.has_field(field_name)? { - self.s1.get_val(field_name) - } else { - self.s2.get_val(field_name) - } - } - - fn has_field(&self, field_name: &str) -> DbResult { - Ok(self.s1.has_field(field_name)? || self.s2.has_field(field_name)?) - } - - fn close(&self) -> DbResult<()> { - self.s1.close()?; - self.s2.close() - } - - fn schema(&self) -> DbResult> { - let s1 = self.s1.schema()?; - let s2 = self.s2.schema()?; - s1.add_all(&s2)?; - Ok(s1) - } -} - -#[allow(dead_code)] -pub struct MergeJoinScan { - lock: RefCell, -} - -#[allow(dead_code)] -impl MergeJoinScan { - pub fn new( - s1: &Rc, - s2: &Rc, - field_name1: &str, - field_name2: &str, - ) -> DbResult { - Ok(Self { - lock: RefCell::new(MergeJoinScanLock::new(s1, s2, field_name1, field_name2)?), - }) - } -} - -impl Scan for MergeJoinScan { - fn before_first(&self) -> DbResult<()> { - let read = self.lock.borrow(); - read.before_first() - } - - fn next(&self) -> DbResult { - let mut write = self.lock.borrow_mut(); - write.next() - } - - fn get_i32(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - read.get_i32(field_name) - } - - fn get_string(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - read.get_string(field_name) - } - - fn get_val(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - read.get_val(field_name) - } - - fn has_field(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - read.has_field(field_name) - } - - fn close(&self) -> DbResult<()> { - let read = self.lock.borrow(); - read.close() - } - - 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 deleted file mode 100644 index 769f6cb..0000000 --- a/engine/src/scan/multibuffer.rs +++ /dev/null @@ -1,204 +0,0 @@ -use std::{cell::RefCell, rc::Rc, sync::Arc}; - -use common::{DbResult, error::DbError}; -use transaction::transaction::Transaction; - -use crate::{ - buffer_needs::BufferNeeds, - layout::Layout, - scan::{Scan, chunk::ChunkScan, product::ProductScan}, - schema::Schema, -}; - -struct MultiBufferProductScanInner { - tx: Arc, - left: Rc, - right: Option>, - prod: Option>, - filename: String, - layout: Arc, - chunk_size: i32, - next_block: i32, - file_size: i32, -} - -impl MultiBufferProductScanInner { - pub(crate) fn new( - tx: &Arc, - left: &Rc, - filename: &str, - layout: &Arc, - ) -> DbResult { - let available = tx.available_buffs()? as i32; - let file_size = tx.size(filename)? as i32; - let mut scan = Self { - tx: Arc::clone(tx), - left: Rc::clone(left), - file_size: tx.size(filename)? as i32, - filename: filename.to_string(), - layout: Arc::clone(layout), - chunk_size: BufferNeeds::best_factor(available, file_size), - next_block: 0, - prod: None, - right: None, - }; - scan.before_first()?; - Ok(scan) - } - - fn use_next_chunk(&mut self) -> DbResult { - if let Some(right) = self.right.take() { - right.close()?; - } - self.prod = None; - if self.next_block >= self.file_size { - return Ok(false); - } - let mut end = self.next_block + self.chunk_size - 1; - if end >= self.file_size { - end = self.file_size - 1; - } - let right: Rc = Rc::new(ChunkScan::new( - &self.tx, - &self.filename, - &self.layout, - self.next_block, - end, - )?); - self.left.before_first()?; - self.prod = Some(Rc::new(ProductScan::new(self.left.clone(), right.clone())?)); - self.right = Some(right); - self.next_block = end + 1; - Ok(true) - } - - fn before_first(&mut self) -> DbResult<()> { - self.next_block = 0; - self.use_next_chunk()?; - Ok(()) - } - - fn next(&mut self) -> DbResult { - loop { - let has_next = match &self.prod { - Some(prod) => prod.next()?, - None => false, - }; - if has_next { - return Ok(true); - } - if !self.use_next_chunk()? { - return Ok(false); - } - } - } - - fn get_i32(&self, field_name: &str) -> DbResult { - if let Some(prod) = &self.prod { - prod.get_i32(field_name) - } else { - Err(DbError::other("cannot get prod")) - } - } - - fn get_string(&self, field_name: &str) -> DbResult { - if let Some(prod) = &self.prod { - prod.get_string(field_name) - } else { - Err(DbError::other("cannot get prod")) - } - } - - fn get_val(&self, field_name: &str) -> DbResult { - if let Some(prod) = &self.prod { - prod.get_val(field_name) - } else { - Err(DbError::other("cannot get prod")) - } - } - - fn has_field(&self, field_name: &str) -> DbResult { - if let Some(prod) = &self.prod { - prod.has_field(field_name) - } else { - Err(DbError::other("cannot get prod")) - } - } - - fn close(&self) -> DbResult<()> { - self.left.close()?; - if let Some(right) = &self.right { - right.close()?; - } - Ok(()) - } - - fn schema(&self) -> DbResult> { - if let Some(prod) = &self.prod { - prod.schema() - } else { - Err(DbError::other("cannot get prod")) - } - } -} - -pub(crate) struct MultiBufferProductScan { - lock: RefCell, -} - -impl MultiBufferProductScan { - pub(crate) fn new( - tx: &Arc, - left: &Rc, - filename: &str, - layout: &Arc, - ) -> DbResult { - Ok(Self { - lock: RefCell::new(MultiBufferProductScanInner::new( - tx, left, filename, layout, - )?), - }) - } -} - -impl Scan for MultiBufferProductScan { - fn before_first(&self) -> DbResult<()> { - let mut write = self.lock.borrow_mut(); - write.before_first() - } - - fn next(&self) -> DbResult { - let mut write = self.lock.borrow_mut(); - write.next() - } - - fn get_i32(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - read.get_i32(field_name) - } - - fn get_string(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - read.get_string(field_name) - } - - fn get_val(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - read.get_val(field_name) - } - - fn has_field(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - read.has_field(field_name) - } - - fn close(&self) -> DbResult<()> { - let read = self.lock.borrow(); - read.close() - } - - 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 deleted file mode 100644 index 0fbd5da..0000000 --- a/engine/src/scan/product.rs +++ /dev/null @@ -1,75 +0,0 @@ -use common::DbResult; -use std::rc::Rc; -use std::sync::Arc; - -use crate::scan::Scan; -use crate::schema::Schema; - -pub struct ProductScan { - s1: Rc, - s2: Rc, -} - -impl ProductScan { - pub fn new(s1: Rc, s2: Rc) -> DbResult { - s1.next()?; - Ok(Self { s1, s2 }) - } -} - -impl Scan for ProductScan { - fn before_first(&self) -> DbResult<()> { - self.s1.before_first()?; - self.s1.next()?; - self.s2.before_first() - } - - fn next(&self) -> DbResult { - if self.s2.next()? { - Ok(true) - } else { - self.s2.before_first()?; - Ok(self.s2.next()? && self.s1.next()?) - } - } - - fn get_i32(&self, field_name: &str) -> DbResult { - if self.s1.has_field(field_name)? { - self.s1.get_i32(field_name) - } else { - self.s2.get_i32(field_name) - } - } - - fn get_string(&self, field_name: &str) -> DbResult { - if self.s1.has_field(field_name)? { - self.s1.get_string(field_name) - } else { - self.s2.get_string(field_name) - } - } - - fn get_val(&self, field_name: &str) -> DbResult { - if self.s1.has_field(field_name)? { - self.s1.get_val(field_name) - } else { - self.s2.get_val(field_name) - } - } - - fn has_field(&self, field_name: &str) -> DbResult { - Ok(self.s1.has_field(field_name)? || self.s2.has_field(field_name)?) - } - - fn close(&self) -> DbResult<()> { - self.s1.close()?; - self.s2.close() - } - - fn schema(&self) -> DbResult> { - let s1 = self.s1.schema()?; - let s2 = self.s2.schema()?; - s1.add_all(&s2)?; - Ok(s1) - } -} diff --git a/engine/src/scan/project.rs b/engine/src/scan/project.rs deleted file mode 100644 index 49b7954..0000000 --- a/engine/src/scan/project.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::scan::Scan; -use crate::schema::Schema; -use common::DbResult; -use common::error::DbError; -use std::sync::Arc; -use std::{collections::HashSet, rc::Rc}; - -pub struct ProjectScan { - scan: Rc, - fields: HashSet, -} - -impl ProjectScan { - pub fn new(scan: Rc, fields: HashSet) -> Self { - Self { scan, fields } - } -} - -impl Scan for ProjectScan { - fn before_first(&self) -> DbResult<()> { - self.scan.before_first() - } - - fn next(&self) -> DbResult { - self.scan.next() - } - - fn get_i32(&self, field_name: &str) -> DbResult { - if self.has_field(field_name)? { - self.scan.get_i32(field_name) - } else { - Err(DbError::field_not_exists(field_name)) - } - } - - fn get_string(&self, field_name: &str) -> DbResult { - if self.has_field(field_name)? { - self.scan.get_string(field_name) - } else { - Err(DbError::field_not_exists(field_name)) - } - } - - fn get_val(&self, field_name: &str) -> DbResult { - if self.has_field(field_name)? { - self.scan.get_val(field_name) - } else { - Err(DbError::field_not_exists(field_name)) - } - } - - fn has_field(&self, field_name: &str) -> DbResult { - Ok(self.fields.contains(field_name)) - } - - fn close(&self) -> DbResult<()> { - self.scan.close() - } - - fn schema(&self) -> DbResult> { - let project = Arc::new(Schema::default()); - let schema = self.scan.schema()?; - for (field, info) in schema.fields()? { - if self.fields.contains(&field) { - project.add_field(field, info)?; - } - } - Ok(project) - } -} diff --git a/engine/src/scan/select.rs b/engine/src/scan/select.rs deleted file mode 100644 index 5b7e512..0000000 --- a/engine/src/scan/select.rs +++ /dev/null @@ -1,84 +0,0 @@ -use common::DbResult; -use std::rc::Rc; -use std::sync::Arc; - -use crate::schema::Schema; -use crate::{predicate::Predicate, scan::Scan}; - -pub struct SelectScan { - scan: Rc, - predicate: Predicate, -} - -impl SelectScan { - pub fn new(scan: Rc, predicate: Predicate) -> Self { - Self { scan, predicate } - } -} - -impl Scan for SelectScan { - fn before_first(&self) -> DbResult<()> { - self.scan.before_first() - } - - fn next(&self) -> DbResult { - while self.scan.next()? { - if self.predicate.is_satisfied(&self.scan)? { - return Ok(true); - } - } - Ok(false) - } - - fn get_i32(&self, field_name: &str) -> DbResult { - self.scan.get_i32(field_name) - } - - fn get_string(&self, field_name: &str) -> DbResult { - self.scan.get_string(field_name) - } - - fn get_val(&self, field_name: &str) -> common::DbResult { - self.scan.get_val(field_name) - } - - fn has_field(&self, field_name: &str) -> common::DbResult { - self.scan.has_field(field_name) - } - - fn close(&self) -> DbResult<()> { - self.scan.close() - } - - fn schema(&self) -> DbResult> { - self.scan.schema() - } - - fn set_i32(&self, field_name: &str, value: i32) -> DbResult<()> { - self.scan.set_i32(field_name, value) - } - - fn set_string(&self, field_name: &str, value: &str) -> DbResult<()> { - self.scan.set_string(field_name, value) - } - - fn set_val(&self, field_name: &str, value: crate::constant::Constant) -> DbResult<()> { - self.scan.set_val(field_name, value) - } - - fn insert(&self) -> DbResult<()> { - self.scan.insert() - } - - fn delete(&self) -> DbResult<()> { - self.scan.delete() - } - - fn get_rid(&self) -> DbResult { - self.scan.get_rid() - } - - fn move_to_rid(&self, rid: crate::rid::RID) -> DbResult<()> { - self.scan.move_to_rid(rid) - } -} diff --git a/engine/src/scan/sort.rs b/engine/src/scan/sort.rs deleted file mode 100644 index 211d3c9..0000000 --- a/engine/src/scan/sort.rs +++ /dev/null @@ -1,245 +0,0 @@ -use common::{DbResult, error::DbError}; -use std::sync::Arc; -use std::{cell::RefCell, cmp::Ordering, rc::Rc}; - -use crate::constant::Constant; -use crate::rid::RID; -use crate::scan::Scan; -use crate::schema::Schema; -use crate::temp::TempTable; - -#[derive(Clone)] -pub struct RecordComparator { - fields: Vec, -} - -impl RecordComparator { - pub fn new(fields: Vec) -> Self { - Self { fields } - } - - pub fn compare(&self, s1: &Rc, s2: &Rc) -> DbResult { - for field in &self.fields { - let val1 = s1.get_val(field)?; - let val2 = s2.get_val(field)?; - let result = val1.cmp(&val2); - if result != Ordering::Equal { - return Ok(result); - } - } - Ok(Ordering::Equal) - } -} - -enum CurrentScan { - None, - S1, - S2, -} - -pub struct SortScanLock { - s1: Rc, - s2: Option>, - current_scan: CurrentScan, - comp: RecordComparator, - has_more1: bool, - has_more2: bool, - saved_position: Vec, -} - -impl SortScanLock { - fn new(runs: Vec, comp: RecordComparator) -> DbResult { - let s1 = runs[0].open()?; - let (s2, has_more2) = if runs.len() > 1 { - let s2 = runs[1].open()?; - let has_more2 = s2.next()?; - (Some(s2), has_more2) - } else { - (None, false) - }; - Ok(Self { - comp, - has_more1: s1.next()?, - has_more2, - s1, - s2, - current_scan: CurrentScan::None, - saved_position: vec![], - }) - } -} - -impl SortScanLock { - fn before_first(&mut self) -> DbResult<()> { - self.s1.before_first()?; - self.has_more1 = self.s1.next()?; - if let Some(s2) = &mut self.s2 { - s2.before_first()?; - self.has_more2 = s2.next()?; - } - Ok(()) - } - - fn next(&mut self) -> DbResult { - match self.current_scan { - CurrentScan::S1 => self.has_more1 = self.s1.next()?, - CurrentScan::S2 if let Some(s2) = &self.s2 => self.has_more2 = s2.next()?, - _ => {} - } - if !self.has_more1 && !self.has_more2 { - return Ok(false); - } else if self.has_more1 - && self.has_more2 - && let Some(s2) = &self.s2 - { - if self.comp.compare(&self.s1, s2)? == Ordering::Less { - self.current_scan = CurrentScan::S1; - } else { - self.current_scan = CurrentScan::S2; - } - } else if self.has_more1 { - self.current_scan = CurrentScan::S1; - } else if self.has_more2 { - self.current_scan = CurrentScan::S2; - } - Ok(true) - } - - fn close(&self) -> DbResult<()> { - self.s1.close()?; - if let Some(s2) = &self.s2 { - s2.close()?; - } - Ok(()) - } - - fn get_val(&self, field_name: &str) -> 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), - _ => Err(DbError::other("invalid scan")), - } - } - - fn get_i32(&self, field_name: &str) -> 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), - _ => Err(DbError::other("invalid scan")), - } - } - - fn get_string(&self, field_name: &str) -> 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), - _ => Err(DbError::other("invalid scan")), - } - } - - fn has_field(&self, field_name: &str) -> 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), - _ => Err(DbError::other("invalid scan")), - } - } - - fn save_position(&mut self) -> DbResult<()> { - let rid1 = self.s1.get_rid()?; - if let Some(s2) = &self.s2 { - let rid2 = s2.get_rid()?; - self.saved_position = vec![rid1, rid2]; - } else { - self.saved_position = vec![rid1]; - } - Ok(()) - } - - fn restore_position(&self) -> DbResult<()> { - if let Some(rid1) = self.saved_position.first().cloned() { - self.s1.move_to_rid(rid1)?; - } - if let Some(rid2) = self.saved_position.get(1).cloned() - && let Some(s2) = &self.s2 - { - s2.move_to_rid(rid2)?; - } - Ok(()) - } - - fn schema(&self) -> DbResult> { - let s1 = self.s1.schema()?; - if let Some(s2) = &self.s2 { - let s2 = s2.schema()?; - s1.add_all(&s2)?; - } - Ok(s1) - } -} - -pub struct SortScan { - lock: RefCell, -} - -impl SortScan { - pub fn new(runs: Vec, comp: RecordComparator) -> DbResult { - Ok(Self { - lock: RefCell::new(SortScanLock::new(runs, comp)?), - }) - } - - #[allow(dead_code)] - pub fn save_position(&self) -> DbResult<()> { - let mut write = self.lock.borrow_mut(); - write.save_position() - } - - #[allow(dead_code)] - pub fn restore_position(&self) -> DbResult<()> { - let read = self.lock.borrow(); - read.restore_position() - } -} - -impl Scan for SortScan { - fn before_first(&self) -> DbResult<()> { - let mut write = self.lock.borrow_mut(); - write.before_first() - } - - fn next(&self) -> DbResult { - let mut write = self.lock.borrow_mut(); - write.next() - } - - fn get_i32(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - read.get_i32(field_name) - } - - fn get_string(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - read.get_string(field_name) - } - - fn get_val(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - read.get_val(field_name) - } - - fn has_field(&self, field_name: &str) -> DbResult { - let read = self.lock.borrow(); - read.has_field(field_name) - } - - fn close(&self) -> DbResult<()> { - let read = self.lock.borrow(); - read.close() - } - - fn schema(&self) -> DbResult> { - let read = self.lock.borrow(); - read.schema() - } -} diff --git a/engine/src/scan/table.rs b/engine/src/scan/table.rs deleted file mode 100644 index cb933b7..0000000 --- a/engine/src/scan/table.rs +++ /dev/null @@ -1,317 +0,0 @@ -use std::cell::RefCell; -use std::sync::Arc; - -use common::{DbResult, error::DbError}; -use file::block::BlockId; -use transaction::transaction::Transaction; - -use crate::schema::Schema; -use crate::{ - constant::Constant, field_info::FieldInfo, layout::Layout, record_page::RecordPage, rid::RID, - scan::Scan, -}; - -struct TableScanInner { - tx: Arc, - layout: Arc, - rp: RecordPage, - filename: String, - current_slot: i32, -} - -impl TableScanInner { - fn new(tx: &Arc, table_name: &str, layout: &Arc) -> 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)?; - rp.format()?; - rp - } else { - let block = BlockId::new(&filename, 0); - RecordPage::new(tx, block, layout)? - }; - Ok(Self { - tx: Arc::clone(tx), - layout: Arc::clone(layout), - rp, - current_slot: -1, - filename, - }) - } - - fn close(&self) -> DbResult<()> { - self.tx.unpin(&self.rp.block()) - } - - pub fn before_first(&mut self) -> DbResult<()> { - self.move_to_block(0) - } - - pub fn next(&mut self) -> DbResult { - self.current_slot = self.rp.next_after(self.current_slot)?; - while self.current_slot < 0 { - if self.at_last_block()? { - return Ok(false); - } - self.move_to_block(self.rp.block().num + 1)?; - self.current_slot = self.rp.next_after(self.current_slot)?; - } - Ok(true) - } - - pub fn get_i32(&self, filename: &str) -> DbResult { - self.rp.get_i32(self.current_slot, filename) - } - - pub fn get_string(&self, filename: &str) -> 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)); - }; - match info { - FieldInfo::Integer => Ok(Constant::Integer(self.get_i32(fieldname)?)), - FieldInfo::Varchar(_) => Ok(Constant::Varchar(self.get_string(fieldname)?)), - } - } - - pub fn has_field(&self, fieldname: &str) -> DbResult { - self.layout.schema().has_field(fieldname) - } - - pub fn set_i32(&self, field: &str, 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<()> { - 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<()> { - match value { - Constant::Integer(value) => self.set_i32(field, value), - Constant::Varchar(value) => self.set_string(field, &value), - } - } - - pub fn insert(&mut self) -> DbResult<()> { - let last_block = self.tx.size(&self.filename)? as i32 - 1; - if self.rp.block().num != last_block { - self.move_to_block(last_block)?; - } - self.current_slot = self.rp.insert_after(self.current_slot)?; - while self.current_slot < 0 { - if self.at_last_block()? { - self.move_to_new_block()?; - } else { - self.move_to_block(self.rp.block().num + 1)?; - } - self.current_slot = self.rp.insert_after(self.current_slot)?; - } - Ok(()) - } - - pub fn delete(&self) -> DbResult<()> { - self.rp.delete(self.current_slot) - } - - 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)?; - self.rp = rp; - self.current_slot = rid.slot(); - Ok(()) - } - - pub fn get_rid(&self) -> RID { - let rp = &self.rp; - RID::new(rp.block().num, self.current_slot) - } - - 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.current_slot = -1; - Ok(()) - } - - 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.format()?; - self.current_slot = -1; - Ok(()) - } - - fn at_last_block(&self) -> DbResult { - Ok(self.rp.block().num == self.tx.size(&self.filename)? as i32 - 1) - } - - fn schema(&self) -> DbResult> { - Ok(self.layout.schema()) - } -} - -pub struct TableScan { - lock: RefCell, -} - -impl TableScan { - pub fn new(tx: &Arc, tablename: &str, layout: &Arc) -> DbResult { - Ok(Self { - lock: RefCell::new(TableScanInner::new(tx, tablename, layout)?), - }) - } -} - -impl Scan for TableScan { - fn before_first(&self) -> DbResult<()> { - let mut write = self.lock.borrow_mut(); - write.before_first() - } - - fn next(&self) -> DbResult { - let mut write = self.lock.borrow_mut(); - write.next() - } - - fn get_i32(&self, field: &str) -> DbResult { - let read = self.lock.borrow(); - read.get_i32(field) - } - - fn get_string(&self, field: &str) -> DbResult { - let read = self.lock.borrow(); - read.get_string(field) - } - - fn get_val(&self, field: &str) -> DbResult { - let read = self.lock.borrow(); - read.get_val(field) - } - - fn has_field(&self, field: &str) -> DbResult { - let read = self.lock.borrow(); - read.has_field(field) - } - - fn close(&self) -> DbResult<()> { - let read = self.lock.borrow(); - read.close() - } - - fn set_i32(&self, field: &str, value: i32) -> DbResult<()> { - let read = self.lock.borrow(); - read.set_i32(field, value) - } - - fn set_string(&self, field: &str, value: &str) -> DbResult<()> { - let read = self.lock.borrow(); - read.set_string(field, value) - } - - fn set_val(&self, field: &str, value: Constant) -> DbResult<()> { - let read = self.lock.borrow(); - read.set_val(field, value) - } - - fn insert(&self) -> DbResult<()> { - let mut write = self.lock.borrow_mut(); - write.insert() - } - - fn delete(&self) -> DbResult<()> { - let read = self.lock.borrow(); - read.delete() - } - - fn move_to_rid(&self, rid: RID) -> DbResult<()> { - let mut write = self.lock.borrow_mut(); - write.move_to_rid(rid) - } - - fn get_rid(&self) -> DbResult { - let read = self.lock.borrow(); - Ok(read.get_rid()) - } - - fn schema(&self) -> DbResult> { - let read = self.lock.borrow(); - read.schema() - } -} - -#[cfg(test)] -mod tests { - use buffer::mgr::BufferMgr; - use file::mgr::FileMgr; - use log::mgr::LogMgr; - use tempfile::tempdir; - use transaction::lock_table::LockTable; - - use crate::schema::Schema; - - use super::*; - - #[test] - fn table_scan() { - let dir = tempdir().unwrap(); - 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 lock_table = Arc::new(LockTable::default()); - - let tx = Arc::new(Transaction::new(&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 layout = Arc::new(Layout::new(&schema).unwrap()); - let offset_a = layout.offset("A"); - let offset_b = layout.offset("B"); - assert_ne!(offset_a, offset_b, "fields must occupy distinct offsets"); - - // Fill the table with 50 records carrying known A-values 0..50. - let ts = TableScan::new(&tx, "T", &layout).unwrap(); - ts.before_first().unwrap(); - for n in 0..50 { - ts.insert().unwrap(); - ts.set_i32("A", n).unwrap(); - ts.set_string("B", &format!("record{}", n)).unwrap(); - } - - // 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(); - if a < 10 { - ts.delete().unwrap(); - deleted += 1; - } - } - 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(); - 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 deleted file mode 100644 index 6aa0b99..0000000 --- a/engine/src/schema.rs +++ /dev/null @@ -1,123 +0,0 @@ -use std::{collections::HashMap, fmt::Debug, sync::RwLock}; - -use common::{DbResult, error::DbError}; - -use crate::field_info::FieldInfo; - -#[derive(Debug)] -struct SchemaLock { - fields: Vec, - infos: HashMap, -} - -impl SchemaLock { - fn new() -> Self { - Self { - fields: Vec::new(), - infos: HashMap::new(), - } - } - - 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) { - self.add_field(fieldname, FieldInfo::Integer); - } - - fn add_string_field(&mut self, fieldname: String, length: i32) { - self.add_field(fieldname, FieldInfo::Varchar(length)); - } - - fn add(&mut self, fieldname: String, schema: &Self) { - if let Some(info) = schema.info(&fieldname) { - self.add_field(fieldname, info); - } - } - - fn add_all(&mut self, schema: &Self) { - for (field, info) in &schema.infos { - self.add_field(field.clone(), info.clone()); - } - } - - fn info(&self, fieldname: &str) -> Option { - self.infos.get(fieldname).cloned() - } -} - -pub struct Schema { - lock: RwLock, -} - -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) - } -} - -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 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 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 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(()) - } - - 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 has_field(&self, fieldname: &str) -> DbResult { - let read = self.lock.read().map_err(DbError::lock)?; - Ok(read.infos.contains_key(fieldname)) - } - - pub fn info(&self, fieldname: &str) -> DbResult> { - let read = self.lock.read().map_err(DbError::lock)?; - Ok(read.info(fieldname)) - } - - 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) - } -} - -impl Default for Schema { - fn default() -> Self { - Self { - lock: RwLock::new(SchemaLock::new()), - } - } -} diff --git a/engine/src/stat_mgr.rs b/engine/src/stat_mgr.rs deleted file mode 100644 index f90cfbe..0000000 --- a/engine/src/stat_mgr.rs +++ /dev/null @@ -1,131 +0,0 @@ -use std::{ - collections::HashMap, - 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}, -}; - -#[derive(Clone, Debug)] -pub struct StatInfo { - num_blocks: i32, - num_recs: u16, -} - -impl StatInfo { - pub fn new(num_blocks: i32, num_recs: u16) -> Self { - Self { - num_blocks, - num_recs, - } - } - - pub fn block_accessed(&self) -> i32 { - self.num_blocks - } - - pub fn records_output(&self) -> i32 { - self.num_recs as i32 - } - - pub fn distinct_values(&self) -> i32 { - 1 + (self.num_recs / 3) as i32 - } -} - -struct StatMgrLock { - table_mgr: Arc, - table_stats: HashMap, - num_calls: u64, -} - -impl StatMgrLock { - fn new(table_mgr: &Arc) -> Self { - Self { - table_mgr: Arc::clone(table_mgr), - table_stats: HashMap::new(), - num_calls: 0, - } - } - - fn get_stat_info( - &mut self, - table_name: &str, - layout: &Arc, - tx: &Arc, - ) -> DbResult { - self.num_calls += 1; - if self.num_calls > 100 { - self.refresh_statisic(tx)?; - } - if let Some(stat) = self.table_stats.get(table_name).cloned() { - Ok(stat) - } else { - let stat = self.calc_table_stats(table_name, layout, tx)?; - self.table_stats - .insert(table_name.to_string(), stat.clone()); - Ok(stat) - } - } - - 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)?; - while ts.next()? { - let table_name = ts.get_string(TABLE_NAME)?; - let layout = self.table_mgr.get_layout(&table_name, tx)?; - let stat = self.calc_table_stats(&table_name, &Arc::new(layout), tx)?; - self.table_stats.insert(table_name, stat); - } - ts.close() - } - - fn calc_table_stats( - &self, - table_name: &str, - layout: &Arc, - tx: &Arc, - ) -> DbResult { - let mut num_recs = 0; - let mut num_blocks = 0; - let ts = TableScan::new(tx, table_name, layout)?; - while ts.next()? { - num_recs += 1; - num_blocks = ts.get_rid()?.block_num() + 1; - } - ts.close()?; - Ok(StatInfo::new(num_blocks, num_recs)) - } -} - -pub struct StatMgr { - lock: RwLock, -} - -impl StatMgr { - pub fn new(table_mgr: &Arc, tx: &Arc) -> DbResult { - let mut lock = StatMgrLock::new(table_mgr); - lock.refresh_statisic(tx)?; - Ok(Self { - lock: RwLock::new(lock), - }) - } - - pub fn get_stat_info( - &self, - table_name: &str, - layout: &Arc, - tx: &Arc, - ) -> DbResult { - let mut write = self.lock.write().map_err(DbError::lock)?; - write.get_stat_info(table_name, layout, tx) - } -} diff --git a/engine/src/table_mgr.rs b/engine/src/table_mgr.rs deleted file mode 100644 index 39e8342..0000000 --- a/engine/src/table_mgr.rs +++ /dev/null @@ -1,143 +0,0 @@ -use std::{collections::HashMap, sync::Arc}; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::{ - field_info::FieldInfo, - layout::Layout, - scan::{Scan, table::TableScan}, - schema::Schema, -}; - -const MAX_NAME: i32 = 16; - -pub(crate) const TABLE_NAME: &str = "sp_table"; -const TABLE_SLOT_SIZE: &str = "slot_size"; -const FIELDS_NAME: &str = "sp_fields"; - -const F_TYPE: &str = "type"; -const F_FIELD_NAME: &str = "field"; -const F_TYPE_LENGTH: &str = "length"; -const F_OFFSET: &str = "offset"; - -pub struct TableMgr { - table_catalog_layout: Arc, - fields_catalog_layout: Arc, -} - -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 mgr = Self { - table_catalog_layout: Arc::new(table_catalog_layout), - fields_catalog_layout: Arc::new(fields_catalog_layout), - }; - - if is_new { - mgr.create_table(TABLE_NAME, &table_catalog_schema, tx)?; - mgr.create_table(FIELDS_NAME, &fields_catalog_schema, tx)?; - } - - Ok(mgr) - } - - pub fn create_table( - &self, - table_name: &str, - schema: &Arc, - tx: &Arc, - ) -> DbResult<()> { - let layout = Layout::new(schema)?; - - let tcat = TableScan::new(tx, TABLE_NAME, &self.table_catalog_layout)?; - tcat.insert()?; - tcat.set_string(TABLE_NAME, table_name)?; - tcat.set_i32(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()? { - 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.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)?; - while tcat.next()? { - if tcat.get_string(TABLE_NAME)? == table_name { - size = tcat.get_i32(TABLE_SLOT_SIZE)?; - break; - } - } - tcat.close()?; - let schema = Arc::new(Schema::default()); - let mut offsets = HashMap::new(); - let fcat = TableScan::new(tx, FIELDS_NAME, &self.fields_catalog_layout)?; - while fcat.next()? { - let table = fcat.get_string(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); - } - } - fcat.close()?; - Ok(Layout::from(&schema, offsets, size)) - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashSet; - - use super::*; - use crate::SimpleDB; - use tempfile::tempdir; - - #[test] - fn manager() { - let dir = tempdir().unwrap(); - let db = SimpleDB::new(dir.path()).unwrap(); - - 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(); - - 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() { - assert!(expected_fields.contains(&(field_name, info))); - } - tx.commit().unwrap(); - } -} diff --git a/engine/src/temp.rs b/engine/src/temp.rs deleted file mode 100644 index dfda0e4..0000000 --- a/engine/src/temp.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::layout::Layout; -use crate::scan::Scan; -use crate::scan::table::TableScan; -use crate::schema::Schema; -use common::DbResult; -use std::rc::Rc; -use std::sync::atomic::AtomicI32; -use std::sync::{Arc, atomic}; -use transaction::transaction::Transaction; - -static TABLE_NUM_COUNTER: AtomicI32 = AtomicI32::new(0); - -fn next_table_num() -> i32 { - TABLE_NUM_COUNTER.fetch_add(1, atomic::Ordering::SeqCst) -} - -#[derive(Clone)] -pub struct TempTable { - tx: Arc, - table_name: String, - layout: Arc, -} - -impl TempTable { - pub fn new(tx: &Arc, schema: &Arc) -> DbResult { - Ok(Self { - table_name: format!("temp_{}", next_table_num()), - tx: Arc::clone(tx), - layout: Arc::new(Layout::new(schema)?), - }) - } - - pub fn open(&self) -> DbResult> { - Ok(Rc::new(TableScan::new( - &self.tx, - &self.table_name, - &self.layout, - )?)) - } - - pub(crate) fn table_name(&self) -> String { - self.table_name.clone() - } - - pub(crate) fn layout(&self) -> Arc { - self.layout.clone() - } -} diff --git a/engine/src/view_mgr.rs b/engine/src/view_mgr.rs deleted file mode 100644 index 77de05b..0000000 --- a/engine/src/view_mgr.rs +++ /dev/null @@ -1,58 +0,0 @@ -use std::sync::Arc; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::{ - scan::{Scan, table::TableScan}, - schema::Schema, - table_mgr::TableMgr, -}; - -const MAX_NAME: i32 = 16; -const MAX_VIEW_DEF: i32 = 100; - -const VIEW_NAME: &str = "view"; -const VIEW_DEF: &str = "view_def"; -const VIEW_TABLE: &str = "sp_view"; - -pub struct ViewMgr { - table_mgr: Arc, -} - -impl ViewMgr { - pub fn new(is_new: bool, table_mgr: &Arc, 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)?; - } - Ok(Self { - table_mgr: Arc::clone(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)?; - ts.insert()?; - ts.set_string(VIEW_NAME, name)?; - ts.set_string(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 mut result = None; - while ts.next()? { - if ts.get_string(VIEW_NAME)? == name { - result = Some(ts.get_string(VIEW_DEF)?); - break; - } - } - ts.close()?; - Ok(result) - } -} diff --git a/planner/src/element.rs b/planner/src/element.rs index 789e730..da100f9 100644 --- a/planner/src/element.rs +++ b/planner/src/element.rs @@ -1,3 +1,6 @@ +use common::DbResult; +use common::error::DbError; + #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Element { Raw(String), @@ -17,6 +20,13 @@ impl Element { 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), + } + } } impl std::fmt::Display for Element { diff --git a/planner/src/plan.rs b/planner/src/plan.rs index c28f51f..03c3b6b 100644 --- a/planner/src/plan.rs +++ b/planner/src/plan.rs @@ -14,6 +14,7 @@ use crate::{ pub(crate) mod select; pub(crate) mod table; +mod project; pub(crate) enum Planner { Table(TablePlan), @@ -31,6 +32,10 @@ impl Plan { Self(Planner::Select(SelectPlan::new(plan, predicate))) } + pub fn project(plan: Box, predicate: Predicate) -> Self { + Self(Planner::Select(SelectPlan::new(plan, predicate))) + } + pub fn open(&self) -> DbResult { match &self.0 { Planner::Table(table) => table.open(), diff --git a/planner/src/plan/project.rs b/planner/src/plan/project.rs new file mode 100644 index 0000000..480ef33 --- /dev/null +++ b/planner/src/plan/project.rs @@ -0,0 +1,55 @@ +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, +}; + +use common::DbResult; + +use crate::schema::SchemaBuilder; +use crate::{element::Element, plan::Plan, scan::Scan, schema::Schema}; + +pub(crate) struct ProjectPlan { + plan: Box, + schema: Schema, + mapping: HashMap, +} + +impl ProjectPlan { + pub fn new(plan: Box, fields: Vec) -> DbResult { + let mut schema = SchemaBuilder::default(); + for field in fields { + let other = plan.schema()?; + schema = schema.add(field, &other); + } + let schema = schema.build(); + Ok(Self { + plan, + schema, + mapping: HashMap::new(), + }) + } +} + +impl ProjectPlan { + pub(crate) fn open(&self) -> DbResult { + let scan = self.plan.open()?; + let fields: HashSet = self.schema.fields().into_iter().map(|(f, _)| f).collect(); + Ok(Scan::project(Box::new(scan), fields)) + } + + pub(crate) fn blocks_accessed(&self) -> DbResult { + self.plan.blocks_accessed() + } + + pub(crate) fn records_output(&self) -> DbResult { + self.plan.records_output() + } + + pub(crate) fn distinct_values(&self, field_name: &Element) -> DbResult { + self.plan.distinct_values(field_name) + } + + pub(crate) fn schema(&self) -> Schema { + self.schema.clone() + } +} diff --git a/planner/src/scan.rs b/planner/src/scan.rs index 7beb4a4..c44e46e 100644 --- a/planner/src/scan.rs +++ b/planner/src/scan.rs @@ -1,9 +1,12 @@ +use std::collections::HashSet; use std::sync::Arc; use common::DbResult; use transaction::transaction::Transaction; use crate::scan::chunk::ChunkScan; +use crate::scan::product::ProductScan; +use crate::scan::project::ProjectScan; use crate::{ element::Element, layout::Layout, @@ -13,7 +16,6 @@ use crate::{ schema::Schema, value::Value, }; -use crate::scan::product::ProductScan; pub(crate) mod chunk; pub(crate) mod group; @@ -53,6 +55,13 @@ impl Scan { }) } + pub fn project(s1: Box, fields: HashSet) -> Self { + let project = ProjectScan::new(s1, fields); + Self { + scan: Scanner::Project(project), + } + } + pub fn chunk( tx: &Arc, filename: &str, diff --git a/planner/src/scan/project.rs b/planner/src/scan/project.rs index aac89ec..8449efe 100644 --- a/planner/src/scan/project.rs +++ b/planner/src/scan/project.rs @@ -22,7 +22,7 @@ impl ProjectScan { self.scan.before_first() } - pub(crate) fn next(&mut self) -> DbResult { + pub(crate) fn next_row(&mut self) -> DbResult { self.scan.next_row() } diff --git a/planner/src/scan/scanner.rs b/planner/src/scan/scanner.rs index 37f1c8c..9bbb9e2 100644 --- a/planner/src/scan/scanner.rs +++ b/planner/src/scan/scanner.rs @@ -9,12 +9,14 @@ use crate::{ }; use crate::scan::chunk::ChunkScan; use crate::scan::product::ProductScan; +use crate::scan::project::ProjectScan; pub(crate) enum Scanner { Table(TableScan), Select(SelectScan), Chunk(ChunkScan), Product(ProductScan), + Project(ProjectScan), } impl Scanner { @@ -27,6 +29,7 @@ impl Scanner { Ok(()) } Self::Product(product) => product.before_first(), + Self::Project(product) => product.before_first(), } } @@ -36,6 +39,7 @@ impl Scanner { Self::Select(select) => select.next(), Self::Chunk(chunk) => chunk.next(), Self::Product(product) => product.next_row(), + Self::Project(product) => product.next_row(), } } @@ -45,6 +49,7 @@ impl Scanner { Self::Select(select) => select.get_i32(field), Self::Chunk(chunk) => chunk.get_i32(field), Self::Product(product) => product.get_i32(field), + Self::Project(product) => product.get_i32(field), } } @@ -54,6 +59,7 @@ impl Scanner { Self::Select(select) => select.get_string(field), Self::Chunk(chunk) => chunk.get_string(field), Self::Product(product) => product.get_string(field), + Self::Project(product) => product.get_string(field), } } @@ -63,6 +69,7 @@ impl Scanner { Self::Select(select) => select.get_val(field), Self::Chunk(chunk) => chunk.get_val(field), Self::Product(product) => product.get_val(field), + Self::Project(product) => product.get_val(field), } } @@ -72,6 +79,7 @@ impl Scanner { Self::Select(select) => select.has_field(field), Self::Chunk(chunk) => Ok(chunk.has_field(field)), Self::Product(product) => product.has_field(field), + Self::Project(product) => product.has_field(field), } } @@ -81,6 +89,7 @@ impl Scanner { Self::Select(select) => select.close(), Self::Chunk(chunk) => chunk.close(), Self::Product(product) => product.close(), + Self::Project(product) => product.close(), } } @@ -90,6 +99,7 @@ impl Scanner { Self::Select(select) => select.schema(), Self::Chunk(chunk) => Ok(chunk.schema()), Self::Product(product) => product.schema(), + Self::Project(product) => product.schema(), } } @@ -97,7 +107,7 @@ impl Scanner { match self { Self::Table(table) => table.set_i32(field, value), Self::Select(select) => select.set_i32(field, value), - Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), + _ => Err(Self::read_only()), } } @@ -105,7 +115,7 @@ impl Scanner { match self { Self::Table(table) => table.set_string(field, value), Self::Select(select) => select.set_string(field, value), - Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), + _ => Err(Self::read_only()), } } @@ -113,7 +123,7 @@ impl Scanner { match self { Self::Table(table) => table.set_val(field, value), Self::Select(select) => select.set_val(field, value), - Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), + _ => Err(Self::read_only()), } } @@ -121,7 +131,7 @@ impl Scanner { match self { Self::Table(table) => table.insert(), Self::Select(select) => select.insert(), - Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), + _ => Err(Self::read_only()), } } @@ -129,7 +139,7 @@ impl Scanner { match self { Self::Table(table) => table.delete(), Self::Select(select) => select.delete(), - Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), + _ => Err(Self::read_only()), } } @@ -137,7 +147,7 @@ impl Scanner { match self { Self::Table(table) => Ok(table.get_rid()), Self::Select(select) => select.get_rid(), - Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), + _ => Err(Self::read_only()), } } @@ -145,7 +155,7 @@ impl Scanner { match self { Self::Table(table) => table.move_to_rid(rid), Self::Select(select) => select.move_to_rid(rid), - Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), + _ => Err(Self::read_only()), } } From f8048a5fe824654b9088b88fde1533936da45de5 Mon Sep 17 00:00:00 2001 From: Daniil Buravlev Date: Tue, 30 Jun 2026 21:31:48 +0300 Subject: [PATCH 10/13] Revert "Break ALL" This reverts commit c5c84976a200bdb2fd6a2a387808b4ca19ad4327. --- Cargo.lock | 1 - engine/Cargo.toml | 1 - engine/src/buffer_needs.rs | 31 +++ engine/src/constant.rs | 66 +++++ engine/src/element.rs | 28 +++ engine/src/field_info.rs | 44 ++++ engine/src/index.rs | 64 +++++ engine/src/index/b_tree.rs | 262 ++++++++++++++++++++ engine/src/index/btree_dir.rs | 124 ++++++++++ engine/src/index/btree_leaf.rs | 173 +++++++++++++ engine/src/index/btree_page.rs | 342 ++++++++++++++++++++++++++ engine/src/index/dir_entry.rs | 12 + engine/src/index_mgr.rs | 154 ++++++++++++ engine/src/layout.rs | 52 ++++ engine/src/lib.rs | 41 ++- engine/src/metadata_mgr.rs | 95 +++++++ engine/src/plan.rs | 28 +++ engine/src/plan/group.rs | 77 ++++++ engine/src/plan/index.rs | 225 +++++++++++++++++ engine/src/plan/materialize.rs | 59 +++++ engine/src/plan/merge.rs | 77 ++++++ engine/src/plan/multibuffer.rs | 92 +++++++ engine/src/plan/product.rs | 54 ++++ engine/src/plan/project.rs | 59 +++++ engine/src/plan/select.rs | 54 ++++ engine/src/plan/sort.rs | 139 +++++++++++ engine/src/plan/table.rs | 59 +++++ engine/src/predicate.rs | 245 ++++++++++++++++++ engine/src/query/basic_planner.rs | 37 +-- engine/src/query/command.rs | 30 ++- engine/src/query/heuristic_planner.rs | 53 ++-- engine/src/query/parser.rs | 2 +- engine/src/query/table_planner.rs | 32 ++- engine/src/query/token.rs | 9 +- engine/src/record_page.rs | 194 +++++++++++++++ engine/src/rid.rs | 22 ++ engine/src/scan.rs | 66 +++++ engine/src/scan/chunk.rs | 180 ++++++++++++++ engine/src/scan/group.rs | 223 +++++++++++++++++ engine/src/scan/index.rs | 234 ++++++++++++++++++ engine/src/scan/merge.rs | 170 +++++++++++++ engine/src/scan/multibuffer.rs | 204 +++++++++++++++ engine/src/scan/product.rs | 75 ++++++ engine/src/scan/project.rs | 70 ++++++ engine/src/scan/select.rs | 84 +++++++ engine/src/scan/sort.rs | 245 ++++++++++++++++++ engine/src/scan/table.rs | 317 ++++++++++++++++++++++++ engine/src/schema.rs | 123 +++++++++ engine/src/{order.rs => sort_by.rs} | 6 +- engine/src/stat_mgr.rs | 131 ++++++++++ engine/src/table_mgr.rs | 143 +++++++++++ engine/src/temp.rs | 48 ++++ engine/src/view_mgr.rs | 58 +++++ planner/src/element.rs | 10 - planner/src/plan.rs | 5 - planner/src/plan/project.rs | 55 ----- planner/src/scan.rs | 11 +- planner/src/scan/project.rs | 2 +- planner/src/scan/scanner.rs | 24 +- 59 files changed, 5347 insertions(+), 174 deletions(-) create mode 100644 engine/src/buffer_needs.rs create mode 100644 engine/src/constant.rs create mode 100644 engine/src/element.rs create mode 100644 engine/src/field_info.rs create mode 100644 engine/src/index.rs create mode 100644 engine/src/index/b_tree.rs create mode 100644 engine/src/index/btree_dir.rs create mode 100644 engine/src/index/btree_leaf.rs create mode 100644 engine/src/index/btree_page.rs create mode 100644 engine/src/index/dir_entry.rs create mode 100644 engine/src/index_mgr.rs create mode 100644 engine/src/layout.rs create mode 100644 engine/src/metadata_mgr.rs create mode 100644 engine/src/plan.rs create mode 100644 engine/src/plan/group.rs create mode 100644 engine/src/plan/index.rs create mode 100644 engine/src/plan/materialize.rs create mode 100644 engine/src/plan/merge.rs create mode 100644 engine/src/plan/multibuffer.rs create mode 100644 engine/src/plan/product.rs create mode 100644 engine/src/plan/project.rs create mode 100644 engine/src/plan/select.rs create mode 100644 engine/src/plan/sort.rs create mode 100644 engine/src/plan/table.rs create mode 100644 engine/src/predicate.rs create mode 100644 engine/src/record_page.rs create mode 100644 engine/src/rid.rs create mode 100644 engine/src/scan.rs create mode 100644 engine/src/scan/chunk.rs create mode 100644 engine/src/scan/group.rs create mode 100644 engine/src/scan/index.rs create mode 100644 engine/src/scan/merge.rs create mode 100644 engine/src/scan/multibuffer.rs create mode 100644 engine/src/scan/product.rs create mode 100644 engine/src/scan/project.rs create mode 100644 engine/src/scan/select.rs create mode 100644 engine/src/scan/sort.rs create mode 100644 engine/src/scan/table.rs create mode 100644 engine/src/schema.rs rename engine/src/{order.rs => sort_by.rs} (84%) create mode 100644 engine/src/stat_mgr.rs create mode 100644 engine/src/table_mgr.rs create mode 100644 engine/src/temp.rs create mode 100644 engine/src/view_mgr.rs delete mode 100644 planner/src/plan/project.rs diff --git a/Cargo.lock b/Cargo.lock index 73fdc6b..6c60576 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -325,7 +325,6 @@ dependencies = [ "criterion", "file", "log 0.0.1", - "planner", "rand", "tempfile", "tracing", diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 93353b0..dc37d1c 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -15,7 +15,6 @@ file = { path = "../file" } transaction = { path = "../transaction" } tracing = { workspace = true } rand = { workspace = true } -planner = { path = "../planner" } [dev-dependencies] criterion = { workspace = true } diff --git a/engine/src/buffer_needs.rs b/engine/src/buffer_needs.rs new file mode 100644 index 0000000..54ca664 --- /dev/null +++ b/engine/src/buffer_needs.rs @@ -0,0 +1,31 @@ +pub struct BufferNeeds {} + +impl BufferNeeds { + pub fn best_root(available: i32, size: i32) -> i32 { + let available = available - 2; + if available <= 1 { + return 1; + } + let mut k = i32::MAX; + let mut i = 1f32; + while k > available { + i += 1f32; + k = (size as f32).powf(1.0 / i).ceil() as i32; + } + k + } + + pub fn best_factor(availabe: i32, size: i32) -> i32 { + let available = availabe - 2; + if available <= 1 { + return 1; + } + let mut k = size; + let mut i: f32 = 1.0; + while k > available { + i += 1f32; + k = (size as f32 / i).ceil() as i32; + } + k + } +} diff --git a/engine/src/constant.rs b/engine/src/constant.rs new file mode 100644 index 0000000..6fa338f --- /dev/null +++ b/engine/src/constant.rs @@ -0,0 +1,66 @@ +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..66a104b --- /dev/null +++ b/engine/src/element.rs @@ -0,0 +1,28 @@ +use common::{DbResult, error::DbError}; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) enum Element { + Raw(String), + Id { source: String, name: String }, + Specifier { source: String, spec: String }, +} + +impl Element { + pub(crate) fn source(&self) -> DbResult<&str> { + match self { + Self::Raw(value) => Ok(value), + Self::Specifier { source, .. } => Ok(source), + e => Err(DbError::UnexpectedToken(e.to_string())), + } + } +} + +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::Id { source, name } => write!(f, "{} {}", source, name), + Self::Specifier { source, spec } => write!(f, "{}.{}", source, spec), + } + } +} diff --git a/engine/src/field_info.rs b/engine/src/field_info.rs new file mode 100644 index 0000000..a3e03ec --- /dev/null +++ b/engine/src/field_info.rs @@ -0,0 +1,44 @@ +use common::{DbResult, error::DbError}; +use file::page::I32_SIZE; + +const INTEGER_TYPE: i32 = 1; +const VARCHAR_TYPE: i32 = 2; + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum FieldInfo { + Integer, + Varchar(i32), +} + +impl FieldInfo { + pub fn new(field_type: i32, len: i32) -> DbResult { + let info = match field_type { + INTEGER_TYPE => Self::Integer, + VARCHAR_TYPE => Self::Varchar((len - I32_SIZE as i32) / 4), + _ => return Err(DbError::UnknownType), + }; + Ok(info) + } + + pub fn type_id(&self) -> i32 { + match self { + Self::Integer => INTEGER_TYPE, + Self::Varchar(_) => VARCHAR_TYPE, + } + } + pub fn length(&self) -> i32 { + match self { + Self::Integer => I32_SIZE as i32, + Self::Varchar(len) => I32_SIZE as i32 + *len * 4, + } + } +} + +impl std::fmt::Display for FieldInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Integer => write!(f, "INTEGER"), + Self::Varchar(len) => write!(f, "VARCHAR({})", len), + } + } +} diff --git a/engine/src/index.rs b/engine/src/index.rs new file mode 100644 index 0000000..d7ebc25 --- /dev/null +++ b/engine/src/index.rs @@ -0,0 +1,64 @@ +use common::DbResult; + +use crate::{constant::Constant, rid::RID}; + +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 next(&self) -> DbResult; + + fn get_data_rid(&self) -> DbResult; + + fn insert(&self, value: Constant, rid: RID) -> DbResult<()>; + + fn delete(&self, value: Constant, rid: RID) -> DbResult<()>; + + fn close(&self) -> DbResult<()>; +} + +#[cfg(test)] +mod tests { + use tempfile::tempdir; + + use crate::{ + SimpleDB, + plan::{Plan, table::TablePlan}, + }; + + #[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 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(); + + md.create_table(table, &schema, &setup_tx).unwrap(); + md.create_index("idx_majorid", table, "majorid", &setup_tx) + .unwrap(); + setup_tx.commit().unwrap(); + + let tx = db.get_tx().unwrap(); + let plan = TablePlan::new(&tx, table.to_string(), &md).unwrap(); + let _scan = plan.open().unwrap(); + + let indexes = md.get_index_info(table, &tx).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 new file mode 100644 index 0000000..8bc9c82 --- /dev/null +++ b/engine/src/index/b_tree.rs @@ -0,0 +1,262 @@ +use std::sync::{Arc, RwLock}; + +use common::{DbResult, error::DbError}; +use file::block::BlockId; +use transaction::transaction::Transaction; + +use crate::{ + constant::Constant, + field_info::FieldInfo, + index::{ + Index, + btree_dir::BTreeDir, + btree_leaf::BTreeLeaf, + btree_page::{BLOCK, BTreePage, VALUE}, + }, + layout::Layout, + schema::Schema, +}; + +struct BTreeIndexLock { + tx: Arc, + dir_layout: Arc, + leaf_layout: Arc, + leaf_table: String, + leaf: Option, + root_block: BlockId, +} + +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()?; + } + Ok(Self { + tx: Arc::clone(tx), + dir_layout, + leaf_layout: Arc::clone(leaf_layout), + leaf_table, + leaf: None, + root_block, + }) + } + + 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 next(&self) -> DbResult { + if let Some(leaf) = &self.leaf { + leaf.next() + } else { + Ok(false) + } + } + + fn get_data_rid(&self) -> DbResult { + if let Some(leaf) = &self.leaf { + leaf.get_data_rid() + } else { + Err(DbError::other("cannot find RID")) + } + } + + 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)?; + } + 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)?; + } + Ok(()) + } + + fn close(&self) -> DbResult<()> { + if let Some(leaf) = &self.leaf { + leaf.close()?; + } + Ok(()) + } +} + +pub struct BTreeIndex { + lock: RwLock, +} + +impl BTreeIndex { + pub fn new(tx: &Arc, name: &str, leaf_layout: &Arc) -> DbResult { + Ok(Self { + lock: RwLock::new(BTreeIndexLock::new(tx, name, leaf_layout)?), + }) + } +} + +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 next(&self) -> DbResult { + let read = self.lock.read().map_err(DbError::lock)?; + read.next() + } + + fn get_data_rid(&self) -> DbResult { + let read = self.lock.read().map_err(DbError::lock)?; + read.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 delete(&self, value: Constant, rid: crate::rid::RID) -> DbResult<()> { + let mut write = self.lock.write().map_err(DbError::lock)?; + write.delete(value, rid) + } + + fn close(&self) -> DbResult<()> { + let read = self.lock.read().map_err(DbError::lock)?; + read.close() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::SimpleDB; + use crate::rid::RID; + use tempfile::tempdir; + + #[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()); + } + + #[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(); + + index.before_first(Constant::varchar("hello")).unwrap(); + assert!(!index.next().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(); + } + } +} diff --git a/engine/src/index/btree_dir.rs b/engine/src/index/btree_dir.rs new file mode 100644 index 0000000..e581074 --- /dev/null +++ b/engine/src/index/btree_dir.rs @@ -0,0 +1,124 @@ +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 new file mode 100644 index 0000000..47c609b --- /dev/null +++ b/engine/src/index/btree_leaf.rs @@ -0,0 +1,173 @@ +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 new file mode 100644 index 0000000..dbfa900 --- /dev/null +++ b/engine/src/index/btree_page.rs @@ -0,0 +1,342 @@ +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 new file mode 100644 index 0000000..91d6fdb --- /dev/null +++ b/engine/src/index/dir_entry.rs @@ -0,0 +1,12 @@ +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_mgr.rs b/engine/src/index_mgr.rs new file mode 100644 index 0000000..27de484 --- /dev/null +++ b/engine/src/index_mgr.rs @@ -0,0 +1,154 @@ +use std::{collections::HashMap, rc::Rc, sync::Arc}; + +use common::DbResult; +use common::error::DbError; +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, +}; + +const IDX_TABLE: &str = "idx"; +const IDX_NAME: &str = "idx_name"; +const TABLE_NAME: &str = "table_name"; +const FIELD_NAME: &str = "field_name"; +const MAX_LENGTH: i32 = 16; + +#[derive(Clone)] +pub struct IndexInfo { + idx_name: String, + field_name: String, + tx: Arc, + layout: Arc, + stat: StatInfo, +} + +impl IndexInfo { + pub fn new( + idx_name: String, + field_name: String, + schema: &Arc, + tx: &Arc, + stat: StatInfo, + ) -> DbResult { + let layout = Arc::new(Self::create_idx_layout(&field_name, schema)?); + Ok(Self { + idx_name, + 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, + )?)) + } + + pub fn block_accessed(&self) -> DbResult { + let rpb = self.tx.block_size() / self.layout.slotsize(); + let num_blocks = self.stat.records_output() / rpb; + Ok(num_blocks) + } + + pub fn records_output(&self) -> i32 { + self.stat.records_output() / self.stat.distinct_values() + } + + pub fn distinct_values(&self, field_name: &str) -> i32 { + if self.field_name == field_name { + 1 + } else { + self.stat.distinct_values() + } + } +} + +pub struct IndexMgr { + layout: Arc, + table_mgr: Arc, + stat_mgr: Arc, +} + +impl IndexMgr { + pub fn new( + is_new: bool, + table_mgr: &Arc, + stat_mgr: &Arc, + 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 layout = Arc::new(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), + }) + } + + pub fn create_index( + &self, + idx_name: &str, + table_name: &str, + field_name: &str, + tx: &Arc, + ) -> DbResult<()> { + let ts = TableScan::new(tx, IDX_TABLE, &self.layout)?; + ts.insert()?; + ts.set_string(IDX_NAME, idx_name)?; + ts.set_string(TABLE_NAME, table_name)?; + ts.set_string(FIELD_NAME, field_name)?; + ts.close() + } + + pub fn get_index_info( + &self, + table_name: &str, + tx: &Arc, + ) -> DbResult> { + let mut result = HashMap::new(); + let ts = TableScan::new(tx, IDX_TABLE, &self.layout)?; + 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); + } + } + ts.close()?; + Ok(result) + } +} diff --git a/engine/src/layout.rs b/engine/src/layout.rs new file mode 100644 index 0000000..00f7d3d --- /dev/null +++ b/engine/src/layout.rs @@ -0,0 +1,52 @@ +use std::{collections::HashMap, sync::Arc}; + +use common::DbResult; +use file::page::I32_SIZE; + +use crate::schema::Schema; + +pub struct Layout { + schema: Arc, + offsets: HashMap, + slotsize: i32, +} + +impl Layout { + pub fn new(schema: &Arc) -> DbResult { + let mut offsets = HashMap::new(); + let mut pos = I32_SIZE as i32; + for (field, info) in schema.fields()? { + offsets.insert(field, pos); + pos += info.length(); + } + Ok(Self { + schema: Arc::clone(schema), + offsets, + slotsize: pos, + }) + } + + pub fn from(schema: &Arc, offsets: HashMap, slotsize: i32) -> Self { + Self { + schema: Arc::clone(schema), + offsets, + slotsize, + } + } + + pub fn offset(&self, fieldname: &str) -> i32 { + if let Some(offset) = self.offsets.get(fieldname) { + *offset + } else { + 0 + } + } + + pub fn schema(&self) -> Arc { + Arc::clone(&self.schema) + } + + pub fn slotsize(&self) -> i32 { + self.slotsize + } +} diff --git a/engine/src/lib.rs b/engine/src/lib.rs index f992a21..8db3cbd 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -4,16 +4,37 @@ use buffer::mgr::BufferMgr; use common::DbResult; use file::mgr::FileMgr; use log::mgr::LogMgr; -use planner::mgr::metadata::MetadataMgr; -use planner::scan::Scan; use transaction::{lock_table::LockTable, transaction::Transaction}; -use crate::query::{ - basic_planner::BasicUpdatePlanner, heuristic_planner::HeuristicQueryPlanner, planner::Planner, +use crate::{ + metadata_mgr::MetadataMgr, + query::{ + basic_planner::BasicUpdatePlanner, heuristic_planner::HeuristicQueryPlanner, + planner::Planner, + }, + scan::Scan, }; +pub mod buffer_needs; +pub mod constant; +pub(crate) mod element; +pub mod field_info; +pub mod index; +pub mod index_mgr; +pub mod layout; +pub mod metadata_mgr; +pub mod plan; +pub mod predicate; pub mod query; -mod order; +pub mod record_page; +pub mod rid; +pub mod scan; +pub mod schema; +pub mod sort_by; +pub mod stat_mgr; +pub mod table_mgr; +mod temp; +pub mod view_mgr; const LOG_FILE: &str = "wal.log"; const BLOCK_SIZE: usize = 8 * 1024; @@ -24,7 +45,7 @@ pub struct SimpleDB { lm: Arc, bm: Arc, lock_table: Arc, - md: MetadataMgr, + md: Arc, } impl SimpleDB { @@ -45,7 +66,7 @@ impl SimpleDB { tracing::debug!("recovering existing database"); tx.recover()?; } - let md = MetadataMgr::new(is_new, &tx)?; + let md = Arc::new(MetadataMgr::new(is_new, &tx)?); tx.commit()?; Ok(Self { fm, @@ -61,11 +82,11 @@ impl SimpleDB { Ok(Arc::new(tx)) } - pub fn metadata_mgr(&self) -> &MetadataMgr { - &self.md + pub fn metadata_mgr(&self) -> Arc { + Arc::clone(&self.md) } - pub fn query(&self, tx: &Arc, query: &str) -> DbResult { + pub fn query(&self, tx: &Arc, query: &str) -> DbResult> { let planner = self.planner(); let plan = planner.create_query_plan(query, tx)?; plan.open() diff --git a/engine/src/metadata_mgr.rs b/engine/src/metadata_mgr.rs new file mode 100644 index 0000000..e401a2e --- /dev/null +++ b/engine/src/metadata_mgr.rs @@ -0,0 +1,95 @@ +use std::{collections::HashMap, sync::Arc}; + +use common::DbResult; +use transaction::transaction::Transaction; + +use crate::{ + index_mgr::{IndexInfo, IndexMgr}, + layout::Layout, + schema::Schema, + stat_mgr::{StatInfo, StatMgr}, + table_mgr::TableMgr, + view_mgr::ViewMgr, +}; + +pub struct MetadataMgr { + table_mgr: Arc, + view_mgr: Arc, + stat_mgr: Arc, + index_mgr: Arc, +} + +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)?); + Ok(Self { + table_mgr, + view_mgr, + stat_mgr, + index_mgr, + }) + } + + pub fn create_table( + &self, + table_name: &str, + schema: &Arc, + tx: &Arc, + ) -> DbResult<()> { + self.table_mgr.create_table(table_name, schema, tx) + } + + pub fn get_layout(&self, table_name: &str, tx: &Arc) -> DbResult { + self.table_mgr.get_layout(table_name, tx) + } + + pub fn create_view( + &self, + view_name: &str, + view_def: &str, + tx: &Arc, + ) -> DbResult<()> { + self.view_mgr.create_view(view_name, view_def, tx) + } + + pub fn get_view_def(&self, view_name: &str, tx: &Arc) -> DbResult> { + self.view_mgr.get_view_def(view_name, tx) + } + + pub fn create_index( + &self, + idx_name: &str, + table_name: &str, + field_name: &str, + tx: &Arc, + ) -> DbResult<()> { + self.index_mgr + .create_index(idx_name, table_name, field_name, tx) + } + + pub fn get_stat_info( + &self, + table: &str, + layout: &Arc, + tx: &Arc, + ) -> DbResult { + self.stat_mgr.get_stat_info(table, layout, tx) + } + + pub fn get_index_info( + &self, + table_name: &str, + tx: &Arc, + ) -> DbResult> { + self.index_mgr.get_index_info(table_name, tx) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn metadata() {} +} diff --git a/engine/src/plan.rs b/engine/src/plan.rs new file mode 100644 index 0000000..a81fca9 --- /dev/null +++ b/engine/src/plan.rs @@ -0,0 +1,28 @@ +use std::{rc::Rc, sync::Arc}; + +use common::DbResult; + +use crate::{scan::Scan, schema::Schema}; + +pub(crate) mod group; +pub mod index; +pub(crate) mod materialize; +pub(crate) mod merge; +pub(crate) mod multibuffer; +pub mod product; +pub mod project; +pub mod select; +pub(crate) mod sort; +pub mod table; + +pub trait Plan { + fn open(&self) -> DbResult>; + + fn blocks_accessed(&self) -> DbResult; + + fn records_output(&self) -> DbResult; + + fn distinct_values(&self, field_name: &str) -> DbResult; + + fn schema(&self) -> DbResult>; +} diff --git a/engine/src/plan/group.rs b/engine/src/plan/group.rs new file mode 100644 index 0000000..c5d2a77 --- /dev/null +++ b/engine/src/plan/group.rs @@ -0,0 +1,77 @@ +use std::{rc::Rc, sync::Arc}; + +use common::DbResult; +use transaction::transaction::Transaction; + +use crate::{ + plan::{Plan, sort::SortPlan}, + scan::group::{AggregationFn, GroupByScan}, + schema::Schema, +}; + +pub struct GroupByPlan { + plan: Rc, + group_fields: Vec, + aggregation_fn: Vec, + schema: Arc, +} + +impl GroupByPlan { + pub fn new( + tx: &Arc, + plan: &Rc, + group_fields: Vec, + aggregation_fn: Vec, + ) -> DbResult { + let schema = Arc::new(Schema::default()); + let s = plan.schema()?; + for field in &group_fields { + schema.add(field.to_string(), &s)?; + } + for f in &aggregation_fn { + schema.add_int_field(f.field_name())?; + } + let plan = Rc::new(SortPlan::new(tx, plan, group_fields.clone())?); + Ok(Self { + plan, + group_fields, + aggregation_fn, + schema, + }) + } +} + +impl Plan for GroupByPlan { + fn open(&self) -> DbResult> { + let s = self.plan.open()?; + Ok(Rc::new(GroupByScan::new( + &s, + self.group_fields.clone(), + self.aggregation_fn.clone(), + )?)) + } + + fn blocks_accessed(&self) -> DbResult { + self.plan.blocks_accessed() + } + + fn records_output(&self) -> DbResult { + let mut num_groups = 1; + for field in &self.group_fields { + num_groups *= self.plan.distinct_values(field)?; + } + Ok(num_groups) + } + + fn distinct_values(&self, field_name: &str) -> 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)) + } +} diff --git a/engine/src/plan/index.rs b/engine/src/plan/index.rs new file mode 100644 index 0000000..38bf056 --- /dev/null +++ b/engine/src/plan/index.rs @@ -0,0 +1,225 @@ +use std::{rc::Rc, sync::Arc}; + +use common::DbResult; +use transaction::transaction::Transaction; + +use crate::{ + constant::Constant, + index_mgr::IndexInfo, + metadata_mgr::MetadataMgr, + plan::{Plan, select::SelectPlan, table::TablePlan}, + query::{ + command::{DeleteData, IndexData, InsertData, TableData, UpdateData, ViewData}, + planner::UpdatePlanner, + }, + scan::index::{IndexJoinScan, IndexSelectScan}, + schema::Schema, +}; + +pub struct IndexSelectPlan { + plan: Rc, + index: IndexInfo, + value: Constant, +} + +impl IndexSelectPlan { + pub fn new(plan: &Rc, index: IndexInfo, value: Constant) -> Self { + Self { + plan: Rc::clone(plan), + index, + value, + } + } +} + +impl Plan for IndexSelectPlan { + fn open(&self) -> DbResult> { + let ts = self.plan.open()?; + let index = self.index.open()?; + Ok(Rc::new(IndexSelectScan::new( + &ts, + &index, + self.value.clone(), + )?)) + } + + fn blocks_accessed(&self) -> DbResult { + Ok(self.index.block_accessed()? + self.index.records_output()) + } + + fn records_output(&self) -> DbResult { + Ok(self.index.records_output()) + } + + fn distinct_values(&self, field_name: &str) -> DbResult { + Ok(self.index.distinct_values(field_name)) + } + + fn schema(&self) -> DbResult> { + self.plan.schema() + } +} + +pub struct IndexJoinPlan { + p1: Rc, + p2: Rc, + index: IndexInfo, + field: String, + schema: Arc, +} + +impl IndexJoinPlan { + pub fn new( + p1: &Rc, + p2: &Rc, + index: IndexInfo, + field: String, + ) -> DbResult { + let schema = Arc::new(Schema::default()); + let s1 = p1.schema()?; + let s2 = p2.schema()?; + schema.add_all(&s1)?; + schema.add_all(&s2)?; + Ok(Self { + p1: Rc::clone(p1), + p2: Rc::clone(p2), + index, + field, + schema, + }) + } +} + +impl Plan for IndexJoinPlan { + fn open(&self) -> DbResult> { + 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)?)) + } + + fn blocks_accessed(&self) -> DbResult { + Ok(self.p1.blocks_accessed()? + + (self.p1.records_output()? * self.index.block_accessed()?) + + self.records_output()?) + } + + fn records_output(&self) -> DbResult { + 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)? { + self.p1.distinct_values(field_name) + } else { + self.p2.distinct_values(field_name) + } + } + + fn schema(&self) -> DbResult> { + Ok(Arc::clone(&self.schema)) + } +} + +pub struct IndexUpdatePlanner { + mg: Arc, +} + +impl UpdatePlanner for IndexUpdatePlanner { + fn execute_insert(&self, data: InsertData, tx: &Arc) -> DbResult { + let table = data.table; + let indexes = self.mg.get_index_info(&table, tx)?; + let plan = TablePlan::new(tx, table, &self.mg)?; + + let s = plan.open()?; + s.insert()?; + let rid = s.get_rid()?; + + for (field, value) in data.fields.iter().zip(data.values) { + tracing::debug!("Modify field: {} {}", field, value); + s.set_val(field, value.clone())?; + + if let Some(info) = indexes.get(field) { + let index = info.open()?; + index.insert(value, rid.clone())?; + index.close()?; + } + } + s.close()?; + Ok(1) + } + + fn execute_update(&self, data: UpdateData, tx: &Arc) -> DbResult { + let table = data.table; + let field = data.field; + let index = if let Some(info) = self.mg.get_index_info(&table, tx)?.get(&field) { + Some(info.open()?) + } else { + None + }; + let plan = TablePlan::new(tx, table, &self.mg)?; + let plan = SelectPlan::new(Rc::new(plan), data.predicate); + let s = plan.open()?; + let mut count = 0; + while s.next()? { + let new_val = data.value.evaluate(&s)?; + let oldval = s.get_val(&field)?; + s.set_val(&field, new_val.clone())?; + if let Some(index) = &index { + let rid = s.get_rid()?; + index.delete(oldval, rid.clone())?; + index.delete(new_val, rid.clone())?; + } + count += 1; + } + if let Some(index) = index { + index.close()?; + } + s.close()?; + Ok(count) + } + + fn execute_delete(&self, data: DeleteData, tx: &Arc) -> DbResult { + let table = data.name; + let indexes = self.mg.get_index_info(&table, tx)?; + let plan = TablePlan::new(tx, table, &self.mg)?; + let plan = SelectPlan::new(Rc::new(plan), data.predicate); + let s = plan.open()?; + let mut count = 0; + while s.next()? { + let rid = s.get_rid()?; + for (field, value) in indexes.iter() { + let val = s.get_val(field)?; + let index = value.open()?; + index.delete(val, rid.clone())?; + index.close()?; + } + s.delete()?; + count += 1; + } + s.close()?; + Ok(count) + } + + fn execute_create_table( + &self, + data: TableData, + tx: &Arc, + ) -> DbResult { + self.mg + .create_table(&data.name, &Arc::new(data.schema), tx)?; + Ok(0) + } + + fn execute_create_view(&self, data: ViewData, tx: &Arc) -> DbResult { + self.mg + .create_view(&data.name, &data.query.to_string(), tx)?; + Ok(0) + } + + fn execute_create_index(&self, data: IndexData, tx: &Arc) -> DbResult { + self.mg + .create_index(&data.index, &data.table, &data.field, tx)?; + Ok(0) + } +} diff --git a/engine/src/plan/materialize.rs b/engine/src/plan/materialize.rs new file mode 100644 index 0000000..fe4fd6f --- /dev/null +++ b/engine/src/plan/materialize.rs @@ -0,0 +1,59 @@ +use crate::layout::Layout; +use crate::plan::Plan; +use crate::scan::Scan; +use crate::schema::Schema; +use crate::temp::TempTable; +use common::DbResult; +use std::rc::Rc; +use std::sync::Arc; +use transaction::transaction::Transaction; + +pub struct MaterializePlan { + source: Rc, + tx: Arc, +} + +impl MaterializePlan { + pub fn new(source: &Rc, tx: &Arc) -> Self { + Self { + source: Rc::clone(source), + tx: Arc::clone(tx), + } + } +} + +impl Plan for MaterializePlan { + fn open(&self) -> DbResult> { + let schema = self.source.schema()?; + let temp = TempTable::new(&self.tx, &schema)?; + let source = self.source.open()?; + let dest = temp.open()?; + while source.next()? { + dest.insert()?; + for (field, _) in schema.fields()? { + dest.set_val(&field, source.get_val(&field)?)?; + } + } + source.close()?; + dest.before_first()?; + Ok(dest) + } + + fn blocks_accessed(&self) -> DbResult { + let layout = Layout::new(&self.source.schema()?)?; + let rpd = self.tx.block_size() / layout.slotsize(); + Ok(self.source.records_output()? / rpd) + } + + fn records_output(&self) -> DbResult { + self.source.records_output() + } + + fn distinct_values(&self, field_name: &str) -> DbResult { + self.source.distinct_values(field_name) + } + + fn schema(&self) -> DbResult> { + self.source.schema() + } +} diff --git a/engine/src/plan/merge.rs b/engine/src/plan/merge.rs new file mode 100644 index 0000000..6d0b981 --- /dev/null +++ b/engine/src/plan/merge.rs @@ -0,0 +1,77 @@ +use std::{rc::Rc, sync::Arc}; + +use common::DbResult; +use transaction::transaction::Transaction; + +use crate::{ + plan::{Plan, sort::SortPlan}, + scan::merge::MergeJoinScan, + schema::Schema, +}; + +pub struct MergeJoinPlan { + p1: Rc, + p2: Rc, + field_name1: String, + field_name2: String, + schema: Arc, +} + +#[allow(dead_code)] +impl MergeJoinPlan { + pub fn new( + tx: &Arc, + p1: &Rc, + p2: &Rc, + field_name1: &str, + field_name2: &str, + ) -> 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()); + Ok(Self { + p1, + p2, + field_name1: field_name1.to_string(), + field_name2: field_name2.to_string(), + schema, + }) + } +} + +impl Plan for MergeJoinPlan { + fn open(&self) -> DbResult> { + let s1 = self.p1.open()?; + let s2 = self.p2.open()?; + Ok(Rc::new(MergeJoinScan::new( + &s1, + &s2, + &self.field_name1, + &self.field_name2, + )?)) + } + + fn blocks_accessed(&self) -> DbResult { + Ok(self.p1.blocks_accessed()? + self.p2.blocks_accessed()?) + } + + fn records_output(&self) -> DbResult { + let max_val = self + .p1 + .distinct_values(&self.field_name1)? + .max(self.p2.distinct_values(&self.field_name2)?); + 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)? { + self.p1.distinct_values(field_name) + } else { + self.p2.distinct_values(field_name) + } + } + + fn schema(&self) -> DbResult> { + Ok(Arc::clone(&self.schema)) + } +} diff --git a/engine/src/plan/multibuffer.rs b/engine/src/plan/multibuffer.rs new file mode 100644 index 0000000..3e8eec4 --- /dev/null +++ b/engine/src/plan/multibuffer.rs @@ -0,0 +1,92 @@ +use std::{rc::Rc, sync::Arc}; + +use common::DbResult; +use transaction::transaction::Transaction; + +use crate::{ + plan::{Plan, materialize::MaterializePlan}, + scan::{Scan, multibuffer::MultiBufferProductScan}, + schema::Schema, + temp::TempTable, +}; + +pub(crate) struct MultiBufferProductPlan { + tx: Arc, + left: Rc, + right: Rc, + schema: Arc, +} + +impl MultiBufferProductPlan { + pub(crate) fn new( + tx: &Arc, + 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 plan = Self { + tx: Arc::clone(tx), + left: Rc::new(MaterializePlan::new(left, tx)), + right: Rc::clone(right), + schema, + }; + Ok(plan) + } + + fn copy_records_from(&self, p: &Rc) -> DbResult { + let source = p.open()?; + let schema = p.schema()?; + let tt = TempTable::new(&self.tx, &schema)?; + let dest = tt.open()?; + while source.next()? { + dest.insert()?; + for (field, _) in schema.fields()? { + let value = source.get_val(&field)?; + dest.set_val(&field, value)?; + } + } + source.close()?; + dest.close()?; + Ok(tt) + } +} + +impl Plan for MultiBufferProductPlan { + fn open(&self) -> DbResult> { + let left = self.left.open()?; + let t = self.copy_records_from(&self.right)?; + Ok(Rc::new(MultiBufferProductScan::new( + &self.tx, + &left, + &t.table_name(), + &t.layout(), + )?)) + } + + fn blocks_accessed(&self) -> DbResult { + let available = self.tx.available_buffs()? as i32; + let size = MaterializePlan::new(&self.right, &self.tx).blocks_accessed()?; + let num_chunks = size / available; + Ok(self.right.blocks_accessed()? + self.left.blocks_accessed()? * num_chunks) + } + + fn records_output(&self) -> DbResult { + 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)? { + self.left.distinct_values(field_name) + } else { + self.right.distinct_values(field_name) + } + } + + fn schema(&self) -> DbResult> { + Ok(Arc::clone(&self.schema)) + } +} diff --git a/engine/src/plan/product.rs b/engine/src/plan/product.rs new file mode 100644 index 0000000..0a212d8 --- /dev/null +++ b/engine/src/plan/product.rs @@ -0,0 +1,54 @@ +use std::{rc::Rc, sync::Arc}; + +use common::DbResult; + +use crate::{ + plan::Plan, + scan::{Scan, product::ProductScan}, + schema::Schema, +}; + +pub struct ProductPlan { + p1: Rc, + p2: Rc, + schema: Arc, +} + +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)?; + Ok(Self { p1, p2, schema }) + } +} + +impl Plan for ProductPlan { + fn open(&self) -> DbResult> { + let s1 = self.p1.open()?; + let s2 = self.p2.open()?; + Ok(Rc::new(ProductScan::new(s1, s2)?)) + } + + fn blocks_accessed(&self) -> DbResult { + Ok(self.p1.blocks_accessed()? + (self.p1.records_output()? * self.p2.blocks_accessed()?)) + } + + fn records_output(&self) -> DbResult { + 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)? { + self.p1.distinct_values(field_name) + } else { + self.p2.distinct_values(field_name) + } + } + + fn schema(&self) -> DbResult> { + Ok(Arc::clone(&self.schema)) + } +} diff --git a/engine/src/plan/project.rs b/engine/src/plan/project.rs new file mode 100644 index 0000000..1a63954 --- /dev/null +++ b/engine/src/plan/project.rs @@ -0,0 +1,59 @@ +use std::{ + collections::{HashMap, HashSet}, + rc::Rc, + sync::Arc, +}; + +use common::DbResult; + +use crate::{ + element::Element, + plan::Plan, + scan::{Scan, project::ProjectScan}, + schema::Schema, +}; + +pub struct ProjectPlan { + plan: Rc, + schema: Arc, + mapping: HashMap, +} + +impl ProjectPlan { + pub fn new(plan: Rc, fields: Vec, tables: Vec) -> DbResult { + let schema = Arc::new(Schema::default()); + //for field in fields { + // let other = plan.schema()?; + // schema.add(field, &other)?; + // } + Ok(Self { + plan, + schema, + mapping: HashMap::new(), + }) + } +} + +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(); + Ok(Rc::new(ProjectScan::new(scan, fields))) + } + + fn blocks_accessed(&self) -> DbResult { + self.plan.blocks_accessed() + } + + fn records_output(&self) -> DbResult { + self.plan.records_output() + } + + fn distinct_values(&self, field_name: &str) -> DbResult { + self.plan.distinct_values(field_name) + } + + fn schema(&self) -> DbResult> { + Ok(Arc::clone(&self.schema)) + } +} diff --git a/engine/src/plan/select.rs b/engine/src/plan/select.rs new file mode 100644 index 0000000..c572b9c --- /dev/null +++ b/engine/src/plan/select.rs @@ -0,0 +1,54 @@ +use std::{rc::Rc, sync::Arc}; + +use common::DbResult; + +use crate::{ + plan::Plan, + predicate::Predicate, + scan::{Scan, select::SelectScan}, + schema::Schema, +}; + +pub struct SelectPlan { + plan: Rc, + predicate: Predicate, +} + +impl SelectPlan { + pub fn new(plan: Rc, predicate: Predicate) -> Self { + Self { plan, predicate } + } +} + +impl Plan for SelectPlan { + fn open(&self) -> DbResult> { + let s = self.plan.open()?; + Ok(Rc::new(SelectScan::new(s, self.predicate.clone()))) + } + + fn blocks_accessed(&self) -> DbResult { + self.plan.blocks_accessed() + } + + fn records_output(&self) -> DbResult { + Ok(self.plan.records_output()? / self.predicate.reduction_factor(&self.plan)?) + } + + fn distinct_values(&self, field_name: &str) -> common::DbResult { + if self.predicate.equates_with_constant(field_name)?.is_some() { + return Ok(1); + } else { + if let Some(second_field) = self.predicate.equates_with_field(field_name)? { + return Ok(self + .plan + .distinct_values(field_name)? + .min(self.plan.distinct_values(&second_field)?)); + } + } + self.plan.distinct_values(field_name) + } + + fn schema(&self) -> DbResult> { + self.plan.schema() + } +} diff --git a/engine/src/plan/sort.rs b/engine/src/plan/sort.rs new file mode 100644 index 0000000..0430902 --- /dev/null +++ b/engine/src/plan/sort.rs @@ -0,0 +1,139 @@ +use std::{cmp::Ordering, rc::Rc, sync::Arc}; + +use common::DbResult; +use transaction::transaction::Transaction; + +use crate::{ + plan::{Plan, materialize::MaterializePlan}, + scan::{ + Scan, + sort::{RecordComparator, SortScan}, + }, + schema::Schema, + temp::TempTable, +}; + +pub struct SortPlan { + plan: Rc, + tx: Arc, + schema: Arc, + comp: RecordComparator, +} + +impl SortPlan { + pub fn new( + tx: &Arc, + plan: &Rc, + sort_fields: Vec, + ) -> DbResult { + let schema = plan.schema()?; + Ok(Self { + plan: Rc::clone(plan), + tx: Arc::clone(tx), + schema, + comp: RecordComparator::new(sort_fields), + }) + } + + fn split_into_runs(&self, source: &Rc) -> DbResult> { + let mut temps = vec![]; + source.before_first()?; + if !source.next()? { + return Ok(temps); + } + let mut current_temp = TempTable::new(&self.tx, &self.schema)?; + 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)?; + temps.push(current_temp.clone()); + current_scan = current_temp.open()?; + } + } + current_scan.close()?; + Ok(temps) + } + + fn do_merge_iteration(&self, mut runs: Vec) -> DbResult> { + let mut result = vec![]; + while runs.len() > 1 { + let p1 = runs.remove(0); + let p2 = runs.remove(0); + result.push(self.merge_two_runs(p1, p2)?); + } + if runs.len() == 1 { + result.push(runs.first().cloned().unwrap()); + } + Ok(result) + } + + 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 dest = result.open()?; + + let mut has_more1 = src1.next()?; + let mut has_more2 = src2.next()?; + while has_more1 && has_more2 { + if self.comp.compare(&src1, &src2)? == Ordering::Less { + has_more1 = self.copy(&src1, &dest)?; + } else { + has_more2 = self.copy(&src2, &dest)?; + } + } + + if has_more1 { + while has_more1 { + has_more1 = self.copy(&src1, &dest)?; + } + } else { + while has_more2 { + has_more2 = self.copy(&src2, &dest)?; + } + } + src1.close()?; + src2.close()?; + dest.close()?; + Ok(result) + } + + fn copy(&self, source: &Rc, dest: &Rc) -> DbResult { + dest.insert()?; + for (field, _) in self.schema.fields()? { + dest.set_val(&field, source.get_val(&field)?)?; + } + source.next() + } +} + +impl Plan for SortPlan { + fn open(&self) -> DbResult> { + let scan = self.plan.open()?; + let mut runs = self.split_into_runs(&scan)?; + scan.close()?; + while runs.len() > 2 { + runs = self.do_merge_iteration(runs)?; + } + Ok(Rc::new(SortScan::new(runs, self.comp.clone())?)) + } + + fn blocks_accessed(&self) -> DbResult { + let mp = MaterializePlan::new(&self.plan, &self.tx); + mp.blocks_accessed() + } + + fn records_output(&self) -> DbResult { + self.plan.records_output() + } + + fn distinct_values(&self, field_name: &str) -> DbResult { + self.plan.distinct_values(field_name) + } + + fn schema(&self) -> DbResult> { + Ok(self.schema.clone()) + } +} diff --git a/engine/src/plan/table.rs b/engine/src/plan/table.rs new file mode 100644 index 0000000..4283085 --- /dev/null +++ b/engine/src/plan/table.rs @@ -0,0 +1,59 @@ +use std::{rc::Rc, sync::Arc}; + +use common::DbResult; +use transaction::transaction::Transaction; + +use crate::{ + layout::Layout, + metadata_mgr::MetadataMgr, + plan::Plan, + scan::{Scan, table::TableScan}, + schema::Schema, + stat_mgr::StatInfo, +}; + +pub struct TablePlan { + tx: Arc, + table: String, + layout: Arc, + 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)?; + Ok(Self { + tx: Arc::clone(tx), + table, + layout, + stat, + }) + } +} + +impl Plan for TablePlan { + fn open(&self) -> DbResult> { + Ok(Rc::new(TableScan::new( + &self.tx, + &self.table, + &self.layout, + )?)) + } + + fn blocks_accessed(&self) -> DbResult { + Ok(self.stat.block_accessed()) + } + + fn records_output(&self) -> DbResult { + Ok(self.stat.records_output()) + } + + fn distinct_values(&self, _: &str) -> DbResult { + Ok(self.stat.distinct_values()) + } + + fn schema(&self) -> DbResult> { + Ok(self.layout.schema()) + } +} diff --git a/engine/src/predicate.rs b/engine/src/predicate.rs new file mode 100644 index 0000000..ac18b46 --- /dev/null +++ b/engine/src/predicate.rs @@ -0,0 +1,245 @@ +use std::{ + fmt::Debug, + rc::Rc, + sync::{Arc, RwLock}, +}; + +use common::{DbResult, error::DbError}; + +use crate::{constant::Constant, plan::Plan, scan::Scan, schema::Schema}; + +#[derive(Debug, Clone)] +pub enum Expression { + Value(Constant), + Field(String), +} + +impl Expression { + 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 { + match self { + Self::Value(_) => Ok(true), + Self::Field(field) => schema.has_field(field), + } + } + + pub fn as_field_name(&self) -> Option<&str> { + match self { + Self::Field(field) => Some(field), + _ => None, + } + } + + pub fn as_constant(&self) -> Option<&Constant> { + match self { + Self::Value(value) => Some(value), + _ => None, + } + } +} + +impl std::fmt::Display for Expression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Value(value) => write!(f, "{}", value), + Self::Field(field) => write!(f, "{}", field), + } + } +} + +#[derive(Clone, Debug)] +pub struct Term { + left: Expression, + right: Expression, +} + +impl Term { + pub fn new(left: Expression, right: Expression) -> Self { + Self { left, right } + } + + pub fn is_satisfied(&self, s: &Rc) -> DbResult { + let left = self.left.evaluate(s)?; + let right = self.right.evaluate(s)?; + Ok(left == right) + } + + pub fn applies_to(&self, schema: &Schema) -> DbResult { + Ok(self.left.applies_to(schema)? && self.right.applies_to(schema)?) + } + + pub fn reduction_factor(&self, p: &Rc) -> DbResult { + if let Some(left) = self.left.as_field_name() + && let Some(right) = self.right.as_field_name() + { + return Ok(p.distinct_values(left)?.max(p.distinct_values(right)?)); + } + if let Some(left) = self.left.as_field_name() { + return p.distinct_values(left); + } + if let Some(right) = self.right.as_field_name() { + return p.distinct_values(right); + } + if let Some(left) = self.left.as_constant() + && let Some(right) = self.right.as_constant() + && left == right + { + Ok(1) + } else { + Ok(i32::MAX) + } + } + + pub fn equates_with_constant(&self, field_name: &str) -> DbResult> { + if let Some(field) = self.left.as_field_name() + && field == field_name + && let Some(value) = self.right.as_constant() + { + Ok(Some(value.clone())) + } else if let Some(right) = self.right.as_field_name() + && right == field_name + && let Some(value) = self.left.as_constant() + { + Ok(Some(value.clone())) + } else { + Ok(None) + } + } + + pub fn equates_with_field(&self, field_name: &str) -> 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())) + } 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())) + } else { + Ok(None) + } + } +} + +impl std::fmt::Display for Term { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}={}", self.left, self.right) + } +} + +#[derive(Default, Clone)] +pub struct Predicate { + terms: Arc>>, +} + +impl Predicate { + pub fn new(term: Term) -> Self { + Self { + terms: Arc::new(RwLock::new(vec![term])), + } + } + + pub fn conjoin_with(&self, p: &Self) -> DbResult<()> { + let mut write = self.terms.write().map_err(DbError::lock)?; + let read = p.terms.read().map_err(DbError::lock)?; + for term in read.iter() { + write.push(term.clone()); + } + Ok(()) + } + + pub fn is_satisfied(&self, s: &Rc) -> DbResult { + let read = self.terms.read().map_err(DbError::lock)?; + for t in read.iter() { + if !t.is_satisfied(s)? { + return Ok(false); + } + } + Ok(true) + } + + pub fn reduction_factor(&self, p: &Rc) -> DbResult { + let mut factor = 1; + let read = self.terms.read().map_err(DbError::lock)?; + for t in read.iter() { + factor *= t.reduction_factor(p)?; + } + Ok(factor) + } + + pub fn select_sub_pred(&self, schema: &Schema) -> DbResult { + let result = Predicate::default(); + 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(schema)? { + terms.push(t.clone()); + } + } + } + Ok(result) + } + + 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 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)? { + terms.push(t.clone()); + } + } + if terms.is_empty() { + return Ok(None); + } + } + Ok(Some(result)) + } + + pub fn equates_with_constant(&self, field_name: &str) -> 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)? { + return Ok(Some(c)); + } + } + Ok(None) + } + + pub fn equates_with_field(&self, field_name: &str) -> 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)? { + return Ok(Some(field)); + } + } + Ok(None) + } +} + +impl std::fmt::Display for Predicate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let terms = self.terms.read().map_err(|_| std::fmt::Error)?; + for (i, t) in terms.iter().enumerate() { + if i == 0 { + write!(f, "{}", t)?; + } else { + write!(f, " AND {}", t)?; + } + } + Ok(()) + } +} diff --git a/engine/src/query/basic_planner.rs b/engine/src/query/basic_planner.rs index 9d4e2be..7dd758d 100644 --- a/engine/src/query/basic_planner.rs +++ b/engine/src/query/basic_planner.rs @@ -1,27 +1,32 @@ use std::{rc::Rc, sync::Arc}; use common::{DbResult, error::DbError}; -use planner::mgr::metadata::MetadataMgr; -use planner::plan::Plan; use transaction::transaction::Transaction; -use crate::query::command::{InsertData, TableData}; -use crate::query::{command::IndexData, planner::UpdatePlanner}; +use crate::{ + metadata_mgr::MetadataMgr, + plan::{Plan, select::SelectPlan, table::TablePlan}, + query::{command::IndexData, planner::UpdatePlanner}, +}; pub struct BasicUpdatePlanner { - md: MetadataMgr, + md: Arc, } impl BasicUpdatePlanner { - pub fn new(md: MetadataMgr) -> Self { - Self { md } + pub fn new(md: &Arc) -> Self { + Self { md: Arc::clone(md) } } } impl UpdatePlanner for BasicUpdatePlanner { - fn execute_insert(&self, data: InsertData, tx: &Arc) -> DbResult { - let p = Plan::table(tx, data.table.clone(), &self.md)?; - let mut s = p.open()?; + fn execute_insert( + &self, + data: super::command::InsertData, + tx: &Arc, + ) -> DbResult { + let p = Rc::new(TablePlan::new(tx, data.table.clone(), &self.md)?); + let s = p.open()?; s.insert()?; if data.fields.len() != data.values.len() { return Err(DbError::InvalidValuesAmount); @@ -40,8 +45,8 @@ impl UpdatePlanner for BasicUpdatePlanner { data: super::command::UpdateData, tx: &Arc, ) -> DbResult { - let p = Box::new(Plan::table(tx, data.table, &self.md)?); - let p = Plan::select(p, data.predicate); + let p = Rc::new(TablePlan::new(tx, data.table, &self.md)?); + let p = Rc::new(SelectPlan::new(p, data.predicate)); let s = p.open()?; let mut count = 0; while s.next()? { @@ -58,8 +63,8 @@ impl UpdatePlanner for BasicUpdatePlanner { data: super::command::DeleteData, tx: &Arc, ) -> DbResult { - let p = Box::new(Plan::table(tx, data.name, &self.md)?); - let p = Plan::select(p, data.predicate); + let p = Rc::new(TablePlan::new(tx, data.name, &self.md)?); + let p = Rc::new(SelectPlan::new(p, data.predicate)); let s = p.open()?; let mut count = 0; while s.next()? { @@ -72,11 +77,11 @@ impl UpdatePlanner for BasicUpdatePlanner { fn execute_create_table( &self, - data: TableData, + data: super::command::TableData, tx: &Arc, ) -> DbResult { self.md - .create_table(&data.name, data.schema, tx)?; + .create_table(&data.name, &Arc::new(data.schema), tx)?; Ok(0) } diff --git a/engine/src/query/command.rs b/engine/src/query/command.rs index ee9deee..755954c 100644 --- a/engine/src/query/command.rs +++ b/engine/src/query/command.rs @@ -1,8 +1,10 @@ -use planner::element::Element; -use planner::predicate::{Expression, Predicate}; -use planner::schema::Schema; -use planner::value::Value; -use crate::order::OrderByData; +use crate::{ + constant::Constant, + element::Element, + predicate::{Expression, Predicate}, + schema::Schema, + sort_by::SortByData, +}; pub enum Command { Insert(InsertData), @@ -33,7 +35,7 @@ pub struct QueryData { pub tables: Vec, pub predicate: Predicate, pub group_by: GroupByData, - pub order_by: OrderByData, + pub sort_by: SortByData, } impl std::fmt::Display for QueryData { @@ -61,8 +63,8 @@ impl std::fmt::Display for QueryData { if !self.group_by.is_empty() { write!(f, " {}", self.group_by)?; } - if !self.order_by.is_empty() { - write!(f, " {}", self.order_by)?; + if !self.sort_by.is_empty() { + write!(f, " {}", self.sort_by)?; } Ok(()) } @@ -97,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 { @@ -166,7 +168,13 @@ 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().iter().enumerate() { + for (i, (field, value)) in self + .schema + .fields() + .map_err(|_| std::fmt::Error)? + .iter() + .enumerate() + { if i == 0 { write!(f, "{} {}", field, value)?; } else { diff --git a/engine/src/query/heuristic_planner.rs b/engine/src/query/heuristic_planner.rs index c651371..d5d4414 100644 --- a/engine/src/query/heuristic_planner.rs +++ b/engine/src/query/heuristic_planner.rs @@ -1,28 +1,32 @@ use std::{cell::RefCell, rc::Rc, sync::Arc}; use common::DbResult; -use planner::mgr::metadata::MetadataMgr; -use planner::plan::Plan; use transaction::transaction::Transaction; -use crate::query::{command::QueryData, planner::QueryPlanner, table_planner::TablePlanner}; +use crate::plan::group::GroupByPlan; +use crate::plan::sort::SortPlan; +use crate::{ + metadata_mgr::MetadataMgr, + plan::{Plan, project::ProjectPlan}, + query::{command::QueryData, planner::QueryPlanner, table_planner::TablePlanner}, +}; -struct HeuristicQueryPlanner { +struct HeuristicQueryPlannerInner { table_planners: Vec, - md: MetadataMgr, + md: Arc, } -impl HeuristicQueryPlanner { - fn new(md: MetadataMgr) -> Self { +impl HeuristicQueryPlannerInner { + fn new(md: &Arc) -> Self { Self { table_planners: vec![], - md, + md: Arc::clone(md), } } - fn create_plan(&mut self, data: QueryData, tx: &Arc) -> DbResult { + fn create_plan(&mut self, data: QueryData, tx: &Arc) -> DbResult> { for table in &data.tables { - let tp = TablePlanner::new(table.as_raw()?, data.predicate.clone(), tx, &self.md)?; + let tp = TablePlanner::new(table.source()?, data.predicate.clone(), tx, &self.md)?; self.table_planners.push(tp); } let mut current = self.get_lowest_select_plan()?; @@ -41,17 +45,17 @@ impl HeuristicQueryPlanner { vec![], )?); } - if !data.order_by.is_empty() { - current = Rc::new(SortPlan::new(tx, ¤t, data.order_by.fields)?); + if !data.sort_by.is_empty() { + current = Rc::new(SortPlan::new(tx, ¤t, data.sort_by.fields)?); } - Ok(Plan::project( + Ok(Rc::new(ProjectPlan::new( current, data.fields, data.tables, - )) + )?)) } - fn get_lowest_select_plan(&mut self) -> DbResult> { + fn get_lowest_select_plan(&mut self) -> DbResult> { let mut index = 0; let mut best_plan = self.table_planners.first().unwrap().make_select_plan()?; for (i, tp) in self.table_planners.iter().skip(1).enumerate() { @@ -65,7 +69,7 @@ impl HeuristicQueryPlanner { Ok(best_plan) } - fn get_lowest_join_plan(&mut self, current: &Plan) -> DbResult> { + fn get_lowest_join_plan(&mut self, current: &Rc) -> DbResult>> { let mut index = 0; let mut best_plan = None; for (i, tp) in self.table_planners.iter().enumerate() { @@ -88,7 +92,7 @@ impl HeuristicQueryPlanner { Ok(best_plan) } - fn get_lowest_product_plan(&mut self, current: &Plan) -> DbResult { + fn get_lowest_product_plan(&mut self, current: &Rc) -> DbResult> { let mut index = 0; let mut best_plan = self .table_planners @@ -106,3 +110,18 @@ impl HeuristicQueryPlanner { Ok(best_plan) } } + +pub(crate) struct HeuristicQueryPlanner(RefCell); + +impl HeuristicQueryPlanner { + pub(crate) fn new(md: &Arc) -> Self { + Self(RefCell::new(HeuristicQueryPlannerInner::new(md))) + } +} + +impl QueryPlanner for HeuristicQueryPlanner { + fn create_plan(&self, data: QueryData, tx: &Arc) -> DbResult> { + let mut write = self.0.borrow_mut(); + write.create_plan(data, tx) + } +} diff --git a/engine/src/query/parser.rs b/engine/src/query/parser.rs index 4883066..db2ea71 100644 --- a/engine/src/query/parser.rs +++ b/engine/src/query/parser.rs @@ -105,7 +105,7 @@ impl Parser { tables, predicate, group_by, - order_by: sort_by, + sort_by, })) } diff --git a/engine/src/query/table_planner.rs b/engine/src/query/table_planner.rs index 4cd9877..a3760ed 100644 --- a/engine/src/query/table_planner.rs +++ b/engine/src/query/table_planner.rs @@ -1,19 +1,27 @@ use std::{collections::HashMap, rc::Rc, sync::Arc}; use common::DbResult; -use planner::element::Element; -use planner::mgr::index::IndexInfo; -use planner::mgr::metadata::MetadataMgr; -use planner::plan::Plan; -use planner::predicate::Predicate; -use planner::schema::Schema; use transaction::transaction::Transaction; +use crate::{ + index_mgr::IndexInfo, + metadata_mgr::MetadataMgr, + plan::{ + Plan, + index::{IndexJoinPlan, IndexSelectPlan}, + multibuffer::MultiBufferProductPlan, + select::SelectPlan, + table::TablePlan, + }, + predicate::Predicate, + schema::Schema, +}; + pub(crate) struct TablePlanner { - plan: Plan, + plan: Rc, predicate: Predicate, - schema: Schema, - indexes: HashMap, + schema: Arc, + indexes: HashMap, tx: Arc, } @@ -22,14 +30,14 @@ impl TablePlanner { table: &str, predicate: Predicate, tx: &Arc, - md: MetadataMgr, + md: &MetadataMgr, ) -> DbResult { - let plan = Plan::table(tx, table.to_string(), &md)?; + let plan = TablePlan::new(tx, table.to_string(), md)?; Ok(Self { predicate, tx: Arc::clone(tx), schema: plan.schema()?, - plan: plan, + plan: Rc::new(plan), indexes: md.get_index_info(table, tx)?, }) } diff --git a/engine/src/query/token.rs b/engine/src/query/token.rs index f055ee8..b37131a 100644 --- a/engine/src/query/token.rs +++ b/engine/src/query/token.rs @@ -1,5 +1,6 @@ use common::error::DbError; -use planner::value::Value; + +use crate::constant::Constant; #[derive(Clone, Debug, PartialEq, Eq)] pub enum Token { @@ -27,7 +28,7 @@ pub enum Token { By, Field(String), Delimiter(char), - Element(Value), + Element(Constant), } impl Token { @@ -76,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(Value::Varchar(token))); + tokens.push(Token::Element(Constant::Varchar(token))); str_char = None; continue; } else if last_idx == i && str_char.is_some() { @@ -98,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(Value::Integer(value))); + tokens.push(Token::Element(Constant::Integer(value))); } else { tokens.push(Token::Field(token)) } diff --git a/engine/src/record_page.rs b/engine/src/record_page.rs new file mode 100644 index 0000000..a5cb769 --- /dev/null +++ b/engine/src/record_page.rs @@ -0,0 +1,194 @@ +use std::sync::Arc; + +use common::DbResult; +use file::block::BlockId; +use transaction::transaction::Transaction; + +use crate::{field_info::FieldInfo, layout::Layout}; + +const EMPTY: u8 = 0; +const USED: u8 = 1; + +#[derive(Clone)] +pub struct RecordPage { + tx: Arc, + block: BlockId, + layout: Arc, +} + +impl RecordPage { + pub fn new(tx: &Arc, block: BlockId, layout: &Arc) -> DbResult { + tx.pin(&block)?; + Ok(Self { + tx: Arc::clone(tx), + block, + layout: Arc::clone(layout), + }) + } + + pub fn get_i32(&self, slot: i32, fieldname: &str) -> DbResult { + let pos = self.offset(slot) + self.layout.offset(fieldname); + 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); + self.tx.get_string(&self.block, pos as usize) + } + + pub fn set_i32(&self, slot: i32, field: &str, 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<()> { + let pos = self.offset(slot) + self.layout.offset(field); + self.tx.set_string(&self.block, pos as usize, value, true) + } + + pub fn delete(&self, slot: i32) -> DbResult<()> { + self.set_flag(slot, EMPTY) + } + + pub fn format(&self) -> DbResult<()> { + let mut slot = 0; + while self.is_valid_slot(slot) { + self.tx + .set_u8(&self.block, self.offset(slot) as usize, EMPTY, false)?; + let schema = self.layout.schema(); + 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)?, + FieldInfo::Varchar(_) => { + self.tx.set_string(&self.block, pos as usize, "", false)? + } + } + } + slot += 1; + } + Ok(()) + } + + pub fn next_after(&self, slot: i32) -> DbResult { + self.search_after(slot, USED) + } + + pub fn insert_after(&self, slot: i32) -> DbResult { + let newslot = self.search_after(slot, EMPTY)?; + if newslot >= 0 { + self.set_flag(newslot, USED)?; + } + Ok(newslot) + } + + pub fn block(&self) -> BlockId { + self.block.clone() + } + + fn set_flag(&self, slot: i32, flag: u8) -> DbResult<()> { + let offset = self.offset(slot) as usize; + self.tx.set_u8(&self.block, offset, flag, true)?; + Ok(()) + } + + fn search_after(&self, mut slot: i32, flag: u8) -> DbResult { + slot += 1; + while self.is_valid_slot(slot) { + if self.tx.get_u8(&self.block, self.offset(slot) as usize)? == flag { + return Ok(slot); + } + slot += 1; + } + Ok(-1) + } + + fn is_valid_slot(&self, slot: i32) -> bool { + let offset = self.offset(slot + 1); + let block_size = self.tx.block_size(); + offset <= block_size + } + + fn offset(&self, slot: i32) -> i32 { + slot * self.layout.slotsize() + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use buffer::mgr::BufferMgr; + use file::mgr::FileMgr; + use log::mgr::LogMgr; + use rand::RngExt; + use tempfile::tempdir; + use transaction::lock_table::LockTable; + + use crate::schema::Schema; + + use super::*; + + #[test] + fn record() { + let dir = tempdir().unwrap(); + 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 lock_table = Arc::new(LockTable::default()); + + 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 block = tx.append("testfile").unwrap(); + tx.pin(&block).unwrap(); + let record_page = RecordPage::new(&tx, block.clone(), &layout).unwrap(); + record_page.format().unwrap(); + + let mut rng = rand::rng(); + + let mut values_less = HashSet::new(); + let mut values_greater = HashSet::new(); + + let mut slot = record_page.insert_after(-1).unwrap(); + while slot >= 0 { + let n = rng.random::(); + if n < 25 { + values_less.insert((n, format!("rec{}", n))); + } else { + values_greater.insert((n, format!("rec{}", n))); + } + record_page.set_i32(slot, "A", n).unwrap(); + record_page + .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(); + if a < 25 { + assert!(values_less.contains(&(a, b))); + record_page.delete(slot).unwrap(); + } + slot = record_page.next_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(); + assert!(values_greater.contains(&(a, b))); + slot = record_page.next_after(slot).unwrap(); + } + tx.unpin(&block).unwrap(); + tx.commit().unwrap(); + } +} diff --git a/engine/src/rid.rs b/engine/src/rid.rs new file mode 100644 index 0000000..5a87bf5 --- /dev/null +++ b/engine/src/rid.rs @@ -0,0 +1,22 @@ +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct RID(i32, i32); + +impl RID { + pub fn new(block_num: i32, slot: i32) -> Self { + Self(block_num, slot) + } + + pub fn block_num(&self) -> i32 { + self.0 + } + + pub fn slot(&self) -> i32 { + self.1 + } +} + +impl std::fmt::Display for RID { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[{}, {}]", self.0, self.1) + } +} diff --git a/engine/src/scan.rs b/engine/src/scan.rs new file mode 100644 index 0000000..43035cb --- /dev/null +++ b/engine/src/scan.rs @@ -0,0 +1,66 @@ +use common::{DbResult, error::DbError}; +use std::sync::Arc; + +use crate::schema::Schema; +use crate::{constant::Constant, rid::RID}; + +pub mod chunk; +pub(crate) mod group; +pub mod index; +pub(crate) mod merge; +pub(crate) mod multibuffer; +pub mod product; +pub mod project; +pub mod select; +pub(crate) mod sort; +pub mod table; + +pub trait Scan { + fn before_first(&self) -> DbResult<()>; + + fn next(&self) -> DbResult; + + fn get_i32(&self, field_name: &str) -> DbResult; + + fn get_string(&self, field_name: &str) -> DbResult; + + fn get_val(&self, field_name: &str) -> DbResult; + + fn has_field(&self, field_name: &str) -> DbResult; + + fn close(&self) -> DbResult<()>; + + fn schema(&self) -> DbResult>; + + fn set_i32(&self, _: &str, _: i32) -> DbResult<()> { + Err(DbError::other("cannot set integer")) + } + + fn set_string(&self, _: &str, _: &str) -> DbResult<()> { + Err(DbError::other("cannot set string")) + } + + fn set_val(&self, _: &str, _: Constant) -> DbResult<()> { + Err(DbError::other("cannot set value")) + } + + fn insert(&self) -> DbResult<()> { + Err(DbError::other("cannot insert")) + } + + fn delete(&self) -> DbResult<()> { + Err(DbError::other("cannot delete")) + } + + fn get_rid(&self) -> DbResult { + Err(DbError::other("cannot get rid")) + } + + fn move_to_rid(&self, _: RID) -> DbResult<()> { + Err(DbError::other("cannot update")) + } + + fn save_position(&self) -> DbResult<()> { + Err(DbError::other("cannot save position")) + } +} diff --git a/engine/src/scan/chunk.rs b/engine/src/scan/chunk.rs new file mode 100644 index 0000000..51f3427 --- /dev/null +++ b/engine/src/scan/chunk.rs @@ -0,0 +1,180 @@ +use std::cell::RefCell; +use std::sync::Arc; + +use common::{DbResult, error::DbError}; +use file::block::BlockId; +use transaction::transaction::Transaction; + +use crate::schema::Schema; +use crate::{ + constant::Constant, field_info::FieldInfo, layout::Layout, record_page::RecordPage, scan::Scan, +}; + +pub struct ChunkScanLock { + buffers: Vec, + tx: Arc, + filename: String, + layout: Arc, + start_b_num: i32, + end_b_num: i32, + current_b_num: i32, + rp: i32, + current_slot: i32, +} + +impl ChunkScanLock { + fn new( + tx: &Arc, + filename: &str, + layout: &Arc, + 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)?); + } + let chunk = Self { + buffers, + tx: Arc::clone(tx), + filename: filename.to_string(), + start_b_num, + end_b_num, + current_b_num: start_b_num, + layout: Arc::clone(layout), + current_slot: -1, + rp: 0, + }; + Ok(chunk) + } + + fn move_to_block(&mut self, block_num: i32) { + self.current_b_num = block_num; + self.rp = self.current_b_num - self.start_b_num; + self.current_slot = -1; + } + + fn close(&self) -> DbResult<()> { + for i in 0..self.buffers.len() { + let block = BlockId::new(&self.filename, self.start_b_num + i as i32); + self.tx.unpin(&block)?; + } + Ok(()) + } + + fn before_first(&mut self) { + self.move_to_block(self.start_b_num); + } + + fn next(&mut self) -> DbResult { + loop { + let Some(rp) = self.buffers.get(self.rp as usize).cloned() else { + return Err(DbError::other("cannot get record page for chunk")); + }; + self.current_slot = rp.next_after(self.current_slot)?; + if self.current_slot >= 0 { + return Ok(true); + } + if self.current_b_num == self.end_b_num { + return Ok(false); + } + self.move_to_block(rp.block().num + 1); + } + } + + fn get_i32(&self, field_name: &str) -> DbResult { + if let Some(rp) = self.buffers.get(self.rp as usize) { + rp.get_i32(self.current_slot, field_name) + } else { + Err(DbError::other("cannot get buffer chunk")) + } + } + + fn get_string(&self, field_name: &str) -> DbResult { + if let Some(rp) = self.buffers.get(self.rp as usize) { + rp.get_string(self.current_slot, field_name) + } else { + Err(DbError::other("cannot get buffer chunk")) + } + } + + 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 has_field(&self, field_name: &str) -> DbResult { + self.layout.schema().has_field(field_name) + } + + fn schema(&self) -> DbResult> { + Ok(self.layout.schema()) + } +} + +pub struct ChunkScan(RefCell); + +impl ChunkScan { + pub fn new( + tx: &Arc, + filename: &str, + layout: &Arc, + start_b_num: i32, + end_b_num: i32, + ) -> DbResult { + Ok(Self(RefCell::new(ChunkScanLock::new( + tx, + filename, + layout, + start_b_num, + end_b_num, + )?))) + } +} + +impl Scan for ChunkScan { + fn before_first(&self) -> DbResult<()> { + let mut write = self.0.borrow_mut(); + write.before_first(); + Ok(()) + } + + fn next(&self) -> DbResult { + let mut write = self.0.borrow_mut(); + write.next() + } + + fn get_i32(&self, field_name: &str) -> DbResult { + let read = self.0.borrow(); + read.get_i32(field_name) + } + + fn get_string(&self, field_name: &str) -> DbResult { + let read = self.0.borrow(); + read.get_string(field_name) + } + + fn get_val(&self, field_name: &str) -> DbResult { + let read = self.0.borrow(); + read.get_val(field_name) + } + + fn has_field(&self, field_name: &str) -> DbResult { + let read = self.0.borrow(); + read.has_field(field_name) + } + + fn close(&self) -> DbResult<()> { + let read = self.0.borrow(); + read.close() + } + + fn schema(&self) -> DbResult> { + let read = self.0.borrow(); + read.schema() + } +} diff --git a/engine/src/scan/group.rs b/engine/src/scan/group.rs new file mode 100644 index 0000000..ceeed2e --- /dev/null +++ b/engine/src/scan/group.rs @@ -0,0 +1,223 @@ +use std::{cell::RefCell, cmp::Ordering, collections::HashMap, rc::Rc, sync::Arc}; + +use common::{DbResult, error::DbError}; + +use crate::{constant::Constant, scan::Scan, schema::Schema}; + +#[allow(dead_code)] +#[derive(Clone)] +pub enum AggregationFn { + MaxFn { field: String, value: Constant }, +} + +impl AggregationFn { + pub fn process_first(&mut self, scan: &Rc) -> DbResult<()> { + match self { + Self::MaxFn { field, value } => { + *value = scan.get_val(field)?; + } + } + Ok(()) + } + + pub fn process_next(&mut self, scan: &Rc) -> DbResult<()> { + match self { + Self::MaxFn { field, value } => { + let new_value = scan.get_val(field)?; + if new_value.cmp(value) == Ordering::Greater { + *value = new_value; + } + } + } + Ok(()) + } + + pub fn field_name(&self) -> String { + match self { + Self::MaxFn { field, .. } => format!("max_of_{}", field), + } + } + + pub fn value(&self) -> Constant { + match self { + Self::MaxFn { value, .. } => value.clone(), + } + } +} + +#[derive(PartialEq, Eq)] +struct GroupValue { + values: HashMap, +} + +impl GroupValue { + fn new(scan: &Rc, fields: Vec) -> DbResult { + let mut values = HashMap::new(); + for field in fields { + let value = scan.get_val(&field)?; + values.insert(field, value); + } + Ok(Self { values }) + } + + fn get_val(&self, field: &str) -> DbResult { + match self.values.get(field) { + Some(value) => Ok(value.clone()), + _ => Err(DbError::field_not_exists(field)), + } + } +} + +struct GroupByScanLock { + scan: Rc, + group_fields: Vec, + agg_fns: Vec, + group_val: GroupValue, + more_groups: bool, +} + +impl GroupByScanLock { + fn new( + scan: &Rc, + group_fields: Vec, + agg_fns: Vec, + ) -> DbResult { + let mut g = Self { + scan: Rc::clone(scan), + group_fields, + agg_fns, + group_val: GroupValue::new(scan, vec![])?, + more_groups: false, + }; + g.before_first()?; + Ok(g) + } + + fn before_first(&mut self) -> DbResult<()> { + self.scan.before_first()?; + self.more_groups = self.scan.next()?; + Ok(()) + } + + fn next(&mut self) -> DbResult { + if !self.more_groups { + return Ok(false); + } + for f in &mut self.agg_fns { + f.process_first(&self.scan)?; + } + self.group_val = GroupValue::new(&self.scan, self.group_fields.clone())?; + loop { + self.more_groups = self.scan.next()?; + if !self.more_groups { + break; + } + let gv = GroupValue::new(&self.scan, self.group_fields.clone())?; + if gv != self.group_val { + break; + } + for f in &mut self.agg_fns { + f.process_next(&self.scan)?; + } + } + Ok(true) + } + + fn close(&self) -> DbResult<()> { + self.scan.close() + } + + fn get_val(&self, field_name: &str) -> DbResult { + if self.group_fields.contains(&field_name.to_string()) { + return self.group_val.get_val(field_name); + } + for f in &self.agg_fns { + if f.field_name() == field_name { + return Ok(f.value()); + } + } + Err(DbError::field_not_exists(field_name)) + } + + fn get_i32(&self, field_name: &str) -> DbResult { + self.get_val(field_name)?.as_i32() + } + + fn get_string(&self, field_name: &str) -> 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()) { + return true; + } + for f in &self.agg_fns { + if f.field_name() == field_name { + return true; + } + } + false + } + + fn schema(&self) -> DbResult> { + self.scan.schema() + } +} + +pub struct GroupByScan { + lock: RefCell, +} + +impl GroupByScan { + pub fn new( + scan: &Rc, + group_fields: Vec, + agg_fns: Vec, + ) -> DbResult { + Ok(Self { + lock: RefCell::new(GroupByScanLock::new(scan, group_fields, agg_fns)?), + }) + } +} + +impl Scan for GroupByScan { + fn before_first(&self) -> DbResult<()> { + let mut write = self.lock.borrow_mut(); + write.before_first() + } + + fn next(&self) -> DbResult { + let mut write = self.lock.borrow_mut(); + write.next() + } + + fn get_i32(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + read.get_i32(field_name) + } + + fn get_string(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + read.get_string(field_name) + } + + fn get_val(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + read.get_val(field_name) + } + + fn has_field(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + Ok(read.has_field(field_name)) + } + + fn close(&self) -> DbResult<()> { + let read = self.lock.borrow(); + read.close() + } + + 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 new file mode 100644 index 0000000..e6aa044 --- /dev/null +++ b/engine/src/scan/index.rs @@ -0,0 +1,234 @@ +use common::DbResult; +use std::rc::Rc; +use std::sync::Arc; + +use crate::schema::Schema; +use crate::{constant::Constant, index::Index, scan::Scan}; + +pub struct IndexSelectScan { + scan: Rc, + index: Rc, + value: Constant, +} + +impl IndexSelectScan { + pub fn new(scan: &Rc, index: &Rc, value: Constant) -> DbResult { + let scan = Self { + scan: Rc::clone(scan), + index: Rc::clone(index), + value, + }; + scan.before_first()?; + Ok(scan) + } +} + +impl Scan for IndexSelectScan { + fn before_first(&self) -> DbResult<()> { + self.index.before_first(self.value.clone()) + } + + fn next(&self) -> DbResult { + let ok = self.index.next()?; + if ok { + let rid = self.index.get_data_rid()?; + self.scan.move_to_rid(rid)?; + } + Ok(ok) + } + + fn get_i32(&self, field_name: &str) -> DbResult { + self.scan.get_i32(field_name) + } + + fn get_string(&self, field_name: &str) -> DbResult { + self.scan.get_string(field_name) + } + + fn get_val(&self, field_name: &str) -> DbResult { + self.scan.get_val(field_name) + } + + fn has_field(&self, field_name: &str) -> DbResult { + self.scan.has_field(field_name) + } + + fn close(&self) -> DbResult<()> { + self.index.close()?; + self.scan.close() + } + + fn schema(&self) -> DbResult> { + self.scan.schema() + } +} + +pub struct IndexJoinScan { + left: Rc, + right: Rc, + index: Rc, + field: String, +} + +impl IndexJoinScan { + pub fn new( + left: &Rc, + index: &Rc, + field: &str, + right: &Rc, + ) -> DbResult { + let scan = Self { + left: Rc::clone(left), + right: Rc::clone(right), + field: field.to_string(), + index: Rc::clone(index), + }; + scan.before_first()?; + Ok(scan) + } + + fn reset_index(&self) -> DbResult<()> { + let key = self.left.get_val(&self.field)?; + self.index.before_first(key) + } +} + +impl Scan for IndexJoinScan { + fn before_first(&self) -> DbResult<()> { + self.left.before_first()?; + self.left.next()?; + self.reset_index() + } + + fn next(&self) -> DbResult { + loop { + if self.index.next()? { + self.right.move_to_rid(self.index.get_data_rid()?)?; + return Ok(true); + } + if !self.left.next()? { + return Ok(false); + } + self.reset_index()?; + } + } + + fn get_i32(&self, field_name: &str) -> DbResult { + if self.right.has_field(field_name)? { + self.right.get_i32(field_name) + } else { + self.left.get_i32(field_name) + } + } + + fn get_string(&self, field_name: &str) -> DbResult { + if self.right.has_field(field_name)? { + self.right.get_string(field_name) + } else { + self.left.get_string(field_name) + } + } + + fn get_val(&self, field_name: &str) -> DbResult { + if self.right.has_field(field_name)? { + self.right.get_val(field_name) + } else { + self.left.get_val(field_name) + } + } + + fn has_field(&self, field_name: &str) -> DbResult { + Ok(self.right.has_field(field_name)? || self.left.has_field(field_name)?) + } + + fn close(&self) -> DbResult<()> { + self.left.close()?; + self.index.close()?; + self.right.close() + } + + fn schema(&self) -> DbResult> { + let s1 = self.left.schema()?; + let s2 = self.right.schema()?; + s1.add_all(&s2)?; + Ok(s1) + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + use std::sync::Arc; + use tempfile::tempdir; + + use crate::schema::Schema; + use crate::{ + SimpleDB, + plan::{Plan, table::TablePlan}, + }; + + #[test] + 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(); + + md.create_table("users", &Arc::new(schema), &tx).unwrap(); + md.create_index("users_ids", "users", "sid", &tx).unwrap(); + + let plan = TablePlan::new(&tx, "users".to_string(), &md).unwrap(); + let s = plan.open().unwrap(); + + let mut indexes = HashMap::new(); + let infos = md.get_index_info("users", &tx).unwrap(); + for (field, info) in infos { + let index = info.open().unwrap(); + indexes.insert(field, index); + } + + 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(); + + let rid = s.get_rid().unwrap(); + for (field, index) in indexes.iter() { + let value = s.get_val(field).unwrap(); + index.insert(value, rid.clone()).unwrap(); + } + + s.before_first().unwrap(); + while s.next().unwrap() { + 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(); + index.delete(value, rid.clone()).unwrap(); + } + s.delete().unwrap(); + break; + } + } + s.before_first().unwrap(); + while s.next().unwrap() { + println!( + "{} {}", + s.get_string("sname").unwrap(), + s.get_i32("sid").unwrap() + ); + } + s.close().unwrap(); + for idx in indexes.values() { + idx.close().unwrap(); + } + tx.commit().unwrap(); + } +} diff --git a/engine/src/scan/merge.rs b/engine/src/scan/merge.rs new file mode 100644 index 0000000..709cdd6 --- /dev/null +++ b/engine/src/scan/merge.rs @@ -0,0 +1,170 @@ +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}; + +#[allow(dead_code)] +struct MergeJoinScanLock { + s1: Rc, + s2: Rc, + field_name1: String, + field_name2: String, + join_val: Option, +} + +#[allow(dead_code)] +impl MergeJoinScanLock { + pub fn new( + s1: &Rc, + s2: &Rc, + field_name1: &str, + field_name2: &str, + ) -> DbResult { + let s = Self { + s1: Rc::clone(s1), + s2: Rc::clone(s2), + field_name1: field_name1.to_string(), + field_name2: field_name2.to_string(), + join_val: None, + }; + s.before_first()?; + Ok(s) + } + + fn before_first(&self) -> DbResult<()> { + self.s1.before_first()?; + self.s2.before_first() + } + + fn next(&mut self) -> DbResult { + let Some(join_val) = &self.join_val else { + return Ok(false); + }; + let mut has_more2 = self.s2.next()?; + if has_more2 && self.s2.get_val(&self.field_name2)? == *join_val { + return Ok(true); + } + let mut has_more1 = self.s1.next()?; + if has_more1 && self.s1.get_val(&self.field_name1)? == *join_val { + return Ok(true); + } + + while has_more1 && has_more2 { + let v1 = self.s1.get_val(&self.field_name1)?; + let v2 = self.s2.get_val(&self.field_name2)?; + match v1.cmp(&v2) { + Ordering::Less => has_more1 = self.s1.next()?, + Ordering::Greater => has_more2 = self.s1.next()?, + _ => { + self.s2.save_position()?; + self.join_val = Some(self.s2.get_val(&self.field_name2)?); + return Ok(true); + } + } + } + Ok(false) + } + + fn get_i32(&self, field_name: &str) -> DbResult { + if self.s1.has_field(field_name)? { + self.s1.get_i32(field_name) + } else { + self.s2.get_i32(field_name) + } + } + + fn get_string(&self, field_name: &str) -> DbResult { + if self.s1.has_field(field_name)? { + self.s1.get_string(field_name) + } else { + self.s2.get_string(field_name) + } + } + + fn get_val(&self, field_name: &str) -> DbResult { + if self.s1.has_field(field_name)? { + self.s1.get_val(field_name) + } else { + self.s2.get_val(field_name) + } + } + + fn has_field(&self, field_name: &str) -> DbResult { + Ok(self.s1.has_field(field_name)? || self.s2.has_field(field_name)?) + } + + fn close(&self) -> DbResult<()> { + self.s1.close()?; + self.s2.close() + } + + fn schema(&self) -> DbResult> { + let s1 = self.s1.schema()?; + let s2 = self.s2.schema()?; + s1.add_all(&s2)?; + Ok(s1) + } +} + +#[allow(dead_code)] +pub struct MergeJoinScan { + lock: RefCell, +} + +#[allow(dead_code)] +impl MergeJoinScan { + pub fn new( + s1: &Rc, + s2: &Rc, + field_name1: &str, + field_name2: &str, + ) -> DbResult { + Ok(Self { + lock: RefCell::new(MergeJoinScanLock::new(s1, s2, field_name1, field_name2)?), + }) + } +} + +impl Scan for MergeJoinScan { + fn before_first(&self) -> DbResult<()> { + let read = self.lock.borrow(); + read.before_first() + } + + fn next(&self) -> DbResult { + let mut write = self.lock.borrow_mut(); + write.next() + } + + fn get_i32(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + read.get_i32(field_name) + } + + fn get_string(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + read.get_string(field_name) + } + + fn get_val(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + read.get_val(field_name) + } + + fn has_field(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + read.has_field(field_name) + } + + fn close(&self) -> DbResult<()> { + let read = self.lock.borrow(); + read.close() + } + + 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 new file mode 100644 index 0000000..769f6cb --- /dev/null +++ b/engine/src/scan/multibuffer.rs @@ -0,0 +1,204 @@ +use std::{cell::RefCell, rc::Rc, sync::Arc}; + +use common::{DbResult, error::DbError}; +use transaction::transaction::Transaction; + +use crate::{ + buffer_needs::BufferNeeds, + layout::Layout, + scan::{Scan, chunk::ChunkScan, product::ProductScan}, + schema::Schema, +}; + +struct MultiBufferProductScanInner { + tx: Arc, + left: Rc, + right: Option>, + prod: Option>, + filename: String, + layout: Arc, + chunk_size: i32, + next_block: i32, + file_size: i32, +} + +impl MultiBufferProductScanInner { + pub(crate) fn new( + tx: &Arc, + left: &Rc, + filename: &str, + layout: &Arc, + ) -> DbResult { + let available = tx.available_buffs()? as i32; + let file_size = tx.size(filename)? as i32; + let mut scan = Self { + tx: Arc::clone(tx), + left: Rc::clone(left), + file_size: tx.size(filename)? as i32, + filename: filename.to_string(), + layout: Arc::clone(layout), + chunk_size: BufferNeeds::best_factor(available, file_size), + next_block: 0, + prod: None, + right: None, + }; + scan.before_first()?; + Ok(scan) + } + + fn use_next_chunk(&mut self) -> DbResult { + if let Some(right) = self.right.take() { + right.close()?; + } + self.prod = None; + if self.next_block >= self.file_size { + return Ok(false); + } + let mut end = self.next_block + self.chunk_size - 1; + if end >= self.file_size { + end = self.file_size - 1; + } + let right: Rc = Rc::new(ChunkScan::new( + &self.tx, + &self.filename, + &self.layout, + self.next_block, + end, + )?); + self.left.before_first()?; + self.prod = Some(Rc::new(ProductScan::new(self.left.clone(), right.clone())?)); + self.right = Some(right); + self.next_block = end + 1; + Ok(true) + } + + fn before_first(&mut self) -> DbResult<()> { + self.next_block = 0; + self.use_next_chunk()?; + Ok(()) + } + + fn next(&mut self) -> DbResult { + loop { + let has_next = match &self.prod { + Some(prod) => prod.next()?, + None => false, + }; + if has_next { + return Ok(true); + } + if !self.use_next_chunk()? { + return Ok(false); + } + } + } + + fn get_i32(&self, field_name: &str) -> DbResult { + if let Some(prod) = &self.prod { + prod.get_i32(field_name) + } else { + Err(DbError::other("cannot get prod")) + } + } + + fn get_string(&self, field_name: &str) -> DbResult { + if let Some(prod) = &self.prod { + prod.get_string(field_name) + } else { + Err(DbError::other("cannot get prod")) + } + } + + fn get_val(&self, field_name: &str) -> DbResult { + if let Some(prod) = &self.prod { + prod.get_val(field_name) + } else { + Err(DbError::other("cannot get prod")) + } + } + + fn has_field(&self, field_name: &str) -> DbResult { + if let Some(prod) = &self.prod { + prod.has_field(field_name) + } else { + Err(DbError::other("cannot get prod")) + } + } + + fn close(&self) -> DbResult<()> { + self.left.close()?; + if let Some(right) = &self.right { + right.close()?; + } + Ok(()) + } + + fn schema(&self) -> DbResult> { + if let Some(prod) = &self.prod { + prod.schema() + } else { + Err(DbError::other("cannot get prod")) + } + } +} + +pub(crate) struct MultiBufferProductScan { + lock: RefCell, +} + +impl MultiBufferProductScan { + pub(crate) fn new( + tx: &Arc, + left: &Rc, + filename: &str, + layout: &Arc, + ) -> DbResult { + Ok(Self { + lock: RefCell::new(MultiBufferProductScanInner::new( + tx, left, filename, layout, + )?), + }) + } +} + +impl Scan for MultiBufferProductScan { + fn before_first(&self) -> DbResult<()> { + let mut write = self.lock.borrow_mut(); + write.before_first() + } + + fn next(&self) -> DbResult { + let mut write = self.lock.borrow_mut(); + write.next() + } + + fn get_i32(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + read.get_i32(field_name) + } + + fn get_string(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + read.get_string(field_name) + } + + fn get_val(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + read.get_val(field_name) + } + + fn has_field(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + read.has_field(field_name) + } + + fn close(&self) -> DbResult<()> { + let read = self.lock.borrow(); + read.close() + } + + 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 new file mode 100644 index 0000000..0fbd5da --- /dev/null +++ b/engine/src/scan/product.rs @@ -0,0 +1,75 @@ +use common::DbResult; +use std::rc::Rc; +use std::sync::Arc; + +use crate::scan::Scan; +use crate::schema::Schema; + +pub struct ProductScan { + s1: Rc, + s2: Rc, +} + +impl ProductScan { + pub fn new(s1: Rc, s2: Rc) -> DbResult { + s1.next()?; + Ok(Self { s1, s2 }) + } +} + +impl Scan for ProductScan { + fn before_first(&self) -> DbResult<()> { + self.s1.before_first()?; + self.s1.next()?; + self.s2.before_first() + } + + fn next(&self) -> DbResult { + if self.s2.next()? { + Ok(true) + } else { + self.s2.before_first()?; + Ok(self.s2.next()? && self.s1.next()?) + } + } + + fn get_i32(&self, field_name: &str) -> DbResult { + if self.s1.has_field(field_name)? { + self.s1.get_i32(field_name) + } else { + self.s2.get_i32(field_name) + } + } + + fn get_string(&self, field_name: &str) -> DbResult { + if self.s1.has_field(field_name)? { + self.s1.get_string(field_name) + } else { + self.s2.get_string(field_name) + } + } + + fn get_val(&self, field_name: &str) -> DbResult { + if self.s1.has_field(field_name)? { + self.s1.get_val(field_name) + } else { + self.s2.get_val(field_name) + } + } + + fn has_field(&self, field_name: &str) -> DbResult { + Ok(self.s1.has_field(field_name)? || self.s2.has_field(field_name)?) + } + + fn close(&self) -> DbResult<()> { + self.s1.close()?; + self.s2.close() + } + + fn schema(&self) -> DbResult> { + let s1 = self.s1.schema()?; + let s2 = self.s2.schema()?; + s1.add_all(&s2)?; + Ok(s1) + } +} diff --git a/engine/src/scan/project.rs b/engine/src/scan/project.rs new file mode 100644 index 0000000..49b7954 --- /dev/null +++ b/engine/src/scan/project.rs @@ -0,0 +1,70 @@ +use crate::scan::Scan; +use crate::schema::Schema; +use common::DbResult; +use common::error::DbError; +use std::sync::Arc; +use std::{collections::HashSet, rc::Rc}; + +pub struct ProjectScan { + scan: Rc, + fields: HashSet, +} + +impl ProjectScan { + pub fn new(scan: Rc, fields: HashSet) -> Self { + Self { scan, fields } + } +} + +impl Scan for ProjectScan { + fn before_first(&self) -> DbResult<()> { + self.scan.before_first() + } + + fn next(&self) -> DbResult { + self.scan.next() + } + + fn get_i32(&self, field_name: &str) -> DbResult { + if self.has_field(field_name)? { + self.scan.get_i32(field_name) + } else { + Err(DbError::field_not_exists(field_name)) + } + } + + fn get_string(&self, field_name: &str) -> DbResult { + if self.has_field(field_name)? { + self.scan.get_string(field_name) + } else { + Err(DbError::field_not_exists(field_name)) + } + } + + fn get_val(&self, field_name: &str) -> DbResult { + if self.has_field(field_name)? { + self.scan.get_val(field_name) + } else { + Err(DbError::field_not_exists(field_name)) + } + } + + fn has_field(&self, field_name: &str) -> DbResult { + Ok(self.fields.contains(field_name)) + } + + fn close(&self) -> DbResult<()> { + self.scan.close() + } + + fn schema(&self) -> DbResult> { + let project = Arc::new(Schema::default()); + let schema = self.scan.schema()?; + for (field, info) in schema.fields()? { + if self.fields.contains(&field) { + project.add_field(field, info)?; + } + } + Ok(project) + } +} diff --git a/engine/src/scan/select.rs b/engine/src/scan/select.rs new file mode 100644 index 0000000..5b7e512 --- /dev/null +++ b/engine/src/scan/select.rs @@ -0,0 +1,84 @@ +use common::DbResult; +use std::rc::Rc; +use std::sync::Arc; + +use crate::schema::Schema; +use crate::{predicate::Predicate, scan::Scan}; + +pub struct SelectScan { + scan: Rc, + predicate: Predicate, +} + +impl SelectScan { + pub fn new(scan: Rc, predicate: Predicate) -> Self { + Self { scan, predicate } + } +} + +impl Scan for SelectScan { + fn before_first(&self) -> DbResult<()> { + self.scan.before_first() + } + + fn next(&self) -> DbResult { + while self.scan.next()? { + if self.predicate.is_satisfied(&self.scan)? { + return Ok(true); + } + } + Ok(false) + } + + fn get_i32(&self, field_name: &str) -> DbResult { + self.scan.get_i32(field_name) + } + + fn get_string(&self, field_name: &str) -> DbResult { + self.scan.get_string(field_name) + } + + fn get_val(&self, field_name: &str) -> common::DbResult { + self.scan.get_val(field_name) + } + + fn has_field(&self, field_name: &str) -> common::DbResult { + self.scan.has_field(field_name) + } + + fn close(&self) -> DbResult<()> { + self.scan.close() + } + + fn schema(&self) -> DbResult> { + self.scan.schema() + } + + fn set_i32(&self, field_name: &str, value: i32) -> DbResult<()> { + self.scan.set_i32(field_name, value) + } + + fn set_string(&self, field_name: &str, value: &str) -> DbResult<()> { + self.scan.set_string(field_name, value) + } + + fn set_val(&self, field_name: &str, value: crate::constant::Constant) -> DbResult<()> { + self.scan.set_val(field_name, value) + } + + fn insert(&self) -> DbResult<()> { + self.scan.insert() + } + + fn delete(&self) -> DbResult<()> { + self.scan.delete() + } + + fn get_rid(&self) -> DbResult { + self.scan.get_rid() + } + + fn move_to_rid(&self, rid: crate::rid::RID) -> DbResult<()> { + self.scan.move_to_rid(rid) + } +} diff --git a/engine/src/scan/sort.rs b/engine/src/scan/sort.rs new file mode 100644 index 0000000..211d3c9 --- /dev/null +++ b/engine/src/scan/sort.rs @@ -0,0 +1,245 @@ +use common::{DbResult, error::DbError}; +use std::sync::Arc; +use std::{cell::RefCell, cmp::Ordering, rc::Rc}; + +use crate::constant::Constant; +use crate::rid::RID; +use crate::scan::Scan; +use crate::schema::Schema; +use crate::temp::TempTable; + +#[derive(Clone)] +pub struct RecordComparator { + fields: Vec, +} + +impl RecordComparator { + pub fn new(fields: Vec) -> Self { + Self { fields } + } + + pub fn compare(&self, s1: &Rc, s2: &Rc) -> DbResult { + for field in &self.fields { + let val1 = s1.get_val(field)?; + let val2 = s2.get_val(field)?; + let result = val1.cmp(&val2); + if result != Ordering::Equal { + return Ok(result); + } + } + Ok(Ordering::Equal) + } +} + +enum CurrentScan { + None, + S1, + S2, +} + +pub struct SortScanLock { + s1: Rc, + s2: Option>, + current_scan: CurrentScan, + comp: RecordComparator, + has_more1: bool, + has_more2: bool, + saved_position: Vec, +} + +impl SortScanLock { + fn new(runs: Vec, comp: RecordComparator) -> DbResult { + let s1 = runs[0].open()?; + let (s2, has_more2) = if runs.len() > 1 { + let s2 = runs[1].open()?; + let has_more2 = s2.next()?; + (Some(s2), has_more2) + } else { + (None, false) + }; + Ok(Self { + comp, + has_more1: s1.next()?, + has_more2, + s1, + s2, + current_scan: CurrentScan::None, + saved_position: vec![], + }) + } +} + +impl SortScanLock { + fn before_first(&mut self) -> DbResult<()> { + self.s1.before_first()?; + self.has_more1 = self.s1.next()?; + if let Some(s2) = &mut self.s2 { + s2.before_first()?; + self.has_more2 = s2.next()?; + } + Ok(()) + } + + fn next(&mut self) -> DbResult { + match self.current_scan { + CurrentScan::S1 => self.has_more1 = self.s1.next()?, + CurrentScan::S2 if let Some(s2) = &self.s2 => self.has_more2 = s2.next()?, + _ => {} + } + if !self.has_more1 && !self.has_more2 { + return Ok(false); + } else if self.has_more1 + && self.has_more2 + && let Some(s2) = &self.s2 + { + if self.comp.compare(&self.s1, s2)? == Ordering::Less { + self.current_scan = CurrentScan::S1; + } else { + self.current_scan = CurrentScan::S2; + } + } else if self.has_more1 { + self.current_scan = CurrentScan::S1; + } else if self.has_more2 { + self.current_scan = CurrentScan::S2; + } + Ok(true) + } + + fn close(&self) -> DbResult<()> { + self.s1.close()?; + if let Some(s2) = &self.s2 { + s2.close()?; + } + Ok(()) + } + + fn get_val(&self, field_name: &str) -> 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), + _ => Err(DbError::other("invalid scan")), + } + } + + fn get_i32(&self, field_name: &str) -> 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), + _ => Err(DbError::other("invalid scan")), + } + } + + fn get_string(&self, field_name: &str) -> 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), + _ => Err(DbError::other("invalid scan")), + } + } + + fn has_field(&self, field_name: &str) -> 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), + _ => Err(DbError::other("invalid scan")), + } + } + + fn save_position(&mut self) -> DbResult<()> { + let rid1 = self.s1.get_rid()?; + if let Some(s2) = &self.s2 { + let rid2 = s2.get_rid()?; + self.saved_position = vec![rid1, rid2]; + } else { + self.saved_position = vec![rid1]; + } + Ok(()) + } + + fn restore_position(&self) -> DbResult<()> { + if let Some(rid1) = self.saved_position.first().cloned() { + self.s1.move_to_rid(rid1)?; + } + if let Some(rid2) = self.saved_position.get(1).cloned() + && let Some(s2) = &self.s2 + { + s2.move_to_rid(rid2)?; + } + Ok(()) + } + + fn schema(&self) -> DbResult> { + let s1 = self.s1.schema()?; + if let Some(s2) = &self.s2 { + let s2 = s2.schema()?; + s1.add_all(&s2)?; + } + Ok(s1) + } +} + +pub struct SortScan { + lock: RefCell, +} + +impl SortScan { + pub fn new(runs: Vec, comp: RecordComparator) -> DbResult { + Ok(Self { + lock: RefCell::new(SortScanLock::new(runs, comp)?), + }) + } + + #[allow(dead_code)] + pub fn save_position(&self) -> DbResult<()> { + let mut write = self.lock.borrow_mut(); + write.save_position() + } + + #[allow(dead_code)] + pub fn restore_position(&self) -> DbResult<()> { + let read = self.lock.borrow(); + read.restore_position() + } +} + +impl Scan for SortScan { + fn before_first(&self) -> DbResult<()> { + let mut write = self.lock.borrow_mut(); + write.before_first() + } + + fn next(&self) -> DbResult { + let mut write = self.lock.borrow_mut(); + write.next() + } + + fn get_i32(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + read.get_i32(field_name) + } + + fn get_string(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + read.get_string(field_name) + } + + fn get_val(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + read.get_val(field_name) + } + + fn has_field(&self, field_name: &str) -> DbResult { + let read = self.lock.borrow(); + read.has_field(field_name) + } + + fn close(&self) -> DbResult<()> { + let read = self.lock.borrow(); + read.close() + } + + fn schema(&self) -> DbResult> { + let read = self.lock.borrow(); + read.schema() + } +} diff --git a/engine/src/scan/table.rs b/engine/src/scan/table.rs new file mode 100644 index 0000000..cb933b7 --- /dev/null +++ b/engine/src/scan/table.rs @@ -0,0 +1,317 @@ +use std::cell::RefCell; +use std::sync::Arc; + +use common::{DbResult, error::DbError}; +use file::block::BlockId; +use transaction::transaction::Transaction; + +use crate::schema::Schema; +use crate::{ + constant::Constant, field_info::FieldInfo, layout::Layout, record_page::RecordPage, rid::RID, + scan::Scan, +}; + +struct TableScanInner { + tx: Arc, + layout: Arc, + rp: RecordPage, + filename: String, + current_slot: i32, +} + +impl TableScanInner { + fn new(tx: &Arc, table_name: &str, layout: &Arc) -> 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)?; + rp.format()?; + rp + } else { + let block = BlockId::new(&filename, 0); + RecordPage::new(tx, block, layout)? + }; + Ok(Self { + tx: Arc::clone(tx), + layout: Arc::clone(layout), + rp, + current_slot: -1, + filename, + }) + } + + fn close(&self) -> DbResult<()> { + self.tx.unpin(&self.rp.block()) + } + + pub fn before_first(&mut self) -> DbResult<()> { + self.move_to_block(0) + } + + pub fn next(&mut self) -> DbResult { + self.current_slot = self.rp.next_after(self.current_slot)?; + while self.current_slot < 0 { + if self.at_last_block()? { + return Ok(false); + } + self.move_to_block(self.rp.block().num + 1)?; + self.current_slot = self.rp.next_after(self.current_slot)?; + } + Ok(true) + } + + pub fn get_i32(&self, filename: &str) -> DbResult { + self.rp.get_i32(self.current_slot, filename) + } + + pub fn get_string(&self, filename: &str) -> 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)); + }; + match info { + FieldInfo::Integer => Ok(Constant::Integer(self.get_i32(fieldname)?)), + FieldInfo::Varchar(_) => Ok(Constant::Varchar(self.get_string(fieldname)?)), + } + } + + pub fn has_field(&self, fieldname: &str) -> DbResult { + self.layout.schema().has_field(fieldname) + } + + pub fn set_i32(&self, field: &str, 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<()> { + 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<()> { + match value { + Constant::Integer(value) => self.set_i32(field, value), + Constant::Varchar(value) => self.set_string(field, &value), + } + } + + pub fn insert(&mut self) -> DbResult<()> { + let last_block = self.tx.size(&self.filename)? as i32 - 1; + if self.rp.block().num != last_block { + self.move_to_block(last_block)?; + } + self.current_slot = self.rp.insert_after(self.current_slot)?; + while self.current_slot < 0 { + if self.at_last_block()? { + self.move_to_new_block()?; + } else { + self.move_to_block(self.rp.block().num + 1)?; + } + self.current_slot = self.rp.insert_after(self.current_slot)?; + } + Ok(()) + } + + pub fn delete(&self) -> DbResult<()> { + self.rp.delete(self.current_slot) + } + + 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)?; + self.rp = rp; + self.current_slot = rid.slot(); + Ok(()) + } + + pub fn get_rid(&self) -> RID { + let rp = &self.rp; + RID::new(rp.block().num, self.current_slot) + } + + 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.current_slot = -1; + Ok(()) + } + + 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.format()?; + self.current_slot = -1; + Ok(()) + } + + fn at_last_block(&self) -> DbResult { + Ok(self.rp.block().num == self.tx.size(&self.filename)? as i32 - 1) + } + + fn schema(&self) -> DbResult> { + Ok(self.layout.schema()) + } +} + +pub struct TableScan { + lock: RefCell, +} + +impl TableScan { + pub fn new(tx: &Arc, tablename: &str, layout: &Arc) -> DbResult { + Ok(Self { + lock: RefCell::new(TableScanInner::new(tx, tablename, layout)?), + }) + } +} + +impl Scan for TableScan { + fn before_first(&self) -> DbResult<()> { + let mut write = self.lock.borrow_mut(); + write.before_first() + } + + fn next(&self) -> DbResult { + let mut write = self.lock.borrow_mut(); + write.next() + } + + fn get_i32(&self, field: &str) -> DbResult { + let read = self.lock.borrow(); + read.get_i32(field) + } + + fn get_string(&self, field: &str) -> DbResult { + let read = self.lock.borrow(); + read.get_string(field) + } + + fn get_val(&self, field: &str) -> DbResult { + let read = self.lock.borrow(); + read.get_val(field) + } + + fn has_field(&self, field: &str) -> DbResult { + let read = self.lock.borrow(); + read.has_field(field) + } + + fn close(&self) -> DbResult<()> { + let read = self.lock.borrow(); + read.close() + } + + fn set_i32(&self, field: &str, value: i32) -> DbResult<()> { + let read = self.lock.borrow(); + read.set_i32(field, value) + } + + fn set_string(&self, field: &str, value: &str) -> DbResult<()> { + let read = self.lock.borrow(); + read.set_string(field, value) + } + + fn set_val(&self, field: &str, value: Constant) -> DbResult<()> { + let read = self.lock.borrow(); + read.set_val(field, value) + } + + fn insert(&self) -> DbResult<()> { + let mut write = self.lock.borrow_mut(); + write.insert() + } + + fn delete(&self) -> DbResult<()> { + let read = self.lock.borrow(); + read.delete() + } + + fn move_to_rid(&self, rid: RID) -> DbResult<()> { + let mut write = self.lock.borrow_mut(); + write.move_to_rid(rid) + } + + fn get_rid(&self) -> DbResult { + let read = self.lock.borrow(); + Ok(read.get_rid()) + } + + fn schema(&self) -> DbResult> { + let read = self.lock.borrow(); + read.schema() + } +} + +#[cfg(test)] +mod tests { + use buffer::mgr::BufferMgr; + use file::mgr::FileMgr; + use log::mgr::LogMgr; + use tempfile::tempdir; + use transaction::lock_table::LockTable; + + use crate::schema::Schema; + + use super::*; + + #[test] + fn table_scan() { + let dir = tempdir().unwrap(); + 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 lock_table = Arc::new(LockTable::default()); + + let tx = Arc::new(Transaction::new(&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 layout = Arc::new(Layout::new(&schema).unwrap()); + let offset_a = layout.offset("A"); + let offset_b = layout.offset("B"); + assert_ne!(offset_a, offset_b, "fields must occupy distinct offsets"); + + // Fill the table with 50 records carrying known A-values 0..50. + let ts = TableScan::new(&tx, "T", &layout).unwrap(); + ts.before_first().unwrap(); + for n in 0..50 { + ts.insert().unwrap(); + ts.set_i32("A", n).unwrap(); + ts.set_string("B", &format!("record{}", n)).unwrap(); + } + + // 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(); + if a < 10 { + ts.delete().unwrap(); + deleted += 1; + } + } + 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(); + 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 new file mode 100644 index 0000000..6aa0b99 --- /dev/null +++ b/engine/src/schema.rs @@ -0,0 +1,123 @@ +use std::{collections::HashMap, fmt::Debug, sync::RwLock}; + +use common::{DbResult, error::DbError}; + +use crate::field_info::FieldInfo; + +#[derive(Debug)] +struct SchemaLock { + fields: Vec, + infos: HashMap, +} + +impl SchemaLock { + fn new() -> Self { + Self { + fields: Vec::new(), + infos: HashMap::new(), + } + } + + 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) { + self.add_field(fieldname, FieldInfo::Integer); + } + + fn add_string_field(&mut self, fieldname: String, length: i32) { + self.add_field(fieldname, FieldInfo::Varchar(length)); + } + + fn add(&mut self, fieldname: String, schema: &Self) { + if let Some(info) = schema.info(&fieldname) { + self.add_field(fieldname, info); + } + } + + fn add_all(&mut self, schema: &Self) { + for (field, info) in &schema.infos { + self.add_field(field.clone(), info.clone()); + } + } + + fn info(&self, fieldname: &str) -> Option { + self.infos.get(fieldname).cloned() + } +} + +pub struct Schema { + lock: RwLock, +} + +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) + } +} + +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 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 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 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(()) + } + + 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 has_field(&self, fieldname: &str) -> DbResult { + let read = self.lock.read().map_err(DbError::lock)?; + Ok(read.infos.contains_key(fieldname)) + } + + pub fn info(&self, fieldname: &str) -> DbResult> { + let read = self.lock.read().map_err(DbError::lock)?; + Ok(read.info(fieldname)) + } + + 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) + } +} + +impl Default for Schema { + fn default() -> Self { + Self { + lock: RwLock::new(SchemaLock::new()), + } + } +} diff --git a/engine/src/order.rs b/engine/src/sort_by.rs similarity index 84% rename from engine/src/order.rs rename to engine/src/sort_by.rs index 64f9760..972a424 100644 --- a/engine/src/order.rs +++ b/engine/src/sort_by.rs @@ -1,15 +1,15 @@ #[derive(Debug, Default)] -pub struct OrderByData { +pub struct SortByData { pub fields: Vec, } -impl OrderByData { +impl SortByData { pub fn is_empty(&self) -> bool { self.fields.is_empty() } } -impl std::fmt::Display for OrderByData { +impl std::fmt::Display for SortByData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "SORT BY")?; for (i, field) in self.fields.iter().enumerate() { diff --git a/engine/src/stat_mgr.rs b/engine/src/stat_mgr.rs new file mode 100644 index 0000000..f90cfbe --- /dev/null +++ b/engine/src/stat_mgr.rs @@ -0,0 +1,131 @@ +use std::{ + collections::HashMap, + 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}, +}; + +#[derive(Clone, Debug)] +pub struct StatInfo { + num_blocks: i32, + num_recs: u16, +} + +impl StatInfo { + pub fn new(num_blocks: i32, num_recs: u16) -> Self { + Self { + num_blocks, + num_recs, + } + } + + pub fn block_accessed(&self) -> i32 { + self.num_blocks + } + + pub fn records_output(&self) -> i32 { + self.num_recs as i32 + } + + pub fn distinct_values(&self) -> i32 { + 1 + (self.num_recs / 3) as i32 + } +} + +struct StatMgrLock { + table_mgr: Arc, + table_stats: HashMap, + num_calls: u64, +} + +impl StatMgrLock { + fn new(table_mgr: &Arc) -> Self { + Self { + table_mgr: Arc::clone(table_mgr), + table_stats: HashMap::new(), + num_calls: 0, + } + } + + fn get_stat_info( + &mut self, + table_name: &str, + layout: &Arc, + tx: &Arc, + ) -> DbResult { + self.num_calls += 1; + if self.num_calls > 100 { + self.refresh_statisic(tx)?; + } + if let Some(stat) = self.table_stats.get(table_name).cloned() { + Ok(stat) + } else { + let stat = self.calc_table_stats(table_name, layout, tx)?; + self.table_stats + .insert(table_name.to_string(), stat.clone()); + Ok(stat) + } + } + + 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)?; + while ts.next()? { + let table_name = ts.get_string(TABLE_NAME)?; + let layout = self.table_mgr.get_layout(&table_name, tx)?; + let stat = self.calc_table_stats(&table_name, &Arc::new(layout), tx)?; + self.table_stats.insert(table_name, stat); + } + ts.close() + } + + fn calc_table_stats( + &self, + table_name: &str, + layout: &Arc, + tx: &Arc, + ) -> DbResult { + let mut num_recs = 0; + let mut num_blocks = 0; + let ts = TableScan::new(tx, table_name, layout)?; + while ts.next()? { + num_recs += 1; + num_blocks = ts.get_rid()?.block_num() + 1; + } + ts.close()?; + Ok(StatInfo::new(num_blocks, num_recs)) + } +} + +pub struct StatMgr { + lock: RwLock, +} + +impl StatMgr { + pub fn new(table_mgr: &Arc, tx: &Arc) -> DbResult { + let mut lock = StatMgrLock::new(table_mgr); + lock.refresh_statisic(tx)?; + Ok(Self { + lock: RwLock::new(lock), + }) + } + + pub fn get_stat_info( + &self, + table_name: &str, + layout: &Arc, + tx: &Arc, + ) -> DbResult { + let mut write = self.lock.write().map_err(DbError::lock)?; + write.get_stat_info(table_name, layout, tx) + } +} diff --git a/engine/src/table_mgr.rs b/engine/src/table_mgr.rs new file mode 100644 index 0000000..39e8342 --- /dev/null +++ b/engine/src/table_mgr.rs @@ -0,0 +1,143 @@ +use std::{collections::HashMap, sync::Arc}; + +use common::DbResult; +use transaction::transaction::Transaction; + +use crate::{ + field_info::FieldInfo, + layout::Layout, + scan::{Scan, table::TableScan}, + schema::Schema, +}; + +const MAX_NAME: i32 = 16; + +pub(crate) const TABLE_NAME: &str = "sp_table"; +const TABLE_SLOT_SIZE: &str = "slot_size"; +const FIELDS_NAME: &str = "sp_fields"; + +const F_TYPE: &str = "type"; +const F_FIELD_NAME: &str = "field"; +const F_TYPE_LENGTH: &str = "length"; +const F_OFFSET: &str = "offset"; + +pub struct TableMgr { + table_catalog_layout: Arc, + fields_catalog_layout: Arc, +} + +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 mgr = Self { + table_catalog_layout: Arc::new(table_catalog_layout), + fields_catalog_layout: Arc::new(fields_catalog_layout), + }; + + if is_new { + mgr.create_table(TABLE_NAME, &table_catalog_schema, tx)?; + mgr.create_table(FIELDS_NAME, &fields_catalog_schema, tx)?; + } + + Ok(mgr) + } + + pub fn create_table( + &self, + table_name: &str, + schema: &Arc, + tx: &Arc, + ) -> DbResult<()> { + let layout = Layout::new(schema)?; + + let tcat = TableScan::new(tx, TABLE_NAME, &self.table_catalog_layout)?; + tcat.insert()?; + tcat.set_string(TABLE_NAME, table_name)?; + tcat.set_i32(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()? { + 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.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)?; + while tcat.next()? { + if tcat.get_string(TABLE_NAME)? == table_name { + size = tcat.get_i32(TABLE_SLOT_SIZE)?; + break; + } + } + tcat.close()?; + let schema = Arc::new(Schema::default()); + let mut offsets = HashMap::new(); + let fcat = TableScan::new(tx, FIELDS_NAME, &self.fields_catalog_layout)?; + while fcat.next()? { + let table = fcat.get_string(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); + } + } + fcat.close()?; + Ok(Layout::from(&schema, offsets, size)) + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use super::*; + use crate::SimpleDB; + use tempfile::tempdir; + + #[test] + fn manager() { + let dir = tempdir().unwrap(); + let db = SimpleDB::new(dir.path()).unwrap(); + + 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(); + + 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() { + assert!(expected_fields.contains(&(field_name, info))); + } + tx.commit().unwrap(); + } +} diff --git a/engine/src/temp.rs b/engine/src/temp.rs new file mode 100644 index 0000000..dfda0e4 --- /dev/null +++ b/engine/src/temp.rs @@ -0,0 +1,48 @@ +use crate::layout::Layout; +use crate::scan::Scan; +use crate::scan::table::TableScan; +use crate::schema::Schema; +use common::DbResult; +use std::rc::Rc; +use std::sync::atomic::AtomicI32; +use std::sync::{Arc, atomic}; +use transaction::transaction::Transaction; + +static TABLE_NUM_COUNTER: AtomicI32 = AtomicI32::new(0); + +fn next_table_num() -> i32 { + TABLE_NUM_COUNTER.fetch_add(1, atomic::Ordering::SeqCst) +} + +#[derive(Clone)] +pub struct TempTable { + tx: Arc, + table_name: String, + layout: Arc, +} + +impl TempTable { + pub fn new(tx: &Arc, schema: &Arc) -> DbResult { + Ok(Self { + table_name: format!("temp_{}", next_table_num()), + tx: Arc::clone(tx), + layout: Arc::new(Layout::new(schema)?), + }) + } + + pub fn open(&self) -> DbResult> { + Ok(Rc::new(TableScan::new( + &self.tx, + &self.table_name, + &self.layout, + )?)) + } + + pub(crate) fn table_name(&self) -> String { + self.table_name.clone() + } + + pub(crate) fn layout(&self) -> Arc { + self.layout.clone() + } +} diff --git a/engine/src/view_mgr.rs b/engine/src/view_mgr.rs new file mode 100644 index 0000000..77de05b --- /dev/null +++ b/engine/src/view_mgr.rs @@ -0,0 +1,58 @@ +use std::sync::Arc; + +use common::DbResult; +use transaction::transaction::Transaction; + +use crate::{ + scan::{Scan, table::TableScan}, + schema::Schema, + table_mgr::TableMgr, +}; + +const MAX_NAME: i32 = 16; +const MAX_VIEW_DEF: i32 = 100; + +const VIEW_NAME: &str = "view"; +const VIEW_DEF: &str = "view_def"; +const VIEW_TABLE: &str = "sp_view"; + +pub struct ViewMgr { + table_mgr: Arc, +} + +impl ViewMgr { + pub fn new(is_new: bool, table_mgr: &Arc, 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)?; + } + Ok(Self { + table_mgr: Arc::clone(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)?; + ts.insert()?; + ts.set_string(VIEW_NAME, name)?; + ts.set_string(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 mut result = None; + while ts.next()? { + if ts.get_string(VIEW_NAME)? == name { + result = Some(ts.get_string(VIEW_DEF)?); + break; + } + } + ts.close()?; + Ok(result) + } +} diff --git a/planner/src/element.rs b/planner/src/element.rs index da100f9..789e730 100644 --- a/planner/src/element.rs +++ b/planner/src/element.rs @@ -1,6 +1,3 @@ -use common::DbResult; -use common::error::DbError; - #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Element { Raw(String), @@ -20,13 +17,6 @@ impl Element { 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), - } - } } impl std::fmt::Display for Element { diff --git a/planner/src/plan.rs b/planner/src/plan.rs index 03c3b6b..c28f51f 100644 --- a/planner/src/plan.rs +++ b/planner/src/plan.rs @@ -14,7 +14,6 @@ use crate::{ pub(crate) mod select; pub(crate) mod table; -mod project; pub(crate) enum Planner { Table(TablePlan), @@ -32,10 +31,6 @@ impl Plan { Self(Planner::Select(SelectPlan::new(plan, predicate))) } - pub fn project(plan: Box, predicate: Predicate) -> Self { - Self(Planner::Select(SelectPlan::new(plan, predicate))) - } - pub fn open(&self) -> DbResult { match &self.0 { Planner::Table(table) => table.open(), diff --git a/planner/src/plan/project.rs b/planner/src/plan/project.rs deleted file mode 100644 index 480ef33..0000000 --- a/planner/src/plan/project.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::{ - collections::{HashMap, HashSet}, - sync::Arc, -}; - -use common::DbResult; - -use crate::schema::SchemaBuilder; -use crate::{element::Element, plan::Plan, scan::Scan, schema::Schema}; - -pub(crate) struct ProjectPlan { - plan: Box, - schema: Schema, - mapping: HashMap, -} - -impl ProjectPlan { - pub fn new(plan: Box, fields: Vec) -> DbResult { - let mut schema = SchemaBuilder::default(); - for field in fields { - let other = plan.schema()?; - schema = schema.add(field, &other); - } - let schema = schema.build(); - Ok(Self { - plan, - schema, - mapping: HashMap::new(), - }) - } -} - -impl ProjectPlan { - pub(crate) fn open(&self) -> DbResult { - let scan = self.plan.open()?; - let fields: HashSet = self.schema.fields().into_iter().map(|(f, _)| f).collect(); - Ok(Scan::project(Box::new(scan), fields)) - } - - pub(crate) fn blocks_accessed(&self) -> DbResult { - self.plan.blocks_accessed() - } - - pub(crate) fn records_output(&self) -> DbResult { - self.plan.records_output() - } - - pub(crate) fn distinct_values(&self, field_name: &Element) -> DbResult { - self.plan.distinct_values(field_name) - } - - pub(crate) fn schema(&self) -> Schema { - self.schema.clone() - } -} diff --git a/planner/src/scan.rs b/planner/src/scan.rs index c44e46e..7beb4a4 100644 --- a/planner/src/scan.rs +++ b/planner/src/scan.rs @@ -1,12 +1,9 @@ -use std::collections::HashSet; use std::sync::Arc; use common::DbResult; use transaction::transaction::Transaction; use crate::scan::chunk::ChunkScan; -use crate::scan::product::ProductScan; -use crate::scan::project::ProjectScan; use crate::{ element::Element, layout::Layout, @@ -16,6 +13,7 @@ use crate::{ schema::Schema, value::Value, }; +use crate::scan::product::ProductScan; pub(crate) mod chunk; pub(crate) mod group; @@ -55,13 +53,6 @@ impl Scan { }) } - pub fn project(s1: Box, fields: HashSet) -> Self { - let project = ProjectScan::new(s1, fields); - Self { - scan: Scanner::Project(project), - } - } - pub fn chunk( tx: &Arc, filename: &str, diff --git a/planner/src/scan/project.rs b/planner/src/scan/project.rs index 8449efe..aac89ec 100644 --- a/planner/src/scan/project.rs +++ b/planner/src/scan/project.rs @@ -22,7 +22,7 @@ impl ProjectScan { self.scan.before_first() } - pub(crate) fn next_row(&mut self) -> DbResult { + pub(crate) fn next(&mut self) -> DbResult { self.scan.next_row() } diff --git a/planner/src/scan/scanner.rs b/planner/src/scan/scanner.rs index 9bbb9e2..37f1c8c 100644 --- a/planner/src/scan/scanner.rs +++ b/planner/src/scan/scanner.rs @@ -9,14 +9,12 @@ use crate::{ }; use crate::scan::chunk::ChunkScan; use crate::scan::product::ProductScan; -use crate::scan::project::ProjectScan; pub(crate) enum Scanner { Table(TableScan), Select(SelectScan), Chunk(ChunkScan), Product(ProductScan), - Project(ProjectScan), } impl Scanner { @@ -29,7 +27,6 @@ impl Scanner { Ok(()) } Self::Product(product) => product.before_first(), - Self::Project(product) => product.before_first(), } } @@ -39,7 +36,6 @@ impl Scanner { Self::Select(select) => select.next(), Self::Chunk(chunk) => chunk.next(), Self::Product(product) => product.next_row(), - Self::Project(product) => product.next_row(), } } @@ -49,7 +45,6 @@ impl Scanner { Self::Select(select) => select.get_i32(field), Self::Chunk(chunk) => chunk.get_i32(field), Self::Product(product) => product.get_i32(field), - Self::Project(product) => product.get_i32(field), } } @@ -59,7 +54,6 @@ impl Scanner { Self::Select(select) => select.get_string(field), Self::Chunk(chunk) => chunk.get_string(field), Self::Product(product) => product.get_string(field), - Self::Project(product) => product.get_string(field), } } @@ -69,7 +63,6 @@ impl Scanner { Self::Select(select) => select.get_val(field), Self::Chunk(chunk) => chunk.get_val(field), Self::Product(product) => product.get_val(field), - Self::Project(product) => product.get_val(field), } } @@ -79,7 +72,6 @@ impl Scanner { Self::Select(select) => select.has_field(field), Self::Chunk(chunk) => Ok(chunk.has_field(field)), Self::Product(product) => product.has_field(field), - Self::Project(product) => product.has_field(field), } } @@ -89,7 +81,6 @@ impl Scanner { Self::Select(select) => select.close(), Self::Chunk(chunk) => chunk.close(), Self::Product(product) => product.close(), - Self::Project(product) => product.close(), } } @@ -99,7 +90,6 @@ impl Scanner { Self::Select(select) => select.schema(), Self::Chunk(chunk) => Ok(chunk.schema()), Self::Product(product) => product.schema(), - Self::Project(product) => product.schema(), } } @@ -107,7 +97,7 @@ impl Scanner { match self { Self::Table(table) => table.set_i32(field, value), Self::Select(select) => select.set_i32(field, value), - _ => Err(Self::read_only()), + Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), } } @@ -115,7 +105,7 @@ impl Scanner { match self { Self::Table(table) => table.set_string(field, value), Self::Select(select) => select.set_string(field, value), - _ => Err(Self::read_only()), + Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), } } @@ -123,7 +113,7 @@ impl Scanner { match self { Self::Table(table) => table.set_val(field, value), Self::Select(select) => select.set_val(field, value), - _ => Err(Self::read_only()), + Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), } } @@ -131,7 +121,7 @@ impl Scanner { match self { Self::Table(table) => table.insert(), Self::Select(select) => select.insert(), - _ => Err(Self::read_only()), + Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), } } @@ -139,7 +129,7 @@ impl Scanner { match self { Self::Table(table) => table.delete(), Self::Select(select) => select.delete(), - _ => Err(Self::read_only()), + Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), } } @@ -147,7 +137,7 @@ impl Scanner { match self { Self::Table(table) => Ok(table.get_rid()), Self::Select(select) => select.get_rid(), - _ => Err(Self::read_only()), + Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), } } @@ -155,7 +145,7 @@ impl Scanner { match self { Self::Table(table) => table.move_to_rid(rid), Self::Select(select) => select.move_to_rid(rid), - _ => Err(Self::read_only()), + Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), } } From b5ee1dfc52c05b3ec8efe24e7ef8a093d4d8912a Mon Sep 17 00:00:00 2001 From: Daniil Buravlev Date: Tue, 30 Jun 2026 22:50:17 +0300 Subject: [PATCH 11/13] Apply migration to new index --- Cargo.lock | 14 - Cargo.toml | 19 +- engine/benches/db_benchmark.rs | 21 +- engine/src/constant.rs | 66 --- engine/src/element.rs | 59 +- engine/src/index.rs | 31 +- engine/src/index/b_tree.rs | 542 ++++++++++++------ {planner => engine}/src/index/b_tree/entry.rs | 0 {planner => engine}/src/index/b_tree/page.rs | 0 .../src/index/b_tree/pointer.rs | 0 engine/src/index/btree_dir.rs | 124 ---- engine/src/index/btree_leaf.rs | 173 ------ engine/src/index/btree_page.rs | 342 ----------- engine/src/index/dir_entry.rs | 12 - {planner => engine}/src/index/indexer.rs | 0 engine/src/index_mgr.rs | 115 ++-- engine/src/layout.rs | 76 ++- engine/src/lib.rs | 37 +- engine/src/metadata_mgr.rs | 23 +- engine/src/plan.rs | 7 +- engine/src/plan/group.rs | 25 +- engine/src/plan/index.rs | 46 +- engine/src/plan/materialize.rs | 11 +- engine/src/plan/merge.rs | 36 +- engine/src/plan/multibuffer.rs | 22 +- engine/src/plan/{sort.rs => order.rs} | 21 +- engine/src/plan/product.rs | 16 +- engine/src/plan/project.rs | 13 +- engine/src/plan/select.rs | 5 +- engine/src/plan/table.rs | 15 +- engine/src/predicate.rs | 42 +- engine/src/query/basic_planner.rs | 2 +- engine/src/query/command.rs | 18 +- engine/src/query/heuristic_planner.rs | 4 +- engine/src/query/lexer.rs | 18 +- engine/src/query/parser.rs | 55 +- engine/src/query/table_planner.rs | 7 +- engine/src/query/token.rs | 8 +- engine/src/record_page.rs | 51 +- engine/src/scan.rs | 21 +- engine/src/scan/chunk.rs | 47 +- engine/src/scan/group.rs | 51 +- engine/src/scan/index.rs | 74 +-- engine/src/scan/merge.rs | 47 +- engine/src/scan/multibuffer.rs | 31 +- engine/src/scan/{sort.rs => order.rs} | 58 +- engine/src/scan/product.rs | 20 +- engine/src/scan/project.rs | 31 +- engine/src/scan/select.rs | 18 +- engine/src/scan/table.rs | 102 ++-- engine/src/schema.rs | 157 ++--- engine/src/sort_by.rs | 4 +- engine/src/stat_mgr.rs | 36 +- engine/src/table_mgr.rs | 122 ++-- engine/src/temp.rs | 10 +- {planner => engine}/src/value.rs | 0 engine/src/view_mgr.rs | 39 +- planner/Cargo.toml | 16 - planner/src/buffer_needs.rs | 31 - planner/src/element.rs | 47 -- planner/src/field_info.rs | 74 --- planner/src/index.rs | 58 -- planner/src/index/b_tree.rs | 388 ------------- planner/src/layout.rs | 92 --- planner/src/lib.rs | 40 -- planner/src/mgr.rs | 6 - planner/src/mgr/index.rs | 143 ----- planner/src/mgr/metadata.rs | 98 ---- planner/src/mgr/stat.rs | 134 ----- planner/src/mgr/table.rs | 150 ----- planner/src/mgr/view.rs | 53 -- planner/src/plan.rs | 68 --- planner/src/plan/select.rs | 105 ---- planner/src/plan/table.rs | 52 -- planner/src/predicate.rs | 248 -------- planner/src/record_page.rs | 199 ------- planner/src/rid.rs | 33 -- planner/src/scan.rs | 189 ------ planner/src/scan/chunk.rs | 116 ---- planner/src/scan/group.rs | 167 ------ planner/src/scan/index.rs | 228 -------- planner/src/scan/merge.rs | 109 ---- planner/src/scan/multibuffer.rs | 154 ----- planner/src/scan/order.rs | 189 ------ planner/src/scan/product.rs | 74 --- planner/src/scan/project.rs | 71 --- planner/src/scan/scanner.rs | 159 ----- planner/src/scan/select.rs | 81 --- planner/src/scan/table.rs | 193 ------- planner/src/schema.rs | 142 ----- planner/src/temp.rs | 42 -- server/src/bin/cli.rs | 5 +- 92 files changed, 1274 insertions(+), 5624 deletions(-) delete mode 100644 engine/src/constant.rs rename {planner => engine}/src/index/b_tree/entry.rs (100%) rename {planner => engine}/src/index/b_tree/page.rs (100%) rename {planner => engine}/src/index/b_tree/pointer.rs (100%) delete mode 100644 engine/src/index/btree_dir.rs delete mode 100644 engine/src/index/btree_leaf.rs delete mode 100644 engine/src/index/btree_page.rs delete mode 100644 engine/src/index/dir_entry.rs rename {planner => engine}/src/index/indexer.rs (100%) rename engine/src/plan/{sort.rs => order.rs} (85%) rename engine/src/scan/{sort.rs => order.rs} (82%) rename {planner => engine}/src/value.rs (100%) delete mode 100644 planner/Cargo.toml delete mode 100644 planner/src/buffer_needs.rs delete mode 100644 planner/src/element.rs delete mode 100644 planner/src/field_info.rs delete mode 100644 planner/src/index.rs delete mode 100644 planner/src/index/b_tree.rs delete mode 100644 planner/src/layout.rs delete mode 100644 planner/src/lib.rs delete mode 100644 planner/src/mgr.rs delete mode 100644 planner/src/mgr/index.rs delete mode 100644 planner/src/mgr/metadata.rs delete mode 100644 planner/src/mgr/stat.rs delete mode 100644 planner/src/mgr/table.rs delete mode 100644 planner/src/mgr/view.rs delete mode 100644 planner/src/plan.rs delete mode 100644 planner/src/plan/select.rs delete mode 100644 planner/src/plan/table.rs delete mode 100644 planner/src/predicate.rs delete mode 100644 planner/src/record_page.rs delete mode 100644 planner/src/rid.rs delete mode 100644 planner/src/scan.rs delete mode 100644 planner/src/scan/chunk.rs delete mode 100644 planner/src/scan/group.rs delete mode 100644 planner/src/scan/index.rs delete mode 100644 planner/src/scan/merge.rs delete mode 100644 planner/src/scan/multibuffer.rs delete mode 100644 planner/src/scan/order.rs delete mode 100644 planner/src/scan/product.rs delete mode 100644 planner/src/scan/project.rs delete mode 100644 planner/src/scan/scanner.rs delete mode 100644 planner/src/scan/select.rs delete mode 100644 planner/src/scan/table.rs delete mode 100644 planner/src/schema.rs delete mode 100644 planner/src/temp.rs diff --git a/Cargo.lock b/Cargo.lock index 6c60576..bb3b754 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -580,20 +580,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "planner" -version = "0.0.1" -dependencies = [ - "buffer", - "common", - "file", - "log 0.0.1", - "rand", - "tempfile", - "tracing", - "transaction", -] - [[package]] name = "plotters" version = "0.3.7" diff --git a/Cargo.toml b/Cargo.toml index 6d52a1a..8fee391 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,16 +4,15 @@ version = "0.0.1" [workspace] resolver = "2" members = [ - "buffer", - "client", - "common", - "engine", - "file", - "log", - "network", - "planner", - "server", - "transaction", + "buffer", + "client", + "common", + "engine", + "file", + "log", + "network", + "server", + "transaction", ] [workspace.dependencies] diff --git a/engine/benches/db_benchmark.rs b/engine/benches/db_benchmark.rs index a8b69d5..515743f 100644 --- a/engine/benches/db_benchmark.rs +++ b/engine/benches/db_benchmark.rs @@ -20,6 +20,25 @@ 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 +68,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 index 66a104b..3b1f00c 100644 --- a/engine/src/element.rs +++ b/engine/src/element.rs @@ -1,18 +1,38 @@ -use common::{DbResult, error::DbError}; +use common::DbResult; +use common::error::DbError; -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(crate) enum Element { +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Element { Raw(String), - Id { source: String, name: String }, - Specifier { source: String, spec: String }, + View(String, String), + Spec(String, String), } impl Element { - pub(crate) fn source(&self) -> DbResult<&str> { + 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(value) => Ok(value), - Self::Specifier { source, .. } => Ok(source), - e => Err(DbError::UnexpectedToken(e.to_string())), + Self::Raw(s) => s.len(), + Self::View(source, name) => source.len() + 4 + name.len(), + Self::Spec(source, target) => source.len() + 4 + target.len(), } } } @@ -21,8 +41,25 @@ 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::Id { source, name } => write!(f, "{} {}", source, name), - Self::Specifier { source, spec } => write!(f, "{}.{}", source, spec), + 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..1761e31 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,10 +22,12 @@ pub trait Index { mod tests { use tempfile::tempdir; + use crate::schema::SchemaBuilder; use crate::{ SimpleDB, plan::{Plan, table::TablePlan}, }; + use crate::element::Element; #[test] fn index_retrieval() { @@ -41,14 +39,19 @@ mod tests { 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 +61,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/planner/src/index/b_tree/entry.rs b/engine/src/index/b_tree/entry.rs similarity index 100% rename from planner/src/index/b_tree/entry.rs rename to engine/src/index/b_tree/entry.rs diff --git a/planner/src/index/b_tree/page.rs b/engine/src/index/b_tree/page.rs similarity index 100% rename from planner/src/index/b_tree/page.rs rename to engine/src/index/b_tree/page.rs diff --git a/planner/src/index/b_tree/pointer.rs b/engine/src/index/b_tree/pointer.rs similarity index 100% rename from planner/src/index/b_tree/pointer.rs rename to engine/src/index/b_tree/pointer.rs 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/planner/src/index/indexer.rs b/engine/src/index/indexer.rs similarity index 100% rename from planner/src/index/indexer.rs rename to engine/src/index/indexer.rs diff --git a/engine/src/index_mgr.rs b/engine/src/index_mgr.rs index 27de484..71cbd92 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() @@ -85,33 +73,34 @@ impl IndexInfo { } 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 +111,11 @@ impl IndexMgr { field_name: &str, tx: &Arc, ) -> DbResult<()> { - let ts = TableScan::new(tx, IDX_TABLE, &self.layout)?; + let mut 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 +123,20 @@ 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 mut 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)?; + 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, field_name.clone(), &layout.schema(), tx, stat)?; - result.insert(field_name, 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 8db3cbd..4b75bd6 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -16,8 +16,8 @@ use crate::{ }; pub mod buffer_needs; -pub mod constant; -pub(crate) mod element; +pub mod value; +pub mod element; pub mod field_info; pub mod index; pub mod index_mgr; @@ -107,10 +107,25 @@ impl SimpleDB { #[cfg(test)] mod tests { use std::collections::HashSet; - use tempfile::tempdir; - + use tempfile::{tempdir, TempDir}; + use crate::element::Element; 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() { let dir = tempdir().unwrap(); @@ -128,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"); } @@ -185,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"); @@ -222,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()); @@ -253,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..2e20ba9 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, @@ -13,18 +14,18 @@ use crate::{ }; 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 +37,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 +74,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 +84,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..387e485 100644 --- a/engine/src/plan.rs +++ b/engine/src/plan.rs @@ -3,6 +3,7 @@ use std::{rc::Rc, sync::Arc}; use common::DbResult; use crate::{scan::Scan, schema::Schema}; +use crate::element::Element; pub(crate) mod group; pub mod index; @@ -12,7 +13,7 @@ pub(crate) mod multibuffer; pub mod product; pub mod project; pub mod select; -pub(crate) mod sort; +pub(crate) mod order; 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..0c0a42a 100644 --- a/engine/src/plan/group.rs +++ b/engine/src/plan/group.rs @@ -4,33 +4,36 @@ use common::DbResult; use transaction::transaction::Transaction; use crate::{ - plan::{Plan, sort::SortPlan}, + plan::{Plan, order::SortPlan}, scan::group::{AggregationFn, GroupByScan}, schema::Schema, }; +use crate::element::Element; +use crate::schema::SchemaBuilder; 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..a8cfaf6 100644 --- a/engine/src/plan/materialize.rs +++ b/engine/src/plan/materialize.rs @@ -7,6 +7,7 @@ use common::DbResult; use std::rc::Rc; use std::sync::Arc; use transaction::transaction::Transaction; +use crate::element::Element; pub struct MaterializePlan { source: Rc, @@ -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..38ba6b5 100644 --- a/engine/src/plan/sort.rs +++ b/engine/src/plan/order.rs @@ -7,16 +7,17 @@ use crate::{ plan::{Plan, materialize::MaterializePlan}, scan::{ Scan, - sort::{RecordComparator, SortScan}, + order::{RecordComparator, OrderScan}, }, schema::Schema, temp::TempTable, }; +use crate::element::Element; 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..3b54cfe 100644 --- a/engine/src/plan/product.rs +++ b/engine/src/plan/product.rs @@ -2,6 +2,8 @@ use std::{rc::Rc, sync::Arc}; 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 1a63954..89e56d0 100644 --- a/engine/src/plan/project.rs +++ b/engine/src/plan/project.rs @@ -12,16 +12,17 @@ use crate::{ scan::{Scan, project::ProjectScan}, schema::Schema, }; +use crate::schema::SchemaBuilder; pub struct ProjectPlan { plan: Rc, - schema: Arc, + schema: Schema, mapping: HashMap, } impl ProjectPlan { pub fn new(plan: Rc, fields: Vec, tables: Vec) -> DbResult { - let schema = Arc::new(Schema::default()); + let schema = SchemaBuilder::default().build(); //for field in fields { // let other = plan.schema()?; // schema.add(field, &other)?; @@ -37,7 +38,7 @@ impl ProjectPlan { 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))) } @@ -49,11 +50,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..9a326e0 100644 --- a/engine/src/plan/select.rs +++ b/engine/src/plan/select.rs @@ -8,6 +8,7 @@ use crate::{ scan::{Scan, select::SelectScan}, schema::Schema, }; +use crate::element::Element; pub struct SelectPlan { plan: Rc, @@ -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..0e55b82 100644 --- a/engine/src/query/basic_planner.rs +++ b/engine/src/query/basic_planner.rs @@ -81,7 +81,7 @@ impl UpdatePlanner for BasicUpdatePlanner { tx: &Arc, ) -> DbResult { self.md - .create_table(&data.name, &Arc::new(data.schema), tx)?; + .create_table(&data.name, data.schema, tx)?; Ok(0) } diff --git a/engine/src/query/command.rs b/engine/src/query/command.rs index 755954c..73f032d 100644 --- a/engine/src/query/command.rs +++ b/engine/src/query/command.rs @@ -1,9 +1,9 @@ use crate::{ - constant::Constant, element::Element, predicate::{Expression, Predicate}, schema::Schema, sort_by::SortByData, + value::Value, }; pub enum Command { @@ -99,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 { @@ -128,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, } @@ -168,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 { @@ -187,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 d5d4414..188818d 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}, @@ -26,7 +26,7 @@ impl HeuristicQueryPlannerInner { fn create_plan(&mut self, data: QueryData, tx: &Arc) -> DbResult> { for table in &data.tables { - let tp = TablePlanner::new(table.source()?, 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()?; diff --git a/engine/src/query/lexer.rs b/engine/src/query/lexer.rs index 4a6278c..81f8daf 100644 --- a/engine/src/query/lexer.rs +++ b/engine/src/query/lexer.rs @@ -1,7 +1,7 @@ use common::{DbResult, error::DbError}; use crate::{ - constant::Constant, + value::Value, query::{token::Token, tokenizer::Tokenizer}, }; @@ -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 db2ea71..c0e837b 100644 --- a/engine/src/query/parser.rs +++ b/engine/src/query/parser.rs @@ -1,7 +1,6 @@ use common::{DbResult, error::DbError}; use crate::{ - constant::Constant, element::Element, predicate::{Expression, Predicate, Term}, query::{ @@ -14,7 +13,9 @@ use crate::{ }, schema::Schema, sort_by::SortByData, + value::Value, }; +use crate::schema::SchemaBuilder; pub(crate) struct Parser { lexer: Lexer, @@ -30,12 +31,12 @@ impl Parser { pub(crate) fn element(&self) -> DbResult { let id = self.lexer.eat_id()?; if self.lexer.match_delim('.') { - self.lexer.eat_delimiter(',')?; + self.lexer.eat_delimiter('.')?; let spec = self.lexer.eat_id()?; - Ok(Element::Specifier { source: id, spec }) + Ok(Element::Spec(id, spec)) } else if self.lexer.match_id() { let name = self.lexer.eat_id()?; - Ok(Element::Id { source: id, name }) + Ok(Element::View(id, name)) } else { Ok(Element::Raw(id)) } @@ -45,7 +46,7 @@ impl Parser { 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 { @@ -55,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()?)) } @@ -97,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 { @@ -154,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(); @@ -200,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(',') { @@ -230,35 +231,35 @@ 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 { @@ -288,19 +289,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..4f1db79 100644 --- a/engine/src/query/table_planner.rs +++ b/engine/src/query/table_planner.rs @@ -16,12 +16,13 @@ use crate::{ predicate::Predicate, schema::Schema, }; +use crate::element::Element; 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 b37131a..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)) } diff --git a/engine/src/record_page.rs b/engine/src/record_page.rs index a5cb769..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)?, @@ -125,7 +125,7 @@ mod tests { use tempfile::tempdir; use transaction::lock_table::LockTable; - use crate::schema::Schema; + use crate::schema::SchemaBuilder; use super::*; @@ -139,15 +139,20 @@ mod tests { 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(); @@ -163,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(); @@ -183,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..6726620 100644 --- a/engine/src/scan.rs +++ b/engine/src/scan.rs @@ -2,7 +2,8 @@ use common::{DbResult, error::DbError}; use std::sync::Arc; use crate::schema::Schema; -use crate::{constant::Constant, rid::RID}; +use crate::{value::Value, rid::RID}; +use crate::element::Element; pub mod chunk; pub(crate) mod group; @@ -12,7 +13,7 @@ pub(crate) mod multibuffer; pub mod product; pub mod project; pub mod select; -pub(crate) mod sort; +pub(crate) mod order; pub mod table; pub trait Scan { @@ -20,27 +21,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..c11ad6f 100644 --- a/engine/src/scan/chunk.rs +++ b/engine/src/scan/chunk.rs @@ -7,14 +7,15 @@ use transaction::transaction::Transaction; use crate::schema::Schema; use crate::{ - constant::Constant, field_info::FieldInfo, layout::Layout, record_page::RecordPage, scan::Scan, + value::Value, field_info::FieldInfo, layout::Layout, record_page::RecordPage, scan::Scan, }; +use crate::element::Element; 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..5d75052 100644 --- a/engine/src/scan/group.rs +++ b/engine/src/scan/group.rs @@ -2,12 +2,13 @@ use std::{cell::RefCell, cmp::Ordering, collections::HashMap, rc::Rc, sync::Arc} 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 e6aa044..677c91c 100644 --- a/engine/src/scan/index.rs +++ b/engine/src/scan/index.rs @@ -2,17 +2,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 +38,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 +59,7 @@ impl Scan for IndexSelectScan { self.scan.close() } - fn schema(&self) -> DbResult> { + fn schema(&self) -> DbResult { self.scan.schema() } } @@ -67,20 +68,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 +114,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 +122,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 +130,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 +138,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,11 +148,11 @@ 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) } } @@ -161,7 +162,8 @@ mod tests { use std::sync::Arc; use tempfile::tempdir; - use crate::schema::Schema; + use crate::element::Element; + use crate::schema::{Schema, SchemaBuilder}; use crate::{ SimpleDB, plan::{Plan, table::TablePlan}, @@ -174,13 +176,19 @@ mod tests { 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 +202,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 +215,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 +229,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..ae1ff83 100644 --- a/engine/src/scan/merge.rs +++ b/engine/src/scan/merge.rs @@ -2,16 +2,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 +20,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 +68,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 +76,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 +84,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 +92,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 +101,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 +119,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 +139,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 +164,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..d48fd3f 100644 --- a/engine/src/scan/multibuffer.rs +++ b/engine/src/scan/multibuffer.rs @@ -9,6 +9,7 @@ use crate::{ scan::{Scan, chunk::ChunkScan, product::ProductScan}, schema::Schema, }; +use crate::element::Element; struct MultiBufferProductScanInner { tx: Arc, @@ -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..541e19c 100644 --- a/engine/src/scan/product.rs +++ b/engine/src/scan/product.rs @@ -1,10 +1,10 @@ +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, s2: Rc, @@ -33,7 +33,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 +41,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 +49,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 +57,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 +66,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..42dc0f9 100644 --- a/engine/src/scan/project.rs +++ b/engine/src/scan/project.rs @@ -1,5 +1,6 @@ +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; @@ -7,11 +8,11 @@ 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 +26,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 +58,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..40dae1e 100644 --- a/engine/src/scan/select.rs +++ b/engine/src/scan/select.rs @@ -1,9 +1,9 @@ use common::DbResult; use std::rc::Rc; -use std::sync::Arc; use crate::schema::Schema; use crate::{predicate::Predicate, scan::Scan}; +use crate::element::Element; pub struct SelectScan { scan: Rc, @@ -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 cb933b7..0d8d8f6 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,13 @@ impl Scan for TableScan { #[cfg(test)] mod tests { + use crate::schema::{Schema, SchemaBuilder}; use buffer::mgr::BufferMgr; use file::mgr::FileMgr; use log::mgr::LogMgr; use tempfile::tempdir; use transaction::lock_table::LockTable; - use crate::schema::Schema; - use super::*; #[test] @@ -272,29 +272,31 @@ mod tests { let lock_table = Arc::new(LockTable::default()); let tx = Arc::new(Transaction::new(&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 layout = Arc::new(Layout::new(&schema).unwrap()); - let offset_a = layout.offset("A"); - let offset_b = layout.offset("B"); + let schema = SchemaBuilder::default() + .add_int_field(Element::raw("A")) + .add_string_field(Element::raw("B"), 9) + .build(); + + 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"); // Fill the table with 50 records carrying known A-values 0..50. - let ts = TableScan::new(&tx, "T", &layout).unwrap(); + let ts = TableScan::new(&tx, "T", layout).unwrap(); ts.before_first().unwrap(); for n in 0..50 { ts.insert().unwrap(); - ts.set_i32("A", n).unwrap(); - ts.set_string("B", &format!("record{}", n)).unwrap(); + ts.set_i32(&Element::raw("A"), n).unwrap(); + ts.set_string(&Element::raw("B"), &format!("record{}", n)) + .unwrap(); } // 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 a = ts.get_i32(&Element::raw("A")).unwrap(); if a < 10 { ts.delete().unwrap(); deleted += 1; @@ -306,8 +308,8 @@ mod tests { 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(); + 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; 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/planner/src/value.rs b/engine/src/value.rs similarity index 100% rename from planner/src/value.rs rename to engine/src/value.rs diff --git a/engine/src/view_mgr.rs b/engine/src/view_mgr.rs index 77de05b..6e97229 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::{element::Element, scan::Scan, schema::SchemaBuilder}; +use crate::scan::table::TableScan; +use crate::table_mgr::TableMgr; const MAX_NAME: i32 = 16; const MAX_VIEW_DEF: i32 = 100; @@ -17,38 +15,37 @@ const VIEW_DEF: &str = "view_def"; const VIEW_TABLE: &str = "sp_view"; 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 mut 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 mut 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/planner/Cargo.toml b/planner/Cargo.toml deleted file mode 100644 index 5882dd8..0000000 --- a/planner/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "planner" -edition = "2024" -version.workspace = true - -[dependencies] -buffer = { path = "../buffer" } -common = { path = "../common" } -file = { path = "../file" } -log = { path = "../log" } -transaction = { path = "../transaction/" } -tracing = { workspace = true } - -[dev-dependencies] -rand = { workspace = true } -tempfile = { workspace = true } diff --git a/planner/src/buffer_needs.rs b/planner/src/buffer_needs.rs deleted file mode 100644 index 54ca664..0000000 --- a/planner/src/buffer_needs.rs +++ /dev/null @@ -1,31 +0,0 @@ -pub struct BufferNeeds {} - -impl BufferNeeds { - pub fn best_root(available: i32, size: i32) -> i32 { - let available = available - 2; - if available <= 1 { - return 1; - } - let mut k = i32::MAX; - let mut i = 1f32; - while k > available { - i += 1f32; - k = (size as f32).powf(1.0 / i).ceil() as i32; - } - k - } - - pub fn best_factor(availabe: i32, size: i32) -> i32 { - let available = availabe - 2; - if available <= 1 { - return 1; - } - let mut k = size; - let mut i: f32 = 1.0; - while k > available { - i += 1f32; - k = (size as f32 / i).ceil() as i32; - } - k - } -} diff --git a/planner/src/element.rs b/planner/src/element.rs deleted file mode 100644 index 789e730..0000000 --- a/planner/src/element.rs +++ /dev/null @@ -1,47 +0,0 @@ -#[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()) - } -} - -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/planner/src/field_info.rs b/planner/src/field_info.rs deleted file mode 100644 index d23d7ae..0000000 --- a/planner/src/field_info.rs +++ /dev/null @@ -1,74 +0,0 @@ -use common::{DbResult, error::DbError}; -use file::page::I32_SIZE; - -const INTEGER_TYPE: i32 = 1; -const VARCHAR_TYPE: i32 = 2; - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum FieldInfo { - Integer, - Varchar(i32), -} - -impl FieldInfo { - pub fn new(field_type: i32, len: i32) -> DbResult { - let info = match field_type { - INTEGER_TYPE => Self::Integer, - VARCHAR_TYPE => Self::Varchar((len - I32_SIZE as i32) / 4), - _ => return Err(DbError::UnknownType), - }; - Ok(info) - } - - pub fn type_id(&self) -> i32 { - match self { - Self::Integer => INTEGER_TYPE, - Self::Varchar(_) => VARCHAR_TYPE, - } - } - - pub fn length(&self) -> i32 { - match self { - Self::Integer => I32_SIZE as i32, - Self::Varchar(len) => I32_SIZE as i32 + *len * 4, - } - } -} - -impl std::fmt::Display for FieldInfo { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Integer => write!(f, "INTEGER"), - Self::Varchar(len) => write!(f, "VARCHAR({})", len), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn type_id() { - let info = FieldInfo::new(1, 16).unwrap(); - assert_eq!(info.type_id(), INTEGER_TYPE); - - let info = FieldInfo::new(2, 0).unwrap(); - assert_eq!(info.type_id(), VARCHAR_TYPE); - } - - #[test] - #[should_panic] - fn invalid_type() { - FieldInfo::new(123, 32).unwrap(); - } - - #[test] - fn to_string() { - let info = FieldInfo::new(1, 0).unwrap(); - assert_eq!("INTEGER", info.to_string()); - - let info = FieldInfo::new(2, 12).unwrap(); - assert_eq!("VARCHAR(2)", info.to_string()); - } -} diff --git a/planner/src/index.rs b/planner/src/index.rs deleted file mode 100644 index b17f8c2..0000000 --- a/planner/src/index.rs +++ /dev/null @@ -1,58 +0,0 @@ -use std::sync::Arc; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::{ - index::{b_tree::BTreeIndex, indexer::Indexer}, - rid::RID, - value::Value, -}; - -pub(crate) mod b_tree; -mod indexer; - -pub struct Index(Indexer); - -impl Index { - pub fn new(index_name: &str, tx: &Arc) -> DbResult { - let index = BTreeIndex::new(index_name, tx)?; - Ok(Self(Indexer::BTree(index))) - } - - pub fn before_first(&mut self, key: Value) -> DbResult<()> { - match &mut self.0 { - Indexer::BTree(index) => index.before_first(key), - } - } - - pub fn next_row(&mut self) -> DbResult { - match &mut self.0 { - Indexer::BTree(index) => index.next(), - } - } - - pub fn get_data_rid(&self) -> DbResult { - match &self.0 { - Indexer::BTree(index) => index.get_data_rid(), - } - } - - pub fn insert(&self, value: Value, rid: RID) -> DbResult<()> { - match &self.0 { - Indexer::BTree(index) => index.insert(value, rid), - } - } - - pub fn delete(&self, value: Value, rid: RID) -> DbResult<()> { - match &self.0 { - Indexer::BTree(index) => index.delete(value, rid), - } - } - - pub fn close(&self) -> DbResult<()> { - match &self.0 { - Indexer::BTree(index) => index.close(), - } - } -} diff --git a/planner/src/index/b_tree.rs b/planner/src/index/b_tree.rs deleted file mode 100644 index c1c0063..0000000 --- a/planner/src/index/b_tree.rs +++ /dev/null @@ -1,388 +0,0 @@ -use std::sync::Arc; - -use common::{DbResult, error::DbError}; -use file::block::BlockId; -use transaction::transaction::Transaction; - -use crate::{ - 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, - }, - rid::RID, - value::Value, -}; - -pub(crate) mod entry; -pub(crate) mod page; -pub(crate) mod pointer; - -pub(crate) struct BTreeIndex { - index_name: String, - tx: Arc, - position: i32, - rid: Vec, -} - -impl BTreeIndex { - pub(crate) 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), - position: 0, - rid: vec![], - }) - } - - pub(crate) 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(()); - } - } - } - } - - pub(crate) fn next(&mut self) -> DbResult { - self.position += 1; - Ok((self.position as usize) < self.rid.len()) - } - - pub(crate) fn get_data_rid(&self) -> DbResult { - Ok(self.rid[self.position as usize].clone()) - } - - pub(crate) 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)?; - } - } - } - } - Ok(()) - } - - pub(crate) 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(()) - } - - pub(crate) fn close(&self) -> DbResult<()> { - // 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(()) - } -} - -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(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::tests::{init, init_with_size}; - - #[test] - fn insert_1000() { - let (_dir, tx) = init(); - let mut index = BTreeIndex::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 two_leafs_one_node() { - let (_dir, tx) = init(); - let index_name = "test"; - let index = BTreeIndex::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); - - 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 small_page_test() { - let (_dir, tx) = init_with_size(32); - let mut index = BTreeIndex::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/planner/src/layout.rs b/planner/src/layout.rs deleted file mode 100644 index 28cfd7f..0000000 --- a/planner/src/layout.rs +++ /dev/null @@ -1,92 +0,0 @@ -use std::{collections::HashMap, rc::Rc}; - -use file::page::I32_SIZE; - -use crate::{element::Element, schema::Schema}; - -#[derive(Debug, Clone)] -struct LayoutInner { - schema: Schema, - offsets: HashMap, - slotsize: i32, -} - -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() { - offsets.insert(field, pos); - pos += info.length(); - } - Self { - schema, - offsets, - slotsize: pos, - } - } - - fn from(schema: Schema, offsets: HashMap, slotsize: i32) -> Self { - Self { - schema, - offsets, - slotsize, - } - } - - fn offset(&self, fieldname: &Element) -> i32 { - if let Some(offset) = self.offsets.get(fieldname) { - *offset - } else { - 0 - } - } - - fn schema(&self) -> &Schema { - &self.schema - } - - 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/planner/src/lib.rs b/planner/src/lib.rs deleted file mode 100644 index 006df24..0000000 --- a/planner/src/lib.rs +++ /dev/null @@ -1,40 +0,0 @@ -pub mod buffer_needs; -pub mod element; -pub mod field_info; -pub mod index; -pub mod layout; -pub mod mgr; -pub mod plan; -pub mod predicate; -pub mod record_page; -pub mod rid; -pub mod scan; -pub mod schema; -pub mod temp; -pub mod value; - -#[cfg(test)] -mod tests { - use std::sync::Arc; - - use buffer::mgr::BufferMgr; - use file::mgr::FileMgr; - use log::mgr::LogMgr; - use tempfile::{TempDir, tempdir}; - use transaction::{lock_table::LockTable, transaction::Transaction}; - - 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) - } -} diff --git a/planner/src/mgr.rs b/planner/src/mgr.rs deleted file mode 100644 index b8c12e1..0000000 --- a/planner/src/mgr.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod index; -pub mod metadata; -pub mod stat; -pub mod table; -pub mod view; - diff --git a/planner/src/mgr/index.rs b/planner/src/mgr/index.rs deleted file mode 100644 index 0351516..0000000 --- a/planner/src/mgr/index.rs +++ /dev/null @@ -1,143 +0,0 @@ -use std::{collections::HashMap, sync::Arc}; - -use common::DbResult; -use common::error::DbError; -use transaction::transaction::Transaction; - -use crate::element::Element; -use crate::index::Index; -use crate::layout::Layout; -use crate::mgr::stat::{StatInfo, StatMgr}; -use crate::mgr::table::TableMgr; -use crate::scan::Scan; -use crate::schema::{Schema, SchemaBuilder}; - -const IDX_TABLE: &str = "idx"; -const IDX_NAME: &str = "idx_name"; -const TABLE_NAME: &str = "table_name"; -const FIELD_NAME: &str = "field_name"; -const MAX_LENGTH: i32 = 16; - -#[derive(Clone)] -pub struct IndexInfo { - idx_name: String, - field_name: String, - tx: Arc, - slot_size: i32, - stat: StatInfo, -} - -impl IndexInfo { - pub fn new( - idx_name: String, - field_name: String, - schema: &Schema, - tx: &Arc, - stat: StatInfo, - ) -> DbResult { - let Some(info) = schema.info(&Element::raw(&field_name)) else { - return Err(DbError::FieldNotExists(field_name)); - }; - Ok(Self { - idx_name, - slot_size: info.length(), - field_name, - tx: Arc::clone(tx), - stat, - }) - } - - pub fn open(&self) -> DbResult { - Index::new(&self.idx_name, &self.tx) - } - - pub fn block_accessed(&self) -> DbResult { - let rpb = self.tx.block_size() / self.slot_size; - let num_blocks = self.stat.records_output() / rpb; - Ok(num_blocks) - } - - pub fn records_output(&self) -> i32 { - self.stat.records_output() / self.stat.distinct_values() - } - - pub fn distinct_values(&self, field_name: &str) -> i32 { - if self.field_name == field_name { - 1 - } else { - self.stat.distinct_values() - } - } -} - -pub struct IndexMgr { - layout: Layout, - table_mgr: TableMgr, - stat_mgr: StatMgr, -} - -impl IndexMgr { - pub fn new( - is_new: bool, - table_mgr: TableMgr, - stat_mgr: StatMgr, - tx: &Arc, - ) -> DbResult { - if is_new { - 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 = 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, - stat_mgr, - }) - } - - pub fn create_index( - &self, - idx_name: &str, - table_name: &str, - field_name: &str, - tx: &Arc, - ) -> DbResult<()> { - let mut ts = Scan::table(tx, IDX_TABLE, self.layout.clone())?; - ts.insert()?; - 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() - } - - pub fn get_index_info( - &self, - table_name: &str, - tx: &Arc, - ) -> DbResult> { - let mut result = HashMap::new(); - let mut ts = Scan::table(tx, IDX_TABLE, self.layout.clone())?; - while ts.next_row()? { - 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, field_name.clone(), layout.schema(), tx, stat)?; - result.insert(Element::Raw(field_name), index); - } - } - ts.close()?; - Ok(result) - } -} diff --git a/planner/src/mgr/metadata.rs b/planner/src/mgr/metadata.rs deleted file mode 100644 index c7bacc0..0000000 --- a/planner/src/mgr/metadata.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::{collections::HashMap, sync::Arc}; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::{ - layout::Layout, - mgr::{ - index::{IndexInfo, IndexMgr}, - stat::{StatInfo, StatMgr}, - table::TableMgr, - view::ViewMgr, - }, - schema::Schema, -}; -use crate::element::Element; - -pub struct MetadataMgr { - 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 = 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, - stat_mgr, - index_mgr, - }) - } - - pub fn create_table( - &self, - table_name: &str, - schema: Schema, - tx: &Arc, - ) -> DbResult<()> { - self.table_mgr.create_table(table_name, schema, tx) - } - - pub fn get_layout(&self, table_name: &str, tx: &Arc) -> DbResult { - self.table_mgr.get_layout(table_name, tx) - } - - pub fn create_view( - &self, - view_name: &str, - view_def: &str, - tx: &Arc, - ) -> DbResult<()> { - self.view_mgr.create_view(view_name, view_def, tx) - } - - pub fn get_view_def(&self, view_name: &str, tx: &Arc) -> DbResult> { - self.view_mgr.get_view_def(view_name, tx) - } - - pub fn create_index( - &self, - idx_name: &str, - table_name: &str, - field_name: &str, - tx: &Arc, - ) -> DbResult<()> { - self.index_mgr - .create_index(idx_name, table_name, field_name, tx) - } - - pub fn get_stat_info( - &self, - table: &str, - layout: Layout, - tx: &Arc, - ) -> DbResult { - self.stat_mgr.get_stat_info(table, layout, tx) - } - - pub fn get_index_info( - &self, - table_name: &str, - tx: &Arc, - ) -> DbResult> { - self.index_mgr.get_index_info(table_name, tx) - } -} - -#[cfg(test)] -mod tests { - #[test] - fn metadata() {} -} diff --git a/planner/src/mgr/stat.rs b/planner/src/mgr/stat.rs deleted file mode 100644 index 3285ea5..0000000 --- a/planner/src/mgr/stat.rs +++ /dev/null @@ -1,134 +0,0 @@ -use std::{ - collections::HashMap, - rc::Rc, - sync::{Arc, RwLock}, -}; - -use common::{DbResult, error::DbError}; -use transaction::transaction::Transaction; - -use crate::{ - element::Element, - layout::Layout, - mgr::table::{TABLE_NAME, TableMgr}, - scan::Scan, -}; - -#[derive(Clone, Debug)] -pub struct StatInfo { - num_blocks: i32, - num_recs: u16, -} - -impl StatInfo { - pub fn new(num_blocks: i32, num_recs: u16) -> Self { - Self { - num_blocks, - num_recs, - } - } - - pub fn block_accessed(&self) -> i32 { - self.num_blocks - } - - pub fn records_output(&self) -> i32 { - self.num_recs as i32 - } - - pub fn distinct_values(&self) -> i32 { - 1 + (self.num_recs / 3) as i32 - } -} - -struct StatMgrLock { - table_mgr: TableMgr, - table_stats: HashMap, - num_calls: u64, -} - -impl StatMgrLock { - fn new(table_mgr: TableMgr) -> Self { - Self { - table_mgr, - table_stats: HashMap::new(), - num_calls: 0, - } - } - - fn get_stat_info( - &mut self, - table_name: &str, - layout: Layout, - tx: &Arc, - ) -> DbResult { - self.num_calls += 1; - if self.num_calls > 100 { - self.refresh_statisic(tx)?; - } - if let Some(stat) = self.table_stats.get(table_name).cloned() { - Ok(stat) - } else { - let stat = self.calc_table_stats(table_name, layout, tx)?; - self.table_stats - .insert(table_name.to_string(), stat.clone()); - Ok(stat) - } - } - - fn refresh_statisic(&mut self, tx: &Arc) -> DbResult<()> { - self.table_stats.clear(); - self.num_calls = 0; - let layout = self.table_mgr.get_layout(TABLE_NAME, tx)?; - let mut ts = Scan::table(tx, TABLE_NAME, layout)?; - while ts.next_row()? { - 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, layout, tx)?; - self.table_stats.insert(table_name, stat); - } - ts.close() - } - - fn calc_table_stats( - &self, - table_name: &str, - layout: Layout, - tx: &Arc, - ) -> DbResult { - let mut num_recs = 0; - let mut num_blocks = 0; - let mut ts = Scan::table(tx, table_name, layout)?; - while ts.next_row()? { - num_recs += 1; - num_blocks = ts.get_rid()?.block_num() + 1; - } - ts.close()?; - Ok(StatInfo::new(num_blocks, num_recs)) - } -} - -#[derive(Clone)] -pub struct StatMgr { - lock: Rc>, -} - -impl StatMgr { - pub fn new(table_mgr: TableMgr, tx: &Arc) -> DbResult { - let mut lock = StatMgrLock::new(table_mgr); - lock.refresh_statisic(tx)?; - Ok(Self { - lock: Rc::new(RwLock::new(lock)), - }) - } - - pub fn get_stat_info( - &self, - table_name: &str, - layout: Layout, - tx: &Arc, - ) -> DbResult { - let mut write = self.lock.write().map_err(DbError::lock)?; - write.get_stat_info(table_name, layout, tx) - } -} diff --git a/planner/src/mgr/table.rs b/planner/src/mgr/table.rs deleted file mode 100644 index d3c0197..0000000 --- a/planner/src/mgr/table.rs +++ /dev/null @@ -1,150 +0,0 @@ -use std::{collections::HashMap, sync::Arc}; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::{ - element::Element, - field_info::FieldInfo, - layout::Layout, - scan::Scan, - schema::{Schema, SchemaBuilder}, -}; - -const MAX_NAME: i32 = 16; - -pub(crate) const TABLE_NAME: &str = "sp_table"; -const TABLE_SLOT_SIZE: &str = "slot_size"; -const FIELDS_NAME: &str = "sp_fields"; - -const F_TYPE: &str = "type"; -const F_FIELD_NAME: &str = "field"; -const F_TYPE_LENGTH: &str = "length"; -const F_OFFSET: &str = "offset"; - -#[derive(Clone, Debug)] -pub struct TableMgr { - table_layout: Layout, - fields_layout: Layout, -} - -impl TableMgr { - pub fn new(is_new: bool, tx: &Arc) -> DbResult { - 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_layout, - fields_layout, - }; - - if is_new { - mgr.create_table(TABLE_NAME, table_schema, tx)?; - mgr.create_table(FIELDS_NAME, fields_schema, tx)?; - } - - Ok(mgr) - } - - pub fn create_table( - &self, - table_name: &str, - schema: Schema, - tx: &Arc, - ) -> DbResult<()> { - let layout = Layout::new(schema.clone()); - - let mut tcat = Scan::table(tx, TABLE_NAME, self.table_layout.clone())?; - tcat.insert()?; - tcat.set_string(&Element::raw(TABLE_NAME), table_name)?; - tcat.set_i32(&Element::raw(TABLE_SLOT_SIZE), layout.slotsize())?; - tcat.close()?; - - let mut fcat = Scan::table(tx, FIELDS_NAME, self.fields_layout.clone())?; - for (field_name, info) in schema.fields() { - fcat.insert()?; - 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 mut tcat = Scan::table(tx, TABLE_NAME, self.table_layout.clone())?; - while tcat.next_row()? { - if tcat.get_string(&Element::raw(TABLE_NAME))? == table_name { - size = tcat.get_i32(&Element::raw(TABLE_SLOT_SIZE))?; - break; - } - } - tcat.close()?; - let mut schema = SchemaBuilder::default(); - let mut offsets = HashMap::new(); - let mut fcat = Scan::table(tx, FIELDS_NAME, self.fields_layout.clone())?; - while fcat.next_row()? { - let table = fcat.get_string(&Element::raw(TABLE_NAME))?; - if table == table_name { - 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)) - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashSet; - - use crate::tests::init; - - use super::*; - - #[test] - fn manager() { - let (_dir, tx) = init(); - - let tm = TableMgr::new(true, &tx).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(); - - let layout = tm.get_layout("MyTable", &tx).unwrap(); - let mut expected_fields = HashSet::new(); - 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/planner/src/mgr/view.rs b/planner/src/mgr/view.rs deleted file mode 100644 index 6b69601..0000000 --- a/planner/src/mgr/view.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::sync::Arc; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::{element::Element, mgr::table::TableMgr, scan::Scan, schema::SchemaBuilder}; - -const MAX_NAME: i32 = 16; -const MAX_VIEW_DEF: i32 = 100; - -const VIEW_NAME: &str = "view"; -const VIEW_DEF: &str = "view_def"; -const VIEW_TABLE: &str = "sp_view"; - -pub struct ViewMgr { - table_mgr: TableMgr, -} - -impl ViewMgr { - pub fn new(is_new: bool, table_mgr: TableMgr, tx: &Arc) -> DbResult { - if is_new { - 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 }) - } - - pub fn create_view(&self, name: &str, def: &str, tx: &Arc) -> DbResult<()> { - let layout = self.table_mgr.get_layout(VIEW_TABLE, tx)?; - let mut ts = Scan::table(tx, VIEW_TABLE, layout)?; - ts.insert()?; - 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 mut ts = Scan::table(tx, VIEW_TABLE, layout)?; - let mut result = None; - while ts.next_row()? { - if ts.get_string(&Element::raw(VIEW_NAME))? == name { - result = Some(ts.get_string(&Element::raw(VIEW_DEF))?); - break; - } - } - ts.close()?; - Ok(result) - } -} diff --git a/planner/src/plan.rs b/planner/src/plan.rs deleted file mode 100644 index c28f51f..0000000 --- a/planner/src/plan.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::sync::Arc; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::{ - element::Element, - mgr::metadata::MetadataMgr, - plan::{select::SelectPlan, table::TablePlan}, - predicate::Predicate, - scan::Scan, - schema::Schema, -}; - -pub(crate) mod select; -pub(crate) mod table; - -pub(crate) enum Planner { - Table(TablePlan), - Select(SelectPlan), -} - -pub struct Plan(Planner); - -impl Plan { - pub fn table(tx: &Arc, table: String, md: &MetadataMgr) -> DbResult { - Ok(Self(Planner::Table(TablePlan::new(tx, table, md)?))) - } - - pub fn select(plan: Box, predicate: Predicate) -> Self { - Self(Planner::Select(SelectPlan::new(plan, predicate))) - } - - pub fn open(&self) -> DbResult { - match &self.0 { - Planner::Table(table) => table.open(), - Planner::Select(table) => table.open(), - } - } - - pub fn blocks_accessed(&self) -> DbResult { - match &self.0 { - Planner::Table(table) => table.blocks_accessed(), - Planner::Select(table) => table.blocks_accessed(), - } - } - - pub fn records_output(&self) -> DbResult { - match &self.0 { - Planner::Table(table) => table.records_output(), - Planner::Select(table) => table.records_output(), - } - } - - pub fn distinct_values(&self, field_name: &Element) -> DbResult { - match &self.0 { - Planner::Table(table) => table.distinct_values(field_name), - Planner::Select(table) => table.distinct_values(field_name), - } - } - - pub fn schema(&self) -> DbResult { - match &self.0 { - Planner::Table(table) => Ok(table.schema()), - Planner::Select(table) => table.schema(), - } - } -} diff --git a/planner/src/plan/select.rs b/planner/src/plan/select.rs deleted file mode 100644 index 6d6bfd1..0000000 --- a/planner/src/plan/select.rs +++ /dev/null @@ -1,105 +0,0 @@ -use common::DbResult; - -use crate::{element::Element, plan::Plan, predicate::Predicate, scan::Scan, schema::Schema}; - -pub(crate) struct SelectPlan { - plan: Box, - predicate: Predicate, -} - -impl SelectPlan { - pub(crate) fn new(plan: Box, predicate: Predicate) -> Self { - Self { plan, predicate } - } - - pub(crate) fn open(&self) -> DbResult { - let s = self.plan.open()?; - Ok(Scan::select(Box::new(s), self.predicate.clone())) - } - - pub(crate) fn blocks_accessed(&self) -> DbResult { - self.plan.blocks_accessed() - } - - pub(crate) fn records_output(&self) -> DbResult { - Ok(self.plan.records_output()? / self.predicate.reduction_factor(&self.plan)?) - } - - pub(crate) fn distinct_values(&self, field_name: &Element) -> common::DbResult { - if self.predicate.equates_with_constant(field_name)?.is_some() { - return Ok(1); - } else { - if let Some(second_field) = self.predicate.equates_with_field(field_name)? { - return Ok(self - .plan - .distinct_values(field_name)? - .min(self.plan.distinct_values(&second_field)?)); - } - } - self.plan.distinct_values(field_name) - } - - pub(crate) fn schema(&self) -> DbResult { - self.plan.schema() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::schema::SchemaBuilder; - use crate::{ - mgr::metadata::MetadataMgr, - predicate::{Expression, Term}, - tests::init, - value::Value, - }; - - #[test] - fn select() { - let (_dir, tx) = init(); - let md = MetadataMgr::new(true, &tx).unwrap(); - let schema = SchemaBuilder::default() - .add_int_field(Element::raw("id")) - .build(); - md.create_table("test", schema.clone(), &tx).unwrap(); - tx.commit().unwrap(); - - let table = Plan::table(&tx, "test".to_string(), &md).unwrap(); - - let field = Element::raw("id"); - let value = Value::Integer(1); - - let predicate = predicate(field.clone(), value.clone()); - - let select = Plan::select(Box::new(table), predicate); - - let mut select_scan = select.open().unwrap(); - - for _ in 0..10 { - for i in 0..10 { - select_scan.insert().unwrap(); - select_scan.set_i32(&field, i).unwrap(); - } - } - - assert_eq!(0, select.blocks_accessed().unwrap()); - assert_eq!(0, select.records_output().unwrap()); - assert_eq!(1, select.distinct_values(&field).unwrap()); - assert_eq!(schema, select.schema().unwrap()); - - select_scan.before_first().unwrap(); - while select_scan.next_row().unwrap() { - let found = select_scan.get_val(&field).unwrap(); - assert_eq!(value, found); - } - } - - fn predicate(field: Element, value: Value) -> Predicate { - let expression1 = Expression::Field(field.clone()); - let expression2 = Expression::Value(value.clone()); - - let term = Term::new(expression1, expression2); - Predicate::new(term) - } -} diff --git a/planner/src/plan/table.rs b/planner/src/plan/table.rs deleted file mode 100644 index 33cebe6..0000000 --- a/planner/src/plan/table.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::sync::Arc; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::{ - element::Element, - layout::Layout, - mgr::{metadata::MetadataMgr, stat::StatInfo}, - scan::Scan, - schema::Schema, -}; - -pub(crate) struct TablePlan { - tx: Arc, - table: String, - layout: Layout, - stat: StatInfo, -} - -impl TablePlan { - pub(crate) fn new(tx: &Arc, table: String, md: &MetadataMgr) -> DbResult { - let layout = md.get_layout(&table, tx)?; - let stat = md.get_stat_info(&table, layout.clone(), tx)?; - Ok(Self { - tx: Arc::clone(tx), - table, - layout, - stat, - }) - } - - pub(crate) fn open(&self) -> DbResult { - Scan::table(&self.tx, &self.table, self.layout.clone()) - } - - pub(crate) fn blocks_accessed(&self) -> DbResult { - Ok(self.stat.block_accessed()) - } - - pub(crate) fn records_output(&self) -> DbResult { - Ok(self.stat.records_output()) - } - - pub(crate) fn distinct_values(&self, _: &Element) -> DbResult { - Ok(self.stat.distinct_values()) - } - - pub(crate) fn schema(&self) -> Schema { - self.layout.schema().clone() - } -} diff --git a/planner/src/predicate.rs b/planner/src/predicate.rs deleted file mode 100644 index 5aa6d7e..0000000 --- a/planner/src/predicate.rs +++ /dev/null @@ -1,248 +0,0 @@ -use std::{ - fmt::Debug, - sync::{Arc, RwLock}, -}; - -use common::{DbResult, error::DbError}; - -use crate::{ - element::Element, - plan::Plan, - scan::Scan, - schema::{Schema, SchemaBuilder}, - value::Value, -}; - -#[derive(Debug, Clone)] -pub enum Expression { - Value(Value), - Field(Element), -} - -impl Expression { - pub fn evaluate(&self, scan: &Scan) -> DbResult { - match self { - Self::Value(value) => Ok(value.clone()), - Self::Field(field) => scan.get_val(field), - } - } - - pub fn applies_to(&self, schema: &Schema) -> bool { - match self { - Self::Value(_) => true, - Self::Field(field) => schema.has_field(field), - } - } - - pub fn as_field_name(&self) -> Option<&Element> { - match self { - Self::Field(field) => Some(field), - _ => None, - } - } - - pub fn as_constant(&self) -> Option<&Value> { - match self { - Self::Value(value) => Some(value), - _ => None, - } - } -} - -impl std::fmt::Display for Expression { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Value(value) => write!(f, "{}", value), - Self::Field(field) => write!(f, "{}", field), - } - } -} - -#[derive(Clone, Debug)] -pub struct Term { - left: Expression, - right: Expression, -} - -impl Term { - pub fn new(left: Expression, right: Expression) -> Self { - Self { left, right } - } - - pub fn is_satisfied(&self, s: &Scan) -> DbResult { - let left = self.left.evaluate(s)?; - let right = self.right.evaluate(s)?; - Ok(left == right) - } - - pub fn applies_to(&self, schema: &Schema) -> bool { - self.left.applies_to(schema) && self.right.applies_to(schema) - } - - pub fn reduction_factor(&self, p: &Plan) -> DbResult { - if let Some(left) = self.left.as_field_name() - && let Some(right) = self.right.as_field_name() - { - return Ok(p.distinct_values(left)?.max(p.distinct_values(right)?)); - } - if let Some(left) = self.left.as_field_name() { - return p.distinct_values(left); - } - if let Some(right) = self.right.as_field_name() { - return p.distinct_values(right); - } - if let Some(left) = self.left.as_constant() - && let Some(right) = self.right.as_constant() - && left == right - { - Ok(1) - } else { - Ok(i32::MAX) - } - } - - 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() - { - Ok(Some(value.clone())) - } else if let Some(right) = self.right.as_field_name() - && right == field_name - && let Some(value) = self.left.as_constant() - { - Ok(Some(value.clone())) - } else { - Ok(None) - } - } - - 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.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.clone())) - } else { - Ok(None) - } - } -} - -impl std::fmt::Display for Term { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}={}", self.left, self.right) - } -} - -#[derive(Default, Clone)] -pub struct Predicate { - terms: Arc>>, -} - -impl Predicate { - pub fn new(term: Term) -> Self { - Self { - terms: Arc::new(RwLock::new(vec![term])), - } - } - - pub fn conjoin_with(&self, p: &Self) -> DbResult<()> { - let mut write = self.terms.write().map_err(DbError::lock)?; - let read = p.terms.read().map_err(DbError::lock)?; - for term in read.iter() { - write.push(term.clone()); - } - Ok(()) - } - - pub fn is_satisfied(&self, s: &Scan) -> DbResult { - let read = self.terms.read().map_err(DbError::lock)?; - for t in read.iter() { - if !t.is_satisfied(s)? { - return Ok(false); - } - } - Ok(true) - } - - pub fn reduction_factor(&self, p: &Plan) -> DbResult { - let mut factor = 1; - let read = self.terms.read().map_err(DbError::lock)?; - for t in read.iter() { - factor *= t.reduction_factor(p)?; - } - Ok(factor) - } - - pub fn select_sub_pred(&self, schema: &Schema) -> DbResult { - let result = Predicate::default(); - 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(schema) { - terms.push(t.clone()); - } - } - } - Ok(result) - } - - pub fn join_sub_pred(&self, s1: &Schema, s2: &Schema) -> DbResult> { - let result = Predicate::default(); - 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) { - terms.push(t.clone()); - } - } - if terms.is_empty() { - return Ok(None); - } - } - Ok(Some(result)) - } - - 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)? { - return Ok(Some(c)); - } - } - Ok(None) - } - - 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)? { - return Ok(Some(field)); - } - } - Ok(None) - } -} - -impl std::fmt::Display for Predicate { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let terms = self.terms.read().map_err(|_| std::fmt::Error)?; - for (i, t) in terms.iter().enumerate() { - if i == 0 { - write!(f, "{}", t)?; - } else { - write!(f, " AND {}", t)?; - } - } - Ok(()) - } -} diff --git a/planner/src/record_page.rs b/planner/src/record_page.rs deleted file mode 100644 index 2b02d70..0000000 --- a/planner/src/record_page.rs +++ /dev/null @@ -1,199 +0,0 @@ -use std::sync::Arc; - -use common::DbResult; -use file::block::BlockId; -use transaction::transaction::Transaction; - -use crate::{element::Element, field_info::FieldInfo, layout::Layout}; - -const EMPTY: u8 = 0; -const USED: u8 = 1; - -#[derive(Clone)] -pub struct RecordPage { - tx: Arc, - block: BlockId, - layout: Layout, -} - -impl RecordPage { - pub fn new(tx: &Arc, block: BlockId, layout: Layout) -> DbResult { - tx.pin(&block)?; - Ok(Self { - tx: Arc::clone(tx), - block, - layout, - }) - } - - 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, 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: &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: &Element, value: &str) -> DbResult<()> { - let pos = self.offset(slot) + self.layout.offset(field); - self.tx.set_string(&self.block, pos as usize, value, true) - } - - pub fn delete(&self, slot: i32) -> DbResult<()> { - self.set_flag(slot, EMPTY) - } - - pub fn format(&self) -> DbResult<()> { - let mut slot = 0; - while self.is_valid_slot(slot) { - self.tx - .set_u8(&self.block, self.offset(slot) as usize, EMPTY, false)?; - let schema = self.layout.schema(); - 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)?, - FieldInfo::Varchar(_) => { - self.tx.set_string(&self.block, pos as usize, "", false)? - } - } - } - slot += 1; - } - Ok(()) - } - - pub fn next_after(&self, slot: i32) -> DbResult { - self.search_after(slot, USED) - } - - pub fn insert_after(&self, slot: i32) -> DbResult { - let newslot = self.search_after(slot, EMPTY)?; - if newslot >= 0 { - self.set_flag(newslot, USED)?; - } - Ok(newslot) - } - - pub fn block(&self) -> BlockId { - self.block.clone() - } - - fn set_flag(&self, slot: i32, flag: u8) -> DbResult<()> { - let offset = self.offset(slot) as usize; - self.tx.set_u8(&self.block, offset, flag, true)?; - Ok(()) - } - - fn search_after(&self, mut slot: i32, flag: u8) -> DbResult { - slot += 1; - while self.is_valid_slot(slot) { - if self.tx.get_u8(&self.block, self.offset(slot) as usize)? == flag { - return Ok(slot); - } - slot += 1; - } - Ok(-1) - } - - fn is_valid_slot(&self, slot: i32) -> bool { - let offset = self.offset(slot + 1); - let block_size = self.tx.block_size(); - offset <= block_size - } - - fn offset(&self, slot: i32) -> i32 { - slot * self.layout.slotsize() - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashSet; - - use buffer::mgr::BufferMgr; - use file::mgr::FileMgr; - use log::mgr::LogMgr; - use rand::RngExt; - use tempfile::tempdir; - use transaction::lock_table::LockTable; - - use crate::schema::SchemaBuilder; - - use super::*; - - #[test] - fn record() { - let dir = tempdir().unwrap(); - 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 lock_table = Arc::new(LockTable::default()); - - let tx = Transaction::new(&fm, &lm, &bm, &lock_table).unwrap(); - let tx = Arc::new(tx); - - 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(); - record_page.format().unwrap(); - - let mut rng = rand::rng(); - - let mut values_less = HashSet::new(); - let mut values_greater = HashSet::new(); - - let mut slot = record_page.insert_after(-1).unwrap(); - while slot >= 0 { - let n = rng.random::(); - if n < 25 { - values_less.insert((n, format!("rec{}", n))); - } else { - values_greater.insert((n, format!("rec{}", n))); - } - record_page.set_i32(slot, &a, n).unwrap(); - record_page - .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(); - if a < 25 { - assert!(values_less.contains(&(a, b))); - record_page.delete(slot).unwrap(); - } - slot = record_page.next_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(); - assert!(values_greater.contains(&(a, b))); - slot = record_page.next_after(slot).unwrap(); - } - tx.unpin(&block).unwrap(); - tx.commit().unwrap(); - } -} diff --git a/planner/src/rid.rs b/planner/src/rid.rs deleted file mode 100644 index a71db62..0000000 --- a/planner/src/rid.rs +++ /dev/null @@ -1,33 +0,0 @@ -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct RID(i32, i32); - -impl RID { - pub fn new(block_num: i32, slot: i32) -> Self { - Self(block_num, slot) - } - - pub fn block_num(&self) -> i32 { - self.0 - } - - pub fn slot(&self) -> i32 { - self.1 - } -} - -impl std::fmt::Display for RID { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "[{}, {}]", self.0, self.1) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn to_string() { - let rid = RID::new(0, 0); - assert_eq!("[0, 0]", rid.to_string()); - } -} diff --git a/planner/src/scan.rs b/planner/src/scan.rs deleted file mode 100644 index 7beb4a4..0000000 --- a/planner/src/scan.rs +++ /dev/null @@ -1,189 +0,0 @@ -use std::sync::Arc; - -use common::DbResult; -use transaction::transaction::Transaction; - -use crate::scan::chunk::ChunkScan; -use crate::{ - element::Element, - layout::Layout, - predicate::Predicate, - rid::RID, - scan::{scanner::Scanner, select::SelectScan, table::TableScan}, - schema::Schema, - value::Value, -}; -use crate::scan::product::ProductScan; - -pub(crate) mod chunk; -pub(crate) mod group; -pub(crate) mod index; -pub(crate) mod merge; -pub(crate) mod multibuffer; -pub(crate) mod order; -pub(crate) mod product; -pub(crate) mod project; -pub(crate) mod scanner; -pub(crate) mod select; -pub(crate) mod table; - -pub struct Scan { - scan: Scanner, -} - -impl Scan { - pub fn table(tx: &Arc, table_name: &str, layout: Layout) -> DbResult { - let table = TableScan::new(tx, table_name, layout)?; - Ok(Self { - scan: Scanner::Table(table), - }) - } - - pub fn select(scan: Box, predicate: Predicate) -> Self { - let select = SelectScan::new(scan, predicate); - Self { - scan: Scanner::Select(select), - } - } - - pub fn product(s1: Box, s2: Box) -> DbResult { - let product = ProductScan::new(s1, s2)?; - Ok(Self { - scan: Scanner::Product(product), - }) - } - - pub fn chunk( - tx: &Arc, - filename: &str, - layout: Layout, - start_b_num: i32, - end_b_num: i32, - ) -> DbResult { - let select = ChunkScan::new(tx, filename, layout, start_b_num, end_b_num)?; - Ok(Self { - scan: Scanner::Chunk(select), - }) - } - - pub fn before_first(&mut self) -> DbResult<()> { - self.scan.before_first() - } - - pub fn next_row(&mut self) -> DbResult { - self.scan.next() - } - - pub fn get_i32(&self, field: &Element) -> DbResult { - self.scan.get_i32(field) - } - - pub fn get_string(&self, field: &Element) -> DbResult { - self.scan.get_string(field) - } - - pub fn get_val(&self, field: &Element) -> DbResult { - self.scan.get_val(field) - } - - pub fn has_field(&self, field: &Element) -> DbResult { - self.scan.has_field(field) - } - - pub fn close(&self) -> DbResult<()> { - self.scan.close() - } - - pub fn schema(&self) -> DbResult { - self.scan.schema() - } - - pub fn set_i32(&self, field: &Element, value: i32) -> DbResult<()> { - self.scan.set_i32(field, value) - } - - pub fn set_string(&self, field: &Element, value: &str) -> DbResult<()> { - self.scan.set_string(field, value) - } - - pub fn set_val(&self, field: &Element, value: Value) -> DbResult<()> { - self.scan.set_val(field, value) - } - - pub fn insert(&mut self) -> DbResult<()> { - self.scan.insert() - } - - pub fn delete(&self) -> DbResult<()> { - self.scan.delete() - } - - pub fn get_rid(&self) -> DbResult { - self.scan.get_rid() - } - - pub fn move_to_rid(&mut self, rid: RID) -> DbResult<()> { - self.scan.move_to_rid(rid) - } - - pub fn save_position(&self) -> DbResult<()> { - self.scan.save_position() - } -} - -#[cfg(test)] -mod tests { - use crate::{schema::SchemaBuilder, tests::init}; - - use super::*; - - #[test] - fn scan_table() { - let (_dir, tx) = init(); - let schema = SchemaBuilder::default() - .add_int_field(Element::raw("id")) - .add_string_field(Element::raw("name"), 16) - .build(); - let layout = Layout::new(schema); - let mut scan = Scan::table(&tx, "test", layout).unwrap(); - - scan.before_first().unwrap(); - assert!(!scan.next_row().unwrap()); - - scan.insert().unwrap(); - scan.set_i32(&Element::raw("id"), 1).unwrap(); - scan.set_string(&Element::raw("name"), "hello").unwrap(); - - scan.before_first().unwrap(); - assert!(scan.next_row().unwrap()); - - assert_eq!(1, scan.get_i32(&Element::raw("id")).unwrap()); - assert_eq!("hello", scan.get_string(&Element::raw("name")).unwrap()); - - scan.set_val(&Element::raw("id"), Value::Integer(10)) - .unwrap(); - assert_eq!( - Value::Integer(10), - scan.get_val(&Element::raw("id")).unwrap() - ); - - assert!(!scan.has_field(&Element::raw("f")).unwrap()); - scan.schema().unwrap(); - - let rid = scan.get_rid().unwrap(); - assert_eq!(rid.slot(), 0); - assert_eq!(rid.block_num(), 0); - - scan.move_to_rid(RID::new(0, 0)).unwrap(); - scan.before_first().unwrap(); - scan.next_row().unwrap(); - scan.delete().unwrap(); - if let Err(e) = scan.save_position() { - println!("{}", e); - } else { - panic!("save position for table scan not implemented"); - } - - scan.close().unwrap(); - } -} diff --git a/planner/src/scan/chunk.rs b/planner/src/scan/chunk.rs deleted file mode 100644 index f837b9d..0000000 --- a/planner/src/scan/chunk.rs +++ /dev/null @@ -1,116 +0,0 @@ -use std::sync::Arc; - -use common::{DbResult, error::DbError}; -use file::block::BlockId; -use transaction::transaction::Transaction; - -use crate::element::Element; -use crate::schema::Schema; -use crate::value::Value; -use crate::{field_info::FieldInfo, layout::Layout, record_page::RecordPage}; - -pub(crate) struct ChunkScan { - buffers: Vec, - tx: Arc, - filename: String, - layout: Layout, - start_b_num: i32, - end_b_num: i32, - current_b_num: i32, - rp: i32, - current_slot: i32, -} - -impl ChunkScan { - pub(crate) fn new( - tx: &Arc, - filename: &str, - 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.clone())?); - } - let chunk = Self { - buffers, - tx: Arc::clone(tx), - filename: filename.to_string(), - start_b_num, - end_b_num, - current_b_num: start_b_num, - layout, - current_slot: -1, - rp: 0, - }; - Ok(chunk) - } - - pub(crate) fn move_to_block(&mut self, block_num: i32) { - self.current_b_num = block_num; - self.rp = self.current_b_num - self.start_b_num; - self.current_slot = -1; - } - - pub(crate) fn close(&self) -> DbResult<()> { - for i in 0..self.buffers.len() { - let block = BlockId::new(&self.filename, self.start_b_num + i as i32); - self.tx.unpin(&block)?; - } - Ok(()) - } - - pub(crate) fn before_first(&mut self) { - self.move_to_block(self.start_b_num); - } - - pub(crate) fn next(&mut self) -> DbResult { - loop { - let Some(rp) = self.buffers.get(self.rp as usize).cloned() else { - return Err(DbError::other("cannot get record page for chunk")); - }; - self.current_slot = rp.next_after(self.current_slot)?; - if self.current_slot >= 0 { - return Ok(true); - } - if self.current_b_num == self.end_b_num { - return Ok(false); - } - self.move_to_block(rp.block().num + 1); - } - } - - pub(crate) 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 { - Err(DbError::other("cannot get buffer chunk")) - } - } - - pub(crate) 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 { - Err(DbError::other("cannot get buffer chunk")) - } - } - - pub(crate) 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())), - } - } - - pub(crate) fn has_field(&self, field_name: &Element) -> bool { - self.layout.schema().has_field(field_name) - } - - pub(crate) fn schema(&self) -> Schema { - self.layout.schema().clone() - } -} diff --git a/planner/src/scan/group.rs b/planner/src/scan/group.rs deleted file mode 100644 index f611129..0000000 --- a/planner/src/scan/group.rs +++ /dev/null @@ -1,167 +0,0 @@ -use std::{cmp::Ordering, collections::HashMap}; - -use common::{DbResult, error::DbError}; - -use crate::element::Element; -use crate::value::Value; -use crate::{scan::Scan, schema::Schema}; - -#[allow(dead_code)] -#[derive(Clone)] -pub enum AggregationFn { - MaxFn { field: Element, value: Value }, -} - -impl AggregationFn { - pub fn process_first(&mut self, scan: &Scan) -> DbResult<()> { - match self { - Self::MaxFn { field, value } => { - *value = scan.get_val(field)?; - } - } - Ok(()) - } - - pub fn process_next(&mut self, scan: &Scan) -> DbResult<()> { - match self { - Self::MaxFn { field, value } => { - let new_value = scan.get_val(field)?; - if new_value.cmp(value) == Ordering::Greater { - *value = new_value; - } - } - } - Ok(()) - } - - pub fn field_name(&self) -> &Element { - match self { - Self::MaxFn { field, .. } => field, - } - } - - pub fn value(&self) -> Value { - match self { - Self::MaxFn { value, .. } => value.clone(), - } - } -} - -#[derive(PartialEq, Eq)] -struct GroupValue { - values: HashMap, -} - -impl GroupValue { - fn new(scan: &Scan, fields: Vec) -> DbResult { - let mut values = HashMap::new(); - for field in fields { - let value = scan.get_val(&field)?; - values.insert(field, value); - } - Ok(Self { values }) - } - - fn get_val(&self, field: &Element) -> DbResult { - match self.values.get(field) { - Some(value) => Ok(value.clone()), - _ => Err(DbError::FieldNotExists(field.to_string())), - } - } -} - -struct GroupByScan { - scan: Box, - group_fields: Vec, - agg_fns: Vec, - group_val: GroupValue, - more_groups: bool, -} - -impl GroupByScan { - fn new( - scan: Box, - group_fields: Vec, - agg_fns: Vec, - ) -> DbResult { - let mut g = Self { - group_val: GroupValue::new(&scan, vec![])?, - scan, - group_fields, - agg_fns, - more_groups: false, - }; - g.before_first()?; - Ok(g) - } - - fn before_first(&mut self) -> DbResult<()> { - self.scan.before_first()?; - self.more_groups = self.scan.next_row()?; - Ok(()) - } - - fn next(&mut self) -> DbResult { - if !self.more_groups { - return Ok(false); - } - for f in &mut self.agg_fns { - f.process_first(&self.scan)?; - } - self.group_val = GroupValue::new(&self.scan, self.group_fields.clone())?; - loop { - self.more_groups = self.scan.next_row()?; - if !self.more_groups { - break; - } - let gv = GroupValue::new(&self.scan, self.group_fields.clone())?; - if gv != self.group_val { - break; - } - for f in &mut self.agg_fns { - f.process_next(&self.scan)?; - } - } - Ok(true) - } - - fn close(&self) -> DbResult<()> { - self.scan.close() - } - - 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 { - if f.field_name() == field_name { - return Ok(f.value()); - } - } - Err(DbError::FieldNotExists(field_name.to_string())) - } - - fn get_i32(&self, field_name: &Element) -> DbResult { - self.get_val(field_name)?.as_i32() - } - - fn get_string(&self, field_name: &Element) -> DbResult { - self.get_val(field_name)?.as_string() - } - - fn has_field(&self, field_name: &Element) -> bool { - if self.group_fields.contains(field_name) { - return true; - } - for f in &self.agg_fns { - if f.field_name() == field_name { - return true; - } - } - false - } - - fn schema(&self) -> DbResult { - self.scan.schema() - } -} diff --git a/planner/src/scan/index.rs b/planner/src/scan/index.rs deleted file mode 100644 index 519b03b..0000000 --- a/planner/src/scan/index.rs +++ /dev/null @@ -1,228 +0,0 @@ -use common::DbResult; - -use crate::element::Element; -use crate::schema::{Schema, SchemaBuilder}; -use crate::value::Value; -use crate::{index::Index, scan::Scan}; - -pub struct IndexSelectScan { - scan: Box, - index: Index, - value: Value, -} - -impl IndexSelectScan { - pub fn new(scan: Box, index: Index, value: Value) -> DbResult { - let mut scan = Self { scan, index, value }; - scan.before_first()?; - Ok(scan) - } - - pub(crate) fn before_first(&mut self) -> DbResult<()> { - self.index.before_first(self.value.clone()) - } - - pub(crate) fn next_rot(&mut self) -> DbResult { - let ok = self.index.next_row()?; - if ok { - let rid = self.index.get_data_rid()?; - self.scan.move_to_rid(rid)?; - } - Ok(ok) - } - - pub(crate) fn get_i32(&self, field_name: &Element) -> DbResult { - self.scan.get_i32(field_name) - } - - pub(crate) fn get_string(&self, field_name: &Element) -> DbResult { - self.scan.get_string(field_name) - } - - pub(crate) fn get_val(&self, field_name: &Element) -> DbResult { - self.scan.get_val(field_name) - } - - pub(crate) fn has_field(&self, field_name: &Element) -> DbResult { - self.scan.has_field(field_name) - } - - pub(crate) fn close(&self) -> DbResult<()> { - self.index.close()?; - self.scan.close() - } - - pub(crate) fn schema(&self) -> DbResult { - self.scan.schema() - } -} - -pub(crate) struct IndexJoinScan { - left: Box, - right: Box, - index: Index, - field: Element, -} - -impl IndexJoinScan { - pub(crate) fn new( - left: Box, - index: Index, - field: Element, - right: Box, - ) -> DbResult { - let mut scan = Self { - left, - right, - field, - index, - }; - scan.before_first()?; - Ok(scan) - } - - pub(crate) fn reset_index(&mut self) -> DbResult<()> { - let key = self.left.get_val(&self.field)?; - self.index.before_first(key) - } - - pub(crate) fn before_first(&mut self) -> DbResult<()> { - self.left.before_first()?; - self.left.next_row()?; - self.reset_index() - } - - pub(crate) fn next_row(&mut self) -> DbResult { - loop { - if self.index.next_row()? { - self.right.move_to_rid(self.index.get_data_rid()?)?; - return Ok(true); - } - if !self.left.next_row()? { - return Ok(false); - } - self.reset_index()?; - } - } - - pub(crate) fn get_i32(&self, field_name: &Element) -> DbResult { - if self.right.has_field(field_name)? { - self.right.get_i32(field_name) - } else { - self.left.get_i32(field_name) - } - } - - pub(crate) fn get_string(&self, field_name: &Element) -> DbResult { - if self.right.has_field(field_name)? { - self.right.get_string(field_name) - } else { - self.left.get_string(field_name) - } - } - - pub(crate) fn get_val(&self, field_name: &Element) -> DbResult { - if self.right.has_field(field_name)? { - self.right.get_val(field_name) - } else { - self.left.get_val(field_name) - } - } - - pub(crate) fn has_field(&self, field_name: &Element) -> DbResult { - Ok(self.right.has_field(field_name)? || self.left.has_field(field_name)?) - } - - pub(crate) fn close(&self) -> DbResult<()> { - self.left.close()?; - self.index.close()?; - self.right.close() - } - - pub(crate) fn schema(&self) -> DbResult { - let s1 = self.left.schema()?; - let s2 = self.right.schema()?; - let s = SchemaBuilder::default().add_all(&s1).add_all(&s2).build(); - Ok(s) - } -} - -#[cfg(test)] -mod tests { - use crate::element::Element; - use crate::mgr::metadata::MetadataMgr; - use crate::plan::table::TablePlan; - use crate::schema::SchemaBuilder; - use crate::tests::init; - use std::collections::HashMap; - - #[test] - fn update_index() { - let (_dir, tx) = init(); - let md = MetadataMgr::new(true, &tx).unwrap(); - tx.commit().unwrap(); - - let sid = Element::raw("sid"); - let sname = Element::raw("sname"); - let gadyear = Element::raw("gadyear"); - let majorid = Element::raw("majorid"); - - 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(); - let mut s = plan.open().unwrap(); - - let mut indexes = HashMap::new(); - let infos = md.get_index_info("users", &tx).unwrap(); - for (field, info) in infos { - let index = info.open().unwrap(); - indexes.insert(field, index); - } - - 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(); - - let rid = s.get_rid().unwrap(); - for (field, index) in indexes.iter() { - let value = s.get_val(field).unwrap(); - index.insert(value, rid.clone()).unwrap(); - } - - s.before_first().unwrap(); - while s.next_row().unwrap() { - 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(); - index.delete(value, rid.clone()).unwrap(); - } - s.delete().unwrap(); - break; - } - } - s.before_first().unwrap(); - while s.next_row().unwrap() { - println!( - "{} {}", - s.get_string(&sname).unwrap(), - s.get_i32(&sid).unwrap() - ); - } - s.close().unwrap(); - for idx in indexes.values() { - idx.close().unwrap(); - } - tx.commit().unwrap(); - } -} diff --git a/planner/src/scan/merge.rs b/planner/src/scan/merge.rs deleted file mode 100644 index 8a9cb6f..0000000 --- a/planner/src/scan/merge.rs +++ /dev/null @@ -1,109 +0,0 @@ -use crate::element::Element; -use crate::scan::Scan; -use crate::schema::{Schema, SchemaBuilder}; -use crate::value::Value; -use common::DbResult; -use std::cmp::Ordering; - -#[allow(dead_code)] -struct MergeJoinScan { - s1: Box, - s2: Box, - field_name1: Element, - field_name2: Element, - join_val: Option, -} - -#[allow(dead_code)] -impl MergeJoinScan { - pub fn new( - s1: Box, - s2: Box, - field_name1: Element, - field_name2: Element, - ) -> DbResult { - let mut s = Self { - s1, - s2, - field_name1, - field_name2, - join_val: None, - }; - s.before_first()?; - Ok(s) - } - - pub(crate) fn before_first(&mut self) -> DbResult<()> { - self.s1.before_first()?; - self.s2.before_first() - } - - pub(crate) fn next(&mut self) -> DbResult { - let Some(join_val) = &self.join_val else { - return Ok(false); - }; - let mut has_more2 = self.s2.next_row()?; - if has_more2 && self.s2.get_val(&self.field_name2)? == *join_val { - return Ok(true); - } - let mut has_more1 = self.s1.next_row()?; - if has_more1 && self.s1.get_val(&self.field_name1)? == *join_val { - return Ok(true); - } - - while has_more1 && has_more2 { - let v1 = self.s1.get_val(&self.field_name1)?; - let v2 = self.s2.get_val(&self.field_name2)?; - match v1.cmp(&v2) { - Ordering::Less => has_more1 = self.s1.next_row()?, - Ordering::Greater => has_more2 = self.s1.next_row()?, - _ => { - self.s2.save_position()?; - self.join_val = Some(self.s2.get_val(&self.field_name2)?); - return Ok(true); - } - } - } - Ok(false) - } - - fn get_i32(&self, field_name: &Element) -> DbResult { - if self.s1.has_field(field_name)? { - self.s1.get_i32(field_name) - } else { - self.s2.get_i32(field_name) - } - } - - fn get_string(&self, field_name: &Element) -> DbResult { - if self.s1.has_field(field_name)? { - self.s1.get_string(field_name) - } else { - self.s2.get_string(field_name) - } - } - - fn get_val(&self, field_name: &Element) -> DbResult { - if self.s1.has_field(field_name)? { - self.s1.get_val(field_name) - } else { - self.s2.get_val(field_name) - } - } - - fn has_field(&self, field_name: &Element) -> DbResult { - Ok(self.s1.has_field(field_name)? || self.s2.has_field(field_name)?) - } - - fn close(&self) -> DbResult<()> { - self.s1.close()?; - self.s2.close() - } - - fn schema(&self) -> DbResult { - let s1 = self.s1.schema()?; - let s2 = self.s2.schema()?; - let s = SchemaBuilder::default().add_all(&s1).add_all(&s2).build(); - Ok(s) - } -} diff --git a/planner/src/scan/multibuffer.rs b/planner/src/scan/multibuffer.rs deleted file mode 100644 index 4a203c2..0000000 --- a/planner/src/scan/multibuffer.rs +++ /dev/null @@ -1,154 +0,0 @@ -use std::sync::Arc; - -use common::{DbResult, error::DbError}; -use transaction::transaction::Transaction; - -use crate::buffer_needs::BufferNeeds; -use crate::element::Element; -use crate::value::Value; -use crate::{ - layout::Layout, - scan::Scan, - schema::{Schema, SchemaBuilder}, -}; - -pub(crate) struct MultiBufferProductScan { - tx: Arc, - left: Box, - right: Option>, - filename: String, - layout: Layout, - chunk_size: i32, - next_block: i32, - file_size: i32, -} - -impl MultiBufferProductScan { - pub(crate) fn new( - tx: &Arc, - left: Box, - filename: &str, - layout: Layout, - ) -> DbResult { - let available = tx.available_buffs()? as i32; - let file_size = tx.size(filename)? as i32; - let mut scan = Self { - tx: Arc::clone(tx), - left, - file_size: tx.size(filename)? as i32, - filename: filename.to_string(), - layout, - chunk_size: BufferNeeds::best_factor(available, file_size), - next_block: 0, - right: None, - }; - scan.before_first()?; - Ok(scan) - } - - - pub(crate) fn use_next_chunk(&mut self) -> DbResult { - if let Some(right) = self.right.take() { - right.close()?; - } - if self.next_block >= self.file_size { - return Ok(false); - } - let mut end = self.next_block + self.chunk_size - 1; - if end >= self.file_size { - end = self.file_size - 1; - } - let mut right = Box::new(Scan::chunk( - &self.tx, - &self.filename, - self.layout.clone(), - self.next_block, - end, - )?); - self.left.before_first()?; - self.left.next_row()?; - right.before_first()?; - self.right = Some(right); - self.next_block = end + 1; - Ok(true) - } - - pub(crate) fn before_first(&mut self) -> DbResult<()> { - self.next_block = 0; - self.use_next_chunk()?; - Ok(()) - } - - pub(crate) fn next(&mut self) -> DbResult { - loop { - if let Some(right) = self.right.as_mut() { - if right.next_row()? { - return Ok(true); - } - right.before_first()?; - if right.next_row()? && self.left.next_row()? { - return Ok(true); - } - } - if !self.use_next_chunk()? { - return Ok(false); - } - } - } - - pub(crate) fn get_i32(&self, field_name: &Element) -> DbResult { - if self.left.has_field(field_name)? { - self.left.get_i32(field_name) - } else { - self.right_scan()?.get_i32(field_name) - } - } - - pub(crate) fn get_string(&self, field_name: &Element) -> DbResult { - if self.left.has_field(field_name)? { - self.left.get_string(field_name) - } else { - self.right_scan()?.get_string(field_name) - } - } - - pub(crate) fn get_val(&self, field_name: &Element) -> DbResult { - if self.left.has_field(field_name)? { - self.left.get_val(field_name) - } else { - self.right_scan()?.get_val(field_name) - } - } - - pub(crate) fn has_field(&self, field_name: &Element) -> DbResult { - if self.left.has_field(field_name)? { - return Ok(true); - } - match &self.right { - Some(right) => right.has_field(field_name), - None => Ok(false), - } - } - - pub(crate) fn close(&self) -> DbResult<()> { - self.left.close()?; - if let Some(right) = &self.right { - right.close()?; - } - Ok(()) - } - - pub(crate) fn schema(&self) -> DbResult { - let mut builder = SchemaBuilder::default().add_all(&self.left.schema()?); - if let Some(right) = &self.right { - builder = builder.add_all(&right.schema()?); - } - Ok(builder.build()) - } - - fn right_scan(&self) -> DbResult<&Scan> { - self.right - .as_deref() - .ok_or_else(|| DbError::other("multibuffer right chunk not open")) - } -} diff --git a/planner/src/scan/order.rs b/planner/src/scan/order.rs deleted file mode 100644 index 4f9811b..0000000 --- a/planner/src/scan/order.rs +++ /dev/null @@ -1,189 +0,0 @@ -use common::{DbResult, error::DbError}; -use std::cmp::Ordering; - -use crate::element::Element; -use crate::rid::RID; -use crate::scan::Scan; -use crate::schema::{Schema, SchemaBuilder}; -use crate::temp::TempTable; -use crate::value::Value; - -#[derive(Clone)] -pub struct RecordComparator { - fields: Vec, -} - -impl RecordComparator { - pub fn new(fields: Vec) -> Self { - Self { fields } - } - - pub fn compare(&self, s1: &Scan, s2: &Scan) -> DbResult { - for field in &self.fields { - let val1 = s1.get_val(field)?; - let val2 = s2.get_val(field)?; - let result = val1.cmp(&val2); - if result != Ordering::Equal { - return Ok(result); - } - } - Ok(Ordering::Equal) - } -} - -enum CurrentScan { - None, - S1, - S2, -} - -pub struct SortScan { - s1: Box, - s2: Option>, - current_scan: CurrentScan, - comp: RecordComparator, - has_more1: bool, - has_more2: bool, - saved_position: Vec, -} - -impl SortScan { - fn new(runs: Vec, comp: RecordComparator) -> DbResult { - let mut s1 = Box::new(runs[0].open()?); - let has_more1 = s1.next_row()?; - let (s2, has_more2) = if runs.len() > 1 { - let mut s2 = Box::new(runs[1].open()?); - let has_more2 = s2.next_row()?; - (Some(s2), has_more2) - } else { - (None, false) - }; - Ok(Self { - comp, - has_more1, - has_more2, - s1, - s2, - current_scan: CurrentScan::None, - saved_position: vec![], - }) - } - - fn before_first(&mut self) -> DbResult<()> { - self.s1.before_first()?; - self.has_more1 = self.s1.next_row()?; - if let Some(s2) = self.s2.as_mut() { - s2.before_first()?; - self.has_more2 = s2.next_row()?; - } - Ok(()) - } - - fn next(&mut self) -> DbResult { - match self.current_scan { - CurrentScan::S1 => self.has_more1 = self.s1.next_row()?, - CurrentScan::S2 => { - if let Some(s2) = self.s2.as_mut() { - self.has_more2 = s2.next_row()?; - } - } - CurrentScan::None => {} - } - if !self.has_more1 && !self.has_more2 { - return Ok(false); - } - if self.has_more1 && self.has_more2 { - if let Some(s2) = self.s2.as_ref() { - if self.comp.compare(&self.s1, s2)? == Ordering::Less { - self.current_scan = CurrentScan::S1; - } else { - self.current_scan = CurrentScan::S2; - } - } else { - self.current_scan = CurrentScan::S1; - } - } else if self.has_more1 { - self.current_scan = CurrentScan::S1; - } else { - self.current_scan = CurrentScan::S2; - } - Ok(true) - } - - fn close(&self) -> DbResult<()> { - self.s1.close()?; - if let Some(s2) = &self.s2 { - s2.close()?; - } - Ok(()) - } - - fn get_val(&self, field_name: &Element) -> DbResult { - match self.current_scan { - CurrentScan::S1 => self.s1.get_val(field_name), - CurrentScan::S2 => self.right()?.get_val(field_name), - CurrentScan::None => Err(DbError::other("invalid scan")), - } - } - - fn get_i32(&self, field_name: &Element) -> DbResult { - match self.current_scan { - CurrentScan::S1 => self.s1.get_i32(field_name), - CurrentScan::S2 => self.right()?.get_i32(field_name), - CurrentScan::None => Err(DbError::other("invalid scan")), - } - } - - fn get_string(&self, field_name: &Element) -> DbResult { - match self.current_scan { - CurrentScan::S1 => self.s1.get_string(field_name), - CurrentScan::S2 => self.right()?.get_string(field_name), - CurrentScan::None => Err(DbError::other("invalid scan")), - } - } - - fn has_field(&self, field_name: &Element) -> DbResult { - match self.current_scan { - CurrentScan::S1 => self.s1.has_field(field_name), - CurrentScan::S2 => self.right()?.has_field(field_name), - CurrentScan::None => Err(DbError::other("invalid scan")), - } - } - - fn save_position(&mut self) -> DbResult<()> { - let rid1 = self.s1.get_rid()?; - if let Some(s2) = &self.s2 { - let rid2 = s2.get_rid()?; - self.saved_position = vec![rid1, rid2]; - } else { - self.saved_position = vec![rid1]; - } - Ok(()) - } - - fn restore_position(&mut self) -> DbResult<()> { - if let Some(rid1) = self.saved_position.first().cloned() { - self.s1.move_to_rid(rid1)?; - } - if let Some(rid2) = self.saved_position.get(1).cloned() { - if let Some(s2) = self.s2.as_mut() { - s2.move_to_rid(rid2)?; - } - } - Ok(()) - } - - fn schema(&self) -> DbResult { - let mut builder = SchemaBuilder::default().add_all(&self.s1.schema()?); - if let Some(s2) = self.s2.as_ref() { - builder = builder.add_all(&s2.schema()?); - } - Ok(builder.build()) - } - - fn right(&self) -> DbResult<&Scan> { - self.s2 - .as_deref() - .ok_or_else(|| DbError::other("invalid scan")) - } -} \ No newline at end of file diff --git a/planner/src/scan/product.rs b/planner/src/scan/product.rs deleted file mode 100644 index 966e1fb..0000000 --- a/planner/src/scan/product.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::element::Element; -use crate::scan::Scan; -use crate::schema::{Schema, SchemaBuilder}; -use crate::value::Value; -use common::DbResult; - -pub struct ProductScan { - s1: Box, - s2: Box, -} - -impl ProductScan { - pub fn new(mut s1: Box, s2: Box) -> DbResult { - s1.next_row()?; - Ok(Self { s1, s2 }) - } -} - -impl ProductScan { - pub(crate) fn before_first(&mut self) -> DbResult<()> { - self.s1.before_first()?; - self.s1.next_row()?; - self.s2.before_first() - } - - pub(crate) fn next_row(&mut self) -> DbResult { - if self.s2.next_row()? { - Ok(true) - } else { - self.s2.before_first()?; - Ok(self.s2.next_row()? && self.s1.next_row()?) - } - } - - pub(crate) fn get_i32(&self, field_name: &Element) -> DbResult { - if self.s1.has_field(field_name)? { - self.s1.get_i32(field_name) - } else { - self.s2.get_i32(field_name) - } - } - - pub(crate) fn get_string(&self, field_name: &Element) -> DbResult { - if self.s1.has_field(field_name)? { - self.s1.get_string(field_name) - } else { - self.s2.get_string(field_name) - } - } - - pub(crate) fn get_val(&self, field_name: &Element) -> DbResult { - if self.s1.has_field(field_name)? { - self.s1.get_val(field_name) - } else { - self.s2.get_val(field_name) - } - } - - pub(crate) fn has_field(&self, field_name: &Element) -> DbResult { - Ok(self.s1.has_field(field_name)? || self.s2.has_field(field_name)?) - } - - pub(crate) fn close(&self) -> DbResult<()> { - self.s1.close()?; - self.s2.close() - } - - pub(crate) fn schema(&self) -> DbResult { - let s1 = self.s1.schema()?; - let s2 = self.s2.schema()?; - let s = SchemaBuilder::default().add_all(&s1).add_all(&s2).build(); - Ok(s) - } -} diff --git a/planner/src/scan/project.rs b/planner/src/scan/project.rs deleted file mode 100644 index aac89ec..0000000 --- a/planner/src/scan/project.rs +++ /dev/null @@ -1,71 +0,0 @@ -use crate::element::Element; -use crate::scan::Scan; -use crate::schema::{Schema, SchemaBuilder}; -use crate::value::Value; -use common::DbResult; -use common::error::DbError; -use std::collections::HashSet; - -pub struct ProjectScan { - scan: Box, - fields: HashSet, -} - -impl ProjectScan { - pub fn new(scan: Box, fields: HashSet) -> Self { - Self { scan, fields } - } -} - -impl ProjectScan { - pub(crate) fn before_first(&mut self) -> DbResult<()> { - self.scan.before_first() - } - - pub(crate) fn next(&mut self) -> DbResult { - self.scan.next_row() - } - - pub(crate) fn get_i32(&self, field_name: &Element) -> DbResult { - if self.has_field(field_name)? { - self.scan.get_i32(field_name) - } else { - Err(DbError::FieldNotExists(field_name.to_string())) - } - } - - pub(crate) fn get_string(&self, field_name: &Element) -> DbResult { - if self.has_field(field_name)? { - self.scan.get_string(field_name) - } else { - Err(DbError::FieldNotExists(field_name.to_string())) - } - } - - pub(crate) fn get_val(&self, field_name: &Element) -> DbResult { - if self.has_field(field_name)? { - self.scan.get_val(field_name) - } else { - Err(DbError::FieldNotExists(field_name.to_string())) - } - } - - pub(crate) fn has_field(&self, field_name: &Element) -> DbResult { - Ok(self.fields.contains(field_name)) - } - - pub(crate) fn close(&self) -> DbResult<()> { - self.scan.close() - } - - pub(crate) fn schema(&self) -> DbResult { - let mut project = SchemaBuilder::default(); - let schema = self.scan.schema()?; - for (field, info) in schema.fields() { - if self.fields.contains(&field) { - project = project.add_field(field, info); - } - } - Ok(project.build()) - } -} diff --git a/planner/src/scan/scanner.rs b/planner/src/scan/scanner.rs deleted file mode 100644 index 37f1c8c..0000000 --- a/planner/src/scan/scanner.rs +++ /dev/null @@ -1,159 +0,0 @@ -use common::{DbResult, error::DbError}; - -use crate::{ - element::Element, - rid::RID, - scan::{select::SelectScan, table::TableScan}, - schema::Schema, - value::Value, -}; -use crate::scan::chunk::ChunkScan; -use crate::scan::product::ProductScan; - -pub(crate) enum Scanner { - Table(TableScan), - Select(SelectScan), - Chunk(ChunkScan), - Product(ProductScan), -} - -impl Scanner { - pub(crate) fn before_first(&mut self) -> DbResult<()> { - match self { - Self::Table(table) => table.before_first(), - Self::Select(select) => select.before_first(), - Self::Chunk(chunk) => { - chunk.before_first(); - Ok(()) - } - Self::Product(product) => product.before_first(), - } - } - - pub fn next(&mut self) -> DbResult { - match self { - Self::Table(table) => table.next(), - Self::Select(select) => select.next(), - Self::Chunk(chunk) => chunk.next(), - Self::Product(product) => product.next_row(), - } - } - - pub fn get_i32(&self, field: &Element) -> DbResult { - match self { - Self::Table(table) => table.get_i32(field), - Self::Select(select) => select.get_i32(field), - Self::Chunk(chunk) => chunk.get_i32(field), - Self::Product(product) => product.get_i32(field), - } - } - - pub fn get_string(&self, field: &Element) -> DbResult { - match self { - Self::Table(table) => table.get_string(field), - Self::Select(select) => select.get_string(field), - Self::Chunk(chunk) => chunk.get_string(field), - Self::Product(product) => product.get_string(field), - } - } - - pub fn get_val(&self, field: &Element) -> DbResult { - match self { - Self::Table(table) => table.get_val(field), - Self::Select(select) => select.get_val(field), - Self::Chunk(chunk) => chunk.get_val(field), - Self::Product(product) => product.get_val(field), - } - } - - pub fn has_field(&self, field: &Element) -> DbResult { - match self { - Self::Table(table) => Ok(table.has_field(field)), - Self::Select(select) => select.has_field(field), - Self::Chunk(chunk) => Ok(chunk.has_field(field)), - Self::Product(product) => product.has_field(field), - } - } - - pub fn close(&self) -> DbResult<()> { - match self { - Self::Table(table) => table.close(), - Self::Select(select) => select.close(), - Self::Chunk(chunk) => chunk.close(), - Self::Product(product) => product.close(), - } - } - - pub fn schema(&self) -> DbResult { - match self { - Self::Table(table) => Ok(table.schema().clone()), - Self::Select(select) => select.schema(), - Self::Chunk(chunk) => Ok(chunk.schema()), - Self::Product(product) => product.schema(), - } - } - - pub fn set_i32(&self, field: &Element, value: i32) -> DbResult<()> { - match self { - Self::Table(table) => table.set_i32(field, value), - Self::Select(select) => select.set_i32(field, value), - Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), - } - } - - pub fn set_string(&self, field: &Element, value: &str) -> DbResult<()> { - match self { - Self::Table(table) => table.set_string(field, value), - Self::Select(select) => select.set_string(field, value), - Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), - } - } - - pub fn set_val(&self, field: &Element, value: Value) -> DbResult<()> { - match self { - Self::Table(table) => table.set_val(field, value), - Self::Select(select) => select.set_val(field, value), - Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), - } - } - - pub fn insert(&mut self) -> DbResult<()> { - match self { - Self::Table(table) => table.insert(), - Self::Select(select) => select.insert(), - Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), - } - } - - pub fn delete(&self) -> DbResult<()> { - match self { - Self::Table(table) => table.delete(), - Self::Select(select) => select.delete(), - Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), - } - } - - pub fn get_rid(&self) -> DbResult { - match self { - Self::Table(table) => Ok(table.get_rid()), - Self::Select(select) => select.get_rid(), - Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), - } - } - - pub fn move_to_rid(&mut self, rid: RID) -> DbResult<()> { - match self { - Self::Table(table) => table.move_to_rid(rid), - Self::Select(select) => select.move_to_rid(rid), - Self::Chunk(_) | Self::Product(_) => Err(Self::read_only()), - } - } - - pub fn save_position(&self) -> DbResult<()> { - Err(DbError::other("cannot save position")) - } - - fn read_only() -> DbError { - DbError::other("scan does not support record modification") - } -} diff --git a/planner/src/scan/select.rs b/planner/src/scan/select.rs deleted file mode 100644 index 5337f05..0000000 --- a/planner/src/scan/select.rs +++ /dev/null @@ -1,81 +0,0 @@ -use common::DbResult; - -use crate::{ - element::Element, predicate::Predicate, rid::RID, scan::Scan, schema::Schema, value::Value, -}; - -pub(crate) struct SelectScan { - scan: Box, - predicate: Predicate, -} - -impl SelectScan { - pub(crate) fn new(scan: Box, predicate: Predicate) -> Self { - Self { scan, predicate } - } - - pub(crate) fn before_first(&mut self) -> DbResult<()> { - self.scan.before_first() - } - - pub(crate) fn next(&mut self) -> DbResult { - while self.scan.next_row()? { - if self.predicate.is_satisfied(&self.scan)? { - return Ok(true); - } - } - Ok(false) - } - - pub(crate) fn get_i32(&self, field_name: &Element) -> DbResult { - self.scan.get_i32(field_name) - } - - pub(crate) fn get_string(&self, field_name: &Element) -> DbResult { - self.scan.get_string(field_name) - } - - pub(crate) fn get_val(&self, field_name: &Element) -> DbResult { - self.scan.get_val(field_name) - } - - pub(crate) fn has_field(&self, field_name: &Element) -> DbResult { - self.scan.has_field(field_name) - } - - pub(crate) fn close(&self) -> DbResult<()> { - self.scan.close() - } - - pub(crate) fn schema(&self) -> DbResult { - self.scan.schema() - } - - pub(crate) fn set_i32(&self, field_name: &Element, value: i32) -> DbResult<()> { - self.scan.set_i32(field_name, value) - } - - pub(crate) fn set_string(&self, field_name: &Element, value: &str) -> DbResult<()> { - self.scan.set_string(field_name, value) - } - - pub(crate) fn set_val(&self, field_name: &Element, value: Value) -> DbResult<()> { - self.scan.set_val(field_name, value) - } - - pub(crate) fn insert(&mut self) -> DbResult<()> { - self.scan.insert() - } - - pub(crate) fn delete(&self) -> DbResult<()> { - self.scan.delete() - } - - pub(crate) fn get_rid(&self) -> DbResult { - self.scan.get_rid() - } - - pub(crate) fn move_to_rid(&mut self, rid: RID) -> DbResult<()> { - self.scan.move_to_rid(rid) - } -} diff --git a/planner/src/scan/table.rs b/planner/src/scan/table.rs deleted file mode 100644 index 3d1e49c..0000000 --- a/planner/src/scan/table.rs +++ /dev/null @@ -1,193 +0,0 @@ -use std::sync::Arc; - -use common::{DbResult, error::DbError}; -use file::block::BlockId; -use transaction::transaction::Transaction; - -use crate::element::Element; -use crate::schema::Schema; -use crate::value::Value; -use crate::{field_info::FieldInfo, layout::Layout, record_page::RecordPage, rid::RID}; - -pub(crate) struct TableScan { - tx: Arc, - layout: Layout, - rp: RecordPage, - filename: String, - current_slot: i32, -} - -impl TableScan { - pub(crate) 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.clone())?; - rp.format()?; - rp - } else { - let block = BlockId::new(&filename, 0); - RecordPage::new(tx, block, layout.clone())? - }; - Ok(Self { - tx: Arc::clone(tx), - layout, - rp, - current_slot: -1, - filename, - }) - } - - pub(crate) fn close(&self) -> DbResult<()> { - self.tx.unpin(&self.rp.block()) - } - - pub(crate) fn before_first(&mut self) -> DbResult<()> { - self.move_to_block(0) - } - - pub(crate) fn next(&mut self) -> DbResult { - self.current_slot = self.rp.next_after(self.current_slot)?; - while self.current_slot < 0 { - if self.at_last_block()? { - return Ok(false); - } - self.move_to_block(self.rp.block().num + 1)?; - self.current_slot = self.rp.next_after(self.current_slot)?; - } - Ok(true) - } - - pub(crate) fn get_i32(&self, filename: &Element) -> DbResult { - self.rp.get_i32(self.current_slot, filename) - } - - pub(crate) fn get_string(&self, filename: &Element) -> DbResult { - self.rp.get_string(self.current_slot, filename) - } - - pub(crate) 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(Value::Integer(self.get_i32(fieldname)?)), - FieldInfo::Varchar(_) => Ok(Value::Varchar(self.get_string(fieldname)?)), - } - } - - pub(crate) fn has_field(&self, fieldname: &Element) -> bool { - self.layout.schema().has_field(fieldname) - } - - pub(crate) 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(crate) 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(crate) fn set_val(&self, field: &Element, value: Value) -> DbResult<()> { - match value { - Value::Integer(value) => self.set_i32(field, value), - Value::Varchar(value) => self.set_string(field, &value), - } - } - - pub(crate) fn insert(&mut self) -> DbResult<()> { - let last_block = self.tx.size(&self.filename)? as i32 - 1; - if self.rp.block().num != last_block { - self.move_to_block(last_block)?; - } - self.current_slot = self.rp.insert_after(self.current_slot)?; - while self.current_slot < 0 { - if self.at_last_block()? { - self.move_to_new_block()?; - } else { - self.move_to_block(self.rp.block().num + 1)?; - } - self.current_slot = self.rp.insert_after(self.current_slot)?; - } - Ok(()) - } - - pub(crate) fn delete(&self) -> DbResult<()> { - self.rp.delete(self.current_slot) - } - - pub(crate) 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.clone())?; - self.rp = rp; - self.current_slot = rid.slot(); - Ok(()) - } - - pub(crate) fn get_rid(&self) -> RID { - let rp = &self.rp; - RID::new(rp.block().num, self.current_slot) - } - - 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.clone())?; - self.current_slot = -1; - Ok(()) - } - - 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.clone())?; - self.rp.format()?; - self.current_slot = -1; - Ok(()) - } - - fn at_last_block(&self) -> DbResult { - Ok(self.rp.block().num == self.tx.size(&self.filename)? as i32 - 1) - } - - pub(crate) fn schema(&self) -> &Schema { - self.layout.schema() - } -} - -#[cfg(test)] -mod tests { - use crate::{schema::SchemaBuilder, tests::init}; - - use super::*; - - #[test] - fn reopen() { - let (_dir, tx) = init(); - let layout = Layout::new(SchemaBuilder::default().build()); - let mut scan = TableScan::new(&tx, "test", layout.clone()).unwrap(); - scan.insert().unwrap(); - drop(scan); - - TableScan::new(&tx, "test", layout).unwrap(); - } - - #[test] - fn move_to_new_block() { - let (_dir, tx) = init(); - let schema = SchemaBuilder::default() - .add_string_field(Element::raw("name"), 16) - .build(); - let layout = Layout::new(schema); - let mut scan = TableScan::new(&tx, "test", layout).unwrap(); - scan.before_first().unwrap(); - for _ in 0..10 { - scan.insert().unwrap(); - } - } -} diff --git a/planner/src/schema.rs b/planner/src/schema.rs deleted file mode 100644 index e62f6b2..0000000 --- a/planner/src/schema.rs +++ /dev/null @@ -1,142 +0,0 @@ -use std::{collections::HashMap, fmt::Debug, rc::Rc}; - -use crate::{element::Element, field_info::FieldInfo}; - -#[derive(Debug, PartialEq, Eq)] -pub struct SchemaInner { - fields: Vec, - infos: HashMap, -} - -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_int_field(&mut self, fieldname: Element) { - self.add_field(fieldname, FieldInfo::Integer); - } - - fn add_string_field(&mut self, fieldname: Element, length: i32) { - self.add_field(fieldname, FieldInfo::Varchar(length)); - } - - fn add(&mut self, fieldname: Element, schema: &Self) { - if let Some(info) = schema.info(&fieldname) { - self.add_field(fieldname, info); - } - } - - fn add_all(&mut self, schema: &Self) { - for (field, info) in &schema.infos { - self.add_field(field.clone(), info.clone()); - } - } - - fn info(&self, fieldname: &Element) -> Option { - self.infos.get(fieldname).cloned() - } - - 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 - } - - 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 info(&self, fieldname: &Element) -> Option { - self.0.info(fieldname) - } - - pub fn fields(&self) -> Vec<(Element, FieldInfo)> { - self.0.fields() - } - - pub fn has_field(&self, field: &Element) -> bool { - self.0.has_field(field) - } -} - -pub struct SchemaBuilder { - schema: SchemaInner, -} - -impl SchemaBuilder { - pub fn add_field(mut self, field: Element, info: FieldInfo) -> Self { - self.schema.add_field(field, info); - self - } - - pub fn add_int_field(mut self, field: Element) -> Self { - self.schema.add_int_field(field); - self - } - - pub fn add_string_field(mut self, field: Element, len: i32) -> Self { - self.schema.add_string_field(field, len); - self - } - - pub fn add(mut self, field: Element, schema: &Schema) -> Self { - self.schema.add(field, &schema.0); - self - } - - 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 SchemaBuilder { - fn default() -> Self { - Self { - 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/planner/src/temp.rs b/planner/src/temp.rs deleted file mode 100644 index 17683c4..0000000 --- a/planner/src/temp.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::layout::Layout; -use crate::scan::Scan; -use crate::schema::Schema; -use common::DbResult; -use std::sync::atomic::AtomicI32; -use std::sync::{Arc, atomic}; -use transaction::transaction::Transaction; - -static TABLE_NUM_COUNTER: AtomicI32 = AtomicI32::new(0); - -fn next_table_num() -> i32 { - TABLE_NUM_COUNTER.fetch_add(1, atomic::Ordering::SeqCst) -} - -#[derive(Clone)] -pub struct TempTable { - tx: Arc, - table_name: String, - layout: Layout, -} - -impl TempTable { - pub fn new(tx: &Arc, schema: Schema) -> DbResult { - Ok(Self { - table_name: format!("temp_{}", next_table_num()), - tx: Arc::clone(tx), - layout: Layout::new(schema), - }) - } - - pub fn open(&self) -> DbResult { - Scan::table(&self.tx, &self.table_name, self.layout.clone()) - } - - pub(crate) fn table_name(&self) -> String { - self.table_name.clone() - } - - pub(crate) fn layout(&self) -> Layout { - self.layout.clone() - } -} 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() { From dc96ea47b7f67b5649c53f5ada57a7e50cb2df01 Mon Sep 17 00:00:00 2001 From: Daniil Buravlev Date: Tue, 30 Jun 2026 22:52:55 +0300 Subject: [PATCH 12/13] Apply formatting --- buffer/src/mgr.rs | 2 +- engine/benches/db_benchmark.rs | 3 ++- engine/src/index.rs | 4 ++-- engine/src/index_mgr.rs | 9 +++++++-- engine/src/lib.rs | 8 ++++---- engine/src/plan.rs | 4 ++-- engine/src/plan/group.rs | 4 ++-- engine/src/plan/materialize.rs | 2 +- engine/src/plan/order.rs | 4 ++-- engine/src/plan/project.rs | 2 +- engine/src/plan/select.rs | 2 +- engine/src/query/basic_planner.rs | 3 +-- engine/src/query/lexer.rs | 2 +- engine/src/query/parser.rs | 8 ++++++-- engine/src/query/table_planner.rs | 2 +- engine/src/scan.rs | 6 +++--- engine/src/scan/chunk.rs | 4 ++-- engine/src/scan/multibuffer.rs | 2 +- engine/src/scan/select.rs | 2 +- engine/src/view_mgr.rs | 2 +- 20 files changed, 42 insertions(+), 33 deletions(-) diff --git a/buffer/src/mgr.rs b/buffer/src/mgr.rs index 6d3c772..5ac75d2 100644 --- a/buffer/src/mgr.rs +++ b/buffer/src/mgr.rs @@ -129,7 +129,7 @@ impl BufferMgr { 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/engine/benches/db_benchmark.rs b/engine/benches/db_benchmark.rs index 515743f..3834c23 100644 --- a/engine/benches/db_benchmark.rs +++ b/engine/benches/db_benchmark.rs @@ -34,7 +34,8 @@ fn bench_index_insert(c: &mut Criterion) { 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(); + db.query(&tx, &format!("SELECT id FROM test WHERE id={}", id)) + .unwrap(); }); }); } diff --git a/engine/src/index.rs b/engine/src/index.rs index 1761e31..a0205d1 100644 --- a/engine/src/index.rs +++ b/engine/src/index.rs @@ -22,12 +22,12 @@ pub trait Index { mod tests { use tempfile::tempdir; + use crate::element::Element; use crate::schema::SchemaBuilder; use crate::{ SimpleDB, plan::{Plan, table::TablePlan}, }; - use crate::element::Element; #[test] fn index_retrieval() { @@ -39,7 +39,7 @@ mod tests { let md = db.metadata_mgr(); let table = "student"; - + let sid = Element::raw("sid"); let sname = Element::raw("sname"); let majorid = Element::raw("majorid"); diff --git a/engine/src/index_mgr.rs b/engine/src/index_mgr.rs index 71cbd92..f509b5e 100644 --- a/engine/src/index_mgr.rs +++ b/engine/src/index_mgr.rs @@ -134,8 +134,13 @@ impl IndexMgr { 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)?; + let index = IndexInfo::new( + idx_name, + Element::raw(&field_name), + layout.schema(), + tx, + stat, + )?; result.insert(Element::Raw(field_name), index); } } diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 4b75bd6..6c1e40e 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -16,7 +16,6 @@ use crate::{ }; pub mod buffer_needs; -pub mod value; pub mod element; pub mod field_info; pub mod index; @@ -34,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"; @@ -106,10 +106,10 @@ impl SimpleDB { #[cfg(test)] mod tests { - use std::collections::HashSet; - use tempfile::{tempdir, TempDir}; - use crate::element::Element; use super::*; + use crate::element::Element; + use std::collections::HashSet; + use tempfile::{TempDir, tempdir}; pub(crate) fn init() -> (TempDir, Arc) { init_with_size(512) diff --git a/engine/src/plan.rs b/engine/src/plan.rs index 387e485..a004993 100644 --- a/engine/src/plan.rs +++ b/engine/src/plan.rs @@ -2,18 +2,18 @@ use std::{rc::Rc, sync::Arc}; use common::DbResult; -use crate::{scan::Scan, schema::Schema}; use crate::element::Element; +use crate::{scan::Scan, schema::Schema}; pub(crate) mod group; 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 order; pub mod table; pub trait Plan { diff --git a/engine/src/plan/group.rs b/engine/src/plan/group.rs index 0c0a42a..ec52f15 100644 --- a/engine/src/plan/group.rs +++ b/engine/src/plan/group.rs @@ -3,13 +3,13 @@ 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, order::SortPlan}, scan::group::{AggregationFn, GroupByScan}, schema::Schema, }; -use crate::element::Element; -use crate::schema::SchemaBuilder; pub struct GroupByPlan { plan: Rc, diff --git a/engine/src/plan/materialize.rs b/engine/src/plan/materialize.rs index a8cfaf6..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; @@ -7,7 +8,6 @@ use common::DbResult; use std::rc::Rc; use std::sync::Arc; use transaction::transaction::Transaction; -use crate::element::Element; pub struct MaterializePlan { source: Rc, diff --git a/engine/src/plan/order.rs b/engine/src/plan/order.rs index 38ba6b5..e12cbe8 100644 --- a/engine/src/plan/order.rs +++ b/engine/src/plan/order.rs @@ -3,16 +3,16 @@ 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, - order::{RecordComparator, OrderScan}, + order::{OrderScan, RecordComparator}, }, schema::Schema, temp::TempTable, }; -use crate::element::Element; pub struct SortPlan { plan: Rc, diff --git a/engine/src/plan/project.rs b/engine/src/plan/project.rs index 89e56d0..3f87f58 100644 --- a/engine/src/plan/project.rs +++ b/engine/src/plan/project.rs @@ -6,13 +6,13 @@ use std::{ use common::DbResult; +use crate::schema::SchemaBuilder; use crate::{ element::Element, plan::Plan, scan::{Scan, project::ProjectScan}, schema::Schema, }; -use crate::schema::SchemaBuilder; pub struct ProjectPlan { plan: Rc, diff --git a/engine/src/plan/select.rs b/engine/src/plan/select.rs index 9a326e0..f2a7efa 100644 --- a/engine/src/plan/select.rs +++ b/engine/src/plan/select.rs @@ -2,13 +2,13 @@ use std::{rc::Rc, sync::Arc}; use common::DbResult; +use crate::element::Element; use crate::{ plan::Plan, predicate::Predicate, scan::{Scan, select::SelectScan}, schema::Schema, }; -use crate::element::Element; pub struct SelectPlan { plan: Rc, diff --git a/engine/src/query/basic_planner.rs b/engine/src/query/basic_planner.rs index 0e55b82..794d4c7 100644 --- a/engine/src/query/basic_planner.rs +++ b/engine/src/query/basic_planner.rs @@ -80,8 +80,7 @@ impl UpdatePlanner for BasicUpdatePlanner { data: super::command::TableData, tx: &Arc, ) -> DbResult { - self.md - .create_table(&data.name, data.schema, tx)?; + self.md.create_table(&data.name, data.schema, tx)?; Ok(0) } diff --git a/engine/src/query/lexer.rs b/engine/src/query/lexer.rs index 81f8daf..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::{ - value::Value, query::{token::Token, tokenizer::Tokenizer}, + value::Value, }; pub struct Lexer { diff --git a/engine/src/query/parser.rs b/engine/src/query/parser.rs index c0e837b..6583b2e 100644 --- a/engine/src/query/parser.rs +++ b/engine/src/query/parser.rs @@ -1,5 +1,6 @@ use common::{DbResult, error::DbError}; +use crate::schema::SchemaBuilder; use crate::{ element::Element, predicate::{Expression, Predicate, Term}, @@ -15,7 +16,6 @@ use crate::{ sort_by::SortByData, value::Value, }; -use crate::schema::SchemaBuilder; pub(crate) struct Parser { lexer: Lexer, @@ -245,7 +245,11 @@ impl Parser { self.field_type(field_name, schema) } - fn field_type(&self, field_name: Element, mut schema: SchemaBuilder) -> 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 = schema.add_int_field(field_name); diff --git a/engine/src/query/table_planner.rs b/engine/src/query/table_planner.rs index 4f1db79..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, @@ -16,7 +17,6 @@ use crate::{ predicate::Predicate, schema::Schema, }; -use crate::element::Element; pub(crate) struct TablePlanner { plan: Rc, diff --git a/engine/src/scan.rs b/engine/src/scan.rs index 6726620..f6fd65c 100644 --- a/engine/src/scan.rs +++ b/engine/src/scan.rs @@ -1,19 +1,19 @@ use common::{DbResult, error::DbError}; use std::sync::Arc; -use crate::schema::Schema; -use crate::{value::Value, rid::RID}; use crate::element::Element; +use crate::schema::Schema; +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 order; pub mod table; pub trait Scan { diff --git a/engine/src/scan/chunk.rs b/engine/src/scan/chunk.rs index c11ad6f..6b8ddf2 100644 --- a/engine/src/scan/chunk.rs +++ b/engine/src/scan/chunk.rs @@ -5,11 +5,11 @@ use common::{DbResult, error::DbError}; use file::block::BlockId; use transaction::transaction::Transaction; +use crate::element::Element; use crate::schema::Schema; use crate::{ - value::Value, field_info::FieldInfo, layout::Layout, record_page::RecordPage, scan::Scan, + field_info::FieldInfo, layout::Layout, record_page::RecordPage, scan::Scan, value::Value, }; -use crate::element::Element; pub struct ChunkScanLock { buffers: Vec, diff --git a/engine/src/scan/multibuffer.rs b/engine/src/scan/multibuffer.rs index d48fd3f..2acedb2 100644 --- a/engine/src/scan/multibuffer.rs +++ b/engine/src/scan/multibuffer.rs @@ -3,13 +3,13 @@ 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, scan::{Scan, chunk::ChunkScan, product::ProductScan}, schema::Schema, }; -use crate::element::Element; struct MultiBufferProductScanInner { tx: Arc, diff --git a/engine/src/scan/select.rs b/engine/src/scan/select.rs index 40dae1e..b003c32 100644 --- a/engine/src/scan/select.rs +++ b/engine/src/scan/select.rs @@ -1,9 +1,9 @@ use common::DbResult; use std::rc::Rc; +use crate::element::Element; use crate::schema::Schema; use crate::{predicate::Predicate, scan::Scan}; -use crate::element::Element; pub struct SelectScan { scan: Rc, diff --git a/engine/src/view_mgr.rs b/engine/src/view_mgr.rs index 6e97229..6b64c64 100644 --- a/engine/src/view_mgr.rs +++ b/engine/src/view_mgr.rs @@ -3,9 +3,9 @@ use std::sync::Arc; use common::DbResult; use transaction::transaction::Transaction; -use crate::{element::Element, scan::Scan, schema::SchemaBuilder}; 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; From cfe5840ea8007f604f5f92477c8bad921216fa86 Mon Sep 17 00:00:00 2001 From: Daniil Buravlev Date: Tue, 30 Jun 2026 23:48:32 +0300 Subject: [PATCH 13/13] Final fix --- engine/src/element.rs | 4 ++++ engine/src/index.rs | 3 --- engine/src/index_mgr.rs | 5 +++-- engine/src/lib.rs | 14 +++++++------- engine/src/metadata_mgr.rs | 1 + engine/src/plan.rs | 2 +- engine/src/plan/product.rs | 2 +- engine/src/plan/project.rs | 22 ++++++++-------------- engine/src/plan/select.rs | 2 +- engine/src/query.rs | 1 - engine/src/query/basic_planner.rs | 6 +++--- engine/src/query/heuristic_planner.rs | 14 +++++--------- engine/src/query/result.rs | 9 --------- engine/src/scan.rs | 1 - engine/src/scan/group.rs | 2 +- engine/src/scan/index.rs | 4 +--- engine/src/scan/merge.rs | 1 - engine/src/scan/product.rs | 1 - engine/src/scan/project.rs | 1 - engine/src/scan/table.rs | 2 +- engine/src/view_mgr.rs | 5 +++-- 21 files changed, 40 insertions(+), 62 deletions(-) delete mode 100644 engine/src/query/result.rs diff --git a/engine/src/element.rs b/engine/src/element.rs index 3b1f00c..9197fd0 100644 --- a/engine/src/element.rs +++ b/engine/src/element.rs @@ -35,6 +35,10 @@ impl Element { Self::Spec(source, target) => source.len() + 4 + target.len(), } } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } } impl std::fmt::Display for Element { diff --git a/engine/src/index.rs b/engine/src/index.rs index a0205d1..0bae38e 100644 --- a/engine/src/index.rs +++ b/engine/src/index.rs @@ -31,9 +31,6 @@ 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(); diff --git a/engine/src/index_mgr.rs b/engine/src/index_mgr.rs index f509b5e..0752260 100644 --- a/engine/src/index_mgr.rs +++ b/engine/src/index_mgr.rs @@ -72,6 +72,7 @@ impl IndexInfo { } } +#[derive(Clone)] pub struct IndexMgr { layout: Layout, table_mgr: TableMgr, @@ -111,7 +112,7 @@ impl IndexMgr { field_name: &str, tx: &Arc, ) -> DbResult<()> { - let mut ts = TableScan::new(tx, IDX_TABLE, self.layout.clone())?; + let ts = TableScan::new(tx, IDX_TABLE, self.layout.clone())?; ts.insert()?; ts.set_string(&Element::raw(IDX_NAME), idx_name)?; ts.set_string(&Element::raw(TABLE_NAME), table_name)?; @@ -125,7 +126,7 @@ impl IndexMgr { tx: &Arc, ) -> DbResult> { let mut result = HashMap::new(); - let mut ts = TableScan::new(tx, IDX_TABLE, self.layout.clone())?; + let ts = TableScan::new(tx, IDX_TABLE, self.layout.clone())?; while ts.next()? { if ts.get_string(&Element::raw(TABLE_NAME))? == table_name { let idx_name = ts.get_string(&Element::raw(IDX_NAME))?; diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 6c1e40e..f6d402e 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -45,7 +45,7 @@ pub struct SimpleDB { lm: Arc, bm: Arc, lock_table: Arc, - md: Arc, + md: MetadataMgr, } impl SimpleDB { @@ -66,7 +66,7 @@ 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 { fm, @@ -82,8 +82,8 @@ impl SimpleDB { 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> { @@ -98,8 +98,8 @@ 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)) } } @@ -175,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(); diff --git a/engine/src/metadata_mgr.rs b/engine/src/metadata_mgr.rs index 2e20ba9..fb5eade 100644 --- a/engine/src/metadata_mgr.rs +++ b/engine/src/metadata_mgr.rs @@ -13,6 +13,7 @@ use crate::{ view_mgr::ViewMgr, }; +#[derive(Clone)] pub struct MetadataMgr { table_mgr: TableMgr, view_mgr: ViewMgr, diff --git a/engine/src/plan.rs b/engine/src/plan.rs index a004993..9056c35 100644 --- a/engine/src/plan.rs +++ b/engine/src/plan.rs @@ -1,4 +1,4 @@ -use std::{rc::Rc, sync::Arc}; +use std::rc::Rc; use common::DbResult; diff --git a/engine/src/plan/product.rs b/engine/src/plan/product.rs index 3b54cfe..c07081b 100644 --- a/engine/src/plan/product.rs +++ b/engine/src/plan/product.rs @@ -1,4 +1,4 @@ -use std::{rc::Rc, sync::Arc}; +use std::rc::Rc; use common::DbResult; diff --git a/engine/src/plan/project.rs b/engine/src/plan/project.rs index 3f87f58..f3a93a8 100644 --- a/engine/src/plan/project.rs +++ b/engine/src/plan/project.rs @@ -1,8 +1,4 @@ -use std::{ - collections::{HashMap, HashSet}, - rc::Rc, - sync::Arc, -}; +use std::{collections::HashSet, rc::Rc}; use common::DbResult; @@ -17,20 +13,18 @@ use crate::{ pub struct ProjectPlan { plan: Rc, schema: Schema, - mapping: HashMap, } impl ProjectPlan { - pub fn new(plan: Rc, fields: Vec, tables: Vec) -> DbResult { - let schema = SchemaBuilder::default().build(); - //for field in fields { - // let other = plan.schema()?; - // schema.add(field, &other)?; - // } + pub fn new(plan: Rc, fields: Vec) -> DbResult { + let mut schema = SchemaBuilder::default(); + for field in fields { + let other = plan.schema()?; + schema = schema.add(field, &other); + } Ok(Self { plan, - schema, - mapping: HashMap::new(), + schema: schema.build(), }) } } diff --git a/engine/src/plan/select.rs b/engine/src/plan/select.rs index f2a7efa..30b4a5b 100644 --- a/engine/src/plan/select.rs +++ b/engine/src/plan/select.rs @@ -1,4 +1,4 @@ -use std::{rc::Rc, sync::Arc}; +use std::rc::Rc; use common::DbResult; diff --git a/engine/src/query.rs b/engine/src/query.rs index 12696aa..a0a4a89 100644 --- a/engine/src/query.rs +++ b/engine/src/query.rs @@ -4,7 +4,6 @@ pub(crate) mod heuristic_planner; pub mod lexer; pub mod parser; pub mod planner; -pub(crate) mod result; pub(crate) mod table_planner; pub mod token; pub mod tokenizer; diff --git a/engine/src/query/basic_planner.rs b/engine/src/query/basic_planner.rs index 794d4c7..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 } } } diff --git a/engine/src/query/heuristic_planner.rs b/engine/src/query/heuristic_planner.rs index 188818d..ca31744 100644 --- a/engine/src/query/heuristic_planner.rs +++ b/engine/src/query/heuristic_planner.rs @@ -13,14 +13,14 @@ 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, } } @@ -48,11 +48,7 @@ impl HeuristicQueryPlannerInner { if !data.sort_by.is_empty() { current = Rc::new(SortPlan::new(tx, ¤t, data.sort_by.fields)?); } - Ok(Rc::new(ProjectPlan::new( - current, - data.fields, - data.tables, - )?)) + Ok(Rc::new(ProjectPlan::new(current, data.fields)?)) } fn get_lowest_select_plan(&mut self) -> DbResult> { @@ -114,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/result.rs b/engine/src/query/result.rs deleted file mode 100644 index 9a416e5..0000000 --- a/engine/src/query/result.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::rc::Rc; - -use crate::scan::Scan; - -pub struct QueryResult { - scan: Rc, -} - -impl QueryResult {} diff --git a/engine/src/scan.rs b/engine/src/scan.rs index f6fd65c..281233b 100644 --- a/engine/src/scan.rs +++ b/engine/src/scan.rs @@ -1,5 +1,4 @@ use common::{DbResult, error::DbError}; -use std::sync::Arc; use crate::element::Element; use crate::schema::Schema; diff --git a/engine/src/scan/group.rs b/engine/src/scan/group.rs index 5d75052..a0e7cca 100644 --- a/engine/src/scan/group.rs +++ b/engine/src/scan/group.rs @@ -1,4 +1,4 @@ -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}; diff --git a/engine/src/scan/index.rs b/engine/src/scan/index.rs index 677c91c..9bfda45 100644 --- a/engine/src/scan/index.rs +++ b/engine/src/scan/index.rs @@ -1,6 +1,5 @@ use common::DbResult; use std::rc::Rc; -use std::sync::Arc; use crate::element::Element; use crate::schema::{Schema, SchemaBuilder}; @@ -159,11 +158,10 @@ impl Scan for IndexJoinScan { #[cfg(test)] mod tests { use std::collections::HashMap; - use std::sync::Arc; use tempfile::tempdir; use crate::element::Element; - use crate::schema::{Schema, SchemaBuilder}; + use crate::schema::SchemaBuilder; use crate::{ SimpleDB, plan::{Plan, table::TablePlan}, diff --git a/engine/src/scan/merge.rs b/engine/src/scan/merge.rs index ae1ff83..4e3708a 100644 --- a/engine/src/scan/merge.rs +++ b/engine/src/scan/merge.rs @@ -1,5 +1,4 @@ use common::DbResult; -use std::sync::Arc; use std::{cell::RefCell, cmp::Ordering, rc::Rc}; use crate::element::Element; diff --git a/engine/src/scan/product.rs b/engine/src/scan/product.rs index 541e19c..c170f56 100644 --- a/engine/src/scan/product.rs +++ b/engine/src/scan/product.rs @@ -3,7 +3,6 @@ use crate::scan::Scan; use crate::schema::{Schema, SchemaBuilder}; use common::DbResult; use std::rc::Rc; -use std::sync::Arc; pub struct ProductScan { s1: Rc, diff --git a/engine/src/scan/project.rs b/engine/src/scan/project.rs index 42dc0f9..52c9f07 100644 --- a/engine/src/scan/project.rs +++ b/engine/src/scan/project.rs @@ -3,7 +3,6 @@ use crate::scan::Scan; 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 { diff --git a/engine/src/scan/table.rs b/engine/src/scan/table.rs index 0d8d8f6..54aa40f 100644 --- a/engine/src/scan/table.rs +++ b/engine/src/scan/table.rs @@ -254,7 +254,7 @@ impl Scan for TableScan { #[cfg(test)] mod tests { - use crate::schema::{Schema, SchemaBuilder}; + use crate::schema::SchemaBuilder; use buffer::mgr::BufferMgr; use file::mgr::FileMgr; use log::mgr::LogMgr; diff --git a/engine/src/view_mgr.rs b/engine/src/view_mgr.rs index 6b64c64..faa4133 100644 --- a/engine/src/view_mgr.rs +++ b/engine/src/view_mgr.rs @@ -14,6 +14,7 @@ const VIEW_NAME: &str = "view"; const VIEW_DEF: &str = "view_def"; const VIEW_TABLE: &str = "sp_view"; +#[derive(Clone)] pub struct ViewMgr { table_mgr: TableMgr, } @@ -32,7 +33,7 @@ impl ViewMgr { pub fn create_view(&self, name: &str, def: &str, tx: &Arc) -> DbResult<()> { let layout = self.table_mgr.get_layout(VIEW_TABLE, tx)?; - let mut ts = TableScan::new(tx, VIEW_TABLE, layout)?; + let ts = TableScan::new(tx, VIEW_TABLE, layout)?; ts.insert()?; ts.set_string(&Element::raw(VIEW_NAME), name)?; ts.set_string(&Element::raw(VIEW_DEF), def)?; @@ -41,7 +42,7 @@ impl ViewMgr { pub fn get_view_def(&self, name: &str, tx: &Arc) -> DbResult> { let layout = self.table_mgr.get_layout(VIEW_TABLE, tx)?; - let mut ts = TableScan::new(tx, VIEW_TABLE, layout)?; + let ts = TableScan::new(tx, VIEW_TABLE, layout)?; let mut result = None; while ts.next()? { if ts.get_string(&Element::raw(VIEW_NAME))? == name {