├── .gitignore ├── docs ├── header.png └── yt-devenv.png ├── src ├── lib.rs ├── btree │ ├── meta.rs │ ├── node.rs │ ├── leaf.rs │ └── branch.rs ├── tuple.rs ├── memcmpable.rs ├── bsearch.rs ├── table.rs ├── disk.rs ├── slotted.rs ├── query.rs ├── buffer.rs └── btree.rs ├── Cargo.toml ├── .github └── workflows │ ├── actionlint.yaml │ └── ci.yaml ├── README.md ├── examples ├── btree-query.rs ├── btree-all.rs ├── btree-range.rs ├── btree-large.rs ├── simple-table-all.rs ├── btree-create.rs ├── btree-large-query.rs ├── simple-table-scan.rs ├── simple-table-range.rs ├── table-index.rs ├── simple-table-create.rs ├── simple-table-plan.rs ├── simple-table-exact.rs ├── table-create.rs └── table-large.rs ├── LICENSE └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.btr 3 | *.rly 4 | -------------------------------------------------------------------------------- /docs/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KOBA789/relly/HEAD/docs/header.png -------------------------------------------------------------------------------- /docs/yt-devenv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KOBA789/relly/HEAD/docs/yt-devenv.png -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod bsearch; 2 | pub mod btree; 3 | pub mod buffer; 4 | pub mod disk; 5 | mod memcmpable; 6 | pub mod query; 7 | mod slotted; 8 | pub mod table; 9 | pub mod tuple; 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "relly" 3 | version = "0.1.0" 4 | authors = ["Hidekazu Kobayashi "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | thiserror = "1.0" 11 | anyhow = "1.0" 12 | serde = { version = "1.0", features = ["derive"] } 13 | zerocopy = "0.3" 14 | bincode = "1.3" 15 | 16 | [dev-dependencies] 17 | tempfile = "3.1" 18 | sha-1 = "0.9" 19 | md-5 = "0.9" 20 | -------------------------------------------------------------------------------- /.github/workflows/actionlint.yaml: -------------------------------------------------------------------------------- 1 | name: actionlint 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - ".github/workflows/**" 7 | 8 | jobs: 9 | actionlint: 10 | name: actionlint with reviewdog 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v2 15 | 16 | - name: actionlint 17 | uses: reviewdog/action-actionlint@v1.15.1 18 | with: 19 | github_token: ${{ secrets.GITHUB_TOKEN }} 20 | reporter: github-pr-review 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # relly 2 | 3 | ![](docs/header.png) 4 | 5 | ## relly とは 6 | 7 | relly は RDBMS のしくみを学ぶための小さな RDBMS 実装です。 8 | 9 | 「WEB+DB PRESS Vol. 122『特集3 作って学ぶ RDBMS のしくみ』」の題材として開発されました。 10 | 11 | ## 環境構築 12 | 13 | ビルドするには Rust のツールチェーンが必要です。未セットアップの場合は [Install Rust - Rust Programming Language](https://www.rust-lang.org/tools/install) などを参考にしながらセットアップしてください。 14 | 15 | 入力補完などの便利な機能のセットアップについては、以下の動画で紹介しています。参考にしてください。 16 | 17 | [![](docs/yt-devenv.png)](https://www.youtube.com/watch?v=677kcyyPwJ4) 18 | 19 | [Rust入門者必見!開発環境をセットアップしよう](https://www.youtube.com/watch?v=677kcyyPwJ4) 20 | -------------------------------------------------------------------------------- /src/btree/meta.rs: -------------------------------------------------------------------------------- 1 | use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified}; 2 | 3 | use crate::disk::PageId; 4 | 5 | #[derive(Debug, FromBytes, AsBytes)] 6 | #[repr(C)] 7 | pub struct Header { 8 | pub root_page_id: PageId, 9 | } 10 | 11 | pub struct Meta { 12 | pub header: LayoutVerified, 13 | _unused: B, 14 | } 15 | 16 | impl Meta { 17 | pub fn new(bytes: B) -> Self { 18 | let (header, _unused) = 19 | LayoutVerified::new_from_prefix(bytes).expect("meta page must be aligned"); 20 | Self { header, _unused } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/btree-query.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use relly::btree::{BTree, SearchMode}; 4 | use relly::buffer::{BufferPool, BufferPoolManager}; 5 | use relly::disk::{DiskManager, PageId}; 6 | 7 | fn main() -> Result<()> { 8 | let disk = DiskManager::open("test.btr")?; 9 | let pool = BufferPool::new(10); 10 | let mut bufmgr = BufferPoolManager::new(disk, pool); 11 | 12 | let btree = BTree::new(PageId(0)); 13 | let mut iter = btree.search(&mut bufmgr, SearchMode::Key(b"Hyogo".to_vec()))?; 14 | let (key, value) = iter.next(&mut bufmgr)?.unwrap(); 15 | println!("{:02x?} = {:02x?}", key, value); 16 | Ok(()) 17 | } 18 | -------------------------------------------------------------------------------- /examples/btree-all.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use relly::btree::{BTree, SearchMode}; 4 | use relly::buffer::{BufferPool, BufferPoolManager}; 5 | use relly::disk::{DiskManager, PageId}; 6 | 7 | fn main() -> Result<()> { 8 | let disk = DiskManager::open("test.btr")?; 9 | let pool = BufferPool::new(10); 10 | let mut bufmgr = BufferPoolManager::new(disk, pool); 11 | 12 | let btree = BTree::new(PageId(0)); 13 | let mut iter = btree.search(&mut bufmgr, SearchMode::Start)?; 14 | 15 | while let Some((key, value)) = iter.next(&mut bufmgr)? { 16 | println!("{:02x?} = {:02x?}", key, value); 17 | } 18 | Ok(()) 19 | } 20 | -------------------------------------------------------------------------------- /examples/btree-range.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use relly::btree::{BTree, SearchMode}; 4 | use relly::buffer::{BufferPool, BufferPoolManager}; 5 | use relly::disk::{DiskManager, PageId}; 6 | 7 | fn main() -> Result<()> { 8 | let disk = DiskManager::open("test.btr")?; 9 | let pool = BufferPool::new(10); 10 | let mut bufmgr = BufferPoolManager::new(disk, pool); 11 | 12 | let btree = BTree::new(PageId(0)); 13 | let mut iter = btree.search(&mut bufmgr, SearchMode::Key(b"Gifu".to_vec()))?; 14 | while let Some((key, value)) = iter.next(&mut bufmgr)? { 15 | println!("{:02x?} = {:02x?}", key, value); 16 | } 17 | Ok(()) 18 | } 19 | -------------------------------------------------------------------------------- /examples/btree-large.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use md5::{Digest, Md5}; 3 | 4 | use relly::btree::BTree; 5 | use relly::buffer::{BufferPool, BufferPoolManager}; 6 | use relly::disk::DiskManager; 7 | 8 | const NUM_PAIRS: u32 = 1_000_000; 9 | 10 | fn main() -> Result<()> { 11 | let disk = DiskManager::open("large.btr")?; 12 | let pool = BufferPool::new(100); 13 | let mut bufmgr = BufferPoolManager::new(disk, pool); 14 | 15 | let btree = BTree::create(&mut bufmgr)?; 16 | for i in 1u32..=NUM_PAIRS { 17 | let pkey = i.to_be_bytes(); 18 | let md5 = Md5::digest(&pkey); 19 | btree.insert(&mut bufmgr, &md5[..], &pkey[..])?; 20 | } 21 | bufmgr.flush()?; 22 | 23 | Ok(()) 24 | } 25 | -------------------------------------------------------------------------------- /examples/simple-table-all.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use relly::btree::{BTree, SearchMode}; 4 | use relly::buffer::{BufferPool, BufferPoolManager}; 5 | use relly::disk::{DiskManager, PageId}; 6 | use relly::tuple; 7 | 8 | fn main() -> Result<()> { 9 | let disk = DiskManager::open("simple.rly")?; 10 | let pool = BufferPool::new(10); 11 | let mut bufmgr = BufferPoolManager::new(disk, pool); 12 | 13 | let btree = BTree::new(PageId(0)); 14 | let mut iter = btree.search(&mut bufmgr, SearchMode::Start)?; 15 | 16 | while let Some((key, value)) = iter.next(&mut bufmgr)? { 17 | let mut record = vec![]; 18 | tuple::decode(&key, &mut record); 19 | tuple::decode(&value, &mut record); 20 | println!("{:?}", tuple::Pretty(&record)); 21 | } 22 | Ok(()) 23 | } 24 | -------------------------------------------------------------------------------- /examples/btree-create.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use relly::btree::BTree; 4 | use relly::buffer::{BufferPool, BufferPoolManager}; 5 | use relly::disk::DiskManager; 6 | 7 | fn main() -> Result<()> { 8 | let disk = DiskManager::open("test.btr")?; 9 | let pool = BufferPool::new(10); 10 | let mut bufmgr = BufferPoolManager::new(disk, pool); 11 | 12 | let btree = BTree::create(&mut bufmgr)?; 13 | 14 | btree.insert(&mut bufmgr, b"Kanagawa", b"Yokohama")?; 15 | btree.insert(&mut bufmgr, b"Osaka", b"Osaka")?; 16 | btree.insert(&mut bufmgr, b"Aichi", b"Nagoya")?; 17 | btree.insert(&mut bufmgr, b"Hokkaido", b"Sapporo")?; 18 | btree.insert(&mut bufmgr, b"Fukuoka", b"Fukuoka")?; 19 | btree.insert(&mut bufmgr, b"Hyogo", b"Kobe")?; 20 | 21 | bufmgr.flush()?; 22 | 23 | Ok(()) 24 | } 25 | -------------------------------------------------------------------------------- /examples/btree-large-query.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use relly::btree::{BTree, SearchMode}; 4 | use relly::buffer::{BufferPool, BufferPoolManager}; 5 | use relly::disk::{DiskManager, PageId}; 6 | 7 | fn main() -> Result<()> { 8 | let disk = DiskManager::open("large.btr")?; 9 | let pool = BufferPool::new(10); 10 | let mut bufmgr = BufferPoolManager::new(disk, pool); 11 | 12 | let btree = BTree::new(PageId(0)); 13 | let mut iter = btree.search( 14 | &mut bufmgr, 15 | SearchMode::Key(vec![ 16 | 0xec, 0x2c, 0xdd, 0x0e, 0x4d, 0x0c, 0x94, 0x67, 0x30, 0x58, 0xc7, 0xd7, 0xbe, 0x7b, 17 | 0x85, 0xd2, 18 | ]), 19 | )?; 20 | 21 | let (key, value) = iter.next(&mut bufmgr)?.unwrap(); 22 | println!("{:02x?} = {:02x?}", key, value); 23 | Ok(()) 24 | } 25 | -------------------------------------------------------------------------------- /examples/simple-table-scan.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use relly::btree::{BTree, SearchMode}; 4 | use relly::buffer::{BufferPool, BufferPoolManager}; 5 | use relly::disk::{DiskManager, PageId}; 6 | use relly::tuple; 7 | 8 | fn main() -> Result<()> { 9 | let disk = DiskManager::open("simple.rly")?; 10 | let pool = BufferPool::new(10); 11 | let mut bufmgr = BufferPoolManager::new(disk, pool); 12 | 13 | let btree = BTree::new(PageId(0)); 14 | let mut iter = btree.search(&mut bufmgr, SearchMode::Start)?; 15 | 16 | while let Some((key, value)) = iter.next(&mut bufmgr)? { 17 | let mut record = vec![]; 18 | tuple::decode(&key, &mut record); 19 | tuple::decode(&value, &mut record); 20 | if record[2] == b"Smith" { 21 | println!("{:?}", tuple::Pretty(&record)); 22 | } 23 | } 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /examples/simple-table-range.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use relly::btree::{BTree, SearchMode}; 4 | use relly::buffer::{BufferPool, BufferPoolManager}; 5 | use relly::disk::{DiskManager, PageId}; 6 | use relly::tuple; 7 | 8 | fn main() -> Result<()> { 9 | let disk = DiskManager::open("simple.rly")?; 10 | let pool = BufferPool::new(10); 11 | let mut bufmgr = BufferPoolManager::new(disk, pool); 12 | 13 | let btree = BTree::new(PageId(0)); 14 | let mut search_key = vec![]; 15 | tuple::encode([b"y"].iter(), &mut search_key); 16 | let mut iter = btree.search(&mut bufmgr, SearchMode::Key(search_key))?; 17 | 18 | while let Some((key, value)) = iter.next(&mut bufmgr)? { 19 | let mut record = vec![]; 20 | tuple::decode(&key, &mut record); 21 | tuple::decode(&value, &mut record); 22 | println!("{:?}", tuple::Pretty(&record)); 23 | } 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /examples/table-index.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use relly::buffer::{BufferPool, BufferPoolManager}; 4 | use relly::disk::{DiskManager, PageId}; 5 | use relly::query::{IndexScan, PlanNode, TupleSearchMode}; 6 | use relly::tuple; 7 | 8 | // SELECT * WHERE last_name = 'Smith' 9 | // with index 10 | fn main() -> Result<()> { 11 | let disk = DiskManager::open("table.rly")?; 12 | let pool = BufferPool::new(10); 13 | let mut bufmgr = BufferPoolManager::new(disk, pool); 14 | 15 | let plan = IndexScan { 16 | table_meta_page_id: PageId(0), 17 | index_meta_page_id: PageId(2), 18 | search_mode: TupleSearchMode::Key(&[b"Smith"]), 19 | while_cond: &|skey| skey[0].as_slice() == b"Smith", 20 | }; 21 | let mut exec = plan.start(&mut bufmgr)?; 22 | 23 | while let Some(record) = exec.next(&mut bufmgr)? { 24 | println!("{:?}", tuple::Pretty(&record)); 25 | } 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /examples/simple-table-create.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use relly::buffer::{BufferPool, BufferPoolManager}; 4 | use relly::disk::{DiskManager, PageId}; 5 | use relly::table::SimpleTable; 6 | 7 | fn main() -> Result<()> { 8 | let disk = DiskManager::open("simple.rly")?; 9 | let pool = BufferPool::new(10); 10 | let mut bufmgr = BufferPoolManager::new(disk, pool); 11 | 12 | let mut table = SimpleTable { 13 | meta_page_id: PageId(0), 14 | num_key_elems: 1, 15 | }; 16 | table.create(&mut bufmgr)?; 17 | dbg!(&table); 18 | table.insert(&mut bufmgr, &[b"z", b"Alice", b"Smith"])?; 19 | table.insert(&mut bufmgr, &[b"x", b"Bob", b"Johnson"])?; 20 | table.insert(&mut bufmgr, &[b"y", b"Charlie", b"Williams"])?; 21 | table.insert(&mut bufmgr, &[b"w", b"Dave", b"Miller"])?; 22 | table.insert(&mut bufmgr, &[b"v", b"Eve", b"Brown"])?; 23 | 24 | bufmgr.flush()?; 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /examples/simple-table-plan.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use relly::buffer::{BufferPool, BufferPoolManager}; 4 | use relly::disk::{DiskManager, PageId}; 5 | use relly::query::{Filter, PlanNode, SeqScan, TupleSearchMode}; 6 | use relly::tuple; 7 | 8 | fn main() -> Result<()> { 9 | let disk = DiskManager::open("simple.rly")?; 10 | let pool = BufferPool::new(10); 11 | let mut bufmgr = BufferPoolManager::new(disk, pool); 12 | 13 | let plan = Filter { 14 | cond: &|record| record[1].as_slice() < b"Dave", 15 | inner_plan: &SeqScan { 16 | table_meta_page_id: PageId(0), 17 | search_mode: TupleSearchMode::Key(&[b"w"]), 18 | while_cond: &|pkey| pkey[0].as_slice() < b"z", 19 | }, 20 | }; 21 | let mut exec = plan.start(&mut bufmgr)?; 22 | 23 | while let Some(record) = exec.next(&mut bufmgr)? { 24 | println!("{:?}", tuple::Pretty(&record)); 25 | } 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /examples/simple-table-exact.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use relly::btree::{BTree, SearchMode}; 4 | use relly::buffer::{BufferPool, BufferPoolManager}; 5 | use relly::disk::{DiskManager, PageId}; 6 | use relly::tuple; 7 | 8 | fn main() -> Result<()> { 9 | let disk = DiskManager::open("simple.rly")?; 10 | let pool = BufferPool::new(10); 11 | let mut bufmgr = BufferPoolManager::new(disk, pool); 12 | 13 | let btree = BTree::new(PageId(0)); 14 | let mut search_key = vec![]; 15 | tuple::encode([b"y"].iter(), &mut search_key); 16 | let mut iter = btree.search(&mut bufmgr, SearchMode::Key(search_key))?; 17 | 18 | while let Some((key, value)) = iter.next(&mut bufmgr)? { 19 | let mut record = vec![]; 20 | tuple::decode(&key, &mut record); 21 | if record[0] != b"y" { 22 | break; 23 | } 24 | tuple::decode(&value, &mut record); 25 | println!("{:?}", tuple::Pretty(&record)); 26 | } 27 | Ok(()) 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - wdb 8 | 9 | env: 10 | CARGO_INCREMENTAL: 0 11 | CARGO_NET_RETRY: 10 12 | RUST_BACKTRACE: short 13 | 14 | jobs: 15 | rust: 16 | name: Rust 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v2 22 | 23 | - name: Install Rust toolchain 24 | uses: actions-rs/toolchain@v1 25 | with: 26 | toolchain: stable 27 | profile: minimal 28 | override: true 29 | components: rustfmt, rust-src 30 | 31 | - name: Cache dependencies 32 | uses: Swatinem/rust-cache@v1.3.0 33 | 34 | - name: Clippy 35 | uses: actions-rs/clippy-check@v1.0.7 36 | with: 37 | token: ${{ secrets.GITHUB_TOKEN }} 38 | 39 | - name: Compile 40 | run: cargo test --no-run --locked 41 | 42 | - name: Test 43 | run: cargo test -- --nocapture --quiet 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Hidekazu Kobayashi 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 19 | OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/tuple.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Debug}; 2 | 3 | use crate::memcmpable; 4 | 5 | pub fn encode(elems: impl Iterator>, bytes: &mut Vec) { 6 | elems.for_each(|elem| { 7 | let elem_bytes = elem.as_ref(); 8 | let len = memcmpable::encoded_size(elem_bytes.len()); 9 | bytes.reserve(len); 10 | memcmpable::encode(elem_bytes, bytes); 11 | }); 12 | } 13 | 14 | pub fn decode(bytes: &[u8], elems: &mut Vec>) { 15 | let mut rest = bytes; 16 | while !rest.is_empty() { 17 | let mut elem = vec![]; 18 | memcmpable::decode(&mut rest, &mut elem); 19 | elems.push(elem); 20 | } 21 | } 22 | 23 | pub struct Pretty<'a, T>(pub &'a [T]); 24 | 25 | impl<'a, T: AsRef<[u8]>> Debug for Pretty<'a, T> { 26 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 27 | let mut d = f.debug_tuple("Tuple"); 28 | for elem in self.0 { 29 | let bytes = elem.as_ref(); 30 | match std::str::from_utf8(bytes) { 31 | Ok(s) => { 32 | d.field(&format_args!("{:?} {:02x?}", s, bytes)); 33 | } 34 | Err(_) => { 35 | d.field(&format_args!("{:02x?}", bytes)); 36 | } 37 | } 38 | } 39 | d.finish() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/table-create.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use relly::buffer::{BufferPool, BufferPoolManager}; 4 | use relly::disk::{DiskManager, PageId}; 5 | use relly::table::{Table, UniqueIndex}; 6 | 7 | /* CREATE TABLE 8 | |id |first_name|last_name| 9 | |------|----------|---------| 10 | |z |Alice |Smith | 11 | |x |Bob |Johonson | 12 | |y |Charlie |Williams | 13 | |w |Dave |Miller | 14 | |v |Eve |Brown | 15 | */ 16 | fn main() -> Result<()> { 17 | let disk = DiskManager::open("table.rly")?; 18 | let pool = BufferPool::new(10); 19 | let mut bufmgr = BufferPoolManager::new(disk, pool); 20 | 21 | let mut table = Table { 22 | meta_page_id: PageId::INVALID_PAGE_ID, 23 | num_key_elems: 1, 24 | unique_indices: vec![UniqueIndex { 25 | meta_page_id: PageId::INVALID_PAGE_ID, 26 | skey: vec![2], 27 | }], 28 | }; 29 | table.create(&mut bufmgr)?; 30 | dbg!(&table); 31 | table.insert(&mut bufmgr, &[b"z", b"Alice", b"Smith"])?; 32 | table.insert(&mut bufmgr, &[b"x", b"Bob", b"Johnson"])?; 33 | table.insert(&mut bufmgr, &[b"y", b"Charlie", b"Williams"])?; 34 | table.insert(&mut bufmgr, &[b"w", b"Dave", b"Miller"])?; 35 | table.insert(&mut bufmgr, &[b"v", b"Eve", b"Brown"])?; 36 | 37 | bufmgr.flush()?; 38 | Ok(()) 39 | } 40 | -------------------------------------------------------------------------------- /src/btree/node.rs: -------------------------------------------------------------------------------- 1 | use zerocopy::{AsBytes, ByteSlice, ByteSliceMut, FromBytes, LayoutVerified}; 2 | 3 | use super::branch::Branch; 4 | use super::leaf::Leaf; 5 | 6 | pub const NODE_TYPE_LEAF: [u8; 8] = *b"LEAF "; 7 | pub const NODE_TYPE_BRANCH: [u8; 8] = *b"BRANCH "; 8 | 9 | #[derive(Debug, FromBytes, AsBytes)] 10 | #[repr(C)] 11 | pub struct Header { 12 | pub node_type: [u8; 8], 13 | } 14 | 15 | pub struct Node { 16 | pub header: LayoutVerified, 17 | pub body: B, 18 | } 19 | 20 | impl Node { 21 | pub fn new(bytes: B) -> Self { 22 | let (header, body) = LayoutVerified::new_from_prefix(bytes).expect("node must be aligned"); 23 | Self { header, body } 24 | } 25 | } 26 | 27 | impl Node { 28 | pub fn initialize_as_leaf(&mut self) { 29 | self.header.node_type = NODE_TYPE_LEAF; 30 | } 31 | 32 | pub fn initialize_as_branch(&mut self) { 33 | self.header.node_type = NODE_TYPE_BRANCH; 34 | } 35 | } 36 | 37 | pub enum Body { 38 | Leaf(Leaf), 39 | Branch(Branch), 40 | } 41 | 42 | impl Body { 43 | pub fn new(node_type: [u8; 8], bytes: B) -> Body { 44 | match node_type { 45 | NODE_TYPE_LEAF => Body::Leaf(Leaf::new(bytes)), 46 | NODE_TYPE_BRANCH => Body::Branch(Branch::new(bytes)), 47 | _ => unreachable!(), 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/table-large.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use md5::Md5; 3 | use relly::buffer::{BufferPool, BufferPoolManager}; 4 | use relly::disk::{DiskManager, PageId}; 5 | use relly::table::{Table, UniqueIndex}; 6 | use sha1::{Digest, Sha1}; 7 | 8 | const NUM_ROWS: u32 = 10_000_000; 9 | 10 | // CREATE TABLE 11 | // |id |first_name|last_name| 12 | // |------|----------|---------| 13 | // |z |Alice |Smith | 14 | // |x |Bob |Johonson | 15 | // |y |Charlie |Williams | 16 | // |w |Dave |Miller | 17 | // |v |Eve |Brown | 18 | // |... | | | 19 | // |BE i32|md5(id) |sha1(id) | 20 | fn main() -> Result<()> { 21 | let disk = DiskManager::open("table.rly")?; 22 | let pool = BufferPool::new(1_000_000); 23 | let mut bufmgr = BufferPoolManager::new(disk, pool); 24 | let mut table = Table { 25 | meta_page_id: PageId(0), 26 | num_key_elems: 1, 27 | unique_indices: vec![UniqueIndex { 28 | meta_page_id: PageId::INVALID_PAGE_ID, 29 | skey: vec![2], 30 | }], 31 | }; 32 | table.create(&mut bufmgr)?; 33 | dbg!(&table); 34 | table.insert(&mut bufmgr, &[b"z", b"Alice", b"Smith"])?; 35 | table.insert(&mut bufmgr, &[b"x", b"Bob", b"Johnson"])?; 36 | table.insert(&mut bufmgr, &[b"y", b"Charlie", b"Williams"])?; 37 | table.insert(&mut bufmgr, &[b"w", b"Dave", b"Miller"])?; 38 | table.insert(&mut bufmgr, &[b"v", b"Eve", b"Brown"])?; 39 | for i in 0u32..NUM_ROWS { 40 | let pkey = i.to_be_bytes(); 41 | let md5 = Md5::digest(&pkey); 42 | let sha1 = Sha1::digest(&pkey); 43 | table.insert(&mut bufmgr, &[&pkey[..], &md5[..], &sha1[..]])?; 44 | } 45 | bufmgr.flush()?; 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /src/memcmpable.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | const ESCAPE_LENGTH: usize = 9; 4 | 5 | pub fn encoded_size(len: usize) -> usize { 6 | (len + (ESCAPE_LENGTH - 1)) / (ESCAPE_LENGTH - 1) * ESCAPE_LENGTH 7 | } 8 | 9 | pub fn encode(mut src: &[u8], dst: &mut Vec) { 10 | loop { 11 | let copy_len = cmp::min(ESCAPE_LENGTH - 1, src.len()); 12 | dst.extend_from_slice(&src[0..copy_len]); 13 | src = &src[copy_len..]; 14 | if src.is_empty() { 15 | let pad_size = ESCAPE_LENGTH - 1 - copy_len; 16 | if pad_size > 0 { 17 | dst.resize(dst.len() + pad_size, 0); 18 | } 19 | dst.push(copy_len as u8); 20 | break; 21 | } 22 | dst.push(ESCAPE_LENGTH as u8); 23 | } 24 | } 25 | 26 | pub fn decode(src: &mut &[u8], dst: &mut Vec) { 27 | loop { 28 | let extra = src[ESCAPE_LENGTH - 1]; 29 | let len = cmp::min(ESCAPE_LENGTH - 1, extra as usize); 30 | dst.extend_from_slice(&src[..len]); 31 | *src = &src[ESCAPE_LENGTH..]; 32 | if extra < ESCAPE_LENGTH as u8 { 33 | break; 34 | } 35 | } 36 | } 37 | 38 | #[cfg(test)] 39 | mod tests { 40 | use super::*; 41 | 42 | #[test] 43 | fn test() { 44 | let org1 = b"helloworld!memcmpable"; 45 | let org2 = b"foobarbazhogehuga"; 46 | 47 | let mut enc = vec![]; 48 | encode(org1, &mut enc); 49 | encode(org2, &mut enc); 50 | 51 | let mut rest = &enc[..]; 52 | 53 | let mut dec1 = vec![]; 54 | decode(&mut rest, &mut dec1); 55 | assert_eq!(org1, dec1.as_slice()); 56 | let mut dec2 = vec![]; 57 | decode(&mut rest, &mut dec2); 58 | assert_eq!(org2, dec2.as_slice()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/bsearch.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering::{self, Greater, Less}; 2 | 3 | #[rustfmt::skip] 4 | /* 5 | * This is originated in Rust core library: 6 | * https://github.com/rust-lang/rust/blob/b01026de465d5a5ef51e32c1012c43927d2a111c/library/core/src/slice/mod.rs#L2186 7 | * 8 | * Permission is hereby granted, free of charge, to any 9 | * person obtaining a copy of this software and associated 10 | * documentation files (the "Software"), to deal in the 11 | * Software without restriction, including without 12 | * limitation the rights to use, copy, modify, merge, 13 | * publish, distribute, sublicense, and/or sell copies of 14 | * the Software, and to permit persons to whom the Software 15 | * is furnished to do so, subject to the following 16 | * conditions: 17 | 18 | * The above copyright notice and this permission notice 19 | * shall be included in all copies or substantial portions 20 | * of the Software. 21 | 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 23 | * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 24 | * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 25 | * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 26 | * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 27 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 29 | * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 30 | * DEALINGS IN THE SOFTWARE. 31 | */ 32 | pub fn binary_search_by(mut size: usize, mut f: F) -> Result 33 | where 34 | F: FnMut(usize) -> Ordering, 35 | { 36 | let mut left = 0; 37 | let mut right = size; 38 | while left < right { 39 | let mid = left + size / 2; 40 | let cmp = f(mid); 41 | if cmp == Less { 42 | left = mid + 1; 43 | } else if cmp == Greater { 44 | right = mid; 45 | } else { 46 | return Ok(mid); 47 | } 48 | size = right - left; 49 | } 50 | Err(left) 51 | } 52 | 53 | #[cfg(test)] 54 | mod tests { 55 | use super::binary_search_by; 56 | 57 | #[test] 58 | fn test() { 59 | let a = vec![1, 2, 3, 5, 8, 13, 21]; 60 | assert_eq!(Ok(0), binary_search_by(a.len(), |idx| a[idx].cmp(&1))); 61 | assert_eq!(Err(0), binary_search_by(a.len(), |idx| a[idx].cmp(&0))); 62 | assert_eq!(Ok(1), binary_search_by(a.len(), |idx| a[idx].cmp(&2))); 63 | assert_eq!(Ok(4), binary_search_by(a.len(), |idx| a[idx].cmp(&8))); 64 | assert_eq!(Err(4), binary_search_by(a.len(), |idx| a[idx].cmp(&6))); 65 | assert_eq!(Ok(6), binary_search_by(a.len(), |idx| a[idx].cmp(&21))); 66 | assert_eq!(Err(7), binary_search_by(a.len(), |idx| a[idx].cmp(&22))); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/table.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::btree::BTree; 4 | use crate::buffer::BufferPoolManager; 5 | use crate::disk::PageId; 6 | use crate::tuple; 7 | 8 | #[derive(Debug)] 9 | pub struct SimpleTable { 10 | pub meta_page_id: PageId, 11 | pub num_key_elems: usize, 12 | } 13 | 14 | impl SimpleTable { 15 | pub fn create(&mut self, bufmgr: &mut BufferPoolManager) -> Result<()> { 16 | let btree = BTree::create(bufmgr)?; 17 | self.meta_page_id = btree.meta_page_id; 18 | Ok(()) 19 | } 20 | 21 | pub fn insert(&self, bufmgr: &mut BufferPoolManager, record: &[&[u8]]) -> Result<()> { 22 | let btree = BTree::new(self.meta_page_id); 23 | let mut key = vec![]; 24 | tuple::encode(record[..self.num_key_elems].iter(), &mut key); 25 | let mut value = vec![]; 26 | tuple::encode(record[self.num_key_elems..].iter(), &mut value); 27 | btree.insert(bufmgr, &key, &value)?; 28 | Ok(()) 29 | } 30 | } 31 | 32 | #[derive(Debug)] 33 | pub struct Table { 34 | pub meta_page_id: PageId, 35 | pub num_key_elems: usize, 36 | pub unique_indices: Vec, 37 | } 38 | 39 | impl Table { 40 | pub fn create(&mut self, bufmgr: &mut BufferPoolManager) -> Result<()> { 41 | let btree = BTree::create(bufmgr)?; 42 | self.meta_page_id = btree.meta_page_id; 43 | for unique_index in &mut self.unique_indices { 44 | unique_index.create(bufmgr)?; 45 | } 46 | Ok(()) 47 | } 48 | 49 | pub fn insert(&self, bufmgr: &mut BufferPoolManager, record: &[&[u8]]) -> Result<()> { 50 | let btree = BTree::new(self.meta_page_id); 51 | let mut key = vec![]; 52 | tuple::encode(record[..self.num_key_elems].iter(), &mut key); 53 | let mut value = vec![]; 54 | tuple::encode(record[self.num_key_elems..].iter(), &mut value); 55 | btree.insert(bufmgr, &key, &value)?; 56 | for unique_index in &self.unique_indices { 57 | unique_index.insert(bufmgr, &key, record)?; 58 | } 59 | Ok(()) 60 | } 61 | } 62 | 63 | #[derive(Debug)] 64 | pub struct UniqueIndex { 65 | pub meta_page_id: PageId, 66 | pub skey: Vec, 67 | } 68 | 69 | impl UniqueIndex { 70 | pub fn create(&mut self, bufmgr: &mut BufferPoolManager) -> Result<()> { 71 | let btree = BTree::create(bufmgr)?; 72 | self.meta_page_id = btree.meta_page_id; 73 | Ok(()) 74 | } 75 | 76 | pub fn insert( 77 | &self, 78 | bufmgr: &mut BufferPoolManager, 79 | pkey: &[u8], 80 | record: &[impl AsRef<[u8]>], 81 | ) -> Result<()> { 82 | let btree = BTree::new(self.meta_page_id); 83 | let mut skey = vec![]; 84 | tuple::encode( 85 | self.skey.iter().map(|&index| record[index].as_ref()), 86 | &mut skey, 87 | ); 88 | btree.insert(bufmgr, &skey, pkey)?; 89 | Ok(()) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/disk.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryInto; 2 | use std::fs::{File, OpenOptions}; 3 | use std::io::{self, prelude::*, SeekFrom}; 4 | use std::path::Path; 5 | 6 | use zerocopy::{AsBytes, FromBytes}; 7 | 8 | pub const PAGE_SIZE: usize = 4096; 9 | 10 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, FromBytes, AsBytes)] 11 | #[repr(C)] 12 | pub struct PageId(pub u64); 13 | impl PageId { 14 | pub const INVALID_PAGE_ID: PageId = PageId(u64::MAX); 15 | 16 | pub fn valid(self) -> Option { 17 | if self == Self::INVALID_PAGE_ID { 18 | None 19 | } else { 20 | Some(self) 21 | } 22 | } 23 | 24 | pub fn to_u64(self) -> u64 { 25 | self.0 26 | } 27 | } 28 | 29 | impl Default for PageId { 30 | fn default() -> Self { 31 | Self::INVALID_PAGE_ID 32 | } 33 | } 34 | 35 | impl From> for PageId { 36 | fn from(page_id: Option) -> Self { 37 | page_id.unwrap_or_default() 38 | } 39 | } 40 | 41 | impl From<&[u8]> for PageId { 42 | fn from(bytes: &[u8]) -> Self { 43 | let arr = bytes.try_into().unwrap(); 44 | PageId(u64::from_ne_bytes(arr)) 45 | } 46 | } 47 | 48 | pub struct DiskManager { 49 | heap_file: File, 50 | next_page_id: u64, 51 | } 52 | 53 | impl DiskManager { 54 | pub fn new(heap_file: File) -> io::Result { 55 | let heap_file_size = heap_file.metadata()?.len(); 56 | let next_page_id = heap_file_size / PAGE_SIZE as u64; 57 | Ok(Self { 58 | heap_file, 59 | next_page_id, 60 | }) 61 | } 62 | 63 | pub fn open(heap_file_path: impl AsRef) -> io::Result { 64 | let heap_file = OpenOptions::new() 65 | .read(true) 66 | .write(true) 67 | .create(true) 68 | .open(heap_file_path)?; 69 | Self::new(heap_file) 70 | } 71 | 72 | pub fn read_page_data(&mut self, page_id: PageId, data: &mut [u8]) -> io::Result<()> { 73 | let offset = PAGE_SIZE as u64 * page_id.to_u64(); 74 | self.heap_file.seek(SeekFrom::Start(offset))?; 75 | self.heap_file.read_exact(data) 76 | } 77 | 78 | pub fn write_page_data(&mut self, page_id: PageId, data: &[u8]) -> io::Result<()> { 79 | let offset = PAGE_SIZE as u64 * page_id.to_u64(); 80 | self.heap_file.seek(SeekFrom::Start(offset))?; 81 | self.heap_file.write_all(data) 82 | } 83 | 84 | pub fn allocate_page(&mut self) -> PageId { 85 | let page_id = self.next_page_id; 86 | self.next_page_id += 1; 87 | PageId(page_id) 88 | } 89 | 90 | pub fn sync(&mut self) -> io::Result<()> { 91 | self.heap_file.flush()?; 92 | self.heap_file.sync_all() 93 | } 94 | } 95 | 96 | #[cfg(test)] 97 | mod tests { 98 | use super::*; 99 | use tempfile::NamedTempFile; 100 | 101 | #[test] 102 | fn test() { 103 | let (data_file, data_file_path) = NamedTempFile::new().unwrap().into_parts(); 104 | let mut disk = DiskManager::new(data_file).unwrap(); 105 | let mut hello = Vec::with_capacity(PAGE_SIZE); 106 | hello.extend_from_slice(b"hello"); 107 | hello.resize(PAGE_SIZE, 0); 108 | let hello_page_id = disk.allocate_page(); 109 | disk.write_page_data(hello_page_id, &hello).unwrap(); 110 | let mut world = Vec::with_capacity(PAGE_SIZE); 111 | world.extend_from_slice(b"world"); 112 | world.resize(PAGE_SIZE, 0); 113 | let world_page_id = disk.allocate_page(); 114 | disk.write_page_data(world_page_id, &world).unwrap(); 115 | drop(disk); 116 | let mut disk2 = DiskManager::open(&data_file_path).unwrap(); 117 | let mut buf = vec![0; PAGE_SIZE]; 118 | disk2.read_page_data(hello_page_id, &mut buf).unwrap(); 119 | assert_eq!(hello, buf); 120 | disk2.read_page_data(world_page_id, &mut buf).unwrap(); 121 | assert_eq!(world, buf); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/slotted.rs: -------------------------------------------------------------------------------- 1 | use std::mem::size_of; 2 | use std::ops::{Index, IndexMut, Range}; 3 | 4 | use zerocopy::{AsBytes, ByteSlice, ByteSliceMut, FromBytes, LayoutVerified}; 5 | 6 | #[derive(Debug, FromBytes, AsBytes)] 7 | #[repr(C)] 8 | pub struct Header { 9 | num_slots: u16, 10 | free_space_offset: u16, 11 | _pad: u32, 12 | } 13 | 14 | #[derive(Debug, FromBytes, AsBytes, Clone, Copy)] 15 | #[repr(C)] 16 | pub struct Pointer { 17 | offset: u16, 18 | len: u16, 19 | } 20 | 21 | impl Pointer { 22 | fn range(&self) -> Range { 23 | let start = self.offset as usize; 24 | let end = start + self.len as usize; 25 | start..end 26 | } 27 | } 28 | 29 | pub type Pointers = LayoutVerified; 30 | 31 | pub struct Slotted { 32 | header: LayoutVerified, 33 | body: B, 34 | } 35 | 36 | impl Slotted { 37 | pub fn new(bytes: B) -> Self { 38 | let (header, body) = 39 | LayoutVerified::new_from_prefix(bytes).expect("slotted header must be aligned"); 40 | Self { header, body } 41 | } 42 | 43 | pub fn capacity(&self) -> usize { 44 | self.body.len() 45 | } 46 | 47 | pub fn num_slots(&self) -> usize { 48 | self.header.num_slots as usize 49 | } 50 | 51 | pub fn free_space(&self) -> usize { 52 | self.header.free_space_offset as usize - self.pointers_size() 53 | } 54 | 55 | fn pointers_size(&self) -> usize { 56 | size_of::() * self.num_slots() 57 | } 58 | 59 | fn pointers(&self) -> Pointers<&[u8]> { 60 | Pointers::new_slice(&self.body[..self.pointers_size()]).unwrap() 61 | } 62 | 63 | fn data(&self, pointer: Pointer) -> &[u8] { 64 | &self.body[pointer.range()] 65 | } 66 | } 67 | 68 | impl Slotted { 69 | pub fn initialize(&mut self) { 70 | self.header.num_slots = 0; 71 | self.header.free_space_offset = self.body.len() as u16; 72 | } 73 | 74 | fn pointers_mut(&mut self) -> Pointers<&mut [u8]> { 75 | let pointers_size = self.pointers_size(); 76 | Pointers::new_slice(&mut self.body[..pointers_size]).unwrap() 77 | } 78 | 79 | fn data_mut(&mut self, pointer: Pointer) -> &mut [u8] { 80 | &mut self.body[pointer.range()] 81 | } 82 | 83 | pub fn insert(&mut self, index: usize, len: usize) -> Option<()> { 84 | if self.free_space() < size_of::() + len { 85 | return None; 86 | } 87 | let num_slots_orig = self.num_slots(); 88 | self.header.free_space_offset -= len as u16; 89 | self.header.num_slots += 1; 90 | let free_space_offset = self.header.free_space_offset; 91 | let mut pointers_mut = self.pointers_mut(); 92 | pointers_mut.copy_within(index..num_slots_orig, index + 1); 93 | let pointer = &mut pointers_mut[index]; 94 | pointer.offset = free_space_offset; 95 | pointer.len = len as u16; 96 | Some(()) 97 | } 98 | 99 | pub fn remove(&mut self, index: usize) { 100 | self.resize(index, 0); 101 | self.pointers_mut().copy_within(index + 1.., index); 102 | self.header.num_slots -= 1; 103 | } 104 | 105 | pub fn resize(&mut self, index: usize, len_new: usize) -> Option<()> { 106 | let pointers = self.pointers(); 107 | let len_orig = pointers[index].len; 108 | let len_incr = len_new as isize - len_orig as isize; 109 | if len_incr == 0 { 110 | return Some(()); 111 | } 112 | if len_incr > self.free_space() as isize { 113 | return None; 114 | } 115 | let free_space_offset = self.header.free_space_offset as usize; 116 | let offset_orig = pointers[index].offset; 117 | let shift_range = free_space_offset..offset_orig as usize; 118 | let free_space_offset_new = (free_space_offset as isize - len_incr) as usize; 119 | self.header.free_space_offset = free_space_offset_new as u16; 120 | self.body 121 | .as_bytes_mut() 122 | .copy_within(shift_range, free_space_offset_new); 123 | let mut pointers_mut = self.pointers_mut(); 124 | for pointer in pointers_mut.iter_mut() { 125 | if pointer.offset <= offset_orig { 126 | pointer.offset = (pointer.offset as isize - len_incr) as u16; 127 | } 128 | } 129 | let pointer = &mut pointers_mut[index]; 130 | pointer.len = len_new as u16; 131 | if len_new == 0 { 132 | pointer.offset = free_space_offset_new as u16; 133 | } 134 | Some(()) 135 | } 136 | } 137 | 138 | impl Index for Slotted { 139 | type Output = [u8]; 140 | 141 | fn index(&self, index: usize) -> &Self::Output { 142 | self.data(self.pointers()[index]) 143 | } 144 | } 145 | 146 | impl IndexMut for Slotted { 147 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 148 | self.data_mut(self.pointers()[index]) 149 | } 150 | } 151 | 152 | #[cfg(test)] 153 | mod tests { 154 | use super::*; 155 | 156 | #[test] 157 | fn test() { 158 | let mut page_data = vec![0u8; 128]; 159 | let mut slotted = Slotted::new(page_data.as_mut_slice()); 160 | let insert = |slotted: &mut Slotted<&mut [u8]>, index: usize, buf: &[u8]| { 161 | slotted.insert(index, buf.len()).unwrap(); 162 | slotted[index].copy_from_slice(buf); 163 | }; 164 | let push = |slotted: &mut Slotted<&mut [u8]>, buf: &[u8]| { 165 | let index = slotted.num_slots() as usize; 166 | insert(slotted, index, buf); 167 | }; 168 | slotted.initialize(); 169 | push(&mut slotted, b"hello"); 170 | push(&mut slotted, b"world"); 171 | assert_eq!(&slotted[0], b"hello"); 172 | assert_eq!(&slotted[1], b"world"); 173 | insert(&mut slotted, 1, b", "); 174 | push(&mut slotted, b"!"); 175 | assert_eq!(&slotted[0], b"hello"); 176 | assert_eq!(&slotted[1], b", "); 177 | assert_eq!(&slotted[2], b"world"); 178 | assert_eq!(&slotted[3], b"!"); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/query.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | 3 | use crate::btree::{self, BTree, SearchMode}; 4 | use crate::buffer::BufferPoolManager; 5 | use crate::disk::PageId; 6 | use crate::tuple; 7 | 8 | pub type Tuple = Vec>; 9 | pub type TupleSlice<'a> = &'a [Vec]; 10 | 11 | pub enum TupleSearchMode<'a> { 12 | Start, 13 | Key(&'a [&'a [u8]]), 14 | } 15 | 16 | impl<'a> TupleSearchMode<'a> { 17 | fn encode(&self) -> SearchMode { 18 | match self { 19 | TupleSearchMode::Start => SearchMode::Start, 20 | TupleSearchMode::Key(tuple) => { 21 | let mut key = vec![]; 22 | tuple::encode(tuple.iter(), &mut key); 23 | SearchMode::Key(key) 24 | } 25 | } 26 | } 27 | } 28 | 29 | pub trait Executor { 30 | fn next(&mut self, bufmgr: &mut BufferPoolManager) -> Result>; 31 | } 32 | 33 | pub type BoxExecutor<'a> = Box; 34 | 35 | pub trait PlanNode { 36 | fn start(&self, bufmgr: &mut BufferPoolManager) -> Result; 37 | } 38 | 39 | pub struct SeqScan<'a> { 40 | pub table_meta_page_id: PageId, 41 | pub search_mode: TupleSearchMode<'a>, 42 | pub while_cond: &'a dyn Fn(TupleSlice) -> bool, 43 | } 44 | 45 | impl<'a> PlanNode for SeqScan<'a> { 46 | fn start(&self, bufmgr: &mut BufferPoolManager) -> Result { 47 | let btree = BTree::new(self.table_meta_page_id); 48 | let table_iter = btree.search(bufmgr, self.search_mode.encode())?; 49 | Ok(Box::new(ExecSeqScan { 50 | table_iter, 51 | while_cond: self.while_cond, 52 | })) 53 | } 54 | } 55 | 56 | pub struct ExecSeqScan<'a> { 57 | table_iter: btree::Iter, 58 | while_cond: &'a dyn Fn(TupleSlice) -> bool, 59 | } 60 | 61 | impl<'a> Executor for ExecSeqScan<'a> { 62 | fn next(&mut self, bufmgr: &mut BufferPoolManager) -> Result> { 63 | let (pkey_bytes, tuple_bytes) = match self.table_iter.next(bufmgr)? { 64 | Some(pair) => pair, 65 | None => return Ok(None), 66 | }; 67 | let mut pkey = vec![]; 68 | tuple::decode(&pkey_bytes, &mut pkey); 69 | if !(self.while_cond)(&pkey) { 70 | return Ok(None); 71 | } 72 | let mut tuple = pkey; 73 | tuple::decode(&tuple_bytes, &mut tuple); 74 | Ok(Some(tuple)) 75 | } 76 | } 77 | 78 | pub struct Filter<'a> { 79 | pub inner_plan: &'a dyn PlanNode, 80 | pub cond: &'a dyn Fn(TupleSlice) -> bool, 81 | } 82 | 83 | impl<'a> PlanNode for Filter<'a> { 84 | fn start(&self, bufmgr: &mut BufferPoolManager) -> Result { 85 | let inner_iter = self.inner_plan.start(bufmgr)?; 86 | Ok(Box::new(ExecFilter { 87 | inner_iter, 88 | cond: self.cond, 89 | })) 90 | } 91 | } 92 | 93 | pub struct ExecFilter<'a> { 94 | inner_iter: BoxExecutor<'a>, 95 | cond: &'a dyn Fn(TupleSlice) -> bool, 96 | } 97 | 98 | impl<'a> Executor for ExecFilter<'a> { 99 | fn next(&mut self, bufmgr: &mut BufferPoolManager) -> Result> { 100 | loop { 101 | match self.inner_iter.next(bufmgr)? { 102 | Some(tuple) => { 103 | if (self.cond)(&tuple) { 104 | return Ok(Some(tuple)); 105 | } 106 | } 107 | None => return Ok(None), 108 | } 109 | } 110 | } 111 | } 112 | 113 | pub struct IndexScan<'a> { 114 | pub table_meta_page_id: PageId, 115 | pub index_meta_page_id: PageId, 116 | pub search_mode: TupleSearchMode<'a>, 117 | pub while_cond: &'a dyn Fn(TupleSlice) -> bool, 118 | } 119 | 120 | impl<'a> PlanNode for IndexScan<'a> { 121 | fn start(&self, bufmgr: &mut BufferPoolManager) -> Result { 122 | let table_btree = BTree::new(self.table_meta_page_id); 123 | let index_btree = BTree::new(self.index_meta_page_id); 124 | let index_iter = index_btree.search(bufmgr, self.search_mode.encode())?; 125 | Ok(Box::new(ExecIndexScan { 126 | table_btree, 127 | index_iter, 128 | while_cond: self.while_cond, 129 | })) 130 | } 131 | } 132 | 133 | pub struct ExecIndexScan<'a> { 134 | table_btree: BTree, 135 | index_iter: btree::Iter, 136 | while_cond: &'a dyn Fn(TupleSlice) -> bool, 137 | } 138 | 139 | impl<'a> Executor for ExecIndexScan<'a> { 140 | fn next(&mut self, bufmgr: &mut BufferPoolManager) -> Result> { 141 | let (skey_bytes, pkey_bytes) = match self.index_iter.next(bufmgr)? { 142 | Some(pair) => pair, 143 | None => return Ok(None), 144 | }; 145 | let mut skey = vec![]; 146 | tuple::decode(&skey_bytes, &mut skey); 147 | if !(self.while_cond)(&skey) { 148 | return Ok(None); 149 | } 150 | let mut table_iter = self 151 | .table_btree 152 | .search(bufmgr, SearchMode::Key(pkey_bytes))?; 153 | let (pkey_bytes, tuple_bytes) = table_iter.next(bufmgr)?.unwrap(); 154 | let mut tuple = vec![]; 155 | tuple::decode(&pkey_bytes, &mut tuple); 156 | tuple::decode(&tuple_bytes, &mut tuple); 157 | Ok(Some(tuple)) 158 | } 159 | } 160 | 161 | pub struct IndexOnlyScan<'a> { 162 | pub index_meta_page_id: PageId, 163 | pub search_mode: TupleSearchMode<'a>, 164 | pub while_cond: &'a dyn Fn(TupleSlice) -> bool, 165 | } 166 | 167 | impl<'a> PlanNode for IndexOnlyScan<'a> { 168 | fn start(&self, bufmgr: &mut BufferPoolManager) -> Result { 169 | let btree = BTree::new(self.index_meta_page_id); 170 | let index_iter = btree.search(bufmgr, self.search_mode.encode())?; 171 | Ok(Box::new(ExecIndexOnlyScan { 172 | index_iter, 173 | while_cond: self.while_cond, 174 | })) 175 | } 176 | } 177 | 178 | pub struct ExecIndexOnlyScan<'a> { 179 | index_iter: btree::Iter, 180 | while_cond: &'a dyn Fn(TupleSlice) -> bool, 181 | } 182 | 183 | impl<'a> Executor for ExecIndexOnlyScan<'a> { 184 | fn next(&mut self, bufmgr: &mut BufferPoolManager) -> Result> { 185 | let (skey_bytes, pkey_bytes) = match self.index_iter.next(bufmgr)? { 186 | Some(pair) => pair, 187 | None => return Ok(None), 188 | }; 189 | let mut skey = vec![]; 190 | tuple::decode(&skey_bytes, &mut skey); 191 | if !(self.while_cond)(&skey) { 192 | return Ok(None); 193 | } 194 | let mut tuple = skey; 195 | tuple::decode(&pkey_bytes, &mut tuple); 196 | Ok(Some(tuple)) 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/btree/leaf.rs: -------------------------------------------------------------------------------- 1 | use std::mem::size_of; 2 | 3 | use zerocopy::{AsBytes, ByteSlice, ByteSliceMut, FromBytes, LayoutVerified}; 4 | 5 | use super::Pair; 6 | use crate::bsearch::binary_search_by; 7 | use crate::disk::PageId; 8 | use crate::slotted::{self, Slotted}; 9 | 10 | #[derive(Debug, FromBytes, AsBytes)] 11 | #[repr(C)] 12 | pub struct Header { 13 | prev_page_id: PageId, 14 | next_page_id: PageId, 15 | } 16 | 17 | pub struct Leaf { 18 | header: LayoutVerified, 19 | body: Slotted, 20 | } 21 | 22 | impl Leaf { 23 | pub fn new(bytes: B) -> Self { 24 | let (header, body) = 25 | LayoutVerified::new_from_prefix(bytes).expect("leaf header must be aligned"); 26 | let body = Slotted::new(body); 27 | Self { header, body } 28 | } 29 | 30 | pub fn prev_page_id(&self) -> Option { 31 | self.header.prev_page_id.valid() 32 | } 33 | 34 | pub fn next_page_id(&self) -> Option { 35 | self.header.next_page_id.valid() 36 | } 37 | 38 | pub fn num_pairs(&self) -> usize { 39 | self.body.num_slots() 40 | } 41 | 42 | pub fn search_slot_id(&self, key: &[u8]) -> Result { 43 | binary_search_by(self.num_pairs(), |slot_id| { 44 | self.pair_at(slot_id).key.cmp(key) 45 | }) 46 | } 47 | 48 | #[cfg(test)] 49 | pub fn search_pair(&self, key: &[u8]) -> Option { 50 | let slot_id = self.search_slot_id(key).ok()?; 51 | Some(self.pair_at(slot_id)) 52 | } 53 | 54 | pub fn pair_at(&self, slot_id: usize) -> Pair { 55 | Pair::from_bytes(&self.body[slot_id]) 56 | } 57 | 58 | pub fn max_pair_size(&self) -> usize { 59 | self.body.capacity() / 2 - size_of::() 60 | } 61 | } 62 | 63 | impl Leaf { 64 | pub fn initialize(&mut self) { 65 | self.header.prev_page_id = PageId::INVALID_PAGE_ID; 66 | self.header.next_page_id = PageId::INVALID_PAGE_ID; 67 | self.body.initialize(); 68 | } 69 | 70 | pub fn set_prev_page_id(&mut self, prev_page_id: Option) { 71 | self.header.prev_page_id = prev_page_id.into() 72 | } 73 | 74 | pub fn set_next_page_id(&mut self, next_page_id: Option) { 75 | self.header.next_page_id = next_page_id.into() 76 | } 77 | 78 | #[must_use = "insertion may fail"] 79 | pub fn insert(&mut self, slot_id: usize, key: &[u8], value: &[u8]) -> Option<()> { 80 | let pair = Pair { key, value }; 81 | let pair_bytes = pair.to_bytes(); 82 | assert!(pair_bytes.len() <= self.max_pair_size()); 83 | self.body.insert(slot_id, pair_bytes.len())?; 84 | self.body[slot_id].copy_from_slice(&pair_bytes); 85 | Some(()) 86 | } 87 | 88 | fn is_half_full(&self) -> bool { 89 | 2 * self.body.free_space() < self.body.capacity() 90 | } 91 | 92 | pub fn split_insert( 93 | &mut self, 94 | new_leaf: &mut Leaf, 95 | new_key: &[u8], 96 | new_value: &[u8], 97 | ) -> Vec { 98 | new_leaf.initialize(); 99 | loop { 100 | if new_leaf.is_half_full() { 101 | let index = self 102 | .search_slot_id(new_key) 103 | .expect_err("key must be unique"); 104 | self.insert(index, new_key, new_value) 105 | .expect("old leaf must have space"); 106 | break; 107 | } 108 | if self.pair_at(0).key < new_key { 109 | self.transfer(new_leaf); 110 | } else { 111 | new_leaf 112 | .insert(new_leaf.num_pairs(), new_key, new_value) 113 | .expect("new leaf must have space"); 114 | while !new_leaf.is_half_full() { 115 | self.transfer(new_leaf); 116 | } 117 | break; 118 | } 119 | } 120 | self.pair_at(0).key.to_vec() 121 | } 122 | 123 | pub fn transfer(&mut self, dest: &mut Leaf) { 124 | let next_index = dest.num_pairs(); 125 | assert!(dest.body.insert(next_index, self.body[0].len()).is_some()); 126 | dest.body[next_index].copy_from_slice(&self.body[0]); 127 | self.body.remove(0); 128 | } 129 | } 130 | 131 | #[cfg(test)] 132 | mod tests { 133 | use super::*; 134 | 135 | #[test] 136 | fn test_leaf_insert() { 137 | let mut page_data = vec![0; 100]; 138 | let mut leaf_page = Leaf::new(page_data.as_mut_slice()); 139 | leaf_page.initialize(); 140 | 141 | let id = leaf_page.search_slot_id(b"deadbeef").unwrap_err(); 142 | assert_eq!(0, id); 143 | leaf_page.insert(id, b"deadbeef", b"world").unwrap(); 144 | assert_eq!(b"deadbeef", leaf_page.pair_at(0).key); 145 | 146 | let id = leaf_page.search_slot_id(b"facebook").unwrap_err(); 147 | assert_eq!(1, id); 148 | leaf_page.insert(id, b"facebook", b"!").unwrap(); 149 | assert_eq!(b"deadbeef", leaf_page.pair_at(0).key); 150 | assert_eq!(b"facebook", leaf_page.pair_at(1).key); 151 | 152 | let id = leaf_page.search_slot_id(b"beefdead").unwrap_err(); 153 | assert_eq!(0, id); 154 | leaf_page.insert(id, b"beefdead", b"hello").unwrap(); 155 | assert_eq!(b"beefdead", leaf_page.pair_at(0).key); 156 | assert_eq!(b"deadbeef", leaf_page.pair_at(1).key); 157 | assert_eq!(b"facebook", leaf_page.pair_at(2).key); 158 | assert_eq!( 159 | &b"hello"[..], 160 | leaf_page.search_pair(b"beefdead").unwrap().value 161 | ); 162 | } 163 | 164 | #[test] 165 | fn test_leaf_split_insert() { 166 | let mut page_data = vec![0; 62]; 167 | let mut leaf_page = Leaf::new(page_data.as_mut_slice()); 168 | leaf_page.initialize(); 169 | let id = leaf_page.search_slot_id(b"deadbeef").unwrap_err(); 170 | leaf_page.insert(id, b"deadbeef", b"world").unwrap(); 171 | let id = leaf_page.search_slot_id(b"facebook").unwrap_err(); 172 | leaf_page.insert(id, b"facebook", b"!").unwrap(); 173 | let id = leaf_page.search_slot_id(b"beefdead").unwrap_err(); 174 | assert!(leaf_page.insert(id, b"beefdead", b"hello").is_none()); 175 | 176 | let mut leaf_page = Leaf::new(page_data.as_mut_slice()); 177 | let mut new_page_data = vec![0; 62]; 178 | let mut new_leaf_page = Leaf::new(new_page_data.as_mut_slice()); 179 | leaf_page.split_insert(&mut new_leaf_page, b"beefdead", b"hello"); 180 | assert_eq!( 181 | &b"world"[..], 182 | new_leaf_page.search_pair(b"deadbeef").unwrap().value 183 | ); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/btree/branch.rs: -------------------------------------------------------------------------------- 1 | use std::mem::size_of; 2 | 3 | use zerocopy::{AsBytes, ByteSlice, ByteSliceMut, FromBytes, LayoutVerified}; 4 | 5 | use super::Pair; 6 | use crate::bsearch::binary_search_by; 7 | use crate::disk::PageId; 8 | use crate::slotted::{self, Slotted}; 9 | 10 | #[derive(Debug, FromBytes, AsBytes)] 11 | #[repr(C)] 12 | pub struct Header { 13 | right_child: PageId, 14 | } 15 | 16 | pub struct Branch { 17 | header: LayoutVerified, 18 | body: Slotted, 19 | } 20 | 21 | impl Branch { 22 | pub fn new(bytes: B) -> Self { 23 | let (header, body) = 24 | LayoutVerified::new_from_prefix(bytes).expect("branch header must be aligned"); 25 | let body = Slotted::new(body); 26 | Self { header, body } 27 | } 28 | 29 | pub fn num_pairs(&self) -> usize { 30 | self.body.num_slots() 31 | } 32 | 33 | pub fn search_slot_id(&self, key: &[u8]) -> Result { 34 | binary_search_by(self.num_pairs(), |slot_id| { 35 | self.pair_at(slot_id).key.cmp(key) 36 | }) 37 | } 38 | 39 | pub fn search_child(&self, key: &[u8]) -> PageId { 40 | let child_idx = self.search_child_idx(key); 41 | self.child_at(child_idx) 42 | } 43 | 44 | pub fn search_child_idx(&self, key: &[u8]) -> usize { 45 | match self.search_slot_id(key) { 46 | Ok(slot_id) => slot_id + 1, 47 | Err(slot_id) => slot_id, 48 | } 49 | } 50 | 51 | pub fn child_at(&self, child_idx: usize) -> PageId { 52 | if child_idx == self.num_pairs() { 53 | self.header.right_child 54 | } else { 55 | self.pair_at(child_idx).value.into() 56 | } 57 | } 58 | 59 | pub fn pair_at(&self, slot_id: usize) -> Pair { 60 | Pair::from_bytes(&self.body[slot_id]) 61 | } 62 | 63 | pub fn max_pair_size(&self) -> usize { 64 | self.body.capacity() / 2 - size_of::() 65 | } 66 | } 67 | 68 | impl Branch { 69 | pub fn initialize(&mut self, key: &[u8], left_child: PageId, right_child: PageId) { 70 | self.body.initialize(); 71 | self.insert(0, key, left_child) 72 | .expect("new leaf must have space"); 73 | self.header.right_child = right_child; 74 | } 75 | 76 | pub fn fill_right_child(&mut self) -> Vec { 77 | let last_id = self.num_pairs() - 1; 78 | let Pair { key, value } = self.pair_at(last_id); 79 | let right_child: PageId = value.into(); 80 | let key_vec = key.to_vec(); 81 | self.body.remove(last_id); 82 | self.header.right_child = right_child; 83 | key_vec 84 | } 85 | 86 | #[must_use = "insertion may fail"] 87 | pub fn insert(&mut self, slot_id: usize, key: &[u8], page_id: PageId) -> Option<()> { 88 | let pair = Pair { 89 | key, 90 | value: page_id.as_bytes(), 91 | }; 92 | let pair_bytes = pair.to_bytes(); 93 | assert!(pair_bytes.len() <= self.max_pair_size()); 94 | self.body.insert(slot_id, pair_bytes.len())?; 95 | self.body[slot_id].copy_from_slice(&pair_bytes); 96 | Some(()) 97 | } 98 | 99 | fn is_half_full(&self) -> bool { 100 | 2 * self.body.free_space() < self.body.capacity() 101 | } 102 | 103 | pub fn split_insert( 104 | &mut self, 105 | new_branch: &mut Branch, 106 | new_key: &[u8], 107 | new_page_id: PageId, 108 | ) -> Vec { 109 | new_branch.body.initialize(); 110 | loop { 111 | if new_branch.is_half_full() { 112 | let index = self 113 | .search_slot_id(new_key) 114 | .expect_err("key must be unique"); 115 | self.insert(index, new_key, new_page_id) 116 | .expect("old branch must have space"); 117 | break; 118 | } 119 | if self.pair_at(0).key < new_key { 120 | self.transfer(new_branch); 121 | } else { 122 | new_branch 123 | .insert(new_branch.num_pairs(), new_key, new_page_id) 124 | .expect("new branch must have space"); 125 | while !new_branch.is_half_full() { 126 | self.transfer(new_branch); 127 | } 128 | break; 129 | } 130 | } 131 | new_branch.fill_right_child() 132 | } 133 | 134 | pub fn transfer(&mut self, dest: &mut Branch) { 135 | let next_index = dest.num_pairs(); 136 | dest.body 137 | .insert(next_index, self.body[0].len()) 138 | .expect("no space in dest branch"); 139 | dest.body[next_index].copy_from_slice(&self.body[0]); 140 | self.body.remove(0); 141 | } 142 | } 143 | 144 | #[cfg(test)] 145 | mod tests { 146 | use super::*; 147 | 148 | #[test] 149 | fn test_insert_search() { 150 | let mut data = vec![0u8; 100]; 151 | let mut branch = Branch::new(data.as_mut_slice()); 152 | branch.initialize(&5u64.to_be_bytes(), PageId(1), PageId(2)); 153 | branch.insert(1, &8u64.to_be_bytes(), PageId(3)).unwrap(); 154 | branch.insert(2, &11u64.to_be_bytes(), PageId(4)).unwrap(); 155 | assert_eq!(PageId(1), branch.search_child(&1u64.to_be_bytes())); 156 | assert_eq!(PageId(3), branch.search_child(&5u64.to_be_bytes())); 157 | assert_eq!(PageId(3), branch.search_child(&6u64.to_be_bytes())); 158 | assert_eq!(PageId(4), branch.search_child(&8u64.to_be_bytes())); 159 | assert_eq!(PageId(4), branch.search_child(&10u64.to_be_bytes())); 160 | assert_eq!(PageId(2), branch.search_child(&11u64.to_be_bytes())); 161 | assert_eq!(PageId(2), branch.search_child(&12u64.to_be_bytes())); 162 | } 163 | 164 | #[test] 165 | fn test_split() { 166 | let mut data = vec![0u8; 100]; 167 | let mut branch = Branch::new(data.as_mut_slice()); 168 | branch.initialize(&5u64.to_be_bytes(), PageId(1), PageId(2)); 169 | branch.insert(1, &8u64.to_be_bytes(), PageId(3)).unwrap(); 170 | branch.insert(2, &11u64.to_be_bytes(), PageId(4)).unwrap(); 171 | 172 | let mut data2 = vec![0u8; 100]; 173 | let mut branch2 = Branch::new(data2.as_mut_slice()); 174 | let mid_key = branch.split_insert(&mut branch2, &10u64.to_be_bytes(), PageId(5)); 175 | assert_eq!(&8u64.to_be_bytes(), mid_key.as_slice()); 176 | 177 | assert_eq!(2, branch.num_pairs()); 178 | assert_eq!(1, branch2.num_pairs()); 179 | 180 | assert_eq!(PageId(1), branch2.search_child(&1u64.to_be_bytes())); 181 | assert_eq!(PageId(3), branch2.search_child(&5u64.to_be_bytes())); 182 | assert_eq!(PageId(3), branch2.search_child(&6u64.to_be_bytes())); 183 | 184 | assert_eq!(PageId(5), branch.search_child(&9u64.to_be_bytes())); 185 | assert_eq!(PageId(4), branch.search_child(&10u64.to_be_bytes())); 186 | assert_eq!(PageId(2), branch.search_child(&11u64.to_be_bytes())); 187 | assert_eq!(PageId(2), branch.search_child(&12u64.to_be_bytes())); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/buffer.rs: -------------------------------------------------------------------------------- 1 | use std::cell::{Cell, RefCell}; 2 | use std::collections::HashMap; 3 | use std::io; 4 | use std::ops::{Index, IndexMut}; 5 | use std::rc::Rc; 6 | 7 | use crate::disk::{DiskManager, PageId, PAGE_SIZE}; 8 | 9 | #[derive(Debug, thiserror::Error)] 10 | pub enum Error { 11 | #[error(transparent)] 12 | Io(#[from] io::Error), 13 | #[error("no free buffer available in buffer pool")] 14 | NoFreeBuffer, 15 | } 16 | 17 | #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)] 18 | pub struct BufferId(usize); 19 | 20 | pub type Page = [u8; PAGE_SIZE]; 21 | 22 | #[derive(Debug)] 23 | pub struct Buffer { 24 | pub page_id: PageId, 25 | pub page: RefCell, 26 | pub is_dirty: Cell, 27 | } 28 | 29 | impl Default for Buffer { 30 | fn default() -> Self { 31 | Self { 32 | page_id: Default::default(), 33 | page: RefCell::new([0u8; PAGE_SIZE]), 34 | is_dirty: Cell::new(false), 35 | } 36 | } 37 | } 38 | 39 | #[derive(Debug, Default)] 40 | pub struct Frame { 41 | usage_count: u64, 42 | buffer: Rc, 43 | } 44 | 45 | pub struct BufferPool { 46 | buffers: Vec, 47 | next_victim_id: BufferId, 48 | } 49 | 50 | impl BufferPool { 51 | pub fn new(pool_size: usize) -> Self { 52 | let mut buffers = vec![]; 53 | buffers.resize_with(pool_size, Default::default); 54 | let next_victim_id = BufferId::default(); 55 | Self { 56 | buffers, 57 | next_victim_id, 58 | } 59 | } 60 | 61 | fn size(&self) -> usize { 62 | self.buffers.len() 63 | } 64 | 65 | fn evict(&mut self) -> Option { 66 | let pool_size = self.size(); 67 | let mut consecutive_pinned = 0; 68 | let victim_id = loop { 69 | let next_victim_id = self.next_victim_id; 70 | let frame = &mut self[next_victim_id]; 71 | if frame.usage_count == 0 { 72 | break self.next_victim_id; 73 | } 74 | if Rc::get_mut(&mut frame.buffer).is_some() { 75 | frame.usage_count -= 1; 76 | consecutive_pinned = 0; 77 | } else { 78 | consecutive_pinned += 1; 79 | if consecutive_pinned >= pool_size { 80 | return None; 81 | } 82 | } 83 | self.next_victim_id = self.increment_id(self.next_victim_id); 84 | }; 85 | Some(victim_id) 86 | } 87 | 88 | fn increment_id(&self, buffer_id: BufferId) -> BufferId { 89 | BufferId((buffer_id.0 + 1) % self.size()) 90 | } 91 | } 92 | 93 | impl Index for BufferPool { 94 | type Output = Frame; 95 | 96 | fn index(&self, index: BufferId) -> &Self::Output { 97 | &self.buffers[index.0] 98 | } 99 | } 100 | 101 | impl IndexMut for BufferPool { 102 | fn index_mut(&mut self, index: BufferId) -> &mut Self::Output { 103 | &mut self.buffers[index.0] 104 | } 105 | } 106 | 107 | pub struct BufferPoolManager { 108 | disk: DiskManager, 109 | pool: BufferPool, 110 | page_table: HashMap, 111 | } 112 | 113 | impl BufferPoolManager { 114 | pub fn new(disk: DiskManager, pool: BufferPool) -> Self { 115 | let page_table = HashMap::new(); 116 | Self { 117 | disk, 118 | pool, 119 | page_table, 120 | } 121 | } 122 | 123 | pub fn fetch_page(&mut self, page_id: PageId) -> Result, Error> { 124 | if let Some(&buffer_id) = self.page_table.get(&page_id) { 125 | let frame = &mut self.pool[buffer_id]; 126 | frame.usage_count += 1; 127 | return Ok(Rc::clone(&frame.buffer)); 128 | } 129 | let buffer_id = self.pool.evict().ok_or(Error::NoFreeBuffer)?; 130 | let frame = &mut self.pool[buffer_id]; 131 | let evict_page_id = frame.buffer.page_id; 132 | { 133 | let buffer = Rc::get_mut(&mut frame.buffer).unwrap(); 134 | if buffer.is_dirty.get() { 135 | self.disk 136 | .write_page_data(evict_page_id, buffer.page.get_mut())?; 137 | } 138 | buffer.page_id = page_id; 139 | buffer.is_dirty.set(false); 140 | self.disk.read_page_data(page_id, buffer.page.get_mut())?; 141 | frame.usage_count = 1; 142 | } 143 | let page = Rc::clone(&frame.buffer); 144 | self.page_table.remove(&evict_page_id); 145 | self.page_table.insert(page_id, buffer_id); 146 | Ok(page) 147 | } 148 | 149 | pub fn create_page(&mut self) -> Result, Error> { 150 | let buffer_id = self.pool.evict().ok_or(Error::NoFreeBuffer)?; 151 | let frame = &mut self.pool[buffer_id]; 152 | let evict_page_id = frame.buffer.page_id; 153 | let page_id = { 154 | let buffer = Rc::get_mut(&mut frame.buffer).unwrap(); 155 | if buffer.is_dirty.get() { 156 | self.disk 157 | .write_page_data(evict_page_id, buffer.page.get_mut())?; 158 | } 159 | let page_id = self.disk.allocate_page(); 160 | *buffer = Buffer::default(); 161 | buffer.page_id = page_id; 162 | buffer.is_dirty.set(true); 163 | frame.usage_count = 1; 164 | page_id 165 | }; 166 | let page = Rc::clone(&frame.buffer); 167 | self.page_table.remove(&evict_page_id); 168 | self.page_table.insert(page_id, buffer_id); 169 | Ok(page) 170 | } 171 | 172 | pub fn flush(&mut self) -> Result<(), Error> { 173 | for (&page_id, &buffer_id) in self.page_table.iter() { 174 | let frame = &self.pool[buffer_id]; 175 | let mut page = frame.buffer.page.borrow_mut(); 176 | self.disk.write_page_data(page_id, page.as_mut())?; 177 | frame.buffer.is_dirty.set(false); 178 | } 179 | self.disk.sync()?; 180 | Ok(()) 181 | } 182 | } 183 | 184 | #[cfg(test)] 185 | mod tests { 186 | use super::*; 187 | use tempfile::tempfile; 188 | 189 | #[test] 190 | fn test() { 191 | let mut hello = Vec::with_capacity(PAGE_SIZE); 192 | hello.extend_from_slice(b"hello"); 193 | hello.resize(PAGE_SIZE, 0); 194 | let mut world = Vec::with_capacity(PAGE_SIZE); 195 | world.extend_from_slice(b"world"); 196 | world.resize(PAGE_SIZE, 0); 197 | 198 | let disk = DiskManager::new(tempfile().unwrap()).unwrap(); 199 | let pool = BufferPool::new(1); 200 | let mut bufmgr = BufferPoolManager::new(disk, pool); 201 | let page1_id = { 202 | let buffer = bufmgr.create_page().unwrap(); 203 | assert!(bufmgr.create_page().is_err()); 204 | let mut page = buffer.page.borrow_mut(); 205 | page.copy_from_slice(&hello); 206 | buffer.is_dirty.set(true); 207 | buffer.page_id 208 | }; 209 | { 210 | let buffer = bufmgr.fetch_page(page1_id).unwrap(); 211 | let page = buffer.page.borrow(); 212 | assert_eq!(&hello, page.as_ref()); 213 | } 214 | let page2_id = { 215 | let buffer = bufmgr.create_page().unwrap(); 216 | let mut page = buffer.page.borrow_mut(); 217 | page.copy_from_slice(&world); 218 | buffer.is_dirty.set(true); 219 | buffer.page_id 220 | }; 221 | { 222 | let buffer = bufmgr.fetch_page(page1_id).unwrap(); 223 | let page = buffer.page.borrow(); 224 | assert_eq!(&hello, page.as_ref()); 225 | } 226 | { 227 | let buffer = bufmgr.fetch_page(page2_id).unwrap(); 228 | let page = buffer.page.borrow(); 229 | assert_eq!(&world, page.as_ref()); 230 | } 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "anyhow" 5 | version = "1.0.38" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" 8 | 9 | [[package]] 10 | name = "bincode" 11 | version = "1.3.1" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" 14 | dependencies = [ 15 | "byteorder", 16 | "serde", 17 | ] 18 | 19 | [[package]] 20 | name = "bitflags" 21 | version = "1.2.1" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 24 | 25 | [[package]] 26 | name = "block-buffer" 27 | version = "0.9.0" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 30 | dependencies = [ 31 | "generic-array", 32 | ] 33 | 34 | [[package]] 35 | name = "byteorder" 36 | version = "1.4.2" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" 39 | 40 | [[package]] 41 | name = "cfg-if" 42 | version = "1.0.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 45 | 46 | [[package]] 47 | name = "cpuid-bool" 48 | version = "0.1.2" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" 51 | 52 | [[package]] 53 | name = "digest" 54 | version = "0.9.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 57 | dependencies = [ 58 | "generic-array", 59 | ] 60 | 61 | [[package]] 62 | name = "generic-array" 63 | version = "0.14.4" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 66 | dependencies = [ 67 | "typenum", 68 | "version_check", 69 | ] 70 | 71 | [[package]] 72 | name = "getrandom" 73 | version = "0.2.2" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" 76 | dependencies = [ 77 | "cfg-if", 78 | "libc", 79 | "wasi", 80 | ] 81 | 82 | [[package]] 83 | name = "libc" 84 | version = "0.2.87" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "265d751d31d6780a3f956bb5b8022feba2d94eeee5a84ba64f4212eedca42213" 87 | 88 | [[package]] 89 | name = "md-5" 90 | version = "0.9.1" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" 93 | dependencies = [ 94 | "block-buffer", 95 | "digest", 96 | "opaque-debug", 97 | ] 98 | 99 | [[package]] 100 | name = "opaque-debug" 101 | version = "0.3.0" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 104 | 105 | [[package]] 106 | name = "ppv-lite86" 107 | version = "0.2.10" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 110 | 111 | [[package]] 112 | name = "proc-macro2" 113 | version = "1.0.24" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 116 | dependencies = [ 117 | "unicode-xid", 118 | ] 119 | 120 | [[package]] 121 | name = "quote" 122 | version = "1.0.9" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 125 | dependencies = [ 126 | "proc-macro2", 127 | ] 128 | 129 | [[package]] 130 | name = "rand" 131 | version = "0.8.3" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" 134 | dependencies = [ 135 | "libc", 136 | "rand_chacha", 137 | "rand_core", 138 | "rand_hc", 139 | ] 140 | 141 | [[package]] 142 | name = "rand_chacha" 143 | version = "0.3.0" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" 146 | dependencies = [ 147 | "ppv-lite86", 148 | "rand_core", 149 | ] 150 | 151 | [[package]] 152 | name = "rand_core" 153 | version = "0.6.2" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" 156 | dependencies = [ 157 | "getrandom", 158 | ] 159 | 160 | [[package]] 161 | name = "rand_hc" 162 | version = "0.3.0" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" 165 | dependencies = [ 166 | "rand_core", 167 | ] 168 | 169 | [[package]] 170 | name = "redox_syscall" 171 | version = "0.2.5" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" 174 | dependencies = [ 175 | "bitflags", 176 | ] 177 | 178 | [[package]] 179 | name = "relly" 180 | version = "0.1.0" 181 | dependencies = [ 182 | "anyhow", 183 | "bincode", 184 | "md-5", 185 | "serde", 186 | "sha-1", 187 | "tempfile", 188 | "thiserror", 189 | "zerocopy", 190 | ] 191 | 192 | [[package]] 193 | name = "remove_dir_all" 194 | version = "0.5.3" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 197 | dependencies = [ 198 | "winapi", 199 | ] 200 | 201 | [[package]] 202 | name = "serde" 203 | version = "1.0.123" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" 206 | dependencies = [ 207 | "serde_derive", 208 | ] 209 | 210 | [[package]] 211 | name = "serde_derive" 212 | version = "1.0.123" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" 215 | dependencies = [ 216 | "proc-macro2", 217 | "quote", 218 | "syn", 219 | ] 220 | 221 | [[package]] 222 | name = "sha-1" 223 | version = "0.9.4" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f" 226 | dependencies = [ 227 | "block-buffer", 228 | "cfg-if", 229 | "cpuid-bool", 230 | "digest", 231 | "opaque-debug", 232 | ] 233 | 234 | [[package]] 235 | name = "syn" 236 | version = "1.0.60" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" 239 | dependencies = [ 240 | "proc-macro2", 241 | "quote", 242 | "unicode-xid", 243 | ] 244 | 245 | [[package]] 246 | name = "synstructure" 247 | version = "0.12.4" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" 250 | dependencies = [ 251 | "proc-macro2", 252 | "quote", 253 | "syn", 254 | "unicode-xid", 255 | ] 256 | 257 | [[package]] 258 | name = "tempfile" 259 | version = "3.2.0" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" 262 | dependencies = [ 263 | "cfg-if", 264 | "libc", 265 | "rand", 266 | "redox_syscall", 267 | "remove_dir_all", 268 | "winapi", 269 | ] 270 | 271 | [[package]] 272 | name = "thiserror" 273 | version = "1.0.24" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" 276 | dependencies = [ 277 | "thiserror-impl", 278 | ] 279 | 280 | [[package]] 281 | name = "thiserror-impl" 282 | version = "1.0.24" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" 285 | dependencies = [ 286 | "proc-macro2", 287 | "quote", 288 | "syn", 289 | ] 290 | 291 | [[package]] 292 | name = "typenum" 293 | version = "1.12.0" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" 296 | 297 | [[package]] 298 | name = "unicode-xid" 299 | version = "0.2.1" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 302 | 303 | [[package]] 304 | name = "version_check" 305 | version = "0.9.2" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 308 | 309 | [[package]] 310 | name = "wasi" 311 | version = "0.10.2+wasi-snapshot-preview1" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 314 | 315 | [[package]] 316 | name = "winapi" 317 | version = "0.3.9" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 320 | dependencies = [ 321 | "winapi-i686-pc-windows-gnu", 322 | "winapi-x86_64-pc-windows-gnu", 323 | ] 324 | 325 | [[package]] 326 | name = "winapi-i686-pc-windows-gnu" 327 | version = "0.4.0" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 330 | 331 | [[package]] 332 | name = "winapi-x86_64-pc-windows-gnu" 333 | version = "0.4.0" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 336 | 337 | [[package]] 338 | name = "zerocopy" 339 | version = "0.3.0" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "6580539ad917b7c026220c4b3f2c08d52ce54d6ce0dc491e66002e35388fab46" 342 | dependencies = [ 343 | "byteorder", 344 | "zerocopy-derive", 345 | ] 346 | 347 | [[package]] 348 | name = "zerocopy-derive" 349 | version = "0.2.0" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "d498dbd1fd7beb83c86709ae1c33ca50942889473473d287d56ce4770a18edfb" 352 | dependencies = [ 353 | "proc-macro2", 354 | "syn", 355 | "synstructure", 356 | ] 357 | -------------------------------------------------------------------------------- /src/btree.rs: -------------------------------------------------------------------------------- 1 | use std::cell::{Ref, RefMut}; 2 | use std::convert::identity; 3 | use std::rc::Rc; 4 | 5 | use bincode::Options; 6 | use serde::{Deserialize, Serialize}; 7 | use thiserror::Error; 8 | use zerocopy::{AsBytes, ByteSlice}; 9 | 10 | use crate::buffer::{self, Buffer, BufferPoolManager}; 11 | use crate::disk::PageId; 12 | 13 | mod branch; 14 | mod leaf; 15 | mod meta; 16 | mod node; 17 | 18 | #[derive(Serialize, Deserialize)] 19 | pub struct Pair<'a> { 20 | pub key: &'a [u8], 21 | pub value: &'a [u8], 22 | } 23 | 24 | impl<'a> Pair<'a> { 25 | fn to_bytes(&self) -> Vec { 26 | bincode::options().serialize(self).unwrap() 27 | } 28 | 29 | fn from_bytes(bytes: &'a [u8]) -> Self { 30 | bincode::options().deserialize(bytes).unwrap() 31 | } 32 | } 33 | 34 | #[derive(Debug, Error)] 35 | pub enum Error { 36 | #[error("duplicate key")] 37 | DuplicateKey, 38 | #[error(transparent)] 39 | Buffer(#[from] buffer::Error), 40 | } 41 | 42 | #[derive(Debug, Clone)] 43 | pub enum SearchMode { 44 | Start, 45 | Key(Vec), 46 | } 47 | 48 | impl SearchMode { 49 | fn child_page_id(&self, branch: &branch::Branch) -> PageId { 50 | match self { 51 | SearchMode::Start => branch.child_at(0), 52 | SearchMode::Key(key) => branch.search_child(key), 53 | } 54 | } 55 | 56 | fn tuple_slot_id(&self, leaf: &leaf::Leaf) -> Result { 57 | match self { 58 | SearchMode::Start => Err(0), 59 | SearchMode::Key(key) => leaf.search_slot_id(key), 60 | } 61 | } 62 | } 63 | 64 | pub struct BTree { 65 | pub meta_page_id: PageId, 66 | } 67 | 68 | impl BTree { 69 | pub fn create(bufmgr: &mut BufferPoolManager) -> Result { 70 | let meta_buffer = bufmgr.create_page()?; 71 | let mut meta = meta::Meta::new(meta_buffer.page.borrow_mut() as RefMut<[_]>); 72 | let root_buffer = bufmgr.create_page()?; 73 | let mut root = node::Node::new(root_buffer.page.borrow_mut() as RefMut<[_]>); 74 | root.initialize_as_leaf(); 75 | let mut leaf = leaf::Leaf::new(root.body); 76 | leaf.initialize(); 77 | meta.header.root_page_id = root_buffer.page_id; 78 | Ok(Self::new(meta_buffer.page_id)) 79 | } 80 | 81 | pub fn new(meta_page_id: PageId) -> Self { 82 | Self { meta_page_id } 83 | } 84 | 85 | fn fetch_root_page(&self, bufmgr: &mut BufferPoolManager) -> Result, Error> { 86 | let root_page_id = { 87 | let meta_buffer = bufmgr.fetch_page(self.meta_page_id)?; 88 | let meta = meta::Meta::new(meta_buffer.page.borrow() as Ref<[_]>); 89 | meta.header.root_page_id 90 | }; 91 | Ok(bufmgr.fetch_page(root_page_id)?) 92 | } 93 | 94 | fn search_internal( 95 | &self, 96 | bufmgr: &mut BufferPoolManager, 97 | node_buffer: Rc, 98 | search_mode: SearchMode, 99 | ) -> Result { 100 | let node = node::Node::new(node_buffer.page.borrow() as Ref<[_]>); 101 | match node::Body::new(node.header.node_type, node.body.as_bytes()) { 102 | node::Body::Leaf(leaf) => { 103 | let slot_id = search_mode.tuple_slot_id(&leaf).unwrap_or_else(identity); 104 | let is_right_most = leaf.num_pairs() == slot_id; 105 | drop(node); 106 | 107 | let mut iter = Iter { 108 | buffer: node_buffer, 109 | slot_id, 110 | }; 111 | if is_right_most { 112 | iter.advance(bufmgr)?; 113 | } 114 | Ok(iter) 115 | } 116 | node::Body::Branch(branch) => { 117 | let child_page_id = search_mode.child_page_id(&branch); 118 | drop(node); 119 | drop(node_buffer); 120 | let child_node_page = bufmgr.fetch_page(child_page_id)?; 121 | self.search_internal(bufmgr, child_node_page, search_mode) 122 | } 123 | } 124 | } 125 | 126 | pub fn search( 127 | &self, 128 | bufmgr: &mut BufferPoolManager, 129 | search_mode: SearchMode, 130 | ) -> Result { 131 | let root_page = self.fetch_root_page(bufmgr)?; 132 | self.search_internal(bufmgr, root_page, search_mode) 133 | } 134 | 135 | fn insert_internal( 136 | &self, 137 | bufmgr: &mut BufferPoolManager, 138 | buffer: Rc, 139 | key: &[u8], 140 | value: &[u8], 141 | ) -> Result, PageId)>, Error> { 142 | let node = node::Node::new(buffer.page.borrow_mut() as RefMut<[_]>); 143 | match node::Body::new(node.header.node_type, node.body) { 144 | node::Body::Leaf(mut leaf) => { 145 | let slot_id = match leaf.search_slot_id(key) { 146 | Ok(_) => return Err(Error::DuplicateKey), 147 | Err(slot_id) => slot_id, 148 | }; 149 | if leaf.insert(slot_id, key, value).is_some() { 150 | buffer.is_dirty.set(true); 151 | Ok(None) 152 | } else { 153 | let prev_leaf_page_id = leaf.prev_page_id(); 154 | let prev_leaf_buffer = prev_leaf_page_id 155 | .map(|next_leaf_page_id| bufmgr.fetch_page(next_leaf_page_id)) 156 | .transpose()?; 157 | 158 | let new_leaf_buffer = bufmgr.create_page()?; 159 | 160 | if let Some(prev_leaf_buffer) = prev_leaf_buffer { 161 | let node = 162 | node::Node::new(prev_leaf_buffer.page.borrow_mut() as RefMut<[_]>); 163 | let mut prev_leaf = leaf::Leaf::new(node.body); 164 | prev_leaf.set_next_page_id(Some(new_leaf_buffer.page_id)); 165 | prev_leaf_buffer.is_dirty.set(true); 166 | } 167 | leaf.set_prev_page_id(Some(new_leaf_buffer.page_id)); 168 | 169 | let mut new_leaf_node = 170 | node::Node::new(new_leaf_buffer.page.borrow_mut() as RefMut<[_]>); 171 | new_leaf_node.initialize_as_leaf(); 172 | let mut new_leaf = leaf::Leaf::new(new_leaf_node.body); 173 | new_leaf.initialize(); 174 | let overflow_key = leaf.split_insert(&mut new_leaf, key, value); 175 | new_leaf.set_next_page_id(Some(buffer.page_id)); 176 | new_leaf.set_prev_page_id(prev_leaf_page_id); 177 | buffer.is_dirty.set(true); 178 | Ok(Some((overflow_key, new_leaf_buffer.page_id))) 179 | } 180 | } 181 | node::Body::Branch(mut branch) => { 182 | let child_idx = branch.search_child_idx(key); 183 | let child_page_id = branch.child_at(child_idx); 184 | let child_node_buffer = bufmgr.fetch_page(child_page_id)?; 185 | if let Some((overflow_key_from_child, overflow_child_page_id)) = 186 | self.insert_internal(bufmgr, child_node_buffer, key, value)? 187 | { 188 | if branch 189 | .insert(child_idx, &overflow_key_from_child, overflow_child_page_id) 190 | .is_some() 191 | { 192 | buffer.is_dirty.set(true); 193 | Ok(None) 194 | } else { 195 | let new_branch_buffer = bufmgr.create_page()?; 196 | let mut new_branch_node = 197 | node::Node::new(new_branch_buffer.page.borrow_mut() as RefMut<[_]>); 198 | new_branch_node.initialize_as_branch(); 199 | let mut new_branch = branch::Branch::new(new_branch_node.body); 200 | let overflow_key = branch.split_insert( 201 | &mut new_branch, 202 | &overflow_key_from_child, 203 | overflow_child_page_id, 204 | ); 205 | buffer.is_dirty.set(true); 206 | new_branch_buffer.is_dirty.set(true); 207 | Ok(Some((overflow_key, new_branch_buffer.page_id))) 208 | } 209 | } else { 210 | Ok(None) 211 | } 212 | } 213 | } 214 | } 215 | 216 | pub fn insert( 217 | &self, 218 | bufmgr: &mut BufferPoolManager, 219 | key: &[u8], 220 | value: &[u8], 221 | ) -> Result<(), Error> { 222 | let meta_buffer = bufmgr.fetch_page(self.meta_page_id)?; 223 | let mut meta = meta::Meta::new(meta_buffer.page.borrow_mut() as RefMut<[_]>); 224 | let root_page_id = meta.header.root_page_id; 225 | let root_buffer = bufmgr.fetch_page(root_page_id)?; 226 | if let Some((key, child_page_id)) = self.insert_internal(bufmgr, root_buffer, key, value)? { 227 | let new_root_buffer = bufmgr.create_page()?; 228 | let mut node = node::Node::new(new_root_buffer.page.borrow_mut() as RefMut<[_]>); 229 | node.initialize_as_branch(); 230 | let mut branch = branch::Branch::new(node.body); 231 | branch.initialize(&key, child_page_id, root_page_id); 232 | meta.header.root_page_id = new_root_buffer.page_id; 233 | meta_buffer.is_dirty.set(true); 234 | } 235 | Ok(()) 236 | } 237 | } 238 | 239 | pub struct Iter { 240 | buffer: Rc, 241 | slot_id: usize, 242 | } 243 | 244 | impl Iter { 245 | fn get(&self) -> Option<(Vec, Vec)> { 246 | let leaf_node = node::Node::new(self.buffer.page.borrow() as Ref<[_]>); 247 | let leaf = leaf::Leaf::new(leaf_node.body); 248 | if self.slot_id < leaf.num_pairs() { 249 | let pair = leaf.pair_at(self.slot_id); 250 | Some((pair.key.to_vec(), pair.value.to_vec())) 251 | } else { 252 | None 253 | } 254 | } 255 | 256 | fn advance(&mut self, bufmgr: &mut BufferPoolManager) -> Result<(), Error> { 257 | self.slot_id += 1; 258 | let next_page_id = { 259 | let leaf_node = node::Node::new(self.buffer.page.borrow() as Ref<[_]>); 260 | let leaf = leaf::Leaf::new(leaf_node.body); 261 | if self.slot_id < leaf.num_pairs() { 262 | return Ok(()); 263 | } 264 | leaf.next_page_id() 265 | }; 266 | if let Some(next_page_id) = next_page_id { 267 | self.buffer = bufmgr.fetch_page(next_page_id)?; 268 | self.slot_id = 0; 269 | } 270 | Ok(()) 271 | } 272 | 273 | #[allow(clippy::type_complexity)] 274 | pub fn next( 275 | &mut self, 276 | bufmgr: &mut BufferPoolManager, 277 | ) -> Result, Vec)>, Error> { 278 | let value = self.get(); 279 | self.advance(bufmgr)?; 280 | Ok(value) 281 | } 282 | } 283 | 284 | #[cfg(test)] 285 | mod tests { 286 | use tempfile::tempfile; 287 | 288 | use crate::{buffer::BufferPool, disk::DiskManager}; 289 | 290 | use super::*; 291 | #[test] 292 | fn test() { 293 | let disk = DiskManager::new(tempfile().unwrap()).unwrap(); 294 | let pool = BufferPool::new(10); 295 | let mut bufmgr = BufferPoolManager::new(disk, pool); 296 | let btree = BTree::create(&mut bufmgr).unwrap(); 297 | btree 298 | .insert(&mut bufmgr, &6u64.to_be_bytes(), b"world") 299 | .unwrap(); 300 | btree 301 | .insert(&mut bufmgr, &3u64.to_be_bytes(), b"hello") 302 | .unwrap(); 303 | btree 304 | .insert(&mut bufmgr, &8u64.to_be_bytes(), b"!") 305 | .unwrap(); 306 | btree 307 | .insert(&mut bufmgr, &4u64.to_be_bytes(), b",") 308 | .unwrap(); 309 | 310 | let (_, value) = btree 311 | .search(&mut bufmgr, SearchMode::Key(3u64.to_be_bytes().to_vec())) 312 | .unwrap() 313 | .get() 314 | .unwrap(); 315 | assert_eq!(b"hello", &value[..]); 316 | let (_, value) = btree 317 | .search(&mut bufmgr, SearchMode::Key(8u64.to_be_bytes().to_vec())) 318 | .unwrap() 319 | .get() 320 | .unwrap(); 321 | assert_eq!(b"!", &value[..]); 322 | } 323 | 324 | #[test] 325 | fn test_search_iter() { 326 | let disk = DiskManager::new(tempfile().unwrap()).unwrap(); 327 | let pool = BufferPool::new(10); 328 | let mut bufmgr = BufferPoolManager::new(disk, pool); 329 | let btree = BTree::create(&mut bufmgr).unwrap(); 330 | 331 | for i in 0u64..16 { 332 | btree 333 | .insert(&mut bufmgr, &(i * 2).to_be_bytes(), &[0; 1024]) 334 | .unwrap(); 335 | } 336 | 337 | for i in 0u64..15 { 338 | let (key, _) = btree 339 | .search( 340 | &mut bufmgr, 341 | SearchMode::Key((i * 2 + 1).to_be_bytes().to_vec()), 342 | ) 343 | .unwrap() 344 | .get() 345 | .unwrap(); 346 | assert_eq!(key.as_slice(), &((i + 1) * 2).to_be_bytes()); 347 | } 348 | } 349 | 350 | #[test] 351 | fn test_split() { 352 | let disk = DiskManager::new(tempfile().unwrap()).unwrap(); 353 | let pool = BufferPool::new(10); 354 | let mut bufmgr = BufferPoolManager::new(disk, pool); 355 | let btree = BTree::create(&mut bufmgr).unwrap(); 356 | let long_data_list = vec![ 357 | vec![0xC0u8; 1000], 358 | vec![0x01u8; 1000], 359 | vec![0xCAu8; 1000], 360 | vec![0xFEu8; 1000], 361 | vec![0xDEu8; 1000], 362 | vec![0xADu8; 1000], 363 | vec![0xBEu8; 1000], 364 | vec![0xAEu8; 1000], 365 | ]; 366 | for data in long_data_list.iter() { 367 | btree.insert(&mut bufmgr, data, data).unwrap(); 368 | } 369 | for data in long_data_list.iter() { 370 | let (k, v) = btree 371 | .search(&mut bufmgr, SearchMode::Key(data.clone())) 372 | .unwrap() 373 | .get() 374 | .unwrap(); 375 | assert_eq!(data, &k); 376 | assert_eq!(data, &v); 377 | } 378 | } 379 | } 380 | --------------------------------------------------------------------------------