From 6647c4b674c7076b0bedab34307387a1a62a2ac1 Mon Sep 17 00:00:00 2001 From: skanehira Date: Thu, 11 Jul 2024 06:40:56 +0900 Subject: [PATCH 01/17] refactor: add comment --- src/record/record_page.rs | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/record/record_page.rs b/src/record/record_page.rs index c49fff1..9eb3ffe 100644 --- a/src/record/record_page.rs +++ b/src/record/record_page.rs @@ -9,6 +9,16 @@ pub enum RecordType { Used, } +impl From for RecordType { + fn from(value: i32) -> Self { + match value { + 0 => RecordType::Empty, + 1 => RecordType::Used, + _ => panic!("invalid record type"), + } + } +} + impl From for i32 { fn from(val: RecordType) -> Self { match val { @@ -50,6 +60,7 @@ impl RecordPage { Self { tx, block, layout } } + /// get_int は指定したスロットにあるフィールドの値を取得する pub fn get_int(&self, slot: i32, field_name: &str) -> Result { let field_pos = self.offset(slot) + self @@ -97,6 +108,12 @@ impl RecordPage { self.set_record_type(slot, RecordType::Empty) } + /// format はレコードページを初期化する + /// 具体的に以下の処理をする + /// - レコードタイプを Empty にする + /// - 各フィールドを初期値で埋める + /// - Integer の場合は 0 + /// - Varchar の場合は空文字 pub fn format(&mut self) -> Result<()> { let mut slot = 0; while self.is_valid_slot(slot) { @@ -135,10 +152,14 @@ impl RecordPage { Ok(()) } + /// next_after は次の使われているスロット番号を返す pub fn next_after(&self, slot: i32) -> i32 { self.search_after(slot, RecordType::Used) } + /// insert_after は指定したスロットのあとに新しい空きスロットを検索して + /// 利用中に変更して、そのスロット番号を返す + /// 空きスロットがない場合は -1 を返す pub fn insert_after(&mut self, slot: i32) -> Result { let new_slot = self.search_after(slot, RecordType::Empty); if new_slot >= 0 { @@ -147,12 +168,16 @@ impl RecordPage { Ok(new_slot) } + /// set_record_type は指定したスロットのレコードタイプを変更する fn set_record_type(&self, slot: i32, record_type: RecordType) -> Result<()> { let offset = self.offset(slot); let mut tx = self.tx.lock().unwrap(); tx.set_int(&self.block, offset, record_type.into(), true) } + /// search_after は指定したスロットの次のスロットから指定したレコードタイプのスロットを検索して + /// スロット番号を返す + /// 見つからない場合は -1 を返す fn search_after(&self, slot: i32, record_type: RecordType) -> i32 { let mut slot = slot + 1; while self.is_valid_slot(slot) { @@ -164,21 +189,21 @@ impl RecordPage { -1 } + /// get_record_type は指定したスロットのレコードタイプを返す fn get_record_type(&self, block: &BlockId, slot: i32) -> RecordType { let offset = self.offset(slot); let mut tx = self.tx.lock().unwrap(); - let record_type = tx.get_int(block, offset); - if record_type == 0 { - RecordType::Empty - } else { - RecordType::Used - } + tx.get_int(block, offset).into() } + /// is_valid_slot は指定したスロットが有効かどうかを返す + /// 有効なスロットとは、スロットの位置がブロックの範囲内に収まっているかどうか pub fn is_valid_slot(&self, slot: i32) -> bool { self.offset(slot + 1) <= self.tx.lock().unwrap().block_size() } + /// offset は指定したスロットのオフセットを返す + /// オフセットはブロックの先頭からの位置を表す pub fn offset(&self, slot: i32) -> i32 { self.layout.slot_size * slot } From 6b8ff9ff4b5caee4beac521d85fa4ef9bb834afe Mon Sep 17 00:00:00 2001 From: skanehira Date: Thu, 11 Jul 2024 06:41:22 +0900 Subject: [PATCH 02/17] wip --- src/lib.rs | 1 + src/query/constant.rs | 4 ++ src/query/mod.rs | 2 + src/query/scan.rs | 22 ++++++++ src/record/mod.rs | 2 + src/record/rid.rs | 17 ++++++ src/record/table_scan.rs | 109 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 157 insertions(+) create mode 100644 src/query/constant.rs create mode 100644 src/query/mod.rs create mode 100644 src/query/scan.rs create mode 100644 src/record/rid.rs create mode 100644 src/record/table_scan.rs diff --git a/src/lib.rs b/src/lib.rs index c5db4f7..74384b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ pub mod log; pub mod record; pub mod server; pub mod tx; +pub mod query; const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(3); const I32_SIZE: usize = size_of::(); diff --git a/src/query/constant.rs b/src/query/constant.rs new file mode 100644 index 0000000..d37e92c --- /dev/null +++ b/src/query/constant.rs @@ -0,0 +1,4 @@ +pub struct Constant { + ival: Option, + sval: Option, +} diff --git a/src/query/mod.rs b/src/query/mod.rs new file mode 100644 index 0000000..04856ce --- /dev/null +++ b/src/query/mod.rs @@ -0,0 +1,2 @@ +pub mod constant; +pub mod scan; diff --git a/src/query/scan.rs b/src/query/scan.rs new file mode 100644 index 0000000..a0c65c2 --- /dev/null +++ b/src/query/scan.rs @@ -0,0 +1,22 @@ +use super::constant::Constant; +use crate::record::rid::RID; + +pub trait Scan { + // for scan + fn before_first(&mut self); + fn next(&mut self) -> bool; + fn get_int(&mut self, field_name: &str) -> i32; + fn get_value(&mut self, fieldname: &str) -> Constant; + fn get_string(&mut self, field_name: &str) -> String; + fn has_field(&self, field_name: &str) -> bool; + fn close(&mut self); + + // for update scan + fn set_val(&mut self, field_name: &str, val: Constant); + fn set_int(&mut self, field_name: &str, val: i32); + fn set_string(&mut self, field_name: &str, val: &str); + fn delete(&mut self); + fn insert(&mut self); + fn get_rid(&self) -> RID; + fn move_to_rid(&mut self, rid: RID); +} diff --git a/src/record/mod.rs b/src/record/mod.rs index 03a2645..f61a20c 100644 --- a/src/record/mod.rs +++ b/src/record/mod.rs @@ -1,3 +1,5 @@ pub mod layout; pub mod record_page; pub mod schema; +pub mod rid; +pub mod table_scan; diff --git a/src/record/rid.rs b/src/record/rid.rs new file mode 100644 index 0000000..34b9b4c --- /dev/null +++ b/src/record/rid.rs @@ -0,0 +1,17 @@ +#[derive(PartialEq, Eq)] +pub struct RID { + pub block_num: i32, + pub slot: i32, +} + +impl RID { + pub fn new(block_num: i32, slot: i32) -> Self { + Self { block_num, slot } + } +} + +impl std::fmt::Display for RID { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "[block {}, slot {}]", self.block_num, self.slot) + } +} diff --git a/src/record/table_scan.rs b/src/record/table_scan.rs new file mode 100644 index 0000000..202c436 --- /dev/null +++ b/src/record/table_scan.rs @@ -0,0 +1,109 @@ +use super::record_page::RecordPage; +use crate::{query::scan::Scan, record::layout::Layout, tx::transaction::Transaction}; +use anyhow::Result; +use std::sync::{Arc, Mutex}; + +pub struct TableScan { + tx: Arc>, + layout: Arc, + rp: Option, + file_name: String, + current_slot: i32, +} + +impl TableScan { + pub fn new( + tx: Arc>, + table_name: String, + layout: Arc, + ) -> Result { + let file_name = table_name + ".tbl"; + let mut scan = Self { + tx: tx.clone(), + layout, + rp: None, + file_name: file_name.clone(), + current_slot: -1, + }; + + if tx.lock().unwrap().size(file_name)? == 0 { + scan.move_to_new_block()? + } else { + scan.move_to_block(0)? + } + Ok(scan) + } + + // ファイルに新しいブロックを追加 + fn move_to_new_block(&mut self) -> Result<()> { + let mut tx = self.tx.lock().unwrap(); + let block = tx.append(self.file_name.clone())?; + let mut rp = RecordPage::new(self.tx.clone(), block, self.layout.clone()); + rp.format()?; + self.rp = Some(rp); + self.current_slot = -1; + Ok(()) + } + + fn move_to_block(&mut self, block_num: i32) -> Result<()> { + todo!() + } +} + +impl Scan for TableScan { + fn before_first(&mut self) { + todo!() + } + + fn next(&mut self) -> bool { + todo!() + } + + fn get_int(&mut self, field_name: &str) -> i32 { + todo!() + } + + fn get_value(&mut self, fieldname: &str) -> crate::query::constant::Constant { + todo!() + } + + fn get_string(&mut self, field_name: &str) -> String { + todo!() + } + + fn has_field(&self, field_name: &str) -> bool { + todo!() + } + + fn close(&mut self) { + todo!() + } + + fn set_val(&mut self, field_name: &str, val: crate::query::constant::Constant) { + todo!() + } + + fn set_int(&mut self, field_name: &str, val: i32) { + todo!() + } + + fn set_string(&mut self, field_name: &str, val: &str) { + todo!() + } + + fn delete(&mut self) { + todo!() + } + + fn insert(&mut self) { + todo!() + } + + fn get_rid(&self) -> super::rid::RID { + todo!() + } + + fn move_to_rid(&mut self, rid: super::rid::RID) { + todo!() + } +} From 72b3b26179a18d09d03be5e59967f12dd6c12726 Mon Sep 17 00:00:00 2001 From: skanehira Date: Thu, 11 Jul 2024 08:35:01 +0900 Subject: [PATCH 03/17] chore: add comments --- src/record/layout.rs | 2 ++ src/record/schema.rs | 21 ++++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/record/layout.rs b/src/record/layout.rs index 2019ee7..5c6ebf5 100644 --- a/src/record/layout.rs +++ b/src/record/layout.rs @@ -43,6 +43,8 @@ impl Layout { }) } + /// offset は指定したフィールドのオフセットを返す + /// オフセットはスキーマの先頭からの位置 pub fn offset(&self, field_name: &str) -> Option { self.offsets.get(field_name).copied() } diff --git a/src/record/schema.rs b/src/record/schema.rs index 2bacef4..531d448 100644 --- a/src/record/schema.rs +++ b/src/record/schema.rs @@ -41,20 +41,28 @@ pub struct Schema { } impl Schema { - pub fn add_field(&mut self, field_name: String, r#type: i32, length: i32) { - let field = FieldInto { r#type, length }; + /// add_field はフィールド名、型、長さを追加する + pub fn add_field(&mut self, field_name: String, r#type: FieldTypes, length: i32) { + let field = FieldInto { + r#type: r#type.into(), + length, + }; self.fields.push(field_name.clone()); self.info.insert(field_name, field); } + /// add_int_field は整数型のフィールドを追加する pub fn add_int_field(&mut self, field_name: String) { - self.add_field(field_name, FieldTypes::Integer.into(), 0); + self.add_field(field_name, FieldTypes::Integer, 0); } + /// add_string_field は文字列型のフィールドを追加する pub fn add_string_field(&mut self, field_name: String, length: i32) { - self.add_field(field_name, FieldTypes::Varchar.into(), length); + self.add_field(field_name, FieldTypes::Varchar, length); } + /// add はスキーマにフィールドを追加する + /// スキーマにフィールドの定義がない場合はエラーを返す pub fn add(&mut self, field_name: String, schema: &Schema) -> Result<()> { let r#type = schema .r#type(&field_name) @@ -62,7 +70,7 @@ impl Schema { let length = schema .length(&field_name) .ok_or(anyhow!("field length not found"))?; - self.add_field(field_name, r#type, length); + self.add_field(field_name, r#type.into(), length); Ok(()) } @@ -73,14 +81,17 @@ impl Schema { Ok(()) } + /// has_field は指定したフィールド名がスキーマに存在するかを返す pub fn has_field(&self, field_name: &str) -> bool { self.info.contains_key(field_name) } + /// r#type は指定したフィールドの型を返す pub fn r#type(&self, field_name: &str) -> Option { self.info.get(field_name)?.r#type.into() } + /// length は指定したフィールドの長さを返す pub fn length(&self, field_name: &str) -> Option { self.info.get(field_name)?.length.into() } From eea9090217dcb1de9341c0d4cb766627bd77f53b Mon Sep 17 00:00:00 2001 From: skanehira Date: Fri, 12 Jul 2024 03:31:05 +0900 Subject: [PATCH 04/17] chore: fix wrong description of the recovery manager --- src/tx/recovery/recovery_manager.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tx/recovery/recovery_manager.rs b/src/tx/recovery/recovery_manager.rs index a593cee..675ec46 100644 --- a/src/tx/recovery/recovery_manager.rs +++ b/src/tx/recovery/recovery_manager.rs @@ -75,9 +75,9 @@ impl RecoveryManager { /// /// file1: /// ```text - /// +----- block 0 -----+-- block N --+ + /// +----- block 0 +------- block N --+ /// | 5 | hello | 10 | ... | - /// +-------------------+-------------+ + /// +--------------+------------------+ /// ``` /// /// file2: @@ -91,9 +91,9 @@ impl RecoveryManager { /// /// file1: /// ```text - /// +------ block 0 ----------+-- block N --+ + /// +------ block 0 -----+------- block N --+ /// | 11 | hello world | 10 | ... | - /// +-------------------------+-------------+ + /// +--------------------+------------------+ /// ``` /// /// file2: From 2c7bad82e9085d2a5d2e9033a9c5b1765ae00cb6 Mon Sep 17 00:00:00 2001 From: skanehira Date: Fri, 12 Jul 2024 04:01:47 +0900 Subject: [PATCH 05/17] refactor: use impl Into for field_name --- src/record/record_page.rs | 14 +++++++------- src/record/schema.rs | 11 ++++++----- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/record/record_page.rs b/src/record/record_page.rs index b33084d..af065de 100644 --- a/src/record/record_page.rs +++ b/src/record/record_page.rs @@ -242,8 +242,8 @@ mod tests { #[test] fn should_can_format() { let mut schema = Schema::default(); - schema.add_int_field("id".into()); - schema.add_string_field("name".into(), 8); + schema.add_int_field("id"); + schema.add_string_field("name", 8); let schema = Arc::new(Mutex::new(schema)); let layout = Arc::new(Layout::try_from_schema(schema.clone()).unwrap()); @@ -265,10 +265,10 @@ mod tests { } #[test] - fn should_can_set_record_date() { + fn should_can_set_record_data() { let mut schema = Schema::default(); - schema.add_int_field("id".into()); - schema.add_string_field("name".into(), 8); + schema.add_int_field("id"); + schema.add_string_field("name", 8); let schema = Arc::new(Mutex::new(schema)); let layout = Arc::new(Layout::try_from_schema(schema.clone()).unwrap()); @@ -290,8 +290,8 @@ mod tests { #[test] fn should_can_delete() { let mut schema = Schema::default(); - schema.add_int_field("id".into()); - schema.add_string_field("name".into(), 8); + schema.add_int_field("id"); + schema.add_string_field("name", 8); let schema = Arc::new(Mutex::new(schema)); let layout = Arc::new(Layout::try_from_schema(schema.clone()).unwrap()); diff --git a/src/record/schema.rs b/src/record/schema.rs index 531d448..54dd2ee 100644 --- a/src/record/schema.rs +++ b/src/record/schema.rs @@ -42,22 +42,23 @@ pub struct Schema { impl Schema { /// add_field はフィールド名、型、長さを追加する - pub fn add_field(&mut self, field_name: String, r#type: FieldTypes, length: i32) { + pub fn add_field(&mut self, field_name: impl Into, r#type: FieldTypes, length: i32) { let field = FieldInto { r#type: r#type.into(), length, }; - self.fields.push(field_name.clone()); - self.info.insert(field_name, field); + let fname = field_name.into(); + self.fields.push(fname.clone()); + self.info.insert(fname, field); } /// add_int_field は整数型のフィールドを追加する - pub fn add_int_field(&mut self, field_name: String) { + pub fn add_int_field(&mut self, field_name: impl Into) { self.add_field(field_name, FieldTypes::Integer, 0); } /// add_string_field は文字列型のフィールドを追加する - pub fn add_string_field(&mut self, field_name: String, length: i32) { + pub fn add_string_field(&mut self, field_name: impl Into, length: i32) { self.add_field(field_name, FieldTypes::Varchar, length); } From cc7915df06c21b38ac2788709344bfb5da5409cf Mon Sep 17 00:00:00 2001 From: skanehira Date: Sat, 13 Jul 2024 20:09:00 +0900 Subject: [PATCH 06/17] impl: table scan --- src/file/file_manager.rs | 1 + src/query/constant.rs | 10 +- src/query/scan.rs | 21 +-- src/record/layout.rs | 24 ++- src/record/record_page.rs | 13 +- src/record/schema.rs | 52 +++---- src/record/table_scan.rs | 173 +++++++++++++++++----- src/tx/concurrency/concurrency_manager.rs | 2 +- src/tx/transaction.rs | 6 + tests/record.rs | 6 +- tests/table_scan.rs | 58 ++++++++ 11 files changed, 265 insertions(+), 101 deletions(-) create mode 100644 tests/table_scan.rs diff --git a/src/file/file_manager.rs b/src/file/file_manager.rs index b549a0a..711d02e 100644 --- a/src/file/file_manager.rs +++ b/src/file/file_manager.rs @@ -77,6 +77,7 @@ impl FileManager { } } + /// append_block 指定したファイルに新しいブロックを追加して、そのブロックのIDを返す pub fn append_block(&mut self, filename: &str) -> Result { let block = BlockId::new(filename.to_string(), self.block_count(filename)? as i32); let offset = block.num * self.block_size; diff --git a/src/query/constant.rs b/src/query/constant.rs index d37e92c..10c4b9d 100644 --- a/src/query/constant.rs +++ b/src/query/constant.rs @@ -1,4 +1,8 @@ -pub struct Constant { - ival: Option, - sval: Option, +pub enum Constant { + Int(i32), + String(String), } +//pub struct Constant { +// ival: Option, +// sval: Option, +//} diff --git a/src/query/scan.rs b/src/query/scan.rs index a0c65c2..e45a70b 100644 --- a/src/query/scan.rs +++ b/src/query/scan.rs @@ -1,22 +1,23 @@ use super::constant::Constant; use crate::record::rid::RID; +use anyhow::Result; pub trait Scan { // for scan fn before_first(&mut self); - fn next(&mut self) -> bool; - fn get_int(&mut self, field_name: &str) -> i32; - fn get_value(&mut self, fieldname: &str) -> Constant; - fn get_string(&mut self, field_name: &str) -> String; + fn next(&mut self) -> Result; + fn get_int(&mut self, field_name: &str) -> Result; + fn get_string(&mut self, field_name: &str) -> Result; + fn get_value(&mut self, fieldname: &str) -> Result; fn has_field(&self, field_name: &str) -> bool; fn close(&mut self); // for update scan - fn set_val(&mut self, field_name: &str, val: Constant); - fn set_int(&mut self, field_name: &str, val: i32); - fn set_string(&mut self, field_name: &str, val: &str); - fn delete(&mut self); - fn insert(&mut self); - fn get_rid(&self) -> RID; + fn set_val(&mut self, field_name: &str, val: Constant) -> Result<()>; + fn set_int(&mut self, field_name: &str, val: i32) -> Result<()>; + fn set_string(&mut self, field_name: &str, val: &str) -> Result<()>; + fn delete(&mut self) -> Result<()>; + fn insert(&mut self) -> Result<()>; + fn get_rid(&mut self) -> Result; fn move_to_rid(&mut self, rid: RID); } diff --git a/src/record/layout.rs b/src/record/layout.rs index 5c6ebf5..015eb2d 100644 --- a/src/record/layout.rs +++ b/src/record/layout.rs @@ -1,28 +1,24 @@ -use crate::I32_SIZE; +use crate::{file::page::Page, I32_SIZE}; use anyhow::{anyhow, Result}; use super::schema::{FieldTypes, Schema}; -use std::{ - collections::HashMap, - sync::{Arc, Mutex}, -}; +use std::{collections::HashMap, sync::Arc}; /// Layout はテーブルレコードのレイアウトを表す /// フィールド名と型、テーブル内の各フィールドのオフセットを保持する pub struct Layout { - pub schema: Arc>, + pub schema: Arc, pub offsets: HashMap, pub slot_size: i32, } impl Layout { - pub fn try_from_schema(schema: Arc>) -> Result { + pub fn try_from_schema(schema: Arc) -> Result { let mut pos = I32_SIZE as i32; let mut offsets = HashMap::new(); - let sch = schema.lock().unwrap(); - for field in &sch.fields { + for field in &schema.fields { offsets.insert(field.clone(), pos); - pos += Self::length_in_bytes(&sch, field)?; + pos += Self::length_in_bytes(&schema, field)?; } Ok(Self { schema: schema.clone(), @@ -32,7 +28,7 @@ impl Layout { } pub fn try_from_metadata( - schema: Arc>, + schema: Arc, offsets: HashMap, slot_size: i32, ) -> Result { @@ -53,13 +49,13 @@ impl Layout { let field_type = schema .r#type(field_name) .ok_or_else(|| anyhow!("field type not found"))?; - match field_type.into() { + match field_type { FieldTypes::Integer => Ok(I32_SIZE as i32), FieldTypes::Varchar => { let length = schema .length(field_name) - .ok_or_else(|| anyhow!("field length not found"))?; - Ok(length) + .ok_or_else(|| anyhow!("field length not found"))? as usize; + Ok(Page::max_length(length) as i32) } } } diff --git a/src/record/record_page.rs b/src/record/record_page.rs index af065de..25e687e 100644 --- a/src/record/record_page.rs +++ b/src/record/record_page.rs @@ -126,7 +126,7 @@ impl RecordPage { false, )?; - let schema = &self.layout.schema.lock().unwrap(); + let schema = &self.layout.schema; for field_name in &schema.fields { // ブロックにあるスロットのオフセット + フィールドのオフセット = フィールドの位置 // フィールドのオフセット自体は変わらないが、ブロックにあるスロットの断片化を防ぐためスロットの位置が調整されることがあるため @@ -139,7 +139,7 @@ impl RecordPage { let field_type = schema .r#type(field_name) .ok_or_else(|| anyhow!("field type not found"))?; - match field_type.into() { + match field_type { FieldTypes::Integer => { tx.set_int(&self.block, field_pos, 0, false)?; } @@ -244,13 +244,14 @@ mod tests { let mut schema = Schema::default(); schema.add_int_field("id"); schema.add_string_field("name", 8); - let schema = Arc::new(Mutex::new(schema)); + let schema = Arc::new(schema); let layout = Arc::new(Layout::try_from_schema(schema.clone()).unwrap()); // 4bytes: record type // 4bytes: id + // 4bytes: name length // 8bytes: name - assert_eq!(layout.slot_size, 16); + assert_eq!(layout.slot_size, 20); let db_dir = tempdir().unwrap(); let tx = new_transaction(db_dir.path()); @@ -269,7 +270,7 @@ mod tests { let mut schema = Schema::default(); schema.add_int_field("id"); schema.add_string_field("name", 8); - let schema = Arc::new(Mutex::new(schema)); + let schema = Arc::new(schema); let layout = Arc::new(Layout::try_from_schema(schema.clone()).unwrap()); let db_dir = tempdir().unwrap(); @@ -292,7 +293,7 @@ mod tests { let mut schema = Schema::default(); schema.add_int_field("id"); schema.add_string_field("name", 8); - let schema = Arc::new(Mutex::new(schema)); + let schema = Arc::new(schema); let layout = Arc::new(Layout::try_from_schema(schema.clone()).unwrap()); let db_dir = tempdir().unwrap(); diff --git a/src/record/schema.rs b/src/record/schema.rs index 54dd2ee..cb9f9a7 100644 --- a/src/record/schema.rs +++ b/src/record/schema.rs @@ -1,34 +1,35 @@ use anyhow::{anyhow, Result}; use std::collections::HashMap; -pub enum FieldTypes { - Integer, - Varchar, -} - /// From java.sql.Types -impl From for i32 { - fn from(value: FieldTypes) -> i32 { - match value { - FieldTypes::Integer => 4, - FieldTypes::Varchar => 12, - } - } +#[derive(Clone, Copy)] +pub enum FieldTypes { + Integer = 4, + Varchar = 12, } -impl From for FieldTypes { - fn from(value: i32) -> FieldTypes { - match value { - 4 => FieldTypes::Integer, - 12 => FieldTypes::Varchar, - _ => unreachable!("unknown type"), - } - } -} +//impl From for i32 { +// fn from(value: FieldTypes) -> i32 { +// match value { +// FieldTypes::Integer => 4, +// FieldTypes::Varchar => 12, +// } +// } +//} +// +//impl From for FieldTypes { +// fn from(value: i32) -> FieldTypes { +// match value { +// 4 => FieldTypes::Integer, +// 12 => FieldTypes::Varchar, +// _ => unreachable!("unknown type"), +// } +// } +//} #[derive(Clone, Copy)] pub struct FieldInto { - r#type: i32, + r#type: FieldTypes, length: i32, } @@ -44,7 +45,7 @@ impl Schema { /// add_field はフィールド名、型、長さを追加する pub fn add_field(&mut self, field_name: impl Into, r#type: FieldTypes, length: i32) { let field = FieldInto { - r#type: r#type.into(), + r#type, length, }; let fname = field_name.into(); @@ -53,6 +54,7 @@ impl Schema { } /// add_int_field は整数型のフィールドを追加する + /// add_fieldのlengthは0だが、integer型の場合長さは固定で4バイトなので、lengthは無視される pub fn add_int_field(&mut self, field_name: impl Into) { self.add_field(field_name, FieldTypes::Integer, 0); } @@ -71,7 +73,7 @@ impl Schema { let length = schema .length(&field_name) .ok_or(anyhow!("field length not found"))?; - self.add_field(field_name, r#type.into(), length); + self.add_field(field_name, r#type, length); Ok(()) } @@ -88,7 +90,7 @@ impl Schema { } /// r#type は指定したフィールドの型を返す - pub fn r#type(&self, field_name: &str) -> Option { + pub fn r#type(&self, field_name: &str) -> Option { self.info.get(field_name)?.r#type.into() } diff --git a/src/record/table_scan.rs b/src/record/table_scan.rs index 202c436..fce0d13 100644 --- a/src/record/table_scan.rs +++ b/src/record/table_scan.rs @@ -1,6 +1,11 @@ -use super::record_page::RecordPage; -use crate::{query::scan::Scan, record::layout::Layout, tx::transaction::Transaction}; -use anyhow::Result; +use super::{record_page::RecordPage, rid::RID, schema::FieldTypes}; +use crate::{ + file::block::BlockId, + query::{constant::Constant, scan::Scan}, + record::layout::Layout, + tx::transaction::Transaction, +}; +use anyhow::{anyhow, bail, Result}; use std::sync::{Arc, Mutex}; pub struct TableScan { @@ -14,10 +19,10 @@ pub struct TableScan { impl TableScan { pub fn new( tx: Arc>, - table_name: String, + table_name: impl Into, layout: Arc, ) -> Result { - let file_name = table_name + ".tbl"; + let file_name = table_name.into() + ".tbl"; let mut scan = Self { tx: tx.clone(), layout, @@ -26,84 +31,174 @@ impl TableScan { current_slot: -1, }; - if tx.lock().unwrap().size(file_name)? == 0 { + let size = tx.lock().unwrap().size(file_name)?; + if size == 0 { scan.move_to_new_block()? } else { - scan.move_to_block(0)? + scan.move_to_block(0); } Ok(scan) } - // ファイルに新しいブロックを追加 + fn record_page(&mut self) -> Result<&mut RecordPage> { + self.rp.as_mut().ok_or(anyhow!("no record page")) + } + + // move_to_new_block はファイルに新しいブロックを追加 fn move_to_new_block(&mut self) -> Result<()> { - let mut tx = self.tx.lock().unwrap(); - let block = tx.append(self.file_name.clone())?; - let mut rp = RecordPage::new(self.tx.clone(), block, self.layout.clone()); + self.close(); + let block_id = { + let mut tx = self.tx.lock().unwrap(); + tx.append(self.file_name.clone())? + }; + let mut rp = RecordPage::new(self.tx.clone(), block_id, self.layout.clone()); rp.format()?; self.rp = Some(rp); self.current_slot = -1; Ok(()) } - fn move_to_block(&mut self, block_num: i32) -> Result<()> { - todo!() + // move_to_block は指定したブロックに移動 + // ブロックへの操作はRecordPageを通して行うので、RecordPageを生成して保持する + fn move_to_block(&mut self, block_num: i32) { + self.close(); + let block_id = BlockId::new(self.file_name.clone(), block_num); + self.rp = Some(RecordPage::new( + self.tx.clone(), + block_id, + self.layout.clone(), + )); + self.current_slot = -1; + } + + /// at_last_block は最後のブロックにいるかどうかを返す + fn at_last_block(&self) -> bool { + let size = self + .tx + .lock() + .unwrap() + .size(self.file_name.clone()) + .unwrap() as i32; + self.rp.as_ref().unwrap().block.num == size - 1 } } impl Scan for TableScan { fn before_first(&mut self) { - todo!() - } + self.move_to_block(0); + } + + fn next(&mut self) -> Result { + loop { + let current_slot = self.current_slot; + self.current_slot = self.record_page()?.next_after(current_slot); + if self.current_slot >= 0 { + break; + } + if self.at_last_block() { + return Ok(false); + } else { + let block_num = self.record_page()?.block.num; + self.move_to_block(block_num + 1); + } + } - fn next(&mut self) -> bool { - todo!() + Ok(true) } - fn get_int(&mut self, field_name: &str) -> i32 { - todo!() + fn get_int(&mut self, field_name: &str) -> Result { + let slot = self.current_slot; + self.record_page()?.get_int(slot, field_name) } - fn get_value(&mut self, fieldname: &str) -> crate::query::constant::Constant { - todo!() + fn get_string(&mut self, field_name: &str) -> Result { + let slot = self.current_slot; + self.record_page()?.get_string(slot, field_name) } - fn get_string(&mut self, field_name: &str) -> String { - todo!() + fn get_value(&mut self, field_name: &str) -> Result { + match self.layout.schema.r#type(field_name) { + Some(FieldTypes::Integer) => { + let val = self.get_int(field_name)?; + Ok(Constant::Int(val)) + } + Some(FieldTypes::Varchar) => { + let val = self.get_string(field_name)?; + Ok(Constant::String(val)) + } + _ => bail!("field type not found: {}", field_name), + } } fn has_field(&self, field_name: &str) -> bool { - todo!() + self.layout.schema.has_field(field_name) } fn close(&mut self) { - todo!() + if let Some(rp) = self.rp.take() { + self.tx.lock().unwrap().unpin(&rp.block); + } } - fn set_val(&mut self, field_name: &str, val: crate::query::constant::Constant) { - todo!() + fn set_val(&mut self, field_name: &str, value: crate::query::constant::Constant) -> Result<()> { + let field_type = self + .layout + .schema + .r#type(field_name) + .ok_or(anyhow!("field type not found"))?; + + match (field_type, value) { + (FieldTypes::Integer, Constant::Int(val)) => self.set_int(field_name, val), + (FieldTypes::Varchar, Constant::String(val)) => self.set_string(field_name, &val), + _ => bail!("type mismatch"), + } } - fn set_int(&mut self, field_name: &str, val: i32) { - todo!() + fn set_int(&mut self, field_name: &str, value: i32) -> Result<()> { + let slot = self.current_slot; + self.record_page()?.set_int(slot, field_name, value) } - fn set_string(&mut self, field_name: &str, val: &str) { - todo!() + fn set_string(&mut self, field_name: &str, value: &str) -> Result<()> { + let slot = self.current_slot; + self.record_page()? + .set_string(slot, field_name, value.into()) } - fn delete(&mut self) { - todo!() + fn delete(&mut self) -> Result<()> { + let slot = self.current_slot; + self.record_page()?.delete(slot) } - fn insert(&mut self) { - todo!() + fn insert(&mut self) -> Result<()> { + loop { + let current_slot = self.current_slot; + self.current_slot = self.record_page()?.insert_after(current_slot)?; + if self.current_slot >= 0 { + return Ok(()); + } + if self.at_last_block() { + self.move_to_new_block()?; + } else { + let block_num = self.record_page()?.block.num; + self.move_to_block(block_num + 1); + } + } } - fn get_rid(&self) -> super::rid::RID { - todo!() + fn get_rid(&mut self) -> Result { + let block_num = self.record_page()?.block.num; + Ok(RID::new(block_num, self.current_slot)) } - fn move_to_rid(&mut self, rid: super::rid::RID) { - todo!() + fn move_to_rid(&mut self, rid: RID) { + self.close(); + let block_id = BlockId::new(self.file_name.clone(), rid.block_num); + self.rp = Some(RecordPage::new( + self.tx.clone(), + block_id, + self.layout.clone(), + )); + self.current_slot = rid.block_num; } } diff --git a/src/tx/concurrency/concurrency_manager.rs b/src/tx/concurrency/concurrency_manager.rs index 15c73b5..acb0d3b 100644 --- a/src/tx/concurrency/concurrency_manager.rs +++ b/src/tx/concurrency/concurrency_manager.rs @@ -62,7 +62,7 @@ impl ConcurrencyManager { /// /// このようなデッドロックを検知するため、共有ロックを取得してから排他ロックを取得する /// 自分以外が握っている共有ロックがある場合、排他ロック時に一度タイムアウトになるまで待機する - /// タイムアウト後はデッドロックとして扱いエラーを返す + /// タイムアウト後はロック待ち失敗タイムアウトエラーを返す pub fn x_lock(&mut self, block: &BlockId) -> Result<()> { if !self.has_x_lock(block) { self.s_lock(block)?; diff --git a/src/tx/transaction.rs b/src/tx/transaction.rs index dcaf2c3..415429a 100644 --- a/src/tx/transaction.rs +++ b/src/tx/transaction.rs @@ -163,14 +163,20 @@ impl Transaction { Ok(()) } + /// size は指定したファイルのブロック数を返す pub fn size(&mut self, filename: String) -> Result { + // 他のトランザクションが同じファイルを変更してブロック数が変わるのを防ぐため + // ダミーブロックを作成して共有ロックを取得する let dummy_block = BlockId::new(filename.clone(), -1); self.concurrency_manager.s_lock(&dummy_block)?; let mut file_manager = self.file_manager.lock().unwrap(); file_manager.block_count(&filename) } + /// append は指定したファイルに新しいブロックを追加して、そのブロックのIDを返す pub fn append(&mut self, filename: String) -> Result { + // 複数のトランザクションが同時に同じファイルにブロックを追加するのを防ぐため + // ダミーブロックを作成して排他ロックを取得する let dummy_block = BlockId::new(filename.clone(), -1); self.concurrency_manager.x_lock(&dummy_block)?; let mut file_manager = self.file_manager.lock().unwrap(); diff --git a/tests/record.rs b/tests/record.rs index 2d0a5b9..95abd6c 100644 --- a/tests/record.rs +++ b/tests/record.rs @@ -1,4 +1,4 @@ -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use tempfile::tempdir; use tinydb::file::block::BlockId; @@ -18,10 +18,10 @@ fn record_test() { let mut schema = Schema::default(); schema.add_int_field("A".to_string()); schema.add_string_field("B".to_string(), 9); - let schema = Arc::new(Mutex::new(schema)); + let schema = Arc::new(schema); let layout = Arc::new(Layout::try_from_schema(schema.clone()).unwrap()); - for field_name in &layout.schema.lock().unwrap().fields { + for field_name in &layout.schema.fields { let offset = layout.offset(field_name).unwrap(); println!("{} has offset {}", field_name, offset); } diff --git a/tests/table_scan.rs b/tests/table_scan.rs new file mode 100644 index 0000000..dc5e2fb --- /dev/null +++ b/tests/table_scan.rs @@ -0,0 +1,58 @@ +use std::sync::Arc; + +use anyhow::Result; +use tempfile::tempdir; +use tinydb::{ + query::scan::Scan as _, + record::{layout::Layout, schema::Schema, table_scan::TableScan}, + server::db::TinyDB, +}; + +#[test] +fn table_scan_test() -> Result<()> { + let test_directory = tempdir()?; + let db = TinyDB::new(test_directory.path(), 400, 8)?; + let tx = db.transaction()?; + + let mut sch = Schema::default(); + sch.add_int_field("A"); + sch.add_string_field("B", 9); + + let layout = Layout::try_from_schema(Arc::new(sch))?; + println!("slot size: {}", layout.slot_size); + + for field_name in layout.schema.fields.iter() { + let offset = layout.offset(field_name).unwrap(); + println!("{} has offset {}", field_name, offset); + } + + println!("Filling the table with 50 random records."); + let mut ts = TableScan::new(tx.clone(), "T", Arc::new(layout))?; + for n in 0..50 { + ts.insert()?; + ts.set_int("A", n)?; + ts.set_string("B", &format!("rec{}", n))?; + } + + println!("Deleting these records, whose A-values are less than 25."); + let mut count = 0; + ts.before_first(); + while ts.next()? { + let a = ts.get_int("A")?; + if a < 25 { + count += 1; + ts.delete()?; + } + } + println!("{} values under 10 were deleted.", count); + + println!("Here are the remaining records."); + ts.before_first(); + while ts.next()? { + let a = ts.get_int("A")?; + assert!(a >= 25, "remaining values should be less than 25"); + } + ts.close(); + tx.lock().unwrap().commit()?; + Ok(()) +} From e0a864fd72a610e9e3dcf001535f0c1e0a32e5c7 Mon Sep 17 00:00:00 2001 From: skanehira Date: Sun, 14 Jul 2024 20:39:35 +0900 Subject: [PATCH 07/17] chore: add table scan unit test --- src/record/table_scan.rs | 58 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/record/table_scan.rs b/src/record/table_scan.rs index fce0d13..27b6178 100644 --- a/src/record/table_scan.rs +++ b/src/record/table_scan.rs @@ -44,7 +44,7 @@ impl TableScan { self.rp.as_mut().ok_or(anyhow!("no record page")) } - // move_to_new_block はファイルに新しいブロックを追加 + // move_to_new_block はファイルに新しいブロックを追加して、そのブロックに移動 fn move_to_new_block(&mut self) -> Result<()> { self.close(); let block_id = { @@ -202,3 +202,59 @@ impl Scan for TableScan { self.current_slot = rid.block_num; } } + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use super::TableScan; + use crate::{ + query::scan::Scan as _, + record::{layout::Layout, schema::Schema}, + server::db::TinyDB, + }; + use anyhow::Result; + use tempfile::tempdir; + + fn create_table_scan() -> Result { + let test_directory = tempdir()?; + let db = TinyDB::new(test_directory.path(), 100, 8)?; + let tx = db.transaction()?; + + let mut sch = Schema::default(); + + // record type: 4 + // int: 4 + // string length: 4 + // string : 8 + // total: 20 + sch.add_int_field("A"); + sch.add_string_field("B", 8); + + let layout = Layout::try_from_schema(Arc::new(sch))?; + + let mut ts = TableScan::new(tx.clone(), "T", Arc::new(layout))?; + for n in 0..50 { + ts.insert()?; + ts.set_int("A", n)?; + ts.set_string("B", &format!("rec{}", n))?; + } + + ts.before_first(); + + Ok(ts) + } + + #[test] + fn should_can_scan_table() -> Result<()> { + let mut ts = create_table_scan()?; + assert_eq!(ts.rp.as_ref().unwrap().block.num, 0); + + for i in 0..50 { + ts.next()?; + assert_eq!(ts.get_int("A")?, i); + assert_eq!(ts.get_string("B")?, format!("rec{}", i)); + } + Ok(()) + } +} From 0213e4c9b0663d8d77faa83d5d69a9ce9d9206e4 Mon Sep 17 00:00:00 2001 From: skanehira Date: Sun, 14 Jul 2024 21:15:57 +0900 Subject: [PATCH 08/17] chore: fix wrong comment about record page --- src/record/record_page.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/record/record_page.rs b/src/record/record_page.rs index 25e687e..d57d15a 100644 --- a/src/record/record_page.rs +++ b/src/record/record_page.rs @@ -42,7 +42,7 @@ impl From for i32 { /// record /// ┏━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━┓ /// ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ -/// │ 1 │ 0 │ 0 │ 0 │ 6 │ 0 │ 0 │ 0 │ h │ e │ l │ l │ o │...│...│...│...│...│...│...│...│...│...│...│...│ +/// │ 1 │ 0 │ 0 │ 0 │ 5 │ 0 │ 0 │ 0 │ h │ e │ l │ l │ o │...│...│...│...│...│...│...│...│...│...│...│...│ /// └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ /// ┗━━━━━━━┳━━━━━━━┻━━━━━━━┳━━━━━━━┻━━━━━━━━━┳━━━━━━━━━┛ /// record type integer varchar(5) From 35f42e8297f7f67c6f59fbb47eeebb1eae71dc63 Mon Sep 17 00:00:00 2001 From: skanehira Date: Sun, 14 Jul 2024 23:44:10 +0900 Subject: [PATCH 09/17] impl: table manager --- src/lib.rs | 3 +- src/metadata/mod.rs | 1 + src/metadata/table_manager.rs | 195 ++++++++++++++++++++++++++++++++++ src/record/schema.rs | 41 ++++--- 4 files changed, 217 insertions(+), 23 deletions(-) create mode 100644 src/metadata/mod.rs create mode 100644 src/metadata/table_manager.rs diff --git a/src/lib.rs b/src/lib.rs index 74384b1..f82b5d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,10 +3,11 @@ use std::mem::size_of; pub mod buffer; pub mod file; pub mod log; +pub mod metadata; +pub mod query; pub mod record; pub mod server; pub mod tx; -pub mod query; const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(3); const I32_SIZE: usize = size_of::(); diff --git a/src/metadata/mod.rs b/src/metadata/mod.rs new file mode 100644 index 0000000..d6f4497 --- /dev/null +++ b/src/metadata/mod.rs @@ -0,0 +1 @@ +pub mod table_manager; diff --git a/src/metadata/table_manager.rs b/src/metadata/table_manager.rs new file mode 100644 index 0000000..de3bfab --- /dev/null +++ b/src/metadata/table_manager.rs @@ -0,0 +1,195 @@ +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + +use crate::{ + query::scan::Scan as _, + record::{layout::Layout, schema::Schema, table_scan::TableScan}, + tx::transaction::Transaction, +}; +use anyhow::Result; + +static MAX_NAME: i32 = 16; + +pub struct TableManager { + /// テーブルごとのメタデータを保持する + /// メタデータは以下となる + /// - テーブル名 + /// - スロット(レコード)のサイズ + table_catlog_layout: Arc, + /// テーブルのカラムごとのメタデータを保持する + /// メタデータは以下となる + /// - テーブル名 + /// - フィールド名(カラム名) + /// - フィールドの種類(FieldTypesの値) + /// - フィールドの長さ + /// - フィールドのオフセット(スロットの先頭からの位置) + field_catlog_layout: Arc, +} + +impl TableManager { + pub fn new(is_new: bool, tx: Arc>) -> Result { + let mut tcs = Schema::default(); + tcs.add_string_field("tblname", MAX_NAME); + tcs.add_int_field("slotsize"); + let table_catlog_layout = Arc::new(Layout::try_from_schema(Arc::new(tcs))?); + + let mut fcs = Schema::default(); + fcs.add_string_field("tblname", MAX_NAME); + fcs.add_string_field("fldname", MAX_NAME); + fcs.add_int_field("type"); + fcs.add_int_field("length"); + fcs.add_int_field("offset"); + let field_catlog_layout = Arc::new(Layout::try_from_schema(Arc::new(fcs))?); + + let mut tm = Self { + table_catlog_layout, + field_catlog_layout, + }; + + if is_new { + tm.create_table("tblcat", tm.table_catlog_layout.schema.clone(), tx.clone())?; + tm.create_table("fldcat", tm.field_catlog_layout.schema.clone(), tx.clone())?; + } + + Ok(tm) + } + + pub fn create_table( + &mut self, + table_name: impl Into, + schema: Arc, + tx: Arc>, + ) -> Result<()> { + let table_name = table_name.into(); + let layout = Arc::new(Layout::try_from_schema(schema)?); + let mut tcat = TableScan::new(tx.clone(), "tblcat", self.table_catlog_layout.clone())?; + tcat.insert()?; + tcat.set_string("tblname", &table_name)?; + tcat.set_int("slotsize", layout.slot_size)?; + tcat.close(); + + let mut fcat = TableScan::new(tx.clone(), "fldcat", self.field_catlog_layout.clone())?; + for field_name in layout.schema.fields.iter() { + fcat.insert()?; + fcat.set_string("tblname", &table_name)?; + fcat.set_string("fldname", field_name)?; + fcat.set_int("type", layout.schema.r#type(field_name).unwrap() as i32)?; + fcat.set_int("length", layout.schema.length(field_name).unwrap())?; + fcat.set_int("offset", layout.offset(field_name).unwrap())?; + } + fcat.close(); + + Ok(()) + } + + pub fn get_layout( + &mut self, + table_name: impl Into, + tx: Arc>, + ) -> Result { + let mut size = -1; + let table_name = table_name.into(); + + let mut tcat = TableScan::new(tx.clone(), "tblcat", self.table_catlog_layout.clone())?; + + while tcat.next()? { + if tcat.get_string("tblname")? == table_name { + size = tcat.get_int("slotsize")?; + break; + } + } + tcat.close(); + + let mut schema = Schema::default(); + let mut offsets: HashMap = HashMap::default(); + + let mut fcat = TableScan::new(tx, "fldcat", self.field_catlog_layout.clone())?; + + while fcat.next()? { + if fcat.get_string("tblname")? == table_name { + let field_name = fcat.get_string("fldname")?; + let field_type = fcat.get_int("type")?; + let length = fcat.get_int("length")?; + let offset = fcat.get_int("offset")?; + schema.add_field(field_name.clone(), field_type.into(), length); + offsets.insert(field_name, offset); + } + } + + fcat.close(); + Layout::try_from_metadata(Arc::new(schema), offsets, size) + } +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use super::TableManager; + use crate::{ + query::scan::Scan as _, + record::{schema::FieldTypes, table_scan::TableScan}, + server::db::TinyDB, + }; + use anyhow::Result; + use tempfile::tempdir; + + #[test] + fn should_can_get_layout() -> Result<()> { + let test_directory = tempdir()?; + let db = TinyDB::new(test_directory.path(), 400, 8)?; + let tx = db.transaction()?; + + let mut table_manager = TableManager::new(true, tx.clone())?; + + let table_catlog_layout = Arc::new(table_manager.get_layout("tblcat", tx.clone())?); + + let mut ts = TableScan::new(tx.clone(), "tblcat", table_catlog_layout.clone())?; + + let wants = vec![("tblcat", 28), ("fldcat", 56)]; + + for want in wants { + ts.next()?; + assert_eq!(ts.get_string("tblname")?, want.0); + assert_eq!(ts.get_int("slotsize")?, want.1); + } + ts.close(); + + let layout = table_manager.get_layout("fldcat", tx.clone())?; + let mut ts = TableScan::new(tx.clone(), "fldcat", Arc::new(layout))?; + + let wants = vec![ + ("tblcat", "tblname", FieldTypes::Varchar, 16, 4), + ("tblcat", "slotsize", FieldTypes::Integer, 0, 24), + ]; + + for want in wants { + ts.next()?; + assert_eq!(ts.get_string("tblname")?, want.0); + assert_eq!(ts.get_string("fldname")?, want.1); + assert_eq!(ts.get_int("type")?, want.2.into()); + assert_eq!(ts.get_int("length")?, want.3); + assert_eq!(ts.get_int("offset")?, want.4); + } + + let wants = vec![ + ("fldcat", "tblname", FieldTypes::Varchar, 16, 4), + ("fldcat", "fldname", FieldTypes::Varchar, 16, 24), + ("fldcat", "type", FieldTypes::Integer, 0, 44), + ("fldcat", "length", FieldTypes::Integer, 0, 48), + ("fldcat", "offset", FieldTypes::Integer, 0, 52), + ]; + + for want in wants { + ts.next()?; + assert_eq!(ts.get_string("tblname")?, want.0); + assert_eq!(ts.get_string("fldname")?, want.1); + assert_eq!(ts.get_int("type")?, want.2.into()); + assert_eq!(ts.get_int("length")?, want.3); + assert_eq!(ts.get_int("offset")?, want.4); + } + Ok(()) + } +} diff --git a/src/record/schema.rs b/src/record/schema.rs index cb9f9a7..791fa75 100644 --- a/src/record/schema.rs +++ b/src/record/schema.rs @@ -8,24 +8,24 @@ pub enum FieldTypes { Varchar = 12, } -//impl From for i32 { -// fn from(value: FieldTypes) -> i32 { -// match value { -// FieldTypes::Integer => 4, -// FieldTypes::Varchar => 12, -// } -// } -//} -// -//impl From for FieldTypes { -// fn from(value: i32) -> FieldTypes { -// match value { -// 4 => FieldTypes::Integer, -// 12 => FieldTypes::Varchar, -// _ => unreachable!("unknown type"), -// } -// } -//} +impl From for i32 { + fn from(value: FieldTypes) -> i32 { + match value { + FieldTypes::Integer => 4, + FieldTypes::Varchar => 12, + } + } +} + +impl From for FieldTypes { + fn from(value: i32) -> FieldTypes { + match value { + 4 => FieldTypes::Integer, + 12 => FieldTypes::Varchar, + _ => unreachable!("unknown type"), + } + } +} #[derive(Clone, Copy)] pub struct FieldInto { @@ -44,10 +44,7 @@ pub struct Schema { impl Schema { /// add_field はフィールド名、型、長さを追加する pub fn add_field(&mut self, field_name: impl Into, r#type: FieldTypes, length: i32) { - let field = FieldInto { - r#type, - length, - }; + let field = FieldInto { r#type, length }; let fname = field_name.into(); self.fields.push(fname.clone()); self.info.insert(fname, field); From b1b9d828685787ec15c483edfb6124ca35ff1f49 Mon Sep 17 00:00:00 2001 From: skanehira Date: Mon, 15 Jul 2024 23:42:38 +0900 Subject: [PATCH 10/17] impl: metadata_manager and stat_manager --- src/metadata/metadata_manager.rs | 0 src/metadata/mod.rs | 3 ++ src/metadata/stat_info.rs | 18 +++++++ src/metadata/stat_manager.rs | 91 ++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 src/metadata/metadata_manager.rs create mode 100644 src/metadata/stat_info.rs create mode 100644 src/metadata/stat_manager.rs diff --git a/src/metadata/metadata_manager.rs b/src/metadata/metadata_manager.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/metadata/mod.rs b/src/metadata/mod.rs index d6f4497..daea2c5 100644 --- a/src/metadata/mod.rs +++ b/src/metadata/mod.rs @@ -1 +1,4 @@ +pub mod metadata_manager; +pub mod stat_info; +pub mod stat_manager; pub mod table_manager; diff --git a/src/metadata/stat_info.rs b/src/metadata/stat_info.rs new file mode 100644 index 0000000..756712c --- /dev/null +++ b/src/metadata/stat_info.rs @@ -0,0 +1,18 @@ +#[derive(Clone)] +pub struct StatInfo { + pub num_blocks: i32, + pub num_records: i32, +} + +impl StatInfo { + pub fn new(num_blocks: i32, num_records: i32) -> Self { + Self { + num_blocks, + num_records, + } + } + + pub fn distinct_values(&self, _field_name: String) -> i32 { + 1 + (self.num_records / 3) + } +} diff --git a/src/metadata/stat_manager.rs b/src/metadata/stat_manager.rs new file mode 100644 index 0000000..5c49301 --- /dev/null +++ b/src/metadata/stat_manager.rs @@ -0,0 +1,91 @@ +use super::{stat_info::StatInfo, table_manager::TableManager}; +use crate::{ + query::scan::Scan, + record::{layout::Layout, table_scan::TableScan}, + tx::transaction::Transaction, +}; +use anyhow::Result; +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + +pub struct StatManager { + table_manager: TableManager, + table_stats: HashMap, + num_calls: i32, +} + +impl StatManager { + pub fn new(table_manager: TableManager, tx: Arc>) -> Result { + let table_stats = HashMap::new(); + let num_calls = 0; + let mut sm = Self { + table_manager, + table_stats, + num_calls, + }; + + sm.refresh_statistics(tx)?; + + Ok(sm) + } + + pub fn get_stat_info( + &mut self, + table_name: String, + layout: Arc, + tx: Arc>, + ) -> Result { + self.num_calls += 1; + if self.num_calls > 100 { + self.refresh_statistics(tx.clone())?; + } + match self.table_stats.get(&table_name) { + Some(stat_info) => Ok(stat_info.clone()), + None => { + let stat_info = self.calc_table_stats(&table_name, layout, tx.clone())?; + self.table_stats.insert(table_name, stat_info.clone()); + Ok(stat_info) + } + } + } + + pub fn refresh_statistics(&mut self, tx: Arc>) -> Result<()> { + self.table_stats = HashMap::new(); + self.num_calls = 0; + + let table_catalog_layout = Arc::new(self.table_manager.get_layout("tblcat", tx.clone())?); + let mut ts = TableScan::new(tx.clone(), "tblcat", table_catalog_layout)?; + + while ts.next()? { + let table_name = ts.get_string("tblname")?; + let layout = Arc::new(self.table_manager.get_layout(&table_name, tx.clone())?); + let stat_info = self.calc_table_stats(&table_name, layout, tx.clone())?; + self.table_stats.insert(table_name, stat_info); + } + ts.close(); + + Ok(()) + } + + fn calc_table_stats( + &mut self, + table_name: impl Into, + layout: Arc, + tx: Arc>, + ) -> Result { + let mut num_records = 0; + let mut num_blocks = 0; + + let mut ts = TableScan::new(tx.clone(), table_name, layout)?; + while ts.next()? { + num_records += 1; + num_blocks = ts.get_rid()?.block_num + 1; + } + ts.close(); + + let stat_info = StatInfo::new(num_blocks, num_records); + Ok(stat_info) + } +} From 2432cd2915e598fe407f90d67a89bafa6472df6b Mon Sep 17 00:00:00 2001 From: skanehira Date: Mon, 15 Jul 2024 23:54:34 +0900 Subject: [PATCH 11/17] chore: fix typo --- src/record/schema.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/record/schema.rs b/src/record/schema.rs index 791fa75..3c64e52 100644 --- a/src/record/schema.rs +++ b/src/record/schema.rs @@ -28,7 +28,7 @@ impl From for FieldTypes { } #[derive(Clone, Copy)] -pub struct FieldInto { +pub struct FieldInfo { r#type: FieldTypes, length: i32, } @@ -38,13 +38,13 @@ pub struct FieldInto { #[derive(Default, Clone)] pub struct Schema { pub fields: Vec, - info: HashMap, + info: HashMap, } impl Schema { /// add_field はフィールド名、型、長さを追加する pub fn add_field(&mut self, field_name: impl Into, r#type: FieldTypes, length: i32) { - let field = FieldInto { r#type, length }; + let field = FieldInfo { r#type, length }; let fname = field_name.into(); self.fields.push(fname.clone()); self.info.insert(fname, field); From c63ae79c887e3f4b8d192ddc45e294efb1fe458b Mon Sep 17 00:00:00 2001 From: skanehira Date: Fri, 19 Jul 2024 00:47:28 +0900 Subject: [PATCH 12/17] impl: index info and index manager --- src/index/hash/mod.rs | 106 ++++++++++++++++++++++++++++++++++ src/index/mod.rs | 13 +++++ src/lib.rs | 1 + src/metadata/index_info.rs | 80 +++++++++++++++++++++++++ src/metadata/index_manager.rs | 0 src/metadata/mod.rs | 2 + src/query/constant.rs | 18 ++++-- src/query/scan.rs | 2 +- src/record/layout.rs | 1 + src/record/table_scan.rs | 2 +- 10 files changed, 219 insertions(+), 6 deletions(-) create mode 100644 src/index/hash/mod.rs create mode 100644 src/index/mod.rs create mode 100644 src/metadata/index_info.rs create mode 100644 src/metadata/index_manager.rs diff --git a/src/index/hash/mod.rs b/src/index/hash/mod.rs new file mode 100644 index 0000000..869b4ad --- /dev/null +++ b/src/index/hash/mod.rs @@ -0,0 +1,106 @@ +use super::Index; +use crate::{ + query::{constant::Constant, scan::Scan as _}, + record::{layout::Layout, rid::RID, table_scan::TableScan}, + tx::transaction::Transaction, +}; +use anyhow::{anyhow, Result}; +use std::sync::{Arc, Mutex}; + +const NUM_BUCKETS: u64 = 100; + +pub struct HashIndex { + tx: Arc>, + index_name: String, + layout: Arc, + search_key: Option, + table_scan: Option, +} + +impl HashIndex { + pub fn new(tx: Arc>, index_name: String, layout: Arc) -> Self { + Self { + tx, + index_name, + layout, + search_key: None, + table_scan: None, + } + } + + pub fn search_cost(num_blocks: u64, _: u64) -> u64 { + num_blocks / NUM_BUCKETS + } +} + +impl Index for HashIndex { + fn before_first(&mut self, search_key: Constant) -> Result<()> { + self.close(); + let hash_code = search_key.hash_code(); + self.search_key = Some(search_key); + + let bucket = hash_code % NUM_BUCKETS; + let table_name = format!("{}{}", self.index_name, bucket); + + self.table_scan = Some(TableScan::new( + self.tx.clone(), + table_name, + self.layout.clone(), + )?); + + Ok(()) + } + + fn next(&mut self) -> Result { + let Some(table_scan) = self.table_scan.as_mut() else { + return Ok(false); + }; + let Some(search_key) = self.search_key.as_ref() else { + return Ok(false); + }; + + while table_scan.next()? { + if table_scan.get_value("dataval")? == *search_key { + return Ok(true); + } + } + Ok(false) + } + + fn get_data_rid(&mut self) -> Result { + let table_scan = self.table_scan.as_mut().ok_or(anyhow!("no table_scan"))?; + let block_num = table_scan.get_int("block")?; + let id = table_scan.get_int("id")?; + Ok(RID::new(block_num, id)) + } + + fn insert(&mut self, data_value: Constant, data_rid: RID) -> Result<()> { + self.before_first(data_value.clone())?; + let table_scan = self.table_scan.as_mut().ok_or(anyhow!("no table_scan"))?; + table_scan.insert()?; + table_scan.set_int("block", data_rid.block_num)?; + table_scan.set_int("id", data_rid.slot)?; + table_scan.set_value("dataval", data_value)?; + todo!() + } + + fn delete(&mut self, data_value: Constant, data_rid: RID) -> Result<()> { + self.before_first(data_value)?; + while self.next()? { + if self.get_data_rid()? == data_rid { + self.table_scan + .as_mut() + .ok_or(anyhow!("no table_scan"))? + .delete()?; + return Ok(()); + } + } + Ok(()) + } + + fn close(&mut self) { + if let Some(table_scan) = self.table_scan.as_mut() { + table_scan.close() + } + } +} diff --git a/src/index/mod.rs b/src/index/mod.rs new file mode 100644 index 0000000..93a35ba --- /dev/null +++ b/src/index/mod.rs @@ -0,0 +1,13 @@ +use crate::{query::constant::Constant, record::rid::RID}; +use anyhow::Result; + +pub mod hash; + +pub trait Index { + fn before_first(&mut self, search_key: Constant) -> Result<()>; + fn next(&mut self) -> Result; + fn get_data_rid(&mut self) -> Result; + fn delete(&mut self, data_value: Constant, data_rid: RID) -> Result<()>; + fn insert(&mut self, data_value: Constant, data_rid: RID) -> Result<()>; + fn close(&mut self); +} diff --git a/src/lib.rs b/src/lib.rs index f82b5d7..1a75f27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ pub mod query; pub mod record; pub mod server; pub mod tx; +pub mod index; const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(3); const I32_SIZE: usize = size_of::(); diff --git a/src/metadata/index_info.rs b/src/metadata/index_info.rs new file mode 100644 index 0000000..a990168 --- /dev/null +++ b/src/metadata/index_info.rs @@ -0,0 +1,80 @@ +use super::stat_info::StatInfo; +use crate::{ + index::hash::HashIndex, + record::{ + layout::Layout, + schema::{FieldTypes, Schema}, + }, + tx::transaction::Transaction, +}; +use anyhow::{bail, Result}; +use std::sync::{Arc, Mutex}; + +pub struct IndexInfo { + index_name: String, + field_name: String, + tx: Arc>, + table_schema: Arc, + index_layout: Arc, + stat_info: StatInfo, +} + +impl IndexInfo { + pub fn new( + index_name: String, + field_name: String, + table_schema: Arc, + tx: Arc>, + stat_info: StatInfo, + ) -> Result { + let mut schema = Schema::default(); + schema.add_int_field("block"); + schema.add_int_field("id"); + match schema.r#type(&field_name) { + Some(FieldTypes::Integer) => { + schema.add_int_field("dataval"); + } + Some(FieldTypes::Varchar) => { + let length = table_schema.length(&field_name).unwrap(); + schema.add_string_field("dataval", length); + } + None => bail!("field not found"), + } + + let index_info = Self { + index_name, + field_name, + tx, + table_schema, + index_layout: Arc::new(Layout::try_from_schema(Arc::new(schema))?), + stat_info, + }; + + Ok(index_info) + } + + pub fn open(&mut self) -> HashIndex { + HashIndex::new( + self.tx.clone(), + self.index_name.clone(), + self.index_layout.clone(), + ) + } + + pub fn blocks_accessed(&self) -> u64 { + let rpb = self.tx.lock().unwrap().block_size() / self.index_layout.slot_size; + let num_blocks = self.stat_info.num_records / rpb; + HashIndex::search_cost(num_blocks as u64, rpb as u64) + } + + pub fn records_output(&self) -> i32 { + self.stat_info.num_records / self.stat_info.distinct_values(self.field_name.clone()) + } + + pub fn distinct_values(&self, field_name: String) -> i32 { + if field_name != self.field_name { + return 1; + } + self.stat_info.num_records / self.stat_info.distinct_values(field_name.clone()) + } +} diff --git a/src/metadata/index_manager.rs b/src/metadata/index_manager.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/metadata/mod.rs b/src/metadata/mod.rs index daea2c5..06e25ba 100644 --- a/src/metadata/mod.rs +++ b/src/metadata/mod.rs @@ -1,3 +1,5 @@ +pub mod index_info; +pub mod index_manager; pub mod metadata_manager; pub mod stat_info; pub mod stat_manager; diff --git a/src/query/constant.rs b/src/query/constant.rs index 10c4b9d..7985e09 100644 --- a/src/query/constant.rs +++ b/src/query/constant.rs @@ -1,8 +1,18 @@ +use std::hash::{DefaultHasher, Hash, Hasher}; + +#[derive(Clone, PartialEq, Eq)] pub enum Constant { Int(i32), String(String), } -//pub struct Constant { -// ival: Option, -// sval: Option, -//} + +impl Constant { + pub fn hash_code(&self) -> u64 { + let mut state = DefaultHasher::new(); + match self { + Constant::Int(i) => i.hash(&mut state), + Constant::String(s) => s.hash(&mut state), + } + state.finish() + } +} diff --git a/src/query/scan.rs b/src/query/scan.rs index e45a70b..2e8da89 100644 --- a/src/query/scan.rs +++ b/src/query/scan.rs @@ -13,7 +13,7 @@ pub trait Scan { fn close(&mut self); // for update scan - fn set_val(&mut self, field_name: &str, val: Constant) -> Result<()>; + fn set_value(&mut self, field_name: &str, val: Constant) -> Result<()>; fn set_int(&mut self, field_name: &str, val: i32) -> Result<()>; fn set_string(&mut self, field_name: &str, val: &str) -> Result<()>; fn delete(&mut self) -> Result<()>; diff --git a/src/record/layout.rs b/src/record/layout.rs index 015eb2d..5f3d17e 100644 --- a/src/record/layout.rs +++ b/src/record/layout.rs @@ -6,6 +6,7 @@ use std::{collections::HashMap, sync::Arc}; /// Layout はテーブルレコードのレイアウトを表す /// フィールド名と型、テーブル内の各フィールドのオフセットを保持する +#[derive(Default)] pub struct Layout { pub schema: Arc, pub offsets: HashMap, diff --git a/src/record/table_scan.rs b/src/record/table_scan.rs index 27b6178..e0e5f52 100644 --- a/src/record/table_scan.rs +++ b/src/record/table_scan.rs @@ -140,7 +140,7 @@ impl Scan for TableScan { } } - fn set_val(&mut self, field_name: &str, value: crate::query::constant::Constant) -> Result<()> { + fn set_value(&mut self, field_name: &str, value: crate::query::constant::Constant) -> Result<()> { let field_type = self .layout .schema From eaa183fd2bec786ba6301ec77bf84b3e544d92f8 Mon Sep 17 00:00:00 2001 From: skanehira Date: Sat, 20 Jul 2024 07:15:35 +0900 Subject: [PATCH 13/17] impl: Drop trait for close table_scan --- src/metadata/stat_manager.rs | 2 -- src/metadata/table_manager.rs | 8 ++------ src/record/table_scan.rs | 12 +++++++++++- tests/table_scan.rs | 1 - 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/metadata/stat_manager.rs b/src/metadata/stat_manager.rs index 5c49301..3165d21 100644 --- a/src/metadata/stat_manager.rs +++ b/src/metadata/stat_manager.rs @@ -64,7 +64,6 @@ impl StatManager { let stat_info = self.calc_table_stats(&table_name, layout, tx.clone())?; self.table_stats.insert(table_name, stat_info); } - ts.close(); Ok(()) } @@ -83,7 +82,6 @@ impl StatManager { num_records += 1; num_blocks = ts.get_rid()?.block_num + 1; } - ts.close(); let stat_info = StatInfo::new(num_blocks, num_records); Ok(stat_info) diff --git a/src/metadata/table_manager.rs b/src/metadata/table_manager.rs index de3bfab..36da4b7 100644 --- a/src/metadata/table_manager.rs +++ b/src/metadata/table_manager.rs @@ -10,8 +10,9 @@ use crate::{ }; use anyhow::Result; -static MAX_NAME: i32 = 16; +pub static MAX_NAME: i32 = 16; +/// TableManager テーブルごとのメタデータを管理する pub struct TableManager { /// テーブルごとのメタデータを保持する /// メタデータは以下となる @@ -68,7 +69,6 @@ impl TableManager { tcat.insert()?; tcat.set_string("tblname", &table_name)?; tcat.set_int("slotsize", layout.slot_size)?; - tcat.close(); let mut fcat = TableScan::new(tx.clone(), "fldcat", self.field_catlog_layout.clone())?; for field_name in layout.schema.fields.iter() { @@ -79,7 +79,6 @@ impl TableManager { fcat.set_int("length", layout.schema.length(field_name).unwrap())?; fcat.set_int("offset", layout.offset(field_name).unwrap())?; } - fcat.close(); Ok(()) } @@ -100,7 +99,6 @@ impl TableManager { break; } } - tcat.close(); let mut schema = Schema::default(); let mut offsets: HashMap = HashMap::default(); @@ -118,7 +116,6 @@ impl TableManager { } } - fcat.close(); Layout::try_from_metadata(Arc::new(schema), offsets, size) } } @@ -155,7 +152,6 @@ mod tests { assert_eq!(ts.get_string("tblname")?, want.0); assert_eq!(ts.get_int("slotsize")?, want.1); } - ts.close(); let layout = table_manager.get_layout("fldcat", tx.clone())?; let mut ts = TableScan::new(tx.clone(), "fldcat", Arc::new(layout))?; diff --git a/src/record/table_scan.rs b/src/record/table_scan.rs index e0e5f52..1805ce6 100644 --- a/src/record/table_scan.rs +++ b/src/record/table_scan.rs @@ -83,6 +83,12 @@ impl TableScan { } } +impl Drop for TableScan { + fn drop(&mut self) { + self.close(); + } +} + impl Scan for TableScan { fn before_first(&mut self) { self.move_to_block(0); @@ -140,7 +146,11 @@ impl Scan for TableScan { } } - fn set_value(&mut self, field_name: &str, value: crate::query::constant::Constant) -> Result<()> { + fn set_value( + &mut self, + field_name: &str, + value: crate::query::constant::Constant, + ) -> Result<()> { let field_type = self .layout .schema diff --git a/tests/table_scan.rs b/tests/table_scan.rs index dc5e2fb..c12bc40 100644 --- a/tests/table_scan.rs +++ b/tests/table_scan.rs @@ -52,7 +52,6 @@ fn table_scan_test() -> Result<()> { let a = ts.get_int("A")?; assert!(a >= 25, "remaining values should be less than 25"); } - ts.close(); tx.lock().unwrap().commit()?; Ok(()) } From 20ff2c3ad95033b2d741b35c205f092c86b557cd Mon Sep 17 00:00:00 2001 From: skanehira Date: Sat, 20 Jul 2024 07:33:47 +0900 Subject: [PATCH 14/17] impl: index manager --- src/lib.rs | 1 + src/metadata/index_manager.rs | 107 ++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 1a75f27..f3f2b92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ use std::mem::size_of; pub mod buffer; pub mod file; +pub mod index; pub mod log; pub mod metadata; pub mod query; diff --git a/src/metadata/index_manager.rs b/src/metadata/index_manager.rs index e69de29..06a849b 100644 --- a/src/metadata/index_manager.rs +++ b/src/metadata/index_manager.rs @@ -0,0 +1,107 @@ +use super::{ + index_info::IndexInfo, + stat_manager::StatManager, + table_manager::{TableManager, MAX_NAME}, +}; +use crate::{ + query::scan::Scan, + record::{layout::Layout, schema::Schema, table_scan::TableScan}, + tx::transaction::Transaction, +}; +use anyhow::Result; +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + +pub struct IndexManager { + layout: Arc, + table_manager: Arc>, + stat_manager: Arc>, +} + +impl IndexManager { + pub fn new( + is_new: bool, + table_manager: Arc>, + stat_manager: Arc>, + tx: Arc>, + ) -> Result { + if is_new { + let mut schema = Schema::default(); + schema.add_string_field("indexname", MAX_NAME); + schema.add_string_field("tablename", MAX_NAME); + schema.add_string_field("fieldname", MAX_NAME); + table_manager + .lock() + .unwrap() + .create_table("idxcat", Arc::new(schema), tx.clone())?; + } + + let layout = Arc::new( + table_manager + .lock() + .unwrap() + .get_layout("idxcat", tx.clone())?, + ); + + Ok(Self { + layout, + table_manager, + stat_manager, + }) + } + + pub fn create_index( + &mut self, + index_name: String, + table_name: String, + field_name: String, + tx: Arc>, + ) -> Result<()> { + let mut ts = TableScan::new(tx, table_name.clone(), self.layout.clone())?; + ts.insert()?; + ts.set_string("indexname", &index_name)?; + ts.set_string("tablename", &table_name)?; + ts.set_string("fieldname", &field_name)?; + Ok(()) + } + + pub fn get_index_info( + &mut self, + table_name: String, + tx: Arc>, + ) -> Result> { + let mut result = HashMap::new(); + + let mut ts = TableScan::new(tx.clone(), table_name.clone(), self.layout.clone())?; + + while ts.next()? { + if ts.get_string("tablename")? == table_name.clone() { + let index_name = ts.get_string("indexname")?; + let field_name = ts.get_string("fieldname")?; + let table_layout = Arc::new( + self.table_manager + .lock() + .unwrap() + .get_layout(table_name.clone(), tx.clone())?, + ); + let table_stat_info = self.stat_manager.lock().unwrap().get_stat_info( + table_name.clone(), + table_layout.clone(), + tx.clone(), + )?; + let index_info = IndexInfo::new( + index_name.clone(), + field_name, + table_layout.schema.clone(), + tx.clone(), + table_stat_info, + )?; + result.insert(index_name, index_info); + } + } + + Ok(result) + } +} From 3a4236cca9168edfc15940f5031c98bdcac72bfc Mon Sep 17 00:00:00 2001 From: skanehira Date: Sat, 20 Jul 2024 07:33:56 +0900 Subject: [PATCH 15/17] impl: view manager --- src/metadata/mod.rs | 1 + src/metadata/view_manager.rs | 78 ++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 src/metadata/view_manager.rs diff --git a/src/metadata/mod.rs b/src/metadata/mod.rs index 06e25ba..1f6c47b 100644 --- a/src/metadata/mod.rs +++ b/src/metadata/mod.rs @@ -4,3 +4,4 @@ pub mod metadata_manager; pub mod stat_info; pub mod stat_manager; pub mod table_manager; +pub mod view_manager; diff --git a/src/metadata/view_manager.rs b/src/metadata/view_manager.rs new file mode 100644 index 0000000..3ec881e --- /dev/null +++ b/src/metadata/view_manager.rs @@ -0,0 +1,78 @@ +use super::table_manager::{TableManager, MAX_NAME}; +use crate::{ + query::scan::Scan as _, + record::{schema::Schema, table_scan::TableScan}, + tx::transaction::Transaction, +}; +use anyhow::Result; +use std::sync::{Arc, Mutex}; + +static MAX_VIEWDEF: i32 = 100; + +pub struct ViewMgr { + table_manager: Arc>, + max_viewdef: i32, +} + +impl ViewMgr { + pub fn new( + is_new: bool, + table_manager: Arc>, + tx: Arc>, + ) -> Result { + let max_viewdef = 100; + if is_new { + let mut sch = Schema::default(); + sch.add_string_field("viewname", MAX_NAME); + sch.add_string_field("viewdef", max_viewdef); + table_manager + .lock() + .unwrap() + .create_table("viewcat", Arc::new(sch), tx.clone())?; + } + Ok(Self { + table_manager, + max_viewdef, + }) + } + + pub fn create_view( + &self, + vname: &str, + vdef: &str, + tx: Arc>, + ) -> Result<()> { + let layout = Arc::new( + self.table_manager + .lock() + .unwrap() + .get_layout("viewcat", tx.clone())?, + ); + let mut ts = TableScan::new(tx, "viewcat", layout)?; + ts.insert()?; + ts.set_string("viewname", vname)?; + ts.set_string("viewdef", vdef)?; + Ok(()) + } + + pub fn get_view_def( + &self, + vname: &str, + tx: Arc>, + ) -> Result> { + let layout = Arc::new( + self.table_manager + .lock() + .unwrap() + .get_layout("viewcat", tx.clone())?, + ); + let mut ts = TableScan::new(tx, "viewcat", layout)?; + while ts.next()? { + if ts.get_string("viewname")? == vname { + let result = ts.get_string("viewdef")?; + return Ok(Some(result)); + } + } + Ok(None) + } +} From 5f5ee5651cac1e5e9b0bb4099df02e08b24e97de Mon Sep 17 00:00:00 2001 From: skanehira Date: Sat, 20 Jul 2024 07:42:45 +0900 Subject: [PATCH 16/17] refactor: add unlock! macros --- src/lib.rs | 2 +- src/macros.rs | 6 ++++++ src/metadata/index_manager.rs | 18 ++++-------------- src/metadata/stat_manager.rs | 13 +++++++++---- src/metadata/view_manager.rs | 33 ++++++--------------------------- 5 files changed, 26 insertions(+), 46 deletions(-) create mode 100644 src/macros.rs diff --git a/src/lib.rs b/src/lib.rs index f3f2b92..2f6755f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,12 +4,12 @@ pub mod buffer; pub mod file; pub mod index; pub mod log; +pub mod macros; pub mod metadata; pub mod query; pub mod record; pub mod server; pub mod tx; -pub mod index; const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(3); const I32_SIZE: usize = size_of::(); diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..9974c0a --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! unlock { + ($e: expr) => {{ + $e.lock().unwrap() + }}; +} diff --git a/src/metadata/index_manager.rs b/src/metadata/index_manager.rs index 06a849b..97cba78 100644 --- a/src/metadata/index_manager.rs +++ b/src/metadata/index_manager.rs @@ -7,6 +7,7 @@ use crate::{ query::scan::Scan, record::{layout::Layout, schema::Schema, table_scan::TableScan}, tx::transaction::Transaction, + unlock, }; use anyhow::Result; use std::{ @@ -32,18 +33,10 @@ impl IndexManager { schema.add_string_field("indexname", MAX_NAME); schema.add_string_field("tablename", MAX_NAME); schema.add_string_field("fieldname", MAX_NAME); - table_manager - .lock() - .unwrap() - .create_table("idxcat", Arc::new(schema), tx.clone())?; + unlock!(table_manager).create_table("idxcat", Arc::new(schema), tx.clone())?; } - let layout = Arc::new( - table_manager - .lock() - .unwrap() - .get_layout("idxcat", tx.clone())?, - ); + let layout = Arc::new(unlock!(table_manager).get_layout("idxcat", tx.clone())?); Ok(Self { layout, @@ -81,10 +74,7 @@ impl IndexManager { let index_name = ts.get_string("indexname")?; let field_name = ts.get_string("fieldname")?; let table_layout = Arc::new( - self.table_manager - .lock() - .unwrap() - .get_layout(table_name.clone(), tx.clone())?, + unlock!(self.table_manager).get_layout(table_name.clone(), tx.clone())?, ); let table_stat_info = self.stat_manager.lock().unwrap().get_stat_info( table_name.clone(), diff --git a/src/metadata/stat_manager.rs b/src/metadata/stat_manager.rs index 3165d21..954fed6 100644 --- a/src/metadata/stat_manager.rs +++ b/src/metadata/stat_manager.rs @@ -3,6 +3,7 @@ use crate::{ query::scan::Scan, record::{layout::Layout, table_scan::TableScan}, tx::transaction::Transaction, + unlock, }; use anyhow::Result; use std::{ @@ -11,13 +12,16 @@ use std::{ }; pub struct StatManager { - table_manager: TableManager, + table_manager: Arc>, table_stats: HashMap, num_calls: i32, } impl StatManager { - pub fn new(table_manager: TableManager, tx: Arc>) -> Result { + pub fn new( + table_manager: Arc>, + tx: Arc>, + ) -> Result { let table_stats = HashMap::new(); let num_calls = 0; let mut sm = Self { @@ -55,12 +59,13 @@ impl StatManager { self.table_stats = HashMap::new(); self.num_calls = 0; - let table_catalog_layout = Arc::new(self.table_manager.get_layout("tblcat", tx.clone())?); + let table_catalog_layout = + Arc::new(unlock!(self.table_manager).get_layout("tblcat", tx.clone())?); let mut ts = TableScan::new(tx.clone(), "tblcat", table_catalog_layout)?; while ts.next()? { let table_name = ts.get_string("tblname")?; - let layout = Arc::new(self.table_manager.get_layout(&table_name, tx.clone())?); + let layout = Arc::new(unlock!(self.table_manager).get_layout(&table_name, tx.clone())?); let stat_info = self.calc_table_stats(&table_name, layout, tx.clone())?; self.table_stats.insert(table_name, stat_info); } diff --git a/src/metadata/view_manager.rs b/src/metadata/view_manager.rs index 3ec881e..fe380e7 100644 --- a/src/metadata/view_manager.rs +++ b/src/metadata/view_manager.rs @@ -3,6 +3,7 @@ use crate::{ query::scan::Scan as _, record::{schema::Schema, table_scan::TableScan}, tx::transaction::Transaction, + unlock, }; use anyhow::Result; use std::sync::{Arc, Mutex}; @@ -25,10 +26,7 @@ impl ViewMgr { let mut sch = Schema::default(); sch.add_string_field("viewname", MAX_NAME); sch.add_string_field("viewdef", max_viewdef); - table_manager - .lock() - .unwrap() - .create_table("viewcat", Arc::new(sch), tx.clone())?; + unlock!(table_manager).create_table("viewcat", Arc::new(sch), tx.clone())?; } Ok(Self { table_manager, @@ -36,18 +34,8 @@ impl ViewMgr { }) } - pub fn create_view( - &self, - vname: &str, - vdef: &str, - tx: Arc>, - ) -> Result<()> { - let layout = Arc::new( - self.table_manager - .lock() - .unwrap() - .get_layout("viewcat", tx.clone())?, - ); + pub fn create_view(&self, vname: &str, vdef: &str, tx: Arc>) -> Result<()> { + let layout = Arc::new(unlock!(self.table_manager).get_layout("viewcat", tx.clone())?); let mut ts = TableScan::new(tx, "viewcat", layout)?; ts.insert()?; ts.set_string("viewname", vname)?; @@ -55,17 +43,8 @@ impl ViewMgr { Ok(()) } - pub fn get_view_def( - &self, - vname: &str, - tx: Arc>, - ) -> Result> { - let layout = Arc::new( - self.table_manager - .lock() - .unwrap() - .get_layout("viewcat", tx.clone())?, - ); + pub fn get_view_def(&self, vname: &str, tx: Arc>) -> Result> { + let layout = Arc::new(unlock!(self.table_manager).get_layout("viewcat", tx.clone())?); let mut ts = TableScan::new(tx, "viewcat", layout)?; while ts.next()? { if ts.get_string("viewname")? == vname { From fe4048bec75d9fde933ee0476550f201c4a10899 Mon Sep 17 00:00:00 2001 From: skanehira Date: Sat, 20 Jul 2024 07:44:36 +0900 Subject: [PATCH 17/17] chore: fix wrong struct name ViewMgr -> ViewManager --- src/metadata/view_manager.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/metadata/view_manager.rs b/src/metadata/view_manager.rs index fe380e7..8e93a4f 100644 --- a/src/metadata/view_manager.rs +++ b/src/metadata/view_manager.rs @@ -10,12 +10,12 @@ use std::sync::{Arc, Mutex}; static MAX_VIEWDEF: i32 = 100; -pub struct ViewMgr { +pub struct ViewManager { table_manager: Arc>, max_viewdef: i32, } -impl ViewMgr { +impl ViewManager { pub fn new( is_new: bool, table_manager: Arc>,